diff --git a/.history/.env_20251019201239 b/.history/.env_20251019201239 new file mode 100644 index 0000000..bbf9a40 --- /dev/null +++ b/.history/.env_20251019201239 @@ -0,0 +1,34 @@ +# Environment Configuration +NODE_ENV=development +PORT=3000 + +# Database +MONGODB_URI=mongodb://localhost:27017/smartsoltech + +# JWT Secret +JWT_SECRET=your_super_secret_jwt_key_here_change_in_production + +# Session Secret +SESSION_SECRET=your_session_secret_here_change_in_production + +# Telegram Bot +TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here +TELEGRAM_CHAT_ID=your_telegram_chat_id_here + +# Email Configuration (for contact forms) +EMAIL_HOST=smtp.gmail.com +EMAIL_PORT=587 +EMAIL_USER=your_email@gmail.com +EMAIL_PASS=your_email_password + +# Admin Credentials (default) +ADMIN_EMAIL=admin@smartsoltech.kr +ADMIN_PASSWORD=admin123456 + +# File Upload +MAX_FILE_SIZE=10485760 +UPLOAD_PATH=./public/uploads + +# Site Configuration +SITE_URL=https://smartsoltech.kr +SITE_NAME=SmartSolTech \ No newline at end of file diff --git a/.history/.env_20251019201325 b/.history/.env_20251019201325 new file mode 100644 index 0000000..71be5b1 --- /dev/null +++ b/.history/.env_20251019201325 @@ -0,0 +1,34 @@ +# Environment Configuration +NODE_ENV=development +PORT=3000 + +# Database +MONGODB_URI=mongodb://localhost:27017/smartsoltech + +# JWT Secret +JWT_SECRET=:AISDFUhbsLKFJADESFIOlugtywow8fryuelhbf2p349[yresdugiflhbjKASDJfbh + +# Session Secret +SESSION_SECRET=LKSJ:DFHGAJKLfbads;oflIAFK:DHJSUGBVdil;asufgtdshjfv + +# Telegram Bot +TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here +TELEGRAM_CHAT_ID=your_telegram_chat_id_here + +# Email Configuration (for contact forms) +EMAIL_HOST=smtp.gmail.com +EMAIL_PORT=587 +EMAIL_USER=your_email@gmail.com +EMAIL_PASS=your_email_password + +# Admin Credentials (default) +ADMIN_EMAIL=admin@smartsoltech.kr +ADMIN_PASSWORD=admin123456 + +# File Upload +MAX_FILE_SIZE=10485760 +UPLOAD_PATH=./public/uploads + +# Site Configuration +SITE_URL=https://smartsoltech.kr +SITE_NAME=SmartSolTech \ No newline at end of file diff --git a/.history/.env_20251019201339 b/.history/.env_20251019201339 new file mode 100644 index 0000000..d0b7103 --- /dev/null +++ b/.history/.env_20251019201339 @@ -0,0 +1,34 @@ +# Environment Configuration +NODE_ENV=development +PORT=3000 + +# Database +MONGODB_URI=mongodb://localhost:27017/smartsoltech + +# JWT Secret +JWT_SECRET=:AISDFUhbsLKFJADESFIOlugtywow8fryuelhbf2p349[yresdugiflhbjKASDJfbh + +# Session Secret +SESSION_SECRET=LKSJ:DFHGAJKLfbads;oflIAFK:DHJSUGBVdil;asufgtdshjfv + +# Telegram Bot +TELEGRAM_BOT_TOKEN=8289431590:AAEVZWjb6PgGbv-jivphXfqCMD9c6NqE870 +TELEGRAM_CHAT_ID=your_telegram_chat_id_here + +# Email Configuration (for contact forms) +EMAIL_HOST=smtp.gmail.com +EMAIL_PORT=587 +EMAIL_USER=your_email@gmail.com +EMAIL_PASS=your_email_password + +# Admin Credentials (default) +ADMIN_EMAIL=admin@smartsoltech.kr +ADMIN_PASSWORD=admin123456 + +# File Upload +MAX_FILE_SIZE=10485760 +UPLOAD_PATH=./public/uploads + +# Site Configuration +SITE_URL=https://smartsoltech.kr +SITE_NAME=SmartSolTech \ No newline at end of file diff --git a/.history/.env_20251019201413 b/.history/.env_20251019201413 new file mode 100644 index 0000000..2387270 --- /dev/null +++ b/.history/.env_20251019201413 @@ -0,0 +1,34 @@ +# Environment Configuration +NODE_ENV=development +PORT=3000 + +# Database +MONGODB_URI=mongodb://localhost:27017/smartsoltech + +# JWT Secret +JWT_SECRET=:AISDFUhbsLKFJADESFIOlugtywow8fryuelhbf2p349[yresdugiflhbjKASDJfbh + +# Session Secret +SESSION_SECRET=LKSJ:DFHGAJKLfbads;oflIAFK:DHJSUGBVdil;asufgtdshjfv + +# Telegram Bot +TELEGRAM_BOT_TOKEN=8289431590:AAEVZWjb6PgGbv-jivphXfqCMD9c6NqE870 +TELEGRAM_CHAT_ID=-4949859416 + +# Email Configuration (for contact forms) +EMAIL_HOST=smtp.gmail.com +EMAIL_PORT=587 +EMAIL_USER=your_email@gmail.com +EMAIL_PASS=your_email_password + +# Admin Credentials (default) +ADMIN_EMAIL=admin@smartsoltech.kr +ADMIN_PASSWORD=admin123456 + +# File Upload +MAX_FILE_SIZE=10485760 +UPLOAD_PATH=./public/uploads + +# Site Configuration +SITE_URL=https://smartsoltech.kr +SITE_NAME=SmartSolTech \ No newline at end of file diff --git a/.history/.env_20251019201552 b/.history/.env_20251019201552 new file mode 100644 index 0000000..5f88f82 --- /dev/null +++ b/.history/.env_20251019201552 @@ -0,0 +1,39 @@ +# Environment Configuration +NODE_ENV=development +PORT=3000 + +# Database - PostgreSQL +DATABASE_URL=postgresql://postgres:password@localhost:5432/smartsoltech +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=smartsoltech +DB_USER=postgres +DB_PASSWORD=password + +# JWT Secret +JWT_SECRET=:AISDFUhbsLKFJADESFIOlugtywow8fryuelhbf2p349[yresdugiflhbjKASDJfbh + +# Session Secret +SESSION_SECRET=LKSJ:DFHGAJKLfbads;oflIAFK:DHJSUGBVdil;asufgtdshjfv + +# Telegram Bot +TELEGRAM_BOT_TOKEN=8289431590:AAEVZWjb6PgGbv-jivphXfqCMD9c6NqE870 +TELEGRAM_CHAT_ID=-4949859416 + +# Email Configuration (for contact forms) +EMAIL_HOST=smtp.gmail.com +EMAIL_PORT=587 +EMAIL_USER=your_email@gmail.com +EMAIL_PASS=your_email_password + +# Admin Credentials (default) +ADMIN_EMAIL=admin@smartsoltech.kr +ADMIN_PASSWORD=admin123456 + +# File Upload +MAX_FILE_SIZE=10485760 +UPLOAD_PATH=./public/uploads + +# Site Configuration +SITE_URL=https://smartsoltech.kr +SITE_NAME=SmartSolTech \ No newline at end of file diff --git a/.history/.env_20251019201634 b/.history/.env_20251019201634 new file mode 100644 index 0000000..036433e --- /dev/null +++ b/.history/.env_20251019201634 @@ -0,0 +1,39 @@ +# Environment Configuration +NODE_ENV=development +PORT=3000 + +# Database - PostgreSQL +DATABASE_URL=postgresql://trevor:R0sebud@localhost:5432/smartsoltech +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=smartsoltech +DB_USER=trevor +DB_PASSWORD=R0sebud + +# JWT Secret +JWT_SECRET=:AISDFUhbsLKFJADESFIOlugtywow8fryuelhbf2p349[yresdugiflhbjKASDJfbh + +# Session Secret +SESSION_SECRET=LKSJ:DFHGAJKLfbads;oflIAFK:DHJSUGBVdil;asufgtdshjfv + +# Telegram Bot +TELEGRAM_BOT_TOKEN=8289431590:AAEVZWjb6PgGbv-jivphXfqCMD9c6NqE870 +TELEGRAM_CHAT_ID=-4949859416 + +# Email Configuration (for contact forms) +EMAIL_HOST=smtp.gmail.com +EMAIL_PORT=587 +EMAIL_USER=your_email@gmail.com +EMAIL_PASS=your_email_password + +# Admin Credentials (default) +ADMIN_EMAIL=admin@smartsoltech.kr +ADMIN_PASSWORD=admin123456 + +# File Upload +MAX_FILE_SIZE=10485760 +UPLOAD_PATH=./public/uploads + +# Site Configuration +SITE_URL=https://smartsoltech.kr +SITE_NAME=SmartSolTech \ No newline at end of file diff --git a/.history/.env_20251021214349 b/.history/.env_20251021214349 new file mode 100644 index 0000000..6cf440b --- /dev/null +++ b/.history/.env_20251021214349 @@ -0,0 +1,39 @@ +# Environment Configuration +NODE_ENV=development +PORT=3000 + +# Database - PostgreSQL +DATABASE_URL=postgresql://trevor:R0sebud@localhost:5432/smartsoltech +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=smartsoltech +DB_USER=trevor +DB_PASSWORD=R0sebud + +# JWT Secret +JWT_SECRET=:AISDFUhbsLKFJADESFIOlugtywow8fryuelhbf2p349[yresdugiflhbjKASDJfbh + +# Session Secret +SESSION_SECRET=LKSJ:DFHGAJKLfbads;oflIAFK:DHJSUGBVdil;asufgtdshjfv + +# Telegram Bot +TELEGRAM_BOT_TOKEN=8289431590:AAEVZWjb6PgGbv-jivphXfqCMD9c6NqE870 +TELEGRAM_CHAT_ID=-4949859416 + +# Email Configuration (for contact forms) +EMAIL_HOST=smtp.gmail.com +EMAIL_PORT=587 +EMAIL_USER=tsoy4uk@gmail.com +EMAIL_PASS=Cl0ud_1985_! + +# Admin Credentials (default) +ADMIN_EMAIL=admin@smartsoltech.kr +ADMIN_PASSWORD=admin123456 + +# File Upload +MAX_FILE_SIZE=10485760 +UPLOAD_PATH=./public/uploads + +# Site Configuration +SITE_URL=https://smartsoltech.kr +SITE_NAME=SmartSolTech \ No newline at end of file diff --git a/.history/.github/copilot-instructions_20251019201336.md b/.history/.github/copilot-instructions_20251019201336.md new file mode 100644 index 0000000..c35af6b --- /dev/null +++ b/.history/.github/copilot-instructions_20251019201336.md @@ -0,0 +1,151 @@ +# SmartSolTech Website - AI Coding Agent Instructions + +## Project Overview +SmartSolTech is a **Korean tech services company** PWA with an admin panel, portfolio management, Telegram integration, and service calculator. Built with Node.js/Express backend, EJS templating, MongoDB, and modern PWA features. + +## Architecture & Key Patterns + +### 🏗️ Backend Architecture +- **Main server**: `server.js` - Express app with comprehensive middleware stack +- **Models**: MongoDB with Mongoose ODM - all in `/models/` (User, Portfolio, Service, Contact, SiteSettings) +- **Routes**: RESTful API patterns in `/routes/` - separate files per domain +- **Authentication**: Dual auth system - JWT tokens for API, sessions for web pages +- **Validation**: express-validator middleware in `/middleware/validation.js` with Korean error messages + +### 🔑 Authentication Pattern +```javascript +// Two authentication strategies: +// 1. JWT for API endpoints (authenticateToken) +// 2. Session-based for web pages (authenticateSession) +// See middleware/auth.js for implementations +``` + +### 📊 Data Models Key Features +- **Portfolio**: Has virtual `primaryImage` getter, text search indexes, and SEO fields +- **Service**: Complex pricing structure with features array (included/not included) +- **User**: bcrypt password hashing with pre-save hook and comparePassword method +- **All models**: Use timestamps and have soft-delete patterns where applicable + +### 🛣️ Route Organization +- **Admin routes** (`/admin`): Session-based auth, server-side rendered EJS views +- **API routes** (`/api/*`): JWT auth, JSON responses, rate-limited +- **Public routes** (`/`): Mixed auth (optional), EJS templates with PWA support + +## Development Workflow + +### 🚀 Essential Commands +```bash +# Development with hot reload and file watching +npm run dev + +# Initialize database with admin user and sample data +npm run init-db + +# Production build with webpack optimization +npm run build + +# Database setup creates: +# - Admin user (from .env: ADMIN_EMAIL/ADMIN_PASSWORD) +# - Sample services, portfolio items, site settings +``` + +### 🔧 Environment Setup +**Critical `.env` variables**: +- `MONGODB_URI` - Database connection +- `SESSION_SECRET`, `JWT_SECRET` - Security keys +- `ADMIN_EMAIL`, `ADMIN_PASSWORD` - Initial admin account +- `TELEGRAM_BOT_TOKEN` - Optional Telegram integration +- `NODE_ENV` - Controls error verbosity and security settings + +## Frontend Architecture + +### 🎨 View Layer (EJS Templates) +- **Layout system**: `views/layout.ejs` is main wrapper +- **Partials**: `views/partials/` for reusable components (navigation, footer) +- **Admin panel**: Separate layout `admin/layout.ejs` with different styling +- **Internationalization**: Content in Korean, supports locales in `/locales/` + +### 📱 PWA Implementation +- **Service Worker**: `public/sw.js` - caches static files, dynamic routes, and API responses +- **Manifest**: `public/manifest.json` - app metadata and icons +- **Offline-first**: Service worker handles network failures gracefully +- **Cache strategy**: Static files cached immediately, dynamic content cached on demand + +### 🎯 Frontend JavaScript Patterns +- **Main script**: `public/js/main.js` - handles navigation, scroll effects, form interactions +- **Calculator**: `public/js/calculator.js` - complex service pricing calculations +- **Libraries**: AOS animations, Tailwind CSS, Socket.io for real-time features + +## Key Integration Points + +### 📧 Communication Systems +- **Telegram Bot**: Optional integration for admin notifications (contact forms, orders) +- **Email**: Nodemailer for SMTP email sending (contact forms, notifications) +- **Real-time**: Socket.io setup for live updates (dashboard, notifications) + +### 🔒 Security Patterns +- **Helmet**: CSP headers allowing specific domains (fonts.googleapis.com, cdnjs.cloudflare.com) +- **Rate limiting**: Applied to `/api/*` routes (100 requests/15min per IP) +- **Input validation**: Korean-language error messages, comprehensive field validation +- **File uploads**: Multer with Sharp image processing, stored in `public/uploads/` + +### 🗄️ Database Patterns +- **Connection**: Single MongoDB connection with connection pooling +- **Indexing**: Text search on Portfolio, compound indexes for performance +- **Session storage**: MongoDB session store for persistence + +## Business Logic Specifics + +### 💰 Service Calculator +- **Complex pricing**: Base price + features + timeline modifiers +- **Multi-step form**: Service selection → customization → contact info → quote +- **Integration**: Calculator data feeds into Contact model for lead tracking + +### 🎨 Portfolio Management +- **Image handling**: Multiple images per project, primary image logic +- **Categories**: Predefined categories (web-development, mobile-app, ui-ux-design, etc.) +- **SEO optimization**: Meta fields, structured data for portfolio items + +### 👑 Admin Panel Features +- **Dashboard**: Statistics, recent activity, quick actions +- **Content management**: CRUD for Portfolio, Services, Site Settings +- **Media gallery**: File upload with image optimization +- **Contact management**: Lead tracking, status updates, response handling + +## Common Tasks & Patterns + +### 🔧 Adding New Features +1. **Model**: Create in `/models/` with proper validation and indexes +2. **Routes**: Add to `/routes/` with appropriate auth middleware +3. **Views**: EJS templates in `/views/` using layout system +4. **API**: JSON endpoints with validation middleware +5. **Frontend**: Add to `public/js/` with proper event handling + +### 🐛 Debugging Workflow +- **Development**: Use `npm run dev` for auto-restart and detailed logging +- **Database**: Check MongoDB connection and sample data with `npm run init-db` +- **Authentication**: Test both JWT (API) and session (web) auth flows +- **PWA**: Check service worker registration and cache behavior in dev tools + +### 📦 Deployment Considerations +- **Environment**: Production requires secure cookies, CSP, and rate limiting +- **Database**: Ensure MongoDB indexes are created for performance +- **Assets**: Static files served by Express, consider CDN for production +- **Monitoring**: Error handling sends different messages based on NODE_ENV + +## File Structure Quick Reference +``` +├── models/ # MongoDB schemas with business logic +├── routes/ # Express routes (API + web pages) +├── middleware/ # Auth, validation, error handling +├── views/ # EJS templates with Korean content +├── public/ # Static assets + PWA files +├── scripts/ # Database init, dev server, build tools +└── server.js # Main Express application +``` + +## Korean Language Notes +- **UI Text**: All user-facing content in Korean +- **Error Messages**: Validation errors in Korean (`middleware/validation.js`) +- **Admin Interface**: Korean labels and messages throughout admin panel +- **SEO Content**: Korean meta descriptions and structured data \ No newline at end of file diff --git a/.history/.github/copilot-instructions_20251019201424.md b/.history/.github/copilot-instructions_20251019201424.md new file mode 100644 index 0000000..c35af6b --- /dev/null +++ b/.history/.github/copilot-instructions_20251019201424.md @@ -0,0 +1,151 @@ +# SmartSolTech Website - AI Coding Agent Instructions + +## Project Overview +SmartSolTech is a **Korean tech services company** PWA with an admin panel, portfolio management, Telegram integration, and service calculator. Built with Node.js/Express backend, EJS templating, MongoDB, and modern PWA features. + +## Architecture & Key Patterns + +### 🏗️ Backend Architecture +- **Main server**: `server.js` - Express app with comprehensive middleware stack +- **Models**: MongoDB with Mongoose ODM - all in `/models/` (User, Portfolio, Service, Contact, SiteSettings) +- **Routes**: RESTful API patterns in `/routes/` - separate files per domain +- **Authentication**: Dual auth system - JWT tokens for API, sessions for web pages +- **Validation**: express-validator middleware in `/middleware/validation.js` with Korean error messages + +### 🔑 Authentication Pattern +```javascript +// Two authentication strategies: +// 1. JWT for API endpoints (authenticateToken) +// 2. Session-based for web pages (authenticateSession) +// See middleware/auth.js for implementations +``` + +### 📊 Data Models Key Features +- **Portfolio**: Has virtual `primaryImage` getter, text search indexes, and SEO fields +- **Service**: Complex pricing structure with features array (included/not included) +- **User**: bcrypt password hashing with pre-save hook and comparePassword method +- **All models**: Use timestamps and have soft-delete patterns where applicable + +### 🛣️ Route Organization +- **Admin routes** (`/admin`): Session-based auth, server-side rendered EJS views +- **API routes** (`/api/*`): JWT auth, JSON responses, rate-limited +- **Public routes** (`/`): Mixed auth (optional), EJS templates with PWA support + +## Development Workflow + +### 🚀 Essential Commands +```bash +# Development with hot reload and file watching +npm run dev + +# Initialize database with admin user and sample data +npm run init-db + +# Production build with webpack optimization +npm run build + +# Database setup creates: +# - Admin user (from .env: ADMIN_EMAIL/ADMIN_PASSWORD) +# - Sample services, portfolio items, site settings +``` + +### 🔧 Environment Setup +**Critical `.env` variables**: +- `MONGODB_URI` - Database connection +- `SESSION_SECRET`, `JWT_SECRET` - Security keys +- `ADMIN_EMAIL`, `ADMIN_PASSWORD` - Initial admin account +- `TELEGRAM_BOT_TOKEN` - Optional Telegram integration +- `NODE_ENV` - Controls error verbosity and security settings + +## Frontend Architecture + +### 🎨 View Layer (EJS Templates) +- **Layout system**: `views/layout.ejs` is main wrapper +- **Partials**: `views/partials/` for reusable components (navigation, footer) +- **Admin panel**: Separate layout `admin/layout.ejs` with different styling +- **Internationalization**: Content in Korean, supports locales in `/locales/` + +### 📱 PWA Implementation +- **Service Worker**: `public/sw.js` - caches static files, dynamic routes, and API responses +- **Manifest**: `public/manifest.json` - app metadata and icons +- **Offline-first**: Service worker handles network failures gracefully +- **Cache strategy**: Static files cached immediately, dynamic content cached on demand + +### 🎯 Frontend JavaScript Patterns +- **Main script**: `public/js/main.js` - handles navigation, scroll effects, form interactions +- **Calculator**: `public/js/calculator.js` - complex service pricing calculations +- **Libraries**: AOS animations, Tailwind CSS, Socket.io for real-time features + +## Key Integration Points + +### 📧 Communication Systems +- **Telegram Bot**: Optional integration for admin notifications (contact forms, orders) +- **Email**: Nodemailer for SMTP email sending (contact forms, notifications) +- **Real-time**: Socket.io setup for live updates (dashboard, notifications) + +### 🔒 Security Patterns +- **Helmet**: CSP headers allowing specific domains (fonts.googleapis.com, cdnjs.cloudflare.com) +- **Rate limiting**: Applied to `/api/*` routes (100 requests/15min per IP) +- **Input validation**: Korean-language error messages, comprehensive field validation +- **File uploads**: Multer with Sharp image processing, stored in `public/uploads/` + +### 🗄️ Database Patterns +- **Connection**: Single MongoDB connection with connection pooling +- **Indexing**: Text search on Portfolio, compound indexes for performance +- **Session storage**: MongoDB session store for persistence + +## Business Logic Specifics + +### 💰 Service Calculator +- **Complex pricing**: Base price + features + timeline modifiers +- **Multi-step form**: Service selection → customization → contact info → quote +- **Integration**: Calculator data feeds into Contact model for lead tracking + +### 🎨 Portfolio Management +- **Image handling**: Multiple images per project, primary image logic +- **Categories**: Predefined categories (web-development, mobile-app, ui-ux-design, etc.) +- **SEO optimization**: Meta fields, structured data for portfolio items + +### 👑 Admin Panel Features +- **Dashboard**: Statistics, recent activity, quick actions +- **Content management**: CRUD for Portfolio, Services, Site Settings +- **Media gallery**: File upload with image optimization +- **Contact management**: Lead tracking, status updates, response handling + +## Common Tasks & Patterns + +### 🔧 Adding New Features +1. **Model**: Create in `/models/` with proper validation and indexes +2. **Routes**: Add to `/routes/` with appropriate auth middleware +3. **Views**: EJS templates in `/views/` using layout system +4. **API**: JSON endpoints with validation middleware +5. **Frontend**: Add to `public/js/` with proper event handling + +### 🐛 Debugging Workflow +- **Development**: Use `npm run dev` for auto-restart and detailed logging +- **Database**: Check MongoDB connection and sample data with `npm run init-db` +- **Authentication**: Test both JWT (API) and session (web) auth flows +- **PWA**: Check service worker registration and cache behavior in dev tools + +### 📦 Deployment Considerations +- **Environment**: Production requires secure cookies, CSP, and rate limiting +- **Database**: Ensure MongoDB indexes are created for performance +- **Assets**: Static files served by Express, consider CDN for production +- **Monitoring**: Error handling sends different messages based on NODE_ENV + +## File Structure Quick Reference +``` +├── models/ # MongoDB schemas with business logic +├── routes/ # Express routes (API + web pages) +├── middleware/ # Auth, validation, error handling +├── views/ # EJS templates with Korean content +├── public/ # Static assets + PWA files +├── scripts/ # Database init, dev server, build tools +└── server.js # Main Express application +``` + +## Korean Language Notes +- **UI Text**: All user-facing content in Korean +- **Error Messages**: Validation errors in Korean (`middleware/validation.js`) +- **Admin Interface**: Korean labels and messages throughout admin panel +- **SEO Content**: Korean meta descriptions and structured data \ No newline at end of file diff --git a/.history/REPORT_20251020225831.md b/.history/REPORT_20251020225831.md new file mode 100644 index 0000000..584e50e --- /dev/null +++ b/.history/REPORT_20251020225831.md @@ -0,0 +1,132 @@ +# 🎯 SmartSolTech - Отчет о Выполненных Исправлениях + +## 📋 Задачи которые были выполнены: + +### 1. 🏠 **Исправление главной страницы** +- ✅ **Проблема**: Стили на главной странице были поломаны +- ✅ **Решение**: + - Полностью переписан `base.css` с принудительными стилями (!important) + - Улучшен градиент hero-секции с многослойными эффектами + - Добавлены анимации и hover-эффекты для кнопок + - Исправлена навигация с backdrop-filter и webkit префиксами + - Оптимизирована отзывчивость для мобильных устройств + +### 2. 📐 **Компактные баннеры для внутренних страниц** +- ✅ **Проблема**: Hero-баннеры на всех страницах были полноэкранными +- ✅ **Решение**: + - Создан класс `.hero-section-compact` для внутренних страниц + - Уменьшена высота с 100vh до 40vh (50vh максимум) + - Обновлены страницы: "О нас", "Услуги" + - Сохранен полноэкранный баннер только на главной странице + +### 3. 🖼️ **Система редактирования изображений баннеров** + +#### **A. Backend API (/routes/media.js)** +- ✅ Загрузка одного изображения: `POST /media/upload` +- ✅ Загрузка нескольких изображений: `POST /media/upload-multiple` +- ✅ Удаление изображений: `DELETE /media/:filename` +- ✅ Список изображений: `GET /media/list` +- ✅ Автоматическая оптимизация изображений с Sharp +- ✅ Создание thumbnails (300x200, 800x600, 1200x900) +- ✅ Конвертация в WebP для лучшего сжатия +- ✅ Безопасность: аутентификация и валидация файлов + +#### **B. Frontend Редактор (/views/admin/banner-editor.ejs)** +- ✅ Интуитивный интерфейс с табами для разных страниц +- ✅ Drag & Drop загрузка изображений +- ✅ Превью изображений с возможностью удаления +- ✅ Индикатор прогресса загрузки +- ✅ Галерея загруженных изображений +- ✅ Мгновенная смена баннеров одним кликом +- ✅ Локальное сохранение настроек в localStorage + +#### **C. Интеграция с админкой** +- ✅ Новый маршрут: `/admin/banner-editor` +- ✅ Аутентификация через админ-сессии +- ✅ Интеграция с существующей админ панелью + +## 🔧 Технические улучшения: + +### **CSS архитектура:** +```css +base.css (16KB) - Принудительные стили и reset +main.css (11KB) - Компоненты и анимации + компактные баннеры +fixes.css (6KB) - Дополнительные исправления +``` + +### **Оптимизация изображений:** +- Автоматическое создание 4 размеров (thumbnail, medium, large, original) +- Конвертация в WebP (экономия до 50% размера) +- Максимальный размер файла: 10MB +- Поддержка: JPG, PNG, GIF, WebP + +### **Безопасность:** +- Валидация типов файлов +- Защита от path traversal атак +- Аутентификация для всех операций +- Автоматическая очистка при ошибках + +## 📊 Результаты тестирования: + +``` +🏠 ГЛАВНАЯ СТРАНИЦА: ✅ 200 OK (0.015s) +🎨 О НАС (компактный): ✅ 200 OK (0.007s) +🎨 УСЛУГИ (компактный): ✅ 200 OK (0.009s) +📱 ПОРТФОЛИО: ✅ 200 OK (0.012s) +🧮 КАЛЬКУЛЯТОР: ✅ 200 OK (0.008s) +📸 РЕДАКТОР БАННЕРОВ: ✅ 302 (требует авторизации) +🎨 CSS ФАЙЛЫ: ✅ Все загружаются корректно +📁 UPLOADS ПАПКА: ✅ Создана и готова +``` + +## 🚀 Как использовать редактор баннеров: + +### **Шаг 1: Доступ** +``` +URL: http://localhost:3000/admin/banner-editor +Требуется: Авторизация в админ панели +``` + +### **Шаг 2: Загрузка изображений** +1. Нажать "Загрузить Изображения" +2. Перетащить файлы или выбрать через кнопку +3. Посмотреть превью и нажать "Загрузить" +4. Система автоматически создаст оптимизированные версии + +### **Шаг 3: Установка баннера** +1. Выбрать страницу во вкладках (Главная, О нас, Услуги, Портфолио) +2. Нажать "Использовать" на нужном изображении +3. Баннер мгновенно обновится +4. Настройки сохраняются автоматически + +### **Шаг 4: Управление** +- Удаление: кнопка "Удалить" в галерее +- Обновление: кнопка "Обновить" +- Сброс к градиенту: кнопка "Удалить" на текущем баннере + +## 🎯 Итоговое состояние: + +### ✅ **Что работает:** +- Главная страница с исправленными стилями +- Компактные баннеры на внутренних страницах +- Полнофункциональный редактор изображений +- API для загрузки и управления медиа +- Автоматическая оптимизация изображений +- Безопасная система аутентификации + +### 🔄 **Готово к использованию:** +- Сервер: `http://localhost:3000` (PID: 24059) +- Админка: `http://localhost:3000/admin` +- Редактор: `http://localhost:3000/admin/banner-editor` +- Папка загрузок: `/public/uploads/` (готова к использованию) + +### 📈 **Преимущества реализации:** +1. **Производительность**: WebP формат + множественные размеры +2. **UX**: Drag & Drop + мгновенные превью +3. **Безопасность**: Полная валидация + аутентификация +4. **Масштабируемость**: Готова к добавлению новых страниц +5. **Мобильность**: Отзывчивый дизайн на всех устройствах + +--- + +**🎉 Все задачи выполнены успешно! Система готова к продуктивному использованию.** \ No newline at end of file diff --git a/.history/REPORT_20251020225845.md b/.history/REPORT_20251020225845.md new file mode 100644 index 0000000..584e50e --- /dev/null +++ b/.history/REPORT_20251020225845.md @@ -0,0 +1,132 @@ +# 🎯 SmartSolTech - Отчет о Выполненных Исправлениях + +## 📋 Задачи которые были выполнены: + +### 1. 🏠 **Исправление главной страницы** +- ✅ **Проблема**: Стили на главной странице были поломаны +- ✅ **Решение**: + - Полностью переписан `base.css` с принудительными стилями (!important) + - Улучшен градиент hero-секции с многослойными эффектами + - Добавлены анимации и hover-эффекты для кнопок + - Исправлена навигация с backdrop-filter и webkit префиксами + - Оптимизирована отзывчивость для мобильных устройств + +### 2. 📐 **Компактные баннеры для внутренних страниц** +- ✅ **Проблема**: Hero-баннеры на всех страницах были полноэкранными +- ✅ **Решение**: + - Создан класс `.hero-section-compact` для внутренних страниц + - Уменьшена высота с 100vh до 40vh (50vh максимум) + - Обновлены страницы: "О нас", "Услуги" + - Сохранен полноэкранный баннер только на главной странице + +### 3. 🖼️ **Система редактирования изображений баннеров** + +#### **A. Backend API (/routes/media.js)** +- ✅ Загрузка одного изображения: `POST /media/upload` +- ✅ Загрузка нескольких изображений: `POST /media/upload-multiple` +- ✅ Удаление изображений: `DELETE /media/:filename` +- ✅ Список изображений: `GET /media/list` +- ✅ Автоматическая оптимизация изображений с Sharp +- ✅ Создание thumbnails (300x200, 800x600, 1200x900) +- ✅ Конвертация в WebP для лучшего сжатия +- ✅ Безопасность: аутентификация и валидация файлов + +#### **B. Frontend Редактор (/views/admin/banner-editor.ejs)** +- ✅ Интуитивный интерфейс с табами для разных страниц +- ✅ Drag & Drop загрузка изображений +- ✅ Превью изображений с возможностью удаления +- ✅ Индикатор прогресса загрузки +- ✅ Галерея загруженных изображений +- ✅ Мгновенная смена баннеров одним кликом +- ✅ Локальное сохранение настроек в localStorage + +#### **C. Интеграция с админкой** +- ✅ Новый маршрут: `/admin/banner-editor` +- ✅ Аутентификация через админ-сессии +- ✅ Интеграция с существующей админ панелью + +## 🔧 Технические улучшения: + +### **CSS архитектура:** +```css +base.css (16KB) - Принудительные стили и reset +main.css (11KB) - Компоненты и анимации + компактные баннеры +fixes.css (6KB) - Дополнительные исправления +``` + +### **Оптимизация изображений:** +- Автоматическое создание 4 размеров (thumbnail, medium, large, original) +- Конвертация в WebP (экономия до 50% размера) +- Максимальный размер файла: 10MB +- Поддержка: JPG, PNG, GIF, WebP + +### **Безопасность:** +- Валидация типов файлов +- Защита от path traversal атак +- Аутентификация для всех операций +- Автоматическая очистка при ошибках + +## 📊 Результаты тестирования: + +``` +🏠 ГЛАВНАЯ СТРАНИЦА: ✅ 200 OK (0.015s) +🎨 О НАС (компактный): ✅ 200 OK (0.007s) +🎨 УСЛУГИ (компактный): ✅ 200 OK (0.009s) +📱 ПОРТФОЛИО: ✅ 200 OK (0.012s) +🧮 КАЛЬКУЛЯТОР: ✅ 200 OK (0.008s) +📸 РЕДАКТОР БАННЕРОВ: ✅ 302 (требует авторизации) +🎨 CSS ФАЙЛЫ: ✅ Все загружаются корректно +📁 UPLOADS ПАПКА: ✅ Создана и готова +``` + +## 🚀 Как использовать редактор баннеров: + +### **Шаг 1: Доступ** +``` +URL: http://localhost:3000/admin/banner-editor +Требуется: Авторизация в админ панели +``` + +### **Шаг 2: Загрузка изображений** +1. Нажать "Загрузить Изображения" +2. Перетащить файлы или выбрать через кнопку +3. Посмотреть превью и нажать "Загрузить" +4. Система автоматически создаст оптимизированные версии + +### **Шаг 3: Установка баннера** +1. Выбрать страницу во вкладках (Главная, О нас, Услуги, Портфолио) +2. Нажать "Использовать" на нужном изображении +3. Баннер мгновенно обновится +4. Настройки сохраняются автоматически + +### **Шаг 4: Управление** +- Удаление: кнопка "Удалить" в галерее +- Обновление: кнопка "Обновить" +- Сброс к градиенту: кнопка "Удалить" на текущем баннере + +## 🎯 Итоговое состояние: + +### ✅ **Что работает:** +- Главная страница с исправленными стилями +- Компактные баннеры на внутренних страницах +- Полнофункциональный редактор изображений +- API для загрузки и управления медиа +- Автоматическая оптимизация изображений +- Безопасная система аутентификации + +### 🔄 **Готово к использованию:** +- Сервер: `http://localhost:3000` (PID: 24059) +- Админка: `http://localhost:3000/admin` +- Редактор: `http://localhost:3000/admin/banner-editor` +- Папка загрузок: `/public/uploads/` (готова к использованию) + +### 📈 **Преимущества реализации:** +1. **Производительность**: WebP формат + множественные размеры +2. **UX**: Drag & Drop + мгновенные превью +3. **Безопасность**: Полная валидация + аутентификация +4. **Масштабируемость**: Готова к добавлению новых страниц +5. **Мобильность**: Отзывчивый дизайн на всех устройствах + +--- + +**🎉 Все задачи выполнены успешно! Система готова к продуктивному использованию.** \ No newline at end of file diff --git a/.history/config/database_20251019201726.js b/.history/config/database_20251019201726.js new file mode 100644 index 0000000..10b6a8f --- /dev/null +++ b/.history/config/database_20251019201726.js @@ -0,0 +1,30 @@ +const { Sequelize } = require('sequelize'); +require('dotenv').config(); + +const sequelize = new Sequelize(process.env.DATABASE_URL || { + host: process.env.DB_HOST || 'localhost', + port: process.env.DB_PORT || 5432, + database: process.env.DB_NAME || 'smartsoltech', + username: process.env.DB_USER || 'postgres', + password: process.env.DB_PASSWORD || 'password', + dialect: 'postgres', + logging: process.env.NODE_ENV === 'development' ? console.log : false, + pool: { + max: 5, + min: 0, + acquire: 30000, + idle: 10000 + } +}); + +// Test the connection +async function testConnection() { + try { + await sequelize.authenticate(); + console.log('✓ PostgreSQL connected successfully'); + } catch (error) { + console.error('✗ PostgreSQL connection error:', error); + } +} + +module.exports = { sequelize, testConnection }; \ No newline at end of file diff --git a/.history/config/database_20251019201735.js b/.history/config/database_20251019201735.js new file mode 100644 index 0000000..10b6a8f --- /dev/null +++ b/.history/config/database_20251019201735.js @@ -0,0 +1,30 @@ +const { Sequelize } = require('sequelize'); +require('dotenv').config(); + +const sequelize = new Sequelize(process.env.DATABASE_URL || { + host: process.env.DB_HOST || 'localhost', + port: process.env.DB_PORT || 5432, + database: process.env.DB_NAME || 'smartsoltech', + username: process.env.DB_USER || 'postgres', + password: process.env.DB_PASSWORD || 'password', + dialect: 'postgres', + logging: process.env.NODE_ENV === 'development' ? console.log : false, + pool: { + max: 5, + min: 0, + acquire: 30000, + idle: 10000 + } +}); + +// Test the connection +async function testConnection() { + try { + await sequelize.authenticate(); + console.log('✓ PostgreSQL connected successfully'); + } catch (error) { + console.error('✗ PostgreSQL connection error:', error); + } +} + +module.exports = { sequelize, testConnection }; \ No newline at end of file diff --git a/.history/locales/en_20251021183321.json b/.history/locales/en_20251021183321.json new file mode 100644 index 0000000..21143ff --- /dev/null +++ b/.history/locales/en_20251021183321.json @@ -0,0 +1,383 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "hero.title.smart", + "solutions": "hero.title.solutions" + }, + "subtitle": "Solutions", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "hero.cta.start", + "portfolio": "hero.cta.portfolio" + } + }, + "services": { + "title": { + "our": "services.title.our", + "services": "services.title.services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "services.subtitle", + "web": { + "title": "services.web.title", + "description": "services.web.description", + "price": "services.web.price" + }, + "mobile": { + "title": "services.mobile.title", + "description": "services.mobile.description", + "price": "services.mobile.price" + }, + "design": { + "title": "services.design.title", + "description": "services.design.description", + "price": "services.design.price" + }, + "marketing": { + "title": "services.marketing.title", + "description": "services.marketing.description", + "price": "services.marketing.price" + } + }, + "portfolio": { + "title": { + "recent": "portfolio.title.recent", + "projects": "portfolio.title.projects" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "portfolio.subtitle" + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "contact.form.title", + "service": { + "select": "contact.form.service.select", + "web": "contact.form.service.web", + "mobile": "contact.form.service.mobile", + "design": "contact.form.service.design", + "branding": "contact.form.service.branding", + "consulting": "contact.form.service.consulting", + "other": "contact.form.service.other" + }, + "success": "contact.form.success", + "error": "contact.form.error" + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "Become a Partner for Success Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021183333.json b/.history/locales/en_20251021183333.json new file mode 100644 index 0000000..21143ff --- /dev/null +++ b/.history/locales/en_20251021183333.json @@ -0,0 +1,383 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "hero.title.smart", + "solutions": "hero.title.solutions" + }, + "subtitle": "Solutions", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "hero.cta.start", + "portfolio": "hero.cta.portfolio" + } + }, + "services": { + "title": { + "our": "services.title.our", + "services": "services.title.services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "services.subtitle", + "web": { + "title": "services.web.title", + "description": "services.web.description", + "price": "services.web.price" + }, + "mobile": { + "title": "services.mobile.title", + "description": "services.mobile.description", + "price": "services.mobile.price" + }, + "design": { + "title": "services.design.title", + "description": "services.design.description", + "price": "services.design.price" + }, + "marketing": { + "title": "services.marketing.title", + "description": "services.marketing.description", + "price": "services.marketing.price" + } + }, + "portfolio": { + "title": { + "recent": "portfolio.title.recent", + "projects": "portfolio.title.projects" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "portfolio.subtitle" + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "contact.form.title", + "service": { + "select": "contact.form.service.select", + "web": "contact.form.service.web", + "mobile": "contact.form.service.mobile", + "design": "contact.form.service.design", + "branding": "contact.form.service.branding", + "consulting": "contact.form.service.consulting", + "other": "contact.form.service.other" + }, + "success": "contact.form.success", + "error": "contact.form.error" + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "Become a Partner for Success Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021184946.json b/.history/locales/en_20251021184946.json new file mode 100644 index 0000000..733cff5 --- /dev/null +++ b/.history/locales/en_20251021184946.json @@ -0,0 +1,383 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "services.title.our", + "services": "services.title.services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "services.subtitle", + "web": { + "title": "services.web.title", + "description": "services.web.description", + "price": "services.web.price" + }, + "mobile": { + "title": "services.mobile.title", + "description": "services.mobile.description", + "price": "services.mobile.price" + }, + "design": { + "title": "services.design.title", + "description": "services.design.description", + "price": "services.design.price" + }, + "marketing": { + "title": "services.marketing.title", + "description": "services.marketing.description", + "price": "services.marketing.price" + } + }, + "portfolio": { + "title": { + "recent": "portfolio.title.recent", + "projects": "portfolio.title.projects" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "portfolio.subtitle" + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "contact.form.title", + "service": { + "select": "contact.form.service.select", + "web": "contact.form.service.web", + "mobile": "contact.form.service.mobile", + "design": "contact.form.service.design", + "branding": "contact.form.service.branding", + "consulting": "contact.form.service.consulting", + "other": "contact.form.service.other" + }, + "success": "contact.form.success", + "error": "contact.form.error" + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "Become a Partner for Success Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021185005.json b/.history/locales/en_20251021185005.json new file mode 100644 index 0000000..64f6e28 --- /dev/null +++ b/.history/locales/en_20251021185005.json @@ -0,0 +1,410 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "Professional development services to turn your ideas into reality", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "portfolio.title.recent", + "projects": "portfolio.title.projects" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "portfolio.subtitle" + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "contact.form.title", + "service": { + "select": "contact.form.service.select", + "web": "contact.form.service.web", + "mobile": "contact.form.service.mobile", + "design": "contact.form.service.design", + "branding": "contact.form.service.branding", + "consulting": "contact.form.service.consulting", + "other": "contact.form.service.other" + }, + "success": "contact.form.success", + "error": "contact.form.error" + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "Become a Partner for Success Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021185016.json b/.history/locales/en_20251021185016.json new file mode 100644 index 0000000..98a74f6 --- /dev/null +++ b/.history/locales/en_20251021185016.json @@ -0,0 +1,431 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "Professional development services to turn your ideas into reality", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "Check out successfully completed projects", + "view_project": "View Project", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "contact.form.title", + "service": { + "select": "contact.form.service.select", + "web": "contact.form.service.web", + "mobile": "contact.form.service.mobile", + "design": "contact.form.service.design", + "branding": "contact.form.service.branding", + "consulting": "contact.form.service.consulting", + "other": "contact.form.service.other" + }, + "success": "contact.form.success", + "error": "contact.form.error" + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "Become a Partner for Success Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021185033.json b/.history/locales/en_20251021185033.json new file mode 100644 index 0000000..266ba3a --- /dev/null +++ b/.history/locales/en_20251021185033.json @@ -0,0 +1,435 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "Professional development services to turn your ideas into reality", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "Check out successfully completed projects", + "view_project": "View Project", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "Project Inquiry", + "service": { + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "Become a Partner for Success Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021185044.json b/.history/locales/en_20251021185044.json new file mode 100644 index 0000000..fc3bd8c --- /dev/null +++ b/.history/locales/en_20251021185044.json @@ -0,0 +1,441 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "Professional development services to turn your ideas into reality", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "Check out successfully completed projects", + "view_project": "View Project", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "Project Inquiry", + "service": { + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "Become a Partner for Success Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + } + }, + "footer": { + "company": { + "description": "Grow your business with innovative technology" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "Quick Links", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "sitemap": "Sitemap" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved.", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "social": { + "follow": "Follow Us" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021185051.json b/.history/locales/en_20251021185051.json new file mode 100644 index 0000000..3983732 --- /dev/null +++ b/.history/locales/en_20251021185051.json @@ -0,0 +1,441 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "Professional development services to turn your ideas into reality", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "Check out successfully completed projects", + "view_project": "View Project", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "Project Inquiry", + "service": { + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "Become a Partner for Success Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + } + }, + "footer": { + "company": { + "description": "Grow your business with innovative technology" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "Quick Links", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "sitemap": "Sitemap" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved.", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "social": { + "follow": "Follow Us" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021185058.json b/.history/locales/en_20251021185058.json new file mode 100644 index 0000000..653a3d9 --- /dev/null +++ b/.history/locales/en_20251021185058.json @@ -0,0 +1,441 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "Professional development services to turn your ideas into reality", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "Check out successfully completed projects", + "view_project": "View Project", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "Project Inquiry", + "service": { + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "Become a Partner for Success Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + } + }, + "footer": { + "company": { + "description": "Grow your business with innovative technology" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "Quick Links", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "sitemap": "Sitemap" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved.", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "social": { + "follow": "Follow Us" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021185105.json b/.history/locales/en_20251021185105.json new file mode 100644 index 0000000..7e51b81 --- /dev/null +++ b/.history/locales/en_20251021185105.json @@ -0,0 +1,441 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "Professional development services to turn your ideas into reality", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "Check out successfully completed projects", + "view_project": "View Project", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "Project Inquiry", + "service": { + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "Become a Partner for Success Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + } + }, + "footer": { + "company": { + "description": "Grow your business with innovative technology" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "Quick Links", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "sitemap": "Sitemap" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved.", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "social": { + "follow": "Follow Us" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021185114.json b/.history/locales/en_20251021185114.json new file mode 100644 index 0000000..cbf7b8c --- /dev/null +++ b/.history/locales/en_20251021185114.json @@ -0,0 +1,449 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "Professional development services to turn your ideas into reality", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "Check out successfully completed projects", + "view_project": "View Project", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "Project Inquiry", + "service": { + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "Become a Partner for Success Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + } + }, + "footer": { + "company": { + "description": "Grow your business with innovative technology" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "Quick Links", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "sitemap": "Sitemap" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved.", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "social": { + "follow": "Follow Us" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021185126.json b/.history/locales/en_20251021185126.json new file mode 100644 index 0000000..250e393 --- /dev/null +++ b/.history/locales/en_20251021185126.json @@ -0,0 +1,473 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "Professional development services to turn your ideas into reality", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "Check out successfully completed projects", + "view_project": "View Project", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "Project Inquiry", + "service": { + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "We'll Grow Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + }, + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + } + }, + "footer": { + "company": { + "description": "Grow your business with innovative technology" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "Quick Links", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "sitemap": "Sitemap" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved.", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "social": { + "follow": "Follow Us" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021190951.json b/.history/locales/en_20251021190951.json new file mode 100644 index 0000000..250e393 --- /dev/null +++ b/.history/locales/en_20251021190951.json @@ -0,0 +1,473 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "Professional development services to turn your ideas into reality", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "Check out successfully completed projects", + "view_project": "View Project", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "Project Inquiry", + "service": { + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "We'll Grow Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + }, + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + } + }, + "footer": { + "company": { + "description": "Grow your business with innovative technology" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "Quick Links", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "sitemap": "Sitemap" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved.", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "social": { + "follow": "Follow Us" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021205934.json b/.history/locales/en_20251021205934.json new file mode 100644 index 0000000..1b9134d --- /dev/null +++ b/.history/locales/en_20251021205934.json @@ -0,0 +1,477 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "Professional development services to turn your ideas into reality", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "Check out successfully completed projects", + "view_project": "View Project", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "hero": { + "title": "Contact Us", + "subtitle": "We're here to help bring your ideas to life" + }, + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "Project Inquiry", + "service": { + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "We'll Grow Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + }, + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + } + }, + "footer": { + "company": { + "description": "Grow your business with innovative technology" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "Quick Links", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "sitemap": "Sitemap" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved.", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "social": { + "follow": "Follow Us" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021205945.json b/.history/locales/en_20251021205945.json new file mode 100644 index 0000000..642345f --- /dev/null +++ b/.history/locales/en_20251021205945.json @@ -0,0 +1,478 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "Professional development services to turn your ideas into reality", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "Check out successfully completed projects", + "view_project": "View Project", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "project_details": "Project Details", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "hero": { + "title": "Contact Us", + "subtitle": "We're here to help bring your ideas to life" + }, + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "Project Inquiry", + "service": { + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "We'll Grow Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + }, + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + } + }, + "footer": { + "company": { + "description": "Grow your business with innovative technology" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "Quick Links", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "sitemap": "Sitemap" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved.", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "social": { + "follow": "Follow Us" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021210243.json b/.history/locales/en_20251021210243.json new file mode 100644 index 0000000..2505905 --- /dev/null +++ b/.history/locales/en_20251021210243.json @@ -0,0 +1,483 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "Professional development services to turn your ideas into reality", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "Check out successfully completed projects", + "view_project": "View Project", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "project_details": "Project Details", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "hero": { + "title": "Contact Us", + "subtitle": "We're here to help bring your ideas to life" + }, + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "Project Inquiry", + "service": { + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678", + "hours": "Mon-Fri 9:00 - 18:00" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr", + "response": "We respond within 24 hours" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + }, + "address": { + "line1": "Office Address", + "line2": "123 Teheran-ro, Gangnam-gu" + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "We'll Grow Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + }, + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + } + }, + "footer": { + "company": { + "description": "Grow your business with innovative technology" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "Quick Links", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "sitemap": "Sitemap" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved.", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "social": { + "follow": "Follow Us" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021210341.json b/.history/locales/en_20251021210341.json new file mode 100644 index 0000000..2505905 --- /dev/null +++ b/.history/locales/en_20251021210341.json @@ -0,0 +1,483 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "title_highlight": "Services", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "web_development": { + "title": "Web Development", + "description": "Modern and responsive websites and web applications development", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Mobile App", + "description": "Native and cross-platform apps for iOS and Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX Design", + "description": "User-centered intuitive and beautiful interface design", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Digital Marketing", + "description": "Digital marketing through SEO, social media, online advertising", + "price": "$2,000~" + }, + "view_all": "View All Services", + "subtitle": "Professional development services to turn your ideas into reality", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "subtitle": "Check out successfully completed projects", + "view_project": "View Project", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "project_details": "Project Details", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + }, + "step1": { + "title": "Step 1: Service Selection", + "subtitle": "Please select the services you need (multiple selection allowed)" + }, + "step2": { + "title": "Step 2: Project Details", + "subtitle": "Select project complexity and timeline" + }, + "complexity": { + "title": "Project Complexity", + "simple": "Simple", + "simple_desc": "Basic features, standard design", + "medium": "Medium", + "medium_desc": "Additional features, custom design", + "complex": "Complex", + "complex_desc": "Advanced features, complex integrations" + }, + "timeline": { + "title": "Development Timeline", + "standard": "Standard", + "standard_desc": "Normal development timeframe", + "rush": "Rush", + "rush_desc": "Fast development (+50%)", + "extended": "Extended", + "extended_desc": "Flexible development timeline (-20%)" + }, + "result": { + "title": "Estimate Results", + "subtitle": "Here's your preliminary project cost estimate", + "estimated_price": "Estimated Price", + "price_note": "* Final cost may vary based on project details", + "summary": "Project Summary", + "selected_services": "Selected Services", + "complexity": "Complexity", + "timeline": "Timeline", + "get_quote": "Get Accurate Quote", + "recalculate": "Recalculate", + "contact_note": "Contact us for an accurate quote and to discuss project details" + }, + "next_step": "Next Step", + "prev_step": "Previous", + "calculate": "Calculate" + }, + "contact": { + "hero": { + "title": "Contact Us", + "subtitle": "We're here to help bring your ideas to life" + }, + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "Name", + "email": "Email", + "phone": "Phone", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "Please briefly describe your project", + "submit": "Apply for Consultation", + "title": "Project Inquiry", + "service": { + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678", + "hours": "Mon-Fri 9:00 - 18:00" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr", + "response": "We respond within 24 hours" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + }, + "address": { + "line1": "Office Address", + "line2": "123 Teheran-ro, Gangnam-gu" + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "We'll Grow Together", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + }, + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + } + }, + "footer": { + "company": { + "description": "Grow your business with innovative technology" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "Quick Links", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "sitemap": "Sitemap" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved.", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "social": { + "follow": "Follow Us" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021210347.json b/.history/locales/en_20251021210347.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.history/locales/en_20251021210347.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.history/locales/en_20251021210512.json b/.history/locales/en_20251021210512.json new file mode 100644 index 0000000..52e83eb --- /dev/null +++ b/.history/locales/en_20251021210512.json @@ -0,0 +1,286 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "subtitle": "Professional development services to turn your ideas into reality", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "view_all": "View All Services", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects" + }, + "subtitle": "Check out successfully completed projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "project_details": "Project Details", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + } + }, + "contact": { + "hero": { + "title": "Contact Us", + "subtitle": "We're here to help bring your ideas to life" + }, + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "form": { + "title": "Project Inquiry", + "name": "Name", + "email": "Email", + "phone": "Phone", + "message": "Message", + "submit": "Send Inquiry", + "service": { + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + } + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + }, + "values": { + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "partnership": { + "title": "Partnership", + "description": "We create the best results through close communication and collaboration with customers." + } + }, + "cta": { + "title": "We'll Grow Together", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + } + }, + "footer": { + "description": "Digital solution specialist leading innovation", + "links": { + "title": "Quick Links" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved." + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "dashboard": "Dashboard", + "title": "SmartSolTech Admin" + }, + "company": { + "name": "SmartSolTech", + "description": "Digital solution specialist leading innovation", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678" + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021210629.json b/.history/locales/en_20251021210629.json new file mode 100644 index 0000000..52e83eb --- /dev/null +++ b/.history/locales/en_20251021210629.json @@ -0,0 +1,286 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "subtitle": "Professional development services to turn your ideas into reality", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "view_all": "View All Services", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects" + }, + "subtitle": "Check out successfully completed projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "project_details": "Project Details", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + } + }, + "contact": { + "hero": { + "title": "Contact Us", + "subtitle": "We're here to help bring your ideas to life" + }, + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "form": { + "title": "Project Inquiry", + "name": "Name", + "email": "Email", + "phone": "Phone", + "message": "Message", + "submit": "Send Inquiry", + "service": { + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + } + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + }, + "values": { + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "partnership": { + "title": "Partnership", + "description": "We create the best results through close communication and collaboration with customers." + } + }, + "cta": { + "title": "We'll Grow Together", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + } + }, + "footer": { + "description": "Digital solution specialist leading innovation", + "links": { + "title": "Quick Links" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved." + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "dashboard": "Dashboard", + "title": "SmartSolTech Admin" + }, + "company": { + "name": "SmartSolTech", + "description": "Digital solution specialist leading innovation", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678" + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021210647.json b/.history/locales/en_20251021210647.json new file mode 100644 index 0000000..97451a5 --- /dev/null +++ b/.history/locales/en_20251021210647.json @@ -0,0 +1,292 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "subtitle": "Professional development services to turn your ideas into reality", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "view_all": "View All Services", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects" + }, + "subtitle": "Check out successfully completed projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "project_details": "Project Details", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + } + }, + "contact": { + "hero": { + "title": "Contact Us", + "subtitle": "We're here to help bring your ideas to life" + }, + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "form": { + "title": "Project Inquiry", + "name": "Name", + "email": "Email", + "phone": "Phone", + "message": "Message", + "submit": "Send Inquiry", + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry", + "service": { + "title": "Service Interest", + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + } + }, + "info": { + "title": "Contact Information" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + }, + "values": { + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "partnership": { + "title": "Partnership", + "description": "We create the best results through close communication and collaboration with customers." + } + }, + "cta": { + "title": "We'll Grow Together", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + } + }, + "footer": { + "description": "Digital solution specialist leading innovation", + "links": { + "title": "Quick Links" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved." + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "dashboard": "Dashboard", + "title": "SmartSolTech Admin" + }, + "company": { + "name": "SmartSolTech", + "description": "Digital solution specialist leading innovation", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678" + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021210656.json b/.history/locales/en_20251021210656.json new file mode 100644 index 0000000..6ad919a --- /dev/null +++ b/.history/locales/en_20251021210656.json @@ -0,0 +1,299 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "subtitle": "Professional development services to turn your ideas into reality", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "view_all": "View All Services", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects" + }, + "subtitle": "Check out successfully completed projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "project_details": "Project Details", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + } + }, + "contact": { + "hero": { + "title": "Contact Us", + "subtitle": "We're here to help bring your ideas to life" + }, + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "form": { + "title": "Project Inquiry", + "name": "Name", + "email": "Email", + "phone": "Phone", + "message": "Message", + "submit": "Send Inquiry", + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry", + "service": { + "title": "Service Interest", + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + } + }, + "info": { + "title": "Contact Information" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678", + "hours": "Mon-Fri 9:00-18:00" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr", + "response": "Response within 24 hours" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "address": { + "title": "Office Address", + "line1": "123 Teheran-ro, Gangnam-gu", + "line2": "Seoul, South Korea" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + }, + "values": { + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "partnership": { + "title": "Partnership", + "description": "We create the best results through close communication and collaboration with customers." + } + }, + "cta": { + "title": "We'll Grow Together", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + } + }, + "footer": { + "description": "Digital solution specialist leading innovation", + "links": { + "title": "Quick Links" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved." + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "dashboard": "Dashboard", + "title": "SmartSolTech Admin" + }, + "company": { + "name": "SmartSolTech", + "description": "Digital solution specialist leading innovation", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678" + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021211404.json b/.history/locales/en_20251021211404.json new file mode 100644 index 0000000..64bfc8b --- /dev/null +++ b/.history/locales/en_20251021211404.json @@ -0,0 +1,331 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "subtitle": "Professional development services to turn your ideas into reality", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "view_all": "View All Services", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects" + }, + "subtitle": "Check out successfully completed projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "project_details": "Project Details", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + } + }, + "contact": { + "hero": { + "title": "Contact Us", + "subtitle": "We're here to help bring your ideas to life" + }, + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "form": { + "title": "Project Inquiry", + "name": "Name", + "email": "Email", + "phone": "Phone", + "message": "Message", + "submit": "Send Inquiry", + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry", + "service": { + "title": "Service Interest", + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + } + }, + "info": { + "title": "Contact Information" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678", + "hours": "Mon-Fri 9:00-18:00" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr", + "response": "Response within 24 hours" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "address": { + "title": "Office Address", + "line1": "123 Teheran-ro, Gangnam-gu", + "line2": "Seoul, South Korea" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + }, + "values": { + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "partnership": { + "title": "Partnership", + "description": "We create the best results through close communication and collaboration with customers." + } + }, + "cta": { + "title": "We'll Grow Together", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + } + }, + "footer": { + "description": "Digital solution specialist leading innovation", + "links": { + "title": "Quick Links" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved." + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "dashboard": "Dashboard", + "title": "SmartSolTech Admin" + }, + "company": { + "name": "SmartSolTech", + "description": "Digital solution specialist leading innovation", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678" + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + }, + "portfolio_page": { + "title": "Our Portfolio", + "subtitle": "Discover innovative projects and creative solutions", + "categories": { + "all": "All", + "web-development": "Web Development", + "mobile-app": "Mobile App", + "ui-ux-design": "UI/UX Design", + "branding": "Branding", + "marketing": "Digital Marketing" + }, + "buttons": { + "details": "View Details", + "projectDetails": "Project Details", + "loadMore": "Load More Projects", + "contact": "Request Project", + "calculate": "Calculate Cost" + }, + "empty": { + "title": "No portfolio yet", + "subtitle": "We'll be showcasing amazing projects soon!" + }, + "cta": { + "title": "Be the star of the next project", + "subtitle": "Create innovative digital solutions with us" + }, + "labels": { + "featured": "FEATURED", + "views": "views", + "likes": "likes" + } + } +} \ No newline at end of file diff --git a/.history/locales/en_20251021211513.json b/.history/locales/en_20251021211513.json new file mode 100644 index 0000000..64bfc8b --- /dev/null +++ b/.history/locales/en_20251021211513.json @@ -0,0 +1,331 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "subtitle": "Professional development services to turn your ideas into reality", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "view_all": "View All Services", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects" + }, + "subtitle": "Check out successfully completed projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "project_details": "Project Details", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + } + }, + "contact": { + "hero": { + "title": "Contact Us", + "subtitle": "We're here to help bring your ideas to life" + }, + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "form": { + "title": "Project Inquiry", + "name": "Name", + "email": "Email", + "phone": "Phone", + "message": "Message", + "submit": "Send Inquiry", + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry", + "service": { + "title": "Service Interest", + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + } + }, + "info": { + "title": "Contact Information" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678", + "hours": "Mon-Fri 9:00-18:00" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr", + "response": "Response within 24 hours" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "address": { + "title": "Office Address", + "line1": "123 Teheran-ro, Gangnam-gu", + "line2": "Seoul, South Korea" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + }, + "values": { + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "partnership": { + "title": "Partnership", + "description": "We create the best results through close communication and collaboration with customers." + } + }, + "cta": { + "title": "We'll Grow Together", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + } + }, + "footer": { + "description": "Digital solution specialist leading innovation", + "links": { + "title": "Quick Links" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved." + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "dashboard": "Dashboard", + "title": "SmartSolTech Admin" + }, + "company": { + "name": "SmartSolTech", + "description": "Digital solution specialist leading innovation", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678" + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + }, + "portfolio_page": { + "title": "Our Portfolio", + "subtitle": "Discover innovative projects and creative solutions", + "categories": { + "all": "All", + "web-development": "Web Development", + "mobile-app": "Mobile App", + "ui-ux-design": "UI/UX Design", + "branding": "Branding", + "marketing": "Digital Marketing" + }, + "buttons": { + "details": "View Details", + "projectDetails": "Project Details", + "loadMore": "Load More Projects", + "contact": "Request Project", + "calculate": "Calculate Cost" + }, + "empty": { + "title": "No portfolio yet", + "subtitle": "We'll be showcasing amazing projects soon!" + }, + "cta": { + "title": "Be the star of the next project", + "subtitle": "Create innovative digital solutions with us" + }, + "labels": { + "featured": "FEATURED", + "views": "views", + "likes": "likes" + } + } +} \ No newline at end of file diff --git a/.history/locales/ko_20251019203946.json b/.history/locales/ko_20251019203946.json new file mode 100644 index 0000000..dd925e3 --- /dev/null +++ b/.history/locales/ko_20251019203946.json @@ -0,0 +1,254 @@ +{ + "meta": { + "description": "스마트솔테크 - 혁신적인 웹 개발, 모바일 앱 개발, UI/UX 디자인 서비스", + "keywords": "웹 개발, 모바일 앱, UI/UX 디자인, 한국", + "title": "스마트솔테크" + }, + "navigation": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기", + "contact": "문의하기" + }, + "language": { + "ko": "한국어", + "korean": "한국어", + "english": "영어", + "russian": "러시아어", + "kazakh": "카자흐어" + }, + "theme": { + "toggle": "테마 전환" + }, + "hero": { + "title": { + "smart": "스마트", + "solutions": "솔루션" + }, + "subtitle": "혁신적인 기술로 당신의 비즈니스를 성장시키세요", + "cta": { + "start": "시작하기", + "portfolio": "포트폴리오 보기" + } + }, + "services": { + "title": { + "our": "우리의", + "services": "서비스" + }, + "subtitle": "전문적인 개발 서비스로 당신의 아이디어를 현실로 만들어드립니다", + "web": { + "title": "웹 개발", + "description": "반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500,000부터" + }, + "mobile": { + "title": "모바일 앱", + "description": "iOS와 Android 네이티브 앱 개발", + "price": "₩1,000,000부터" + }, + "design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 인터페이스 및 경험 디자인", + "price": "₩300,000부터" + }, + "marketing": { + "title": "디지털 마케팅", + "description": "SEO, SNS 마케팅, 광고 운영", + "price": "₩200,000부터" + }, + "view_all": "모든 서비스 보기" + }, + "portfolio": { + "title": { + "recent": "최근", + "projects": "프로젝트", + "our": "우리의", + "portfolio": "포트폴리오" + }, + "subtitle": "성공적으로 완료한 프로젝트들을 확인해보세요", + "view_all": "모든 프로젝트 보기", + "view_project": "프로젝트 보기", + "categories": { + "all": "전체", + "web": "웹 개발", + "mobile": "모바일 앱", + "uiux": "UI/UX 디자인" + } + }, + "common": { + "view_details": "자세히 보기" + }, + "about": { + "meta": { + "title": "회사소개", + "description": "스마트솔테크는 혁신적인 기술로 고객의 비즈니스 성장을 지원하는 전문 개발 회사입니다" + }, + "hero": { + "title": "스마트솔테크 소개", + "subtitle": "혁신과 기술로 미래를 만들어갑니다" + }, + "company": { + "title": "회사 정보", + "description1": "스마트솔테크는 2020년 설립된 기술 전문 회사로, 웹 개발, 모바일 앱 개발, UI/UX 디자인 분야에서 전문성을 인정받고 있습니다.", + "description2": "우리는 고객의 니즈를 정확히 파악하고, 최신 기술을 활용하여 혁신적인 솔루션을 제공합니다." + }, + "stats": { + "projects": "완료된 프로젝트", + "experience": "년간 경험", + "clients": "만족한 고객" + }, + "mission": { + "title": "우리의 미션", + "description": "기술을 통해 고객의 비즈니스 성장을 지원하고, 디지털 혁신을 이끌어나가는 것이 우리의 사명입니다." + }, + "values": { + "innovation": { + "title": "혁신", + "description": "최신 기술과 트렌드를 빠르게 적용하여 혁신적인 솔루션을 제공합니다." + }, + "quality": { + "title": "품질", + "description": "높은 품질의 코드와 디자인으로 안정적이고 우수한 제품을 개발합니다." + }, + "partnership": { + "title": "파트너십", + "description": "고객과의 긴밀한 협력을 통해 최상의 결과를 만들어냅니다." + } + }, + "cta": { + "title": "함께 성장하겠습니다", + "subtitle": "당신의 아이디어를 현실로 만들어보세요" + } + }, + "contact": { + "meta": { + "title": "문의하기", + "description": "프로젝트 문의나 상담이 필요하시면 언제든 연락주세요" + }, + "cta": { + "ready": "준비되셨나요?", + "start": "시작해보세요", + "question": "질문이 있으신가요?", + "subtitle": "프로젝트에 대해 상담해드립니다" + }, + "phone": { + "title": "전화 문의", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "이메일 문의", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "텔레그램", + "subtitle": "빠른 응답을 원하신다면" + }, + "form": { + "title": "프로젝트 문의", + "name": "이름", + "email": "이메일", + "phone": "전화번호", + "service": { + "select": "관심 서비스를 선택하세요", + "web": "웹 개발", + "mobile": "모바일 앱", + "design": "UI/UX 디자인", + "branding": "브랜딩", + "consulting": "컨설팅", + "other": "기타" + }, + "message": "메시지", + "submit": "문의 보내기", + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다" + } + }, + "calculator": { + "meta": { + "title": "견적 계산기", + "description": "프로젝트 예상 비용을 간편하게 계산해보세요" + }, + "title": "견적 계산기", + "subtitle": "프로젝트 예상 비용을 확인해보세요", + "cta": { + "title": "정확한 견적이 필요하신가요?", + "subtitle": "간단한 정보로 예상 비용을 확인하세요", + "button": "견적 계산하기" + }, + "step1": { + "title": "1단계: 서비스 선택", + "subtitle": "필요한 서비스를 선택해주세요" + }, + "step2": { + "title": "2단계: 옵션 선택", + "subtitle": "프로젝트 세부사항을 선택해주세요" + }, + "complexity": { + "title": "프로젝트 복잡도", + "simple": "단순", + "simple_desc": "기본적인 기능", + "medium": "보통", + "medium_desc": "표준 기능", + "complex": "복잡", + "complex_desc": "고급 기능" + }, + "timeline": { + "title": "개발 기간", + "standard": "표준", + "standard_desc": "일반적인 개발 기간", + "rush": "급행", + "rush_desc": "빠른 개발 (추가 비용)", + "extended": "여유", + "extended_desc": "충분한 개발 기간 (할인)" + }, + "next_step": "다음 단계", + "prev_step": "이전 단계", + "calculate": "견적 계산하기", + "result": { + "title": "견적 결과", + "subtitle": "프로젝트 예상 비용입니다", + "estimated_price": "예상 비용", + "price_note": "* 정확한 견적은 상담 후 확정됩니다", + "summary": "견적 요약", + "get_quote": "정확한 견적 요청", + "recalculate": "다시 계산하기", + "contact_note": "더 정확한 견적을 위해 상담을 받아보세요", + "selected_services": "선택된 서비스", + "complexity": "복잡도", + "timeline": "개발 기간" + } + }, + "footer": { + "company": { + "description": "혁신적인 기술로 당신의 비즈니스를 성장시키세요" + }, + "links": { + "title": "빠른 링크", + "privacy": "개인정보처리방침", + "terms": "이용약관", + "sitemap": "사이트맵" + }, + "contact": { + "title": "연락처", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "서울특별시 강남구 테헤란로 123" + }, + "social": { + "follow": "팔로우하기" + }, + "copyright": "© 2024 스마트솔테크. 모든 권리 보유.", + "privacy": "개인정보보호", + "terms": "이용약관" + }, + "nav": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기" + } +} \ No newline at end of file diff --git a/.history/locales/ko_20251019204000.json b/.history/locales/ko_20251019204000.json new file mode 100644 index 0000000..dd925e3 --- /dev/null +++ b/.history/locales/ko_20251019204000.json @@ -0,0 +1,254 @@ +{ + "meta": { + "description": "스마트솔테크 - 혁신적인 웹 개발, 모바일 앱 개발, UI/UX 디자인 서비스", + "keywords": "웹 개발, 모바일 앱, UI/UX 디자인, 한국", + "title": "스마트솔테크" + }, + "navigation": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기", + "contact": "문의하기" + }, + "language": { + "ko": "한국어", + "korean": "한국어", + "english": "영어", + "russian": "러시아어", + "kazakh": "카자흐어" + }, + "theme": { + "toggle": "테마 전환" + }, + "hero": { + "title": { + "smart": "스마트", + "solutions": "솔루션" + }, + "subtitle": "혁신적인 기술로 당신의 비즈니스를 성장시키세요", + "cta": { + "start": "시작하기", + "portfolio": "포트폴리오 보기" + } + }, + "services": { + "title": { + "our": "우리의", + "services": "서비스" + }, + "subtitle": "전문적인 개발 서비스로 당신의 아이디어를 현실로 만들어드립니다", + "web": { + "title": "웹 개발", + "description": "반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500,000부터" + }, + "mobile": { + "title": "모바일 앱", + "description": "iOS와 Android 네이티브 앱 개발", + "price": "₩1,000,000부터" + }, + "design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 인터페이스 및 경험 디자인", + "price": "₩300,000부터" + }, + "marketing": { + "title": "디지털 마케팅", + "description": "SEO, SNS 마케팅, 광고 운영", + "price": "₩200,000부터" + }, + "view_all": "모든 서비스 보기" + }, + "portfolio": { + "title": { + "recent": "최근", + "projects": "프로젝트", + "our": "우리의", + "portfolio": "포트폴리오" + }, + "subtitle": "성공적으로 완료한 프로젝트들을 확인해보세요", + "view_all": "모든 프로젝트 보기", + "view_project": "프로젝트 보기", + "categories": { + "all": "전체", + "web": "웹 개발", + "mobile": "모바일 앱", + "uiux": "UI/UX 디자인" + } + }, + "common": { + "view_details": "자세히 보기" + }, + "about": { + "meta": { + "title": "회사소개", + "description": "스마트솔테크는 혁신적인 기술로 고객의 비즈니스 성장을 지원하는 전문 개발 회사입니다" + }, + "hero": { + "title": "스마트솔테크 소개", + "subtitle": "혁신과 기술로 미래를 만들어갑니다" + }, + "company": { + "title": "회사 정보", + "description1": "스마트솔테크는 2020년 설립된 기술 전문 회사로, 웹 개발, 모바일 앱 개발, UI/UX 디자인 분야에서 전문성을 인정받고 있습니다.", + "description2": "우리는 고객의 니즈를 정확히 파악하고, 최신 기술을 활용하여 혁신적인 솔루션을 제공합니다." + }, + "stats": { + "projects": "완료된 프로젝트", + "experience": "년간 경험", + "clients": "만족한 고객" + }, + "mission": { + "title": "우리의 미션", + "description": "기술을 통해 고객의 비즈니스 성장을 지원하고, 디지털 혁신을 이끌어나가는 것이 우리의 사명입니다." + }, + "values": { + "innovation": { + "title": "혁신", + "description": "최신 기술과 트렌드를 빠르게 적용하여 혁신적인 솔루션을 제공합니다." + }, + "quality": { + "title": "품질", + "description": "높은 품질의 코드와 디자인으로 안정적이고 우수한 제품을 개발합니다." + }, + "partnership": { + "title": "파트너십", + "description": "고객과의 긴밀한 협력을 통해 최상의 결과를 만들어냅니다." + } + }, + "cta": { + "title": "함께 성장하겠습니다", + "subtitle": "당신의 아이디어를 현실로 만들어보세요" + } + }, + "contact": { + "meta": { + "title": "문의하기", + "description": "프로젝트 문의나 상담이 필요하시면 언제든 연락주세요" + }, + "cta": { + "ready": "준비되셨나요?", + "start": "시작해보세요", + "question": "질문이 있으신가요?", + "subtitle": "프로젝트에 대해 상담해드립니다" + }, + "phone": { + "title": "전화 문의", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "이메일 문의", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "텔레그램", + "subtitle": "빠른 응답을 원하신다면" + }, + "form": { + "title": "프로젝트 문의", + "name": "이름", + "email": "이메일", + "phone": "전화번호", + "service": { + "select": "관심 서비스를 선택하세요", + "web": "웹 개발", + "mobile": "모바일 앱", + "design": "UI/UX 디자인", + "branding": "브랜딩", + "consulting": "컨설팅", + "other": "기타" + }, + "message": "메시지", + "submit": "문의 보내기", + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다" + } + }, + "calculator": { + "meta": { + "title": "견적 계산기", + "description": "프로젝트 예상 비용을 간편하게 계산해보세요" + }, + "title": "견적 계산기", + "subtitle": "프로젝트 예상 비용을 확인해보세요", + "cta": { + "title": "정확한 견적이 필요하신가요?", + "subtitle": "간단한 정보로 예상 비용을 확인하세요", + "button": "견적 계산하기" + }, + "step1": { + "title": "1단계: 서비스 선택", + "subtitle": "필요한 서비스를 선택해주세요" + }, + "step2": { + "title": "2단계: 옵션 선택", + "subtitle": "프로젝트 세부사항을 선택해주세요" + }, + "complexity": { + "title": "프로젝트 복잡도", + "simple": "단순", + "simple_desc": "기본적인 기능", + "medium": "보통", + "medium_desc": "표준 기능", + "complex": "복잡", + "complex_desc": "고급 기능" + }, + "timeline": { + "title": "개발 기간", + "standard": "표준", + "standard_desc": "일반적인 개발 기간", + "rush": "급행", + "rush_desc": "빠른 개발 (추가 비용)", + "extended": "여유", + "extended_desc": "충분한 개발 기간 (할인)" + }, + "next_step": "다음 단계", + "prev_step": "이전 단계", + "calculate": "견적 계산하기", + "result": { + "title": "견적 결과", + "subtitle": "프로젝트 예상 비용입니다", + "estimated_price": "예상 비용", + "price_note": "* 정확한 견적은 상담 후 확정됩니다", + "summary": "견적 요약", + "get_quote": "정확한 견적 요청", + "recalculate": "다시 계산하기", + "contact_note": "더 정확한 견적을 위해 상담을 받아보세요", + "selected_services": "선택된 서비스", + "complexity": "복잡도", + "timeline": "개발 기간" + } + }, + "footer": { + "company": { + "description": "혁신적인 기술로 당신의 비즈니스를 성장시키세요" + }, + "links": { + "title": "빠른 링크", + "privacy": "개인정보처리방침", + "terms": "이용약관", + "sitemap": "사이트맵" + }, + "contact": { + "title": "연락처", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "서울특별시 강남구 테헤란로 123" + }, + "social": { + "follow": "팔로우하기" + }, + "copyright": "© 2024 스마트솔테크. 모든 권리 보유.", + "privacy": "개인정보보호", + "terms": "이용약관" + }, + "nav": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기" + } +} \ No newline at end of file diff --git a/.history/locales/ko_20251021185144.json b/.history/locales/ko_20251021185144.json new file mode 100644 index 0000000..73fd3b7 --- /dev/null +++ b/.history/locales/ko_20251021185144.json @@ -0,0 +1,429 @@ +{ + "navigation": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "contact": "문의하기", + "calculator": "견적 계산기", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "스마트", + "solutions": "솔루션" + }, + "subtitle": "혁신적인 기술로 당신의 비즈니스를 성장시키세요", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta_primary": "Start Project", + "cta_secondary": "View Portfolio", + "cta": { + "start": "시작하기", + "portfolio": "포트폴리오 보기" + } + }, + "services": { + "title": { + "our": "우리의", + "services": "서비스" + }, + "title_highlight": "서비스", + "description": "최첨단 기술과 창의적 아이디어로 완성된 디지털 솔루션", + "web_development": { + "title": "웹 개발", + "description": "현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500만원~" + }, + "mobile_app": { + "title": "모바일 앱", + "description": "iOS와 Android용 네이티브 및 크로스 플랫폼 앱", + "price": "₩800만원~" + }, + "ui_ux_design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 직관적이고 아름다운 인터페이스 디자인", + "price": "₩300만원~" + }, + "digital_marketing": { + "title": "디지털 마케팅", + "description": "SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅", + "price": "₩200만원~" + }, + "view_all": "모든 서비스 보기", + "subtitle": "전문적인 개발 서비스로 당신의 아이디어를 현실로 만들어드립니다", + "web": { + "title": "웹 개발", + "description": "반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500,000부터" + }, + "mobile": { + "title": "모바일 앱", + "description": "iOS와 Android 네이티브 앱 개발", + "price": "₩1,000,000부터" + }, + "design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 인터페이스 및 경험 디자인", + "price": "₩300,000부터" + }, + "marketing": { + "title": "디지털 마케팅", + "description": "SEO, SNS 마케팅, 광고 운영", + "price": "₩200,000부터" + } + }, + "portfolio": { + "title": { + "recent": "최근", + "projects": "프로젝트", + "our": "우리의", + "portfolio": "포트폴리오" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "모든 프로젝트 보기", + "subtitle": "성공적으로 완료한 프로젝트들을 확인해보세요", + "view_project": "프로젝트 보기", + "categories": { + "all": "전체", + "web": "웹 개발", + "mobile": "모바일 앱", + "uiux": "UI/UX 디자인" + } + }, + "calculator": { + "title": "견적 계산기", + "subtitle": "프로젝트 예상 비용을 확인해보세요", + "meta": { + "title": "견적 계산기", + "description": "프로젝트 예상 비용을 간편하게 계산해보세요" + }, + "cta": { + "title": "정확한 견적이 필요하신가요?", + "subtitle": "간단한 정보로 예상 비용을 확인하세요", + "button": "견적 계산하기" + }, + "step1": { + "title": "1단계: 서비스 선택", + "subtitle": "필요한 서비스를 선택해주세요" + }, + "step2": { + "title": "2단계: 옵션 선택", + "subtitle": "프로젝트 세부사항을 선택해주세요" + }, + "complexity": { + "title": "프로젝트 복잡도", + "simple": "단순", + "simple_desc": "기본적인 기능", + "medium": "보통", + "medium_desc": "표준 기능", + "complex": "복잡", + "complex_desc": "고급 기능" + }, + "timeline": { + "title": "개발 기간", + "standard": "표준", + "standard_desc": "일반적인 개발 기간", + "rush": "급행", + "rush_desc": "빠른 개발 (추가 비용)", + "extended": "여유", + "extended_desc": "충분한 개발 기간 (할인)" + }, + "result": { + "title": "견적 결과", + "subtitle": "프로젝트 예상 비용입니다", + "estimated_price": "예상 비용", + "price_note": "* 정확한 견적은 상담 후 확정됩니다", + "summary": "견적 요약", + "selected_services": "선택된 서비스", + "complexity": "복잡도", + "timeline": "개발 기간", + "get_quote": "정확한 견적 요청", + "recalculate": "다시 계산하기", + "contact_note": "더 정확한 견적을 위해 상담을 받아보세요" + }, + "next_step": "다음 단계", + "prev_step": "이전 단계", + "calculate": "견적 계산하기" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "이름", + "email": "이메일", + "phone": "전화번호", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "메시지", + "submit": "문의 보내기", + "title": "프로젝트 문의", + "service": { + "select": "관심 서비스를 선택하세요", + "web": "웹 개발", + "mobile": "모바일 앱", + "design": "UI/UX 디자인", + "branding": "브랜딩", + "consulting": "컨설팅", + "other": "기타" + }, + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다" + }, + "cta": { + "ready": "준비되셨나요?", + "start": "시작해보세요", + "question": "질문이 있으신가요?", + "subtitle": "프로젝트에 대해 상담해드립니다" + }, + "phone": { + "title": "전화 문의", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "이메일 문의", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "텔레그램", + "subtitle": "빠른 응답을 원하신다면" + }, + "meta": { + "title": "문의하기", + "description": "프로젝트 문의나 상담이 필요하시면 언제든 연락주세요" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "혁신", + "description": "최신 기술과 트렌드를 빠르게 적용하여 혁신적인 솔루션을 제공합니다." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "품질", + "description": "높은 품질의 코드와 디자인으로 안정적이고 우수한 제품을 개발합니다." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + }, + "partnership": { + "title": "파트너십", + "description": "고객과의 긴밀한 협력을 통해 최상의 결과를 만들어냅니다." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "함께 성장하겠습니다", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio", + "subtitle": "당신의 아이디어를 현실로 만들어보세요" + }, + "meta": { + "title": "회사소개", + "description": "스마트솔테크는 혁신적인 기술로 고객의 비즈니스 성장을 지원하는 전문 개발 회사입니다" + }, + "hero": { + "title": "스마트솔테크 소개", + "subtitle": "혁신과 기술로 미래를 만들어갑니다" + }, + "company": { + "title": "회사 정보", + "description1": "스마트솔테크는 2020년 설립된 기술 전문 회사로, 웹 개발, 모바일 앱 개발, UI/UX 디자인 분야에서 전문성을 인정받고 있습니다.", + "description2": "우리는 고객의 니즈를 정확히 파악하고, 최신 기술을 활용하여 혁신적인 솔루션을 제공합니다." + }, + "stats": { + "projects": "완료된 프로젝트", + "experience": "년간 경험", + "clients": "만족한 고객" + }, + "mission": { + "title": "우리의 미션", + "description": "기술을 통해 고객의 비즈니스 성장을 지원하고, 디지털 혁신을 이끌어나가는 것이 우리의 사명입니다." + } + }, + "footer": { + "company": { + "description": "혁신적인 기술로 당신의 비즈니스를 성장시키세요" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "빠른 링크", + "privacy": "개인정보처리방침", + "terms": "이용약관", + "sitemap": "사이트맵" + }, + "contact": { + "title": "연락처", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "서울특별시 강남구 테헤란로 123" + }, + "copyright": "© 2024 스마트솔테크. 모든 권리 보유.", + "privacy": "개인정보보호", + "terms": "이용약관", + "social": { + "follow": "팔로우하기" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "테마 전환" + }, + "language": { + "english": "영어", + "korean": "한국어", + "russian": "러시아어", + "kazakh": "카자흐어", + "ko": "한국어" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "자세히 보기" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "스마트솔테크 - 혁신적인 웹 개발, 모바일 앱 개발, UI/UX 디자인 서비스", + "keywords": "웹 개발, 모바일 앱, UI/UX 디자인, 한국", + "title": "스마트솔테크" + }, + "nav": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/ko_20251021185152.json b/.history/locales/ko_20251021185152.json new file mode 100644 index 0000000..3878de2 --- /dev/null +++ b/.history/locales/ko_20251021185152.json @@ -0,0 +1,429 @@ +{ + "navigation": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "contact": "문의하기", + "calculator": "견적 계산기", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "스마트", + "solutions": "솔루션" + }, + "subtitle": "혁신적인 기술로 당신의 비즈니스를 성장시키세요", + "description": "혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비�니스 디지털 변혁을 이끕니다", + "cta_primary": "프로젝트 시작", + "cta_secondary": "포트폴리오 보기", + "cta": { + "start": "시작하기", + "portfolio": "포트폴리오 보기" + } + }, + "services": { + "title": { + "our": "우리의", + "services": "서비스" + }, + "title_highlight": "서비스", + "description": "최첨단 기술과 창의적 아이디어로 완성된 디지털 솔루션", + "web_development": { + "title": "웹 개발", + "description": "현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500만원~" + }, + "mobile_app": { + "title": "모바일 앱", + "description": "iOS와 Android용 네이티브 및 크로스 플랫폼 앱", + "price": "₩800만원~" + }, + "ui_ux_design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 직관적이고 아름다운 인터페이스 디자인", + "price": "₩300만원~" + }, + "digital_marketing": { + "title": "디지털 마케팅", + "description": "SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅", + "price": "₩200만원~" + }, + "view_all": "모든 서비스 보기", + "subtitle": "전문적인 개발 서비스로 당신의 아이디어를 현실로 만들어드립니다", + "web": { + "title": "웹 개발", + "description": "반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500,000부터" + }, + "mobile": { + "title": "모바일 앱", + "description": "iOS와 Android 네이티브 앱 개발", + "price": "₩1,000,000부터" + }, + "design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 인터페이스 및 경험 디자인", + "price": "₩300,000부터" + }, + "marketing": { + "title": "디지털 마케팅", + "description": "SEO, SNS 마케팅, 광고 운영", + "price": "₩200,000부터" + } + }, + "portfolio": { + "title": { + "recent": "최근", + "projects": "프로젝트", + "our": "우리의", + "portfolio": "포트폴리오" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "모든 프로젝트 보기", + "subtitle": "성공적으로 완료한 프로젝트들을 확인해보세요", + "view_project": "프로젝트 보기", + "categories": { + "all": "전체", + "web": "웹 개발", + "mobile": "모바일 앱", + "uiux": "UI/UX 디자인" + } + }, + "calculator": { + "title": "견적 계산기", + "subtitle": "프로젝트 예상 비용을 확인해보세요", + "meta": { + "title": "견적 계산기", + "description": "프로젝트 예상 비용을 간편하게 계산해보세요" + }, + "cta": { + "title": "정확한 견적이 필요하신가요?", + "subtitle": "간단한 정보로 예상 비용을 확인하세요", + "button": "견적 계산하기" + }, + "step1": { + "title": "1단계: 서비스 선택", + "subtitle": "필요한 서비스를 선택해주세요" + }, + "step2": { + "title": "2단계: 옵션 선택", + "subtitle": "프로젝트 세부사항을 선택해주세요" + }, + "complexity": { + "title": "프로젝트 복잡도", + "simple": "단순", + "simple_desc": "기본적인 기능", + "medium": "보통", + "medium_desc": "표준 기능", + "complex": "복잡", + "complex_desc": "고급 기능" + }, + "timeline": { + "title": "개발 기간", + "standard": "표준", + "standard_desc": "일반적인 개발 기간", + "rush": "급행", + "rush_desc": "빠른 개발 (추가 비용)", + "extended": "여유", + "extended_desc": "충분한 개발 기간 (할인)" + }, + "result": { + "title": "견적 결과", + "subtitle": "프로젝트 예상 비용입니다", + "estimated_price": "예상 비용", + "price_note": "* 정확한 견적은 상담 후 확정됩니다", + "summary": "견적 요약", + "selected_services": "선택된 서비스", + "complexity": "복잡도", + "timeline": "개발 기간", + "get_quote": "정확한 견적 요청", + "recalculate": "다시 계산하기", + "contact_note": "더 정확한 견적을 위해 상담을 받아보세요" + }, + "next_step": "다음 단계", + "prev_step": "이전 단계", + "calculate": "견적 계산하기" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "이름", + "email": "이메일", + "phone": "전화번호", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "메시지", + "submit": "문의 보내기", + "title": "프로젝트 문의", + "service": { + "select": "관심 서비스를 선택하세요", + "web": "웹 개발", + "mobile": "모바일 앱", + "design": "UI/UX 디자인", + "branding": "브랜딩", + "consulting": "컨설팅", + "other": "기타" + }, + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다" + }, + "cta": { + "ready": "준비되셨나요?", + "start": "시작해보세요", + "question": "질문이 있으신가요?", + "subtitle": "프로젝트에 대해 상담해드립니다" + }, + "phone": { + "title": "전화 문의", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "이메일 문의", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "텔레그램", + "subtitle": "빠른 응답을 원하신다면" + }, + "meta": { + "title": "문의하기", + "description": "프로젝트 문의나 상담이 필요하시면 언제든 연락주세요" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "혁신", + "description": "최신 기술과 트렌드를 빠르게 적용하여 혁신적인 솔루션을 제공합니다." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "품질", + "description": "높은 품질의 코드와 디자인으로 안정적이고 우수한 제품을 개발합니다." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + }, + "partnership": { + "title": "파트너십", + "description": "고객과의 긴밀한 협력을 통해 최상의 결과를 만들어냅니다." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "함께 성장하겠습니다", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio", + "subtitle": "당신의 아이디어를 현실로 만들어보세요" + }, + "meta": { + "title": "회사소개", + "description": "스마트솔테크는 혁신적인 기술로 고객의 비즈니스 성장을 지원하는 전문 개발 회사입니다" + }, + "hero": { + "title": "스마트솔테크 소개", + "subtitle": "혁신과 기술로 미래를 만들어갑니다" + }, + "company": { + "title": "회사 정보", + "description1": "스마트솔테크는 2020년 설립된 기술 전문 회사로, 웹 개발, 모바일 앱 개발, UI/UX 디자인 분야에서 전문성을 인정받고 있습니다.", + "description2": "우리는 고객의 니즈를 정확히 파악하고, 최신 기술을 활용하여 혁신적인 솔루션을 제공합니다." + }, + "stats": { + "projects": "완료된 프로젝트", + "experience": "년간 경험", + "clients": "만족한 고객" + }, + "mission": { + "title": "우리의 미션", + "description": "기술을 통해 고객의 비즈니스 성장을 지원하고, 디지털 혁신을 이끌어나가는 것이 우리의 사명입니다." + } + }, + "footer": { + "company": { + "description": "혁신적인 기술로 당신의 비즈니스를 성장시키세요" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "빠른 링크", + "privacy": "개인정보처리방침", + "terms": "이용약관", + "sitemap": "사이트맵" + }, + "contact": { + "title": "연락처", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "서울특별시 강남구 테헤란로 123" + }, + "copyright": "© 2024 스마트솔테크. 모든 권리 보유.", + "privacy": "개인정보보호", + "terms": "이용약관", + "social": { + "follow": "팔로우하기" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "테마 전환" + }, + "language": { + "english": "영어", + "korean": "한국어", + "russian": "러시아어", + "kazakh": "카자흐어", + "ko": "한국어" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "자세히 보기" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "스마트솔테크 - 혁신적인 웹 개발, 모바일 앱 개발, UI/UX 디자인 서비스", + "keywords": "웹 개발, 모바일 앱, UI/UX 디자인, 한국", + "title": "스마트솔테크" + }, + "nav": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/ko_20251021190951.json b/.history/locales/ko_20251021190951.json new file mode 100644 index 0000000..3878de2 --- /dev/null +++ b/.history/locales/ko_20251021190951.json @@ -0,0 +1,429 @@ +{ + "navigation": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "contact": "문의하기", + "calculator": "견적 계산기", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "스마트", + "solutions": "솔루션" + }, + "subtitle": "혁신적인 기술로 당신의 비즈니스를 성장시키세요", + "description": "혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비�니스 디지털 변혁을 이끕니다", + "cta_primary": "프로젝트 시작", + "cta_secondary": "포트폴리오 보기", + "cta": { + "start": "시작하기", + "portfolio": "포트폴리오 보기" + } + }, + "services": { + "title": { + "our": "우리의", + "services": "서비스" + }, + "title_highlight": "서비스", + "description": "최첨단 기술과 창의적 아이디어로 완성된 디지털 솔루션", + "web_development": { + "title": "웹 개발", + "description": "현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500만원~" + }, + "mobile_app": { + "title": "모바일 앱", + "description": "iOS와 Android용 네이티브 및 크로스 플랫폼 앱", + "price": "₩800만원~" + }, + "ui_ux_design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 직관적이고 아름다운 인터페이스 디자인", + "price": "₩300만원~" + }, + "digital_marketing": { + "title": "디지털 마케팅", + "description": "SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅", + "price": "₩200만원~" + }, + "view_all": "모든 서비스 보기", + "subtitle": "전문적인 개발 서비스로 당신의 아이디어를 현실로 만들어드립니다", + "web": { + "title": "웹 개발", + "description": "반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500,000부터" + }, + "mobile": { + "title": "모바일 앱", + "description": "iOS와 Android 네이티브 앱 개발", + "price": "₩1,000,000부터" + }, + "design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 인터페이스 및 경험 디자인", + "price": "₩300,000부터" + }, + "marketing": { + "title": "디지털 마케팅", + "description": "SEO, SNS 마케팅, 광고 운영", + "price": "₩200,000부터" + } + }, + "portfolio": { + "title": { + "recent": "최근", + "projects": "프로젝트", + "our": "우리의", + "portfolio": "포트폴리오" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "모든 프로젝트 보기", + "subtitle": "성공적으로 완료한 프로젝트들을 확인해보세요", + "view_project": "프로젝트 보기", + "categories": { + "all": "전체", + "web": "웹 개발", + "mobile": "모바일 앱", + "uiux": "UI/UX 디자인" + } + }, + "calculator": { + "title": "견적 계산기", + "subtitle": "프로젝트 예상 비용을 확인해보세요", + "meta": { + "title": "견적 계산기", + "description": "프로젝트 예상 비용을 간편하게 계산해보세요" + }, + "cta": { + "title": "정확한 견적이 필요하신가요?", + "subtitle": "간단한 정보로 예상 비용을 확인하세요", + "button": "견적 계산하기" + }, + "step1": { + "title": "1단계: 서비스 선택", + "subtitle": "필요한 서비스를 선택해주세요" + }, + "step2": { + "title": "2단계: 옵션 선택", + "subtitle": "프로젝트 세부사항을 선택해주세요" + }, + "complexity": { + "title": "프로젝트 복잡도", + "simple": "단순", + "simple_desc": "기본적인 기능", + "medium": "보통", + "medium_desc": "표준 기능", + "complex": "복잡", + "complex_desc": "고급 기능" + }, + "timeline": { + "title": "개발 기간", + "standard": "표준", + "standard_desc": "일반적인 개발 기간", + "rush": "급행", + "rush_desc": "빠른 개발 (추가 비용)", + "extended": "여유", + "extended_desc": "충분한 개발 기간 (할인)" + }, + "result": { + "title": "견적 결과", + "subtitle": "프로젝트 예상 비용입니다", + "estimated_price": "예상 비용", + "price_note": "* 정확한 견적은 상담 후 확정됩니다", + "summary": "견적 요약", + "selected_services": "선택된 서비스", + "complexity": "복잡도", + "timeline": "개발 기간", + "get_quote": "정확한 견적 요청", + "recalculate": "다시 계산하기", + "contact_note": "더 정확한 견적을 위해 상담을 받아보세요" + }, + "next_step": "다음 단계", + "prev_step": "이전 단계", + "calculate": "견적 계산하기" + }, + "contact": { + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "이름", + "email": "이메일", + "phone": "전화번호", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "메시지", + "submit": "문의 보내기", + "title": "프로젝트 문의", + "service": { + "select": "관심 서비스를 선택하세요", + "web": "웹 개발", + "mobile": "모바일 앱", + "design": "UI/UX 디자인", + "branding": "브랜딩", + "consulting": "컨설팅", + "other": "기타" + }, + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다" + }, + "cta": { + "ready": "준비되셨나요?", + "start": "시작해보세요", + "question": "질문이 있으신가요?", + "subtitle": "프로젝트에 대해 상담해드립니다" + }, + "phone": { + "title": "전화 문의", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "이메일 문의", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "텔레그램", + "subtitle": "빠른 응답을 원하신다면" + }, + "meta": { + "title": "문의하기", + "description": "프로젝트 문의나 상담이 필요하시면 언제든 연락주세요" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "혁신", + "description": "최신 기술과 트렌드를 빠르게 적용하여 혁신적인 솔루션을 제공합니다." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "품질", + "description": "높은 품질의 코드와 디자인으로 안정적이고 우수한 제품을 개발합니다." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + }, + "partnership": { + "title": "파트너십", + "description": "고객과의 긴밀한 협력을 통해 최상의 결과를 만들어냅니다." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "함께 성장하겠습니다", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio", + "subtitle": "당신의 아이디어를 현실로 만들어보세요" + }, + "meta": { + "title": "회사소개", + "description": "스마트솔테크는 혁신적인 기술로 고객의 비즈니스 성장을 지원하는 전문 개발 회사입니다" + }, + "hero": { + "title": "스마트솔테크 소개", + "subtitle": "혁신과 기술로 미래를 만들어갑니다" + }, + "company": { + "title": "회사 정보", + "description1": "스마트솔테크는 2020년 설립된 기술 전문 회사로, 웹 개발, 모바일 앱 개발, UI/UX 디자인 분야에서 전문성을 인정받고 있습니다.", + "description2": "우리는 고객의 니즈를 정확히 파악하고, 최신 기술을 활용하여 혁신적인 솔루션을 제공합니다." + }, + "stats": { + "projects": "완료된 프로젝트", + "experience": "년간 경험", + "clients": "만족한 고객" + }, + "mission": { + "title": "우리의 미션", + "description": "기술을 통해 고객의 비즈니스 성장을 지원하고, 디지털 혁신을 이끌어나가는 것이 우리의 사명입니다." + } + }, + "footer": { + "company": { + "description": "혁신적인 기술로 당신의 비즈니스를 성장시키세요" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "빠른 링크", + "privacy": "개인정보처리방침", + "terms": "이용약관", + "sitemap": "사이트맵" + }, + "contact": { + "title": "연락처", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "서울특별시 강남구 테헤란로 123" + }, + "copyright": "© 2024 스마트솔테크. 모든 권리 보유.", + "privacy": "개인정보보호", + "terms": "이용약관", + "social": { + "follow": "팔로우하기" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "테마 전환" + }, + "language": { + "english": "영어", + "korean": "한국어", + "russian": "러시아어", + "kazakh": "카자흐어", + "ko": "한국어" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "자세히 보기" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "스마트솔테크 - 혁신적인 웹 개발, 모바일 앱 개발, UI/UX 디자인 서비스", + "keywords": "웹 개발, 모바일 앱, UI/UX 디자인, 한국", + "title": "스마트솔테크" + }, + "nav": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/ko_20251021210103.json b/.history/locales/ko_20251021210103.json new file mode 100644 index 0000000..a7414ef --- /dev/null +++ b/.history/locales/ko_20251021210103.json @@ -0,0 +1,481 @@ +{ + "navigation": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "contact": "문의하기", + "calculator": "견적 계산기", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "스마트", + "solutions": "솔루션" + }, + "subtitle": "혁신적인 기술로 당신의 비즈니스를 성장시키세요", + "description": "혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비�니스 디지털 변혁을 이끕니다", + "cta_primary": "프로젝트 시작", + "cta_secondary": "포트폴리오 보기", + "cta": { + "start": "시작하기", + "portfolio": "포트폴리오 보기" + } + }, + "services": { + "title": { + "our": "우리의", + "services": "서비스" + }, + "title_highlight": "서비스", + "description": "최첨단 기술과 창의적 아이디어로 완성된 디지털 솔루션", + "web_development": { + "title": "웹 개발", + "description": "현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500만원~" + }, + "mobile_app": { + "title": "모바일 앱", + "description": "iOS와 Android용 네이티브 및 크로스 플랫폼 앱", + "price": "₩800만원~" + }, + "ui_ux_design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 직관적이고 아름다운 인터페이스 디자인", + "price": "₩300만원~" + }, + "digital_marketing": { + "title": "디지털 마케팅", + "description": "SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅", + "price": "₩200만원~" + }, + "view_all": "모든 서비스 보기", + "subtitle": "전문적인 개발 서비스로 당신의 아이디어를 현실로 만들어드립니다", + "web": { + "title": "웹 개발", + "description": "반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500,000부터" + }, + "mobile": { + "title": "모바일 앱", + "description": "iOS와 Android 네이티브 앱 개발", + "price": "₩1,000,000부터" + }, + "design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 인터페이스 및 경험 디자인", + "price": "₩300,000부터" + }, + "marketing": { + "title": "디지털 마케팅", + "description": "SEO, SNS 마케팅, 광고 운영", + "price": "₩200,000부터" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "최근", + "projects": "프로젝트", + "our": "우리의", + "portfolio": "포트폴리오" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "모든 프로젝트 보기", + "subtitle": "성공적으로 완료한 프로젝트들을 확인해보세요", + "view_project": "프로젝트 보기", + "categories": { + "all": "전체", + "web": "웹 개발", + "mobile": "모바일 앱", + "uiux": "UI/UX 디자인" + }, + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "견적 계산기", + "subtitle": "프로젝트 예상 비용을 확인해보세요", + "meta": { + "title": "견적 계산기", + "description": "프로젝트 예상 비용을 간편하게 계산해보세요" + }, + "cta": { + "title": "정확한 견적이 필요하신가요?", + "subtitle": "간단한 정보로 예상 비용을 확인하세요", + "button": "견적 계산하기" + }, + "step1": { + "title": "1단계: 서비스 선택", + "subtitle": "필요한 서비스를 선택해주세요" + }, + "step2": { + "title": "2단계: 옵션 선택", + "subtitle": "프로젝트 세부사항을 선택해주세요" + }, + "complexity": { + "title": "프로젝트 복잡도", + "simple": "단순", + "simple_desc": "기본적인 기능", + "medium": "보통", + "medium_desc": "표준 기능", + "complex": "복잡", + "complex_desc": "고급 기능" + }, + "timeline": { + "title": "개발 기간", + "standard": "표준", + "standard_desc": "일반적인 개발 기간", + "rush": "급행", + "rush_desc": "빠른 개발 (추가 비용)", + "extended": "여유", + "extended_desc": "충분한 개발 기간 (할인)" + }, + "result": { + "title": "견적 결과", + "subtitle": "프로젝트 예상 비용입니다", + "estimated_price": "예상 비용", + "price_note": "* 정확한 견적은 상담 후 확정됩니다", + "summary": "견적 요약", + "selected_services": "선택된 서비스", + "complexity": "복잡도", + "timeline": "개발 기간", + "get_quote": "정확한 견적 요청", + "recalculate": "다시 계산하기", + "contact_note": "더 정확한 견적을 위해 상담을 받아보세요" + }, + "next_step": "다음 단계", + "prev_step": "이전 단계", + "calculate": "견적 계산하기" + }, + "contact": { + "hero": { + "title": "문의하기", + "subtitle": "여러분의 아이디어를 현실로 만들어드립니다" + }, + "ready_title": "프로젝트를 시작할 준비가 되셨나요?", + "ready_description": "아이디어를 현실로 바꿔보세요. 전문가가 최고의 솔루션을 제공합니다.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "이름", + "email": "이메일", + "phone": "전화번호", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "메시지", + "submit": "문의 보내기", + "title": "프로젝트 문의", + "service": { + "select": "관심 서비스를 선택하세요", + "web": "웹 개발", + "mobile": "모바일 앱", + "design": "UI/UX 디자인", + "branding": "브랜딩", + "consulting": "컨설팅", + "other": "기타" + }, + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다" + }, + "cta": { + "ready": "준비되셨나요?", + "start": "시작해보세요", + "question": "질문이 있으신가요?", + "subtitle": "프로젝트에 대해 상담해드립니다" + }, + "phone": { + "title": "전화 문의", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "이메일 문의", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "텔레그램", + "subtitle": "빠른 응답을 원하신다면" + }, + "meta": { + "title": "문의하기", + "description": "프로젝트 문의나 상담이 필요하시면 언제든 연락주세요" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "혁신", + "description": "최신 기술과 트렌드를 빠르게 적용하여 혁신적인 솔루션을 제공합니다." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "품질", + "description": "높은 품질의 코드와 디자인으로 안정적이고 우수한 제품을 개발합니다." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + }, + "partnership": { + "title": "파트너십", + "description": "고객과의 긴밀한 협력을 통해 최상의 결과를 만들어냅니다." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "함께 성장하겠습니다", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio", + "subtitle": "당신의 아이디어를 현실로 만들어보세요", + "button": "Contact Us" + }, + "meta": { + "title": "회사소개", + "description": "스마트솔테크는 혁신적인 기술로 고객의 비즈니스 성장을 지원하는 전문 개발 회사입니다" + }, + "hero": { + "title": "스마트솔테크 소개", + "subtitle": "혁신과 기술로 미래를 만들어갑니다" + }, + "company": { + "title": "회사 정보", + "description1": "스마트솔테크는 2020년 설립된 기술 전문 회사로, 웹 개발, 모바일 앱 개발, UI/UX 디자인 분야에서 전문성을 인정받고 있습니다.", + "description2": "우리는 고객의 니즈를 정확히 파악하고, 최신 기술을 활용하여 혁신적인 솔루션을 제공합니다." + }, + "stats": { + "projects": "완료된 프로젝트", + "experience": "년간 경험", + "clients": "만족한 고객" + }, + "mission": { + "title": "우리의 미션", + "description": "기술을 통해 고객의 비즈니스 성장을 지원하고, 디지털 혁신을 이끌어나가는 것이 우리의 사명입니다." + } + }, + "footer": { + "company": { + "description": "혁신적인 기술로 당신의 비즈니스를 성장시키세요" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "빠른 링크", + "privacy": "개인정보처리방침", + "terms": "이용약관", + "sitemap": "사이트맵" + }, + "contact": { + "title": "연락처", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "서울특별시 강남구 테헤란로 123" + }, + "copyright": "© 2024 스마트솔테크. 모든 권리 보유.", + "privacy": "개인정보보호", + "terms": "이용약관", + "social": { + "follow": "팔로우하기" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "테마 전환" + }, + "language": { + "english": "영어", + "korean": "한국어", + "russian": "러시아어", + "kazakh": "카자흐어", + "ko": "한국어" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "자세히 보기" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "스마트솔테크 - 혁신적인 웹 개발, 모바일 앱 개발, UI/UX 디자인 서비스", + "keywords": "웹 개발, 모바일 앱, UI/UX 디자인, 한국", + "title": "스마트솔테크" + }, + "nav": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/ko_20251021210120.json b/.history/locales/ko_20251021210120.json new file mode 100644 index 0000000..a97fe4b --- /dev/null +++ b/.history/locales/ko_20251021210120.json @@ -0,0 +1,482 @@ +{ + "navigation": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "contact": "문의하기", + "calculator": "견적 계산기", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "스마트", + "solutions": "솔루션" + }, + "subtitle": "혁신적인 기술로 당신의 비즈니스를 성장시키세요", + "description": "혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비�니스 디지털 변혁을 이끕니다", + "cta_primary": "프로젝트 시작", + "cta_secondary": "포트폴리오 보기", + "cta": { + "start": "시작하기", + "portfolio": "포트폴리오 보기" + } + }, + "services": { + "title": { + "our": "우리의", + "services": "서비스" + }, + "title_highlight": "서비스", + "description": "최첨단 기술과 창의적 아이디어로 완성된 디지털 솔루션", + "web_development": { + "title": "웹 개발", + "description": "현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500만원~" + }, + "mobile_app": { + "title": "모바일 앱", + "description": "iOS와 Android용 네이티브 및 크로스 플랫폼 앱", + "price": "₩800만원~" + }, + "ui_ux_design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 직관적이고 아름다운 인터페이스 디자인", + "price": "₩300만원~" + }, + "digital_marketing": { + "title": "디지털 마케팅", + "description": "SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅", + "price": "₩200만원~" + }, + "view_all": "모든 서비스 보기", + "subtitle": "전문적인 개발 서비스로 당신의 아이디어를 현실로 만들어드립니다", + "web": { + "title": "웹 개발", + "description": "반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500,000부터" + }, + "mobile": { + "title": "모바일 앱", + "description": "iOS와 Android 네이티브 앱 개발", + "price": "₩1,000,000부터" + }, + "design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 인터페이스 및 경험 디자인", + "price": "₩300,000부터" + }, + "marketing": { + "title": "디지털 마케팅", + "description": "SEO, SNS 마케팅, 광고 운영", + "price": "₩200,000부터" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "최근", + "projects": "프로젝트", + "our": "우리의", + "portfolio": "포트폴리오" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "모든 프로젝트 보기", + "subtitle": "성공적으로 완료한 프로젝트들을 확인해보세요", + "view_project": "프로젝트 보기", + "categories": { + "all": "전체", + "web": "웹 개발", + "mobile": "모바일 앱", + "uiux": "UI/UX 디자인" + }, + "project_details": "프로젝트 상세보기", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "견적 계산기", + "subtitle": "프로젝트 예상 비용을 확인해보세요", + "meta": { + "title": "견적 계산기", + "description": "프로젝트 예상 비용을 간편하게 계산해보세요" + }, + "cta": { + "title": "정확한 견적이 필요하신가요?", + "subtitle": "간단한 정보로 예상 비용을 확인하세요", + "button": "견적 계산하기" + }, + "step1": { + "title": "1단계: 서비스 선택", + "subtitle": "필요한 서비스를 선택해주세요" + }, + "step2": { + "title": "2단계: 옵션 선택", + "subtitle": "프로젝트 세부사항을 선택해주세요" + }, + "complexity": { + "title": "프로젝트 복잡도", + "simple": "단순", + "simple_desc": "기본적인 기능", + "medium": "보통", + "medium_desc": "표준 기능", + "complex": "복잡", + "complex_desc": "고급 기능" + }, + "timeline": { + "title": "개발 기간", + "standard": "표준", + "standard_desc": "일반적인 개발 기간", + "rush": "급행", + "rush_desc": "빠른 개발 (추가 비용)", + "extended": "여유", + "extended_desc": "충분한 개발 기간 (할인)" + }, + "result": { + "title": "견적 결과", + "subtitle": "프로젝트 예상 비용입니다", + "estimated_price": "예상 비용", + "price_note": "* 정확한 견적은 상담 후 확정됩니다", + "summary": "견적 요약", + "selected_services": "선택된 서비스", + "complexity": "복잡도", + "timeline": "개발 기간", + "get_quote": "정확한 견적 요청", + "recalculate": "다시 계산하기", + "contact_note": "더 정확한 견적을 위해 상담을 받아보세요" + }, + "next_step": "다음 단계", + "prev_step": "이전 단계", + "calculate": "견적 계산하기" + }, + "contact": { + "hero": { + "title": "문의하기", + "subtitle": "여러분의 아이디어를 현실로 만들어드립니다" + }, + "ready_title": "프로젝트를 시작할 준비가 되셨나요?", + "ready_description": "아이디어를 현실로 바꿔보세요. 전문가가 최고의 솔루션을 제공합니다.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "이름", + "email": "이메일", + "phone": "전화번호", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "메시지", + "submit": "문의 보내기", + "title": "프로젝트 문의", + "service": { + "select": "관심 서비스를 선택하세요", + "web": "웹 개발", + "mobile": "모바일 앱", + "design": "UI/UX 디자인", + "branding": "브랜딩", + "consulting": "컨설팅", + "other": "기타" + }, + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다" + }, + "cta": { + "ready": "준비되셨나요?", + "start": "시작해보세요", + "question": "질문이 있으신가요?", + "subtitle": "프로젝트에 대해 상담해드립니다" + }, + "phone": { + "title": "전화 문의", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "이메일 문의", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "텔레그램", + "subtitle": "빠른 응답을 원하신다면" + }, + "meta": { + "title": "문의하기", + "description": "프로젝트 문의나 상담이 필요하시면 언제든 연락주세요" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "혁신", + "description": "최신 기술과 트렌드를 빠르게 적용하여 혁신적인 솔루션을 제공합니다." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "품질", + "description": "높은 품질의 코드와 디자인으로 안정적이고 우수한 제품을 개발합니다." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + }, + "partnership": { + "title": "파트너십", + "description": "고객과의 긴밀한 협력을 통해 최상의 결과를 만들어냅니다." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "함께 성장하겠습니다", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio", + "subtitle": "당신의 아이디어를 현실로 만들어보세요", + "button": "Contact Us" + }, + "meta": { + "title": "회사소개", + "description": "스마트솔테크는 혁신적인 기술로 고객의 비즈니스 성장을 지원하는 전문 개발 회사입니다" + }, + "hero": { + "title": "스마트솔테크 소개", + "subtitle": "혁신과 기술로 미래를 만들어갑니다" + }, + "company": { + "title": "회사 정보", + "description1": "스마트솔테크는 2020년 설립된 기술 전문 회사로, 웹 개발, 모바일 앱 개발, UI/UX 디자인 분야에서 전문성을 인정받고 있습니다.", + "description2": "우리는 고객의 니즈를 정확히 파악하고, 최신 기술을 활용하여 혁신적인 솔루션을 제공합니다." + }, + "stats": { + "projects": "완료된 프로젝트", + "experience": "년간 경험", + "clients": "만족한 고객" + }, + "mission": { + "title": "우리의 미션", + "description": "기술을 통해 고객의 비즈니스 성장을 지원하고, 디지털 혁신을 이끌어나가는 것이 우리의 사명입니다." + } + }, + "footer": { + "company": { + "description": "혁신적인 기술로 당신의 비즈니스를 성장시키세요" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "빠른 링크", + "privacy": "개인정보처리방침", + "terms": "이용약관", + "sitemap": "사이트맵" + }, + "contact": { + "title": "연락처", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "서울특별시 강남구 테헤란로 123" + }, + "copyright": "© 2024 스마트솔테크. 모든 권리 보유.", + "privacy": "개인정보보호", + "terms": "이용약관", + "social": { + "follow": "팔로우하기" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "테마 전환" + }, + "language": { + "english": "영어", + "korean": "한국어", + "russian": "러시아어", + "kazakh": "카자흐어", + "ko": "한국어" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "자세히 보기" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "스마트솔테크 - 혁신적인 웹 개발, 모바일 앱 개발, UI/UX 디자인 서비스", + "keywords": "웹 개발, 모바일 앱, UI/UX 디자인, 한국", + "title": "스마트솔테크" + }, + "nav": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/ko_20251021210347.json b/.history/locales/ko_20251021210347.json new file mode 100644 index 0000000..a97fe4b --- /dev/null +++ b/.history/locales/ko_20251021210347.json @@ -0,0 +1,482 @@ +{ + "navigation": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "contact": "문의하기", + "calculator": "견적 계산기", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "스마트", + "solutions": "솔루션" + }, + "subtitle": "혁신적인 기술로 당신의 비즈니스를 성장시키세요", + "description": "혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비�니스 디지털 변혁을 이끕니다", + "cta_primary": "프로젝트 시작", + "cta_secondary": "포트폴리오 보기", + "cta": { + "start": "시작하기", + "portfolio": "포트폴리오 보기" + } + }, + "services": { + "title": { + "our": "우리의", + "services": "서비스" + }, + "title_highlight": "서비스", + "description": "최첨단 기술과 창의적 아이디어로 완성된 디지털 솔루션", + "web_development": { + "title": "웹 개발", + "description": "현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500만원~" + }, + "mobile_app": { + "title": "모바일 앱", + "description": "iOS와 Android용 네이티브 및 크로스 플랫폼 앱", + "price": "₩800만원~" + }, + "ui_ux_design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 직관적이고 아름다운 인터페이스 디자인", + "price": "₩300만원~" + }, + "digital_marketing": { + "title": "디지털 마케팅", + "description": "SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅", + "price": "₩200만원~" + }, + "view_all": "모든 서비스 보기", + "subtitle": "전문적인 개발 서비스로 당신의 아이디어를 현실로 만들어드립니다", + "web": { + "title": "웹 개발", + "description": "반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500,000부터" + }, + "mobile": { + "title": "모바일 앱", + "description": "iOS와 Android 네이티브 앱 개발", + "price": "₩1,000,000부터" + }, + "design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 인터페이스 및 경험 디자인", + "price": "₩300,000부터" + }, + "marketing": { + "title": "디지털 마케팅", + "description": "SEO, SNS 마케팅, 광고 운영", + "price": "₩200,000부터" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + } + }, + "portfolio": { + "title": { + "recent": "최근", + "projects": "프로젝트", + "our": "우리의", + "portfolio": "포트폴리오" + }, + "title_highlight": "Projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "모든 프로젝트 보기", + "subtitle": "성공적으로 완료한 프로젝트들을 확인해보세요", + "view_project": "프로젝트 보기", + "categories": { + "all": "전체", + "web": "웹 개발", + "mobile": "모바일 앱", + "uiux": "UI/UX 디자인" + }, + "project_details": "프로젝트 상세보기", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + } + }, + "calculator": { + "title": "견적 계산기", + "subtitle": "프로젝트 예상 비용을 확인해보세요", + "meta": { + "title": "견적 계산기", + "description": "프로젝트 예상 비용을 간편하게 계산해보세요" + }, + "cta": { + "title": "정확한 견적이 필요하신가요?", + "subtitle": "간단한 정보로 예상 비용을 확인하세요", + "button": "견적 계산하기" + }, + "step1": { + "title": "1단계: 서비스 선택", + "subtitle": "필요한 서비스를 선택해주세요" + }, + "step2": { + "title": "2단계: 옵션 선택", + "subtitle": "프로젝트 세부사항을 선택해주세요" + }, + "complexity": { + "title": "프로젝트 복잡도", + "simple": "단순", + "simple_desc": "기본적인 기능", + "medium": "보통", + "medium_desc": "표준 기능", + "complex": "복잡", + "complex_desc": "고급 기능" + }, + "timeline": { + "title": "개발 기간", + "standard": "표준", + "standard_desc": "일반적인 개발 기간", + "rush": "급행", + "rush_desc": "빠른 개발 (추가 비용)", + "extended": "여유", + "extended_desc": "충분한 개발 기간 (할인)" + }, + "result": { + "title": "견적 결과", + "subtitle": "프로젝트 예상 비용입니다", + "estimated_price": "예상 비용", + "price_note": "* 정확한 견적은 상담 후 확정됩니다", + "summary": "견적 요약", + "selected_services": "선택된 서비스", + "complexity": "복잡도", + "timeline": "개발 기간", + "get_quote": "정확한 견적 요청", + "recalculate": "다시 계산하기", + "contact_note": "더 정확한 견적을 위해 상담을 받아보세요" + }, + "next_step": "다음 단계", + "prev_step": "이전 단계", + "calculate": "견적 계산하기" + }, + "contact": { + "hero": { + "title": "문의하기", + "subtitle": "여러분의 아이디어를 현실로 만들어드립니다" + }, + "ready_title": "프로젝트를 시작할 준비가 되셨나요?", + "ready_description": "아이디어를 현실로 바꿔보세요. 전문가가 최고의 솔루션을 제공합니다.", + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application", + "form": { + "name": "이름", + "email": "이메일", + "phone": "전화번호", + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "message": "메시지", + "submit": "문의 보내기", + "title": "프로젝트 문의", + "service": { + "select": "관심 서비스를 선택하세요", + "web": "웹 개발", + "mobile": "모바일 앱", + "design": "UI/UX 디자인", + "branding": "브랜딩", + "consulting": "컨설팅", + "other": "기타" + }, + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다" + }, + "cta": { + "ready": "준비되셨나요?", + "start": "시작해보세요", + "question": "질문이 있으신가요?", + "subtitle": "프로젝트에 대해 상담해드립니다" + }, + "phone": { + "title": "전화 문의", + "number": "+82-2-1234-5678" + }, + "email": { + "title": "이메일 문의", + "address": "info@smartsoltech.co.kr" + }, + "telegram": { + "title": "텔레그램", + "subtitle": "빠른 응답을 원하신다면" + }, + "meta": { + "title": "문의하기", + "description": "프로젝트 문의나 상담이 필요하시면 언제든 연락주세요" + } + }, + "about": { + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "values": { + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "innovation": { + "title": "혁신", + "description": "최신 기술과 트렌드를 빠르게 적용하여 혁신적인 솔루션을 제공합니다." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "품질", + "description": "높은 품질의 코드와 디자인으로 안정적이고 우수한 제품을 개발합니다." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + }, + "partnership": { + "title": "파트너십", + "description": "고객과의 긴밀한 협력을 통해 최상의 결과를 만들어냅니다." + } + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + }, + "cta": { + "title": "함께 성장하겠습니다", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio", + "subtitle": "당신의 아이디어를 현실로 만들어보세요", + "button": "Contact Us" + }, + "meta": { + "title": "회사소개", + "description": "스마트솔테크는 혁신적인 기술로 고객의 비즈니스 성장을 지원하는 전문 개발 회사입니다" + }, + "hero": { + "title": "스마트솔테크 소개", + "subtitle": "혁신과 기술로 미래를 만들어갑니다" + }, + "company": { + "title": "회사 정보", + "description1": "스마트솔테크는 2020년 설립된 기술 전문 회사로, 웹 개발, 모바일 앱 개발, UI/UX 디자인 분야에서 전문성을 인정받고 있습니다.", + "description2": "우리는 고객의 니즈를 정확히 파악하고, 최신 기술을 활용하여 혁신적인 솔루션을 제공합니다." + }, + "stats": { + "projects": "완료된 프로젝트", + "experience": "년간 경험", + "clients": "만족한 고객" + }, + "mission": { + "title": "우리의 미션", + "description": "기술을 통해 고객의 비즈니스 성장을 지원하고, 디지털 혁신을 이끌어나가는 것이 우리의 사명입니다." + } + }, + "footer": { + "company": { + "description": "혁신적인 기술로 당신의 비즈니스를 성장시키세요" + }, + "description": "Digital solution specialist leading innovation", + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "links": { + "title": "빠른 링크", + "privacy": "개인정보처리방침", + "terms": "이용약관", + "sitemap": "사이트맵" + }, + "contact": { + "title": "연락처", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "서울특별시 강남구 테헤란로 123" + }, + "copyright": "© 2024 스마트솔테크. 모든 권리 보유.", + "privacy": "개인정보보호", + "terms": "이용약관", + "social": { + "follow": "팔로우하기" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "테마 전환" + }, + "language": { + "english": "영어", + "korean": "한국어", + "russian": "러시아어", + "kazakh": "카자흐어", + "ko": "한국어" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "자세히 보기" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "스마트솔테크 - 혁신적인 웹 개발, 모바일 앱 개발, UI/UX 디자인 서비스", + "keywords": "웹 개발, 모바일 앱, UI/UX 디자인, 한국", + "title": "스마트솔테크" + }, + "nav": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기" + }, + "admin": { + "login": "Admin Panel Login", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard": "Dashboard", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "title": "SmartSolTech Admin", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "description": "Digital solution specialist leading innovation", + "tagline": "Future begins here", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "contact_us": "Contact us", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + } +} \ No newline at end of file diff --git a/.history/locales/ko_20251021211024.json b/.history/locales/ko_20251021211024.json new file mode 100644 index 0000000..7aa2067 --- /dev/null +++ b/.history/locales/ko_20251021211024.json @@ -0,0 +1,500 @@ +{ + "navigation": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "contact": "문의하기", + "calculator": "견적 계산기", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "스마트", + "solutions": "솔루션" + }, + "subtitle": "혁신적인 기술로 당신의 비즈니스를 성장시키세요", + "description": "혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비�니스 디지털 변혁을 이끕니다", + "cta": { + "start": "시작하기", + "portfolio": "포트폴리오 보기" + }, + "cta_primary": "프로젝트 시작", + "cta_secondary": "포트폴리오 보기" + }, + "services": { + "title": { + "our": "우리의", + "services": "서비스" + }, + "subtitle": "전문적인 개발 서비스로 당신의 아이디어를 현실로 만들어드립니다", + "description": "최첨단 기술과 창의적 아이디어로 완성된 디지털 솔루션", + "view_all": "모든 서비스 보기", + "web": { + "title": "웹 개발", + "description": "반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500,000부터" + }, + "mobile": { + "title": "모바일 앱", + "description": "iOS와 Android 네이티브 앱 개발", + "price": "₩1,000,000부터" + }, + "design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 인터페이스 및 경험 디자인", + "price": "₩300,000부터" + }, + "marketing": { + "title": "디지털 마케팅", + "description": "SEO, SNS 마케팅, 광고 운영", + "price": "₩200,000부터" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + }, + "title_highlight": "서비스", + "web_development": { + "title": "웹 개발", + "description": "현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500만원~" + }, + "mobile_app": { + "title": "모바일 앱", + "description": "iOS와 Android용 네이티브 및 크로스 플랫폼 앱", + "price": "₩800만원~" + }, + "ui_ux_design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 직관적이고 아름다운 인터페이스 디자인", + "price": "₩300만원~" + }, + "digital_marketing": { + "title": "디지털 마케팅", + "description": "SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅", + "price": "₩200만원~" + } + }, + "portfolio": { + "title": { + "recent": "최근", + "projects": "프로젝트", + "our": "우리의", + "portfolio": "포트폴리오" + }, + "subtitle": "성공적으로 완료한 프로젝트들을 확인해보세요", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "모든 프로젝트 보기", + "categories": { + "all": "전체", + "web": "웹 개발", + "mobile": "모바일 앱", + "uiux": "UI/UX 디자인" + }, + "project_details": "프로젝트 상세보기", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + }, + "title_highlight": "Projects", + "view_project": "프로젝트 보기" + }, + "calculator": { + "title": "견적 계산기", + "subtitle": "프로젝트 예상 비용을 확인해보세요", + "meta": { + "title": "견적 계산기", + "description": "프로젝트 예상 비용을 간편하게 계산해보세요" + }, + "cta": { + "title": "정확한 견적이 필요하신가요?", + "subtitle": "간단한 정보로 예상 비용을 확인하세요", + "button": "견적 계산하기" + }, + "step1": { + "title": "1단계: 서비스 선택", + "subtitle": "필요한 서비스를 선택해주세요" + }, + "step2": { + "title": "2단계: 옵션 선택", + "subtitle": "프로젝트 세부사항을 선택해주세요" + }, + "complexity": { + "title": "프로젝트 복잡도", + "simple": "단순", + "simple_desc": "기본적인 기능", + "medium": "보통", + "medium_desc": "표준 기능", + "complex": "복잡", + "complex_desc": "고급 기능" + }, + "timeline": { + "title": "개발 기간", + "standard": "표준", + "standard_desc": "일반적인 개발 기간", + "rush": "급행", + "rush_desc": "빠른 개발 (추가 비용)", + "extended": "여유", + "extended_desc": "충분한 개발 기간 (할인)" + }, + "result": { + "title": "견적 결과", + "subtitle": "프로젝트 예상 비용입니다", + "estimated_price": "예상 비용", + "price_note": "* 정확한 견적은 상담 후 확정됩니다", + "summary": "견적 요약", + "selected_services": "선택된 서비스", + "complexity": "복잡도", + "timeline": "개발 기간", + "get_quote": "정확한 견적 요청", + "recalculate": "다시 계산하기", + "contact_note": "더 정확한 견적을 위해 상담을 받아보세요" + }, + "next_step": "다음 단계", + "prev_step": "이전 단계", + "calculate": "견적 계산하기" + }, + "contact": { + "hero": { + "title": "문의하기", + "subtitle": "여러분의 아이디어를 현실로 만들어드립니다" + }, + "ready_title": "프로젝트를 시작할 준비가 되셨나요?", + "ready_description": "아이디어를 현실로 바꿔보세요. 전문가가 최고의 솔루션을 제공합니다.", + "form": { + "title": "프로젝트 문의", + "name": "이름", + "email": "이메일", + "phone": "전화번호", + "message": "메시지", + "submit": "문의 보내기", + "service": { + "select": "관심 서비스를 선택하세요", + "web": "웹 개발", + "mobile": "모바일 앱", + "design": "UI/UX 디자인", + "branding": "브랜딩", + "consulting": "컨설팅", + "other": "기타" + }, + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다" + }, + "form": { + "title": "프로젝트 문의", + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다", + "service": { + "title": "관심 서비스" + } + }, + "info": { + "title": "연락처 정보" + }, + "phone": { + "title": "전화 문의", + "number": "+82-2-1234-5678", + "hours": "월-금 9:00-18:00" + }, + "email": { + "title": "이메일 문의", + "address": "info@smartsoltech.co.kr", + "response": "24시간 내 응답" + }, + "telegram": { + "title": "텔레그램", + "subtitle": "빠른 응답을 원하신다면" + }, + "address": { + "title": "사무실 주소", + "line1": "테헤란로 123, 강남구", + "line2": "서울, 대한민국" + }, + "cta": { + "ready": "준비되셨나요?", + "start": "시작해보세요", + "question": "질문이 있으신가요?", + "subtitle": "프로젝트에 대해 상담해드립니다" + }, + "meta": { + "title": "문의하기", + "description": "프로젝트 문의나 상담이 필요하시면 언제든 연락주세요" + }, + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application" + }, + "about": { + "hero": { + "title": "스마트솔테크 소개", + "subtitle": "혁신과 기술로 미래를 만들어갑니다" + }, + "company": { + "title": "회사 정보", + "description1": "스마트솔테크는 2020년 설립된 기술 전문 회사로, 웹 개발, 모바일 앱 개발, UI/UX 디자인 분야에서 전문성을 인정받고 있습니다.", + "description2": "우리는 고객의 니즈를 정확히 파악하고, 최신 기술을 활용하여 혁신적인 솔루션을 제공합니다." + }, + "stats": { + "projects": "완료된 프로젝트", + "experience": "년간 경험", + "clients": "만족한 고객" + }, + "mission": { + "title": "우리의 미션", + "description": "기술을 통해 고객의 비즈니스 성장을 지원하고, 디지털 혁신을 이끌어나가는 것이 우리의 사명입니다." + }, + "values": { + "innovation": { + "title": "혁신", + "description": "최신 기술과 트렌드를 빠르게 적용하여 혁신적인 솔루션을 제공합니다." + }, + "quality": { + "title": "품질", + "description": "높은 품질의 코드와 디자인으로 안정적이고 우수한 제품을 개발합니다." + }, + "partnership": { + "title": "파트너십", + "description": "고객과의 긴밀한 협력을 통해 최상의 결과를 만들어냅니다." + }, + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "cta": { + "title": "함께 성장하겠습니다", + "subtitle": "당신의 아이디어를 현실로 만들어보세요", + "button": "Contact Us", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + }, + "meta": { + "title": "회사소개", + "description": "스마트솔테크는 혁신적인 기술로 고객의 비즈니스 성장을 지원하는 전문 개발 회사입니다" + }, + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + } + }, + "footer": { + "description": "Digital solution specialist leading innovation", + "links": { + "title": "빠른 링크", + "privacy": "개인정보처리방침", + "terms": "이용약관", + "sitemap": "사이트맵" + }, + "contact": { + "title": "연락처", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "서울특별시 강남구 테헤란로 123" + }, + "copyright": "© 2024 스마트솔테크. 모든 권리 보유.", + "company": { + "description": "혁신적인 기술로 당신의 비즈니스를 성장시키세요" + }, + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "privacy": "개인정보보호", + "terms": "이용약관", + "social": { + "follow": "팔로우하기" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "테마 전환" + }, + "language": { + "english": "영어", + "korean": "한국어", + "russian": "러시아어", + "kazakh": "카자흐어", + "ko": "한국어" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "자세히 보기" + }, + "meta": { + "description": "스마트솔테크 - 혁신적인 웹 개발, 모바일 앱 개발, UI/UX 디자인 서비스", + "keywords": "웹 개발, 모바일 앱, UI/UX 디자인, 한국", + "title": "스마트솔테크" + }, + "nav": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기" + }, + "admin": { + "login": "Admin Panel Login", + "dashboard": "Dashboard", + "title": "SmartSolTech Admin", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "description": "Digital solution specialist leading innovation", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "tagline": "Future begins here", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech" +} \ No newline at end of file diff --git a/.history/locales/ko_20251021211047.json b/.history/locales/ko_20251021211047.json new file mode 100644 index 0000000..81687c8 --- /dev/null +++ b/.history/locales/ko_20251021211047.json @@ -0,0 +1,492 @@ +{ + "navigation": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "contact": "문의하기", + "calculator": "견적 계산기", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "스마트", + "solutions": "솔루션" + }, + "subtitle": "혁신적인 기술로 당신의 비즈니스를 성장시키세요", + "description": "혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비�니스 디지털 변혁을 이끕니다", + "cta": { + "start": "시작하기", + "portfolio": "포트폴리오 보기" + }, + "cta_primary": "프로젝트 시작", + "cta_secondary": "포트폴리오 보기" + }, + "services": { + "title": { + "our": "우리의", + "services": "서비스" + }, + "subtitle": "전문적인 개발 서비스로 당신의 아이디어를 현실로 만들어드립니다", + "description": "최첨단 기술과 창의적 아이디어로 완성된 디지털 솔루션", + "view_all": "모든 서비스 보기", + "web": { + "title": "웹 개발", + "description": "반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500,000부터" + }, + "mobile": { + "title": "모바일 앱", + "description": "iOS와 Android 네이티브 앱 개발", + "price": "₩1,000,000부터" + }, + "design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 인터페이스 및 경험 디자인", + "price": "₩300,000부터" + }, + "marketing": { + "title": "디지털 마케팅", + "description": "SEO, SNS 마케팅, 광고 운영", + "price": "₩200,000부터" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + }, + "title_highlight": "서비스", + "web_development": { + "title": "웹 개발", + "description": "현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500만원~" + }, + "mobile_app": { + "title": "모바일 앱", + "description": "iOS와 Android용 네이티브 및 크로스 플랫폼 앱", + "price": "₩800만원~" + }, + "ui_ux_design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 직관적이고 아름다운 인터페이스 디자인", + "price": "₩300만원~" + }, + "digital_marketing": { + "title": "디지털 마케팅", + "description": "SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅", + "price": "₩200만원~" + } + }, + "portfolio": { + "title": { + "recent": "최근", + "projects": "프로젝트", + "our": "우리의", + "portfolio": "포트폴리오" + }, + "subtitle": "성공적으로 완료한 프로젝트들을 확인해보세요", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "모든 프로젝트 보기", + "categories": { + "all": "전체", + "web": "웹 개발", + "mobile": "모바일 앱", + "uiux": "UI/UX 디자인" + }, + "project_details": "프로젝트 상세보기", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + }, + "title_highlight": "Projects", + "view_project": "프로젝트 보기" + }, + "calculator": { + "title": "견적 계산기", + "subtitle": "프로젝트 예상 비용을 확인해보세요", + "meta": { + "title": "견적 계산기", + "description": "프로젝트 예상 비용을 간편하게 계산해보세요" + }, + "cta": { + "title": "정확한 견적이 필요하신가요?", + "subtitle": "간단한 정보로 예상 비용을 확인하세요", + "button": "견적 계산하기" + }, + "step1": { + "title": "1단계: 서비스 선택", + "subtitle": "필요한 서비스를 선택해주세요" + }, + "step2": { + "title": "2단계: 옵션 선택", + "subtitle": "프로젝트 세부사항을 선택해주세요" + }, + "complexity": { + "title": "프로젝트 복잡도", + "simple": "단순", + "simple_desc": "기본적인 기능", + "medium": "보통", + "medium_desc": "표준 기능", + "complex": "복잡", + "complex_desc": "고급 기능" + }, + "timeline": { + "title": "개발 기간", + "standard": "표준", + "standard_desc": "일반적인 개발 기간", + "rush": "급행", + "rush_desc": "빠른 개발 (추가 비용)", + "extended": "여유", + "extended_desc": "충분한 개발 기간 (할인)" + }, + "result": { + "title": "견적 결과", + "subtitle": "프로젝트 예상 비용입니다", + "estimated_price": "예상 비용", + "price_note": "* 정확한 견적은 상담 후 확정됩니다", + "summary": "견적 요약", + "selected_services": "선택된 서비스", + "complexity": "복잡도", + "timeline": "개발 기간", + "get_quote": "정확한 견적 요청", + "recalculate": "다시 계산하기", + "contact_note": "더 정확한 견적을 위해 상담을 받아보세요" + }, + "next_step": "다음 단계", + "prev_step": "이전 단계", + "calculate": "견적 계산하기" + }, + "contact": { + "hero": { + "title": "문의하기", + "subtitle": "여러분의 아이디어를 현실로 만들어드립니다" + }, + "ready_title": "프로젝트를 시작할 준비가 되셨나요?", + "ready_description": "아이디어를 현실로 바꿔보세요. 전문가가 최고의 솔루션을 제공합니다.", + "form": { + "title": "프로젝트 문의", + "name": "이름", + "email": "이메일", + "phone": "전화번호", + "message": "메시지", + "submit": "문의 보내기", + "service": { + "select": "관심 서비스를 선택하세요", + "web": "웹 개발", + "mobile": "모바일 앱", + "design": "UI/UX 디자인", + "branding": "브랜딩", + "consulting": "컨설팅", + "other": "기타" + }, + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다" + }, + "info": { + "title": "연락처 정보" + }, + "phone": { + "title": "전화 문의", + "number": "+82-2-1234-5678", + "hours": "월-금 9:00-18:00" + }, + "email": { + "title": "이메일 문의", + "address": "info@smartsoltech.co.kr", + "response": "24시간 내 응답" + }, + "telegram": { + "title": "텔레그램", + "subtitle": "빠른 응답을 원하신다면" + }, + "address": { + "title": "사무실 주소", + "line1": "테헤란로 123, 강남구", + "line2": "서울, 대한민국" + }, + "cta": { + "ready": "준비되셨나요?", + "start": "시작해보세요", + "question": "질문이 있으신가요?", + "subtitle": "프로젝트에 대해 상담해드립니다" + }, + "meta": { + "title": "문의하기", + "description": "프로젝트 문의나 상담이 필요하시면 언제든 연락주세요" + }, + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application" + }, + "about": { + "hero": { + "title": "스마트솔테크 소개", + "subtitle": "혁신과 기술로 미래를 만들어갑니다" + }, + "company": { + "title": "회사 정보", + "description1": "스마트솔테크는 2020년 설립된 기술 전문 회사로, 웹 개발, 모바일 앱 개발, UI/UX 디자인 분야에서 전문성을 인정받고 있습니다.", + "description2": "우리는 고객의 니즈를 정확히 파악하고, 최신 기술을 활용하여 혁신적인 솔루션을 제공합니다." + }, + "stats": { + "projects": "완료된 프로젝트", + "experience": "년간 경험", + "clients": "만족한 고객" + }, + "mission": { + "title": "우리의 미션", + "description": "기술을 통해 고객의 비즈니스 성장을 지원하고, 디지털 혁신을 이끌어나가는 것이 우리의 사명입니다." + }, + "values": { + "innovation": { + "title": "혁신", + "description": "최신 기술과 트렌드를 빠르게 적용하여 혁신적인 솔루션을 제공합니다." + }, + "quality": { + "title": "품질", + "description": "높은 품질의 코드와 디자인으로 안정적이고 우수한 제품을 개발합니다." + }, + "partnership": { + "title": "파트너십", + "description": "고객과의 긴밀한 협력을 통해 최상의 결과를 만들어냅니다." + }, + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "cta": { + "title": "함께 성장하겠습니다", + "subtitle": "당신의 아이디어를 현실로 만들어보세요", + "button": "Contact Us", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + }, + "meta": { + "title": "회사소개", + "description": "스마트솔테크는 혁신적인 기술로 고객의 비즈니스 성장을 지원하는 전문 개발 회사입니다" + }, + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + } + }, + "footer": { + "description": "Digital solution specialist leading innovation", + "links": { + "title": "빠른 링크", + "privacy": "개인정보처리방침", + "terms": "이용약관", + "sitemap": "사이트맵" + }, + "contact": { + "title": "연락처", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "서울특별시 강남구 테헤란로 123" + }, + "copyright": "© 2024 스마트솔테크. 모든 권리 보유.", + "company": { + "description": "혁신적인 기술로 당신의 비즈니스를 성장시키세요" + }, + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "privacy": "개인정보보호", + "terms": "이용약관", + "social": { + "follow": "팔로우하기" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "테마 전환" + }, + "language": { + "english": "영어", + "korean": "한국어", + "russian": "러시아어", + "kazakh": "카자흐어", + "ko": "한국어" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "자세히 보기" + }, + "meta": { + "description": "스마트솔테크 - 혁신적인 웹 개발, 모바일 앱 개발, UI/UX 디자인 서비스", + "keywords": "웹 개발, 모바일 앱, UI/UX 디자인, 한국", + "title": "스마트솔테크" + }, + "nav": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기" + }, + "admin": { + "login": "Admin Panel Login", + "dashboard": "Dashboard", + "title": "SmartSolTech Admin", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "description": "Digital solution specialist leading innovation", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "tagline": "Future begins here", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech" +} \ No newline at end of file diff --git a/.history/locales/ko_20251021211057.json b/.history/locales/ko_20251021211057.json new file mode 100644 index 0000000..e4a476d --- /dev/null +++ b/.history/locales/ko_20251021211057.json @@ -0,0 +1,496 @@ +{ + "navigation": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "contact": "문의하기", + "calculator": "견적 계산기", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "스마트", + "solutions": "솔루션" + }, + "subtitle": "혁신적인 기술로 당신의 비즈니스를 성장시키세요", + "description": "혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비�니스 디지털 변혁을 이끕니다", + "cta": { + "start": "시작하기", + "portfolio": "포트폴리오 보기" + }, + "cta_primary": "프로젝트 시작", + "cta_secondary": "포트폴리오 보기" + }, + "services": { + "title": { + "our": "우리의", + "services": "서비스" + }, + "subtitle": "전문적인 개발 서비스로 당신의 아이디어를 현실로 만들어드립니다", + "description": "최첨단 기술과 창의적 아이디어로 완성된 디지털 솔루션", + "view_all": "모든 서비스 보기", + "web": { + "title": "웹 개발", + "description": "반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500,000부터" + }, + "mobile": { + "title": "모바일 앱", + "description": "iOS와 Android 네이티브 앱 개발", + "price": "₩1,000,000부터" + }, + "design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 인터페이스 및 경험 디자인", + "price": "₩300,000부터" + }, + "marketing": { + "title": "디지털 마케팅", + "description": "SEO, SNS 마케팅, 광고 운영", + "price": "₩200,000부터" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + }, + "title_highlight": "서비스", + "web_development": { + "title": "웹 개발", + "description": "현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500만원~" + }, + "mobile_app": { + "title": "모바일 앱", + "description": "iOS와 Android용 네이티브 및 크로스 플랫폼 앱", + "price": "₩800만원~" + }, + "ui_ux_design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 직관적이고 아름다운 인터페이스 디자인", + "price": "₩300만원~" + }, + "digital_marketing": { + "title": "디지털 마케팅", + "description": "SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅", + "price": "₩200만원~" + } + }, + "portfolio": { + "title": { + "recent": "최근", + "projects": "프로젝트", + "our": "우리의", + "portfolio": "포트폴리오" + }, + "subtitle": "성공적으로 완료한 프로젝트들을 확인해보세요", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "모든 프로젝트 보기", + "categories": { + "all": "전체", + "web": "웹 개발", + "mobile": "모바일 앱", + "uiux": "UI/UX 디자인" + }, + "project_details": "프로젝트 상세보기", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + }, + "title_highlight": "Projects", + "view_project": "프로젝트 보기" + }, + "calculator": { + "title": "견적 계산기", + "subtitle": "프로젝트 예상 비용을 확인해보세요", + "meta": { + "title": "견적 계산기", + "description": "프로젝트 예상 비용을 간편하게 계산해보세요" + }, + "cta": { + "title": "정확한 견적이 필요하신가요?", + "subtitle": "간단한 정보로 예상 비용을 확인하세요", + "button": "견적 계산하기" + }, + "step1": { + "title": "1단계: 서비스 선택", + "subtitle": "필요한 서비스를 선택해주세요" + }, + "step2": { + "title": "2단계: 옵션 선택", + "subtitle": "프로젝트 세부사항을 선택해주세요" + }, + "complexity": { + "title": "프로젝트 복잡도", + "simple": "단순", + "simple_desc": "기본적인 기능", + "medium": "보통", + "medium_desc": "표준 기능", + "complex": "복잡", + "complex_desc": "고급 기능" + }, + "timeline": { + "title": "개발 기간", + "standard": "표준", + "standard_desc": "일반적인 개발 기간", + "rush": "급행", + "rush_desc": "빠른 개발 (추가 비용)", + "extended": "여유", + "extended_desc": "충분한 개발 기간 (할인)" + }, + "result": { + "title": "견적 결과", + "subtitle": "프로젝트 예상 비용입니다", + "estimated_price": "예상 비용", + "price_note": "* 정확한 견적은 상담 후 확정됩니다", + "summary": "견적 요약", + "selected_services": "선택된 서비스", + "complexity": "복잡도", + "timeline": "개발 기간", + "get_quote": "정확한 견적 요청", + "recalculate": "다시 계산하기", + "contact_note": "더 정확한 견적을 위해 상담을 받아보세요" + }, + "next_step": "다음 단계", + "prev_step": "이전 단계", + "calculate": "견적 계산하기" + }, + "contact": { + "hero": { + "title": "문의하기", + "subtitle": "여러분의 아이디어를 현실로 만들어드립니다" + }, + "ready_title": "프로젝트를 시작할 준비가 되셨나요?", + "ready_description": "아이디어를 현실로 바꿔보세요. 전문가가 최고의 솔루션을 제공합니다.", + "form": { + "title": "프로젝트 문의", + "name": "이름", + "email": "이메일", + "phone": "전화번호", + "message": "메시지", + "submit": "문의 보내기", + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다", + "service": { + "title": "관심 서비스", + "select": "관심 서비스를 선택하세요", + "web": "웹 개발", + "mobile": "모바일 앱", + "design": "UI/UX 디자인", + "branding": "브랜딩", + "consulting": "컨설팅", + "other": "기타" + } + }, + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다" + }, + "info": { + "title": "연락처 정보" + }, + "phone": { + "title": "전화 문의", + "number": "+82-2-1234-5678", + "hours": "월-금 9:00-18:00" + }, + "email": { + "title": "이메일 문의", + "address": "info@smartsoltech.co.kr", + "response": "24시간 내 응답" + }, + "telegram": { + "title": "텔레그램", + "subtitle": "빠른 응답을 원하신다면" + }, + "address": { + "title": "사무실 주소", + "line1": "테헤란로 123, 강남구", + "line2": "서울, 대한민국" + }, + "cta": { + "ready": "준비되셨나요?", + "start": "시작해보세요", + "question": "질문이 있으신가요?", + "subtitle": "프로젝트에 대해 상담해드립니다" + }, + "meta": { + "title": "문의하기", + "description": "프로젝트 문의나 상담이 필요하시면 언제든 연락주세요" + }, + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application" + }, + "about": { + "hero": { + "title": "스마트솔테크 소개", + "subtitle": "혁신과 기술로 미래를 만들어갑니다" + }, + "company": { + "title": "회사 정보", + "description1": "스마트솔테크는 2020년 설립된 기술 전문 회사로, 웹 개발, 모바일 앱 개발, UI/UX 디자인 분야에서 전문성을 인정받고 있습니다.", + "description2": "우리는 고객의 니즈를 정확히 파악하고, 최신 기술을 활용하여 혁신적인 솔루션을 제공합니다." + }, + "stats": { + "projects": "완료된 프로젝트", + "experience": "년간 경험", + "clients": "만족한 고객" + }, + "mission": { + "title": "우리의 미션", + "description": "기술을 통해 고객의 비즈니스 성장을 지원하고, 디지털 혁신을 이끌어나가는 것이 우리의 사명입니다." + }, + "values": { + "innovation": { + "title": "혁신", + "description": "최신 기술과 트렌드를 빠르게 적용하여 혁신적인 솔루션을 제공합니다." + }, + "quality": { + "title": "품질", + "description": "높은 품질의 코드와 디자인으로 안정적이고 우수한 제품을 개발합니다." + }, + "partnership": { + "title": "파트너십", + "description": "고객과의 긴밀한 협력을 통해 최상의 결과를 만들어냅니다." + }, + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "cta": { + "title": "함께 성장하겠습니다", + "subtitle": "당신의 아이디어를 현실로 만들어보세요", + "button": "Contact Us", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + }, + "meta": { + "title": "회사소개", + "description": "스마트솔테크는 혁신적인 기술로 고객의 비즈니스 성장을 지원하는 전문 개발 회사입니다" + }, + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + } + }, + "footer": { + "description": "Digital solution specialist leading innovation", + "links": { + "title": "빠른 링크", + "privacy": "개인정보처리방침", + "terms": "이용약관", + "sitemap": "사이트맵" + }, + "contact": { + "title": "연락처", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "서울특별시 강남구 테헤란로 123" + }, + "copyright": "© 2024 스마트솔테크. 모든 권리 보유.", + "company": { + "description": "혁신적인 기술로 당신의 비즈니스를 성장시키세요" + }, + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "privacy": "개인정보보호", + "terms": "이용약관", + "social": { + "follow": "팔로우하기" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "테마 전환" + }, + "language": { + "english": "영어", + "korean": "한국어", + "russian": "러시아어", + "kazakh": "카자흐어", + "ko": "한국어" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "자세히 보기" + }, + "meta": { + "description": "스마트솔테크 - 혁신적인 웹 개발, 모바일 앱 개발, UI/UX 디자인 서비스", + "keywords": "웹 개발, 모바일 앱, UI/UX 디자인, 한국", + "title": "스마트솔테크" + }, + "nav": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기" + }, + "admin": { + "login": "Admin Panel Login", + "dashboard": "Dashboard", + "title": "SmartSolTech Admin", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "description": "Digital solution specialist leading innovation", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "tagline": "Future begins here", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech" +} \ No newline at end of file diff --git a/.history/locales/ko_20251021211426.json b/.history/locales/ko_20251021211426.json new file mode 100644 index 0000000..50922bc --- /dev/null +++ b/.history/locales/ko_20251021211426.json @@ -0,0 +1,528 @@ +{ + "navigation": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "contact": "문의하기", + "calculator": "견적 계산기", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "스마트", + "solutions": "솔루션" + }, + "subtitle": "혁신적인 기술로 당신의 비즈니스를 성장시키세요", + "description": "혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비�니스 디지털 변혁을 이끕니다", + "cta": { + "start": "시작하기", + "portfolio": "포트폴리오 보기" + }, + "cta_primary": "프로젝트 시작", + "cta_secondary": "포트폴리오 보기" + }, + "services": { + "title": { + "our": "우리의", + "services": "서비스" + }, + "subtitle": "전문적인 개발 서비스로 당신의 아이디어를 현실로 만들어드립니다", + "description": "최첨단 기술과 창의적 아이디어로 완성된 디지털 솔루션", + "view_all": "모든 서비스 보기", + "web": { + "title": "웹 개발", + "description": "반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500,000부터" + }, + "mobile": { + "title": "모바일 앱", + "description": "iOS와 Android 네이티브 앱 개발", + "price": "₩1,000,000부터" + }, + "design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 인터페이스 및 경험 디자인", + "price": "₩300,000부터" + }, + "marketing": { + "title": "디지털 마케팅", + "description": "SEO, SNS 마케팅, 광고 운영", + "price": "₩200,000부터" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + }, + "title_highlight": "서비스", + "web_development": { + "title": "웹 개발", + "description": "현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500만원~" + }, + "mobile_app": { + "title": "모바일 앱", + "description": "iOS와 Android용 네이티브 및 크로스 플랫폼 앱", + "price": "₩800만원~" + }, + "ui_ux_design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 직관적이고 아름다운 인터페이스 디자인", + "price": "₩300만원~" + }, + "digital_marketing": { + "title": "디지털 마케팅", + "description": "SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅", + "price": "₩200만원~" + } + }, + "portfolio": { + "title": { + "recent": "최근", + "projects": "프로젝트", + "our": "우리의", + "portfolio": "포트폴리오" + }, + "subtitle": "성공적으로 완료한 프로젝트들을 확인해보세요", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "모든 프로젝트 보기", + "categories": { + "all": "전체", + "web": "웹 개발", + "mobile": "모바일 앱", + "uiux": "UI/UX 디자인" + }, + "project_details": "프로젝트 상세보기", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + }, + "title_highlight": "Projects", + "view_project": "프로젝트 보기" + }, + "calculator": { + "title": "견적 계산기", + "subtitle": "프로젝트 예상 비용을 확인해보세요", + "meta": { + "title": "견적 계산기", + "description": "프로젝트 예상 비용을 간편하게 계산해보세요" + }, + "cta": { + "title": "정확한 견적이 필요하신가요?", + "subtitle": "간단한 정보로 예상 비용을 확인하세요", + "button": "견적 계산하기" + }, + "step1": { + "title": "1단계: 서비스 선택", + "subtitle": "필요한 서비스를 선택해주세요" + }, + "step2": { + "title": "2단계: 옵션 선택", + "subtitle": "프로젝트 세부사항을 선택해주세요" + }, + "complexity": { + "title": "프로젝트 복잡도", + "simple": "단순", + "simple_desc": "기본적인 기능", + "medium": "보통", + "medium_desc": "표준 기능", + "complex": "복잡", + "complex_desc": "고급 기능" + }, + "timeline": { + "title": "개발 기간", + "standard": "표준", + "standard_desc": "일반적인 개발 기간", + "rush": "급행", + "rush_desc": "빠른 개발 (추가 비용)", + "extended": "여유", + "extended_desc": "충분한 개발 기간 (할인)" + }, + "result": { + "title": "견적 결과", + "subtitle": "프로젝트 예상 비용입니다", + "estimated_price": "예상 비용", + "price_note": "* 정확한 견적은 상담 후 확정됩니다", + "summary": "견적 요약", + "selected_services": "선택된 서비스", + "complexity": "복잡도", + "timeline": "개발 기간", + "get_quote": "정확한 견적 요청", + "recalculate": "다시 계산하기", + "contact_note": "더 정확한 견적을 위해 상담을 받아보세요" + }, + "next_step": "다음 단계", + "prev_step": "이전 단계", + "calculate": "견적 계산하기" + }, + "contact": { + "hero": { + "title": "문의하기", + "subtitle": "여러분의 아이디어를 현실로 만들어드립니다" + }, + "ready_title": "프로젝트를 시작할 준비가 되셨나요?", + "ready_description": "아이디어를 현실로 바꿔보세요. 전문가가 최고의 솔루션을 제공합니다.", + "form": { + "title": "프로젝트 문의", + "name": "이름", + "email": "이메일", + "phone": "전화번호", + "message": "메시지", + "submit": "문의 보내기", + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다", + "service": { + "title": "관심 서비스", + "select": "관심 서비스를 선택하세요", + "web": "웹 개발", + "mobile": "모바일 앱", + "design": "UI/UX 디자인", + "branding": "브랜딩", + "consulting": "컨설팅", + "other": "기타" + } + }, + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다" + }, + "info": { + "title": "연락처 정보" + }, + "phone": { + "title": "전화 문의", + "number": "+82-2-1234-5678", + "hours": "월-금 9:00-18:00" + }, + "email": { + "title": "이메일 문의", + "address": "info@smartsoltech.co.kr", + "response": "24시간 내 응답" + }, + "telegram": { + "title": "텔레그램", + "subtitle": "빠른 응답을 원하신다면" + }, + "address": { + "title": "사무실 주소", + "line1": "테헤란로 123, 강남구", + "line2": "서울, 대한민국" + }, + "cta": { + "ready": "준비되셨나요?", + "start": "시작해보세요", + "question": "질문이 있으신가요?", + "subtitle": "프로젝트에 대해 상담해드립니다" + }, + "meta": { + "title": "문의하기", + "description": "프로젝트 문의나 상담이 필요하시면 언제든 연락주세요" + }, + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application" + }, + "about": { + "hero": { + "title": "스마트솔테크 소개", + "subtitle": "혁신과 기술로 미래를 만들어갑니다" + }, + "company": { + "title": "회사 정보", + "description1": "스마트솔테크는 2020년 설립된 기술 전문 회사로, 웹 개발, 모바일 앱 개발, UI/UX 디자인 분야에서 전문성을 인정받고 있습니다.", + "description2": "우리는 고객의 니즈를 정확히 파악하고, 최신 기술을 활용하여 혁신적인 솔루션을 제공합니다." + }, + "stats": { + "projects": "완료된 프로젝트", + "experience": "년간 경험", + "clients": "만족한 고객" + }, + "mission": { + "title": "우리의 미션", + "description": "기술을 통해 고객의 비즈니스 성장을 지원하고, 디지털 혁신을 이끌어나가는 것이 우리의 사명입니다." + }, + "values": { + "innovation": { + "title": "혁신", + "description": "최신 기술과 트렌드를 빠르게 적용하여 혁신적인 솔루션을 제공합니다." + }, + "quality": { + "title": "품질", + "description": "높은 품질의 코드와 디자인으로 안정적이고 우수한 제품을 개발합니다." + }, + "partnership": { + "title": "파트너십", + "description": "고객과의 긴밀한 협력을 통해 최상의 결과를 만들어냅니다." + }, + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "cta": { + "title": "함께 성장하겠습니다", + "subtitle": "당신의 아이디어를 현실로 만들어보세요", + "button": "Contact Us", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + }, + "meta": { + "title": "회사소개", + "description": "스마트솔테크는 혁신적인 기술로 고객의 비즈니스 성장을 지원하는 전문 개발 회사입니다" + }, + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + } + }, + "footer": { + "description": "Digital solution specialist leading innovation", + "links": { + "title": "빠른 링크", + "privacy": "개인정보처리방침", + "terms": "이용약관", + "sitemap": "사이트맵" + }, + "contact": { + "title": "연락처", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "서울특별시 강남구 테헤란로 123" + }, + "copyright": "© 2024 스마트솔테크. 모든 권리 보유.", + "company": { + "description": "혁신적인 기술로 당신의 비즈니스를 성장시키세요" + }, + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "privacy": "개인정보보호", + "terms": "이용약관", + "social": { + "follow": "팔로우하기" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "테마 전환" + }, + "language": { + "english": "영어", + "korean": "한국어", + "russian": "러시아어", + "kazakh": "카자흐어", + "ko": "한국어" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "자세히 보기" + }, + "meta": { + "description": "스마트솔테크 - 혁신적인 웹 개발, 모바일 앱 개발, UI/UX 디자인 서비스", + "keywords": "웹 개발, 모바일 앱, UI/UX 디자인, 한국", + "title": "스마트솔테크" + }, + "nav": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기" + }, + "admin": { + "login": "Admin Panel Login", + "dashboard": "Dashboard", + "title": "SmartSolTech Admin", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "description": "Digital solution specialist leading innovation", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "tagline": "Future begins here", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + }, + "portfolio_page": { + "title": "우리의 포트폴리오", + "subtitle": "혁신적인 프로젝트와 창의적인 솔루션들을 만나보세요", + "categories": { + "all": "전체", + "web-development": "웹 개발", + "mobile-app": "모바일 앱", + "ui-ux-design": "UI/UX 디자인", + "branding": "브랜딩", + "marketing": "디지털 마케팅" + }, + "buttons": { + "details": "자세히 보기", + "projectDetails": "프로젝트 상세보기", + "loadMore": "더 많은 프로젝트 보기", + "contact": "프로젝트 문의하기", + "calculate": "비용 계산하기" + }, + "empty": { + "title": "아직 포트폴리오가 없습니다", + "subtitle": "곧 멋진 프로젝트들을 공개할 예정입니다!" + }, + "cta": { + "title": "다음 프로젝트의 주인공이 되어보세요", + "subtitle": "우리와 함께 혁신적인 디지털 솔루션을 만들어보세요" + }, + "labels": { + "featured": "FEATURED", + "views": "조회수", + "likes": "좋아요" + } + }, + "undefined - SmartSolTech": "undefined - SmartSolTech" +} \ No newline at end of file diff --git a/.history/locales/ko_20251021211513.json b/.history/locales/ko_20251021211513.json new file mode 100644 index 0000000..50922bc --- /dev/null +++ b/.history/locales/ko_20251021211513.json @@ -0,0 +1,528 @@ +{ + "navigation": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "contact": "문의하기", + "calculator": "견적 계산기", + "admin": "Admin", + "home - SmartSolTech": "navigation.home - SmartSolTech" + }, + "hero": { + "title": { + "smart": "스마트", + "solutions": "솔루션" + }, + "subtitle": "혁신적인 기술로 당신의 비즈니스를 성장시키세요", + "description": "혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비�니스 디지털 변혁을 이끕니다", + "cta": { + "start": "시작하기", + "portfolio": "포트폴리오 보기" + }, + "cta_primary": "프로젝트 시작", + "cta_secondary": "포트폴리오 보기" + }, + "services": { + "title": { + "our": "우리의", + "services": "서비스" + }, + "subtitle": "전문적인 개발 서비스로 당신의 아이디어를 현실로 만들어드립니다", + "description": "최첨단 기술과 창의적 아이디어로 완성된 디지털 솔루션", + "view_all": "모든 서비스 보기", + "web": { + "title": "웹 개발", + "description": "반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500,000부터" + }, + "mobile": { + "title": "모바일 앱", + "description": "iOS와 Android 네이티브 앱 개발", + "price": "₩1,000,000부터" + }, + "design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 인터페이스 및 경험 디자인", + "price": "₩300,000부터" + }, + "marketing": { + "title": "디지털 마케팅", + "description": "SEO, SNS 마케팅, 광고 운영", + "price": "₩200,000부터" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements and" + } + }, + "title_highlight": "서비스", + "web_development": { + "title": "웹 개발", + "description": "현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발", + "price": "₩500만원~" + }, + "mobile_app": { + "title": "모바일 앱", + "description": "iOS와 Android용 네이티브 및 크로스 플랫폼 앱", + "price": "₩800만원~" + }, + "ui_ux_design": { + "title": "UI/UX 디자인", + "description": "사용자 중심의 직관적이고 아름다운 인터페이스 디자인", + "price": "₩300만원~" + }, + "digital_marketing": { + "title": "디지털 마케팅", + "description": "SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅", + "price": "₩200만원~" + } + }, + "portfolio": { + "title": { + "recent": "최근", + "projects": "프로젝트", + "our": "우리의", + "portfolio": "포트폴리오" + }, + "subtitle": "성공적으로 완료한 프로젝트들을 확인해보세요", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "모든 프로젝트 보기", + "categories": { + "all": "전체", + "web": "웹 개발", + "mobile": "모바일 앱", + "uiux": "UI/UX 디자인" + }, + "project_details": "프로젝트 상세보기", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech", + "og_title": "Portfolio - SmartSolTech", + "og_description": "SmartSolTech's diverse projects and success stories" + }, + "title_highlight": "Projects", + "view_project": "프로젝트 보기" + }, + "calculator": { + "title": "견적 계산기", + "subtitle": "프로젝트 예상 비용을 확인해보세요", + "meta": { + "title": "견적 계산기", + "description": "프로젝트 예상 비용을 간편하게 계산해보세요" + }, + "cta": { + "title": "정확한 견적이 필요하신가요?", + "subtitle": "간단한 정보로 예상 비용을 확인하세요", + "button": "견적 계산하기" + }, + "step1": { + "title": "1단계: 서비스 선택", + "subtitle": "필요한 서비스를 선택해주세요" + }, + "step2": { + "title": "2단계: 옵션 선택", + "subtitle": "프로젝트 세부사항을 선택해주세요" + }, + "complexity": { + "title": "프로젝트 복잡도", + "simple": "단순", + "simple_desc": "기본적인 기능", + "medium": "보통", + "medium_desc": "표준 기능", + "complex": "복잡", + "complex_desc": "고급 기능" + }, + "timeline": { + "title": "개발 기간", + "standard": "표준", + "standard_desc": "일반적인 개발 기간", + "rush": "급행", + "rush_desc": "빠른 개발 (추가 비용)", + "extended": "여유", + "extended_desc": "충분한 개발 기간 (할인)" + }, + "result": { + "title": "견적 결과", + "subtitle": "프로젝트 예상 비용입니다", + "estimated_price": "예상 비용", + "price_note": "* 정확한 견적은 상담 후 확정됩니다", + "summary": "견적 요약", + "selected_services": "선택된 서비스", + "complexity": "복잡도", + "timeline": "개발 기간", + "get_quote": "정확한 견적 요청", + "recalculate": "다시 계산하기", + "contact_note": "더 정확한 견적을 위해 상담을 받아보세요" + }, + "next_step": "다음 단계", + "prev_step": "이전 단계", + "calculate": "견적 계산하기" + }, + "contact": { + "hero": { + "title": "문의하기", + "subtitle": "여러분의 아이디어를 현실로 만들어드립니다" + }, + "ready_title": "프로젝트를 시작할 준비가 되셨나요?", + "ready_description": "아이디어를 현실로 바꿔보세요. 전문가가 최고의 솔루션을 제공합니다.", + "form": { + "title": "프로젝트 문의", + "name": "이름", + "email": "이메일", + "phone": "전화번호", + "message": "메시지", + "submit": "문의 보내기", + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다", + "service": { + "title": "관심 서비스", + "select": "관심 서비스를 선택하세요", + "web": "웹 개발", + "mobile": "모바일 앱", + "design": "UI/UX 디자인", + "branding": "브랜딩", + "consulting": "컨설팅", + "other": "기타" + } + }, + "service_interest": "Service Interest", + "service_options": { + "select": "Select Service Interest", + "web_development": "Web Development", + "mobile_app": "Mobile App", + "ui_ux_design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + }, + "success": "문의가 성공적으로 전송되었습니다", + "error": "문의 전송 중 오류가 발생했습니다" + }, + "info": { + "title": "연락처 정보" + }, + "phone": { + "title": "전화 문의", + "number": "+82-2-1234-5678", + "hours": "월-금 9:00-18:00" + }, + "email": { + "title": "이메일 문의", + "address": "info@smartsoltech.co.kr", + "response": "24시간 내 응답" + }, + "telegram": { + "title": "텔레그램", + "subtitle": "빠른 응답을 원하신다면" + }, + "address": { + "title": "사무실 주소", + "line1": "테헤란로 123, 강남구", + "line2": "서울, 대한민국" + }, + "cta": { + "ready": "준비되셨나요?", + "start": "시작해보세요", + "question": "질문이 있으신가요?", + "subtitle": "프로젝트에 대해 상담해드립니다" + }, + "meta": { + "title": "문의하기", + "description": "프로젝트 문의나 상담이 필요하시면 언제든 연락주세요" + }, + "phone_consultation": "Phone Consultation", + "email_inquiry": "Email Inquiry", + "telegram_chat": "Telegram Chat", + "instant_response": "Instant response available", + "free_consultation": "Free Consultation Application" + }, + "about": { + "hero": { + "title": "스마트솔테크 소개", + "subtitle": "혁신과 기술로 미래를 만들어갑니다" + }, + "company": { + "title": "회사 정보", + "description1": "스마트솔테크는 2020년 설립된 기술 전문 회사로, 웹 개발, 모바일 앱 개발, UI/UX 디자인 분야에서 전문성을 인정받고 있습니다.", + "description2": "우리는 고객의 니즈를 정확히 파악하고, 최신 기술을 활용하여 혁신적인 솔루션을 제공합니다." + }, + "stats": { + "projects": "완료된 프로젝트", + "experience": "년간 경험", + "clients": "만족한 고객" + }, + "mission": { + "title": "우리의 미션", + "description": "기술을 통해 고객의 비즈니스 성장을 지원하고, 디지털 혁신을 이끌어나가는 것이 우리의 사명입니다." + }, + "values": { + "innovation": { + "title": "혁신", + "description": "최신 기술과 트렌드를 빠르게 적용하여 혁신적인 솔루션을 제공합니다." + }, + "quality": { + "title": "품질", + "description": "높은 품질의 코드와 디자인으로 안정적이고 우수한 제품을 개발합니다." + }, + "partnership": { + "title": "파트너십", + "description": "고객과의 긴밀한 협력을 통해 최상의 결과를 만들어냅니다." + }, + "title": "Core", + "title_highlight": "Values", + "description": "Core values pursued by SmartSolTech", + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "growth": { + "title": "Growth", + "description": "We grow together with customers and pursue continuous learning and development." + } + }, + "cta": { + "title": "함께 성장하겠습니다", + "subtitle": "당신의 아이디어를 현실로 만들어보세요", + "button": "Contact Us", + "description": "Take your business to the next level with SmartSolTech", + "partnership": "Partnership Inquiry", + "portfolio": "View Portfolio" + }, + "meta": { + "title": "회사소개", + "description": "스마트솔테크는 혁신적인 기술로 고객의 비즈니스 성장을 지원하는 전문 개발 회사입니다" + }, + "hero_title": "About", + "hero_highlight": "SmartSolTech", + "hero_description": "Digital solution specialist leading customer success with innovative technology", + "overview": { + "title": "Creating Future with Innovation and Creativity", + "description_1": "SmartSolTech is a digital solution specialist established in 2020, supporting customer business success with innovative technology and creative ideas in web development, mobile apps, and UI/UX design.", + "description_2": "We don't just provide technology, but understand customer goals and propose optimal solutions to become partners growing together.", + "stats": { + "projects": "100+", + "projects_label": "Completed Projects", + "clients": "50+", + "clients_label": "Satisfied Customers", + "experience": "4 years", + "experience_label": "Industry Experience" + }, + "mission": "Our Mission", + "mission_text": "Helping all businesses succeed in the digital age through technology", + "vision": "Our Vision", + "vision_text": "Growing as a global digital solution company representing Korea to lead digital innovation for customers worldwide" + }, + "team": { + "title": "Our", + "title_highlight": "Team", + "description": "Introducing the SmartSolTech team with expertise and passion" + }, + "tech_stack": { + "title": "Technology", + "title_highlight": "Stack", + "description": "We provide the best solutions with cutting-edge technology and proven tools", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Mobile" + } + }, + "footer": { + "description": "Digital solution specialist leading innovation", + "links": { + "title": "빠른 링크", + "privacy": "개인정보처리방침", + "terms": "이용약관", + "sitemap": "사이트맵" + }, + "contact": { + "title": "연락처", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "서울특별시 강남구 테헤란로 123" + }, + "copyright": "© 2024 스마트솔테크. 모든 권리 보유.", + "company": { + "description": "혁신적인 기술로 당신의 비즈니스를 성장시키세요" + }, + "quick_links": "Quick Links", + "services": "Services", + "contact_info": "Contact Information", + "follow_us": "Follow Us", + "rights": "All rights reserved.", + "privacy": "개인정보보호", + "terms": "이용약관", + "social": { + "follow": "팔로우하기" + } + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "테마 전환" + }, + "language": { + "english": "영어", + "korean": "한국어", + "russian": "러시아어", + "kazakh": "카자흐어", + "ko": "한국어" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "자세히 보기" + }, + "meta": { + "description": "스마트솔테크 - 혁신적인 웹 개발, 모바일 앱 개발, UI/UX 디자인 서비스", + "keywords": "웹 개발, 모바일 앱, UI/UX 디자인, 한국", + "title": "스마트솔테크" + }, + "nav": { + "home": "홈", + "about": "회사소개", + "services": "서비스", + "portfolio": "포트폴리오", + "calculator": "견적 계산기" + }, + "admin": { + "login": "Admin Panel Login", + "dashboard": "Dashboard", + "title": "SmartSolTech Admin", + "login_title": "Admin Panel Login", + "login_subtitle": "Login to your account to manage the site", + "login_button": "Login", + "email": "Email", + "password": "Password", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Enter password", + "back_to_site": "Back to site", + "dashboard_subtitle": "Overview of main site metrics", + "portfolio": "Portfolio", + "services": "Services", + "contacts": "Messages", + "settings": "Settings", + "users": "Users", + "logout": "Logout", + "view_site": "View site", + "view_all": "View all", + "portfolio_projects": "Projects", + "contact_messages": "Messages", + "recent_portfolio": "Recent projects", + "recent_contacts": "Recent messages", + "no_recent_portfolio": "No recent projects", + "no_recent_contacts": "No recent messages", + "quick_actions": "Quick actions", + "add_portfolio": "Add project", + "add_service": "Add service", + "site_settings": "Site settings", + "banner_editor": "Banner Editor", + "current_banner": "Current banner", + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio" + } + }, + "company": { + "name": "SmartSolTech", + "description": "Digital solution specialist leading innovation", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "full_name": "SmartSolTech - Innovative Technology Solutions", + "tagline": "Future begins here", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support", + "contact_us": "Contact us" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + }, + "portfolio_page": { + "title": "우리의 포트폴리오", + "subtitle": "혁신적인 프로젝트와 창의적인 솔루션들을 만나보세요", + "categories": { + "all": "전체", + "web-development": "웹 개발", + "mobile-app": "모바일 앱", + "ui-ux-design": "UI/UX 디자인", + "branding": "브랜딩", + "marketing": "디지털 마케팅" + }, + "buttons": { + "details": "자세히 보기", + "projectDetails": "프로젝트 상세보기", + "loadMore": "더 많은 프로젝트 보기", + "contact": "프로젝트 문의하기", + "calculate": "비용 계산하기" + }, + "empty": { + "title": "아직 포트폴리오가 없습니다", + "subtitle": "곧 멋진 프로젝트들을 공개할 예정입니다!" + }, + "cta": { + "title": "다음 프로젝트의 주인공이 되어보세요", + "subtitle": "우리와 함께 혁신적인 디지털 솔루션을 만들어보세요" + }, + "labels": { + "featured": "FEATURED", + "views": "조회수", + "likes": "좋아요" + } + }, + "undefined - SmartSolTech": "undefined - SmartSolTech" +} \ No newline at end of file diff --git a/.history/locales/ko_20251021211904.json b/.history/locales/ko_20251021211904.json new file mode 100644 index 0000000..ea46c6e --- /dev/null +++ b/.history/locales/ko_20251021211904.json @@ -0,0 +1,331 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "subtitle": "Professional development services to turn your ideas into reality", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "view_all": "View All Services", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects" + }, + "subtitle": "Check out successfully completed projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "project_details": "Project Details", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + } + }, + "contact": { + "hero": { + "title": "Contact Us", + "subtitle": "We're here to help bring your ideas to life" + }, + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "form": { + "title": "Project Inquiry", + "name": "Name", + "email": "Email", + "phone": "Phone", + "message": "Message", + "submit": "Send Inquiry", + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry", + "service": { + "title": "Service Interest", + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + } + }, + "info": { + "title": "Contact Information" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678", + "hours": "Mon-Fri 9:00-18:00" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr", + "response": "Response within 24 hours" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "address": { + "title": "Office Address", + "line1": "123 Teheran-ro, Gangnam-gu", + "line2": "Seoul, South Korea" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + }, + "values": { + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "partnership": { + "title": "Partnership", + "description": "We create the best results through close communication and collaboration with customers." + } + }, + "cta": { + "title": "We'll Grow Together", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + } + }, + "footer": { + "description": "Digital solution specialist leading innovation", + "links": { + "title": "Quick Links" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved." + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "dashboard": "Dashboard", + "title": "SmartSolTech Admin" + }, + "company": { + "name": "SmartSolTech", + "description": "Digital solution specialist leading innovation", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678" + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + }, + "portfolio_page": { + "title": "우리의 포트폴리오", + "subtitle": "혁신적인 프로젝트와 창의적인 솔루션들을 만나보세요", + "categories": { + "all": "전체", + "web-development": "웹 개발", + "mobile-app": "모바일 앱", + "ui-ux-design": "UI/UX 디자인", + "branding": "브랜딩", + "marketing": "디지털 마케팅" + }, + "buttons": { + "details": "자세히 보기", + "projectDetails": "프로젝트 상세보기", + "loadMore": "더 많은 프로젝트 보기", + "contact": "프로젝트 문의하기", + "calculate": "비용 계산하기" + }, + "empty": { + "title": "아직 포트폴리오가 없습니다", + "subtitle": "곧 멋진 프로젝트들을 공개할 예정입니다!" + }, + "cta": { + "title": "다음 프로젝트의 주인공이 되어보세요", + "subtitle": "우리와 함께 혁신적인 디지털 솔루션을 만들어보세요" + }, + "labels": { + "featured": "추천", + "views": "조회수", + "likes": "좋아요" + } + } +} \ No newline at end of file diff --git a/.history/locales/ko_20251021211925.json b/.history/locales/ko_20251021211925.json new file mode 100644 index 0000000..ea46c6e --- /dev/null +++ b/.history/locales/ko_20251021211925.json @@ -0,0 +1,331 @@ +{ + "navigation": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator", + "admin": "Admin" + }, + "hero": { + "title": { + "smart": "Smart", + "solutions": "Solutions" + }, + "subtitle": "Grow your business with innovative technology", + "description": "Innovative web development, mobile apps, UI/UX design leading your business digital transformation", + "cta": { + "start": "Get Started", + "portfolio": "View Portfolio" + } + }, + "services": { + "title": { + "our": "Our", + "services": "Services" + }, + "subtitle": "Professional development services to turn your ideas into reality", + "description": "Digital solutions completed with cutting-edge technology and creative ideas", + "view_all": "View All Services", + "web": { + "title": "Web Development", + "description": "Responsive websites and web application development", + "price": "From $500" + }, + "mobile": { + "title": "Mobile Apps", + "description": "iOS and Android native app development", + "price": "From $1,000" + }, + "design": { + "title": "UI/UX Design", + "description": "User-centered interface and experience design", + "price": "From $300" + }, + "marketing": { + "title": "Digital Marketing", + "description": "SEO, social media marketing, advertising management", + "price": "From $200" + }, + "meta": { + "title": "Services", + "description": "Check out SmartSolTech's professional services. Web development, mobile apps, UI/UX design, digital marketing and other technology solutions.", + "keywords": "web development, mobile apps, UI/UX design, digital marketing, technology solutions, SmartSolTech" + }, + "hero": { + "title": "Our", + "title_highlight": "Services", + "subtitle": "Support business growth with innovative technology" + }, + "cards": { + "starting_price": "Starting Price", + "consultation": "consultation", + "contact": "Contact", + "calculate_cost": "Calculate Cost", + "popular": "Popular", + "coming_soon": "Services Coming Soon", + "coming_soon_desc": "We'll soon offer various services!" + }, + "process": { + "title": "Project Implementation Process", + "subtitle": "We conduct projects with systematic and professional processes", + "consultation": { + "title": "Consultation and Planning", + "description": "Accurately understand customer requirements" + } + } + }, + "portfolio": { + "title": { + "recent": "Recent", + "projects": "Projects" + }, + "subtitle": "Check out successfully completed projects", + "description": "Check out the projects completed for customer success", + "view_details": "View Details", + "view_all": "View All Portfolio", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "project_details": "Project Details", + "default": { + "ecommerce": "E-commerce", + "title": "E-commerce Platform", + "description": "Modern online commerce solution with intuitive interface" + }, + "meta": { + "title": "Portfolio", + "description": "Check out SmartSolTech's diverse projects and success stories. Web development, mobile apps, UI/UX design portfolio.", + "keywords": "portfolio, web development, mobile apps, UI/UX design, projects, SmartSolTech" + } + }, + "calculator": { + "title": "Project Cost Calculator", + "subtitle": "Select your desired services and requirements to get accurate cost estimates in real time", + "meta": { + "title": "Project Cost Calculator", + "description": "Calculate the cost of your web development, mobile app, or design project with our interactive calculator" + }, + "cta": { + "title": "Check Your Project Estimate", + "subtitle": "Select your desired services and requirements to calculate costs in real time", + "button": "Use Cost Calculator" + } + }, + "contact": { + "hero": { + "title": "Contact Us", + "subtitle": "We're here to help bring your ideas to life" + }, + "ready_title": "Ready to Start Your Project?", + "ready_description": "Turn your ideas into reality. Experts provide the best solutions.", + "form": { + "title": "Project Inquiry", + "name": "Name", + "email": "Email", + "phone": "Phone", + "message": "Message", + "submit": "Send Inquiry", + "success": "Inquiry sent successfully", + "error": "Error occurred while sending inquiry", + "service": { + "title": "Service Interest", + "select": "Select service of interest", + "web": "Web Development", + "mobile": "Mobile App", + "design": "UI/UX Design", + "branding": "Branding", + "consulting": "Consulting", + "other": "Other" + } + }, + "info": { + "title": "Contact Information" + }, + "phone": { + "title": "Phone Inquiry", + "number": "+82-2-1234-5678", + "hours": "Mon-Fri 9:00-18:00" + }, + "email": { + "title": "Email Inquiry", + "address": "info@smartsoltech.co.kr", + "response": "Response within 24 hours" + }, + "telegram": { + "title": "Telegram", + "subtitle": "For quick response" + }, + "address": { + "title": "Office Address", + "line1": "123 Teheran-ro, Gangnam-gu", + "line2": "Seoul, South Korea" + }, + "cta": { + "ready": "Ready?", + "start": "Get Started", + "question": "Have questions?", + "subtitle": "We provide consultation on projects" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + }, + "values": { + "innovation": { + "title": "Innovation", + "description": "We provide innovative solutions through continuous R&D and adoption of cutting-edge technology." + }, + "quality": { + "title": "Quality", + "description": "We maintain high quality standards and provide high-quality products that customers can be satisfied with." + }, + "partnership": { + "title": "Partnership", + "description": "We create the best results through close communication and collaboration with customers." + } + }, + "cta": { + "title": "We'll Grow Together", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + } + }, + "footer": { + "description": "Digital solution specialist leading innovation", + "links": { + "title": "Quick Links" + }, + "contact": { + "title": "Contact", + "email": "info@smartsoltech.co.kr", + "phone": "+82-2-1234-5678", + "address": "123 Teheran-ro, Gangnam-gu, Seoul" + }, + "copyright": "© 2024 SmartSolTech. All rights reserved." + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "toggle": "Toggle Theme" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша" + }, + "common": { + "loading": "Loading...", + "error": "Error occurred", + "success": "Success", + "view_more": "View More", + "back": "Back", + "next": "Next", + "previous": "Previous", + "view_details": "View Details" + }, + "meta": { + "description": "SmartSolTech - Innovative web development, mobile app development, UI/UX design services", + "keywords": "web development, mobile apps, UI/UX design, Korea", + "title": "SmartSolTech" + }, + "nav": { + "home": "Home", + "about": "About", + "services": "Services", + "portfolio": "Portfolio", + "calculator": "Calculator" + }, + "admin": { + "login": "Admin Panel Login", + "dashboard": "Dashboard", + "title": "SmartSolTech Admin" + }, + "company": { + "name": "SmartSolTech", + "description": "Digital solution specialist leading innovation", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678" + }, + "errors": { + "page_not_found": "Page not found", + "error_occurred": "Error occurred", + "title": "Error - SmartSolTech", + "default_title": "An Error Occurred", + "default_message": "A problem occurred while processing the request.", + "back_home": "Back to Home", + "go_back": "Go Back", + "need_help": "Need Help?", + "help_message": "If the problem persists, please contact us anytime.", + "contact_support": "Contact Support" + }, + "pages": { + "home": "Home page", + "about": "About us", + "services": "Services", + "portfolio": "Portfolio", + "contact": "Contact", + "calculator": "Calculator" + }, + "portfolio_page": { + "title": "우리의 포트폴리오", + "subtitle": "혁신적인 프로젝트와 창의적인 솔루션들을 만나보세요", + "categories": { + "all": "전체", + "web-development": "웹 개발", + "mobile-app": "모바일 앱", + "ui-ux-design": "UI/UX 디자인", + "branding": "브랜딩", + "marketing": "디지털 마케팅" + }, + "buttons": { + "details": "자세히 보기", + "projectDetails": "프로젝트 상세보기", + "loadMore": "더 많은 프로젝트 보기", + "contact": "프로젝트 문의하기", + "calculate": "비용 계산하기" + }, + "empty": { + "title": "아직 포트폴리오가 없습니다", + "subtitle": "곧 멋진 프로젝트들을 공개할 예정입니다!" + }, + "cta": { + "title": "다음 프로젝트의 주인공이 되어보세요", + "subtitle": "우리와 함께 혁신적인 디지털 솔루션을 만들어보세요" + }, + "labels": { + "featured": "추천", + "views": "조회수", + "likes": "좋아요" + } + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251019203653.json b/.history/locales/ru_20251019203653.json new file mode 100644 index 0000000..56261d2 --- /dev/null +++ b/.history/locales/ru_20251019203653.json @@ -0,0 +1,318 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "hero.title.solutions" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "hero.cta.start", + "portfolio": "hero.cta.portfolio" + } + }, + "services": { + "title": { + "our": "services.title.our", + "services": "services.title.services" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "services.subtitle", + "web": { + "title": "services.web.title", + "description": "services.web.description", + "price": "services.web.price" + }, + "mobile": { + "title": "services.mobile.title", + "description": "services.mobile.description", + "price": "services.mobile.price" + }, + "design": { + "title": "services.design.title", + "description": "services.design.description", + "price": "services.design.price" + }, + "marketing": { + "title": "services.marketing.title", + "description": "services.marketing.description", + "price": "services.marketing.price" + } + }, + "portfolio": { + "title": { + "recent": "portfolio.title.recent", + "projects": "portfolio.title.projects" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "portfolio.subtitle" + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "contact.form.title", + "service": { + "select": "contact.form.service.select", + "web": "contact.form.service.web", + "mobile": "contact.form.service.mobile", + "design": "contact.form.service.design", + "branding": "contact.form.service.branding", + "consulting": "contact.form.service.consulting", + "other": "contact.form.service.other" + }, + "success": "contact.form.success", + "error": "contact.form.error" + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020044247.json b/.history/locales/ru_20251020044247.json new file mode 100644 index 0000000..f75e8b5 --- /dev/null +++ b/.history/locales/ru_20251020044247.json @@ -0,0 +1,349 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "hero.title.solutions" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "hero.cta.start", + "portfolio": "hero.cta.portfolio" + } + }, + "services": { + "title": { + "our": "services.title.our", + "services": "services.title.services" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "services.subtitle", + "web": { + "title": "services.web.title", + "description": "services.web.description", + "price": "services.web.price" + }, + "mobile": { + "title": "services.mobile.title", + "description": "services.mobile.description", + "price": "services.mobile.price" + }, + "design": { + "title": "services.design.title", + "description": "services.design.description", + "price": "services.design.price" + }, + "marketing": { + "title": "services.marketing.title", + "description": "services.marketing.description", + "price": "services.marketing.price" + } + }, + "portfolio": { + "title": { + "recent": "portfolio.title.recent", + "projects": "portfolio.title.projects" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "portfolio.subtitle" + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "contact.form.title", + "service": { + "select": "contact.form.service.select", + "web": "contact.form.service.web", + "mobile": "contact.form.service.mobile", + "design": "contact.form.service.design", + "branding": "contact.form.service.branding", + "consulting": "contact.form.service.consulting", + "other": "contact.form.service.other" + }, + "success": "contact.form.success", + "error": "contact.form.error" + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020044352.json b/.history/locales/ru_20251020044352.json new file mode 100644 index 0000000..f75e8b5 --- /dev/null +++ b/.history/locales/ru_20251020044352.json @@ -0,0 +1,349 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "hero.title.solutions" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "hero.cta.start", + "portfolio": "hero.cta.portfolio" + } + }, + "services": { + "title": { + "our": "services.title.our", + "services": "services.title.services" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "services.subtitle", + "web": { + "title": "services.web.title", + "description": "services.web.description", + "price": "services.web.price" + }, + "mobile": { + "title": "services.mobile.title", + "description": "services.mobile.description", + "price": "services.mobile.price" + }, + "design": { + "title": "services.design.title", + "description": "services.design.description", + "price": "services.design.price" + }, + "marketing": { + "title": "services.marketing.title", + "description": "services.marketing.description", + "price": "services.marketing.price" + } + }, + "portfolio": { + "title": { + "recent": "portfolio.title.recent", + "projects": "portfolio.title.projects" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "portfolio.subtitle" + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "contact.form.title", + "service": { + "select": "contact.form.service.select", + "web": "contact.form.service.web", + "mobile": "contact.form.service.mobile", + "design": "contact.form.service.design", + "branding": "contact.form.service.branding", + "consulting": "contact.form.service.consulting", + "other": "contact.form.service.other" + }, + "success": "contact.form.success", + "error": "contact.form.error" + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020225647.json b/.history/locales/ru_20251020225647.json new file mode 100644 index 0000000..7d907d3 --- /dev/null +++ b/.history/locales/ru_20251020225647.json @@ -0,0 +1,349 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "hero.cta.start", + "portfolio": "hero.cta.portfolio" + } + }, + "services": { + "title": { + "our": "services.title.our", + "services": "services.title.services" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "services.subtitle", + "web": { + "title": "services.web.title", + "description": "services.web.description", + "price": "services.web.price" + }, + "mobile": { + "title": "services.mobile.title", + "description": "services.mobile.description", + "price": "services.mobile.price" + }, + "design": { + "title": "services.design.title", + "description": "services.design.description", + "price": "services.design.price" + }, + "marketing": { + "title": "services.marketing.title", + "description": "services.marketing.description", + "price": "services.marketing.price" + } + }, + "portfolio": { + "title": { + "recent": "portfolio.title.recent", + "projects": "portfolio.title.projects" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "portfolio.subtitle" + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "contact.form.title", + "service": { + "select": "contact.form.service.select", + "web": "contact.form.service.web", + "mobile": "contact.form.service.mobile", + "design": "contact.form.service.design", + "branding": "contact.form.service.branding", + "consulting": "contact.form.service.consulting", + "other": "contact.form.service.other" + }, + "success": "contact.form.success", + "error": "contact.form.error" + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020230125.json b/.history/locales/ru_20251020230125.json new file mode 100644 index 0000000..5328610 --- /dev/null +++ b/.history/locales/ru_20251020230125.json @@ -0,0 +1,349 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "services.title.our", + "services": "services.title.services" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "services.subtitle", + "web": { + "title": "services.web.title", + "description": "services.web.description", + "price": "services.web.price" + }, + "mobile": { + "title": "services.mobile.title", + "description": "services.mobile.description", + "price": "services.mobile.price" + }, + "design": { + "title": "services.design.title", + "description": "services.design.description", + "price": "services.design.price" + }, + "marketing": { + "title": "services.marketing.title", + "description": "services.marketing.description", + "price": "services.marketing.price" + } + }, + "portfolio": { + "title": { + "recent": "portfolio.title.recent", + "projects": "portfolio.title.projects" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "portfolio.subtitle" + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "contact.form.title", + "service": { + "select": "contact.form.service.select", + "web": "contact.form.service.web", + "mobile": "contact.form.service.mobile", + "design": "contact.form.service.design", + "branding": "contact.form.service.branding", + "consulting": "contact.form.service.consulting", + "other": "contact.form.service.other" + }, + "success": "contact.form.success", + "error": "contact.form.error" + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020230133.json b/.history/locales/ru_20251020230133.json new file mode 100644 index 0000000..c636666 --- /dev/null +++ b/.history/locales/ru_20251020230133.json @@ -0,0 +1,349 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "services.subtitle", + "web": { + "title": "services.web.title", + "description": "services.web.description", + "price": "services.web.price" + }, + "mobile": { + "title": "services.mobile.title", + "description": "services.mobile.description", + "price": "services.mobile.price" + }, + "design": { + "title": "services.design.title", + "description": "services.design.description", + "price": "services.design.price" + }, + "marketing": { + "title": "services.marketing.title", + "description": "services.marketing.description", + "price": "services.marketing.price" + } + }, + "portfolio": { + "title": { + "recent": "portfolio.title.recent", + "projects": "portfolio.title.projects" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "portfolio.subtitle" + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "contact.form.title", + "service": { + "select": "contact.form.service.select", + "web": "contact.form.service.web", + "mobile": "contact.form.service.mobile", + "design": "contact.form.service.design", + "branding": "contact.form.service.branding", + "consulting": "contact.form.service.consulting", + "other": "contact.form.service.other" + }, + "success": "contact.form.success", + "error": "contact.form.error" + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020230144.json b/.history/locales/ru_20251020230144.json new file mode 100644 index 0000000..5facc8d --- /dev/null +++ b/.history/locales/ru_20251020230144.json @@ -0,0 +1,349 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "portfolio.title.recent", + "projects": "portfolio.title.projects" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "portfolio.subtitle" + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "contact.form.title", + "service": { + "select": "contact.form.service.select", + "web": "contact.form.service.web", + "mobile": "contact.form.service.mobile", + "design": "contact.form.service.design", + "branding": "contact.form.service.branding", + "consulting": "contact.form.service.consulting", + "other": "contact.form.service.other" + }, + "success": "contact.form.success", + "error": "contact.form.error" + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020230151.json b/.history/locales/ru_20251020230151.json new file mode 100644 index 0000000..0e75ac0 --- /dev/null +++ b/.history/locales/ru_20251020230151.json @@ -0,0 +1,349 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "portfolio.subtitle" + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "contact.form.title", + "service": { + "select": "contact.form.service.select", + "web": "contact.form.service.web", + "mobile": "contact.form.service.mobile", + "design": "contact.form.service.design", + "branding": "contact.form.service.branding", + "consulting": "contact.form.service.consulting", + "other": "contact.form.service.other" + }, + "success": "contact.form.success", + "error": "contact.form.error" + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020230159.json b/.history/locales/ru_20251020230159.json new file mode 100644 index 0000000..844651b --- /dev/null +++ b/.history/locales/ru_20251020230159.json @@ -0,0 +1,354 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "contact.form.title", + "service": { + "select": "contact.form.service.select", + "web": "contact.form.service.web", + "mobile": "contact.form.service.mobile", + "design": "contact.form.service.design", + "branding": "contact.form.service.branding", + "consulting": "contact.form.service.consulting", + "other": "contact.form.service.other" + }, + "success": "contact.form.success", + "error": "contact.form.error" + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020230209.json b/.history/locales/ru_20251020230209.json new file mode 100644 index 0000000..1420f03 --- /dev/null +++ b/.history/locales/ru_20251020230209.json @@ -0,0 +1,354 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "contact.cta.ready", + "start": "contact.cta.start", + "question": "contact.cta.question", + "subtitle": "contact.cta.subtitle" + }, + "phone": { + "title": "contact.phone.title", + "number": "contact.phone.number" + }, + "email": { + "title": "contact.email.title", + "address": "contact.email.address" + }, + "telegram": { + "title": "contact.telegram.title", + "subtitle": "contact.telegram.subtitle" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020230219.json b/.history/locales/ru_20251020230219.json new file mode 100644 index 0000000..0fbd309 --- /dev/null +++ b/.history/locales/ru_20251020230219.json @@ -0,0 +1,354 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "common.view_details" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020230226.json b/.history/locales/ru_20251020230226.json new file mode 100644 index 0000000..045275a --- /dev/null +++ b/.history/locales/ru_20251020230226.json @@ -0,0 +1,354 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "meta.description", + "keywords": "meta.keywords", + "title": "meta.title" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020230234.json b/.history/locales/ru_20251020230234.json new file mode 100644 index 0000000..fce0c92 --- /dev/null +++ b/.history/locales/ru_20251020230234.json @@ -0,0 +1,354 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020230247.json b/.history/locales/ru_20251020230247.json new file mode 100644 index 0000000..fce0c92 --- /dev/null +++ b/.history/locales/ru_20251020230247.json @@ -0,0 +1,354 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "language.ko" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020230428.json b/.history/locales/ru_20251020230428.json new file mode 100644 index 0000000..c1dd95d --- /dev/null +++ b/.history/locales/ru_20251020230428.json @@ -0,0 +1,357 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251020230440.json b/.history/locales/ru_20251020230440.json new file mode 100644 index 0000000..c1dd95d --- /dev/null +++ b/.history/locales/ru_20251020230440.json @@ -0,0 +1,357 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021040800.json b/.history/locales/ru_20251021040800.json new file mode 100644 index 0000000..486afea --- /dev/null +++ b/.history/locales/ru_20251021040800.json @@ -0,0 +1,357 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаунт для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021175542.json b/.history/locales/ru_20251021175542.json new file mode 100644 index 0000000..ef7b41d --- /dev/null +++ b/.history/locales/ru_20251021175542.json @@ -0,0 +1,395 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021183217.json b/.history/locales/ru_20251021183217.json new file mode 100644 index 0000000..ef7b41d --- /dev/null +++ b/.history/locales/ru_20251021183217.json @@ -0,0 +1,395 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021183706.json b/.history/locales/ru_20251021183706.json new file mode 100644 index 0000000..78efed5 --- /dev/null +++ b/.history/locales/ru_20251021183706.json @@ -0,0 +1,402 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + }, + "portfolio": { + "default": { + "ecommerce": "Электронная коммерция", + "title": "Интернет-магазин", + "description": "Современная платформа электронной коммерции с интуитивным интерфейсом" + } + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021183802.json b/.history/locales/ru_20251021183802.json new file mode 100644 index 0000000..b28e6c4 --- /dev/null +++ b/.history/locales/ru_20251021183802.json @@ -0,0 +1,395 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021184019.json b/.history/locales/ru_20251021184019.json new file mode 100644 index 0000000..788e599 --- /dev/null +++ b/.history/locales/ru_20251021184019.json @@ -0,0 +1,403 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021184027.json b/.history/locales/ru_20251021184027.json new file mode 100644 index 0000000..788e599 --- /dev/null +++ b/.history/locales/ru_20251021184027.json @@ -0,0 +1,403 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021184143.json b/.history/locales/ru_20251021184143.json new file mode 100644 index 0000000..f60ff92 --- /dev/null +++ b/.history/locales/ru_20251021184143.json @@ -0,0 +1,408 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + }, + "meta": { + "title": "Услуги", + "description": "Ознакомьтесь с профессиональными услугами SmartSolTech. Веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг и другие технологические решения.", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг, технологические решения, SmartSolTech" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021184308.json b/.history/locales/ru_20251021184308.json new file mode 100644 index 0000000..f60ff92 --- /dev/null +++ b/.history/locales/ru_20251021184308.json @@ -0,0 +1,408 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + }, + "meta": { + "title": "Услуги", + "description": "Ознакомьтесь с профессиональными услугами SmartSolTech. Веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг и другие технологические решения.", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг, технологические решения, SmartSolTech" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021184331.json b/.history/locales/ru_20251021184331.json new file mode 100644 index 0000000..9634358 --- /dev/null +++ b/.history/locales/ru_20251021184331.json @@ -0,0 +1,430 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + }, + "meta": { + "title": "Услуги", + "description": "Ознакомьтесь с профессиональными услугами SmartSolTech. Веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг и другие технологические решения.", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг, технологические решения, SmartSolTech" + }, + "hero": { + "title": "Наши", + "title_highlight": "Услуги", + "subtitle": "Поддерживаем рост бизнеса с помощью инновационных технологий" + }, + "cards": { + "starting_price": "Стартовая цена", + "consultation": "консультация", + "contact": "Связаться", + "calculate_cost": "Рассчитать стоимость", + "popular": "Популярно", + "coming_soon": "Услуги готовятся", + "coming_soon_desc": "Скоро мы предложим разнообразные услуги!" + }, + "process": { + "title": "Процесс реализации проекта", + "subtitle": "Мы ведем проекты с помощью систематических и профессиональных процессов", + "consultation": { + "title": "Консультация и планирование", + "description": "Точно понимаем требования клиента и" + } + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021184333.json b/.history/locales/ru_20251021184333.json new file mode 100644 index 0000000..9634358 --- /dev/null +++ b/.history/locales/ru_20251021184333.json @@ -0,0 +1,430 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + }, + "meta": { + "title": "Услуги", + "description": "Ознакомьтесь с профессиональными услугами SmartSolTech. Веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг и другие технологические решения.", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг, технологические решения, SmartSolTech" + }, + "hero": { + "title": "Наши", + "title_highlight": "Услуги", + "subtitle": "Поддерживаем рост бизнеса с помощью инновационных технологий" + }, + "cards": { + "starting_price": "Стартовая цена", + "consultation": "консультация", + "contact": "Связаться", + "calculate_cost": "Рассчитать стоимость", + "popular": "Популярно", + "coming_soon": "Услуги готовятся", + "coming_soon_desc": "Скоро мы предложим разнообразные услуги!" + }, + "process": { + "title": "Процесс реализации проекта", + "subtitle": "Мы ведем проекты с помощью систематических и профессиональных процессов", + "consultation": { + "title": "Консультация и планирование", + "description": "Точно понимаем требования клиента и" + } + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021184528.json b/.history/locales/ru_20251021184528.json new file mode 100644 index 0000000..c4e67e7 --- /dev/null +++ b/.history/locales/ru_20251021184528.json @@ -0,0 +1,437 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + }, + "meta": { + "title": "Услуги", + "description": "Ознакомьтесь с профессиональными услугами SmartSolTech. Веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг и другие технологические решения.", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг, технологические решения, SmartSolTech" + }, + "hero": { + "title": "Наши", + "title_highlight": "Услуги", + "subtitle": "Поддерживаем рост бизнеса с помощью инновационных технологий" + }, + "cards": { + "starting_price": "Стартовая цена", + "consultation": "консультация", + "contact": "Связаться", + "calculate_cost": "Рассчитать стоимость", + "popular": "Популярно", + "coming_soon": "Услуги готовятся", + "coming_soon_desc": "Скоро мы предложим разнообразные услуги!" + }, + "process": { + "title": "Процесс реализации проекта", + "subtitle": "Мы ведем проекты с помощью систематических и профессиональных процессов", + "consultation": { + "title": "Консультация и планирование", + "description": "Точно понимаем требования клиента и" + } + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + }, + "meta": { + "title": "Портфолио", + "description": "Ознакомьтесь с разнообразными проектами и историями успеха SmartSolTech. Портфолио веб-разработки, мобильных приложений, UI/UX дизайна.", + "keywords": "портфолио, веб-разработка, мобильные приложения, UI/UX дизайн, проекты, SmartSolTech", + "og_title": "Портфолио - SmartSolTech", + "og_description": "Разнообразные проекты и истории успеха SmartSolTech" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021184547.json b/.history/locales/ru_20251021184547.json new file mode 100644 index 0000000..c4e67e7 --- /dev/null +++ b/.history/locales/ru_20251021184547.json @@ -0,0 +1,437 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + }, + "meta": { + "title": "Услуги", + "description": "Ознакомьтесь с профессиональными услугами SmartSolTech. Веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг и другие технологические решения.", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг, технологические решения, SmartSolTech" + }, + "hero": { + "title": "Наши", + "title_highlight": "Услуги", + "subtitle": "Поддерживаем рост бизнеса с помощью инновационных технологий" + }, + "cards": { + "starting_price": "Стартовая цена", + "consultation": "консультация", + "contact": "Связаться", + "calculate_cost": "Рассчитать стоимость", + "popular": "Популярно", + "coming_soon": "Услуги готовятся", + "coming_soon_desc": "Скоро мы предложим разнообразные услуги!" + }, + "process": { + "title": "Процесс реализации проекта", + "subtitle": "Мы ведем проекты с помощью систематических и профессиональных процессов", + "consultation": { + "title": "Консультация и планирование", + "description": "Точно понимаем требования клиента и" + } + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + }, + "meta": { + "title": "Портфолио", + "description": "Ознакомьтесь с разнообразными проектами и историями успеха SmartSolTech. Портфолио веб-разработки, мобильных приложений, UI/UX дизайна.", + "keywords": "портфолио, веб-разработка, мобильные приложения, UI/UX дизайн, проекты, SmartSolTech", + "og_title": "Портфолио - SmartSolTech", + "og_description": "Разнообразные проекты и истории успеха SmartSolTech" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms" + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021210015.json b/.history/locales/ru_20251021210015.json new file mode 100644 index 0000000..3608079 --- /dev/null +++ b/.history/locales/ru_20251021210015.json @@ -0,0 +1,484 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + }, + "meta": { + "title": "Услуги", + "description": "Ознакомьтесь с профессиональными услугами SmartSolTech. Веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг и другие технологические решения.", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг, технологические решения, SmartSolTech" + }, + "hero": { + "title": "Наши", + "title_highlight": "Услуги", + "subtitle": "Поддерживаем рост бизнеса с помощью инновационных технологий" + }, + "cards": { + "starting_price": "Стартовая цена", + "consultation": "консультация", + "contact": "Связаться", + "calculate_cost": "Рассчитать стоимость", + "popular": "Популярно", + "coming_soon": "Услуги готовятся", + "coming_soon_desc": "Скоро мы предложим разнообразные услуги!" + }, + "process": { + "title": "Процесс реализации проекта", + "subtitle": "Мы ведем проекты с помощью систематических и профессиональных процессов", + "consultation": { + "title": "Консультация и планирование", + "description": "Точно понимаем требования клиента и" + } + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_project": "View Project", + "categories": { + "all": "All", + "web": "Web Development", + "mobile": "Mobile Apps", + "uiux": "UI/UX Design" + }, + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + }, + "meta": { + "title": "Портфолио", + "description": "Ознакомьтесь с разнообразными проектами и историями успеха SmartSolTech. Портфолио веб-разработки, мобильных приложений, UI/UX дизайна.", + "keywords": "портфолио, веб-разработка, мобильные приложения, UI/UX дизайн, проекты, SmartSolTech", + "og_title": "Портфолио - SmartSolTech", + "og_description": "Разнообразные проекты и истории успеха SmartSolTech" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "hero": { + "title": "Свяжитесь с нами", + "subtitle": "Мы здесь, чтобы помочь воплотить ваши идеи в жизнь" + }, + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + }, + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "sitemap": "Sitemap" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms", + "social": { + "follow": "Follow Us" + } + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021210040.json b/.history/locales/ru_20251021210040.json new file mode 100644 index 0000000..753c566 --- /dev/null +++ b/.history/locales/ru_20251021210040.json @@ -0,0 +1,485 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + }, + "meta": { + "title": "Услуги", + "description": "Ознакомьтесь с профессиональными услугами SmartSolTech. Веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг и другие технологические решения.", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг, технологические решения, SmartSolTech" + }, + "hero": { + "title": "Наши", + "title_highlight": "Услуги", + "subtitle": "Поддерживаем рост бизнеса с помощью инновационных технологий" + }, + "cards": { + "starting_price": "Стартовая цена", + "consultation": "консультация", + "contact": "Связаться", + "calculate_cost": "Рассчитать стоимость", + "popular": "Популярно", + "coming_soon": "Услуги готовятся", + "coming_soon_desc": "Скоро мы предложим разнообразные услуги!" + }, + "process": { + "title": "Процесс реализации проекта", + "subtitle": "Мы ведем проекты с помощью систематических и профессиональных процессов", + "consultation": { + "title": "Консультация и планирование", + "description": "Точно понимаем требования клиента и" + } + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_project": "View Project", + "categories": { + "all": "Все", + "web": "Веб-разработка", + "mobile": "Мобильные приложения", + "uiux": "UI/UX дизайн" + }, + "project_details": "Детали проекта", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + }, + "meta": { + "title": "Портфолио", + "description": "Ознакомьтесь с разнообразными проектами и историями успеха SmartSolTech. Портфолио веб-разработки, мобильных приложений, UI/UX дизайна.", + "keywords": "портфолио, веб-разработка, мобильные приложения, UI/UX дизайн, проекты, SmartSolTech", + "og_title": "Портфолио - SmartSolTech", + "og_description": "Разнообразные проекты и истории успеха SmartSolTech" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "hero": { + "title": "Свяжитесь с нами", + "subtitle": "Мы здесь, чтобы помочь воплотить ваши идеи в жизнь" + }, + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + }, + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "sitemap": "Sitemap" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms", + "social": { + "follow": "Follow Us" + } + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021210347.json b/.history/locales/ru_20251021210347.json new file mode 100644 index 0000000..34141a4 --- /dev/null +++ b/.history/locales/ru_20251021210347.json @@ -0,0 +1,485 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + } + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "title_highlight": "Услуги", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + }, + "view_all": "Посмотреть все услуги", + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + }, + "meta": { + "title": "Услуги", + "description": "Ознакомьтесь с профессиональными услугами SmartSolTech. Веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг и другие технологические решения.", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг, технологические решения, SmartSolTech" + }, + "hero": { + "title": "Наши", + "title_highlight": "Услуги", + "subtitle": "Поддерживаем рост бизнеса с помощью инновационных технологий" + }, + "cards": { + "starting_price": "Стартовая цена", + "consultation": "консультация", + "contact": "Связаться", + "calculate_cost": "Рассчитать стоимость", + "popular": "Популярно", + "coming_soon": "Услуги готовятся", + "coming_soon_desc": "Скоро мы предложим разнообразные услуги!" + }, + "process": { + "title": "Процесс реализации проекта", + "subtitle": "Мы ведем проекты с помощью систематических и профессиональных процессов", + "consultation": { + "title": "Консультация и планирование", + "description": "Точно понимаем требования клиента и" + } + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты", + "our": "Our", + "portfolio": "Portfolio" + }, + "title_highlight": "Проекты", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_project": "View Project", + "categories": { + "all": "Все", + "web": "Веб-разработка", + "mobile": "Мобильные приложения", + "uiux": "UI/UX дизайн" + }, + "project_details": "Детали проекта", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + }, + "meta": { + "title": "Портфолио", + "description": "Ознакомьтесь с разнообразными проектами и историями успеха SmartSolTech. Портфолио веб-разработки, мобильных приложений, UI/UX дизайна.", + "keywords": "портфолио, веб-разработка, мобильные приложения, UI/UX дизайн, проекты, SmartSolTech", + "og_title": "Портфолио - SmartSolTech", + "og_description": "Разнообразные проекты и истории успеха SmartSolTech" + } + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "hero": { + "title": "Свяжитесь с нами", + "subtitle": "Мы здесь, чтобы помочь воплотить ваши идеи в жизнь" + }, + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию", + "form": { + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "message": "Кратко опишите ваш проект", + "submit": "Подать заявку на консультацию", + "title": "Заявка на бесплатную консультацию", + "service": { + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + } + }, + "about": { + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "values": { + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + } + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио", + "subtitle": "Turn your ideas into reality", + "button": "Contact Us" + }, + "meta": { + "title": "About Us", + "description": "SmartSolTech is a professional development company that supports customer business growth with innovative technology" + }, + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + } + }, + "footer": { + "company": { + "description": "footer.company.description" + }, + "description": "Специалист по цифровым решениям, ведущий инновации", + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "links": { + "title": "footer.links.title", + "privacy": "Privacy Policy", + "terms": "Terms of Service", + "sitemap": "Sitemap" + }, + "contact": { + "title": "footer.contact.title", + "email": "footer.contact.email", + "phone": "footer.contact.phone", + "address": "footer.contact.address" + }, + "copyright": "footer.copyright", + "privacy": "footer.privacy", + "terms": "footer.terms", + "social": { + "follow": "Follow Us" + } + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech", + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard": "Панель управления", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "title": "SmartSolTech Admin", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "full_name": "SmartSolTech - Инновационные технологические решения", + "description": "Специалист по цифровым решениям, ведущий инновации", + "tagline": "Будущее начинается здесь", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "contact_us": "Свяжитесь с нами", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + } +} \ No newline at end of file diff --git a/.history/locales/ru_20251021210903.json b/.history/locales/ru_20251021210903.json new file mode 100644 index 0000000..c8f4b9f --- /dev/null +++ b/.history/locales/ru_20251021210903.json @@ -0,0 +1,492 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + }, + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио" + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "view_all": "Посмотреть все услуги", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + }, + "meta": { + "title": "Услуги", + "description": "Ознакомьтесь с профессиональными услугами SmartSolTech. Веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг и другие технологические решения.", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг, технологические решения, SmartSolTech" + }, + "hero": { + "title": "Наши", + "title_highlight": "Услуги", + "subtitle": "Поддерживаем рост бизнеса с помощью инновационных технологий" + }, + "cards": { + "starting_price": "Стартовая цена", + "consultation": "консультация", + "contact": "Связаться", + "calculate_cost": "Рассчитать стоимость", + "popular": "Популярно", + "coming_soon": "Услуги готовятся", + "coming_soon_desc": "Скоро мы предложим разнообразные услуги!" + }, + "process": { + "title": "Процесс реализации проекта", + "subtitle": "Мы ведем проекты с помощью систематических и профессиональных процессов", + "consultation": { + "title": "Консультация и планирование", + "description": "Точно понимаем требования клиента и" + } + }, + "title_highlight": "Услуги", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты", + "our": "Our", + "portfolio": "Portfolio" + }, + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "categories": { + "all": "Все", + "web": "Веб-разработка", + "mobile": "Мобильные приложения", + "uiux": "UI/UX дизайн" + }, + "project_details": "Детали проекта", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + }, + "meta": { + "title": "Портфолио", + "description": "Ознакомьтесь с разнообразными проектами и историями успеха SmartSolTech. Портфолио веб-разработки, мобильных приложений, UI/UX дизайна.", + "keywords": "портфолио, веб-разработка, мобильные приложения, UI/UX дизайн, проекты, SmartSolTech", + "og_title": "Портфолио - SmartSolTech", + "og_description": "Разнообразные проекты и истории успеха SmartSolTech" + }, + "title_highlight": "Проекты", + "view_project": "View Project" + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "hero": { + "title": "Свяжитесь с нами", + "subtitle": "Мы здесь, чтобы помочь воплотить ваши идеи в жизнь" + }, + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "form": { + "title": "Заявка на проект", + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "message": "Кратко опишите ваш проект", + "submit": "Отправить заявку", + "success": "Заявка успешно отправлена", + "error": "Произошла ошибка при отправке заявки", + "service": { + "title": "Интересующая услуга", + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + } + }, + "info": { + "title": "Контактная информация" + }, + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-10-1234-5678" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.kr" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Мгновенный ответ доступен" + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + }, + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию" + }, + "about": { + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + }, + "values": { + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "button": "Связаться с нами", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + }, + "meta": { + "title": "О нас", + "description": "SmartSolTech - это профессиональная компания по разработке, которая поддерживает рост бизнеса клиентов с помощью инновационных технологий" + }, + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + } + }, + "footer": { + "description": "Специалист по цифровым решениям, ведущий инновации", + "links": { + "title": "footer.links.title", + "privacy": "Политика конфиденциальности", + "terms": "Условия обслуживания", + "sitemap": "Карта сайта" + }, + "contact": { + "title": "Контакты", + "email": "E-mail", + "phone": "Телефон", + "address": "Адрес" + }, + "copyright": "© 2024 SmartSolTech. Все права защищены.", + "company": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса" + }, + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "privacy": "footer.privacy", + "terms": "footer.terms", + "social": { + "follow": "Подписывайтесь на нас" + } + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "dashboard": "Панель управления", + "title": "SmartSolTech Admin", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "description": "Специалист по цифровым решениям, ведущий инновации", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "full_name": "SmartSolTech - Инновационные технологические решения", + "tagline": "Будущее начинается здесь", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку", + "contact_us": "Свяжитесь с нами" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech" +} \ No newline at end of file diff --git a/.history/locales/ru_20251021210932.json b/.history/locales/ru_20251021210932.json new file mode 100644 index 0000000..75b1b2d --- /dev/null +++ b/.history/locales/ru_20251021210932.json @@ -0,0 +1,499 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + }, + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио" + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "view_all": "Посмотреть все услуги", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + }, + "meta": { + "title": "Услуги", + "description": "Ознакомьтесь с профессиональными услугами SmartSolTech. Веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг и другие технологические решения.", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг, технологические решения, SmartSolTech" + }, + "hero": { + "title": "Наши", + "title_highlight": "Услуги", + "subtitle": "Поддерживаем рост бизнеса с помощью инновационных технологий" + }, + "cards": { + "starting_price": "Стартовая цена", + "consultation": "консультация", + "contact": "Связаться", + "calculate_cost": "Рассчитать стоимость", + "popular": "Популярно", + "coming_soon": "Услуги готовятся", + "coming_soon_desc": "Скоро мы предложим разнообразные услуги!" + }, + "process": { + "title": "Процесс реализации проекта", + "subtitle": "Мы ведем проекты с помощью систематических и профессиональных процессов", + "consultation": { + "title": "Консультация и планирование", + "description": "Точно понимаем требования клиента и" + } + }, + "title_highlight": "Услуги", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты", + "our": "Our", + "portfolio": "Portfolio" + }, + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "categories": { + "all": "Все", + "web": "Веб-разработка", + "mobile": "Мобильные приложения", + "uiux": "UI/UX дизайн" + }, + "project_details": "Детали проекта", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + }, + "meta": { + "title": "Портфолио", + "description": "Ознакомьтесь с разнообразными проектами и историями успеха SmartSolTech. Портфолио веб-разработки, мобильных приложений, UI/UX дизайна.", + "keywords": "портфолио, веб-разработка, мобильные приложения, UI/UX дизайн, проекты, SmartSolTech", + "og_title": "Портфолио - SmartSolTech", + "og_description": "Разнообразные проекты и истории успеха SmartSolTech" + }, + "title_highlight": "Проекты", + "view_project": "View Project" + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "hero": { + "title": "Свяжитесь с нами", + "subtitle": "Мы здесь, чтобы помочь воплотить ваши идеи в жизнь" + }, + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "form": { + "title": "Заявка на проект", + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "message": "Кратко опишите ваш проект", + "submit": "Отправить заявку", + "success": "Заявка успешно отправлена", + "error": "Произошла ошибка при отправке заявки", + "service": { + "title": "Интересующая услуга", + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + } + }, + "info": { + "title": "Контактная информация" + }, + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-2-1234-5678", + "hours": "Пн-Пт 9:00-18:00" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.co.kr", + "response": "Ответ в течение 24 часов" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Для быстрого ответа" + }, + "address": { + "title": "Адрес офиса", + "line1": "Теheran-ro 123, Gangnam-gu", + "line2": "Сеул, Южная Корея" + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + }, + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию" + }, + "about": { + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + }, + "values": { + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "button": "Связаться с нами", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + }, + "meta": { + "title": "О нас", + "description": "SmartSolTech - это профессиональная компания по разработке, которая поддерживает рост бизнеса клиентов с помощью инновационных технологий" + }, + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + } + }, + "footer": { + "description": "Специалист по цифровым решениям, ведущий инновации", + "links": { + "title": "footer.links.title", + "privacy": "Политика конфиденциальности", + "terms": "Условия обслуживания", + "sitemap": "Карта сайта" + }, + "contact": { + "title": "Контакты", + "email": "E-mail", + "phone": "Телефон", + "address": "Адрес" + }, + "copyright": "© 2024 SmartSolTech. Все права защищены.", + "company": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса" + }, + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "privacy": "footer.privacy", + "terms": "footer.terms", + "social": { + "follow": "Подписывайтесь на нас" + } + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "dashboard": "Панель управления", + "title": "SmartSolTech Admin", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "description": "Специалист по цифровым решениям, ведущий инновации", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "full_name": "SmartSolTech - Инновационные технологические решения", + "tagline": "Будущее начинается здесь", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку", + "contact_us": "Свяжитесь с нами" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + }, + "undefined - SmartSolTech": "undefined - SmartSolTech" +} \ No newline at end of file diff --git a/.history/locales/ru_20251021211322.json b/.history/locales/ru_20251021211322.json new file mode 100644 index 0000000..ad76eca --- /dev/null +++ b/.history/locales/ru_20251021211322.json @@ -0,0 +1,531 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + }, + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио" + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "view_all": "Посмотреть все услуги", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + }, + "meta": { + "title": "Услуги", + "description": "Ознакомьтесь с профессиональными услугами SmartSolTech. Веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг и другие технологические решения.", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг, технологические решения, SmartSolTech" + }, + "hero": { + "title": "Наши", + "title_highlight": "Услуги", + "subtitle": "Поддерживаем рост бизнеса с помощью инновационных технологий" + }, + "cards": { + "starting_price": "Стартовая цена", + "consultation": "консультация", + "contact": "Связаться", + "calculate_cost": "Рассчитать стоимость", + "popular": "Популярно", + "coming_soon": "Услуги готовятся", + "coming_soon_desc": "Скоро мы предложим разнообразные услуги!" + }, + "process": { + "title": "Процесс реализации проекта", + "subtitle": "Мы ведем проекты с помощью систематических и профессиональных процессов", + "consultation": { + "title": "Консультация и планирование", + "description": "Точно понимаем требования клиента и" + } + }, + "title_highlight": "Услуги", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты", + "our": "Our", + "portfolio": "Portfolio" + }, + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "categories": { + "all": "Все", + "web": "Веб-разработка", + "mobile": "Мобильные приложения", + "uiux": "UI/UX дизайн" + }, + "project_details": "Детали проекта", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + }, + "meta": { + "title": "Портфолио", + "description": "Ознакомьтесь с разнообразными проектами и историями успеха SmartSolTech. Портфолио веб-разработки, мобильных приложений, UI/UX дизайна.", + "keywords": "портфолио, веб-разработка, мобильные приложения, UI/UX дизайн, проекты, SmartSolTech", + "og_title": "Портфолио - SmartSolTech", + "og_description": "Разнообразные проекты и истории успеха SmartSolTech" + }, + "title_highlight": "Проекты", + "view_project": "View Project" + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "hero": { + "title": "Свяжитесь с нами", + "subtitle": "Мы здесь, чтобы помочь воплотить ваши идеи в жизнь" + }, + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "form": { + "title": "Заявка на проект", + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "message": "Кратко опишите ваш проект", + "submit": "Отправить заявку", + "success": "Заявка успешно отправлена", + "error": "Произошла ошибка при отправке заявки", + "service": { + "title": "Интересующая услуга", + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + } + }, + "info": { + "title": "Контактная информация" + }, + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-2-1234-5678", + "hours": "Пн-Пт 9:00-18:00" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.co.kr", + "response": "Ответ в течение 24 часов" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Для быстрого ответа" + }, + "address": { + "title": "Адрес офиса", + "line1": "Теheran-ro 123, Gangnam-gu", + "line2": "Сеул, Южная Корея" + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + }, + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию" + }, + "about": { + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + }, + "values": { + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "button": "Связаться с нами", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + }, + "meta": { + "title": "О нас", + "description": "SmartSolTech - это профессиональная компания по разработке, которая поддерживает рост бизнеса клиентов с помощью инновационных технологий" + }, + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + } + }, + "footer": { + "description": "Специалист по цифровым решениям, ведущий инновации", + "links": { + "title": "footer.links.title", + "privacy": "Политика конфиденциальности", + "terms": "Условия обслуживания", + "sitemap": "Карта сайта" + }, + "contact": { + "title": "Контакты", + "email": "E-mail", + "phone": "Телефон", + "address": "Адрес" + }, + "copyright": "© 2024 SmartSolTech. Все права защищены.", + "company": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса" + }, + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "privacy": "footer.privacy", + "terms": "footer.terms", + "social": { + "follow": "Подписывайтесь на нас" + } + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "dashboard": "Панель управления", + "title": "SmartSolTech Admin", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "description": "Специалист по цифровым решениям, ведущий инновации", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "full_name": "SmartSolTech - Инновационные технологические решения", + "tagline": "Будущее начинается здесь", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку", + "contact_us": "Свяжитесь с нами" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + }, + "portfolio_page": { + "title": "Наше портфолио", + "subtitle": "Знакомьтесь с инновационными проектами и креативными решениями", + "categories": { + "all": "Все", + "web-development": "Веб-разработка", + "mobile-app": "Мобильные приложения", + "ui-ux-design": "UI/UX дизайн", + "branding": "Брендинг", + "marketing": "Цифровой маркетинг" + }, + "buttons": { + "details": "Подробнее", + "projectDetails": "Подробности проекта", + "loadMore": "Больше проектов", + "contact": "Запросить проект", + "calculate": "Рассчитать стоимость" + }, + "empty": { + "title": "Портфолио пока пусто", + "subtitle": "Скоро мы представим замечательные проекты!" + }, + "cta": { + "title": "Станьте героем следующего проекта", + "subtitle": "Создайте с нами инновационное цифровое решение" + }, + "labels": { + "featured": "РЕКОМЕНДУЕМОЕ", + "views": "просмотров", + "likes": "лайков" + } + }, + "undefined - SmartSolTech": "undefined - SmartSolTech" +} \ No newline at end of file diff --git a/.history/locales/ru_20251021211513.json b/.history/locales/ru_20251021211513.json new file mode 100644 index 0000000..ad76eca --- /dev/null +++ b/.history/locales/ru_20251021211513.json @@ -0,0 +1,531 @@ +{ + "navigation": { + "home": "Главная", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор", + "admin": "Админ", + "home - SmartSolTech": "Главная - SmartSolTech" + }, + "hero": { + "title": { + "smart": "SmartSolTech", + "solutions": "Future begins here" + }, + "subtitle": "Решения", + "description": "Инновационная веб-разработка, мобильные приложения, UI/UX дизайн для цифровой трансформации вашего бизнеса", + "cta": { + "start": "Начать проект", + "portfolio": "Посмотреть портфолио" + }, + "cta_primary": "Начать проект", + "cta_secondary": "Посмотреть портфолио" + }, + "services": { + "title": { + "our": "Наши", + "services": "Услуги" + }, + "subtitle": "Цифровые решения с использованием передовых технологий и творческих идей", + "description": "Цифровые решения с использованием передовых технологий и творческих идей", + "view_all": "Посмотреть все услуги", + "web": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "от $5,000" + }, + "mobile": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "от $8,000" + }, + "design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "от $3,000" + }, + "marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "от $2,000" + }, + "meta": { + "title": "Услуги", + "description": "Ознакомьтесь с профессиональными услугами SmartSolTech. Веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг и другие технологические решения.", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, цифровой маркетинг, технологические решения, SmartSolTech" + }, + "hero": { + "title": "Наши", + "title_highlight": "Услуги", + "subtitle": "Поддерживаем рост бизнеса с помощью инновационных технологий" + }, + "cards": { + "starting_price": "Стартовая цена", + "consultation": "консультация", + "contact": "Связаться", + "calculate_cost": "Рассчитать стоимость", + "popular": "Популярно", + "coming_soon": "Услуги готовятся", + "coming_soon_desc": "Скоро мы предложим разнообразные услуги!" + }, + "process": { + "title": "Процесс реализации проекта", + "subtitle": "Мы ведем проекты с помощью систематических и профессиональных процессов", + "consultation": { + "title": "Консультация и планирование", + "description": "Точно понимаем требования клиента и" + } + }, + "title_highlight": "Услуги", + "web_development": { + "title": "Веб-разработка", + "description": "Современные и адаптивные веб-сайты и веб-приложения", + "price": "$5,000~" + }, + "mobile_app": { + "title": "Мобильные приложения", + "description": "Нативные и кроссплатформенные приложения для iOS и Android", + "price": "$8,000~" + }, + "ui_ux_design": { + "title": "UI/UX дизайн", + "description": "Ориентированный на пользователя интуитивный и красивый дизайн интерфейса", + "price": "$3,000~" + }, + "digital_marketing": { + "title": "Цифровой маркетинг", + "description": "Цифровой маркетинг через SEO, социальные сети, онлайн-рекламу", + "price": "$2,000~" + } + }, + "portfolio": { + "title": { + "recent": "Последние", + "projects": "Проекты", + "our": "Our", + "portfolio": "Portfolio" + }, + "subtitle": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "description": "Ознакомьтесь с проектами, выполненными для успеха клиентов", + "view_details": "Подробнее", + "view_all": "Посмотреть все портфолио", + "categories": { + "all": "Все", + "web": "Веб-разработка", + "mobile": "Мобильные приложения", + "uiux": "UI/UX дизайн" + }, + "project_details": "Детали проекта", + "default": { + "ecommerce": "Электронная коммерция", + "title": "Платформа электронной коммерции", + "description": "Современное решение для онлайн-торговли с интуитивным интерфейсом" + }, + "meta": { + "title": "Портфолио", + "description": "Ознакомьтесь с разнообразными проектами и историями успеха SmartSolTech. Портфолио веб-разработки, мобильных приложений, UI/UX дизайна.", + "keywords": "портфолио, веб-разработка, мобильные приложения, UI/UX дизайн, проекты, SmartSolTech", + "og_title": "Портфолио - SmartSolTech", + "og_description": "Разнообразные проекты и истории успеха SmartSolTech" + }, + "title_highlight": "Проекты", + "view_project": "View Project" + }, + "calculator": { + "title": "Калькулятор Стоимости Проекта", + "subtitle": "Выберите нужные услуги и требования для получения точной оценки стоимости в режиме реального времени", + "meta": { + "title": "Калькулятор стоимости проекта", + "description": "Рассчитайте стоимость вашего проекта веб-разработки, мобильного приложения или дизайна с помощью нашего интерактивного калькулятора" + }, + "cta": { + "title": "Узнайте стоимость вашего проекта", + "subtitle": "Выберите необходимые услуги и требования, и мы рассчитаем стоимость в режиме реального времени", + "button": "Использовать калькулятор стоимости" + }, + "step1": { + "title": "Шаг 1: Выбор услуг", + "subtitle": "Выберите необходимые услуги (можно выбрать несколько)" + }, + "step2": { + "title": "Шаг 2: Детали проекта", + "subtitle": "Выберите сложность проекта и сроки" + }, + "complexity": { + "title": "Сложность проекта", + "simple": "Простой", + "simple_desc": "Базовый функционал, стандартный дизайн", + "medium": "Средний", + "medium_desc": "Дополнительные функции, кастомный дизайн", + "complex": "Сложный", + "complex_desc": "Расширенный функционал, интеграции" + }, + "timeline": { + "title": "Временные рамки", + "standard": "Стандартные", + "standard_desc": "Обычные сроки разработки", + "rush": "Срочно", + "rush_desc": "Ускоренная разработка (+50%)", + "extended": "Расширенные", + "extended_desc": "Длительная разработка (-20%)" + }, + "result": { + "title": "Результат расчета", + "subtitle": "Вот ваша предварительная оценка стоимости проекта", + "estimated_price": "Предварительная стоимость", + "price_note": "* Окончательная стоимость может варьироваться в зависимости от деталей проекта", + "summary": "Сводка проекта", + "selected_services": "Выбранные услуги", + "complexity": "Сложность", + "timeline": "Временные рамки", + "get_quote": "Получить точное предложение", + "recalculate": "Пересчитать", + "contact_note": "Свяжитесь с нами для получения точного предложения и обсуждения деталей проекта" + }, + "next_step": "Следующий шаг", + "prev_step": "Назад", + "calculate": "Рассчитать" + }, + "contact": { + "hero": { + "title": "Свяжитесь с нами", + "subtitle": "Мы здесь, чтобы помочь воплотить ваши идеи в жизнь" + }, + "ready_title": "Готовы начать свой проект?", + "ready_description": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "form": { + "title": "Заявка на проект", + "name": "Имя", + "email": "Электронная почта", + "phone": "Телефон", + "message": "Кратко опишите ваш проект", + "submit": "Отправить заявку", + "success": "Заявка успешно отправлена", + "error": "Произошла ошибка при отправке заявки", + "service": { + "title": "Интересующая услуга", + "select": "Выберите интересующую услугу", + "web": "Веб-разработка", + "mobile": "Мобильное приложение", + "design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + } + }, + "info": { + "title": "Контактная информация" + }, + "service_interest": "Интересующая услуга", + "service_options": { + "select": "Выберите интересующую услугу", + "web_development": "Веб-разработка", + "mobile_app": "Мобильное приложение", + "ui_ux_design": "UI/UX дизайн", + "branding": "Брендинг", + "consulting": "Консалтинг", + "other": "Другое" + }, + "success": "Спасибо! Мы свяжемся с вами в ближайшее время.", + "error": "Произошла ошибка. Попробуйте снова." + }, + "phone": { + "title": "Телефонная консультация", + "number": "+82-2-1234-5678", + "hours": "Пн-Пт 9:00-18:00" + }, + "email": { + "title": "Электронная почта", + "address": "info@smartsoltech.co.kr", + "response": "Ответ в течение 24 часов" + }, + "telegram": { + "title": "Telegram", + "subtitle": "Для быстрого ответа" + }, + "address": { + "title": "Адрес офиса", + "line1": "Теheran-ro 123, Gangnam-gu", + "line2": "Сеул, Южная Корея" + }, + "cta": { + "ready": "Готовы начать", + "start": "свой проект", + "question": "?", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения." + }, + "meta": { + "title": "Contact", + "description": "Contact us anytime for project inquiries or consultation" + }, + "phone_consultation": "Телефонная консультация", + "email_inquiry": "Запрос по электронной почте", + "telegram_chat": "Чат в Telegram", + "instant_response": "Мгновенный ответ доступен", + "free_consultation": "Заявка на бесплатную консультацию" + }, + "about": { + "hero": { + "title": "About SmartSolTech", + "subtitle": "Creating the future with innovation and technology" + }, + "company": { + "title": "Company Information", + "description1": "SmartSolTech is a technology company established in 2020, recognized for expertise in web development, mobile app development, and UI/UX design.", + "description2": "We accurately understand customer needs and provide innovative solutions using the latest technology." + }, + "stats": { + "projects": "Completed Projects", + "experience": "Years Experience", + "clients": "Satisfied Customers" + }, + "mission": { + "title": "Our Mission", + "description": "Our mission is to support customer business growth through technology and lead digital innovation." + }, + "values": { + "innovation": { + "title": "Инновации", + "description": "Мы предоставляем инновационные решения через непрерывные исследования и внедрение передовых технологий." + }, + "quality": { + "title": "Качество", + "description": "Мы поддерживаем высокие стандарты качества и предоставляем высококачественные продукты, которыми клиенты могут быть довольны." + }, + "partnership": { + "title": "Сотрудничество", + "description": "Мы создаем лучшие результаты через тесное общение и сотрудничество с клиентами." + }, + "title": "Основные", + "title_highlight": "Ценности", + "description": "Основные ценности, которых придерживается SmartSolTech", + "collaboration": { + "title": "Collaboration", + "description": "We create the best results through close communication and collaboration with customers." + }, + "growth": { + "title": "Рост", + "description": "Мы растем вместе с клиентами и стремимся к непрерывному обучению и развитию." + } + }, + "cta": { + "title": "Станьте партнером для совместного успеха", + "subtitle": "Превратите свои идеи в реальность. Эксперты предоставят лучшие решения.", + "button": "Связаться с нами", + "description": "Выведите свой бизнес на следующий уровень с SmartSolTech", + "partnership": "Запрос о партнерстве", + "portfolio": "Посмотреть портфолио" + }, + "meta": { + "title": "О нас", + "description": "SmartSolTech - это профессиональная компания по разработке, которая поддерживает рост бизнеса клиентов с помощью инновационных технологий" + }, + "hero_title": "О", + "hero_highlight": "SmartSolTech", + "hero_description": "Специалист по цифровым решениям, ведущий к успеху клиентов с помощью инновационных технологий", + "overview": { + "title": "Создавая будущее с инновациями и креативностью", + "description_1": "SmartSolTech - это специалист по цифровым решениям, основанный в 2020 году, поддерживающий успех клиентского бизнеса с помощью инновационных технологий и творческих идей в области веб-разработки, мобильных приложений и UI/UX дизайна.", + "description_2": "Мы не просто предоставляем технологии, но понимаем цели клиентов и предлагаем оптимальные решения, чтобы стать партнерами, растущими вместе.", + "stats": { + "projects": "100+", + "projects_label": "Завершенные проекты", + "clients": "50+", + "clients_label": "Довольные клиенты", + "experience": "4 года", + "experience_label": "Опыт в отрасли" + }, + "mission": "Наша миссия", + "mission_text": "Помощь всем предприятиям в достижении успеха в цифровую эпоху с помощью технологий", + "vision": "Наше видение", + "vision_text": "Рост как глобальной компании цифровых решений, представляющей Корею, для ведения цифровых инноваций для клиентов по всему миру" + }, + "team": { + "title": "Наша", + "title_highlight": "Команда", + "description": "Представляем команду SmartSolTech с экспертизой и страстью" + }, + "tech_stack": { + "title": "Технологический", + "title_highlight": "Стек", + "description": "Мы предоставляем лучшие решения с передовыми технологиями и проверенными инструментами", + "frontend": "Frontend", + "backend": "Backend", + "mobile": "Мобильные" + } + }, + "footer": { + "description": "Специалист по цифровым решениям, ведущий инновации", + "links": { + "title": "footer.links.title", + "privacy": "Политика конфиденциальности", + "terms": "Условия обслуживания", + "sitemap": "Карта сайта" + }, + "contact": { + "title": "Контакты", + "email": "E-mail", + "phone": "Телефон", + "address": "Адрес" + }, + "copyright": "© 2024 SmartSolTech. Все права защищены.", + "company": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса" + }, + "quick_links": "Быстрые ссылки", + "services": "Услуги", + "contact_info": "Контактная информация", + "follow_us": "Подписывайтесь", + "rights": "Все права защищены.", + "privacy": "footer.privacy", + "terms": "footer.terms", + "social": { + "follow": "Подписывайтесь на нас" + } + }, + "theme": { + "light": "Светлая тема", + "dark": "Темная тема", + "toggle": "Переключить тему" + }, + "language": { + "english": "English", + "korean": "한국어", + "russian": "Русский", + "kazakh": "Қазақша", + "ko": "한국어", + "en": "English", + "ru": "Русский", + "kk": "Қазақша" + }, + "common": { + "loading": "Загрузка...", + "error": "Произошла ошибка", + "success": "Успешно", + "view_more": "Посмотреть еще", + "back": "Назад", + "next": "Далее", + "previous": "Предыдущий", + "view_details": "Подробнее" + }, + "meta": { + "description": "SmartSolTech - Инновационные технологические решения для вашего бизнеса", + "keywords": "веб-разработка, мобильные приложения, UI/UX дизайн, SmartSolTech", + "title": "SmartSolTech - Инновационные технологические решения" + }, + "nav": { + "home": "nav.home", + "about": "nav.about", + "services": "nav.services", + "portfolio": "nav.portfolio", + "calculator": "nav.calculator" + }, + "admin": { + "login": "Вход в админ панель", + "dashboard": "Панель управления", + "title": "SmartSolTech Admin", + "login_title": "Вход в админ панель", + "login_subtitle": "Войдите в свой аккаunt для управления сайтом", + "login_button": "Войти", + "email": "Email", + "password": "Пароль", + "email_placeholder": "admin@smartsoltech.com", + "password_placeholder": "Введите пароль", + "back_to_site": "Вернуться на сайт", + "dashboard_subtitle": "Обзор основных показателей сайта", + "portfolio": "Портфолио", + "services": "Услуги", + "contacts": "Сообщения", + "settings": "Настройки", + "users": "Пользователи", + "logout": "Выход", + "view_site": "Посмотреть сайт", + "view_all": "Посмотреть всё", + "portfolio_projects": "Проекты", + "contact_messages": "Сообщения", + "recent_portfolio": "Последние проекты", + "recent_contacts": "Последние сообщения", + "no_recent_portfolio": "Нет недавних проектов", + "no_recent_contacts": "Нет недавних сообщений", + "quick_actions": "Быстрые действия", + "add_portfolio": "Добавить проект", + "add_service": "Добавить услугу", + "site_settings": "Настройки сайта", + "banner_editor": "Редактор Баннеров", + "current_banner": "Текущий баннер", + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио" + } + }, + "company": { + "name": "SmartSolTech", + "description": "Специалист по цифровым решениям, ведущий инновации", + "email": "info@smartsoltech.kr", + "phone": "+82-10-1234-5678", + "full_name": "SmartSolTech - Инновационные технологические решения", + "tagline": "Будущее начинается здесь", + "address": "Seoul, South Korea", + "social": { + "telegram": "@smartsoltech" + } + }, + "errors": { + "page_not_found": "Страница не найдена", + "error_occurred": "Произошла ошибка", + "title": "Произошла ошибка - SmartSolTech", + "default_title": "Произошла ошибка", + "default_message": "При обработке запроса возникла проблема.", + "back_home": "Вернуться на главную", + "go_back": "Назад", + "need_help": "Нужна помощь?", + "help_message": "Если проблема продолжается, свяжитесь с нами в любое время.", + "contact_support": "Обратиться в поддержку", + "contact_us": "Свяжитесь с нами" + }, + "pages": { + "home": "Главная страница", + "about": "О нас", + "services": "Услуги", + "portfolio": "Портфолио", + "contact": "Контакты", + "calculator": "Калькулятор" + }, + "portfolio_page": { + "title": "Наше портфолио", + "subtitle": "Знакомьтесь с инновационными проектами и креативными решениями", + "categories": { + "all": "Все", + "web-development": "Веб-разработка", + "mobile-app": "Мобильные приложения", + "ui-ux-design": "UI/UX дизайн", + "branding": "Брендинг", + "marketing": "Цифровой маркетинг" + }, + "buttons": { + "details": "Подробнее", + "projectDetails": "Подробности проекта", + "loadMore": "Больше проектов", + "contact": "Запросить проект", + "calculate": "Рассчитать стоимость" + }, + "empty": { + "title": "Портфолио пока пусто", + "subtitle": "Скоро мы представим замечательные проекты!" + }, + "cta": { + "title": "Станьте героем следующего проекта", + "subtitle": "Создайте с нами инновационное цифровое решение" + }, + "labels": { + "featured": "РЕКОМЕНДУЕМОЕ", + "views": "просмотров", + "likes": "лайков" + } + }, + "undefined - SmartSolTech": "undefined - SmartSolTech" +} \ No newline at end of file diff --git a/.history/middleware/auth_20251019202041.js b/.history/middleware/auth_20251019202041.js new file mode 100644 index 0000000..6a338bd --- /dev/null +++ b/.history/middleware/auth_20251019202041.js @@ -0,0 +1,154 @@ +const jwt = require('jsonwebtoken'); +const { User } = require('../models'); + +/** + * Authentication middleware + * Verifies JWT token and attaches user to request + */ +const authenticateToken = async (req, res, next) => { + try { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN + + if (!token) { + return res.status(401).json({ + success: false, + message: 'Access token required' + }); + } + + const decoded = jwt.verify(token, process.env.JWT_SECRET); + const user = await User.findById(decoded.userId).select('-password'); + + if (!user || !user.isActive) { + return res.status(401).json({ + success: false, + message: 'Invalid or inactive user' + }); + } + + req.user = user; + next(); + } catch (error) { + console.error('Token verification error:', error); + return res.status(403).json({ + success: false, + message: 'Invalid token' + }); + } +}; + +/** + * Session-based authentication middleware + * For web pages using sessions + */ +const authenticateSession = async (req, res, next) => { + try { + if (!req.session.userId) { + req.flash('error', '로그인이 필요합니다.'); + return res.redirect('/auth/login'); + } + + const user = await User.findById(req.session.userId).select('-password'); + + if (!user || !user.isActive) { + req.session.destroy(); + req.flash('error', '유효하지 않은 사용자입니다.'); + return res.redirect('/auth/login'); + } + + req.user = user; + res.locals.user = user; + next(); + } catch (error) { + console.error('Session authentication error:', error); + req.session.destroy(); + req.flash('error', '인증 오류가 발생했습니다.'); + return res.redirect('/auth/login'); + } +}; + +/** + * Admin role middleware + * Requires user to be authenticated and have admin role + */ +const requireAdmin = (req, res, next) => { + if (!req.user) { + return res.status(401).json({ + success: false, + message: 'Authentication required' + }); + } + + if (req.user.role !== 'admin') { + return res.status(403).json({ + success: false, + message: 'Admin access required' + }); + } + + next(); +}; + +/** + * Admin session middleware for web pages + */ +const requireAdminSession = (req, res, next) => { + if (!req.user) { + req.flash('error', '로그인이 필요합니다.'); + return res.redirect('/auth/login'); + } + + if (req.user.role !== 'admin') { + req.flash('error', '관리자 권한이 필요합니다.'); + return res.redirect('/'); + } + + next(); +}; + +/** + * Optional authentication middleware + * Attaches user if token exists but doesn't require it + */ +const optionalAuth = async (req, res, next) => { + try { + // Check session first + if (req.session.userId) { + const user = await User.findById(req.session.userId).select('-password'); + if (user && user.isActive) { + req.user = user; + res.locals.user = user; + } + } + + // Check JWT token if no session + if (!req.user) { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; + + if (token) { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + const user = await User.findById(decoded.userId).select('-password'); + + if (user && user.isActive) { + req.user = user; + res.locals.user = user; + } + } + } + + next(); + } catch (error) { + // Continue without authentication if token is invalid + next(); + } +}; + +module.exports = { + authenticateToken, + authenticateSession, + requireAdmin, + requireAdminSession, + optionalAuth +}; \ No newline at end of file diff --git a/.history/middleware/auth_20251019202048.js b/.history/middleware/auth_20251019202048.js new file mode 100644 index 0000000..a76c5f2 --- /dev/null +++ b/.history/middleware/auth_20251019202048.js @@ -0,0 +1,156 @@ +const jwt = require('jsonwebtoken'); +const { User } = require('../models'); + +/** + * Authentication middleware + * Verifies JWT token and attaches user to request + */ +const authenticateToken = async (req, res, next) => { + try { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN + + if (!token) { + return res.status(401).json({ + success: false, + message: 'Access token required' + }); + } + + const decoded = jwt.verify(token, process.env.JWT_SECRET); + const user = await User.findByPk(decoded.userId, { + attributes: { exclude: ['password'] } + }); + + if (!user || !user.isActive) { + return res.status(401).json({ + success: false, + message: 'Invalid or inactive user' + }); + } + + req.user = user; + next(); + } catch (error) { + console.error('Token verification error:', error); + return res.status(403).json({ + success: false, + message: 'Invalid token' + }); + } +}; + +/** + * Session-based authentication middleware + * For web pages using sessions + */ +const authenticateSession = async (req, res, next) => { + try { + if (!req.session.userId) { + req.flash('error', '로그인이 필요합니다.'); + return res.redirect('/auth/login'); + } + + const user = await User.findById(req.session.userId).select('-password'); + + if (!user || !user.isActive) { + req.session.destroy(); + req.flash('error', '유효하지 않은 사용자입니다.'); + return res.redirect('/auth/login'); + } + + req.user = user; + res.locals.user = user; + next(); + } catch (error) { + console.error('Session authentication error:', error); + req.session.destroy(); + req.flash('error', '인증 오류가 발생했습니다.'); + return res.redirect('/auth/login'); + } +}; + +/** + * Admin role middleware + * Requires user to be authenticated and have admin role + */ +const requireAdmin = (req, res, next) => { + if (!req.user) { + return res.status(401).json({ + success: false, + message: 'Authentication required' + }); + } + + if (req.user.role !== 'admin') { + return res.status(403).json({ + success: false, + message: 'Admin access required' + }); + } + + next(); +}; + +/** + * Admin session middleware for web pages + */ +const requireAdminSession = (req, res, next) => { + if (!req.user) { + req.flash('error', '로그인이 필요합니다.'); + return res.redirect('/auth/login'); + } + + if (req.user.role !== 'admin') { + req.flash('error', '관리자 권한이 필요합니다.'); + return res.redirect('/'); + } + + next(); +}; + +/** + * Optional authentication middleware + * Attaches user if token exists but doesn't require it + */ +const optionalAuth = async (req, res, next) => { + try { + // Check session first + if (req.session.userId) { + const user = await User.findById(req.session.userId).select('-password'); + if (user && user.isActive) { + req.user = user; + res.locals.user = user; + } + } + + // Check JWT token if no session + if (!req.user) { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; + + if (token) { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + const user = await User.findById(decoded.userId).select('-password'); + + if (user && user.isActive) { + req.user = user; + res.locals.user = user; + } + } + } + + next(); + } catch (error) { + // Continue without authentication if token is invalid + next(); + } +}; + +module.exports = { + authenticateToken, + authenticateSession, + requireAdmin, + requireAdminSession, + optionalAuth +}; \ No newline at end of file diff --git a/.history/middleware/auth_20251019202102.js b/.history/middleware/auth_20251019202102.js new file mode 100644 index 0000000..50bd0e7 --- /dev/null +++ b/.history/middleware/auth_20251019202102.js @@ -0,0 +1,158 @@ +const jwt = require('jsonwebtoken'); +const { User } = require('../models'); + +/** + * Authentication middleware + * Verifies JWT token and attaches user to request + */ +const authenticateToken = async (req, res, next) => { + try { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN + + if (!token) { + return res.status(401).json({ + success: false, + message: 'Access token required' + }); + } + + const decoded = jwt.verify(token, process.env.JWT_SECRET); + const user = await User.findByPk(decoded.userId, { + attributes: { exclude: ['password'] } + }); + + if (!user || !user.isActive) { + return res.status(401).json({ + success: false, + message: 'Invalid or inactive user' + }); + } + + req.user = user; + next(); + } catch (error) { + console.error('Token verification error:', error); + return res.status(403).json({ + success: false, + message: 'Invalid token' + }); + } +}; + +/** + * Session-based authentication middleware + * For web pages using sessions + */ +const authenticateSession = async (req, res, next) => { + try { + if (!req.session.userId) { + req.flash('error', '로그인이 필요합니다.'); + return res.redirect('/auth/login'); + } + + const user = await User.findByPk(req.session.userId, { + attributes: { exclude: ['password'] } + }); + + if (!user || !user.isActive) { + req.session.destroy(); + req.flash('error', '유효하지 않은 사용자입니다.'); + return res.redirect('/auth/login'); + } + + req.user = user; + res.locals.user = user; + next(); + } catch (error) { + console.error('Session authentication error:', error); + req.session.destroy(); + req.flash('error', '인증 오류가 발생했습니다.'); + return res.redirect('/auth/login'); + } +}; + +/** + * Admin role middleware + * Requires user to be authenticated and have admin role + */ +const requireAdmin = (req, res, next) => { + if (!req.user) { + return res.status(401).json({ + success: false, + message: 'Authentication required' + }); + } + + if (req.user.role !== 'admin') { + return res.status(403).json({ + success: false, + message: 'Admin access required' + }); + } + + next(); +}; + +/** + * Admin session middleware for web pages + */ +const requireAdminSession = (req, res, next) => { + if (!req.user) { + req.flash('error', '로그인이 필요합니다.'); + return res.redirect('/auth/login'); + } + + if (req.user.role !== 'admin') { + req.flash('error', '관리자 권한이 필요합니다.'); + return res.redirect('/'); + } + + next(); +}; + +/** + * Optional authentication middleware + * Attaches user if token exists but doesn't require it + */ +const optionalAuth = async (req, res, next) => { + try { + // Check session first + if (req.session.userId) { + const user = await User.findById(req.session.userId).select('-password'); + if (user && user.isActive) { + req.user = user; + res.locals.user = user; + } + } + + // Check JWT token if no session + if (!req.user) { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; + + if (token) { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + const user = await User.findById(decoded.userId).select('-password'); + + if (user && user.isActive) { + req.user = user; + res.locals.user = user; + } + } + } + + next(); + } catch (error) { + // Continue without authentication if token is invalid + next(); + } +}; + +module.exports = { + authenticateToken, + authenticateSession, + requireAdmin, + requireAdminSession, + optionalAuth +}; \ No newline at end of file diff --git a/.history/middleware/auth_20251019202112.js b/.history/middleware/auth_20251019202112.js new file mode 100644 index 0000000..cee7633 --- /dev/null +++ b/.history/middleware/auth_20251019202112.js @@ -0,0 +1,162 @@ +const jwt = require('jsonwebtoken'); +const { User } = require('../models'); + +/** + * Authentication middleware + * Verifies JWT token and attaches user to request + */ +const authenticateToken = async (req, res, next) => { + try { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN + + if (!token) { + return res.status(401).json({ + success: false, + message: 'Access token required' + }); + } + + const decoded = jwt.verify(token, process.env.JWT_SECRET); + const user = await User.findByPk(decoded.userId, { + attributes: { exclude: ['password'] } + }); + + if (!user || !user.isActive) { + return res.status(401).json({ + success: false, + message: 'Invalid or inactive user' + }); + } + + req.user = user; + next(); + } catch (error) { + console.error('Token verification error:', error); + return res.status(403).json({ + success: false, + message: 'Invalid token' + }); + } +}; + +/** + * Session-based authentication middleware + * For web pages using sessions + */ +const authenticateSession = async (req, res, next) => { + try { + if (!req.session.userId) { + req.flash('error', '로그인이 필요합니다.'); + return res.redirect('/auth/login'); + } + + const user = await User.findByPk(req.session.userId, { + attributes: { exclude: ['password'] } + }); + + if (!user || !user.isActive) { + req.session.destroy(); + req.flash('error', '유효하지 않은 사용자입니다.'); + return res.redirect('/auth/login'); + } + + req.user = user; + res.locals.user = user; + next(); + } catch (error) { + console.error('Session authentication error:', error); + req.session.destroy(); + req.flash('error', '인증 오류가 발생했습니다.'); + return res.redirect('/auth/login'); + } +}; + +/** + * Admin role middleware + * Requires user to be authenticated and have admin role + */ +const requireAdmin = (req, res, next) => { + if (!req.user) { + return res.status(401).json({ + success: false, + message: 'Authentication required' + }); + } + + if (req.user.role !== 'admin') { + return res.status(403).json({ + success: false, + message: 'Admin access required' + }); + } + + next(); +}; + +/** + * Admin session middleware for web pages + */ +const requireAdminSession = (req, res, next) => { + if (!req.user) { + req.flash('error', '로그인이 필요합니다.'); + return res.redirect('/auth/login'); + } + + if (req.user.role !== 'admin') { + req.flash('error', '관리자 권한이 필요합니다.'); + return res.redirect('/'); + } + + next(); +}; + +/** + * Optional authentication middleware + * Attaches user if token exists but doesn't require it + */ +const optionalAuth = async (req, res, next) => { + try { + // Check session first + if (req.session.userId) { + const user = await User.findByPk(req.session.userId, { + attributes: { exclude: ['password'] } + }); + if (user && user.isActive) { + req.user = user; + res.locals.user = user; + } + } + + // Check JWT token if no session + if (!req.user) { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; + + if (token) { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + const user = await User.findByPk(decoded.userId, { + attributes: { exclude: ['password'] } + }); + + if (user && user.isActive) { + req.user = user; + res.locals.user = user; + } + } + } + + next(); + } catch (error) { + // Continue without authentication if token is invalid + next(); + } +}; + +module.exports = { + authenticateToken, + authenticateSession, + requireAdmin, + requireAdminSession, + optionalAuth +}; \ No newline at end of file diff --git a/.history/middleware/auth_20251019202631.js b/.history/middleware/auth_20251019202631.js new file mode 100644 index 0000000..cee7633 --- /dev/null +++ b/.history/middleware/auth_20251019202631.js @@ -0,0 +1,162 @@ +const jwt = require('jsonwebtoken'); +const { User } = require('../models'); + +/** + * Authentication middleware + * Verifies JWT token and attaches user to request + */ +const authenticateToken = async (req, res, next) => { + try { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN + + if (!token) { + return res.status(401).json({ + success: false, + message: 'Access token required' + }); + } + + const decoded = jwt.verify(token, process.env.JWT_SECRET); + const user = await User.findByPk(decoded.userId, { + attributes: { exclude: ['password'] } + }); + + if (!user || !user.isActive) { + return res.status(401).json({ + success: false, + message: 'Invalid or inactive user' + }); + } + + req.user = user; + next(); + } catch (error) { + console.error('Token verification error:', error); + return res.status(403).json({ + success: false, + message: 'Invalid token' + }); + } +}; + +/** + * Session-based authentication middleware + * For web pages using sessions + */ +const authenticateSession = async (req, res, next) => { + try { + if (!req.session.userId) { + req.flash('error', '로그인이 필요합니다.'); + return res.redirect('/auth/login'); + } + + const user = await User.findByPk(req.session.userId, { + attributes: { exclude: ['password'] } + }); + + if (!user || !user.isActive) { + req.session.destroy(); + req.flash('error', '유효하지 않은 사용자입니다.'); + return res.redirect('/auth/login'); + } + + req.user = user; + res.locals.user = user; + next(); + } catch (error) { + console.error('Session authentication error:', error); + req.session.destroy(); + req.flash('error', '인증 오류가 발생했습니다.'); + return res.redirect('/auth/login'); + } +}; + +/** + * Admin role middleware + * Requires user to be authenticated and have admin role + */ +const requireAdmin = (req, res, next) => { + if (!req.user) { + return res.status(401).json({ + success: false, + message: 'Authentication required' + }); + } + + if (req.user.role !== 'admin') { + return res.status(403).json({ + success: false, + message: 'Admin access required' + }); + } + + next(); +}; + +/** + * Admin session middleware for web pages + */ +const requireAdminSession = (req, res, next) => { + if (!req.user) { + req.flash('error', '로그인이 필요합니다.'); + return res.redirect('/auth/login'); + } + + if (req.user.role !== 'admin') { + req.flash('error', '관리자 권한이 필요합니다.'); + return res.redirect('/'); + } + + next(); +}; + +/** + * Optional authentication middleware + * Attaches user if token exists but doesn't require it + */ +const optionalAuth = async (req, res, next) => { + try { + // Check session first + if (req.session.userId) { + const user = await User.findByPk(req.session.userId, { + attributes: { exclude: ['password'] } + }); + if (user && user.isActive) { + req.user = user; + res.locals.user = user; + } + } + + // Check JWT token if no session + if (!req.user) { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; + + if (token) { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + const user = await User.findByPk(decoded.userId, { + attributes: { exclude: ['password'] } + }); + + if (user && user.isActive) { + req.user = user; + res.locals.user = user; + } + } + } + + next(); + } catch (error) { + // Continue without authentication if token is invalid + next(); + } +}; + +module.exports = { + authenticateToken, + authenticateSession, + requireAdmin, + requireAdminSession, + optionalAuth +}; \ No newline at end of file diff --git a/.history/models/Banner_20251022194806.js b/.history/models/Banner_20251022194806.js new file mode 100644 index 0000000..ab3dd28 --- /dev/null +++ b/.history/models/Banner_20251022194806.js @@ -0,0 +1,195 @@ +const { DataTypes } = require('sequelize'); +const { sequelize } = require('../config/database'); + +const Banner = sequelize.define('Banner', { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + title: { + type: DataTypes.STRING, + allowNull: false, + validate: { + notEmpty: true, + len: [1, 200] + } + }, + subtitle: { + type: DataTypes.STRING, + allowNull: true, + validate: { + len: [0, 300] + } + }, + description: { + type: DataTypes.TEXT, + allowNull: true + }, + buttonText: { + type: DataTypes.STRING, + allowNull: true, + validate: { + len: [0, 50] + } + }, + buttonUrl: { + type: DataTypes.STRING, + allowNull: true, + validate: { + isUrl: true + } + }, + image: { + type: DataTypes.STRING, + allowNull: true, + comment: 'Banner background image URL' + }, + mobileImage: { + type: DataTypes.STRING, + allowNull: true, + comment: 'Mobile-optimized banner image URL' + }, + position: { + type: DataTypes.ENUM('hero', 'secondary', 'footer'), + defaultValue: 'hero', + allowNull: false + }, + order: { + type: DataTypes.INTEGER, + defaultValue: 0, + allowNull: false, + comment: 'Display order (lower numbers appear first)' + }, + isActive: { + type: DataTypes.BOOLEAN, + defaultValue: true, + allowNull: false + }, + startDate: { + type: DataTypes.DATE, + allowNull: true, + comment: 'Banner start display date' + }, + endDate: { + type: DataTypes.DATE, + allowNull: true, + comment: 'Banner end display date' + }, + clickCount: { + type: DataTypes.INTEGER, + defaultValue: 0, + allowNull: false + }, + impressions: { + type: DataTypes.INTEGER, + defaultValue: 0, + allowNull: false + }, + targetAudience: { + type: DataTypes.ENUM('all', 'mobile', 'desktop'), + defaultValue: 'all', + allowNull: false + }, + backgroundColor: { + type: DataTypes.STRING, + allowNull: true, + validate: { + is: /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/ + }, + comment: 'Hex color code for banner background' + }, + textColor: { + type: DataTypes.STRING, + allowNull: true, + validate: { + is: /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/ + }, + comment: 'Hex color code for banner text' + }, + animation: { + type: DataTypes.ENUM('none', 'fade', 'slide', 'zoom'), + defaultValue: 'none', + allowNull: false + }, + metadata: { + type: DataTypes.JSONB, + allowNull: true, + defaultValue: {}, + comment: 'Additional banner metadata and settings' + } +}, { + tableName: 'banners', + timestamps: true, + paranoid: true, + indexes: [ + { + fields: ['isActive'] + }, + { + fields: ['position'] + }, + { + fields: ['order'] + }, + { + fields: ['startDate', 'endDate'] + } + ] +}); + +// Virtual field for checking if banner is currently active +Banner.prototype.isCurrentlyActive = function() { + if (!this.isActive) return false; + + const now = new Date(); + + if (this.startDate && now < this.startDate) return false; + if (this.endDate && now > this.endDate) return false; + + return true; +}; + +// Method to increment click count +Banner.prototype.recordClick = async function() { + this.clickCount += 1; + await this.save(); + return this; +}; + +// Method to increment impressions +Banner.prototype.recordImpression = async function() { + this.impressions += 1; + await this.save(); + return this; +}; + +// Static method to get active banners +Banner.getActiveBanners = async function(position = null) { + const whereClause = { + isActive: true + }; + + if (position) { + whereClause.position = position; + } + + const now = new Date(); + + return await this.findAll({ + where: { + ...whereClause, + [require('sequelize').Op.or]: [ + { startDate: null }, + { startDate: { [require('sequelize').Op.lte]: now } } + ], + [require('sequelize').Op.or]: [ + { endDate: null }, + { endDate: { [require('sequelize').Op.gte]: now } } + ] + }, + order: [['order', 'ASC'], ['createdAt', 'DESC']] + }); +}; + +module.exports = Banner; \ No newline at end of file diff --git a/.history/models/Banner_20251022195905.js b/.history/models/Banner_20251022195905.js new file mode 100644 index 0000000..ab3dd28 --- /dev/null +++ b/.history/models/Banner_20251022195905.js @@ -0,0 +1,195 @@ +const { DataTypes } = require('sequelize'); +const { sequelize } = require('../config/database'); + +const Banner = sequelize.define('Banner', { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + title: { + type: DataTypes.STRING, + allowNull: false, + validate: { + notEmpty: true, + len: [1, 200] + } + }, + subtitle: { + type: DataTypes.STRING, + allowNull: true, + validate: { + len: [0, 300] + } + }, + description: { + type: DataTypes.TEXT, + allowNull: true + }, + buttonText: { + type: DataTypes.STRING, + allowNull: true, + validate: { + len: [0, 50] + } + }, + buttonUrl: { + type: DataTypes.STRING, + allowNull: true, + validate: { + isUrl: true + } + }, + image: { + type: DataTypes.STRING, + allowNull: true, + comment: 'Banner background image URL' + }, + mobileImage: { + type: DataTypes.STRING, + allowNull: true, + comment: 'Mobile-optimized banner image URL' + }, + position: { + type: DataTypes.ENUM('hero', 'secondary', 'footer'), + defaultValue: 'hero', + allowNull: false + }, + order: { + type: DataTypes.INTEGER, + defaultValue: 0, + allowNull: false, + comment: 'Display order (lower numbers appear first)' + }, + isActive: { + type: DataTypes.BOOLEAN, + defaultValue: true, + allowNull: false + }, + startDate: { + type: DataTypes.DATE, + allowNull: true, + comment: 'Banner start display date' + }, + endDate: { + type: DataTypes.DATE, + allowNull: true, + comment: 'Banner end display date' + }, + clickCount: { + type: DataTypes.INTEGER, + defaultValue: 0, + allowNull: false + }, + impressions: { + type: DataTypes.INTEGER, + defaultValue: 0, + allowNull: false + }, + targetAudience: { + type: DataTypes.ENUM('all', 'mobile', 'desktop'), + defaultValue: 'all', + allowNull: false + }, + backgroundColor: { + type: DataTypes.STRING, + allowNull: true, + validate: { + is: /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/ + }, + comment: 'Hex color code for banner background' + }, + textColor: { + type: DataTypes.STRING, + allowNull: true, + validate: { + is: /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/ + }, + comment: 'Hex color code for banner text' + }, + animation: { + type: DataTypes.ENUM('none', 'fade', 'slide', 'zoom'), + defaultValue: 'none', + allowNull: false + }, + metadata: { + type: DataTypes.JSONB, + allowNull: true, + defaultValue: {}, + comment: 'Additional banner metadata and settings' + } +}, { + tableName: 'banners', + timestamps: true, + paranoid: true, + indexes: [ + { + fields: ['isActive'] + }, + { + fields: ['position'] + }, + { + fields: ['order'] + }, + { + fields: ['startDate', 'endDate'] + } + ] +}); + +// Virtual field for checking if banner is currently active +Banner.prototype.isCurrentlyActive = function() { + if (!this.isActive) return false; + + const now = new Date(); + + if (this.startDate && now < this.startDate) return false; + if (this.endDate && now > this.endDate) return false; + + return true; +}; + +// Method to increment click count +Banner.prototype.recordClick = async function() { + this.clickCount += 1; + await this.save(); + return this; +}; + +// Method to increment impressions +Banner.prototype.recordImpression = async function() { + this.impressions += 1; + await this.save(); + return this; +}; + +// Static method to get active banners +Banner.getActiveBanners = async function(position = null) { + const whereClause = { + isActive: true + }; + + if (position) { + whereClause.position = position; + } + + const now = new Date(); + + return await this.findAll({ + where: { + ...whereClause, + [require('sequelize').Op.or]: [ + { startDate: null }, + { startDate: { [require('sequelize').Op.lte]: now } } + ], + [require('sequelize').Op.or]: [ + { endDate: null }, + { endDate: { [require('sequelize').Op.gte]: now } } + ] + }, + order: [['order', 'ASC'], ['createdAt', 'DESC']] + }); +}; + +module.exports = Banner; \ No newline at end of file diff --git a/.history/models/Contact_20251019201845.js b/.history/models/Contact_20251019201845.js new file mode 100644 index 0000000..be16e21 --- /dev/null +++ b/.history/models/Contact_20251019201845.js @@ -0,0 +1,108 @@ +const { DataTypes } = require('sequelize'); +const { sequelize } = require('../config/database'); + +const Contact = sequelize.define('Contact', { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + name: { + type: DataTypes.STRING, + allowNull: false, + set(value) { + this.setDataValue('name', value.trim()); + } + }, + email: { + type: DataTypes.STRING, + allowNull: false, + validate: { + isEmail: true + }, + set(value) { + this.setDataValue('email', value.toLowerCase().trim()); + } + }, + phone: { + type: DataTypes.STRING, + allowNull: true, + set(value) { + this.setDataValue('phone', value ? value.trim() : null); + } + }, + company: { + type: DataTypes.STRING, + allowNull: true, + set(value) { + this.setDataValue('company', value ? value.trim() : null); + } + }, + subject: { + type: DataTypes.STRING, + allowNull: false, + set(value) { + this.setDataValue('subject', value.trim()); + } + }, + message: { + type: DataTypes.TEXT, + allowNull: false + }, + serviceInterest: { + type: DataTypes.ENUM('web-development', 'mobile-app', 'ui-ux-design', 'branding', 'consulting', 'other'), + allowNull: true + }, + budget: { + type: DataTypes.ENUM('under-1m', '1m-5m', '5m-10m', '10m-20m', '20m-50m', 'over-50m'), + allowNull: true + }, + timeline: { + type: DataTypes.ENUM('asap', '1-month', '1-3-months', '3-6-months', 'flexible'), + allowNull: true + }, + status: { + type: DataTypes.ENUM('new', 'in-progress', 'replied', 'closed'), + defaultValue: 'new' + }, + priority: { + type: DataTypes.ENUM('low', 'medium', 'high', 'urgent'), + defaultValue: 'medium' + }, + source: { + type: DataTypes.ENUM('website', 'telegram', 'email', 'phone', 'referral'), + defaultValue: 'website' + }, + isRead: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, + adminNotes: { + type: DataTypes.TEXT, + allowNull: true + }, + ipAddress: { + type: DataTypes.STRING, + allowNull: true + }, + userAgent: { + type: DataTypes.TEXT, + allowNull: true + } +}, { + tableName: 'contacts', + timestamps: true, + indexes: [ + { + fields: ['status', 'createdAt'] + }, + { + fields: ['isRead', 'createdAt'] + }, + { + fields: ['email'] + } + ] +}); + +module.exports = Contact; \ No newline at end of file diff --git a/.history/models/Contact_20251019202631.js b/.history/models/Contact_20251019202631.js new file mode 100644 index 0000000..be16e21 --- /dev/null +++ b/.history/models/Contact_20251019202631.js @@ -0,0 +1,108 @@ +const { DataTypes } = require('sequelize'); +const { sequelize } = require('../config/database'); + +const Contact = sequelize.define('Contact', { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + name: { + type: DataTypes.STRING, + allowNull: false, + set(value) { + this.setDataValue('name', value.trim()); + } + }, + email: { + type: DataTypes.STRING, + allowNull: false, + validate: { + isEmail: true + }, + set(value) { + this.setDataValue('email', value.toLowerCase().trim()); + } + }, + phone: { + type: DataTypes.STRING, + allowNull: true, + set(value) { + this.setDataValue('phone', value ? value.trim() : null); + } + }, + company: { + type: DataTypes.STRING, + allowNull: true, + set(value) { + this.setDataValue('company', value ? value.trim() : null); + } + }, + subject: { + type: DataTypes.STRING, + allowNull: false, + set(value) { + this.setDataValue('subject', value.trim()); + } + }, + message: { + type: DataTypes.TEXT, + allowNull: false + }, + serviceInterest: { + type: DataTypes.ENUM('web-development', 'mobile-app', 'ui-ux-design', 'branding', 'consulting', 'other'), + allowNull: true + }, + budget: { + type: DataTypes.ENUM('under-1m', '1m-5m', '5m-10m', '10m-20m', '20m-50m', 'over-50m'), + allowNull: true + }, + timeline: { + type: DataTypes.ENUM('asap', '1-month', '1-3-months', '3-6-months', 'flexible'), + allowNull: true + }, + status: { + type: DataTypes.ENUM('new', 'in-progress', 'replied', 'closed'), + defaultValue: 'new' + }, + priority: { + type: DataTypes.ENUM('low', 'medium', 'high', 'urgent'), + defaultValue: 'medium' + }, + source: { + type: DataTypes.ENUM('website', 'telegram', 'email', 'phone', 'referral'), + defaultValue: 'website' + }, + isRead: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, + adminNotes: { + type: DataTypes.TEXT, + allowNull: true + }, + ipAddress: { + type: DataTypes.STRING, + allowNull: true + }, + userAgent: { + type: DataTypes.TEXT, + allowNull: true + } +}, { + tableName: 'contacts', + timestamps: true, + indexes: [ + { + fields: ['status', 'createdAt'] + }, + { + fields: ['isRead', 'createdAt'] + }, + { + fields: ['email'] + } + ] +}); + +module.exports = Contact; \ No newline at end of file diff --git a/.history/models/Portfolio_20251019201803.js b/.history/models/Portfolio_20251019201803.js new file mode 100644 index 0000000..089f685 --- /dev/null +++ b/.history/models/Portfolio_20251019201803.js @@ -0,0 +1,121 @@ +const { DataTypes } = require('sequelize'); +const { sequelize } = require('../config/database'); + +const Portfolio = sequelize.define('Portfolio', { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + title: { + type: DataTypes.STRING, + allowNull: false, + validate: { + len: [1, 255] + }, + set(value) { + this.setDataValue('title', value.trim()); + } + }, + description: { + type: DataTypes.TEXT, + allowNull: false + }, + shortDescription: { + type: DataTypes.STRING(200), + allowNull: false + }, + category: { + type: DataTypes.ENUM('web-development', 'mobile-app', 'ui-ux-design', 'branding', 'e-commerce', 'other'), + allowNull: false + }, + technologies: { + type: DataTypes.ARRAY(DataTypes.STRING), + defaultValue: [] + }, + images: { + type: DataTypes.JSONB, + defaultValue: [] + }, + clientName: { + type: DataTypes.STRING, + allowNull: true, + set(value) { + this.setDataValue('clientName', value ? value.trim() : null); + } + }, + projectUrl: { + type: DataTypes.STRING, + allowNull: true, + validate: { + isUrl: true + } + }, + githubUrl: { + type: DataTypes.STRING, + allowNull: true, + validate: { + isUrl: true + } + }, + status: { + type: DataTypes.ENUM('completed', 'in-progress', 'planning'), + defaultValue: 'completed' + }, + featured: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, + publishedAt: { + type: DataTypes.DATE, + defaultValue: DataTypes.NOW + }, + completedAt: { + type: DataTypes.DATE, + allowNull: true + }, + isPublished: { + type: DataTypes.BOOLEAN, + defaultValue: true + }, + viewCount: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + likes: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + order: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + seo: { + type: DataTypes.JSONB, + defaultValue: {} + } +}, { + tableName: 'portfolios', + timestamps: true, + indexes: [ + { + fields: ['category', 'publishedAt'] + }, + { + fields: ['featured', 'publishedAt'] + }, + { + type: 'gin', + fields: ['technologies'] + } + ] +}); + +// Virtual for primary image +Portfolio.prototype.getPrimaryImage = function() { + if (!this.images || this.images.length === 0) return null; + const primary = this.images.find(img => img.isPrimary); + return primary || this.images[0]; +}; + +module.exports = Portfolio; \ No newline at end of file diff --git a/.history/models/Portfolio_20251019202630.js b/.history/models/Portfolio_20251019202630.js new file mode 100644 index 0000000..089f685 --- /dev/null +++ b/.history/models/Portfolio_20251019202630.js @@ -0,0 +1,121 @@ +const { DataTypes } = require('sequelize'); +const { sequelize } = require('../config/database'); + +const Portfolio = sequelize.define('Portfolio', { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + title: { + type: DataTypes.STRING, + allowNull: false, + validate: { + len: [1, 255] + }, + set(value) { + this.setDataValue('title', value.trim()); + } + }, + description: { + type: DataTypes.TEXT, + allowNull: false + }, + shortDescription: { + type: DataTypes.STRING(200), + allowNull: false + }, + category: { + type: DataTypes.ENUM('web-development', 'mobile-app', 'ui-ux-design', 'branding', 'e-commerce', 'other'), + allowNull: false + }, + technologies: { + type: DataTypes.ARRAY(DataTypes.STRING), + defaultValue: [] + }, + images: { + type: DataTypes.JSONB, + defaultValue: [] + }, + clientName: { + type: DataTypes.STRING, + allowNull: true, + set(value) { + this.setDataValue('clientName', value ? value.trim() : null); + } + }, + projectUrl: { + type: DataTypes.STRING, + allowNull: true, + validate: { + isUrl: true + } + }, + githubUrl: { + type: DataTypes.STRING, + allowNull: true, + validate: { + isUrl: true + } + }, + status: { + type: DataTypes.ENUM('completed', 'in-progress', 'planning'), + defaultValue: 'completed' + }, + featured: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, + publishedAt: { + type: DataTypes.DATE, + defaultValue: DataTypes.NOW + }, + completedAt: { + type: DataTypes.DATE, + allowNull: true + }, + isPublished: { + type: DataTypes.BOOLEAN, + defaultValue: true + }, + viewCount: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + likes: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + order: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + seo: { + type: DataTypes.JSONB, + defaultValue: {} + } +}, { + tableName: 'portfolios', + timestamps: true, + indexes: [ + { + fields: ['category', 'publishedAt'] + }, + { + fields: ['featured', 'publishedAt'] + }, + { + type: 'gin', + fields: ['technologies'] + } + ] +}); + +// Virtual for primary image +Portfolio.prototype.getPrimaryImage = function() { + if (!this.images || this.images.length === 0) return null; + const primary = this.images.find(img => img.isPrimary); + return primary || this.images[0]; +}; + +module.exports = Portfolio; \ No newline at end of file diff --git a/.history/models/Service_20251019201824.js b/.history/models/Service_20251019201824.js new file mode 100644 index 0000000..61a9098 --- /dev/null +++ b/.history/models/Service_20251019201824.js @@ -0,0 +1,96 @@ +const { DataTypes } = require('sequelize'); +const { sequelize } = require('../config/database'); + +const Service = sequelize.define('Service', { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + name: { + type: DataTypes.STRING, + allowNull: false, + validate: { + len: [1, 255] + }, + set(value) { + this.setDataValue('name', value.trim()); + } + }, + description: { + type: DataTypes.TEXT, + allowNull: false + }, + shortDescription: { + type: DataTypes.STRING(150), + allowNull: false + }, + icon: { + type: DataTypes.STRING, + allowNull: false + }, + category: { + type: DataTypes.ENUM('development', 'design', 'consulting', 'marketing', 'maintenance'), + allowNull: false + }, + features: { + type: DataTypes.JSONB, + defaultValue: [] + }, + pricing: { + type: DataTypes.JSONB, + allowNull: false, + validate: { + isValidPricing(value) { + if (!value.basePrice || value.basePrice < 0) { + throw new Error('Base price must be a positive number'); + } + } + } + }, + estimatedTime: { + type: DataTypes.JSONB, + allowNull: false, + validate: { + isValidTime(value) { + if (!value.min || !value.max || value.min > value.max) { + throw new Error('Invalid estimated time range'); + } + } + } + }, + isActive: { + type: DataTypes.BOOLEAN, + defaultValue: true + }, + featured: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, + order: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + tags: { + type: DataTypes.ARRAY(DataTypes.STRING), + defaultValue: [] + }, + seo: { + type: DataTypes.JSONB, + defaultValue: {} + } +}, { + tableName: 'services', + timestamps: true, + indexes: [ + { + fields: ['category', 'featured', 'order'] + }, + { + type: 'gin', + fields: ['tags'] + } + ] +}); + +module.exports = Service; \ No newline at end of file diff --git a/.history/models/Service_20251019202630.js b/.history/models/Service_20251019202630.js new file mode 100644 index 0000000..61a9098 --- /dev/null +++ b/.history/models/Service_20251019202630.js @@ -0,0 +1,96 @@ +const { DataTypes } = require('sequelize'); +const { sequelize } = require('../config/database'); + +const Service = sequelize.define('Service', { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + name: { + type: DataTypes.STRING, + allowNull: false, + validate: { + len: [1, 255] + }, + set(value) { + this.setDataValue('name', value.trim()); + } + }, + description: { + type: DataTypes.TEXT, + allowNull: false + }, + shortDescription: { + type: DataTypes.STRING(150), + allowNull: false + }, + icon: { + type: DataTypes.STRING, + allowNull: false + }, + category: { + type: DataTypes.ENUM('development', 'design', 'consulting', 'marketing', 'maintenance'), + allowNull: false + }, + features: { + type: DataTypes.JSONB, + defaultValue: [] + }, + pricing: { + type: DataTypes.JSONB, + allowNull: false, + validate: { + isValidPricing(value) { + if (!value.basePrice || value.basePrice < 0) { + throw new Error('Base price must be a positive number'); + } + } + } + }, + estimatedTime: { + type: DataTypes.JSONB, + allowNull: false, + validate: { + isValidTime(value) { + if (!value.min || !value.max || value.min > value.max) { + throw new Error('Invalid estimated time range'); + } + } + } + }, + isActive: { + type: DataTypes.BOOLEAN, + defaultValue: true + }, + featured: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, + order: { + type: DataTypes.INTEGER, + defaultValue: 0 + }, + tags: { + type: DataTypes.ARRAY(DataTypes.STRING), + defaultValue: [] + }, + seo: { + type: DataTypes.JSONB, + defaultValue: {} + } +}, { + tableName: 'services', + timestamps: true, + indexes: [ + { + fields: ['category', 'featured', 'order'] + }, + { + type: 'gin', + fields: ['tags'] + } + ] +}); + +module.exports = Service; \ No newline at end of file diff --git a/.history/models/SiteSettings_20251019201906.js b/.history/models/SiteSettings_20251019201906.js new file mode 100644 index 0000000..a8574c2 --- /dev/null +++ b/.history/models/SiteSettings_20251019201906.js @@ -0,0 +1,82 @@ +const { DataTypes } = require('sequelize'); +const { sequelize } = require('../config/database'); + +const SiteSettings = sequelize.define('SiteSettings', { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + siteName: { + type: DataTypes.STRING, + defaultValue: 'SmartSolTech' + }, + siteDescription: { + type: DataTypes.TEXT, + defaultValue: 'Innovative technology solutions for modern businesses' + }, + logo: { + type: DataTypes.STRING, + defaultValue: '/images/logo.png' + }, + favicon: { + type: DataTypes.STRING, + defaultValue: '/images/favicon.ico' + }, + contact: { + type: DataTypes.JSONB, + defaultValue: { + email: 'info@smartsoltech.kr', + phone: '+82-10-0000-0000', + address: 'Seoul, South Korea' + } + }, + social: { + type: DataTypes.JSONB, + defaultValue: {} + }, + telegram: { + type: DataTypes.JSONB, + defaultValue: { + isEnabled: false + } + }, + seo: { + type: DataTypes.JSONB, + defaultValue: { + metaTitle: 'SmartSolTech - Technology Solutions', + metaDescription: 'Professional web development, mobile apps, and digital solutions in Korea', + keywords: 'web development, mobile apps, UI/UX design, Korea, technology' + } + }, + hero: { + type: DataTypes.JSONB, + defaultValue: { + title: 'Smart Technology Solutions', + subtitle: 'We create innovative digital experiences that drive business growth', + backgroundImage: '/images/hero-bg.jpg', + ctaText: 'Get Started', + ctaLink: '#contact' + } + }, + about: { + type: DataTypes.JSONB, + defaultValue: { + title: 'About SmartSolTech', + description: 'We are a team of passionate developers and designers creating cutting-edge technology solutions.', + image: '/images/about.jpg' + } + }, + maintenance: { + type: DataTypes.JSONB, + defaultValue: { + isEnabled: false, + message: 'We are currently performing maintenance. Please check back soon.' + } + } +}, { + tableName: 'site_settings', + timestamps: true +}); + +module.exports = SiteSettings; \ No newline at end of file diff --git a/.history/models/SiteSettings_20251019202631.js b/.history/models/SiteSettings_20251019202631.js new file mode 100644 index 0000000..a8574c2 --- /dev/null +++ b/.history/models/SiteSettings_20251019202631.js @@ -0,0 +1,82 @@ +const { DataTypes } = require('sequelize'); +const { sequelize } = require('../config/database'); + +const SiteSettings = sequelize.define('SiteSettings', { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + siteName: { + type: DataTypes.STRING, + defaultValue: 'SmartSolTech' + }, + siteDescription: { + type: DataTypes.TEXT, + defaultValue: 'Innovative technology solutions for modern businesses' + }, + logo: { + type: DataTypes.STRING, + defaultValue: '/images/logo.png' + }, + favicon: { + type: DataTypes.STRING, + defaultValue: '/images/favicon.ico' + }, + contact: { + type: DataTypes.JSONB, + defaultValue: { + email: 'info@smartsoltech.kr', + phone: '+82-10-0000-0000', + address: 'Seoul, South Korea' + } + }, + social: { + type: DataTypes.JSONB, + defaultValue: {} + }, + telegram: { + type: DataTypes.JSONB, + defaultValue: { + isEnabled: false + } + }, + seo: { + type: DataTypes.JSONB, + defaultValue: { + metaTitle: 'SmartSolTech - Technology Solutions', + metaDescription: 'Professional web development, mobile apps, and digital solutions in Korea', + keywords: 'web development, mobile apps, UI/UX design, Korea, technology' + } + }, + hero: { + type: DataTypes.JSONB, + defaultValue: { + title: 'Smart Technology Solutions', + subtitle: 'We create innovative digital experiences that drive business growth', + backgroundImage: '/images/hero-bg.jpg', + ctaText: 'Get Started', + ctaLink: '#contact' + } + }, + about: { + type: DataTypes.JSONB, + defaultValue: { + title: 'About SmartSolTech', + description: 'We are a team of passionate developers and designers creating cutting-edge technology solutions.', + image: '/images/about.jpg' + } + }, + maintenance: { + type: DataTypes.JSONB, + defaultValue: { + isEnabled: false, + message: 'We are currently performing maintenance. Please check back soon.' + } + } +}, { + tableName: 'site_settings', + timestamps: true +}); + +module.exports = SiteSettings; \ No newline at end of file diff --git a/.history/models/User_20251019201741.js b/.history/models/User_20251019201741.js new file mode 100644 index 0000000..8643388 --- /dev/null +++ b/.history/models/User_20251019201741.js @@ -0,0 +1,78 @@ +const { DataTypes } = require('sequelize'); +const bcrypt = require('bcryptjs'); +const { sequelize } = require('../config/database'); + +const User = sequelize.define('User', { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + validate: { + isEmail: true + }, + set(value) { + this.setDataValue('email', value.toLowerCase().trim()); + } + }, + password: { + type: DataTypes.STRING, + allowNull: false, + validate: { + len: [6, 255] + } + }, + name: { + type: DataTypes.STRING, + allowNull: false, + validate: { + len: [1, 100] + }, + set(value) { + this.setDataValue('name', value.trim()); + } + }, + role: { + type: DataTypes.ENUM('admin', 'moderator'), + defaultValue: 'admin' + }, + avatar: { + type: DataTypes.STRING, + allowNull: true + }, + isActive: { + type: DataTypes.BOOLEAN, + defaultValue: true + }, + lastLogin: { + type: DataTypes.DATE, + allowNull: true + } +}, { + tableName: 'users', + timestamps: true, + hooks: { + beforeSave: async (user) => { + if (user.changed('password')) { + const salt = await bcrypt.genSalt(12); + user.password = await bcrypt.hash(user.password, salt); + } + } + } +}); + +// Instance methods +User.prototype.comparePassword = async function(candidatePassword) { + return bcrypt.compare(candidatePassword, this.password); +}; + +User.prototype.updateLastLogin = function() { + this.lastLogin = new Date(); + return this.save(); +}; + +module.exports = User; \ No newline at end of file diff --git a/.history/models/User_20251019202630.js b/.history/models/User_20251019202630.js new file mode 100644 index 0000000..8643388 --- /dev/null +++ b/.history/models/User_20251019202630.js @@ -0,0 +1,78 @@ +const { DataTypes } = require('sequelize'); +const bcrypt = require('bcryptjs'); +const { sequelize } = require('../config/database'); + +const User = sequelize.define('User', { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true + }, + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + validate: { + isEmail: true + }, + set(value) { + this.setDataValue('email', value.toLowerCase().trim()); + } + }, + password: { + type: DataTypes.STRING, + allowNull: false, + validate: { + len: [6, 255] + } + }, + name: { + type: DataTypes.STRING, + allowNull: false, + validate: { + len: [1, 100] + }, + set(value) { + this.setDataValue('name', value.trim()); + } + }, + role: { + type: DataTypes.ENUM('admin', 'moderator'), + defaultValue: 'admin' + }, + avatar: { + type: DataTypes.STRING, + allowNull: true + }, + isActive: { + type: DataTypes.BOOLEAN, + defaultValue: true + }, + lastLogin: { + type: DataTypes.DATE, + allowNull: true + } +}, { + tableName: 'users', + timestamps: true, + hooks: { + beforeSave: async (user) => { + if (user.changed('password')) { + const salt = await bcrypt.genSalt(12); + user.password = await bcrypt.hash(user.password, salt); + } + } + } +}); + +// Instance methods +User.prototype.comparePassword = async function(candidatePassword) { + return bcrypt.compare(candidatePassword, this.password); +}; + +User.prototype.updateLastLogin = function() { + this.lastLogin = new Date(); + return this.save(); +}; + +module.exports = User; \ No newline at end of file diff --git a/.history/models/index_20251019201914.js b/.history/models/index_20251019201914.js new file mode 100644 index 0000000..659182c --- /dev/null +++ b/.history/models/index_20251019201914.js @@ -0,0 +1,23 @@ +const { sequelize } = require('../config/database'); + +// Import models +const User = require('./User'); +const Portfolio = require('./Portfolio'); +const Service = require('./Service'); +const Contact = require('./Contact'); +const SiteSettings = require('./SiteSettings'); + +// Define associations here if needed +// For example: +// Service.belongsToMany(Portfolio, { through: 'ServicePortfolio' }); +// Portfolio.belongsToMany(Service, { through: 'ServicePortfolio' }); + +// Export models and sequelize instance +module.exports = { + sequelize, + User, + Portfolio, + Service, + Contact, + SiteSettings +}; \ No newline at end of file diff --git a/.history/models/index_20251019202631.js b/.history/models/index_20251019202631.js new file mode 100644 index 0000000..659182c --- /dev/null +++ b/.history/models/index_20251019202631.js @@ -0,0 +1,23 @@ +const { sequelize } = require('../config/database'); + +// Import models +const User = require('./User'); +const Portfolio = require('./Portfolio'); +const Service = require('./Service'); +const Contact = require('./Contact'); +const SiteSettings = require('./SiteSettings'); + +// Define associations here if needed +// For example: +// Service.belongsToMany(Portfolio, { through: 'ServicePortfolio' }); +// Portfolio.belongsToMany(Service, { through: 'ServicePortfolio' }); + +// Export models and sequelize instance +module.exports = { + sequelize, + User, + Portfolio, + Service, + Contact, + SiteSettings +}; \ No newline at end of file diff --git a/.history/models/index_20251022194816.js b/.history/models/index_20251022194816.js new file mode 100644 index 0000000..2a040ac --- /dev/null +++ b/.history/models/index_20251022194816.js @@ -0,0 +1,25 @@ +const { sequelize } = require('../config/database'); + +// Import models +const User = require('./User'); +const Portfolio = require('./Portfolio'); +const Service = require('./Service'); +const Contact = require('./Contact'); +const SiteSettings = require('./SiteSettings'); +const Banner = require('./Banner'); + +// Define associations here if needed +// For example: +// Service.belongsToMany(Portfolio, { through: 'ServicePortfolio' }); +// Portfolio.belongsToMany(Service, { through: 'ServicePortfolio' }); + +// Export models and sequelize instance +module.exports = { + sequelize, + User, + Portfolio, + Service, + Contact, + SiteSettings, + Banner +}; \ No newline at end of file diff --git a/.history/models/index_20251022195905.js b/.history/models/index_20251022195905.js new file mode 100644 index 0000000..2a040ac --- /dev/null +++ b/.history/models/index_20251022195905.js @@ -0,0 +1,25 @@ +const { sequelize } = require('../config/database'); + +// Import models +const User = require('./User'); +const Portfolio = require('./Portfolio'); +const Service = require('./Service'); +const Contact = require('./Contact'); +const SiteSettings = require('./SiteSettings'); +const Banner = require('./Banner'); + +// Define associations here if needed +// For example: +// Service.belongsToMany(Portfolio, { through: 'ServicePortfolio' }); +// Portfolio.belongsToMany(Service, { through: 'ServicePortfolio' }); + +// Export models and sequelize instance +module.exports = { + sequelize, + User, + Portfolio, + Service, + Contact, + SiteSettings, + Banner +}; \ No newline at end of file diff --git a/.history/package_20251021172116.json b/.history/package_20251021172116.json new file mode 100644 index 0000000..7b42816 --- /dev/null +++ b/.history/package_20251021172116.json @@ -0,0 +1,62 @@ +{ + "name": "smartsoltech-website", + "version": "1.0.0", + "description": "Modern PWA website for SmartSolTech with admin panel and Telegram integration", + "main": "server.js", + "scripts": { + "start": "node server.js", + "demo": "node server-demo.js", + "dev": "node scripts/dev.js", + "build": "node scripts/build.js", + "init-db": "node scripts/init-db.js", + "test": "echo \"Error: no test specified\" && exit 1", + "sync-locales": "node scripts/sync-locales.js" + }, + "keywords": [ + "pwa", + "nodejs", + "express", + "telegram", + "portfolio", + "calculator" + ], + "author": "SmartSolTech", + "license": "MIT", + "dependencies": { + "bcryptjs": "^2.4.3", + "compression": "^1.7.4", + "connect-flash": "^0.1.1", + "connect-session-sequelize": "^8.0.2", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "express-rate-limit": "^7.1.5", + "express-session": "^1.17.3", + "express-validator": "^7.0.1", + "helmet": "^7.1.0", + "i18n": "^0.15.2", + "jsonwebtoken": "^9.0.2", + "morgan": "^1.10.0", + "multer": "^1.4.5-lts.1", + "node-telegram-bot-api": "^0.64.0", + "nodemailer": "^6.9.7", + "pg": "^8.16.3", + "pg-hstore": "^2.3.4", + "sequelize": "^6.37.7", + "sharp": "^0.33.0", + "socket.io": "^4.7.4" + }, + "devDependencies": { + "css-loader": "^6.8.1", + "file-loader": "^6.2.0", + "html-webpack-plugin": "^5.5.3", + "mini-css-extract-plugin": "^2.7.6", + "nodemon": "^3.0.2", + "style-loader": "^3.3.3", + "terser-webpack-plugin": "^5.3.9", + "webpack": "^5.89.0", + "webpack-cli": "^5.1.4", + "workbox-webpack-plugin": "^7.0.0" + } +} diff --git a/.history/package_20251021172239.json b/.history/package_20251021172239.json new file mode 100644 index 0000000..7b42816 --- /dev/null +++ b/.history/package_20251021172239.json @@ -0,0 +1,62 @@ +{ + "name": "smartsoltech-website", + "version": "1.0.0", + "description": "Modern PWA website for SmartSolTech with admin panel and Telegram integration", + "main": "server.js", + "scripts": { + "start": "node server.js", + "demo": "node server-demo.js", + "dev": "node scripts/dev.js", + "build": "node scripts/build.js", + "init-db": "node scripts/init-db.js", + "test": "echo \"Error: no test specified\" && exit 1", + "sync-locales": "node scripts/sync-locales.js" + }, + "keywords": [ + "pwa", + "nodejs", + "express", + "telegram", + "portfolio", + "calculator" + ], + "author": "SmartSolTech", + "license": "MIT", + "dependencies": { + "bcryptjs": "^2.4.3", + "compression": "^1.7.4", + "connect-flash": "^0.1.1", + "connect-session-sequelize": "^8.0.2", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "express-rate-limit": "^7.1.5", + "express-session": "^1.17.3", + "express-validator": "^7.0.1", + "helmet": "^7.1.0", + "i18n": "^0.15.2", + "jsonwebtoken": "^9.0.2", + "morgan": "^1.10.0", + "multer": "^1.4.5-lts.1", + "node-telegram-bot-api": "^0.64.0", + "nodemailer": "^6.9.7", + "pg": "^8.16.3", + "pg-hstore": "^2.3.4", + "sequelize": "^6.37.7", + "sharp": "^0.33.0", + "socket.io": "^4.7.4" + }, + "devDependencies": { + "css-loader": "^6.8.1", + "file-loader": "^6.2.0", + "html-webpack-plugin": "^5.5.3", + "mini-css-extract-plugin": "^2.7.6", + "nodemon": "^3.0.2", + "style-loader": "^3.3.3", + "terser-webpack-plugin": "^5.3.9", + "webpack": "^5.89.0", + "webpack-cli": "^5.1.4", + "workbox-webpack-plugin": "^7.0.0" + } +} diff --git a/.history/package_20251022203957.json b/.history/package_20251022203957.json new file mode 100644 index 0000000..8db9954 --- /dev/null +++ b/.history/package_20251022203957.json @@ -0,0 +1,64 @@ +{ + "name": "smartsoltech-website", + "version": "1.0.0", + "description": "Modern PWA website for SmartSolTech with admin panel and Telegram integration", + "main": "server.js", + "scripts": { + "start": "node server.js", + "demo": "node server-demo.js", + "dev": "node scripts/dev.js", + "build": "node scripts/build.js", + "init-db": "node scripts/init-db.js", + "test": "echo \"Error: no test specified\" && exit 1", + "sync-locales": "node scripts/sync-locales.js" + }, + "keywords": [ + "pwa", + "nodejs", + "express", + "telegram", + "portfolio", + "calculator" + ], + "author": "SmartSolTech", + "license": "MIT", + "dependencies": { + "axios": "^1.12.2", + "bcryptjs": "^2.4.3", + "compression": "^1.7.4", + "connect-flash": "^0.1.1", + "connect-session-sequelize": "^7.1.7", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "express-ejs-layouts": "^2.5.1", + "express-rate-limit": "^7.1.5", + "express-session": "^1.17.3", + "express-validator": "^7.0.1", + "helmet": "^7.1.0", + "i18n": "^0.15.2", + "jsonwebtoken": "^9.0.2", + "morgan": "^1.10.0", + "multer": "^1.4.5-lts.1", + "node-telegram-bot-api": "^0.64.0", + "nodemailer": "^6.9.7", + "pg": "^8.16.3", + "pg-hstore": "^2.3.4", + "sequelize": "^6.37.7", + "sharp": "^0.33.5", + "socket.io": "^4.7.4" + }, + "devDependencies": { + "css-loader": "^6.8.1", + "file-loader": "^6.2.0", + "html-webpack-plugin": "^5.5.3", + "mini-css-extract-plugin": "^2.7.6", + "nodemon": "^3.0.2", + "style-loader": "^3.3.3", + "terser-webpack-plugin": "^5.3.9", + "webpack": "^5.89.0", + "webpack-cli": "^5.1.4", + "workbox-webpack-plugin": "^7.0.0" + } +} diff --git a/.history/package_20251022204114.json b/.history/package_20251022204114.json new file mode 100644 index 0000000..8db9954 --- /dev/null +++ b/.history/package_20251022204114.json @@ -0,0 +1,64 @@ +{ + "name": "smartsoltech-website", + "version": "1.0.0", + "description": "Modern PWA website for SmartSolTech with admin panel and Telegram integration", + "main": "server.js", + "scripts": { + "start": "node server.js", + "demo": "node server-demo.js", + "dev": "node scripts/dev.js", + "build": "node scripts/build.js", + "init-db": "node scripts/init-db.js", + "test": "echo \"Error: no test specified\" && exit 1", + "sync-locales": "node scripts/sync-locales.js" + }, + "keywords": [ + "pwa", + "nodejs", + "express", + "telegram", + "portfolio", + "calculator" + ], + "author": "SmartSolTech", + "license": "MIT", + "dependencies": { + "axios": "^1.12.2", + "bcryptjs": "^2.4.3", + "compression": "^1.7.4", + "connect-flash": "^0.1.1", + "connect-session-sequelize": "^7.1.7", + "cookie-parser": "^1.4.7", + "cors": "^2.8.5", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "express-ejs-layouts": "^2.5.1", + "express-rate-limit": "^7.1.5", + "express-session": "^1.17.3", + "express-validator": "^7.0.1", + "helmet": "^7.1.0", + "i18n": "^0.15.2", + "jsonwebtoken": "^9.0.2", + "morgan": "^1.10.0", + "multer": "^1.4.5-lts.1", + "node-telegram-bot-api": "^0.64.0", + "nodemailer": "^6.9.7", + "pg": "^8.16.3", + "pg-hstore": "^2.3.4", + "sequelize": "^6.37.7", + "sharp": "^0.33.5", + "socket.io": "^4.7.4" + }, + "devDependencies": { + "css-loader": "^6.8.1", + "file-loader": "^6.2.0", + "html-webpack-plugin": "^5.5.3", + "mini-css-extract-plugin": "^2.7.6", + "nodemon": "^3.0.2", + "style-loader": "^3.3.3", + "terser-webpack-plugin": "^5.3.9", + "webpack": "^5.89.0", + "webpack-cli": "^5.1.4", + "workbox-webpack-plugin": "^7.0.0" + } +} diff --git a/.history/public/css/base_20251020045405.css b/.history/public/css/base_20251020045405.css new file mode 100644 index 0000000..cced8b5 --- /dev/null +++ b/.history/public/css/base_20251020045405.css @@ -0,0 +1,325 @@ +/* SmartSolTech - Base Styles for Elements */ + +/* Ensure proper styling without Tailwind conflicts */ +.navbar { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + z-index: 1000 !important; + background: rgba(255, 255, 255, 0.95) !important; + backdrop-filter: blur(10px) !important; + border-bottom: 1px solid #e5e7eb !important; + padding: 1rem 0 !important; +} + +.navbar-brand { + font-size: 1.5rem !important; + font-weight: 700 !important; + color: #3B82F6 !important; + text-decoration: none !important; +} + +.navbar-nav { + display: flex !important; + align-items: center !important; + gap: 2rem !important; + list-style: none !important; + margin: 0 !important; + padding: 0 !important; +} + +.nav-link { + color: #6b7280 !important; + text-decoration: none !important; + font-weight: 500 !important; + padding: 0.5rem 1rem !important; + border-radius: 0.5rem !important; + transition: all 0.3s ease !important; +} + +.nav-link:hover, +.nav-link.active { + color: #3B82F6 !important; + background-color: #eff6ff !important; +} + +/* Hero Section */ +.hero-section { + min-height: 100vh !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; + color: white !important; + text-align: center !important; + padding: 2rem !important; +} + +.hero-title { + font-size: 3.5rem !important; + font-weight: 700 !important; + margin-bottom: 1rem !important; + line-height: 1.2 !important; +} + +.hero-subtitle { + font-size: 1.25rem !important; + margin-bottom: 2rem !important; + color: rgba(255, 255, 255, 0.9) !important; +} + +/* Buttons */ +.btn { + display: inline-block !important; + padding: 0.75rem 1.5rem !important; + border-radius: 0.5rem !important; + text-decoration: none !important; + font-weight: 600 !important; + text-align: center !important; + border: none !important; + cursor: pointer !important; + transition: all 0.3s ease !important; + font-size: 1rem !important; +} + +.btn-primary { + background: linear-gradient(135deg, #3B82F6, #1D4ED8) !important; + color: white !important; +} + +.btn-primary:hover { + transform: translateY(-2px) !important; + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3) !important; +} + +.btn-secondary { + background: transparent !important; + color: white !important; + border: 2px solid white !important; +} + +.btn-secondary:hover { + background: white !important; + color: #3B82F6 !important; +} + +/* Cards */ +.card { + background: white !important; + border-radius: 1rem !important; + padding: 2rem !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; + transition: all 0.3s ease !important; +} + +.card:hover { + transform: translateY(-4px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +/* Sections */ +.section { + padding: 4rem 2rem !important; +} + +.section-title { + font-size: 2.5rem !important; + font-weight: 700 !important; + text-align: center !important; + margin-bottom: 1rem !important; + color: #1f2937 !important; +} + +.section-description { + font-size: 1.125rem !important; + text-align: center !important; + color: #6b7280 !important; + max-width: 600px !important; + margin: 0 auto 3rem !important; +} + +/* Container */ +.container { + max-width: 1200px !important; + margin: 0 auto !important; + padding: 0 1rem !important; +} + +/* Grid Layouts */ +.grid-2 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)) !important; + gap: 2rem !important; +} + +.grid-3 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)) !important; + gap: 2rem !important; +} + +.grid-4 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) !important; + gap: 1.5rem !important; +} + +/* Language Selector */ +.language-selector { + display: flex !important; + align-items: center !important; + gap: 0.5rem !important; +} + +.language-selector a { + color: #6b7280 !important; + text-decoration: none !important; + padding: 0.25rem 0.5rem !important; + border-radius: 0.25rem !important; + font-size: 0.875rem !important; +} + +.language-selector a:hover { + color: #3B82F6 !important; + background-color: #f3f4f6 !important; +} + +/* Mobile Menu */ +.mobile-menu-button { + display: none !important; + background: none !important; + border: none !important; + font-size: 1.5rem !important; + color: #6b7280 !important; + cursor: pointer !important; +} + +/* Mobile Styles */ +@media (max-width: 768px) { + .hero-title { + font-size: 2.5rem !important; + } + + .mobile-menu-button { + display: block !important; + } + + .navbar-nav { + display: none !important; + position: absolute !important; + top: 100% !important; + left: 0 !important; + right: 0 !important; + background: white !important; + flex-direction: column !important; + padding: 1rem !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) !important; + } + + .navbar-nav.show { + display: flex !important; + } + + .section { + padding: 2rem 1rem !important; + } + + .section-title { + font-size: 2rem !important; + } +} + +/* Footer */ +.footer { + background: #1f2937 !important; + color: white !important; + padding: 3rem 2rem 1rem !important; + text-align: center !important; +} + +.footer p { + color: #d1d5db !important; +} + +/* Service Icons */ +.service-icon { + width: 80px !important; + height: 80px !important; + background: linear-gradient(135deg, #3B82F6, #8B5CF6) !important; + border-radius: 50% !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + font-size: 2rem !important; + color: white !important; + margin: 0 auto 1rem !important; +} + +/* Portfolio Items */ +.portfolio-item { + background: white !important; + border-radius: 1rem !important; + overflow: hidden !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; + transition: all 0.3s ease !important; +} + +.portfolio-item:hover { + transform: translateY(-4px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +.portfolio-image { + width: 100% !important; + height: 200px !important; + object-fit: cover !important; + display: block !important; +} + +.portfolio-content { + padding: 1.5rem !important; +} + +.portfolio-title { + font-size: 1.25rem !important; + font-weight: 600 !important; + margin-bottom: 0.5rem !important; + color: #1f2937 !important; +} + +.portfolio-description { + color: #6b7280 !important; + margin-bottom: 1rem !important; +} + +/* Form Styles */ +.form-group { + margin-bottom: 1.5rem !important; +} + +.form-label { + display: block !important; + font-weight: 500 !important; + margin-bottom: 0.5rem !important; + color: #374151 !important; +} + +.form-input, +.form-textarea, +.form-select { + width: 100% !important; + padding: 0.75rem !important; + border: 2px solid #e5e7eb !important; + border-radius: 0.5rem !important; + font-size: 1rem !important; + transition: all 0.3s ease !important; +} + +.form-input:focus, +.form-textarea:focus, +.form-select:focus { + outline: none !important; + border-color: #3B82F6 !important; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; +} \ No newline at end of file diff --git a/.history/public/css/base_20251020045409.css b/.history/public/css/base_20251020045409.css new file mode 100644 index 0000000..cced8b5 --- /dev/null +++ b/.history/public/css/base_20251020045409.css @@ -0,0 +1,325 @@ +/* SmartSolTech - Base Styles for Elements */ + +/* Ensure proper styling without Tailwind conflicts */ +.navbar { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + z-index: 1000 !important; + background: rgba(255, 255, 255, 0.95) !important; + backdrop-filter: blur(10px) !important; + border-bottom: 1px solid #e5e7eb !important; + padding: 1rem 0 !important; +} + +.navbar-brand { + font-size: 1.5rem !important; + font-weight: 700 !important; + color: #3B82F6 !important; + text-decoration: none !important; +} + +.navbar-nav { + display: flex !important; + align-items: center !important; + gap: 2rem !important; + list-style: none !important; + margin: 0 !important; + padding: 0 !important; +} + +.nav-link { + color: #6b7280 !important; + text-decoration: none !important; + font-weight: 500 !important; + padding: 0.5rem 1rem !important; + border-radius: 0.5rem !important; + transition: all 0.3s ease !important; +} + +.nav-link:hover, +.nav-link.active { + color: #3B82F6 !important; + background-color: #eff6ff !important; +} + +/* Hero Section */ +.hero-section { + min-height: 100vh !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; + color: white !important; + text-align: center !important; + padding: 2rem !important; +} + +.hero-title { + font-size: 3.5rem !important; + font-weight: 700 !important; + margin-bottom: 1rem !important; + line-height: 1.2 !important; +} + +.hero-subtitle { + font-size: 1.25rem !important; + margin-bottom: 2rem !important; + color: rgba(255, 255, 255, 0.9) !important; +} + +/* Buttons */ +.btn { + display: inline-block !important; + padding: 0.75rem 1.5rem !important; + border-radius: 0.5rem !important; + text-decoration: none !important; + font-weight: 600 !important; + text-align: center !important; + border: none !important; + cursor: pointer !important; + transition: all 0.3s ease !important; + font-size: 1rem !important; +} + +.btn-primary { + background: linear-gradient(135deg, #3B82F6, #1D4ED8) !important; + color: white !important; +} + +.btn-primary:hover { + transform: translateY(-2px) !important; + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3) !important; +} + +.btn-secondary { + background: transparent !important; + color: white !important; + border: 2px solid white !important; +} + +.btn-secondary:hover { + background: white !important; + color: #3B82F6 !important; +} + +/* Cards */ +.card { + background: white !important; + border-radius: 1rem !important; + padding: 2rem !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; + transition: all 0.3s ease !important; +} + +.card:hover { + transform: translateY(-4px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +/* Sections */ +.section { + padding: 4rem 2rem !important; +} + +.section-title { + font-size: 2.5rem !important; + font-weight: 700 !important; + text-align: center !important; + margin-bottom: 1rem !important; + color: #1f2937 !important; +} + +.section-description { + font-size: 1.125rem !important; + text-align: center !important; + color: #6b7280 !important; + max-width: 600px !important; + margin: 0 auto 3rem !important; +} + +/* Container */ +.container { + max-width: 1200px !important; + margin: 0 auto !important; + padding: 0 1rem !important; +} + +/* Grid Layouts */ +.grid-2 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)) !important; + gap: 2rem !important; +} + +.grid-3 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)) !important; + gap: 2rem !important; +} + +.grid-4 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) !important; + gap: 1.5rem !important; +} + +/* Language Selector */ +.language-selector { + display: flex !important; + align-items: center !important; + gap: 0.5rem !important; +} + +.language-selector a { + color: #6b7280 !important; + text-decoration: none !important; + padding: 0.25rem 0.5rem !important; + border-radius: 0.25rem !important; + font-size: 0.875rem !important; +} + +.language-selector a:hover { + color: #3B82F6 !important; + background-color: #f3f4f6 !important; +} + +/* Mobile Menu */ +.mobile-menu-button { + display: none !important; + background: none !important; + border: none !important; + font-size: 1.5rem !important; + color: #6b7280 !important; + cursor: pointer !important; +} + +/* Mobile Styles */ +@media (max-width: 768px) { + .hero-title { + font-size: 2.5rem !important; + } + + .mobile-menu-button { + display: block !important; + } + + .navbar-nav { + display: none !important; + position: absolute !important; + top: 100% !important; + left: 0 !important; + right: 0 !important; + background: white !important; + flex-direction: column !important; + padding: 1rem !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) !important; + } + + .navbar-nav.show { + display: flex !important; + } + + .section { + padding: 2rem 1rem !important; + } + + .section-title { + font-size: 2rem !important; + } +} + +/* Footer */ +.footer { + background: #1f2937 !important; + color: white !important; + padding: 3rem 2rem 1rem !important; + text-align: center !important; +} + +.footer p { + color: #d1d5db !important; +} + +/* Service Icons */ +.service-icon { + width: 80px !important; + height: 80px !important; + background: linear-gradient(135deg, #3B82F6, #8B5CF6) !important; + border-radius: 50% !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + font-size: 2rem !important; + color: white !important; + margin: 0 auto 1rem !important; +} + +/* Portfolio Items */ +.portfolio-item { + background: white !important; + border-radius: 1rem !important; + overflow: hidden !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; + transition: all 0.3s ease !important; +} + +.portfolio-item:hover { + transform: translateY(-4px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +.portfolio-image { + width: 100% !important; + height: 200px !important; + object-fit: cover !important; + display: block !important; +} + +.portfolio-content { + padding: 1.5rem !important; +} + +.portfolio-title { + font-size: 1.25rem !important; + font-weight: 600 !important; + margin-bottom: 0.5rem !important; + color: #1f2937 !important; +} + +.portfolio-description { + color: #6b7280 !important; + margin-bottom: 1rem !important; +} + +/* Form Styles */ +.form-group { + margin-bottom: 1.5rem !important; +} + +.form-label { + display: block !important; + font-weight: 500 !important; + margin-bottom: 0.5rem !important; + color: #374151 !important; +} + +.form-input, +.form-textarea, +.form-select { + width: 100% !important; + padding: 0.75rem !important; + border: 2px solid #e5e7eb !important; + border-radius: 0.5rem !important; + font-size: 1rem !important; + transition: all 0.3s ease !important; +} + +.form-input:focus, +.form-textarea:focus, +.form-select:focus { + outline: none !important; + border-color: #3B82F6 !important; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; +} \ No newline at end of file diff --git a/.history/public/css/base_20251020050032.css b/.history/public/css/base_20251020050032.css new file mode 100644 index 0000000..8bfbc24 --- /dev/null +++ b/.history/public/css/base_20251020050032.css @@ -0,0 +1,590 @@ +/* SmartSolTech - Base Styles for Elements */ + +/* Ensure proper styling without Tailwind conflicts */ +.navbar { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + z-index: 1000 !important; + background: rgba(255, 255, 255, 0.95) !important; + backdrop-filter: blur(10px) !important; + border-bottom: 1px solid #e5e7eb !important; + padding: 1rem 0 !important; +} + +.navbar-brand { + font-size: 1.5rem !important; + font-weight: 700 !important; + color: #3B82F6 !important; + text-decoration: none !important; +} + +.navbar-nav { + display: flex !important; + align-items: center !important; + gap: 2rem !important; + list-style: none !important; + margin: 0 !important; + padding: 0 !important; +} + +.nav-link { + color: #6b7280 !important; + text-decoration: none !important; + font-weight: 500 !important; + padding: 0.5rem 1rem !important; + border-radius: 0.5rem !important; + transition: all 0.3s ease !important; +} + +.nav-link:hover, +.nav-link.active { + color: #3B82F6 !important; + background-color: #eff6ff !important; +} + +/* Base CSS - Принудительные стили для правильного отображения */ + +/* Reset и базовые стили */ +* { + box-sizing: border-box !important; +} + +html, body { + margin: 0 !important; + padding: 0 !important; + font-family: 'Inter', system-ui, -apple-system, sans-serif !important; + line-height: 1.6 !important; + color: #333 !important; + scroll-behavior: smooth !important; +} + +body { + padding-top: 80px !important; /* Отступ для фиксированной навигации */ +} + +/* Навигация */ +nav, .navigation { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + z-index: 1000 !important; + background: rgba(255, 255, 255, 0.95) !important; + backdrop-filter: blur(10px) !important; + border-bottom: 1px solid #e5e7eb !important; + height: 80px !important; +} + +.navbar, .nav-container { + display: flex !important; + justify-content: space-between !important; + align-items: center !important; + padding: 1rem 2rem !important; + max-width: 1200px !important; + margin: 0 auto !important; + height: 100% !important; +} + +.logo, .brand { + font-size: 1.5rem !important; + font-weight: 700 !important; + color: #1d4ed8 !important; + text-decoration: none !important; +} + +.nav-links, .menu { + display: flex !important; + list-style: none !important; + margin: 0 !important; + padding: 0 !important; + gap: 2rem !important; +} + +.nav-links a, .menu a { + color: #374151 !important; + text-decoration: none !important; + font-weight: 500 !important; + transition: color 0.3s ease !important; + padding: 0.5rem 0 !important; +} + +.nav-links a:hover, .menu a:hover { + color: #1d4ed8 !important; +} + +/* Hero секция */ +.hero-section { + min-height: 100vh !important; + background: linear-gradient(135deg, #1e3a8a 0%, #7c3aed 50%, #3730a3 100%) !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + text-align: center !important; + color: white !important; + position: relative !important; + overflow: hidden !important; + margin-top: -80px !important; /* Компенсация отступа */ + padding-top: 80px !important; +} + +.hero-section h1 { + font-size: 3.5rem !important; + font-weight: 700 !important; + margin-bottom: 1.5rem !important; + line-height: 1.2 !important; +} + +.hero-section p { + font-size: 1.25rem !important; + margin-bottom: 2rem !important; + opacity: 0.9 !important; +} + +/* Кнопки */ +.btn-primary, .btn { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + padding: 1rem 2rem !important; + background: linear-gradient(45deg, #3b82f6, #8b5cf6) !important; + color: white !important; + text-decoration: none !important; + border-radius: 50px !important; + font-weight: 600 !important; + transition: all 0.3s ease !important; + border: none !important; + cursor: pointer !important; + font-size: 1rem !important; +} + +.btn-primary:hover, .btn:hover { + transform: translateY(-2px) !important; + box-shadow: 0 10px 30px rgba(59, 130, 246, 0.3) !important; +} + +/* Карточки */ +.card-hover, .card { + transition: all 0.3s ease !important; + border-radius: 1rem !important; + overflow: hidden !important; + background: white !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; +} + +.card-hover:hover, .card:hover { + transform: translateY(-8px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +/* Секции */ +section { + padding: 5rem 1rem !important; +} + +.container, .max-w-7xl { + max-width: 1200px !important; + margin: 0 auto !important; + padding-left: 1rem !important; + padding-right: 1rem !important; +} + +/* Грид системы */ +.grid { + display: grid !important; +} + +.grid-cols-1 { + grid-template-columns: 1fr !important; +} + +.grid-cols-2 { + grid-template-columns: repeat(2, 1fr) !important; +} + +.grid-cols-3 { + grid-template-columns: repeat(3, 1fr) !important; +} + +.grid-cols-4 { + grid-template-columns: repeat(4, 1fr) !important; +} + +.gap-8 { + gap: 2rem !important; +} + +/* Заголовки */ +h1, h2, h3, h4, h5, h6 { + color: #1f2937 !important; + font-weight: 600 !important; +} + +h1 { + font-size: 3rem !important; +} + +h2 { + font-size: 2.5rem !important; +} + +h3 { + font-size: 1.75rem !important; +} + +/* Footer */ +footer { + background: #1f2937 !important; + color: white !important; + padding: 3rem 1rem 1rem !important; +} + +/* Отзывчивость */ +@media (max-width: 768px) { + .nav-links, .menu { + display: none !important; + } + + body { + padding-top: 60px !important; + } + + nav, .navigation { + height: 60px !important; + } + + .hero-section { + margin-top: -60px !important; + padding-top: 60px !important; + } + + section { + padding: 3rem 1rem !important; + } + + .hero-section h1 { + font-size: 2.5rem !important; + } + + .grid-cols-2, .grid-cols-3, .grid-cols-4 { + grid-template-columns: 1fr !important; + } +} + +/* Дополнительные утилитарные классы */ +.text-center { + text-align: center !important; +} + +.text-white { + color: white !important; +} + +.bg-white { + background-color: white !important; +} + +.rounded-lg { + border-radius: 0.5rem !important; +} + +.rounded-2xl { + border-radius: 1rem !important; +} + +.shadow-lg { + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1) !important; +} + +.mb-4 { + margin-bottom: 1rem !important; +} + +.mb-8 { + margin-bottom: 2rem !important; +} + +.p-8 { + padding: 2rem !important; +} + +.flex { + display: flex !important; +} + +.items-center { + align-items: center !important; +} + +.justify-center { + justify-content: center !important; +} + +.hero-title { + font-size: 3.5rem !important; + font-weight: 700 !important; + margin-bottom: 1rem !important; + line-height: 1.2 !important; +} + +.hero-subtitle { + font-size: 1.25rem !important; + margin-bottom: 2rem !important; + color: rgba(255, 255, 255, 0.9) !important; +} + +/* Buttons */ +.btn { + display: inline-block !important; + padding: 0.75rem 1.5rem !important; + border-radius: 0.5rem !important; + text-decoration: none !important; + font-weight: 600 !important; + text-align: center !important; + border: none !important; + cursor: pointer !important; + transition: all 0.3s ease !important; + font-size: 1rem !important; +} + +.btn-primary { + background: linear-gradient(135deg, #3B82F6, #1D4ED8) !important; + color: white !important; +} + +.btn-primary:hover { + transform: translateY(-2px) !important; + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3) !important; +} + +.btn-secondary { + background: transparent !important; + color: white !important; + border: 2px solid white !important; +} + +.btn-secondary:hover { + background: white !important; + color: #3B82F6 !important; +} + +/* Cards */ +.card { + background: white !important; + border-radius: 1rem !important; + padding: 2rem !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; + transition: all 0.3s ease !important; +} + +.card:hover { + transform: translateY(-4px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +/* Sections */ +.section { + padding: 4rem 2rem !important; +} + +.section-title { + font-size: 2.5rem !important; + font-weight: 700 !important; + text-align: center !important; + margin-bottom: 1rem !important; + color: #1f2937 !important; +} + +.section-description { + font-size: 1.125rem !important; + text-align: center !important; + color: #6b7280 !important; + max-width: 600px !important; + margin: 0 auto 3rem !important; +} + +/* Container */ +.container { + max-width: 1200px !important; + margin: 0 auto !important; + padding: 0 1rem !important; +} + +/* Grid Layouts */ +.grid-2 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)) !important; + gap: 2rem !important; +} + +.grid-3 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)) !important; + gap: 2rem !important; +} + +.grid-4 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) !important; + gap: 1.5rem !important; +} + +/* Language Selector */ +.language-selector { + display: flex !important; + align-items: center !important; + gap: 0.5rem !important; +} + +.language-selector a { + color: #6b7280 !important; + text-decoration: none !important; + padding: 0.25rem 0.5rem !important; + border-radius: 0.25rem !important; + font-size: 0.875rem !important; +} + +.language-selector a:hover { + color: #3B82F6 !important; + background-color: #f3f4f6 !important; +} + +/* Mobile Menu */ +.mobile-menu-button { + display: none !important; + background: none !important; + border: none !important; + font-size: 1.5rem !important; + color: #6b7280 !important; + cursor: pointer !important; +} + +/* Mobile Styles */ +@media (max-width: 768px) { + .hero-title { + font-size: 2.5rem !important; + } + + .mobile-menu-button { + display: block !important; + } + + .navbar-nav { + display: none !important; + position: absolute !important; + top: 100% !important; + left: 0 !important; + right: 0 !important; + background: white !important; + flex-direction: column !important; + padding: 1rem !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) !important; + } + + .navbar-nav.show { + display: flex !important; + } + + .section { + padding: 2rem 1rem !important; + } + + .section-title { + font-size: 2rem !important; + } +} + +/* Footer */ +.footer { + background: #1f2937 !important; + color: white !important; + padding: 3rem 2rem 1rem !important; + text-align: center !important; +} + +.footer p { + color: #d1d5db !important; +} + +/* Service Icons */ +.service-icon { + width: 80px !important; + height: 80px !important; + background: linear-gradient(135deg, #3B82F6, #8B5CF6) !important; + border-radius: 50% !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + font-size: 2rem !important; + color: white !important; + margin: 0 auto 1rem !important; +} + +/* Portfolio Items */ +.portfolio-item { + background: white !important; + border-radius: 1rem !important; + overflow: hidden !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; + transition: all 0.3s ease !important; +} + +.portfolio-item:hover { + transform: translateY(-4px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +.portfolio-image { + width: 100% !important; + height: 200px !important; + object-fit: cover !important; + display: block !important; +} + +.portfolio-content { + padding: 1.5rem !important; +} + +.portfolio-title { + font-size: 1.25rem !important; + font-weight: 600 !important; + margin-bottom: 0.5rem !important; + color: #1f2937 !important; +} + +.portfolio-description { + color: #6b7280 !important; + margin-bottom: 1rem !important; +} + +/* Form Styles */ +.form-group { + margin-bottom: 1.5rem !important; +} + +.form-label { + display: block !important; + font-weight: 500 !important; + margin-bottom: 0.5rem !important; + color: #374151 !important; +} + +.form-input, +.form-textarea, +.form-select { + width: 100% !important; + padding: 0.75rem !important; + border: 2px solid #e5e7eb !important; + border-radius: 0.5rem !important; + font-size: 1rem !important; + transition: all 0.3s ease !important; +} + +.form-input:focus, +.form-textarea:focus, +.form-select:focus { + outline: none !important; + border-color: #3B82F6 !important; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; +} \ No newline at end of file diff --git a/.history/public/css/base_20251020050046.css b/.history/public/css/base_20251020050046.css new file mode 100644 index 0000000..c9eacf9 --- /dev/null +++ b/.history/public/css/base_20251020050046.css @@ -0,0 +1,591 @@ +/* SmartSolTech - Base Styles for Elements */ + +/* Ensure proper styling without Tailwind conflicts */ +.navbar { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + z-index: 1000 !important; + background: rgba(255, 255, 255, 0.95) !important; + backdrop-filter: blur(10px) !important; + border-bottom: 1px solid #e5e7eb !important; + padding: 1rem 0 !important; +} + +.navbar-brand { + font-size: 1.5rem !important; + font-weight: 700 !important; + color: #3B82F6 !important; + text-decoration: none !important; +} + +.navbar-nav { + display: flex !important; + align-items: center !important; + gap: 2rem !important; + list-style: none !important; + margin: 0 !important; + padding: 0 !important; +} + +.nav-link { + color: #6b7280 !important; + text-decoration: none !important; + font-weight: 500 !important; + padding: 0.5rem 1rem !important; + border-radius: 0.5rem !important; + transition: all 0.3s ease !important; +} + +.nav-link:hover, +.nav-link.active { + color: #3B82F6 !important; + background-color: #eff6ff !important; +} + +/* Base CSS - Принудительные стили для правильного отображения */ + +/* Reset и базовые стили */ +* { + box-sizing: border-box !important; +} + +html, body { + margin: 0 !important; + padding: 0 !important; + font-family: 'Inter', system-ui, -apple-system, sans-serif !important; + line-height: 1.6 !important; + color: #333 !important; + scroll-behavior: smooth !important; +} + +body { + padding-top: 80px !important; /* Отступ для фиксированной навигации */ +} + +/* Навигация */ +nav, .navigation { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + z-index: 1000 !important; + background: rgba(255, 255, 255, 0.95) !important; + -webkit-backdrop-filter: blur(10px) !important; + backdrop-filter: blur(10px) !important; + border-bottom: 1px solid #e5e7eb !important; + height: 80px !important; +} + +.navbar, .nav-container { + display: flex !important; + justify-content: space-between !important; + align-items: center !important; + padding: 1rem 2rem !important; + max-width: 1200px !important; + margin: 0 auto !important; + height: 100% !important; +} + +.logo, .brand { + font-size: 1.5rem !important; + font-weight: 700 !important; + color: #1d4ed8 !important; + text-decoration: none !important; +} + +.nav-links, .menu { + display: flex !important; + list-style: none !important; + margin: 0 !important; + padding: 0 !important; + gap: 2rem !important; +} + +.nav-links a, .menu a { + color: #374151 !important; + text-decoration: none !important; + font-weight: 500 !important; + transition: color 0.3s ease !important; + padding: 0.5rem 0 !important; +} + +.nav-links a:hover, .menu a:hover { + color: #1d4ed8 !important; +} + +/* Hero секция */ +.hero-section { + min-height: 100vh !important; + background: linear-gradient(135deg, #1e3a8a 0%, #7c3aed 50%, #3730a3 100%) !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + text-align: center !important; + color: white !important; + position: relative !important; + overflow: hidden !important; + margin-top: -80px !important; /* Компенсация отступа */ + padding-top: 80px !important; +} + +.hero-section h1 { + font-size: 3.5rem !important; + font-weight: 700 !important; + margin-bottom: 1.5rem !important; + line-height: 1.2 !important; +} + +.hero-section p { + font-size: 1.25rem !important; + margin-bottom: 2rem !important; + opacity: 0.9 !important; +} + +/* Кнопки */ +.btn-primary, .btn { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + padding: 1rem 2rem !important; + background: linear-gradient(45deg, #3b82f6, #8b5cf6) !important; + color: white !important; + text-decoration: none !important; + border-radius: 50px !important; + font-weight: 600 !important; + transition: all 0.3s ease !important; + border: none !important; + cursor: pointer !important; + font-size: 1rem !important; +} + +.btn-primary:hover, .btn:hover { + transform: translateY(-2px) !important; + box-shadow: 0 10px 30px rgba(59, 130, 246, 0.3) !important; +} + +/* Карточки */ +.card-hover, .card { + transition: all 0.3s ease !important; + border-radius: 1rem !important; + overflow: hidden !important; + background: white !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; +} + +.card-hover:hover, .card:hover { + transform: translateY(-8px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +/* Секции */ +section { + padding: 5rem 1rem !important; +} + +.container, .max-w-7xl { + max-width: 1200px !important; + margin: 0 auto !important; + padding-left: 1rem !important; + padding-right: 1rem !important; +} + +/* Грид системы */ +.grid { + display: grid !important; +} + +.grid-cols-1 { + grid-template-columns: 1fr !important; +} + +.grid-cols-2 { + grid-template-columns: repeat(2, 1fr) !important; +} + +.grid-cols-3 { + grid-template-columns: repeat(3, 1fr) !important; +} + +.grid-cols-4 { + grid-template-columns: repeat(4, 1fr) !important; +} + +.gap-8 { + gap: 2rem !important; +} + +/* Заголовки */ +h1, h2, h3, h4, h5, h6 { + color: #1f2937 !important; + font-weight: 600 !important; +} + +h1 { + font-size: 3rem !important; +} + +h2 { + font-size: 2.5rem !important; +} + +h3 { + font-size: 1.75rem !important; +} + +/* Footer */ +footer { + background: #1f2937 !important; + color: white !important; + padding: 3rem 1rem 1rem !important; +} + +/* Отзывчивость */ +@media (max-width: 768px) { + .nav-links, .menu { + display: none !important; + } + + body { + padding-top: 60px !important; + } + + nav, .navigation { + height: 60px !important; + } + + .hero-section { + margin-top: -60px !important; + padding-top: 60px !important; + } + + section { + padding: 3rem 1rem !important; + } + + .hero-section h1 { + font-size: 2.5rem !important; + } + + .grid-cols-2, .grid-cols-3, .grid-cols-4 { + grid-template-columns: 1fr !important; + } +} + +/* Дополнительные утилитарные классы */ +.text-center { + text-align: center !important; +} + +.text-white { + color: white !important; +} + +.bg-white { + background-color: white !important; +} + +.rounded-lg { + border-radius: 0.5rem !important; +} + +.rounded-2xl { + border-radius: 1rem !important; +} + +.shadow-lg { + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1) !important; +} + +.mb-4 { + margin-bottom: 1rem !important; +} + +.mb-8 { + margin-bottom: 2rem !important; +} + +.p-8 { + padding: 2rem !important; +} + +.flex { + display: flex !important; +} + +.items-center { + align-items: center !important; +} + +.justify-center { + justify-content: center !important; +} + +.hero-title { + font-size: 3.5rem !important; + font-weight: 700 !important; + margin-bottom: 1rem !important; + line-height: 1.2 !important; +} + +.hero-subtitle { + font-size: 1.25rem !important; + margin-bottom: 2rem !important; + color: rgba(255, 255, 255, 0.9) !important; +} + +/* Buttons */ +.btn { + display: inline-block !important; + padding: 0.75rem 1.5rem !important; + border-radius: 0.5rem !important; + text-decoration: none !important; + font-weight: 600 !important; + text-align: center !important; + border: none !important; + cursor: pointer !important; + transition: all 0.3s ease !important; + font-size: 1rem !important; +} + +.btn-primary { + background: linear-gradient(135deg, #3B82F6, #1D4ED8) !important; + color: white !important; +} + +.btn-primary:hover { + transform: translateY(-2px) !important; + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3) !important; +} + +.btn-secondary { + background: transparent !important; + color: white !important; + border: 2px solid white !important; +} + +.btn-secondary:hover { + background: white !important; + color: #3B82F6 !important; +} + +/* Cards */ +.card { + background: white !important; + border-radius: 1rem !important; + padding: 2rem !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; + transition: all 0.3s ease !important; +} + +.card:hover { + transform: translateY(-4px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +/* Sections */ +.section { + padding: 4rem 2rem !important; +} + +.section-title { + font-size: 2.5rem !important; + font-weight: 700 !important; + text-align: center !important; + margin-bottom: 1rem !important; + color: #1f2937 !important; +} + +.section-description { + font-size: 1.125rem !important; + text-align: center !important; + color: #6b7280 !important; + max-width: 600px !important; + margin: 0 auto 3rem !important; +} + +/* Container */ +.container { + max-width: 1200px !important; + margin: 0 auto !important; + padding: 0 1rem !important; +} + +/* Grid Layouts */ +.grid-2 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)) !important; + gap: 2rem !important; +} + +.grid-3 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)) !important; + gap: 2rem !important; +} + +.grid-4 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) !important; + gap: 1.5rem !important; +} + +/* Language Selector */ +.language-selector { + display: flex !important; + align-items: center !important; + gap: 0.5rem !important; +} + +.language-selector a { + color: #6b7280 !important; + text-decoration: none !important; + padding: 0.25rem 0.5rem !important; + border-radius: 0.25rem !important; + font-size: 0.875rem !important; +} + +.language-selector a:hover { + color: #3B82F6 !important; + background-color: #f3f4f6 !important; +} + +/* Mobile Menu */ +.mobile-menu-button { + display: none !important; + background: none !important; + border: none !important; + font-size: 1.5rem !important; + color: #6b7280 !important; + cursor: pointer !important; +} + +/* Mobile Styles */ +@media (max-width: 768px) { + .hero-title { + font-size: 2.5rem !important; + } + + .mobile-menu-button { + display: block !important; + } + + .navbar-nav { + display: none !important; + position: absolute !important; + top: 100% !important; + left: 0 !important; + right: 0 !important; + background: white !important; + flex-direction: column !important; + padding: 1rem !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) !important; + } + + .navbar-nav.show { + display: flex !important; + } + + .section { + padding: 2rem 1rem !important; + } + + .section-title { + font-size: 2rem !important; + } +} + +/* Footer */ +.footer { + background: #1f2937 !important; + color: white !important; + padding: 3rem 2rem 1rem !important; + text-align: center !important; +} + +.footer p { + color: #d1d5db !important; +} + +/* Service Icons */ +.service-icon { + width: 80px !important; + height: 80px !important; + background: linear-gradient(135deg, #3B82F6, #8B5CF6) !important; + border-radius: 50% !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + font-size: 2rem !important; + color: white !important; + margin: 0 auto 1rem !important; +} + +/* Portfolio Items */ +.portfolio-item { + background: white !important; + border-radius: 1rem !important; + overflow: hidden !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; + transition: all 0.3s ease !important; +} + +.portfolio-item:hover { + transform: translateY(-4px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +.portfolio-image { + width: 100% !important; + height: 200px !important; + object-fit: cover !important; + display: block !important; +} + +.portfolio-content { + padding: 1.5rem !important; +} + +.portfolio-title { + font-size: 1.25rem !important; + font-weight: 600 !important; + margin-bottom: 0.5rem !important; + color: #1f2937 !important; +} + +.portfolio-description { + color: #6b7280 !important; + margin-bottom: 1rem !important; +} + +/* Form Styles */ +.form-group { + margin-bottom: 1.5rem !important; +} + +.form-label { + display: block !important; + font-weight: 500 !important; + margin-bottom: 0.5rem !important; + color: #374151 !important; +} + +.form-input, +.form-textarea, +.form-select { + width: 100% !important; + padding: 0.75rem !important; + border: 2px solid #e5e7eb !important; + border-radius: 0.5rem !important; + font-size: 1rem !important; + transition: all 0.3s ease !important; +} + +.form-input:focus, +.form-textarea:focus, +.form-select:focus { + outline: none !important; + border-color: #3B82F6 !important; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; +} \ No newline at end of file diff --git a/.history/public/css/base_20251020050055.css b/.history/public/css/base_20251020050055.css new file mode 100644 index 0000000..c9eacf9 --- /dev/null +++ b/.history/public/css/base_20251020050055.css @@ -0,0 +1,591 @@ +/* SmartSolTech - Base Styles for Elements */ + +/* Ensure proper styling without Tailwind conflicts */ +.navbar { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + z-index: 1000 !important; + background: rgba(255, 255, 255, 0.95) !important; + backdrop-filter: blur(10px) !important; + border-bottom: 1px solid #e5e7eb !important; + padding: 1rem 0 !important; +} + +.navbar-brand { + font-size: 1.5rem !important; + font-weight: 700 !important; + color: #3B82F6 !important; + text-decoration: none !important; +} + +.navbar-nav { + display: flex !important; + align-items: center !important; + gap: 2rem !important; + list-style: none !important; + margin: 0 !important; + padding: 0 !important; +} + +.nav-link { + color: #6b7280 !important; + text-decoration: none !important; + font-weight: 500 !important; + padding: 0.5rem 1rem !important; + border-radius: 0.5rem !important; + transition: all 0.3s ease !important; +} + +.nav-link:hover, +.nav-link.active { + color: #3B82F6 !important; + background-color: #eff6ff !important; +} + +/* Base CSS - Принудительные стили для правильного отображения */ + +/* Reset и базовые стили */ +* { + box-sizing: border-box !important; +} + +html, body { + margin: 0 !important; + padding: 0 !important; + font-family: 'Inter', system-ui, -apple-system, sans-serif !important; + line-height: 1.6 !important; + color: #333 !important; + scroll-behavior: smooth !important; +} + +body { + padding-top: 80px !important; /* Отступ для фиксированной навигации */ +} + +/* Навигация */ +nav, .navigation { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + z-index: 1000 !important; + background: rgba(255, 255, 255, 0.95) !important; + -webkit-backdrop-filter: blur(10px) !important; + backdrop-filter: blur(10px) !important; + border-bottom: 1px solid #e5e7eb !important; + height: 80px !important; +} + +.navbar, .nav-container { + display: flex !important; + justify-content: space-between !important; + align-items: center !important; + padding: 1rem 2rem !important; + max-width: 1200px !important; + margin: 0 auto !important; + height: 100% !important; +} + +.logo, .brand { + font-size: 1.5rem !important; + font-weight: 700 !important; + color: #1d4ed8 !important; + text-decoration: none !important; +} + +.nav-links, .menu { + display: flex !important; + list-style: none !important; + margin: 0 !important; + padding: 0 !important; + gap: 2rem !important; +} + +.nav-links a, .menu a { + color: #374151 !important; + text-decoration: none !important; + font-weight: 500 !important; + transition: color 0.3s ease !important; + padding: 0.5rem 0 !important; +} + +.nav-links a:hover, .menu a:hover { + color: #1d4ed8 !important; +} + +/* Hero секция */ +.hero-section { + min-height: 100vh !important; + background: linear-gradient(135deg, #1e3a8a 0%, #7c3aed 50%, #3730a3 100%) !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + text-align: center !important; + color: white !important; + position: relative !important; + overflow: hidden !important; + margin-top: -80px !important; /* Компенсация отступа */ + padding-top: 80px !important; +} + +.hero-section h1 { + font-size: 3.5rem !important; + font-weight: 700 !important; + margin-bottom: 1.5rem !important; + line-height: 1.2 !important; +} + +.hero-section p { + font-size: 1.25rem !important; + margin-bottom: 2rem !important; + opacity: 0.9 !important; +} + +/* Кнопки */ +.btn-primary, .btn { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + padding: 1rem 2rem !important; + background: linear-gradient(45deg, #3b82f6, #8b5cf6) !important; + color: white !important; + text-decoration: none !important; + border-radius: 50px !important; + font-weight: 600 !important; + transition: all 0.3s ease !important; + border: none !important; + cursor: pointer !important; + font-size: 1rem !important; +} + +.btn-primary:hover, .btn:hover { + transform: translateY(-2px) !important; + box-shadow: 0 10px 30px rgba(59, 130, 246, 0.3) !important; +} + +/* Карточки */ +.card-hover, .card { + transition: all 0.3s ease !important; + border-radius: 1rem !important; + overflow: hidden !important; + background: white !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; +} + +.card-hover:hover, .card:hover { + transform: translateY(-8px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +/* Секции */ +section { + padding: 5rem 1rem !important; +} + +.container, .max-w-7xl { + max-width: 1200px !important; + margin: 0 auto !important; + padding-left: 1rem !important; + padding-right: 1rem !important; +} + +/* Грид системы */ +.grid { + display: grid !important; +} + +.grid-cols-1 { + grid-template-columns: 1fr !important; +} + +.grid-cols-2 { + grid-template-columns: repeat(2, 1fr) !important; +} + +.grid-cols-3 { + grid-template-columns: repeat(3, 1fr) !important; +} + +.grid-cols-4 { + grid-template-columns: repeat(4, 1fr) !important; +} + +.gap-8 { + gap: 2rem !important; +} + +/* Заголовки */ +h1, h2, h3, h4, h5, h6 { + color: #1f2937 !important; + font-weight: 600 !important; +} + +h1 { + font-size: 3rem !important; +} + +h2 { + font-size: 2.5rem !important; +} + +h3 { + font-size: 1.75rem !important; +} + +/* Footer */ +footer { + background: #1f2937 !important; + color: white !important; + padding: 3rem 1rem 1rem !important; +} + +/* Отзывчивость */ +@media (max-width: 768px) { + .nav-links, .menu { + display: none !important; + } + + body { + padding-top: 60px !important; + } + + nav, .navigation { + height: 60px !important; + } + + .hero-section { + margin-top: -60px !important; + padding-top: 60px !important; + } + + section { + padding: 3rem 1rem !important; + } + + .hero-section h1 { + font-size: 2.5rem !important; + } + + .grid-cols-2, .grid-cols-3, .grid-cols-4 { + grid-template-columns: 1fr !important; + } +} + +/* Дополнительные утилитарные классы */ +.text-center { + text-align: center !important; +} + +.text-white { + color: white !important; +} + +.bg-white { + background-color: white !important; +} + +.rounded-lg { + border-radius: 0.5rem !important; +} + +.rounded-2xl { + border-radius: 1rem !important; +} + +.shadow-lg { + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1) !important; +} + +.mb-4 { + margin-bottom: 1rem !important; +} + +.mb-8 { + margin-bottom: 2rem !important; +} + +.p-8 { + padding: 2rem !important; +} + +.flex { + display: flex !important; +} + +.items-center { + align-items: center !important; +} + +.justify-center { + justify-content: center !important; +} + +.hero-title { + font-size: 3.5rem !important; + font-weight: 700 !important; + margin-bottom: 1rem !important; + line-height: 1.2 !important; +} + +.hero-subtitle { + font-size: 1.25rem !important; + margin-bottom: 2rem !important; + color: rgba(255, 255, 255, 0.9) !important; +} + +/* Buttons */ +.btn { + display: inline-block !important; + padding: 0.75rem 1.5rem !important; + border-radius: 0.5rem !important; + text-decoration: none !important; + font-weight: 600 !important; + text-align: center !important; + border: none !important; + cursor: pointer !important; + transition: all 0.3s ease !important; + font-size: 1rem !important; +} + +.btn-primary { + background: linear-gradient(135deg, #3B82F6, #1D4ED8) !important; + color: white !important; +} + +.btn-primary:hover { + transform: translateY(-2px) !important; + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3) !important; +} + +.btn-secondary { + background: transparent !important; + color: white !important; + border: 2px solid white !important; +} + +.btn-secondary:hover { + background: white !important; + color: #3B82F6 !important; +} + +/* Cards */ +.card { + background: white !important; + border-radius: 1rem !important; + padding: 2rem !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; + transition: all 0.3s ease !important; +} + +.card:hover { + transform: translateY(-4px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +/* Sections */ +.section { + padding: 4rem 2rem !important; +} + +.section-title { + font-size: 2.5rem !important; + font-weight: 700 !important; + text-align: center !important; + margin-bottom: 1rem !important; + color: #1f2937 !important; +} + +.section-description { + font-size: 1.125rem !important; + text-align: center !important; + color: #6b7280 !important; + max-width: 600px !important; + margin: 0 auto 3rem !important; +} + +/* Container */ +.container { + max-width: 1200px !important; + margin: 0 auto !important; + padding: 0 1rem !important; +} + +/* Grid Layouts */ +.grid-2 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)) !important; + gap: 2rem !important; +} + +.grid-3 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)) !important; + gap: 2rem !important; +} + +.grid-4 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) !important; + gap: 1.5rem !important; +} + +/* Language Selector */ +.language-selector { + display: flex !important; + align-items: center !important; + gap: 0.5rem !important; +} + +.language-selector a { + color: #6b7280 !important; + text-decoration: none !important; + padding: 0.25rem 0.5rem !important; + border-radius: 0.25rem !important; + font-size: 0.875rem !important; +} + +.language-selector a:hover { + color: #3B82F6 !important; + background-color: #f3f4f6 !important; +} + +/* Mobile Menu */ +.mobile-menu-button { + display: none !important; + background: none !important; + border: none !important; + font-size: 1.5rem !important; + color: #6b7280 !important; + cursor: pointer !important; +} + +/* Mobile Styles */ +@media (max-width: 768px) { + .hero-title { + font-size: 2.5rem !important; + } + + .mobile-menu-button { + display: block !important; + } + + .navbar-nav { + display: none !important; + position: absolute !important; + top: 100% !important; + left: 0 !important; + right: 0 !important; + background: white !important; + flex-direction: column !important; + padding: 1rem !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) !important; + } + + .navbar-nav.show { + display: flex !important; + } + + .section { + padding: 2rem 1rem !important; + } + + .section-title { + font-size: 2rem !important; + } +} + +/* Footer */ +.footer { + background: #1f2937 !important; + color: white !important; + padding: 3rem 2rem 1rem !important; + text-align: center !important; +} + +.footer p { + color: #d1d5db !important; +} + +/* Service Icons */ +.service-icon { + width: 80px !important; + height: 80px !important; + background: linear-gradient(135deg, #3B82F6, #8B5CF6) !important; + border-radius: 50% !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + font-size: 2rem !important; + color: white !important; + margin: 0 auto 1rem !important; +} + +/* Portfolio Items */ +.portfolio-item { + background: white !important; + border-radius: 1rem !important; + overflow: hidden !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; + transition: all 0.3s ease !important; +} + +.portfolio-item:hover { + transform: translateY(-4px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +.portfolio-image { + width: 100% !important; + height: 200px !important; + object-fit: cover !important; + display: block !important; +} + +.portfolio-content { + padding: 1.5rem !important; +} + +.portfolio-title { + font-size: 1.25rem !important; + font-weight: 600 !important; + margin-bottom: 0.5rem !important; + color: #1f2937 !important; +} + +.portfolio-description { + color: #6b7280 !important; + margin-bottom: 1rem !important; +} + +/* Form Styles */ +.form-group { + margin-bottom: 1.5rem !important; +} + +.form-label { + display: block !important; + font-weight: 500 !important; + margin-bottom: 0.5rem !important; + color: #374151 !important; +} + +.form-input, +.form-textarea, +.form-select { + width: 100% !important; + padding: 0.75rem !important; + border: 2px solid #e5e7eb !important; + border-radius: 0.5rem !important; + font-size: 1rem !important; + transition: all 0.3s ease !important; +} + +.form-input:focus, +.form-textarea:focus, +.form-select:focus { + outline: none !important; + border-color: #3B82F6 !important; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; +} \ No newline at end of file diff --git a/.history/public/css/base_20251020225302.css b/.history/public/css/base_20251020225302.css new file mode 100644 index 0000000..0df34a4 --- /dev/null +++ b/.history/public/css/base_20251020225302.css @@ -0,0 +1,686 @@ +/* SmartSolTech - Base Styles for Elements */ + +/* Ensure proper styling without Tailwind conflicts */ +.navbar { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + z-index: 1000 !important; + background: rgba(255, 255, 255, 0.95) !important; + backdrop-filter: blur(10px) !important; + border-bottom: 1px solid #e5e7eb !important; + padding: 1rem 0 !important; +} + +.navbar-brand { + font-size: 1.5rem !important; + font-weight: 700 !important; + color: #3B82F6 !important; + text-decoration: none !important; +} + +.navbar-nav { + display: flex !important; + align-items: center !important; + gap: 2rem !important; + list-style: none !important; + margin: 0 !important; + padding: 0 !important; +} + +.nav-link { + color: #6b7280 !important; + text-decoration: none !important; + font-weight: 500 !important; + padding: 0.5rem 1rem !important; + border-radius: 0.5rem !important; + transition: all 0.3s ease !important; +} + +.nav-link:hover, +.nav-link.active { + color: #3B82F6 !important; + background-color: #eff6ff !important; +} + +/* Base CSS - Исправленные стили для главной страницы */ + +/* Reset и базовые стили */ +* { + box-sizing: border-box !important; + margin: 0 !important; + padding: 0 !important; +} + +html { + font-size: 16px !important; + scroll-behavior: smooth !important; +} + +body { + font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important; + line-height: 1.6 !important; + color: #1f2937 !important; + background-color: #ffffff !important; + margin: 0 !important; + padding: 0 !important; + padding-top: 80px !important; /* Отступ для навигации */ +} + +/* Навигация - исправленная */ +nav, .navigation { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + z-index: 1000 !important; + background: rgba(255, 255, 255, 0.95) !important; + -webkit-backdrop-filter: blur(20px) !important; + backdrop-filter: blur(20px) !important; + border-bottom: 1px solid rgba(0, 0, 0, 0.1) !important; + height: 80px !important; + box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1) !important; +} + +.navbar, .nav-container { + display: flex !important; + justify-content: space-between !important; + align-items: center !important; + padding: 0 2rem !important; + max-width: 1200px !important; + margin: 0 auto !important; + height: 100% !important; +} + +.logo, .brand { + font-size: 1.75rem !important; + font-weight: 800 !important; + color: #1d4ed8 !important; + text-decoration: none !important; + letter-spacing: -0.025em !important; +} + +.nav-links, .menu { + display: flex !important; + list-style: none !important; + margin: 0 !important; + padding: 0 !important; + gap: 2.5rem !important; +} + +.nav-links li, .menu li { + margin: 0 !important; + padding: 0 !important; +} + +.nav-links a, .menu a { + color: #374151 !important; + text-decoration: none !important; + font-weight: 500 !important; + font-size: 0.95rem !important; + transition: all 0.3s ease !important; + padding: 0.75rem 0 !important; + position: relative !important; +} + +.nav-links a:hover, .menu a:hover { + color: #1d4ed8 !important; + transform: translateY(-1px) !important; +} + +.nav-links a::after, .menu a::after { + content: '' !important; + position: absolute !important; + bottom: 0.5rem !important; + left: 0 !important; + width: 0 !important; + height: 2px !important; + background: linear-gradient(45deg, #1d4ed8, #8b5cf6) !important; + transition: width 0.3s ease !important; +} + +.nav-links a:hover::after, .menu a:hover::after { + width: 100% !important; +} + +/* Hero секция - исправленная */ +.hero-section { + min-height: 100vh !important; + background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 25%, #312e81 50%, #7c3aed 75%, #3730a3 100%) !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + text-align: center !important; + color: white !important; + position: relative !important; + overflow: hidden !important; + margin-top: -80px !important; + padding-top: 80px !important; +} + +.hero-section::before { + content: '' !important; + position: absolute !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + bottom: 0 !important; + background: radial-gradient(circle at 30% 40%, rgba(59, 130, 246, 0.3) 0%, transparent 50%), + radial-gradient(circle at 80% 20%, rgba(139, 92, 246, 0.3) 0%, transparent 50%), + radial-gradient(circle at 40% 80%, rgba(16, 185, 129, 0.2) 0%, transparent 50%) !important; + z-index: 1 !important; +} + +.hero-section > div { + position: relative !important; + z-index: 2 !important; +} + +.hero-section h1 { + font-size: 4rem !important; + font-weight: 800 !important; + margin-bottom: 1.5rem !important; + line-height: 1.1 !important; + letter-spacing: -0.025em !important; +} + +.hero-section p { + font-size: 1.35rem !important; + margin-bottom: 2.5rem !important; + opacity: 0.9 !important; + max-width: 600px !important; + margin-left: auto !important; + margin-right: auto !important; +} + +/* Кнопки - улучшенные */ +.btn-primary, .btn { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + padding: 1.25rem 2.5rem !important; + background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%) !important; + color: white !important; + text-decoration: none !important; + border-radius: 50px !important; + font-weight: 700 !important; + font-size: 1.1rem !important; + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1) !important; + border: none !important; + cursor: pointer !important; + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.3) !important; + position: relative !important; + overflow: hidden !important; +} + +.btn-primary:hover, .btn:hover { + transform: translateY(-3px) scale(1.05) !important; + box-shadow: 0 15px 35px rgba(59, 130, 246, 0.4) !important; + background: linear-gradient(135deg, #2563eb 0%, #7c3aed 100%) !important; +} + +.btn-primary::before, .btn::before { + content: '' !important; + position: absolute !important; + top: 0 !important; + left: -100% !important; + width: 100% !important; + height: 100% !important; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent) !important; + transition: left 0.6s !important; +} + +.btn-primary:hover::before, .btn:hover::before { + left: 100% !important; +} + +/* Карточки - улучшенные */ +.card-hover, .card { + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1) !important; + border-radius: 1.5rem !important; + overflow: hidden !important; + background: white !important; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05) !important; + border: 1px solid rgba(0, 0, 0, 0.05) !important; +} + +.card-hover:hover, .card:hover { + transform: translateY(-12px) scale(1.02) !important; + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15) !important; + border-color: rgba(59, 130, 246, 0.2) !important; +} + +/* Секции */ +section { + padding: 6rem 1rem !important; + position: relative !important; +} + +.container, .max-w-7xl { + max-width: 1200px !important; + margin: 0 auto !important; + padding-left: 1rem !important; + padding-right: 1rem !important; +} + +/* Заголовки */ +h1, h2, h3, h4, h5, h6 { + color: #1f2937 !important; + font-weight: 700 !important; + line-height: 1.2 !important; +} + +h2 { + font-size: 2.75rem !important; + margin-bottom: 1rem !important; +} + +h3 { + font-size: 1.5rem !important; + margin-bottom: 0.75rem !important; +} + +/* Параграфы и текст */ +p { + color: #6b7280 !important; + line-height: 1.7 !important; + margin-bottom: 1rem !important; +} + +/* Утилитарные классы Tailwind - принудительные */ +.text-center { + text-align: center !important; +} + +.text-white { + color: white !important; +} + +.bg-white { + background-color: white !important; +} + +.bg-gray-50 { + background-color: #f9fafb !important; +} + +.rounded-lg { + border-radius: 0.5rem !important; +} + +.rounded-2xl { + border-radius: 1rem !important; +} + +.shadow-lg { + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1) !important; +} + +.mb-4 { + margin-bottom: 1rem !important; +} + +.mb-8 { + margin-bottom: 2rem !important; +} + +.p-8 { + padding: 2rem !important; +} + +.flex { + display: flex !important; +} + +.grid { + display: grid !important; +} + +.items-center { + align-items: center !important; +} + +.justify-center { + justify-content: center !important; +} + +/* Грид системы */ +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)) !important; +} + +.grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)) !important; +} + +.grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)) !important; +} + +.grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)) !important; +} + +.gap-8 { + gap: 2rem !important; +} + +/* Отзывчивость - улучшенная */ +@media (max-width: 1024px) { + .grid-cols-4 { + grid-template-columns: repeat(2, minmax(0, 1fr)) !important; + } + + .hero-section h1 { + font-size: 3.5rem !important; + } +} + +@media (max-width: 768px) { + .nav-links, .menu { + display: none !important; + } + + body { + padding-top: 60px !important; + } + + nav, .navigation { + height: 60px !important; + } + + .hero-section { + margin-top: -60px !important; + padding-top: 60px !important; + } + + section { + padding: 4rem 1rem !important; + } + + .hero-section h1 { + font-size: 2.75rem !important; + } + + .hero-section p { + font-size: 1.1rem !important; + } + + .grid-cols-2, .grid-cols-3, .grid-cols-4 { + grid-template-columns: repeat(1, minmax(0, 1fr)) !important; + } + + .btn-primary, .btn { + padding: 1rem 2rem !important; + font-size: 1rem !important; + } +} + +.hero-title { + font-size: 3.5rem !important; + font-weight: 700 !important; + margin-bottom: 1rem !important; + line-height: 1.2 !important; +} + +.hero-subtitle { + font-size: 1.25rem !important; + margin-bottom: 2rem !important; + color: rgba(255, 255, 255, 0.9) !important; +} + +/* Buttons */ +.btn { + display: inline-block !important; + padding: 0.75rem 1.5rem !important; + border-radius: 0.5rem !important; + text-decoration: none !important; + font-weight: 600 !important; + text-align: center !important; + border: none !important; + cursor: pointer !important; + transition: all 0.3s ease !important; + font-size: 1rem !important; +} + +.btn-primary { + background: linear-gradient(135deg, #3B82F6, #1D4ED8) !important; + color: white !important; +} + +.btn-primary:hover { + transform: translateY(-2px) !important; + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3) !important; +} + +.btn-secondary { + background: transparent !important; + color: white !important; + border: 2px solid white !important; +} + +.btn-secondary:hover { + background: white !important; + color: #3B82F6 !important; +} + +/* Cards */ +.card { + background: white !important; + border-radius: 1rem !important; + padding: 2rem !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; + transition: all 0.3s ease !important; +} + +.card:hover { + transform: translateY(-4px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +/* Sections */ +.section { + padding: 4rem 2rem !important; +} + +.section-title { + font-size: 2.5rem !important; + font-weight: 700 !important; + text-align: center !important; + margin-bottom: 1rem !important; + color: #1f2937 !important; +} + +.section-description { + font-size: 1.125rem !important; + text-align: center !important; + color: #6b7280 !important; + max-width: 600px !important; + margin: 0 auto 3rem !important; +} + +/* Container */ +.container { + max-width: 1200px !important; + margin: 0 auto !important; + padding: 0 1rem !important; +} + +/* Grid Layouts */ +.grid-2 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)) !important; + gap: 2rem !important; +} + +.grid-3 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)) !important; + gap: 2rem !important; +} + +.grid-4 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) !important; + gap: 1.5rem !important; +} + +/* Language Selector */ +.language-selector { + display: flex !important; + align-items: center !important; + gap: 0.5rem !important; +} + +.language-selector a { + color: #6b7280 !important; + text-decoration: none !important; + padding: 0.25rem 0.5rem !important; + border-radius: 0.25rem !important; + font-size: 0.875rem !important; +} + +.language-selector a:hover { + color: #3B82F6 !important; + background-color: #f3f4f6 !important; +} + +/* Mobile Menu */ +.mobile-menu-button { + display: none !important; + background: none !important; + border: none !important; + font-size: 1.5rem !important; + color: #6b7280 !important; + cursor: pointer !important; +} + +/* Mobile Styles */ +@media (max-width: 768px) { + .hero-title { + font-size: 2.5rem !important; + } + + .mobile-menu-button { + display: block !important; + } + + .navbar-nav { + display: none !important; + position: absolute !important; + top: 100% !important; + left: 0 !important; + right: 0 !important; + background: white !important; + flex-direction: column !important; + padding: 1rem !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) !important; + } + + .navbar-nav.show { + display: flex !important; + } + + .section { + padding: 2rem 1rem !important; + } + + .section-title { + font-size: 2rem !important; + } +} + +/* Footer */ +.footer { + background: #1f2937 !important; + color: white !important; + padding: 3rem 2rem 1rem !important; + text-align: center !important; +} + +.footer p { + color: #d1d5db !important; +} + +/* Service Icons */ +.service-icon { + width: 80px !important; + height: 80px !important; + background: linear-gradient(135deg, #3B82F6, #8B5CF6) !important; + border-radius: 50% !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + font-size: 2rem !important; + color: white !important; + margin: 0 auto 1rem !important; +} + +/* Portfolio Items */ +.portfolio-item { + background: white !important; + border-radius: 1rem !important; + overflow: hidden !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; + transition: all 0.3s ease !important; +} + +.portfolio-item:hover { + transform: translateY(-4px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +.portfolio-image { + width: 100% !important; + height: 200px !important; + object-fit: cover !important; + display: block !important; +} + +.portfolio-content { + padding: 1.5rem !important; +} + +.portfolio-title { + font-size: 1.25rem !important; + font-weight: 600 !important; + margin-bottom: 0.5rem !important; + color: #1f2937 !important; +} + +.portfolio-description { + color: #6b7280 !important; + margin-bottom: 1rem !important; +} + +/* Form Styles */ +.form-group { + margin-bottom: 1.5rem !important; +} + +.form-label { + display: block !important; + font-weight: 500 !important; + margin-bottom: 0.5rem !important; + color: #374151 !important; +} + +.form-input, +.form-textarea, +.form-select { + width: 100% !important; + padding: 0.75rem !important; + border: 2px solid #e5e7eb !important; + border-radius: 0.5rem !important; + font-size: 1rem !important; + transition: all 0.3s ease !important; +} + +.form-input:focus, +.form-textarea:focus, +.form-select:focus { + outline: none !important; + border-color: #3B82F6 !important; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; +} \ No newline at end of file diff --git a/.history/public/css/base_20251020225419.css b/.history/public/css/base_20251020225419.css new file mode 100644 index 0000000..0df34a4 --- /dev/null +++ b/.history/public/css/base_20251020225419.css @@ -0,0 +1,686 @@ +/* SmartSolTech - Base Styles for Elements */ + +/* Ensure proper styling without Tailwind conflicts */ +.navbar { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + z-index: 1000 !important; + background: rgba(255, 255, 255, 0.95) !important; + backdrop-filter: blur(10px) !important; + border-bottom: 1px solid #e5e7eb !important; + padding: 1rem 0 !important; +} + +.navbar-brand { + font-size: 1.5rem !important; + font-weight: 700 !important; + color: #3B82F6 !important; + text-decoration: none !important; +} + +.navbar-nav { + display: flex !important; + align-items: center !important; + gap: 2rem !important; + list-style: none !important; + margin: 0 !important; + padding: 0 !important; +} + +.nav-link { + color: #6b7280 !important; + text-decoration: none !important; + font-weight: 500 !important; + padding: 0.5rem 1rem !important; + border-radius: 0.5rem !important; + transition: all 0.3s ease !important; +} + +.nav-link:hover, +.nav-link.active { + color: #3B82F6 !important; + background-color: #eff6ff !important; +} + +/* Base CSS - Исправленные стили для главной страницы */ + +/* Reset и базовые стили */ +* { + box-sizing: border-box !important; + margin: 0 !important; + padding: 0 !important; +} + +html { + font-size: 16px !important; + scroll-behavior: smooth !important; +} + +body { + font-family: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important; + line-height: 1.6 !important; + color: #1f2937 !important; + background-color: #ffffff !important; + margin: 0 !important; + padding: 0 !important; + padding-top: 80px !important; /* Отступ для навигации */ +} + +/* Навигация - исправленная */ +nav, .navigation { + position: fixed !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + z-index: 1000 !important; + background: rgba(255, 255, 255, 0.95) !important; + -webkit-backdrop-filter: blur(20px) !important; + backdrop-filter: blur(20px) !important; + border-bottom: 1px solid rgba(0, 0, 0, 0.1) !important; + height: 80px !important; + box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1) !important; +} + +.navbar, .nav-container { + display: flex !important; + justify-content: space-between !important; + align-items: center !important; + padding: 0 2rem !important; + max-width: 1200px !important; + margin: 0 auto !important; + height: 100% !important; +} + +.logo, .brand { + font-size: 1.75rem !important; + font-weight: 800 !important; + color: #1d4ed8 !important; + text-decoration: none !important; + letter-spacing: -0.025em !important; +} + +.nav-links, .menu { + display: flex !important; + list-style: none !important; + margin: 0 !important; + padding: 0 !important; + gap: 2.5rem !important; +} + +.nav-links li, .menu li { + margin: 0 !important; + padding: 0 !important; +} + +.nav-links a, .menu a { + color: #374151 !important; + text-decoration: none !important; + font-weight: 500 !important; + font-size: 0.95rem !important; + transition: all 0.3s ease !important; + padding: 0.75rem 0 !important; + position: relative !important; +} + +.nav-links a:hover, .menu a:hover { + color: #1d4ed8 !important; + transform: translateY(-1px) !important; +} + +.nav-links a::after, .menu a::after { + content: '' !important; + position: absolute !important; + bottom: 0.5rem !important; + left: 0 !important; + width: 0 !important; + height: 2px !important; + background: linear-gradient(45deg, #1d4ed8, #8b5cf6) !important; + transition: width 0.3s ease !important; +} + +.nav-links a:hover::after, .menu a:hover::after { + width: 100% !important; +} + +/* Hero секция - исправленная */ +.hero-section { + min-height: 100vh !important; + background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 25%, #312e81 50%, #7c3aed 75%, #3730a3 100%) !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + text-align: center !important; + color: white !important; + position: relative !important; + overflow: hidden !important; + margin-top: -80px !important; + padding-top: 80px !important; +} + +.hero-section::before { + content: '' !important; + position: absolute !important; + top: 0 !important; + left: 0 !important; + right: 0 !important; + bottom: 0 !important; + background: radial-gradient(circle at 30% 40%, rgba(59, 130, 246, 0.3) 0%, transparent 50%), + radial-gradient(circle at 80% 20%, rgba(139, 92, 246, 0.3) 0%, transparent 50%), + radial-gradient(circle at 40% 80%, rgba(16, 185, 129, 0.2) 0%, transparent 50%) !important; + z-index: 1 !important; +} + +.hero-section > div { + position: relative !important; + z-index: 2 !important; +} + +.hero-section h1 { + font-size: 4rem !important; + font-weight: 800 !important; + margin-bottom: 1.5rem !important; + line-height: 1.1 !important; + letter-spacing: -0.025em !important; +} + +.hero-section p { + font-size: 1.35rem !important; + margin-bottom: 2.5rem !important; + opacity: 0.9 !important; + max-width: 600px !important; + margin-left: auto !important; + margin-right: auto !important; +} + +/* Кнопки - улучшенные */ +.btn-primary, .btn { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + padding: 1.25rem 2.5rem !important; + background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%) !important; + color: white !important; + text-decoration: none !important; + border-radius: 50px !important; + font-weight: 700 !important; + font-size: 1.1rem !important; + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1) !important; + border: none !important; + cursor: pointer !important; + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.3) !important; + position: relative !important; + overflow: hidden !important; +} + +.btn-primary:hover, .btn:hover { + transform: translateY(-3px) scale(1.05) !important; + box-shadow: 0 15px 35px rgba(59, 130, 246, 0.4) !important; + background: linear-gradient(135deg, #2563eb 0%, #7c3aed 100%) !important; +} + +.btn-primary::before, .btn::before { + content: '' !important; + position: absolute !important; + top: 0 !important; + left: -100% !important; + width: 100% !important; + height: 100% !important; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent) !important; + transition: left 0.6s !important; +} + +.btn-primary:hover::before, .btn:hover::before { + left: 100% !important; +} + +/* Карточки - улучшенные */ +.card-hover, .card { + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1) !important; + border-radius: 1.5rem !important; + overflow: hidden !important; + background: white !important; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05) !important; + border: 1px solid rgba(0, 0, 0, 0.05) !important; +} + +.card-hover:hover, .card:hover { + transform: translateY(-12px) scale(1.02) !important; + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15) !important; + border-color: rgba(59, 130, 246, 0.2) !important; +} + +/* Секции */ +section { + padding: 6rem 1rem !important; + position: relative !important; +} + +.container, .max-w-7xl { + max-width: 1200px !important; + margin: 0 auto !important; + padding-left: 1rem !important; + padding-right: 1rem !important; +} + +/* Заголовки */ +h1, h2, h3, h4, h5, h6 { + color: #1f2937 !important; + font-weight: 700 !important; + line-height: 1.2 !important; +} + +h2 { + font-size: 2.75rem !important; + margin-bottom: 1rem !important; +} + +h3 { + font-size: 1.5rem !important; + margin-bottom: 0.75rem !important; +} + +/* Параграфы и текст */ +p { + color: #6b7280 !important; + line-height: 1.7 !important; + margin-bottom: 1rem !important; +} + +/* Утилитарные классы Tailwind - принудительные */ +.text-center { + text-align: center !important; +} + +.text-white { + color: white !important; +} + +.bg-white { + background-color: white !important; +} + +.bg-gray-50 { + background-color: #f9fafb !important; +} + +.rounded-lg { + border-radius: 0.5rem !important; +} + +.rounded-2xl { + border-radius: 1rem !important; +} + +.shadow-lg { + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1) !important; +} + +.mb-4 { + margin-bottom: 1rem !important; +} + +.mb-8 { + margin-bottom: 2rem !important; +} + +.p-8 { + padding: 2rem !important; +} + +.flex { + display: flex !important; +} + +.grid { + display: grid !important; +} + +.items-center { + align-items: center !important; +} + +.justify-center { + justify-content: center !important; +} + +/* Грид системы */ +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)) !important; +} + +.grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)) !important; +} + +.grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)) !important; +} + +.grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)) !important; +} + +.gap-8 { + gap: 2rem !important; +} + +/* Отзывчивость - улучшенная */ +@media (max-width: 1024px) { + .grid-cols-4 { + grid-template-columns: repeat(2, minmax(0, 1fr)) !important; + } + + .hero-section h1 { + font-size: 3.5rem !important; + } +} + +@media (max-width: 768px) { + .nav-links, .menu { + display: none !important; + } + + body { + padding-top: 60px !important; + } + + nav, .navigation { + height: 60px !important; + } + + .hero-section { + margin-top: -60px !important; + padding-top: 60px !important; + } + + section { + padding: 4rem 1rem !important; + } + + .hero-section h1 { + font-size: 2.75rem !important; + } + + .hero-section p { + font-size: 1.1rem !important; + } + + .grid-cols-2, .grid-cols-3, .grid-cols-4 { + grid-template-columns: repeat(1, minmax(0, 1fr)) !important; + } + + .btn-primary, .btn { + padding: 1rem 2rem !important; + font-size: 1rem !important; + } +} + +.hero-title { + font-size: 3.5rem !important; + font-weight: 700 !important; + margin-bottom: 1rem !important; + line-height: 1.2 !important; +} + +.hero-subtitle { + font-size: 1.25rem !important; + margin-bottom: 2rem !important; + color: rgba(255, 255, 255, 0.9) !important; +} + +/* Buttons */ +.btn { + display: inline-block !important; + padding: 0.75rem 1.5rem !important; + border-radius: 0.5rem !important; + text-decoration: none !important; + font-weight: 600 !important; + text-align: center !important; + border: none !important; + cursor: pointer !important; + transition: all 0.3s ease !important; + font-size: 1rem !important; +} + +.btn-primary { + background: linear-gradient(135deg, #3B82F6, #1D4ED8) !important; + color: white !important; +} + +.btn-primary:hover { + transform: translateY(-2px) !important; + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3) !important; +} + +.btn-secondary { + background: transparent !important; + color: white !important; + border: 2px solid white !important; +} + +.btn-secondary:hover { + background: white !important; + color: #3B82F6 !important; +} + +/* Cards */ +.card { + background: white !important; + border-radius: 1rem !important; + padding: 2rem !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; + transition: all 0.3s ease !important; +} + +.card:hover { + transform: translateY(-4px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +/* Sections */ +.section { + padding: 4rem 2rem !important; +} + +.section-title { + font-size: 2.5rem !important; + font-weight: 700 !important; + text-align: center !important; + margin-bottom: 1rem !important; + color: #1f2937 !important; +} + +.section-description { + font-size: 1.125rem !important; + text-align: center !important; + color: #6b7280 !important; + max-width: 600px !important; + margin: 0 auto 3rem !important; +} + +/* Container */ +.container { + max-width: 1200px !important; + margin: 0 auto !important; + padding: 0 1rem !important; +} + +/* Grid Layouts */ +.grid-2 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)) !important; + gap: 2rem !important; +} + +.grid-3 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)) !important; + gap: 2rem !important; +} + +.grid-4 { + display: grid !important; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)) !important; + gap: 1.5rem !important; +} + +/* Language Selector */ +.language-selector { + display: flex !important; + align-items: center !important; + gap: 0.5rem !important; +} + +.language-selector a { + color: #6b7280 !important; + text-decoration: none !important; + padding: 0.25rem 0.5rem !important; + border-radius: 0.25rem !important; + font-size: 0.875rem !important; +} + +.language-selector a:hover { + color: #3B82F6 !important; + background-color: #f3f4f6 !important; +} + +/* Mobile Menu */ +.mobile-menu-button { + display: none !important; + background: none !important; + border: none !important; + font-size: 1.5rem !important; + color: #6b7280 !important; + cursor: pointer !important; +} + +/* Mobile Styles */ +@media (max-width: 768px) { + .hero-title { + font-size: 2.5rem !important; + } + + .mobile-menu-button { + display: block !important; + } + + .navbar-nav { + display: none !important; + position: absolute !important; + top: 100% !important; + left: 0 !important; + right: 0 !important; + background: white !important; + flex-direction: column !important; + padding: 1rem !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) !important; + } + + .navbar-nav.show { + display: flex !important; + } + + .section { + padding: 2rem 1rem !important; + } + + .section-title { + font-size: 2rem !important; + } +} + +/* Footer */ +.footer { + background: #1f2937 !important; + color: white !important; + padding: 3rem 2rem 1rem !important; + text-align: center !important; +} + +.footer p { + color: #d1d5db !important; +} + +/* Service Icons */ +.service-icon { + width: 80px !important; + height: 80px !important; + background: linear-gradient(135deg, #3B82F6, #8B5CF6) !important; + border-radius: 50% !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + font-size: 2rem !important; + color: white !important; + margin: 0 auto 1rem !important; +} + +/* Portfolio Items */ +.portfolio-item { + background: white !important; + border-radius: 1rem !important; + overflow: hidden !important; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05) !important; + transition: all 0.3s ease !important; +} + +.portfolio-item:hover { + transform: translateY(-4px) !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +.portfolio-image { + width: 100% !important; + height: 200px !important; + object-fit: cover !important; + display: block !important; +} + +.portfolio-content { + padding: 1.5rem !important; +} + +.portfolio-title { + font-size: 1.25rem !important; + font-weight: 600 !important; + margin-bottom: 0.5rem !important; + color: #1f2937 !important; +} + +.portfolio-description { + color: #6b7280 !important; + margin-bottom: 1rem !important; +} + +/* Form Styles */ +.form-group { + margin-bottom: 1.5rem !important; +} + +.form-label { + display: block !important; + font-weight: 500 !important; + margin-bottom: 0.5rem !important; + color: #374151 !important; +} + +.form-input, +.form-textarea, +.form-select { + width: 100% !important; + padding: 0.75rem !important; + border: 2px solid #e5e7eb !important; + border-radius: 0.5rem !important; + font-size: 1rem !important; + transition: all 0.3s ease !important; +} + +.form-input:focus, +.form-textarea:focus, +.form-select:focus { + outline: none !important; + border-color: #3B82F6 !important; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1) !important; +} \ No newline at end of file diff --git a/.history/public/css/dark-theme_20251020042230.css b/.history/public/css/dark-theme_20251020042230.css new file mode 100644 index 0000000..19d8f4f --- /dev/null +++ b/.history/public/css/dark-theme_20251020042230.css @@ -0,0 +1,315 @@ +/* Dark Theme Support for SmartSolTech */ + +/* Base Dark Theme */ +html.dark { + color-scheme: dark; +} + +.dark body { + background-color: #111827; + color: #f9fafb; +} + +/* Navigation Dark Theme */ +.dark nav { + background-color: #1f2937; + border-color: #374151; +} + +.dark .nav-link { + color: #d1d5db; +} + +.dark .nav-link:hover { + color: #60a5fa; +} + +.dark .nav-link.active { + color: #60a5fa; +} + +/* Sections Dark Theme */ +.dark section { + background-color: #111827; + color: #f9fafb; +} + +.dark .bg-white { + background-color: #1f2937 !important; +} + +.dark .bg-gray-50 { + background-color: #111827 !important; +} + +.dark .bg-gray-100 { + background-color: #374151 !important; +} + +.dark .bg-gray-900 { + background-color: #030712 !important; +} + +/* Text Colors Dark Theme */ +.dark .text-gray-900 { + color: #f9fafb !important; +} + +.dark .text-gray-800 { + color: #e5e7eb !important; +} + +.dark .text-gray-700 { + color: #d1d5db !important; +} + +.dark .text-gray-600 { + color: #9ca3af !important; +} + +.dark .text-gray-500 { + color: #6b7280 !important; +} + +.dark .text-gray-400 { + color: #9ca3af !important; +} + +.dark .text-gray-300 { + color: #d1d5db !important; +} + +/* Cards Dark Theme */ +.dark .card-hover { + background-color: #1f2937; + border-color: #374151; +} + +.dark .card-hover:hover { + background-color: #374151; + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5); +} + +/* Forms Dark Theme */ +.dark .form-input { + background-color: #374151; + border-color: #4b5563; + color: #f9fafb; +} + +.dark .form-input:focus { + border-color: #60a5fa; + background-color: #1f2937; +} + +.dark .form-input::placeholder { + color: #9ca3af; +} + +.dark input, +.dark textarea, +.dark select { + background-color: #374151; + border-color: #4b5563; + color: #f9fafb; +} + +.dark input:focus, +.dark textarea:focus, +.dark select:focus { + border-color: #60a5fa; + background-color: #1f2937; +} + +/* Borders Dark Theme */ +.dark .border-gray-300 { + border-color: #4b5563 !important; +} + +.dark .border-gray-200 { + border-color: #374151 !important; +} + +.dark .border-t { + border-color: #374151; +} + +/* Contact Form Dark Theme */ +.dark .contact-form { + background: linear-gradient(145deg, rgba(31,41,55,0.9), rgba(31,41,55,0.95)); + border-color: #374151; +} + +/* Service Cards Dark Theme */ +.dark .service-card { + background: linear-gradient(145deg, rgba(31,41,55,0.8), rgba(31,41,55,0.6)); + border-color: #374151; +} + +/* Team Cards Dark Theme */ +.dark .team-card { + background-color: #1f2937; + border-color: #374151; +} + +/* Portfolio Items Dark Theme */ +.dark .portfolio-item { + background-color: #1f2937; +} + +/* Hero Section Dark Theme */ +.dark .hero-section { + background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%); +} + +/* Shadows Dark Theme */ +.dark .shadow-lg { + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2); +} + +.dark .shadow-xl { + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.3), 0 10px 10px -5px rgba(0, 0, 0, 0.2); +} + +.dark .shadow-2xl { + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); +} + +/* Dropdown Dark Theme */ +.dark .dropdown-menu { + background-color: #1f2937; + border-color: #374151; +} + +.dark .dropdown-menu a { + color: #d1d5db; +} + +.dark .dropdown-menu a:hover { + background-color: #374151; + color: #f9fafb; +} + +/* Icons Dark Theme */ +.dark .text-blue-600 { + color: #60a5fa !important; +} + +.dark .text-purple-600 { + color: #a78bfa !important; +} + +.dark .text-green-600 { + color: #34d399 !important; +} + +.dark .text-yellow-600 { + color: #fbbf24 !important; +} + +/* Buttons Dark Theme */ +.dark .btn-primary { + background: linear-gradient(135deg, #3b82f6, #1d4ed8); +} + +.dark .btn-primary:hover { + background: linear-gradient(135deg, #1d4ed8, #1e40af); +} + +/* Footer Dark Theme */ +.dark footer { + background-color: #030712; + border-color: #1f2937; +} + +.dark .footer-gradient { + background: linear-gradient(135deg, #030712 0%, #111827 100%); +} + +/* Scrollbar Dark Theme */ +.dark ::-webkit-scrollbar-track { + background: #1f2937; +} + +.dark ::-webkit-scrollbar-thumb { + background: linear-gradient(135deg, #3b82f6, #1d4ed8); +} + +/* Technology Stack Dark Theme */ +.dark .tech-stack { + background-color: #030712; +} + +.dark .tech-card { + background-color: #1f2937; + border-color: #374151; +} + +/* CTA Section Dark Theme */ +.dark .cta-section { + background: linear-gradient(135deg, #1e40af 0%, #7c3aed 100%); +} + +/* Testimonials Dark Theme */ +.dark .testimonial-card { + background: rgba(31, 41, 55, 0.8); + border-color: #374151; +} + +/* Alert Messages Dark Theme */ +.dark .alert-success { + background: rgba(16, 185, 129, 0.1); + border-color: rgba(16, 185, 129, 0.3); + color: #10b981; +} + +.dark .alert-error { + background: rgba(239, 68, 68, 0.1); + border-color: rgba(239, 68, 68, 0.3); + color: #ef4444; +} + +/* Mobile Menu Dark Theme */ +.dark #mobile-menu { + background-color: #1f2937; + border-color: #374151; +} + +/* Language Dropdown Dark Theme */ +.dark #mobile-language-menu { + background-color: #1f2937; + border-color: #374151; +} + +/* Smooth Theme Transition */ +* { + transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; +} + +/* Print styles for dark theme */ +@media print { + .dark * { + background: white !important; + color: black !important; + } +} + +/* High contrast mode */ +@media (prefers-contrast: high) { + .dark { + color: white !important; + background-color: black !important; + } + + .dark .border { + border-width: 2px; + } +} + +/* Reduced motion for accessibility */ +@media (prefers-reduced-motion: reduce) { + .dark * { + transition: none !important; + animation: none !important; + } +} \ No newline at end of file diff --git a/.history/public/css/dark-theme_20251020042243.css b/.history/public/css/dark-theme_20251020042243.css new file mode 100644 index 0000000..19d8f4f --- /dev/null +++ b/.history/public/css/dark-theme_20251020042243.css @@ -0,0 +1,315 @@ +/* Dark Theme Support for SmartSolTech */ + +/* Base Dark Theme */ +html.dark { + color-scheme: dark; +} + +.dark body { + background-color: #111827; + color: #f9fafb; +} + +/* Navigation Dark Theme */ +.dark nav { + background-color: #1f2937; + border-color: #374151; +} + +.dark .nav-link { + color: #d1d5db; +} + +.dark .nav-link:hover { + color: #60a5fa; +} + +.dark .nav-link.active { + color: #60a5fa; +} + +/* Sections Dark Theme */ +.dark section { + background-color: #111827; + color: #f9fafb; +} + +.dark .bg-white { + background-color: #1f2937 !important; +} + +.dark .bg-gray-50 { + background-color: #111827 !important; +} + +.dark .bg-gray-100 { + background-color: #374151 !important; +} + +.dark .bg-gray-900 { + background-color: #030712 !important; +} + +/* Text Colors Dark Theme */ +.dark .text-gray-900 { + color: #f9fafb !important; +} + +.dark .text-gray-800 { + color: #e5e7eb !important; +} + +.dark .text-gray-700 { + color: #d1d5db !important; +} + +.dark .text-gray-600 { + color: #9ca3af !important; +} + +.dark .text-gray-500 { + color: #6b7280 !important; +} + +.dark .text-gray-400 { + color: #9ca3af !important; +} + +.dark .text-gray-300 { + color: #d1d5db !important; +} + +/* Cards Dark Theme */ +.dark .card-hover { + background-color: #1f2937; + border-color: #374151; +} + +.dark .card-hover:hover { + background-color: #374151; + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5); +} + +/* Forms Dark Theme */ +.dark .form-input { + background-color: #374151; + border-color: #4b5563; + color: #f9fafb; +} + +.dark .form-input:focus { + border-color: #60a5fa; + background-color: #1f2937; +} + +.dark .form-input::placeholder { + color: #9ca3af; +} + +.dark input, +.dark textarea, +.dark select { + background-color: #374151; + border-color: #4b5563; + color: #f9fafb; +} + +.dark input:focus, +.dark textarea:focus, +.dark select:focus { + border-color: #60a5fa; + background-color: #1f2937; +} + +/* Borders Dark Theme */ +.dark .border-gray-300 { + border-color: #4b5563 !important; +} + +.dark .border-gray-200 { + border-color: #374151 !important; +} + +.dark .border-t { + border-color: #374151; +} + +/* Contact Form Dark Theme */ +.dark .contact-form { + background: linear-gradient(145deg, rgba(31,41,55,0.9), rgba(31,41,55,0.95)); + border-color: #374151; +} + +/* Service Cards Dark Theme */ +.dark .service-card { + background: linear-gradient(145deg, rgba(31,41,55,0.8), rgba(31,41,55,0.6)); + border-color: #374151; +} + +/* Team Cards Dark Theme */ +.dark .team-card { + background-color: #1f2937; + border-color: #374151; +} + +/* Portfolio Items Dark Theme */ +.dark .portfolio-item { + background-color: #1f2937; +} + +/* Hero Section Dark Theme */ +.dark .hero-section { + background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%); +} + +/* Shadows Dark Theme */ +.dark .shadow-lg { + box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3), 0 4px 6px -2px rgba(0, 0, 0, 0.2); +} + +.dark .shadow-xl { + box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.3), 0 10px 10px -5px rgba(0, 0, 0, 0.2); +} + +.dark .shadow-2xl { + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); +} + +/* Dropdown Dark Theme */ +.dark .dropdown-menu { + background-color: #1f2937; + border-color: #374151; +} + +.dark .dropdown-menu a { + color: #d1d5db; +} + +.dark .dropdown-menu a:hover { + background-color: #374151; + color: #f9fafb; +} + +/* Icons Dark Theme */ +.dark .text-blue-600 { + color: #60a5fa !important; +} + +.dark .text-purple-600 { + color: #a78bfa !important; +} + +.dark .text-green-600 { + color: #34d399 !important; +} + +.dark .text-yellow-600 { + color: #fbbf24 !important; +} + +/* Buttons Dark Theme */ +.dark .btn-primary { + background: linear-gradient(135deg, #3b82f6, #1d4ed8); +} + +.dark .btn-primary:hover { + background: linear-gradient(135deg, #1d4ed8, #1e40af); +} + +/* Footer Dark Theme */ +.dark footer { + background-color: #030712; + border-color: #1f2937; +} + +.dark .footer-gradient { + background: linear-gradient(135deg, #030712 0%, #111827 100%); +} + +/* Scrollbar Dark Theme */ +.dark ::-webkit-scrollbar-track { + background: #1f2937; +} + +.dark ::-webkit-scrollbar-thumb { + background: linear-gradient(135deg, #3b82f6, #1d4ed8); +} + +/* Technology Stack Dark Theme */ +.dark .tech-stack { + background-color: #030712; +} + +.dark .tech-card { + background-color: #1f2937; + border-color: #374151; +} + +/* CTA Section Dark Theme */ +.dark .cta-section { + background: linear-gradient(135deg, #1e40af 0%, #7c3aed 100%); +} + +/* Testimonials Dark Theme */ +.dark .testimonial-card { + background: rgba(31, 41, 55, 0.8); + border-color: #374151; +} + +/* Alert Messages Dark Theme */ +.dark .alert-success { + background: rgba(16, 185, 129, 0.1); + border-color: rgba(16, 185, 129, 0.3); + color: #10b981; +} + +.dark .alert-error { + background: rgba(239, 68, 68, 0.1); + border-color: rgba(239, 68, 68, 0.3); + color: #ef4444; +} + +/* Mobile Menu Dark Theme */ +.dark #mobile-menu { + background-color: #1f2937; + border-color: #374151; +} + +/* Language Dropdown Dark Theme */ +.dark #mobile-language-menu { + background-color: #1f2937; + border-color: #374151; +} + +/* Smooth Theme Transition */ +* { + transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; +} + +/* Print styles for dark theme */ +@media print { + .dark * { + background: white !important; + color: black !important; + } +} + +/* High contrast mode */ +@media (prefers-contrast: high) { + .dark { + color: white !important; + background-color: black !important; + } + + .dark .border { + border-width: 2px; + } +} + +/* Reduced motion for accessibility */ +@media (prefers-reduced-motion: reduce) { + .dark * { + transition: none !important; + animation: none !important; + } +} \ No newline at end of file diff --git a/.history/public/css/fixes_20251021185301.css b/.history/public/css/fixes_20251021185301.css new file mode 100644 index 0000000..d161529 --- /dev/null +++ b/.history/public/css/fixes_20251021185301.css @@ -0,0 +1,310 @@ +/* SmartSolTech - Design Fixes & Enhancements */ + +/* Glass effect improvements */ +.glass-effect { + background: rgba(255, 255, 255, 0.15); + border: 1px solid rgba(255, 255, 255, 0.3); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); + /* Fallback for browsers that don't support backdrop-filter */ +} + +/* Support backdrop-filter for modern browsers */ +@supports (backdrop-filter: blur(10px)) { + .glass-effect { + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + } +} + +/* Hero section improvements */ +.hero-section { + position: relative; + overflow: hidden; + min-height: 100vh; +} + +/* Background blob animations */ +@keyframes blob { + 0% { transform: translate(0px, 0px) scale(1); } + 33% { transform: translate(30px, -50px) scale(1.1); } + 66% { transform: translate(-20px, 20px) scale(0.9); } + 100% { transform: translate(0px, 0px) scale(1); } +} + +.animate-blob { + animation: blob 7s infinite; +} + +.animation-delay-2000 { + animation-delay: 2s; +} + +.animation-delay-4000 { + animation-delay: 4s; +} + +/* Enhanced card hover effects */ +.card-hover { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + backface-visibility: hidden; +} + +.card-hover:hover { + transform: translateY(-8px) scale(1.02); + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15); +} + +/* Portfolio item enhancements */ +.portfolio-item { + overflow: hidden; + border-radius: 16px; + position: relative; +} + +.portfolio-image { + transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1); +} + +.portfolio-item:hover .portfolio-image { + transform: scale(1.1); +} + +/* Button improvements */ +.btn-primary { + background: linear-gradient(135deg, #3B82F6, #1D4ED8); + border: none; + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.btn-primary::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); + transition: left 0.5s; +} + +.btn-primary:hover::before { + left: 100%; +} + +.btn-primary:hover { + background: linear-gradient(135deg, #1D4ED8, #1E40AF); + transform: translateY(-3px); + box-shadow: 0 15px 35px rgba(59, 130, 246, 0.4); +} + +/* Navigation improvements */ +.nav-link { + position: relative; + transition: all 0.3s ease; +} + +.nav-link::after { + content: ''; + position: absolute; + bottom: -2px; + left: 50%; + width: 0; + height: 2px; + background: linear-gradient(90deg, #3B82F6, #8B5CF6); + transition: all 0.3s ease; + transform: translateX(-50%); +} + +.nav-link:hover::after, +.nav-link.active::after { + width: 100%; +} + +/* Form improvements */ +.form-input { + transition: all 0.3s ease; + border: 2px solid #E5E7EB; + background: rgba(255, 255, 255, 0.95); +} + +.form-input:focus { + border-color: #3B82F6; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); + transform: translateY(-1px); +} + +/* Contact form styling */ +.contact-form { + background: linear-gradient(145deg, rgba(255,255,255,0.9), rgba(255,255,255,0.95)); + border: 1px solid rgba(255,255,255,0.3); + box-shadow: 0 20px 40px rgba(0,0,0,0.1); +} + +/* CTA section improvements */ +.cta-section { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + position: relative; +} + +.cta-section::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: url('data:image/svg+xml,'); + opacity: 0.3; +} + +/* Service cards */ +.service-card { + background: linear-gradient(145deg, rgba(255,255,255,0.1), rgba(255,255,255,0.05)); + border: 1px solid rgba(255,255,255,0.2); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); +} + +/* Team member cards */ +.team-card { + transition: all 0.3s ease; + background: rgba(255, 255, 255, 0.95); +} + +.team-card:hover { + transform: translateY(-10px); + box-shadow: 0 30px 60px rgba(0,0,0,0.12); +} + +/* Technology icons */ +.tech-icon { + transition: all 0.3s ease; +} + +.tech-icon:hover { + transform: scale(1.1) rotate(5deg); + filter: brightness(1.2); +} + +/* Loading states */ +.loading { + opacity: 0.7; + pointer-events: none; +} + +.loading::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 20px; + height: 20px; + margin: -10px 0 0 -10px; + border: 2px solid #f3f3f3; + border-top: 2px solid #3B82F6; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Mobile optimizations */ +@media (max-width: 768px) { + .card-hover:hover { + transform: translateY(-4px) scale(1.01); + } + + .btn-primary:hover { + transform: translateY(-2px); + } + + .hero-section { + min-height: 80vh; + } + + .portfolio-item:hover .portfolio-image { + transform: scale(1.05); + } +} + +/* Accessibility improvements */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* Focus styles for better accessibility */ +button:focus, +input:focus, +textarea:focus, +select:focus, +a:focus { + outline: 2px solid #3B82F6; + outline-offset: 2px; +} + +/* Smooth scrolling */ +html { + scroll-behavior: smooth; +} + +/* Print styles */ +@media print { + .no-print { + display: none !important; + } + + body { + color: black !important; + background: white !important; + } +} + +/* iOS Style Theme Toggle */ +.theme-toggle-slider { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* Theme toggle background states */ +input#theme-toggle:checked + label > div { + background-color: #4F46E5 !important; +} + +input#theme-toggle + label > div { + background-color: #D1D5DB; +} + +/* Dark mode adjustments for toggle */ +.dark input#theme-toggle + label > div { + background-color: #374151; +} + +.dark input#theme-toggle:checked + label > div { + background-color: #6366F1 !important; +} + +/* Smooth transitions for icons */ +.theme-sun-icon, +.theme-moon-icon { + transition: opacity 0.3s ease; +} + +/* Dark mode support (if needed) */ +@media (prefers-color-scheme: dark) { + .auto-dark { + color: #f9fafb; + background-color: #111827; + } +} \ No newline at end of file diff --git a/.history/public/css/fixes_20251021190951.css b/.history/public/css/fixes_20251021190951.css new file mode 100644 index 0000000..d161529 --- /dev/null +++ b/.history/public/css/fixes_20251021190951.css @@ -0,0 +1,310 @@ +/* SmartSolTech - Design Fixes & Enhancements */ + +/* Glass effect improvements */ +.glass-effect { + background: rgba(255, 255, 255, 0.15); + border: 1px solid rgba(255, 255, 255, 0.3); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1); + /* Fallback for browsers that don't support backdrop-filter */ +} + +/* Support backdrop-filter for modern browsers */ +@supports (backdrop-filter: blur(10px)) { + .glass-effect { + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + } +} + +/* Hero section improvements */ +.hero-section { + position: relative; + overflow: hidden; + min-height: 100vh; +} + +/* Background blob animations */ +@keyframes blob { + 0% { transform: translate(0px, 0px) scale(1); } + 33% { transform: translate(30px, -50px) scale(1.1); } + 66% { transform: translate(-20px, 20px) scale(0.9); } + 100% { transform: translate(0px, 0px) scale(1); } +} + +.animate-blob { + animation: blob 7s infinite; +} + +.animation-delay-2000 { + animation-delay: 2s; +} + +.animation-delay-4000 { + animation-delay: 4s; +} + +/* Enhanced card hover effects */ +.card-hover { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + backface-visibility: hidden; +} + +.card-hover:hover { + transform: translateY(-8px) scale(1.02); + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15); +} + +/* Portfolio item enhancements */ +.portfolio-item { + overflow: hidden; + border-radius: 16px; + position: relative; +} + +.portfolio-image { + transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1); +} + +.portfolio-item:hover .portfolio-image { + transform: scale(1.1); +} + +/* Button improvements */ +.btn-primary { + background: linear-gradient(135deg, #3B82F6, #1D4ED8); + border: none; + transition: all 0.3s ease; + position: relative; + overflow: hidden; +} + +.btn-primary::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); + transition: left 0.5s; +} + +.btn-primary:hover::before { + left: 100%; +} + +.btn-primary:hover { + background: linear-gradient(135deg, #1D4ED8, #1E40AF); + transform: translateY(-3px); + box-shadow: 0 15px 35px rgba(59, 130, 246, 0.4); +} + +/* Navigation improvements */ +.nav-link { + position: relative; + transition: all 0.3s ease; +} + +.nav-link::after { + content: ''; + position: absolute; + bottom: -2px; + left: 50%; + width: 0; + height: 2px; + background: linear-gradient(90deg, #3B82F6, #8B5CF6); + transition: all 0.3s ease; + transform: translateX(-50%); +} + +.nav-link:hover::after, +.nav-link.active::after { + width: 100%; +} + +/* Form improvements */ +.form-input { + transition: all 0.3s ease; + border: 2px solid #E5E7EB; + background: rgba(255, 255, 255, 0.95); +} + +.form-input:focus { + border-color: #3B82F6; + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); + transform: translateY(-1px); +} + +/* Contact form styling */ +.contact-form { + background: linear-gradient(145deg, rgba(255,255,255,0.9), rgba(255,255,255,0.95)); + border: 1px solid rgba(255,255,255,0.3); + box-shadow: 0 20px 40px rgba(0,0,0,0.1); +} + +/* CTA section improvements */ +.cta-section { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + position: relative; +} + +.cta-section::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: url('data:image/svg+xml,'); + opacity: 0.3; +} + +/* Service cards */ +.service-card { + background: linear-gradient(145deg, rgba(255,255,255,0.1), rgba(255,255,255,0.05)); + border: 1px solid rgba(255,255,255,0.2); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); +} + +/* Team member cards */ +.team-card { + transition: all 0.3s ease; + background: rgba(255, 255, 255, 0.95); +} + +.team-card:hover { + transform: translateY(-10px); + box-shadow: 0 30px 60px rgba(0,0,0,0.12); +} + +/* Technology icons */ +.tech-icon { + transition: all 0.3s ease; +} + +.tech-icon:hover { + transform: scale(1.1) rotate(5deg); + filter: brightness(1.2); +} + +/* Loading states */ +.loading { + opacity: 0.7; + pointer-events: none; +} + +.loading::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 20px; + height: 20px; + margin: -10px 0 0 -10px; + border: 2px solid #f3f3f3; + border-top: 2px solid #3B82F6; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Mobile optimizations */ +@media (max-width: 768px) { + .card-hover:hover { + transform: translateY(-4px) scale(1.01); + } + + .btn-primary:hover { + transform: translateY(-2px); + } + + .hero-section { + min-height: 80vh; + } + + .portfolio-item:hover .portfolio-image { + transform: scale(1.05); + } +} + +/* Accessibility improvements */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* Focus styles for better accessibility */ +button:focus, +input:focus, +textarea:focus, +select:focus, +a:focus { + outline: 2px solid #3B82F6; + outline-offset: 2px; +} + +/* Smooth scrolling */ +html { + scroll-behavior: smooth; +} + +/* Print styles */ +@media print { + .no-print { + display: none !important; + } + + body { + color: black !important; + background: white !important; + } +} + +/* iOS Style Theme Toggle */ +.theme-toggle-slider { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* Theme toggle background states */ +input#theme-toggle:checked + label > div { + background-color: #4F46E5 !important; +} + +input#theme-toggle + label > div { + background-color: #D1D5DB; +} + +/* Dark mode adjustments for toggle */ +.dark input#theme-toggle + label > div { + background-color: #374151; +} + +.dark input#theme-toggle:checked + label > div { + background-color: #6366F1 !important; +} + +/* Smooth transitions for icons */ +.theme-sun-icon, +.theme-moon-icon { + transition: opacity 0.3s ease; +} + +/* Dark mode support (if needed) */ +@media (prefers-color-scheme: dark) { + .auto-dark { + color: #f9fafb; + background-color: #111827; + } +} \ No newline at end of file diff --git a/.history/public/css/main_20251020042218.css b/.history/public/css/main_20251020042218.css new file mode 100644 index 0000000..815736f --- /dev/null +++ b/.history/public/css/main_20251020042218.css @@ -0,0 +1,554 @@ +/* SmartSolTech - Main Styles */ + +/* CSS Reset and Base */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + line-height: 1.6; + color: #1f2937; +} + +/* Utility Classes */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 1rem; +} + +.section-padding { + padding: 4rem 0; +} + +/* Navigation */ +.navbar { + background: rgba(255, 255, 255, 0.95); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + transition: all 0.3s ease; +} + +.navbar-brand { + font-size: 1.5rem; + font-weight: bold; + color: #3b82f6; + text-decoration: none; +} + +.navbar-nav { + display: flex; + gap: 2rem; + list-style: none; +} + +.nav-link { + color: #6b7280; + text-decoration: none; + font-weight: 500; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + transition: all 0.3s ease; +} + +.nav-link:hover, +.nav-link.active { + color: #3b82f6; + background-color: #eff6ff; +} + +.mobile-menu { + overflow: hidden; +} + +.mobile-menu.show { + max-height: 500px; +} + +/* Button Hover Effects */ +.btn-primary { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + transition: all 0.3s ease; + transform: translateY(0); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3); +} + +/* Card Hover Effects */ +.card-hover { + transition: all 0.3s ease; + transform: translateY(0); +} + +.card-hover:hover { + transform: translateY(-8px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +/* Portfolio Grid */ +.portfolio-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 2rem; +} + +.portfolio-item { + border-radius: 1rem; + overflow: hidden; + background: white; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); + transition: all 0.3s ease; +} + +.portfolio-item:hover { + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +.portfolio-image { + position: relative; + overflow: hidden; + aspect-ratio: 16/10; +} + +.portfolio-image img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.portfolio-item:hover .portfolio-image img { + transform: scale(1.05); +} + +.portfolio-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(59, 130, 246, 0.8), rgba(139, 92, 246, 0.8)); + opacity: 0; + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.portfolio-item:hover .portfolio-overlay { + opacity: 1; +} + +/* Service Cards */ +.service-card { + background: white; + border-radius: 1rem; + padding: 2rem; + text-align: center; + transition: all 0.3s ease; + border: 2px solid transparent; +} + +.service-card:hover { + border-color: var(--primary-color); + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(59, 130, 246, 0.1); +} + +.service-icon { + width: 80px; + height: 80px; + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1rem; + font-size: 2rem; + color: white; + transition: all 0.3s ease; +} + +.service-card:hover .service-icon { + transform: scale(1.1) rotate(5deg); +} + +/* Contact Form */ +.contact-form { + background: white; + border-radius: 1rem; + padding: 2rem; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); +} + +.form-group { + margin-bottom: 1.5rem; +} + +.form-control { + width: 100%; + padding: 1rem; + border: 2px solid var(--border-color); + border-radius: 0.5rem; + font-size: 1rem; + transition: all 0.3s ease; + background: white; +} + +.form-control:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +/* Calculator Styles */ +.calculator-step { + animation: fadeIn 0.5s ease-in-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +.option-card { + border: 2px solid var(--border-color); + border-radius: 1rem; + padding: 1.5rem; + cursor: pointer; + transition: all 0.3s ease; + background: white; +} + +.option-card:hover { + border-color: var(--primary-color); + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); +} + +.option-card.selected { + border-color: var(--primary-color); + background: rgba(59, 130, 246, 0.05); +} + +/* Progress Bar */ +.progress-bar { + width: 100%; + height: 8px; + background: var(--border-color); + border-radius: 4px; + overflow: hidden; + margin-bottom: 2rem; +} + +.progress-fill { + height: 100%; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + transition: width 0.3s ease; +} + +/* Hero Section Animations */ +.hero-content { + animation: heroFadeIn 1s ease-out; +} + +@keyframes heroFadeIn { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Parallax Effect */ +.parallax { + background-attachment: fixed; + background-position: center; + background-repeat: no-repeat; + background-size: cover; +} + +/* Scroll Animations */ +.fade-in-up { + opacity: 0; + transform: translateY(30px); + transition: all 0.8s ease; +} + +.fade-in-up.animate { + opacity: 1; + transform: translateY(0); +} + +/* Typography */ +.gradient-text { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Status Badges */ +.status-badge { + padding: 0.25rem 0.75rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.status-new { + background: rgba(239, 68, 68, 0.1); + color: #dc2626; +} + +.status-in-progress { + background: rgba(245, 158, 11, 0.1); + color: #d97706; +} + +.status-completed { + background: rgba(16, 185, 129, 0.1); + color: #059669; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .portfolio-grid { + grid-template-columns: 1fr; + } + + .hero-title { + font-size: 2.5rem; + } + + .service-card { + padding: 1.5rem; + } + + .calculator-step { + padding: 1rem; + } +} + +/* Dark Mode Support */ +@media (prefers-color-scheme: dark) { + :root { + --text-dark: #f9fafb; + --text-light: #d1d5db; + --bg-light: #1f2937; + --border-color: #374151; + } + + body { + background-color: #111827; + color: var(--text-dark); + } + + .card-hover, .service-card, .contact-form, .option-card { + background: #1f2937; + border-color: var(--border-color); + } + + .form-control { + background: #374151; + border-color: #4b5563; + color: white; + } + + .form-control:focus { + border-color: var(--primary-color); + } +} + +/* Print Styles */ +@media print { + .navbar, .footer, .contact-form, .mobile-menu { + display: none !important; + } + + body { + font-size: 12pt; + line-height: 1.5; + } + + .portfolio-item, .service-card { + break-inside: avoid; + margin-bottom: 1rem; + } +} + +/* Accessibility */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* Focus styles for better accessibility */ +.form-control:focus, +.btn:focus, +.option-card:focus { + outline: 2px solid var(--primary-color); + outline-offset: 2px; +} + +/* Loading States */ +.btn-loading { + position: relative; + color: transparent; +} + +.btn-loading::after { + content: ''; + position: absolute; + width: 16px; + height: 16px; + top: 50%; + left: 50%; + margin-left: -8px; + margin-top: -8px; + border: 2px solid transparent; + border-top-color: currentColor; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +/* Calculator Styles */ +.calculator-step { + display: none; +} + +.calculator-step.active { + display: block; +} + +.service-option, +.complexity-option, +.timeline-option { + cursor: pointer; + transition: all 0.3s ease; + position: relative; +} + +.service-option:hover, +.complexity-option:hover, +.timeline-option:hover { + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected, +.complexity-option.selected, +.timeline-option.selected { + border-color: #3B82F6 !important; + background-color: #EBF8FF !important; + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected::after, +.complexity-option.selected::after, +.timeline-option.selected::after { + content: '✓'; + position: absolute; + top: 0.5rem; + right: 0.5rem; + width: 24px; + height: 24px; + background: #3B82F6; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: bold; +} + +/* Price Display Animation */ +#final-price { + animation: priceReveal 0.8s ease-in-out; +} + +@keyframes priceReveal { + 0% { + opacity: 0; + transform: scale(0.8); + } + 50% { + transform: scale(1.1); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +/* Calculator Progress Bar */ +.calculator-progress { + width: 100%; + height: 4px; + background: #e5e7eb; + border-radius: 2px; + margin-bottom: 2rem; + overflow: hidden; +} + +.calculator-progress-bar { + height: 100%; + background: linear-gradient(90deg, #3B82F6, #8B5CF6); + border-radius: 2px; + transition: width 0.3s ease; + width: 33.33%; +} + +.calculator-progress-bar.step-2 { + width: 66.66%; +} + +.calculator-progress-bar.step-3 { + width: 100%; +} + +/* Calculator Mobile Improvements */ +@media (max-width: 768px) { + .service-option, + .complexity-option, + .timeline-option { + margin-bottom: 1rem; + } + + #final-price { + font-size: 2.5rem; + } +} \ No newline at end of file diff --git a/.history/public/css/main_20251020042243.css b/.history/public/css/main_20251020042243.css new file mode 100644 index 0000000..815736f --- /dev/null +++ b/.history/public/css/main_20251020042243.css @@ -0,0 +1,554 @@ +/* SmartSolTech - Main Styles */ + +/* CSS Reset and Base */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + line-height: 1.6; + color: #1f2937; +} + +/* Utility Classes */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 1rem; +} + +.section-padding { + padding: 4rem 0; +} + +/* Navigation */ +.navbar { + background: rgba(255, 255, 255, 0.95); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + transition: all 0.3s ease; +} + +.navbar-brand { + font-size: 1.5rem; + font-weight: bold; + color: #3b82f6; + text-decoration: none; +} + +.navbar-nav { + display: flex; + gap: 2rem; + list-style: none; +} + +.nav-link { + color: #6b7280; + text-decoration: none; + font-weight: 500; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + transition: all 0.3s ease; +} + +.nav-link:hover, +.nav-link.active { + color: #3b82f6; + background-color: #eff6ff; +} + +.mobile-menu { + overflow: hidden; +} + +.mobile-menu.show { + max-height: 500px; +} + +/* Button Hover Effects */ +.btn-primary { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + transition: all 0.3s ease; + transform: translateY(0); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3); +} + +/* Card Hover Effects */ +.card-hover { + transition: all 0.3s ease; + transform: translateY(0); +} + +.card-hover:hover { + transform: translateY(-8px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +/* Portfolio Grid */ +.portfolio-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 2rem; +} + +.portfolio-item { + border-radius: 1rem; + overflow: hidden; + background: white; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); + transition: all 0.3s ease; +} + +.portfolio-item:hover { + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +.portfolio-image { + position: relative; + overflow: hidden; + aspect-ratio: 16/10; +} + +.portfolio-image img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.portfolio-item:hover .portfolio-image img { + transform: scale(1.05); +} + +.portfolio-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(59, 130, 246, 0.8), rgba(139, 92, 246, 0.8)); + opacity: 0; + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.portfolio-item:hover .portfolio-overlay { + opacity: 1; +} + +/* Service Cards */ +.service-card { + background: white; + border-radius: 1rem; + padding: 2rem; + text-align: center; + transition: all 0.3s ease; + border: 2px solid transparent; +} + +.service-card:hover { + border-color: var(--primary-color); + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(59, 130, 246, 0.1); +} + +.service-icon { + width: 80px; + height: 80px; + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1rem; + font-size: 2rem; + color: white; + transition: all 0.3s ease; +} + +.service-card:hover .service-icon { + transform: scale(1.1) rotate(5deg); +} + +/* Contact Form */ +.contact-form { + background: white; + border-radius: 1rem; + padding: 2rem; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); +} + +.form-group { + margin-bottom: 1.5rem; +} + +.form-control { + width: 100%; + padding: 1rem; + border: 2px solid var(--border-color); + border-radius: 0.5rem; + font-size: 1rem; + transition: all 0.3s ease; + background: white; +} + +.form-control:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +/* Calculator Styles */ +.calculator-step { + animation: fadeIn 0.5s ease-in-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +.option-card { + border: 2px solid var(--border-color); + border-radius: 1rem; + padding: 1.5rem; + cursor: pointer; + transition: all 0.3s ease; + background: white; +} + +.option-card:hover { + border-color: var(--primary-color); + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); +} + +.option-card.selected { + border-color: var(--primary-color); + background: rgba(59, 130, 246, 0.05); +} + +/* Progress Bar */ +.progress-bar { + width: 100%; + height: 8px; + background: var(--border-color); + border-radius: 4px; + overflow: hidden; + margin-bottom: 2rem; +} + +.progress-fill { + height: 100%; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + transition: width 0.3s ease; +} + +/* Hero Section Animations */ +.hero-content { + animation: heroFadeIn 1s ease-out; +} + +@keyframes heroFadeIn { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Parallax Effect */ +.parallax { + background-attachment: fixed; + background-position: center; + background-repeat: no-repeat; + background-size: cover; +} + +/* Scroll Animations */ +.fade-in-up { + opacity: 0; + transform: translateY(30px); + transition: all 0.8s ease; +} + +.fade-in-up.animate { + opacity: 1; + transform: translateY(0); +} + +/* Typography */ +.gradient-text { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Status Badges */ +.status-badge { + padding: 0.25rem 0.75rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.status-new { + background: rgba(239, 68, 68, 0.1); + color: #dc2626; +} + +.status-in-progress { + background: rgba(245, 158, 11, 0.1); + color: #d97706; +} + +.status-completed { + background: rgba(16, 185, 129, 0.1); + color: #059669; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .portfolio-grid { + grid-template-columns: 1fr; + } + + .hero-title { + font-size: 2.5rem; + } + + .service-card { + padding: 1.5rem; + } + + .calculator-step { + padding: 1rem; + } +} + +/* Dark Mode Support */ +@media (prefers-color-scheme: dark) { + :root { + --text-dark: #f9fafb; + --text-light: #d1d5db; + --bg-light: #1f2937; + --border-color: #374151; + } + + body { + background-color: #111827; + color: var(--text-dark); + } + + .card-hover, .service-card, .contact-form, .option-card { + background: #1f2937; + border-color: var(--border-color); + } + + .form-control { + background: #374151; + border-color: #4b5563; + color: white; + } + + .form-control:focus { + border-color: var(--primary-color); + } +} + +/* Print Styles */ +@media print { + .navbar, .footer, .contact-form, .mobile-menu { + display: none !important; + } + + body { + font-size: 12pt; + line-height: 1.5; + } + + .portfolio-item, .service-card { + break-inside: avoid; + margin-bottom: 1rem; + } +} + +/* Accessibility */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* Focus styles for better accessibility */ +.form-control:focus, +.btn:focus, +.option-card:focus { + outline: 2px solid var(--primary-color); + outline-offset: 2px; +} + +/* Loading States */ +.btn-loading { + position: relative; + color: transparent; +} + +.btn-loading::after { + content: ''; + position: absolute; + width: 16px; + height: 16px; + top: 50%; + left: 50%; + margin-left: -8px; + margin-top: -8px; + border: 2px solid transparent; + border-top-color: currentColor; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +/* Calculator Styles */ +.calculator-step { + display: none; +} + +.calculator-step.active { + display: block; +} + +.service-option, +.complexity-option, +.timeline-option { + cursor: pointer; + transition: all 0.3s ease; + position: relative; +} + +.service-option:hover, +.complexity-option:hover, +.timeline-option:hover { + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected, +.complexity-option.selected, +.timeline-option.selected { + border-color: #3B82F6 !important; + background-color: #EBF8FF !important; + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected::after, +.complexity-option.selected::after, +.timeline-option.selected::after { + content: '✓'; + position: absolute; + top: 0.5rem; + right: 0.5rem; + width: 24px; + height: 24px; + background: #3B82F6; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: bold; +} + +/* Price Display Animation */ +#final-price { + animation: priceReveal 0.8s ease-in-out; +} + +@keyframes priceReveal { + 0% { + opacity: 0; + transform: scale(0.8); + } + 50% { + transform: scale(1.1); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +/* Calculator Progress Bar */ +.calculator-progress { + width: 100%; + height: 4px; + background: #e5e7eb; + border-radius: 2px; + margin-bottom: 2rem; + overflow: hidden; +} + +.calculator-progress-bar { + height: 100%; + background: linear-gradient(90deg, #3B82F6, #8B5CF6); + border-radius: 2px; + transition: width 0.3s ease; + width: 33.33%; +} + +.calculator-progress-bar.step-2 { + width: 66.66%; +} + +.calculator-progress-bar.step-3 { + width: 100%; +} + +/* Calculator Mobile Improvements */ +@media (max-width: 768px) { + .service-option, + .complexity-option, + .timeline-option { + margin-bottom: 1rem; + } + + #final-price { + font-size: 2.5rem; + } +} \ No newline at end of file diff --git a/.history/public/css/main_20251020045321.css b/.history/public/css/main_20251020045321.css new file mode 100644 index 0000000..4026b93 --- /dev/null +++ b/.history/public/css/main_20251020045321.css @@ -0,0 +1,570 @@ +/* SmartSolTech - Main Styles */ + +/* CSS Reset and Base */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + line-height: 1.6; + color: #1f2937; + background-color: #ffffff; +} + +/* Root Variables */ +:root { + --primary-color: #3B82F6; + --secondary-color: #8B5CF6; + --accent-color: #10B981; + --text-dark: #1f2937; + --text-light: #6b7280; + --bg-light: #f9fafb; + --border-color: #e5e7eb; +} + +/* Utility Classes */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 1rem; +} + +.section-padding { + padding: 4rem 0; +} + +/* Navigation */ +.navbar { + background: rgba(255, 255, 255, 0.95); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + transition: all 0.3s ease; +} + +.navbar-brand { + font-size: 1.5rem; + font-weight: bold; + color: #3b82f6; + text-decoration: none; +} + +.navbar-nav { + display: flex; + gap: 2rem; + list-style: none; +} + +.nav-link { + color: #6b7280; + text-decoration: none; + font-weight: 500; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + transition: all 0.3s ease; +} + +.nav-link:hover, +.nav-link.active { + color: #3b82f6; + background-color: #eff6ff; +} + +.mobile-menu { + overflow: hidden; +} + +.mobile-menu.show { + max-height: 500px; +} + +/* Button Hover Effects */ +.btn-primary { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + transition: all 0.3s ease; + transform: translateY(0); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3); +} + +/* Card Hover Effects */ +.card-hover { + transition: all 0.3s ease; + transform: translateY(0); +} + +.card-hover:hover { + transform: translateY(-8px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +/* Portfolio Grid */ +.portfolio-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 2rem; +} + +.portfolio-item { + border-radius: 1rem; + overflow: hidden; + background: white; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); + transition: all 0.3s ease; +} + +.portfolio-item:hover { + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +.portfolio-image { + position: relative; + overflow: hidden; + aspect-ratio: 16/10; +} + +.portfolio-image img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.portfolio-item:hover .portfolio-image img { + transform: scale(1.05); +} + +.portfolio-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(59, 130, 246, 0.8), rgba(139, 92, 246, 0.8)); + opacity: 0; + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.portfolio-item:hover .portfolio-overlay { + opacity: 1; +} + +/* Service Cards */ +.service-card { + background: white; + border-radius: 1rem; + padding: 2rem; + text-align: center; + transition: all 0.3s ease; + border: 2px solid transparent; +} + +.service-card:hover { + border-color: var(--primary-color); + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(59, 130, 246, 0.1); +} + +.service-icon { + width: 80px; + height: 80px; + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1rem; + font-size: 2rem; + color: white; + transition: all 0.3s ease; +} + +.service-card:hover .service-icon { + transform: scale(1.1) rotate(5deg); +} + +/* Contact Form */ +.contact-form { + background: white; + border-radius: 1rem; + padding: 2rem; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); +} + +.form-group { + margin-bottom: 1.5rem; +} + +.form-control { + width: 100%; + padding: 1rem; + border: 2px solid var(--border-color); + border-radius: 0.5rem; + font-size: 1rem; + transition: all 0.3s ease; + background: white; +} + +.form-control:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +/* Calculator Styles */ +.calculator-step { + animation: fadeIn 0.5s ease-in-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +.option-card { + border: 2px solid var(--border-color); + border-radius: 1rem; + padding: 1.5rem; + cursor: pointer; + transition: all 0.3s ease; + background: white; +} + +.option-card:hover { + border-color: var(--primary-color); + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); +} + +.option-card.selected { + border-color: var(--primary-color); + background: rgba(59, 130, 246, 0.05); +} + +/* Progress Bar */ +.progress-bar { + width: 100%; + height: 8px; + background: var(--border-color); + border-radius: 4px; + overflow: hidden; + margin-bottom: 2rem; +} + +.progress-fill { + height: 100%; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + transition: width 0.3s ease; +} + +/* Hero Section Animations */ +.hero-content { + animation: heroFadeIn 1s ease-out; +} + +@keyframes heroFadeIn { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Parallax Effect */ +.parallax { + background-attachment: fixed; + background-position: center; + background-repeat: no-repeat; + background-size: cover; +} + +/* Scroll Animations */ +.fade-in-up { + opacity: 0; + transform: translateY(30px); + transition: all 0.8s ease; +} + +.fade-in-up.animate { + opacity: 1; + transform: translateY(0); +} + +/* Typography */ +.gradient-text { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Status Badges */ +.status-badge { + padding: 0.25rem 0.75rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.status-new { + background: rgba(239, 68, 68, 0.1); + color: #dc2626; +} + +.status-in-progress { + background: rgba(245, 158, 11, 0.1); + color: #d97706; +} + +.status-completed { + background: rgba(16, 185, 129, 0.1); + color: #059669; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .portfolio-grid { + grid-template-columns: 1fr; + } + + .hero-title { + font-size: 2.5rem; + } + + .service-card { + padding: 1.5rem; + } + + .calculator-step { + padding: 1rem; + } +} + +/* Dark Mode Support */ +@media (prefers-color-scheme: dark) { + :root { + --text-dark: #f9fafb; + --text-light: #d1d5db; + --bg-light: #1f2937; + --border-color: #374151; + } + + body { + background-color: #111827; + color: var(--text-dark); + } + + .card-hover, .service-card, .contact-form, .option-card { + background: #1f2937; + border-color: var(--border-color); + } + + .form-control { + background: #374151; + border-color: #4b5563; + color: white; + } + + .form-control:focus { + border-color: var(--primary-color); + } +} + +/* Print Styles */ +@media print { + .navbar, .footer, .contact-form, .mobile-menu { + display: none !important; + } + + body { + font-size: 12pt; + line-height: 1.5; + } + + .portfolio-item, .service-card { + break-inside: avoid; + margin-bottom: 1rem; + } +} + +/* Accessibility */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* Focus styles for better accessibility */ +.form-control:focus, +.btn:focus, +.option-card:focus { + outline: 2px solid var(--primary-color); + outline-offset: 2px; +} + +/* Loading States */ +.btn-loading { + position: relative; + color: transparent; +} + +.btn-loading::after { + content: ''; + position: absolute; + width: 16px; + height: 16px; + top: 50%; + left: 50%; + margin-left: -8px; + margin-top: -8px; + border: 2px solid transparent; + border-top-color: currentColor; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +/* Calculator Styles */ +.calculator-step { + display: none; +} + +.calculator-step.active { + display: block; +} + +.service-option, +.complexity-option, +.timeline-option { + cursor: pointer; + transition: all 0.3s ease; + position: relative; +} + +.service-option:hover, +.complexity-option:hover, +.timeline-option:hover { + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected, +.complexity-option.selected, +.timeline-option.selected { + border-color: #3B82F6 !important; + background-color: #EBF8FF !important; + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected::after, +.complexity-option.selected::after, +.timeline-option.selected::after { + content: '✓'; + position: absolute; + top: 0.5rem; + right: 0.5rem; + width: 24px; + height: 24px; + background: #3B82F6; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: bold; +} + +/* Price Display Animation */ +#final-price { + animation: priceReveal 0.8s ease-in-out; +} + +@keyframes priceReveal { + 0% { + opacity: 0; + transform: scale(0.8); + } + 50% { + transform: scale(1.1); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +/* Calculator Progress Bar */ +.calculator-progress { + width: 100%; + height: 4px; + background: #e5e7eb; + border-radius: 2px; + margin-bottom: 2rem; + overflow: hidden; +} + +.calculator-progress-bar { + height: 100%; + background: linear-gradient(90deg, #3B82F6, #8B5CF6); + border-radius: 2px; + transition: width 0.3s ease; + width: 33.33%; +} + +.calculator-progress-bar.step-2 { + width: 66.66%; +} + +.calculator-progress-bar.step-3 { + width: 100%; +} + +/* Calculator Mobile Improvements */ +@media (max-width: 768px) { + .service-option, + .complexity-option, + .timeline-option { + margin-bottom: 1rem; + } + + #final-price { + font-size: 2.5rem; + } +} \ No newline at end of file diff --git a/.history/public/css/main_20251020045323.css b/.history/public/css/main_20251020045323.css new file mode 100644 index 0000000..4026b93 --- /dev/null +++ b/.history/public/css/main_20251020045323.css @@ -0,0 +1,570 @@ +/* SmartSolTech - Main Styles */ + +/* CSS Reset and Base */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + line-height: 1.6; + color: #1f2937; + background-color: #ffffff; +} + +/* Root Variables */ +:root { + --primary-color: #3B82F6; + --secondary-color: #8B5CF6; + --accent-color: #10B981; + --text-dark: #1f2937; + --text-light: #6b7280; + --bg-light: #f9fafb; + --border-color: #e5e7eb; +} + +/* Utility Classes */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 1rem; +} + +.section-padding { + padding: 4rem 0; +} + +/* Navigation */ +.navbar { + background: rgba(255, 255, 255, 0.95); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + transition: all 0.3s ease; +} + +.navbar-brand { + font-size: 1.5rem; + font-weight: bold; + color: #3b82f6; + text-decoration: none; +} + +.navbar-nav { + display: flex; + gap: 2rem; + list-style: none; +} + +.nav-link { + color: #6b7280; + text-decoration: none; + font-weight: 500; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + transition: all 0.3s ease; +} + +.nav-link:hover, +.nav-link.active { + color: #3b82f6; + background-color: #eff6ff; +} + +.mobile-menu { + overflow: hidden; +} + +.mobile-menu.show { + max-height: 500px; +} + +/* Button Hover Effects */ +.btn-primary { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + transition: all 0.3s ease; + transform: translateY(0); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3); +} + +/* Card Hover Effects */ +.card-hover { + transition: all 0.3s ease; + transform: translateY(0); +} + +.card-hover:hover { + transform: translateY(-8px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +/* Portfolio Grid */ +.portfolio-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 2rem; +} + +.portfolio-item { + border-radius: 1rem; + overflow: hidden; + background: white; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); + transition: all 0.3s ease; +} + +.portfolio-item:hover { + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +.portfolio-image { + position: relative; + overflow: hidden; + aspect-ratio: 16/10; +} + +.portfolio-image img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.portfolio-item:hover .portfolio-image img { + transform: scale(1.05); +} + +.portfolio-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(59, 130, 246, 0.8), rgba(139, 92, 246, 0.8)); + opacity: 0; + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.portfolio-item:hover .portfolio-overlay { + opacity: 1; +} + +/* Service Cards */ +.service-card { + background: white; + border-radius: 1rem; + padding: 2rem; + text-align: center; + transition: all 0.3s ease; + border: 2px solid transparent; +} + +.service-card:hover { + border-color: var(--primary-color); + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(59, 130, 246, 0.1); +} + +.service-icon { + width: 80px; + height: 80px; + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1rem; + font-size: 2rem; + color: white; + transition: all 0.3s ease; +} + +.service-card:hover .service-icon { + transform: scale(1.1) rotate(5deg); +} + +/* Contact Form */ +.contact-form { + background: white; + border-radius: 1rem; + padding: 2rem; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); +} + +.form-group { + margin-bottom: 1.5rem; +} + +.form-control { + width: 100%; + padding: 1rem; + border: 2px solid var(--border-color); + border-radius: 0.5rem; + font-size: 1rem; + transition: all 0.3s ease; + background: white; +} + +.form-control:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +/* Calculator Styles */ +.calculator-step { + animation: fadeIn 0.5s ease-in-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +.option-card { + border: 2px solid var(--border-color); + border-radius: 1rem; + padding: 1.5rem; + cursor: pointer; + transition: all 0.3s ease; + background: white; +} + +.option-card:hover { + border-color: var(--primary-color); + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); +} + +.option-card.selected { + border-color: var(--primary-color); + background: rgba(59, 130, 246, 0.05); +} + +/* Progress Bar */ +.progress-bar { + width: 100%; + height: 8px; + background: var(--border-color); + border-radius: 4px; + overflow: hidden; + margin-bottom: 2rem; +} + +.progress-fill { + height: 100%; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + transition: width 0.3s ease; +} + +/* Hero Section Animations */ +.hero-content { + animation: heroFadeIn 1s ease-out; +} + +@keyframes heroFadeIn { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Parallax Effect */ +.parallax { + background-attachment: fixed; + background-position: center; + background-repeat: no-repeat; + background-size: cover; +} + +/* Scroll Animations */ +.fade-in-up { + opacity: 0; + transform: translateY(30px); + transition: all 0.8s ease; +} + +.fade-in-up.animate { + opacity: 1; + transform: translateY(0); +} + +/* Typography */ +.gradient-text { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Status Badges */ +.status-badge { + padding: 0.25rem 0.75rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.status-new { + background: rgba(239, 68, 68, 0.1); + color: #dc2626; +} + +.status-in-progress { + background: rgba(245, 158, 11, 0.1); + color: #d97706; +} + +.status-completed { + background: rgba(16, 185, 129, 0.1); + color: #059669; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .portfolio-grid { + grid-template-columns: 1fr; + } + + .hero-title { + font-size: 2.5rem; + } + + .service-card { + padding: 1.5rem; + } + + .calculator-step { + padding: 1rem; + } +} + +/* Dark Mode Support */ +@media (prefers-color-scheme: dark) { + :root { + --text-dark: #f9fafb; + --text-light: #d1d5db; + --bg-light: #1f2937; + --border-color: #374151; + } + + body { + background-color: #111827; + color: var(--text-dark); + } + + .card-hover, .service-card, .contact-form, .option-card { + background: #1f2937; + border-color: var(--border-color); + } + + .form-control { + background: #374151; + border-color: #4b5563; + color: white; + } + + .form-control:focus { + border-color: var(--primary-color); + } +} + +/* Print Styles */ +@media print { + .navbar, .footer, .contact-form, .mobile-menu { + display: none !important; + } + + body { + font-size: 12pt; + line-height: 1.5; + } + + .portfolio-item, .service-card { + break-inside: avoid; + margin-bottom: 1rem; + } +} + +/* Accessibility */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* Focus styles for better accessibility */ +.form-control:focus, +.btn:focus, +.option-card:focus { + outline: 2px solid var(--primary-color); + outline-offset: 2px; +} + +/* Loading States */ +.btn-loading { + position: relative; + color: transparent; +} + +.btn-loading::after { + content: ''; + position: absolute; + width: 16px; + height: 16px; + top: 50%; + left: 50%; + margin-left: -8px; + margin-top: -8px; + border: 2px solid transparent; + border-top-color: currentColor; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +/* Calculator Styles */ +.calculator-step { + display: none; +} + +.calculator-step.active { + display: block; +} + +.service-option, +.complexity-option, +.timeline-option { + cursor: pointer; + transition: all 0.3s ease; + position: relative; +} + +.service-option:hover, +.complexity-option:hover, +.timeline-option:hover { + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected, +.complexity-option.selected, +.timeline-option.selected { + border-color: #3B82F6 !important; + background-color: #EBF8FF !important; + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected::after, +.complexity-option.selected::after, +.timeline-option.selected::after { + content: '✓'; + position: absolute; + top: 0.5rem; + right: 0.5rem; + width: 24px; + height: 24px; + background: #3B82F6; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: bold; +} + +/* Price Display Animation */ +#final-price { + animation: priceReveal 0.8s ease-in-out; +} + +@keyframes priceReveal { + 0% { + opacity: 0; + transform: scale(0.8); + } + 50% { + transform: scale(1.1); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +/* Calculator Progress Bar */ +.calculator-progress { + width: 100%; + height: 4px; + background: #e5e7eb; + border-radius: 2px; + margin-bottom: 2rem; + overflow: hidden; +} + +.calculator-progress-bar { + height: 100%; + background: linear-gradient(90deg, #3B82F6, #8B5CF6); + border-radius: 2px; + transition: width 0.3s ease; + width: 33.33%; +} + +.calculator-progress-bar.step-2 { + width: 66.66%; +} + +.calculator-progress-bar.step-3 { + width: 100%; +} + +/* Calculator Mobile Improvements */ +@media (max-width: 768px) { + .service-option, + .complexity-option, + .timeline-option { + margin-bottom: 1rem; + } + + #final-price { + font-size: 2.5rem; + } +} \ No newline at end of file diff --git a/.history/public/css/main_20251020045335.css b/.history/public/css/main_20251020045335.css new file mode 100644 index 0000000..d45d96e --- /dev/null +++ b/.history/public/css/main_20251020045335.css @@ -0,0 +1,573 @@ +/* SmartSolTech - Main Styles */ + +/* Tailwind Base (if not loading properly) */ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); + +/* CSS Reset and Base */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + line-height: 1.6; + color: #1f2937; + background-color: #ffffff; +} + +/* Root Variables */ +:root { + --primary-color: #3B82F6; + --secondary-color: #8B5CF6; + --accent-color: #10B981; + --text-dark: #1f2937; + --text-light: #6b7280; + --bg-light: #f9fafb; + --border-color: #e5e7eb; +} + +/* Utility Classes */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 1rem; +} + +.section-padding { + padding: 4rem 0; +} + +/* Navigation */ +.navbar { + background: rgba(255, 255, 255, 0.95); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + transition: all 0.3s ease; +} + +.navbar-brand { + font-size: 1.5rem; + font-weight: bold; + color: #3b82f6; + text-decoration: none; +} + +.navbar-nav { + display: flex; + gap: 2rem; + list-style: none; +} + +.nav-link { + color: #6b7280; + text-decoration: none; + font-weight: 500; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + transition: all 0.3s ease; +} + +.nav-link:hover, +.nav-link.active { + color: #3b82f6; + background-color: #eff6ff; +} + +.mobile-menu { + overflow: hidden; +} + +.mobile-menu.show { + max-height: 500px; +} + +/* Button Hover Effects */ +.btn-primary { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + transition: all 0.3s ease; + transform: translateY(0); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3); +} + +/* Card Hover Effects */ +.card-hover { + transition: all 0.3s ease; + transform: translateY(0); +} + +.card-hover:hover { + transform: translateY(-8px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +/* Portfolio Grid */ +.portfolio-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 2rem; +} + +.portfolio-item { + border-radius: 1rem; + overflow: hidden; + background: white; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); + transition: all 0.3s ease; +} + +.portfolio-item:hover { + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +.portfolio-image { + position: relative; + overflow: hidden; + aspect-ratio: 16/10; +} + +.portfolio-image img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.portfolio-item:hover .portfolio-image img { + transform: scale(1.05); +} + +.portfolio-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(59, 130, 246, 0.8), rgba(139, 92, 246, 0.8)); + opacity: 0; + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.portfolio-item:hover .portfolio-overlay { + opacity: 1; +} + +/* Service Cards */ +.service-card { + background: white; + border-radius: 1rem; + padding: 2rem; + text-align: center; + transition: all 0.3s ease; + border: 2px solid transparent; +} + +.service-card:hover { + border-color: var(--primary-color); + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(59, 130, 246, 0.1); +} + +.service-icon { + width: 80px; + height: 80px; + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1rem; + font-size: 2rem; + color: white; + transition: all 0.3s ease; +} + +.service-card:hover .service-icon { + transform: scale(1.1) rotate(5deg); +} + +/* Contact Form */ +.contact-form { + background: white; + border-radius: 1rem; + padding: 2rem; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); +} + +.form-group { + margin-bottom: 1.5rem; +} + +.form-control { + width: 100%; + padding: 1rem; + border: 2px solid var(--border-color); + border-radius: 0.5rem; + font-size: 1rem; + transition: all 0.3s ease; + background: white; +} + +.form-control:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +/* Calculator Styles */ +.calculator-step { + animation: fadeIn 0.5s ease-in-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +.option-card { + border: 2px solid var(--border-color); + border-radius: 1rem; + padding: 1.5rem; + cursor: pointer; + transition: all 0.3s ease; + background: white; +} + +.option-card:hover { + border-color: var(--primary-color); + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); +} + +.option-card.selected { + border-color: var(--primary-color); + background: rgba(59, 130, 246, 0.05); +} + +/* Progress Bar */ +.progress-bar { + width: 100%; + height: 8px; + background: var(--border-color); + border-radius: 4px; + overflow: hidden; + margin-bottom: 2rem; +} + +.progress-fill { + height: 100%; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + transition: width 0.3s ease; +} + +/* Hero Section Animations */ +.hero-content { + animation: heroFadeIn 1s ease-out; +} + +@keyframes heroFadeIn { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Parallax Effect */ +.parallax { + background-attachment: fixed; + background-position: center; + background-repeat: no-repeat; + background-size: cover; +} + +/* Scroll Animations */ +.fade-in-up { + opacity: 0; + transform: translateY(30px); + transition: all 0.8s ease; +} + +.fade-in-up.animate { + opacity: 1; + transform: translateY(0); +} + +/* Typography */ +.gradient-text { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Status Badges */ +.status-badge { + padding: 0.25rem 0.75rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.status-new { + background: rgba(239, 68, 68, 0.1); + color: #dc2626; +} + +.status-in-progress { + background: rgba(245, 158, 11, 0.1); + color: #d97706; +} + +.status-completed { + background: rgba(16, 185, 129, 0.1); + color: #059669; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .portfolio-grid { + grid-template-columns: 1fr; + } + + .hero-title { + font-size: 2.5rem; + } + + .service-card { + padding: 1.5rem; + } + + .calculator-step { + padding: 1rem; + } +} + +/* Dark Mode Support */ +@media (prefers-color-scheme: dark) { + :root { + --text-dark: #f9fafb; + --text-light: #d1d5db; + --bg-light: #1f2937; + --border-color: #374151; + } + + body { + background-color: #111827; + color: var(--text-dark); + } + + .card-hover, .service-card, .contact-form, .option-card { + background: #1f2937; + border-color: var(--border-color); + } + + .form-control { + background: #374151; + border-color: #4b5563; + color: white; + } + + .form-control:focus { + border-color: var(--primary-color); + } +} + +/* Print Styles */ +@media print { + .navbar, .footer, .contact-form, .mobile-menu { + display: none !important; + } + + body { + font-size: 12pt; + line-height: 1.5; + } + + .portfolio-item, .service-card { + break-inside: avoid; + margin-bottom: 1rem; + } +} + +/* Accessibility */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* Focus styles for better accessibility */ +.form-control:focus, +.btn:focus, +.option-card:focus { + outline: 2px solid var(--primary-color); + outline-offset: 2px; +} + +/* Loading States */ +.btn-loading { + position: relative; + color: transparent; +} + +.btn-loading::after { + content: ''; + position: absolute; + width: 16px; + height: 16px; + top: 50%; + left: 50%; + margin-left: -8px; + margin-top: -8px; + border: 2px solid transparent; + border-top-color: currentColor; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +/* Calculator Styles */ +.calculator-step { + display: none; +} + +.calculator-step.active { + display: block; +} + +.service-option, +.complexity-option, +.timeline-option { + cursor: pointer; + transition: all 0.3s ease; + position: relative; +} + +.service-option:hover, +.complexity-option:hover, +.timeline-option:hover { + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected, +.complexity-option.selected, +.timeline-option.selected { + border-color: #3B82F6 !important; + background-color: #EBF8FF !important; + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected::after, +.complexity-option.selected::after, +.timeline-option.selected::after { + content: '✓'; + position: absolute; + top: 0.5rem; + right: 0.5rem; + width: 24px; + height: 24px; + background: #3B82F6; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: bold; +} + +/* Price Display Animation */ +#final-price { + animation: priceReveal 0.8s ease-in-out; +} + +@keyframes priceReveal { + 0% { + opacity: 0; + transform: scale(0.8); + } + 50% { + transform: scale(1.1); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +/* Calculator Progress Bar */ +.calculator-progress { + width: 100%; + height: 4px; + background: #e5e7eb; + border-radius: 2px; + margin-bottom: 2rem; + overflow: hidden; +} + +.calculator-progress-bar { + height: 100%; + background: linear-gradient(90deg, #3B82F6, #8B5CF6); + border-radius: 2px; + transition: width 0.3s ease; + width: 33.33%; +} + +.calculator-progress-bar.step-2 { + width: 66.66%; +} + +.calculator-progress-bar.step-3 { + width: 100%; +} + +/* Calculator Mobile Improvements */ +@media (max-width: 768px) { + .service-option, + .complexity-option, + .timeline-option { + margin-bottom: 1rem; + } + + #final-price { + font-size: 2.5rem; + } +} \ No newline at end of file diff --git a/.history/public/css/main_20251020045359.css b/.history/public/css/main_20251020045359.css new file mode 100644 index 0000000..d45d96e --- /dev/null +++ b/.history/public/css/main_20251020045359.css @@ -0,0 +1,573 @@ +/* SmartSolTech - Main Styles */ + +/* Tailwind Base (if not loading properly) */ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); + +/* CSS Reset and Base */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + line-height: 1.6; + color: #1f2937; + background-color: #ffffff; +} + +/* Root Variables */ +:root { + --primary-color: #3B82F6; + --secondary-color: #8B5CF6; + --accent-color: #10B981; + --text-dark: #1f2937; + --text-light: #6b7280; + --bg-light: #f9fafb; + --border-color: #e5e7eb; +} + +/* Utility Classes */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 1rem; +} + +.section-padding { + padding: 4rem 0; +} + +/* Navigation */ +.navbar { + background: rgba(255, 255, 255, 0.95); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + transition: all 0.3s ease; +} + +.navbar-brand { + font-size: 1.5rem; + font-weight: bold; + color: #3b82f6; + text-decoration: none; +} + +.navbar-nav { + display: flex; + gap: 2rem; + list-style: none; +} + +.nav-link { + color: #6b7280; + text-decoration: none; + font-weight: 500; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + transition: all 0.3s ease; +} + +.nav-link:hover, +.nav-link.active { + color: #3b82f6; + background-color: #eff6ff; +} + +.mobile-menu { + overflow: hidden; +} + +.mobile-menu.show { + max-height: 500px; +} + +/* Button Hover Effects */ +.btn-primary { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + transition: all 0.3s ease; + transform: translateY(0); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3); +} + +/* Card Hover Effects */ +.card-hover { + transition: all 0.3s ease; + transform: translateY(0); +} + +.card-hover:hover { + transform: translateY(-8px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +/* Portfolio Grid */ +.portfolio-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 2rem; +} + +.portfolio-item { + border-radius: 1rem; + overflow: hidden; + background: white; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); + transition: all 0.3s ease; +} + +.portfolio-item:hover { + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +.portfolio-image { + position: relative; + overflow: hidden; + aspect-ratio: 16/10; +} + +.portfolio-image img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.portfolio-item:hover .portfolio-image img { + transform: scale(1.05); +} + +.portfolio-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(59, 130, 246, 0.8), rgba(139, 92, 246, 0.8)); + opacity: 0; + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.portfolio-item:hover .portfolio-overlay { + opacity: 1; +} + +/* Service Cards */ +.service-card { + background: white; + border-radius: 1rem; + padding: 2rem; + text-align: center; + transition: all 0.3s ease; + border: 2px solid transparent; +} + +.service-card:hover { + border-color: var(--primary-color); + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(59, 130, 246, 0.1); +} + +.service-icon { + width: 80px; + height: 80px; + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1rem; + font-size: 2rem; + color: white; + transition: all 0.3s ease; +} + +.service-card:hover .service-icon { + transform: scale(1.1) rotate(5deg); +} + +/* Contact Form */ +.contact-form { + background: white; + border-radius: 1rem; + padding: 2rem; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); +} + +.form-group { + margin-bottom: 1.5rem; +} + +.form-control { + width: 100%; + padding: 1rem; + border: 2px solid var(--border-color); + border-radius: 0.5rem; + font-size: 1rem; + transition: all 0.3s ease; + background: white; +} + +.form-control:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +/* Calculator Styles */ +.calculator-step { + animation: fadeIn 0.5s ease-in-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +.option-card { + border: 2px solid var(--border-color); + border-radius: 1rem; + padding: 1.5rem; + cursor: pointer; + transition: all 0.3s ease; + background: white; +} + +.option-card:hover { + border-color: var(--primary-color); + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); +} + +.option-card.selected { + border-color: var(--primary-color); + background: rgba(59, 130, 246, 0.05); +} + +/* Progress Bar */ +.progress-bar { + width: 100%; + height: 8px; + background: var(--border-color); + border-radius: 4px; + overflow: hidden; + margin-bottom: 2rem; +} + +.progress-fill { + height: 100%; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + transition: width 0.3s ease; +} + +/* Hero Section Animations */ +.hero-content { + animation: heroFadeIn 1s ease-out; +} + +@keyframes heroFadeIn { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Parallax Effect */ +.parallax { + background-attachment: fixed; + background-position: center; + background-repeat: no-repeat; + background-size: cover; +} + +/* Scroll Animations */ +.fade-in-up { + opacity: 0; + transform: translateY(30px); + transition: all 0.8s ease; +} + +.fade-in-up.animate { + opacity: 1; + transform: translateY(0); +} + +/* Typography */ +.gradient-text { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Status Badges */ +.status-badge { + padding: 0.25rem 0.75rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.status-new { + background: rgba(239, 68, 68, 0.1); + color: #dc2626; +} + +.status-in-progress { + background: rgba(245, 158, 11, 0.1); + color: #d97706; +} + +.status-completed { + background: rgba(16, 185, 129, 0.1); + color: #059669; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .portfolio-grid { + grid-template-columns: 1fr; + } + + .hero-title { + font-size: 2.5rem; + } + + .service-card { + padding: 1.5rem; + } + + .calculator-step { + padding: 1rem; + } +} + +/* Dark Mode Support */ +@media (prefers-color-scheme: dark) { + :root { + --text-dark: #f9fafb; + --text-light: #d1d5db; + --bg-light: #1f2937; + --border-color: #374151; + } + + body { + background-color: #111827; + color: var(--text-dark); + } + + .card-hover, .service-card, .contact-form, .option-card { + background: #1f2937; + border-color: var(--border-color); + } + + .form-control { + background: #374151; + border-color: #4b5563; + color: white; + } + + .form-control:focus { + border-color: var(--primary-color); + } +} + +/* Print Styles */ +@media print { + .navbar, .footer, .contact-form, .mobile-menu { + display: none !important; + } + + body { + font-size: 12pt; + line-height: 1.5; + } + + .portfolio-item, .service-card { + break-inside: avoid; + margin-bottom: 1rem; + } +} + +/* Accessibility */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* Focus styles for better accessibility */ +.form-control:focus, +.btn:focus, +.option-card:focus { + outline: 2px solid var(--primary-color); + outline-offset: 2px; +} + +/* Loading States */ +.btn-loading { + position: relative; + color: transparent; +} + +.btn-loading::after { + content: ''; + position: absolute; + width: 16px; + height: 16px; + top: 50%; + left: 50%; + margin-left: -8px; + margin-top: -8px; + border: 2px solid transparent; + border-top-color: currentColor; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +/* Calculator Styles */ +.calculator-step { + display: none; +} + +.calculator-step.active { + display: block; +} + +.service-option, +.complexity-option, +.timeline-option { + cursor: pointer; + transition: all 0.3s ease; + position: relative; +} + +.service-option:hover, +.complexity-option:hover, +.timeline-option:hover { + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected, +.complexity-option.selected, +.timeline-option.selected { + border-color: #3B82F6 !important; + background-color: #EBF8FF !important; + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected::after, +.complexity-option.selected::after, +.timeline-option.selected::after { + content: '✓'; + position: absolute; + top: 0.5rem; + right: 0.5rem; + width: 24px; + height: 24px; + background: #3B82F6; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: bold; +} + +/* Price Display Animation */ +#final-price { + animation: priceReveal 0.8s ease-in-out; +} + +@keyframes priceReveal { + 0% { + opacity: 0; + transform: scale(0.8); + } + 50% { + transform: scale(1.1); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +/* Calculator Progress Bar */ +.calculator-progress { + width: 100%; + height: 4px; + background: #e5e7eb; + border-radius: 2px; + margin-bottom: 2rem; + overflow: hidden; +} + +.calculator-progress-bar { + height: 100%; + background: linear-gradient(90deg, #3B82F6, #8B5CF6); + border-radius: 2px; + transition: width 0.3s ease; + width: 33.33%; +} + +.calculator-progress-bar.step-2 { + width: 66.66%; +} + +.calculator-progress-bar.step-3 { + width: 100%; +} + +/* Calculator Mobile Improvements */ +@media (max-width: 768px) { + .service-option, + .complexity-option, + .timeline-option { + margin-bottom: 1rem; + } + + #final-price { + font-size: 2.5rem; + } +} \ No newline at end of file diff --git a/.history/public/css/main_20251020225556.css b/.history/public/css/main_20251020225556.css new file mode 100644 index 0000000..8858763 --- /dev/null +++ b/.history/public/css/main_20251020225556.css @@ -0,0 +1,606 @@ +/* SmartSolTech - Main Styles */ + +/* Tailwind Base (if not loading properly) */ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); + +/* CSS Reset and Base */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + line-height: 1.6; + color: #1f2937; + background-color: #ffffff; +} + +/* Root Variables */ +:root { + --primary-color: #3B82F6; + --secondary-color: #8B5CF6; + --accent-color: #10B981; + --text-dark: #1f2937; + --text-light: #6b7280; + --bg-light: #f9fafb; + --border-color: #e5e7eb; +} + +/* Utility Classes */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 1rem; +} + +.section-padding { + padding: 4rem 0; +} + +/* Navigation */ +.navbar { + background: rgba(255, 255, 255, 0.95); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + transition: all 0.3s ease; +} + +.navbar-brand { + font-size: 1.5rem; + font-weight: bold; + color: #3b82f6; + text-decoration: none; +} + +.navbar-nav { + display: flex; + gap: 2rem; + list-style: none; +} + +.nav-link { + color: #6b7280; + text-decoration: none; + font-weight: 500; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + transition: all 0.3s ease; +} + +.nav-link:hover, +.nav-link.active { + color: #3b82f6; + background-color: #eff6ff; +} + +.mobile-menu { + overflow: hidden; +} + +.mobile-menu.show { + max-height: 500px; +} + +/* Button Hover Effects */ +.btn-primary { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + transition: all 0.3s ease; + transform: translateY(0); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3); +} + +/* Card Hover Effects */ +.card-hover { + transition: all 0.3s ease; + transform: translateY(0); +} + +.card-hover:hover { + transform: translateY(-8px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +/* Portfolio Grid */ +.portfolio-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 2rem; +} + +.portfolio-item { + border-radius: 1rem; + overflow: hidden; + background: white; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); + transition: all 0.3s ease; +} + +.portfolio-item:hover { + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +.portfolio-image { + position: relative; + overflow: hidden; + aspect-ratio: 16/10; +} + +.portfolio-image img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.portfolio-item:hover .portfolio-image img { + transform: scale(1.05); +} + +.portfolio-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(59, 130, 246, 0.8), rgba(139, 92, 246, 0.8)); + opacity: 0; + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.portfolio-item:hover .portfolio-overlay { + opacity: 1; +} + +/* Service Cards */ +.service-card { + background: white; + border-radius: 1rem; + padding: 2rem; + text-align: center; + transition: all 0.3s ease; + border: 2px solid transparent; +} + +.service-card:hover { + border-color: var(--primary-color); + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(59, 130, 246, 0.1); +} + +.service-icon { + width: 80px; + height: 80px; + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1rem; + font-size: 2rem; + color: white; + transition: all 0.3s ease; +} + +.service-card:hover .service-icon { + transform: scale(1.1) rotate(5deg); +} + +/* Contact Form */ +.contact-form { + background: white; + border-radius: 1rem; + padding: 2rem; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); +} + +.form-group { + margin-bottom: 1.5rem; +} + +.form-control { + width: 100%; + padding: 1rem; + border: 2px solid var(--border-color); + border-radius: 0.5rem; + font-size: 1rem; + transition: all 0.3s ease; + background: white; +} + +.form-control:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +/* Calculator Styles */ +.calculator-step { + animation: fadeIn 0.5s ease-in-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +.option-card { + border: 2px solid var(--border-color); + border-radius: 1rem; + padding: 1.5rem; + cursor: pointer; + transition: all 0.3s ease; + background: white; +} + +.option-card:hover { + border-color: var(--primary-color); + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); +} + +.option-card.selected { + border-color: var(--primary-color); + background: rgba(59, 130, 246, 0.05); +} + +/* Progress Bar */ +.progress-bar { + width: 100%; + height: 8px; + background: var(--border-color); + border-radius: 4px; + overflow: hidden; + margin-bottom: 2rem; +} + +.progress-fill { + height: 100%; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + transition: width 0.3s ease; +} + +/* Hero Section Animations */ +.hero-content { + animation: heroFadeIn 1s ease-out; +} + +@keyframes heroFadeIn { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Parallax Effect */ +.parallax { + background-attachment: fixed; + background-position: center; + background-repeat: no-repeat; + background-size: cover; +} + +/* Scroll Animations */ +.fade-in-up { + opacity: 0; + transform: translateY(30px); + transition: all 0.8s ease; +} + +.fade-in-up.animate { + opacity: 1; + transform: translateY(0); +} + +/* Typography */ +.gradient-text { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Status Badges */ +.status-badge { + padding: 0.25rem 0.75rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.status-new { + background: rgba(239, 68, 68, 0.1); + color: #dc2626; +} + +.status-in-progress { + background: rgba(245, 158, 11, 0.1); + color: #d97706; +} + +.status-completed { + background: rgba(16, 185, 129, 0.1); + color: #059669; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .portfolio-grid { + grid-template-columns: 1fr; + } + + .hero-title { + font-size: 2.5rem; + } + + .service-card { + padding: 1.5rem; + } + + .calculator-step { + padding: 1rem; + } +} + +/* Dark Mode Support */ +@media (prefers-color-scheme: dark) { + :root { + --text-dark: #f9fafb; + --text-light: #d1d5db; + --bg-light: #1f2937; + --border-color: #374151; + } + + body { + background-color: #111827; + color: var(--text-dark); + } + + .card-hover, .service-card, .contact-form, .option-card { + background: #1f2937; + border-color: var(--border-color); + } + + .form-control { + background: #374151; + border-color: #4b5563; + color: white; + } + + .form-control:focus { + border-color: var(--primary-color); + } +} + +/* Print Styles */ +@media print { + .navbar, .footer, .contact-form, .mobile-menu { + display: none !important; + } + + body { + font-size: 12pt; + line-height: 1.5; + } + + .portfolio-item, .service-card { + break-inside: avoid; + margin-bottom: 1rem; + } +} + +/* Accessibility */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* Focus styles for better accessibility */ +.form-control:focus, +.btn:focus, +.option-card:focus { + outline: 2px solid var(--primary-color); + outline-offset: 2px; +} + +/* Loading States */ +.btn-loading { + position: relative; + color: transparent; +} + +.btn-loading::after { + content: ''; + position: absolute; + width: 16px; + height: 16px; + top: 50%; + left: 50%; + margin-left: -8px; + margin-top: -8px; + border: 2px solid transparent; + border-top-color: currentColor; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +/* Calculator Styles */ +.calculator-step { + display: none; +} + +.calculator-step.active { + display: block; +} + +.service-option, +.complexity-option, +.timeline-option { + cursor: pointer; + transition: all 0.3s ease; + position: relative; +} + +.service-option:hover, +.complexity-option:hover, +.timeline-option:hover { + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected, +.complexity-option.selected, +.timeline-option.selected { + border-color: #3B82F6 !important; + background-color: #EBF8FF !important; + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected::after, +.complexity-option.selected::after, +.timeline-option.selected::after { + content: '✓'; + position: absolute; + top: 0.5rem; + right: 0.5rem; + width: 24px; + height: 24px; + background: #3B82F6; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: bold; +} + +/* Price Display Animation */ +#final-price { + animation: priceReveal 0.8s ease-in-out; +} + +@keyframes priceReveal { + 0% { + opacity: 0; + transform: scale(0.8); + } + 50% { + transform: scale(1.1); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +/* Calculator Progress Bar */ +.calculator-progress { + width: 100%; + height: 4px; + background: #e5e7eb; + border-radius: 2px; + margin-bottom: 2rem; + overflow: hidden; +} + +.calculator-progress-bar { + height: 100%; + background: linear-gradient(90deg, #3B82F6, #8B5CF6); + border-radius: 2px; + transition: width 0.3s ease; + width: 33.33%; +} + +.calculator-progress-bar.step-2 { + width: 66.66%; +} + +.calculator-progress-bar.step-3 { + width: 100%; +} + +/* Calculator Mobile Improvements */ +@media (max-width: 768px) { + .service-option, + .complexity-option, + .timeline-option { + margin-bottom: 1rem; + } + + #final-price { + font-size: 2rem; + } +} + +/* Hero секции - компактные для внутренних страниц */ +.hero-section-compact { + min-height: 40vh !important; + max-height: 50vh !important; + padding: 4rem 0 !important; +} + +.hero-section-compact h1 { + font-size: 3rem !important; + margin-bottom: 1rem !important; +} + +.hero-section-compact p { + font-size: 1.125rem !important; + opacity: 0.9 !important; +} + +@media (max-width: 768px) { + .hero-section-compact { + min-height: 30vh !important; + padding: 3rem 0 !important; + } + + .hero-section-compact h1 { + font-size: 2.5rem !important; + } +} + +/* Полноэкранный Hero только для главной */ +.hero-section { + min-height: 100vh !important; +} \ No newline at end of file diff --git a/.history/public/css/main_20251020225722.css b/.history/public/css/main_20251020225722.css new file mode 100644 index 0000000..8858763 --- /dev/null +++ b/.history/public/css/main_20251020225722.css @@ -0,0 +1,606 @@ +/* SmartSolTech - Main Styles */ + +/* Tailwind Base (if not loading properly) */ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); + +/* CSS Reset and Base */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + line-height: 1.6; + color: #1f2937; + background-color: #ffffff; +} + +/* Root Variables */ +:root { + --primary-color: #3B82F6; + --secondary-color: #8B5CF6; + --accent-color: #10B981; + --text-dark: #1f2937; + --text-light: #6b7280; + --bg-light: #f9fafb; + --border-color: #e5e7eb; +} + +/* Utility Classes */ +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 1rem; +} + +.section-padding { + padding: 4rem 0; +} + +/* Navigation */ +.navbar { + background: rgba(255, 255, 255, 0.95); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + transition: all 0.3s ease; +} + +.navbar-brand { + font-size: 1.5rem; + font-weight: bold; + color: #3b82f6; + text-decoration: none; +} + +.navbar-nav { + display: flex; + gap: 2rem; + list-style: none; +} + +.nav-link { + color: #6b7280; + text-decoration: none; + font-weight: 500; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + transition: all 0.3s ease; +} + +.nav-link:hover, +.nav-link.active { + color: #3b82f6; + background-color: #eff6ff; +} + +.mobile-menu { + overflow: hidden; +} + +.mobile-menu.show { + max-height: 500px; +} + +/* Button Hover Effects */ +.btn-primary { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + transition: all 0.3s ease; + transform: translateY(0); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3); +} + +/* Card Hover Effects */ +.card-hover { + transition: all 0.3s ease; + transform: translateY(0); +} + +.card-hover:hover { + transform: translateY(-8px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +/* Portfolio Grid */ +.portfolio-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); + gap: 2rem; +} + +.portfolio-item { + border-radius: 1rem; + overflow: hidden; + background: white; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05); + transition: all 0.3s ease; +} + +.portfolio-item:hover { + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +.portfolio-image { + position: relative; + overflow: hidden; + aspect-ratio: 16/10; +} + +.portfolio-image img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.portfolio-item:hover .portfolio-image img { + transform: scale(1.05); +} + +.portfolio-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, rgba(59, 130, 246, 0.8), rgba(139, 92, 246, 0.8)); + opacity: 0; + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; +} + +.portfolio-item:hover .portfolio-overlay { + opacity: 1; +} + +/* Service Cards */ +.service-card { + background: white; + border-radius: 1rem; + padding: 2rem; + text-align: center; + transition: all 0.3s ease; + border: 2px solid transparent; +} + +.service-card:hover { + border-color: var(--primary-color); + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(59, 130, 246, 0.1); +} + +.service-icon { + width: 80px; + height: 80px; + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin: 0 auto 1rem; + font-size: 2rem; + color: white; + transition: all 0.3s ease; +} + +.service-card:hover .service-icon { + transform: scale(1.1) rotate(5deg); +} + +/* Contact Form */ +.contact-form { + background: white; + border-radius: 1rem; + padding: 2rem; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); +} + +.form-group { + margin-bottom: 1.5rem; +} + +.form-control { + width: 100%; + padding: 1rem; + border: 2px solid var(--border-color); + border-radius: 0.5rem; + font-size: 1rem; + transition: all 0.3s ease; + background: white; +} + +.form-control:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); +} + +/* Calculator Styles */ +.calculator-step { + animation: fadeIn 0.5s ease-in-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +.option-card { + border: 2px solid var(--border-color); + border-radius: 1rem; + padding: 1.5rem; + cursor: pointer; + transition: all 0.3s ease; + background: white; +} + +.option-card:hover { + border-color: var(--primary-color); + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); +} + +.option-card.selected { + border-color: var(--primary-color); + background: rgba(59, 130, 246, 0.05); +} + +/* Progress Bar */ +.progress-bar { + width: 100%; + height: 8px; + background: var(--border-color); + border-radius: 4px; + overflow: hidden; + margin-bottom: 2rem; +} + +.progress-fill { + height: 100%; + background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)); + transition: width 0.3s ease; +} + +/* Hero Section Animations */ +.hero-content { + animation: heroFadeIn 1s ease-out; +} + +@keyframes heroFadeIn { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Parallax Effect */ +.parallax { + background-attachment: fixed; + background-position: center; + background-repeat: no-repeat; + background-size: cover; +} + +/* Scroll Animations */ +.fade-in-up { + opacity: 0; + transform: translateY(30px); + transition: all 0.8s ease; +} + +.fade-in-up.animate { + opacity: 1; + transform: translateY(0); +} + +/* Typography */ +.gradient-text { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +/* Status Badges */ +.status-badge { + padding: 0.25rem 0.75rem; + border-radius: 9999px; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.status-new { + background: rgba(239, 68, 68, 0.1); + color: #dc2626; +} + +.status-in-progress { + background: rgba(245, 158, 11, 0.1); + color: #d97706; +} + +.status-completed { + background: rgba(16, 185, 129, 0.1); + color: #059669; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .portfolio-grid { + grid-template-columns: 1fr; + } + + .hero-title { + font-size: 2.5rem; + } + + .service-card { + padding: 1.5rem; + } + + .calculator-step { + padding: 1rem; + } +} + +/* Dark Mode Support */ +@media (prefers-color-scheme: dark) { + :root { + --text-dark: #f9fafb; + --text-light: #d1d5db; + --bg-light: #1f2937; + --border-color: #374151; + } + + body { + background-color: #111827; + color: var(--text-dark); + } + + .card-hover, .service-card, .contact-form, .option-card { + background: #1f2937; + border-color: var(--border-color); + } + + .form-control { + background: #374151; + border-color: #4b5563; + color: white; + } + + .form-control:focus { + border-color: var(--primary-color); + } +} + +/* Print Styles */ +@media print { + .navbar, .footer, .contact-form, .mobile-menu { + display: none !important; + } + + body { + font-size: 12pt; + line-height: 1.5; + } + + .portfolio-item, .service-card { + break-inside: avoid; + margin-bottom: 1rem; + } +} + +/* Accessibility */ +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + +/* Focus styles for better accessibility */ +.form-control:focus, +.btn:focus, +.option-card:focus { + outline: 2px solid var(--primary-color); + outline-offset: 2px; +} + +/* Loading States */ +.btn-loading { + position: relative; + color: transparent; +} + +.btn-loading::after { + content: ''; + position: absolute; + width: 16px; + height: 16px; + top: 50%; + left: 50%; + margin-left: -8px; + margin-top: -8px; + border: 2px solid transparent; + border-top-color: currentColor; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +/* Calculator Styles */ +.calculator-step { + display: none; +} + +.calculator-step.active { + display: block; +} + +.service-option, +.complexity-option, +.timeline-option { + cursor: pointer; + transition: all 0.3s ease; + position: relative; +} + +.service-option:hover, +.complexity-option:hover, +.timeline-option:hover { + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected, +.complexity-option.selected, +.timeline-option.selected { + border-color: #3B82F6 !important; + background-color: #EBF8FF !important; + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.15); +} + +.service-option.selected::after, +.complexity-option.selected::after, +.timeline-option.selected::after { + content: '✓'; + position: absolute; + top: 0.5rem; + right: 0.5rem; + width: 24px; + height: 24px; + background: #3B82F6; + color: white; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 14px; + font-weight: bold; +} + +/* Price Display Animation */ +#final-price { + animation: priceReveal 0.8s ease-in-out; +} + +@keyframes priceReveal { + 0% { + opacity: 0; + transform: scale(0.8); + } + 50% { + transform: scale(1.1); + } + 100% { + opacity: 1; + transform: scale(1); + } +} + +/* Calculator Progress Bar */ +.calculator-progress { + width: 100%; + height: 4px; + background: #e5e7eb; + border-radius: 2px; + margin-bottom: 2rem; + overflow: hidden; +} + +.calculator-progress-bar { + height: 100%; + background: linear-gradient(90deg, #3B82F6, #8B5CF6); + border-radius: 2px; + transition: width 0.3s ease; + width: 33.33%; +} + +.calculator-progress-bar.step-2 { + width: 66.66%; +} + +.calculator-progress-bar.step-3 { + width: 100%; +} + +/* Calculator Mobile Improvements */ +@media (max-width: 768px) { + .service-option, + .complexity-option, + .timeline-option { + margin-bottom: 1rem; + } + + #final-price { + font-size: 2rem; + } +} + +/* Hero секции - компактные для внутренних страниц */ +.hero-section-compact { + min-height: 40vh !important; + max-height: 50vh !important; + padding: 4rem 0 !important; +} + +.hero-section-compact h1 { + font-size: 3rem !important; + margin-bottom: 1rem !important; +} + +.hero-section-compact p { + font-size: 1.125rem !important; + opacity: 0.9 !important; +} + +@media (max-width: 768px) { + .hero-section-compact { + min-height: 30vh !important; + padding: 3rem 0 !important; + } + + .hero-section-compact h1 { + font-size: 2.5rem !important; + } +} + +/* Полноэкранный Hero только для главной */ +.hero-section { + min-height: 100vh !important; +} \ No newline at end of file diff --git a/.history/public/images/icon-144x144_20251020042420.png b/.history/public/images/icon-144x144_20251020042420.png new file mode 100644 index 0000000..6edd642 --- /dev/null +++ b/.history/public/images/icon-144x144_20251020042420.png @@ -0,0 +1,4 @@ + + + ST + \ No newline at end of file diff --git a/.history/public/images/icon-144x144_20251020042429.png b/.history/public/images/icon-144x144_20251020042429.png new file mode 100644 index 0000000..6edd642 --- /dev/null +++ b/.history/public/images/icon-144x144_20251020042429.png @@ -0,0 +1,4 @@ + + + ST + \ No newline at end of file diff --git a/.history/public/images/icons/icon-192x192_20251020041929.png b/.history/public/images/icons/icon-192x192_20251020041929.png new file mode 100644 index 0000000..6d2ce2b --- /dev/null +++ b/.history/public/images/icons/icon-192x192_20251020041929.png @@ -0,0 +1,4 @@ + + + ST + \ No newline at end of file diff --git a/.history/public/images/icons/icon-192x192_20251020041934.png b/.history/public/images/icons/icon-192x192_20251020041934.png new file mode 100644 index 0000000..6d2ce2b --- /dev/null +++ b/.history/public/images/icons/icon-192x192_20251020041934.png @@ -0,0 +1,4 @@ + + + ST + \ No newline at end of file diff --git a/.history/public/images/logo_20251020041922.png b/.history/public/images/logo_20251020041922.png new file mode 100644 index 0000000..4054628 --- /dev/null +++ b/.history/public/images/logo_20251020041922.png @@ -0,0 +1,4 @@ + + + ST + \ No newline at end of file diff --git a/.history/public/images/logo_20251020041933.png b/.history/public/images/logo_20251020041933.png new file mode 100644 index 0000000..4054628 --- /dev/null +++ b/.history/public/images/logo_20251020041933.png @@ -0,0 +1,4 @@ + + + ST + \ No newline at end of file diff --git a/.history/public/images/portfolio/corporate-1_20251020041915.jpg b/.history/public/images/portfolio/corporate-1_20251020041915.jpg new file mode 100644 index 0000000..5649601 --- /dev/null +++ b/.history/public/images/portfolio/corporate-1_20251020041915.jpg @@ -0,0 +1,4 @@ + + + Corporate Website + \ No newline at end of file diff --git a/.history/public/images/portfolio/corporate-1_20251020041933.jpg b/.history/public/images/portfolio/corporate-1_20251020041933.jpg new file mode 100644 index 0000000..5649601 --- /dev/null +++ b/.history/public/images/portfolio/corporate-1_20251020041933.jpg @@ -0,0 +1,4 @@ + + + Corporate Website + \ No newline at end of file diff --git a/.history/public/images/portfolio/ecommerce-1_20251020041900.jpg b/.history/public/images/portfolio/ecommerce-1_20251020041900.jpg new file mode 100644 index 0000000..559feb7 --- /dev/null +++ b/.history/public/images/portfolio/ecommerce-1_20251020041900.jpg @@ -0,0 +1,4 @@ + + + E-commerce Project + \ No newline at end of file diff --git a/.history/public/images/portfolio/ecommerce-1_20251020041933.jpg b/.history/public/images/portfolio/ecommerce-1_20251020041933.jpg new file mode 100644 index 0000000..559feb7 --- /dev/null +++ b/.history/public/images/portfolio/ecommerce-1_20251020041933.jpg @@ -0,0 +1,4 @@ + + + E-commerce Project + \ No newline at end of file diff --git a/.history/public/images/portfolio/fitness-1_20251020041907.jpg b/.history/public/images/portfolio/fitness-1_20251020041907.jpg new file mode 100644 index 0000000..70126d4 --- /dev/null +++ b/.history/public/images/portfolio/fitness-1_20251020041907.jpg @@ -0,0 +1,4 @@ + + + Fitness App Project + \ No newline at end of file diff --git a/.history/public/images/portfolio/fitness-1_20251020041933.jpg b/.history/public/images/portfolio/fitness-1_20251020041933.jpg new file mode 100644 index 0000000..70126d4 --- /dev/null +++ b/.history/public/images/portfolio/fitness-1_20251020041933.jpg @@ -0,0 +1,4 @@ + + + Fitness App Project + \ No newline at end of file diff --git a/.history/public/sw_20251020042616.js b/.history/public/sw_20251020042616.js new file mode 100644 index 0000000..adeb970 --- /dev/null +++ b/.history/public/sw_20251020042616.js @@ -0,0 +1,399 @@ +// Service Worker for SmartSolTech PWA +const CACHE_NAME = 'smartsoltech-v1.0.0'; +const STATIC_CACHE_NAME = 'smartsoltech-static-v1.0.0'; +const DYNAMIC_CACHE_NAME = 'smartsoltech-dynamic-v1.0.0'; + +// Files to cache immediately +const STATIC_FILES = [ + '/', + '/css/main.css', + '/css/fixes.css', + '/css/dark-theme.css', + '/js/main.js', + '/images/logo.png', + '/images/icon-192x192.png', + '/images/icon-144x144.png', + '/manifest.json', + 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap', + 'https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css', + 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css', + 'https://unpkg.com/aos@2.3.1/dist/aos.css', + 'https://unpkg.com/aos@2.3.1/dist/aos.js' +]; + +// Routes to cache dynamically +const DYNAMIC_ROUTES = [ + '/about', + '/services', + '/portfolio', + '/calculator', + '/contact' +]; + +// API endpoints to cache +const API_CACHE_PATTERNS = [ + /^\/api\/portfolio/, + /^\/api\/services/, + /^\/api\/calculator\/services/ +]; + +// Install event - cache static files +self.addEventListener('install', event => { + console.log('Service Worker: Installing...'); + + event.waitUntil( + caches.open(STATIC_CACHE_NAME) + .then(cache => { + console.log('Service Worker: Caching static files'); + return cache.addAll(STATIC_FILES); + }) + .then(() => { + console.log('Service Worker: Static files cached'); + return self.skipWaiting(); + }) + .catch(error => { + console.error('Service Worker: Error caching static files', error); + }) + ); +}); + +// Activate event - clean up old caches +self.addEventListener('activate', event => { + console.log('Service Worker: Activating...'); + + event.waitUntil( + caches.keys() + .then(cacheNames => { + return Promise.all( + cacheNames.map(cacheName => { + if (cacheName !== STATIC_CACHE_NAME && + cacheName !== DYNAMIC_CACHE_NAME) { + console.log('Service Worker: Deleting old cache', cacheName); + return caches.delete(cacheName); + } + }) + ); + }) + .then(() => { + console.log('Service Worker: Activated'); + return self.clients.claim(); + }) + ); +}); + +// Fetch event - serve cached files or fetch from network +self.addEventListener('fetch', event => { + const request = event.request; + const url = new URL(request.url); + + // Skip non-GET requests + if (request.method !== 'GET') { + return; + } + + // Skip Chrome extension requests + if (url.protocol === 'chrome-extension:') { + return; + } + + // Handle different types of requests + if (isStaticFile(request.url)) { + event.respondWith(cacheFirst(request)); + } else if (isAPIRequest(request.url)) { + event.respondWith(networkFirst(request)); + } else if (isDynamicRoute(request.url)) { + event.respondWith(staleWhileRevalidate(request)); + } else { + event.respondWith(networkFirst(request)); + } +}); + +// Cache strategies +async function cacheFirst(request) { + try { + const cachedResponse = await caches.match(request); + if (cachedResponse) { + return cachedResponse; + } + + const networkResponse = await fetch(request); + const cache = await caches.open(STATIC_CACHE_NAME); + cache.put(request, networkResponse.clone()); + return networkResponse; + } catch (error) { + console.error('Cache first strategy failed:', error); + return new Response('Offline', { status: 503 }); + } +} + +async function networkFirst(request) { + try { + const networkResponse = await fetch(request); + + // Cache successful responses + if (networkResponse.ok) { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + cache.put(request, networkResponse.clone()); + } + + return networkResponse; + } catch (error) { + console.log('Network first: Falling back to cache for', request.url); + + const cachedResponse = await caches.match(request); + if (cachedResponse) { + return cachedResponse; + } + + // Return offline page for navigation requests + if (request.mode === 'navigate') { + return caches.match('/offline.html') || new Response('Offline', { + status: 503, + headers: { 'Content-Type': 'text/html' } + }); + } + + return new Response('Network Error', { status: 503 }); + } +} + +async function staleWhileRevalidate(request) { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + const cachedResponse = await cache.match(request); + + const fetchPromise = fetch(request).then(networkResponse => { + if (networkResponse.ok) { + cache.put(request, networkResponse.clone()); + } + return networkResponse; + }); + + return cachedResponse || fetchPromise; +} + +// Helper functions +function isStaticFile(url) { + return url.includes('/css/') || + url.includes('/js/') || + url.includes('/images/') || + url.includes('/fonts/') || + url.includes('googleapis.com') || + url.includes('cdnjs.cloudflare.com'); +} + +function isAPIRequest(url) { + return url.includes('/api/') || + API_CACHE_PATTERNS.some(pattern => pattern.test(url)); +} + +function isDynamicRoute(url) { + const pathname = new URL(url).pathname; + return DYNAMIC_ROUTES.includes(pathname) || + pathname.startsWith('/portfolio/') || + pathname.startsWith('/services/'); +} + +// Background sync for form submissions +self.addEventListener('sync', event => { + console.log('Service Worker: Background sync triggered', event.tag); + + if (event.tag === 'contact-form-sync') { + event.waitUntil(syncContactForms()); + } +}); + +async function syncContactForms() { + try { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + const requests = await cache.keys(); + + const contactRequests = requests.filter(request => + request.url.includes('/api/contact/submit') + ); + + for (const request of contactRequests) { + try { + await fetch(request); + await cache.delete(request); + console.log('Contact form synced successfully'); + } catch (error) { + console.error('Failed to sync contact form:', error); + } + } + } catch (error) { + console.error('Background sync failed:', error); + } +} + +// Push notification handling +self.addEventListener('push', event => { + console.log('Service Worker: Push received', event); + + let data = {}; + if (event.data) { + data = event.data.json(); + } + + const title = data.title || 'SmartSolTech'; + const options = { + body: data.body || 'You have a new notification', + icon: '/images/icon-192x192.png', + badge: '/images/icon-72x72.png', + tag: data.tag || 'default', + data: data.url || '/', + actions: [ + { + action: 'open', + title: '열기', + icon: '/images/icon-open.png' + }, + { + action: 'close', + title: '닫기', + icon: '/images/icon-close.png' + } + ], + requireInteraction: data.requireInteraction || false, + silent: data.silent || false, + vibrate: data.vibrate || [200, 100, 200] + }; + + event.waitUntil( + self.registration.showNotification(title, options) + ); +}); + +// Notification click handling +self.addEventListener('notificationclick', event => { + console.log('Service Worker: Notification clicked', event); + + event.notification.close(); + + if (event.action === 'close') { + return; + } + + const url = event.notification.data || '/'; + + event.waitUntil( + clients.matchAll({ type: 'window' }).then(clientList => { + // Check if window is already open + for (const client of clientList) { + if (client.url === url && 'focus' in client) { + return client.focus(); + } + } + + // Open new window + if (clients.openWindow) { + return clients.openWindow(url); + } + }) + ); +}); + +// Handle messages from main thread +self.addEventListener('message', event => { + console.log('Service Worker: Message received', event.data); + + if (event.data && event.data.type === 'SKIP_WAITING') { + self.skipWaiting(); + } + + if (event.data && event.data.type === 'CACHE_URLS') { + cacheUrls(event.data.urls); + } +}); + +async function cacheUrls(urls) { + try { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + await cache.addAll(urls); + console.log('URLs cached successfully:', urls); + } catch (error) { + console.error('Failed to cache URLs:', error); + } +} + +// Periodic background sync (if supported) +self.addEventListener('periodicsync', event => { + console.log('Service Worker: Periodic sync triggered', event.tag); + + if (event.tag === 'content-sync') { + event.waitUntil(syncContent()); + } +}); + +async function syncContent() { + try { + // Fetch fresh portfolio and services data + const portfolioResponse = await fetch('/api/portfolio?featured=true'); + const servicesResponse = await fetch('/api/services?featured=true'); + + if (portfolioResponse.ok && servicesResponse.ok) { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + cache.put('/api/portfolio?featured=true', portfolioResponse.clone()); + cache.put('/api/services?featured=true', servicesResponse.clone()); + console.log('Content synced successfully'); + } + } catch (error) { + console.error('Content sync failed:', error); + } +} + +// Cache management utilities +async function cleanupCaches() { + const cacheNames = await caches.keys(); + const currentCaches = [STATIC_CACHE_NAME, DYNAMIC_CACHE_NAME]; + + return Promise.all( + cacheNames.map(cacheName => { + if (!currentCaches.includes(cacheName)) { + console.log('Deleting old cache:', cacheName); + return caches.delete(cacheName); + } + }) + ); +} + +// Limit cache size +async function limitCacheSize(cacheName, maxItems) { + const cache = await caches.open(cacheName); + const keys = await cache.keys(); + + if (keys.length > maxItems) { + const keysToDelete = keys.slice(0, keys.length - maxItems); + return Promise.all(keysToDelete.map(key => cache.delete(key))); + } +} + +// Performance monitoring +self.addEventListener('fetch', event => { + if (event.request.url.includes('/api/')) { + const start = performance.now(); + + event.respondWith( + fetch(event.request).then(response => { + const duration = performance.now() - start; + + // Log slow API requests + if (duration > 2000) { + console.warn('Slow API request:', event.request.url, duration + 'ms'); + } + + return response; + }) + ); + } +}); + +// Error tracking +self.addEventListener('error', event => { + console.error('Service Worker error:', event.error); + // Could send to analytics service +}); + +self.addEventListener('unhandledrejection', event => { + console.error('Service Worker unhandled rejection:', event.reason); + // Could send to analytics service +}); \ No newline at end of file diff --git a/.history/public/sw_20251020042624.js b/.history/public/sw_20251020042624.js new file mode 100644 index 0000000..9c6821c --- /dev/null +++ b/.history/public/sw_20251020042624.js @@ -0,0 +1,399 @@ +// Service Worker for SmartSolTech PWA +const CACHE_NAME = 'smartsoltech-v1.0.1'; +const STATIC_CACHE_NAME = 'smartsoltech-static-v1.0.1'; +const DYNAMIC_CACHE_NAME = 'smartsoltech-dynamic-v1.0.1'; + +// Files to cache immediately +const STATIC_FILES = [ + '/', + '/css/main.css', + '/css/fixes.css', + '/css/dark-theme.css', + '/js/main.js', + '/images/logo.png', + '/images/icon-192x192.png', + '/images/icon-144x144.png', + '/manifest.json', + 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap', + 'https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css', + 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css', + 'https://unpkg.com/aos@2.3.1/dist/aos.css', + 'https://unpkg.com/aos@2.3.1/dist/aos.js' +]; + +// Routes to cache dynamically +const DYNAMIC_ROUTES = [ + '/about', + '/services', + '/portfolio', + '/calculator', + '/contact' +]; + +// API endpoints to cache +const API_CACHE_PATTERNS = [ + /^\/api\/portfolio/, + /^\/api\/services/, + /^\/api\/calculator\/services/ +]; + +// Install event - cache static files +self.addEventListener('install', event => { + console.log('Service Worker: Installing...'); + + event.waitUntil( + caches.open(STATIC_CACHE_NAME) + .then(cache => { + console.log('Service Worker: Caching static files'); + return cache.addAll(STATIC_FILES); + }) + .then(() => { + console.log('Service Worker: Static files cached'); + return self.skipWaiting(); + }) + .catch(error => { + console.error('Service Worker: Error caching static files', error); + }) + ); +}); + +// Activate event - clean up old caches +self.addEventListener('activate', event => { + console.log('Service Worker: Activating...'); + + event.waitUntil( + caches.keys() + .then(cacheNames => { + return Promise.all( + cacheNames.map(cacheName => { + if (cacheName !== STATIC_CACHE_NAME && + cacheName !== DYNAMIC_CACHE_NAME) { + console.log('Service Worker: Deleting old cache', cacheName); + return caches.delete(cacheName); + } + }) + ); + }) + .then(() => { + console.log('Service Worker: Activated'); + return self.clients.claim(); + }) + ); +}); + +// Fetch event - serve cached files or fetch from network +self.addEventListener('fetch', event => { + const request = event.request; + const url = new URL(request.url); + + // Skip non-GET requests + if (request.method !== 'GET') { + return; + } + + // Skip Chrome extension requests + if (url.protocol === 'chrome-extension:') { + return; + } + + // Handle different types of requests + if (isStaticFile(request.url)) { + event.respondWith(cacheFirst(request)); + } else if (isAPIRequest(request.url)) { + event.respondWith(networkFirst(request)); + } else if (isDynamicRoute(request.url)) { + event.respondWith(staleWhileRevalidate(request)); + } else { + event.respondWith(networkFirst(request)); + } +}); + +// Cache strategies +async function cacheFirst(request) { + try { + const cachedResponse = await caches.match(request); + if (cachedResponse) { + return cachedResponse; + } + + const networkResponse = await fetch(request); + const cache = await caches.open(STATIC_CACHE_NAME); + cache.put(request, networkResponse.clone()); + return networkResponse; + } catch (error) { + console.error('Cache first strategy failed:', error); + return new Response('Offline', { status: 503 }); + } +} + +async function networkFirst(request) { + try { + const networkResponse = await fetch(request); + + // Cache successful responses + if (networkResponse.ok) { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + cache.put(request, networkResponse.clone()); + } + + return networkResponse; + } catch (error) { + console.log('Network first: Falling back to cache for', request.url); + + const cachedResponse = await caches.match(request); + if (cachedResponse) { + return cachedResponse; + } + + // Return offline page for navigation requests + if (request.mode === 'navigate') { + return caches.match('/offline.html') || new Response('Offline', { + status: 503, + headers: { 'Content-Type': 'text/html' } + }); + } + + return new Response('Network Error', { status: 503 }); + } +} + +async function staleWhileRevalidate(request) { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + const cachedResponse = await cache.match(request); + + const fetchPromise = fetch(request).then(networkResponse => { + if (networkResponse.ok) { + cache.put(request, networkResponse.clone()); + } + return networkResponse; + }); + + return cachedResponse || fetchPromise; +} + +// Helper functions +function isStaticFile(url) { + return url.includes('/css/') || + url.includes('/js/') || + url.includes('/images/') || + url.includes('/fonts/') || + url.includes('googleapis.com') || + url.includes('cdnjs.cloudflare.com'); +} + +function isAPIRequest(url) { + return url.includes('/api/') || + API_CACHE_PATTERNS.some(pattern => pattern.test(url)); +} + +function isDynamicRoute(url) { + const pathname = new URL(url).pathname; + return DYNAMIC_ROUTES.includes(pathname) || + pathname.startsWith('/portfolio/') || + pathname.startsWith('/services/'); +} + +// Background sync for form submissions +self.addEventListener('sync', event => { + console.log('Service Worker: Background sync triggered', event.tag); + + if (event.tag === 'contact-form-sync') { + event.waitUntil(syncContactForms()); + } +}); + +async function syncContactForms() { + try { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + const requests = await cache.keys(); + + const contactRequests = requests.filter(request => + request.url.includes('/api/contact/submit') + ); + + for (const request of contactRequests) { + try { + await fetch(request); + await cache.delete(request); + console.log('Contact form synced successfully'); + } catch (error) { + console.error('Failed to sync contact form:', error); + } + } + } catch (error) { + console.error('Background sync failed:', error); + } +} + +// Push notification handling +self.addEventListener('push', event => { + console.log('Service Worker: Push received', event); + + let data = {}; + if (event.data) { + data = event.data.json(); + } + + const title = data.title || 'SmartSolTech'; + const options = { + body: data.body || 'You have a new notification', + icon: '/images/icon-192x192.png', + badge: '/images/icon-72x72.png', + tag: data.tag || 'default', + data: data.url || '/', + actions: [ + { + action: 'open', + title: '열기', + icon: '/images/icon-open.png' + }, + { + action: 'close', + title: '닫기', + icon: '/images/icon-close.png' + } + ], + requireInteraction: data.requireInteraction || false, + silent: data.silent || false, + vibrate: data.vibrate || [200, 100, 200] + }; + + event.waitUntil( + self.registration.showNotification(title, options) + ); +}); + +// Notification click handling +self.addEventListener('notificationclick', event => { + console.log('Service Worker: Notification clicked', event); + + event.notification.close(); + + if (event.action === 'close') { + return; + } + + const url = event.notification.data || '/'; + + event.waitUntil( + clients.matchAll({ type: 'window' }).then(clientList => { + // Check if window is already open + for (const client of clientList) { + if (client.url === url && 'focus' in client) { + return client.focus(); + } + } + + // Open new window + if (clients.openWindow) { + return clients.openWindow(url); + } + }) + ); +}); + +// Handle messages from main thread +self.addEventListener('message', event => { + console.log('Service Worker: Message received', event.data); + + if (event.data && event.data.type === 'SKIP_WAITING') { + self.skipWaiting(); + } + + if (event.data && event.data.type === 'CACHE_URLS') { + cacheUrls(event.data.urls); + } +}); + +async function cacheUrls(urls) { + try { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + await cache.addAll(urls); + console.log('URLs cached successfully:', urls); + } catch (error) { + console.error('Failed to cache URLs:', error); + } +} + +// Periodic background sync (if supported) +self.addEventListener('periodicsync', event => { + console.log('Service Worker: Periodic sync triggered', event.tag); + + if (event.tag === 'content-sync') { + event.waitUntil(syncContent()); + } +}); + +async function syncContent() { + try { + // Fetch fresh portfolio and services data + const portfolioResponse = await fetch('/api/portfolio?featured=true'); + const servicesResponse = await fetch('/api/services?featured=true'); + + if (portfolioResponse.ok && servicesResponse.ok) { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + cache.put('/api/portfolio?featured=true', portfolioResponse.clone()); + cache.put('/api/services?featured=true', servicesResponse.clone()); + console.log('Content synced successfully'); + } + } catch (error) { + console.error('Content sync failed:', error); + } +} + +// Cache management utilities +async function cleanupCaches() { + const cacheNames = await caches.keys(); + const currentCaches = [STATIC_CACHE_NAME, DYNAMIC_CACHE_NAME]; + + return Promise.all( + cacheNames.map(cacheName => { + if (!currentCaches.includes(cacheName)) { + console.log('Deleting old cache:', cacheName); + return caches.delete(cacheName); + } + }) + ); +} + +// Limit cache size +async function limitCacheSize(cacheName, maxItems) { + const cache = await caches.open(cacheName); + const keys = await cache.keys(); + + if (keys.length > maxItems) { + const keysToDelete = keys.slice(0, keys.length - maxItems); + return Promise.all(keysToDelete.map(key => cache.delete(key))); + } +} + +// Performance monitoring +self.addEventListener('fetch', event => { + if (event.request.url.includes('/api/')) { + const start = performance.now(); + + event.respondWith( + fetch(event.request).then(response => { + const duration = performance.now() - start; + + // Log slow API requests + if (duration > 2000) { + console.warn('Slow API request:', event.request.url, duration + 'ms'); + } + + return response; + }) + ); + } +}); + +// Error tracking +self.addEventListener('error', event => { + console.error('Service Worker error:', event.error); + // Could send to analytics service +}); + +self.addEventListener('unhandledrejection', event => { + console.error('Service Worker unhandled rejection:', event.reason); + // Could send to analytics service +}); \ No newline at end of file diff --git a/.history/public/sw_20251020042633.js b/.history/public/sw_20251020042633.js new file mode 100644 index 0000000..9c6821c --- /dev/null +++ b/.history/public/sw_20251020042633.js @@ -0,0 +1,399 @@ +// Service Worker for SmartSolTech PWA +const CACHE_NAME = 'smartsoltech-v1.0.1'; +const STATIC_CACHE_NAME = 'smartsoltech-static-v1.0.1'; +const DYNAMIC_CACHE_NAME = 'smartsoltech-dynamic-v1.0.1'; + +// Files to cache immediately +const STATIC_FILES = [ + '/', + '/css/main.css', + '/css/fixes.css', + '/css/dark-theme.css', + '/js/main.js', + '/images/logo.png', + '/images/icon-192x192.png', + '/images/icon-144x144.png', + '/manifest.json', + 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap', + 'https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css', + 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css', + 'https://unpkg.com/aos@2.3.1/dist/aos.css', + 'https://unpkg.com/aos@2.3.1/dist/aos.js' +]; + +// Routes to cache dynamically +const DYNAMIC_ROUTES = [ + '/about', + '/services', + '/portfolio', + '/calculator', + '/contact' +]; + +// API endpoints to cache +const API_CACHE_PATTERNS = [ + /^\/api\/portfolio/, + /^\/api\/services/, + /^\/api\/calculator\/services/ +]; + +// Install event - cache static files +self.addEventListener('install', event => { + console.log('Service Worker: Installing...'); + + event.waitUntil( + caches.open(STATIC_CACHE_NAME) + .then(cache => { + console.log('Service Worker: Caching static files'); + return cache.addAll(STATIC_FILES); + }) + .then(() => { + console.log('Service Worker: Static files cached'); + return self.skipWaiting(); + }) + .catch(error => { + console.error('Service Worker: Error caching static files', error); + }) + ); +}); + +// Activate event - clean up old caches +self.addEventListener('activate', event => { + console.log('Service Worker: Activating...'); + + event.waitUntil( + caches.keys() + .then(cacheNames => { + return Promise.all( + cacheNames.map(cacheName => { + if (cacheName !== STATIC_CACHE_NAME && + cacheName !== DYNAMIC_CACHE_NAME) { + console.log('Service Worker: Deleting old cache', cacheName); + return caches.delete(cacheName); + } + }) + ); + }) + .then(() => { + console.log('Service Worker: Activated'); + return self.clients.claim(); + }) + ); +}); + +// Fetch event - serve cached files or fetch from network +self.addEventListener('fetch', event => { + const request = event.request; + const url = new URL(request.url); + + // Skip non-GET requests + if (request.method !== 'GET') { + return; + } + + // Skip Chrome extension requests + if (url.protocol === 'chrome-extension:') { + return; + } + + // Handle different types of requests + if (isStaticFile(request.url)) { + event.respondWith(cacheFirst(request)); + } else if (isAPIRequest(request.url)) { + event.respondWith(networkFirst(request)); + } else if (isDynamicRoute(request.url)) { + event.respondWith(staleWhileRevalidate(request)); + } else { + event.respondWith(networkFirst(request)); + } +}); + +// Cache strategies +async function cacheFirst(request) { + try { + const cachedResponse = await caches.match(request); + if (cachedResponse) { + return cachedResponse; + } + + const networkResponse = await fetch(request); + const cache = await caches.open(STATIC_CACHE_NAME); + cache.put(request, networkResponse.clone()); + return networkResponse; + } catch (error) { + console.error('Cache first strategy failed:', error); + return new Response('Offline', { status: 503 }); + } +} + +async function networkFirst(request) { + try { + const networkResponse = await fetch(request); + + // Cache successful responses + if (networkResponse.ok) { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + cache.put(request, networkResponse.clone()); + } + + return networkResponse; + } catch (error) { + console.log('Network first: Falling back to cache for', request.url); + + const cachedResponse = await caches.match(request); + if (cachedResponse) { + return cachedResponse; + } + + // Return offline page for navigation requests + if (request.mode === 'navigate') { + return caches.match('/offline.html') || new Response('Offline', { + status: 503, + headers: { 'Content-Type': 'text/html' } + }); + } + + return new Response('Network Error', { status: 503 }); + } +} + +async function staleWhileRevalidate(request) { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + const cachedResponse = await cache.match(request); + + const fetchPromise = fetch(request).then(networkResponse => { + if (networkResponse.ok) { + cache.put(request, networkResponse.clone()); + } + return networkResponse; + }); + + return cachedResponse || fetchPromise; +} + +// Helper functions +function isStaticFile(url) { + return url.includes('/css/') || + url.includes('/js/') || + url.includes('/images/') || + url.includes('/fonts/') || + url.includes('googleapis.com') || + url.includes('cdnjs.cloudflare.com'); +} + +function isAPIRequest(url) { + return url.includes('/api/') || + API_CACHE_PATTERNS.some(pattern => pattern.test(url)); +} + +function isDynamicRoute(url) { + const pathname = new URL(url).pathname; + return DYNAMIC_ROUTES.includes(pathname) || + pathname.startsWith('/portfolio/') || + pathname.startsWith('/services/'); +} + +// Background sync for form submissions +self.addEventListener('sync', event => { + console.log('Service Worker: Background sync triggered', event.tag); + + if (event.tag === 'contact-form-sync') { + event.waitUntil(syncContactForms()); + } +}); + +async function syncContactForms() { + try { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + const requests = await cache.keys(); + + const contactRequests = requests.filter(request => + request.url.includes('/api/contact/submit') + ); + + for (const request of contactRequests) { + try { + await fetch(request); + await cache.delete(request); + console.log('Contact form synced successfully'); + } catch (error) { + console.error('Failed to sync contact form:', error); + } + } + } catch (error) { + console.error('Background sync failed:', error); + } +} + +// Push notification handling +self.addEventListener('push', event => { + console.log('Service Worker: Push received', event); + + let data = {}; + if (event.data) { + data = event.data.json(); + } + + const title = data.title || 'SmartSolTech'; + const options = { + body: data.body || 'You have a new notification', + icon: '/images/icon-192x192.png', + badge: '/images/icon-72x72.png', + tag: data.tag || 'default', + data: data.url || '/', + actions: [ + { + action: 'open', + title: '열기', + icon: '/images/icon-open.png' + }, + { + action: 'close', + title: '닫기', + icon: '/images/icon-close.png' + } + ], + requireInteraction: data.requireInteraction || false, + silent: data.silent || false, + vibrate: data.vibrate || [200, 100, 200] + }; + + event.waitUntil( + self.registration.showNotification(title, options) + ); +}); + +// Notification click handling +self.addEventListener('notificationclick', event => { + console.log('Service Worker: Notification clicked', event); + + event.notification.close(); + + if (event.action === 'close') { + return; + } + + const url = event.notification.data || '/'; + + event.waitUntil( + clients.matchAll({ type: 'window' }).then(clientList => { + // Check if window is already open + for (const client of clientList) { + if (client.url === url && 'focus' in client) { + return client.focus(); + } + } + + // Open new window + if (clients.openWindow) { + return clients.openWindow(url); + } + }) + ); +}); + +// Handle messages from main thread +self.addEventListener('message', event => { + console.log('Service Worker: Message received', event.data); + + if (event.data && event.data.type === 'SKIP_WAITING') { + self.skipWaiting(); + } + + if (event.data && event.data.type === 'CACHE_URLS') { + cacheUrls(event.data.urls); + } +}); + +async function cacheUrls(urls) { + try { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + await cache.addAll(urls); + console.log('URLs cached successfully:', urls); + } catch (error) { + console.error('Failed to cache URLs:', error); + } +} + +// Periodic background sync (if supported) +self.addEventListener('periodicsync', event => { + console.log('Service Worker: Periodic sync triggered', event.tag); + + if (event.tag === 'content-sync') { + event.waitUntil(syncContent()); + } +}); + +async function syncContent() { + try { + // Fetch fresh portfolio and services data + const portfolioResponse = await fetch('/api/portfolio?featured=true'); + const servicesResponse = await fetch('/api/services?featured=true'); + + if (portfolioResponse.ok && servicesResponse.ok) { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + cache.put('/api/portfolio?featured=true', portfolioResponse.clone()); + cache.put('/api/services?featured=true', servicesResponse.clone()); + console.log('Content synced successfully'); + } + } catch (error) { + console.error('Content sync failed:', error); + } +} + +// Cache management utilities +async function cleanupCaches() { + const cacheNames = await caches.keys(); + const currentCaches = [STATIC_CACHE_NAME, DYNAMIC_CACHE_NAME]; + + return Promise.all( + cacheNames.map(cacheName => { + if (!currentCaches.includes(cacheName)) { + console.log('Deleting old cache:', cacheName); + return caches.delete(cacheName); + } + }) + ); +} + +// Limit cache size +async function limitCacheSize(cacheName, maxItems) { + const cache = await caches.open(cacheName); + const keys = await cache.keys(); + + if (keys.length > maxItems) { + const keysToDelete = keys.slice(0, keys.length - maxItems); + return Promise.all(keysToDelete.map(key => cache.delete(key))); + } +} + +// Performance monitoring +self.addEventListener('fetch', event => { + if (event.request.url.includes('/api/')) { + const start = performance.now(); + + event.respondWith( + fetch(event.request).then(response => { + const duration = performance.now() - start; + + // Log slow API requests + if (duration > 2000) { + console.warn('Slow API request:', event.request.url, duration + 'ms'); + } + + return response; + }) + ); + } +}); + +// Error tracking +self.addEventListener('error', event => { + console.error('Service Worker error:', event.error); + // Could send to analytics service +}); + +self.addEventListener('unhandledrejection', event => { + console.error('Service Worker unhandled rejection:', event.reason); + // Could send to analytics service +}); \ No newline at end of file diff --git a/.history/public/sw_20251021172445.js b/.history/public/sw_20251021172445.js new file mode 100644 index 0000000..94421c1 --- /dev/null +++ b/.history/public/sw_20251021172445.js @@ -0,0 +1,407 @@ +// Service Worker for SmartSolTech PWA +const CACHE_NAME = 'smartsoltech-v1.0.1'; +const STATIC_CACHE_NAME = 'smartsoltech-static-v1.0.1'; +const DYNAMIC_CACHE_NAME = 'smartsoltech-dynamic-v1.0.1'; + +// Files to cache immediately +const STATIC_FILES = [ + '/', + '/css/main.css', + '/css/fixes.css', + '/css/dark-theme.css', + '/js/main.js', + '/images/logo.png', + '/images/icon-192x192.png', + '/images/icon-144x144.png', + '/manifest.json', + 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap', + 'https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css', + 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css', + 'https://unpkg.com/aos@2.3.1/dist/aos.css', + 'https://unpkg.com/aos@2.3.1/dist/aos.js' +]; + +// Routes to cache dynamically +const DYNAMIC_ROUTES = [ + '/about', + '/services', + '/portfolio', + '/calculator', + '/contact' +]; + +// API endpoints to cache +const API_CACHE_PATTERNS = [ + /^\/api\/portfolio/, + /^\/api\/services/, + /^\/api\/calculator\/services/ +]; + +// Install event - cache static files +self.addEventListener('install', event => { + console.log('Service Worker: Installing...'); + + event.waitUntil( + caches.open(STATIC_CACHE_NAME) + .then(cache => { + console.log('Service Worker: Caching static files'); + return cache.addAll(STATIC_FILES); + }) + .then(() => { + console.log('Service Worker: Static files cached'); + return self.skipWaiting(); + }) + .catch(error => { + console.error('Service Worker: Error caching static files', error); + }) + ); +}); + +// Activate event - clean up old caches +self.addEventListener('activate', event => { + console.log('Service Worker: Activating...'); + + event.waitUntil( + caches.keys() + .then(cacheNames => { + return Promise.all( + cacheNames.map(cacheName => { + if (cacheName !== STATIC_CACHE_NAME && + cacheName !== DYNAMIC_CACHE_NAME) { + console.log('Service Worker: Deleting old cache', cacheName); + return caches.delete(cacheName); + } + }) + ); + }) + .then(() => { + console.log('Service Worker: Activated'); + return self.clients.claim(); + }) + ); +}); + +// Fetch event - serve cached files or fetch from network +self.addEventListener('fetch', event => { + const request = event.request; + const url = new URL(request.url); + + // Skip non-GET requests + if (request.method !== 'GET') { + return; + } + + // Skip Chrome extension requests + if (url.protocol === 'chrome-extension:') { + return; + } + + // Handle different types of requests + if (isStaticFile(request.url)) { + event.respondWith(cacheFirst(request)); + } else if (isAPIRequest(request.url)) { + event.respondWith(networkFirst(request)); + } else if (isDynamicRoute(request.url)) { + event.respondWith(staleWhileRevalidate(request)); + } else { + event.respondWith(networkFirst(request)); + } +}); + +// Cache strategies +async function cacheFirst(request) { + try { + const cachedResponse = await caches.match(request); + if (cachedResponse) { + return cachedResponse; + } + + const networkResponse = await fetch(request); + const cache = await caches.open(STATIC_CACHE_NAME); + cache.put(request, networkResponse.clone()); + return networkResponse; + } catch (error) { + console.error('Cache first strategy failed:', error); + return new Response('Offline', { status: 503 }); + } +} + +async function networkFirst(request) { + try { + const networkResponse = await fetch(request); + + // Cache successful responses + if (networkResponse.ok) { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + cache.put(request, networkResponse.clone()); + } + + return networkResponse; + } catch (error) { + console.log('Network first: Falling back to cache for', request.url); + + const cachedResponse = await caches.match(request); + if (cachedResponse) { + return cachedResponse; + } + + // Return offline page for navigation requests + if (request.mode === 'navigate') { + return caches.match('/offline.html') || new Response('Offline', { + status: 503, + headers: { 'Content-Type': 'text/html' } + }); + } + + return new Response('Network Error', { status: 503 }); + } +} + +async function staleWhileRevalidate(request) { + try { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + const cachedResponse = await cache.match(request); + + const fetchPromise = fetch(request).then(networkResponse => { + if (networkResponse && networkResponse.ok) { + cache.put(request, networkResponse.clone()); + } + return networkResponse; + }).catch(error => { + console.log('staleWhileRevalidate fetch failed:', error); + return null; + }); + + return cachedResponse || fetchPromise || new Response('Not available', { status: 503 }); + } catch (error) { + console.error('staleWhileRevalidate error:', error); + return new Response('Service unavailable', { status: 503 }); + } +} + +// Helper functions +function isStaticFile(url) { + return url.includes('/css/') || + url.includes('/js/') || + url.includes('/images/') || + url.includes('/fonts/') || + url.includes('googleapis.com') || + url.includes('cdnjs.cloudflare.com'); +} + +function isAPIRequest(url) { + return url.includes('/api/') || + API_CACHE_PATTERNS.some(pattern => pattern.test(url)); +} + +function isDynamicRoute(url) { + const pathname = new URL(url).pathname; + return DYNAMIC_ROUTES.includes(pathname) || + pathname.startsWith('/portfolio/') || + pathname.startsWith('/services/'); +} + +// Background sync for form submissions +self.addEventListener('sync', event => { + console.log('Service Worker: Background sync triggered', event.tag); + + if (event.tag === 'contact-form-sync') { + event.waitUntil(syncContactForms()); + } +}); + +async function syncContactForms() { + try { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + const requests = await cache.keys(); + + const contactRequests = requests.filter(request => + request.url.includes('/api/contact/submit') + ); + + for (const request of contactRequests) { + try { + await fetch(request); + await cache.delete(request); + console.log('Contact form synced successfully'); + } catch (error) { + console.error('Failed to sync contact form:', error); + } + } + } catch (error) { + console.error('Background sync failed:', error); + } +} + +// Push notification handling +self.addEventListener('push', event => { + console.log('Service Worker: Push received', event); + + let data = {}; + if (event.data) { + data = event.data.json(); + } + + const title = data.title || 'SmartSolTech'; + const options = { + body: data.body || 'You have a new notification', + icon: '/images/icon-192x192.png', + badge: '/images/icon-72x72.png', + tag: data.tag || 'default', + data: data.url || '/', + actions: [ + { + action: 'open', + title: '열기', + icon: '/images/icon-open.png' + }, + { + action: 'close', + title: '닫기', + icon: '/images/icon-close.png' + } + ], + requireInteraction: data.requireInteraction || false, + silent: data.silent || false, + vibrate: data.vibrate || [200, 100, 200] + }; + + event.waitUntil( + self.registration.showNotification(title, options) + ); +}); + +// Notification click handling +self.addEventListener('notificationclick', event => { + console.log('Service Worker: Notification clicked', event); + + event.notification.close(); + + if (event.action === 'close') { + return; + } + + const url = event.notification.data || '/'; + + event.waitUntil( + clients.matchAll({ type: 'window' }).then(clientList => { + // Check if window is already open + for (const client of clientList) { + if (client.url === url && 'focus' in client) { + return client.focus(); + } + } + + // Open new window + if (clients.openWindow) { + return clients.openWindow(url); + } + }) + ); +}); + +// Handle messages from main thread +self.addEventListener('message', event => { + console.log('Service Worker: Message received', event.data); + + if (event.data && event.data.type === 'SKIP_WAITING') { + self.skipWaiting(); + } + + if (event.data && event.data.type === 'CACHE_URLS') { + cacheUrls(event.data.urls); + } +}); + +async function cacheUrls(urls) { + try { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + await cache.addAll(urls); + console.log('URLs cached successfully:', urls); + } catch (error) { + console.error('Failed to cache URLs:', error); + } +} + +// Periodic background sync (if supported) +self.addEventListener('periodicsync', event => { + console.log('Service Worker: Periodic sync triggered', event.tag); + + if (event.tag === 'content-sync') { + event.waitUntil(syncContent()); + } +}); + +async function syncContent() { + try { + // Fetch fresh portfolio and services data + const portfolioResponse = await fetch('/api/portfolio?featured=true'); + const servicesResponse = await fetch('/api/services?featured=true'); + + if (portfolioResponse.ok && servicesResponse.ok) { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + cache.put('/api/portfolio?featured=true', portfolioResponse.clone()); + cache.put('/api/services?featured=true', servicesResponse.clone()); + console.log('Content synced successfully'); + } + } catch (error) { + console.error('Content sync failed:', error); + } +} + +// Cache management utilities +async function cleanupCaches() { + const cacheNames = await caches.keys(); + const currentCaches = [STATIC_CACHE_NAME, DYNAMIC_CACHE_NAME]; + + return Promise.all( + cacheNames.map(cacheName => { + if (!currentCaches.includes(cacheName)) { + console.log('Deleting old cache:', cacheName); + return caches.delete(cacheName); + } + }) + ); +} + +// Limit cache size +async function limitCacheSize(cacheName, maxItems) { + const cache = await caches.open(cacheName); + const keys = await cache.keys(); + + if (keys.length > maxItems) { + const keysToDelete = keys.slice(0, keys.length - maxItems); + return Promise.all(keysToDelete.map(key => cache.delete(key))); + } +} + +// Performance monitoring +self.addEventListener('fetch', event => { + if (event.request.url.includes('/api/')) { + const start = performance.now(); + + event.respondWith( + fetch(event.request).then(response => { + const duration = performance.now() - start; + + // Log slow API requests + if (duration > 2000) { + console.warn('Slow API request:', event.request.url, duration + 'ms'); + } + + return response; + }) + ); + } +}); + +// Error tracking +self.addEventListener('error', event => { + console.error('Service Worker error:', event.error); + // Could send to analytics service +}); + +self.addEventListener('unhandledrejection', event => { + console.error('Service Worker unhandled rejection:', event.reason); + // Could send to analytics service +}); \ No newline at end of file diff --git a/.history/public/sw_20251021172602.js b/.history/public/sw_20251021172602.js new file mode 100644 index 0000000..94421c1 --- /dev/null +++ b/.history/public/sw_20251021172602.js @@ -0,0 +1,407 @@ +// Service Worker for SmartSolTech PWA +const CACHE_NAME = 'smartsoltech-v1.0.1'; +const STATIC_CACHE_NAME = 'smartsoltech-static-v1.0.1'; +const DYNAMIC_CACHE_NAME = 'smartsoltech-dynamic-v1.0.1'; + +// Files to cache immediately +const STATIC_FILES = [ + '/', + '/css/main.css', + '/css/fixes.css', + '/css/dark-theme.css', + '/js/main.js', + '/images/logo.png', + '/images/icon-192x192.png', + '/images/icon-144x144.png', + '/manifest.json', + 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap', + 'https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css', + 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css', + 'https://unpkg.com/aos@2.3.1/dist/aos.css', + 'https://unpkg.com/aos@2.3.1/dist/aos.js' +]; + +// Routes to cache dynamically +const DYNAMIC_ROUTES = [ + '/about', + '/services', + '/portfolio', + '/calculator', + '/contact' +]; + +// API endpoints to cache +const API_CACHE_PATTERNS = [ + /^\/api\/portfolio/, + /^\/api\/services/, + /^\/api\/calculator\/services/ +]; + +// Install event - cache static files +self.addEventListener('install', event => { + console.log('Service Worker: Installing...'); + + event.waitUntil( + caches.open(STATIC_CACHE_NAME) + .then(cache => { + console.log('Service Worker: Caching static files'); + return cache.addAll(STATIC_FILES); + }) + .then(() => { + console.log('Service Worker: Static files cached'); + return self.skipWaiting(); + }) + .catch(error => { + console.error('Service Worker: Error caching static files', error); + }) + ); +}); + +// Activate event - clean up old caches +self.addEventListener('activate', event => { + console.log('Service Worker: Activating...'); + + event.waitUntil( + caches.keys() + .then(cacheNames => { + return Promise.all( + cacheNames.map(cacheName => { + if (cacheName !== STATIC_CACHE_NAME && + cacheName !== DYNAMIC_CACHE_NAME) { + console.log('Service Worker: Deleting old cache', cacheName); + return caches.delete(cacheName); + } + }) + ); + }) + .then(() => { + console.log('Service Worker: Activated'); + return self.clients.claim(); + }) + ); +}); + +// Fetch event - serve cached files or fetch from network +self.addEventListener('fetch', event => { + const request = event.request; + const url = new URL(request.url); + + // Skip non-GET requests + if (request.method !== 'GET') { + return; + } + + // Skip Chrome extension requests + if (url.protocol === 'chrome-extension:') { + return; + } + + // Handle different types of requests + if (isStaticFile(request.url)) { + event.respondWith(cacheFirst(request)); + } else if (isAPIRequest(request.url)) { + event.respondWith(networkFirst(request)); + } else if (isDynamicRoute(request.url)) { + event.respondWith(staleWhileRevalidate(request)); + } else { + event.respondWith(networkFirst(request)); + } +}); + +// Cache strategies +async function cacheFirst(request) { + try { + const cachedResponse = await caches.match(request); + if (cachedResponse) { + return cachedResponse; + } + + const networkResponse = await fetch(request); + const cache = await caches.open(STATIC_CACHE_NAME); + cache.put(request, networkResponse.clone()); + return networkResponse; + } catch (error) { + console.error('Cache first strategy failed:', error); + return new Response('Offline', { status: 503 }); + } +} + +async function networkFirst(request) { + try { + const networkResponse = await fetch(request); + + // Cache successful responses + if (networkResponse.ok) { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + cache.put(request, networkResponse.clone()); + } + + return networkResponse; + } catch (error) { + console.log('Network first: Falling back to cache for', request.url); + + const cachedResponse = await caches.match(request); + if (cachedResponse) { + return cachedResponse; + } + + // Return offline page for navigation requests + if (request.mode === 'navigate') { + return caches.match('/offline.html') || new Response('Offline', { + status: 503, + headers: { 'Content-Type': 'text/html' } + }); + } + + return new Response('Network Error', { status: 503 }); + } +} + +async function staleWhileRevalidate(request) { + try { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + const cachedResponse = await cache.match(request); + + const fetchPromise = fetch(request).then(networkResponse => { + if (networkResponse && networkResponse.ok) { + cache.put(request, networkResponse.clone()); + } + return networkResponse; + }).catch(error => { + console.log('staleWhileRevalidate fetch failed:', error); + return null; + }); + + return cachedResponse || fetchPromise || new Response('Not available', { status: 503 }); + } catch (error) { + console.error('staleWhileRevalidate error:', error); + return new Response('Service unavailable', { status: 503 }); + } +} + +// Helper functions +function isStaticFile(url) { + return url.includes('/css/') || + url.includes('/js/') || + url.includes('/images/') || + url.includes('/fonts/') || + url.includes('googleapis.com') || + url.includes('cdnjs.cloudflare.com'); +} + +function isAPIRequest(url) { + return url.includes('/api/') || + API_CACHE_PATTERNS.some(pattern => pattern.test(url)); +} + +function isDynamicRoute(url) { + const pathname = new URL(url).pathname; + return DYNAMIC_ROUTES.includes(pathname) || + pathname.startsWith('/portfolio/') || + pathname.startsWith('/services/'); +} + +// Background sync for form submissions +self.addEventListener('sync', event => { + console.log('Service Worker: Background sync triggered', event.tag); + + if (event.tag === 'contact-form-sync') { + event.waitUntil(syncContactForms()); + } +}); + +async function syncContactForms() { + try { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + const requests = await cache.keys(); + + const contactRequests = requests.filter(request => + request.url.includes('/api/contact/submit') + ); + + for (const request of contactRequests) { + try { + await fetch(request); + await cache.delete(request); + console.log('Contact form synced successfully'); + } catch (error) { + console.error('Failed to sync contact form:', error); + } + } + } catch (error) { + console.error('Background sync failed:', error); + } +} + +// Push notification handling +self.addEventListener('push', event => { + console.log('Service Worker: Push received', event); + + let data = {}; + if (event.data) { + data = event.data.json(); + } + + const title = data.title || 'SmartSolTech'; + const options = { + body: data.body || 'You have a new notification', + icon: '/images/icon-192x192.png', + badge: '/images/icon-72x72.png', + tag: data.tag || 'default', + data: data.url || '/', + actions: [ + { + action: 'open', + title: '열기', + icon: '/images/icon-open.png' + }, + { + action: 'close', + title: '닫기', + icon: '/images/icon-close.png' + } + ], + requireInteraction: data.requireInteraction || false, + silent: data.silent || false, + vibrate: data.vibrate || [200, 100, 200] + }; + + event.waitUntil( + self.registration.showNotification(title, options) + ); +}); + +// Notification click handling +self.addEventListener('notificationclick', event => { + console.log('Service Worker: Notification clicked', event); + + event.notification.close(); + + if (event.action === 'close') { + return; + } + + const url = event.notification.data || '/'; + + event.waitUntil( + clients.matchAll({ type: 'window' }).then(clientList => { + // Check if window is already open + for (const client of clientList) { + if (client.url === url && 'focus' in client) { + return client.focus(); + } + } + + // Open new window + if (clients.openWindow) { + return clients.openWindow(url); + } + }) + ); +}); + +// Handle messages from main thread +self.addEventListener('message', event => { + console.log('Service Worker: Message received', event.data); + + if (event.data && event.data.type === 'SKIP_WAITING') { + self.skipWaiting(); + } + + if (event.data && event.data.type === 'CACHE_URLS') { + cacheUrls(event.data.urls); + } +}); + +async function cacheUrls(urls) { + try { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + await cache.addAll(urls); + console.log('URLs cached successfully:', urls); + } catch (error) { + console.error('Failed to cache URLs:', error); + } +} + +// Periodic background sync (if supported) +self.addEventListener('periodicsync', event => { + console.log('Service Worker: Periodic sync triggered', event.tag); + + if (event.tag === 'content-sync') { + event.waitUntil(syncContent()); + } +}); + +async function syncContent() { + try { + // Fetch fresh portfolio and services data + const portfolioResponse = await fetch('/api/portfolio?featured=true'); + const servicesResponse = await fetch('/api/services?featured=true'); + + if (portfolioResponse.ok && servicesResponse.ok) { + const cache = await caches.open(DYNAMIC_CACHE_NAME); + cache.put('/api/portfolio?featured=true', portfolioResponse.clone()); + cache.put('/api/services?featured=true', servicesResponse.clone()); + console.log('Content synced successfully'); + } + } catch (error) { + console.error('Content sync failed:', error); + } +} + +// Cache management utilities +async function cleanupCaches() { + const cacheNames = await caches.keys(); + const currentCaches = [STATIC_CACHE_NAME, DYNAMIC_CACHE_NAME]; + + return Promise.all( + cacheNames.map(cacheName => { + if (!currentCaches.includes(cacheName)) { + console.log('Deleting old cache:', cacheName); + return caches.delete(cacheName); + } + }) + ); +} + +// Limit cache size +async function limitCacheSize(cacheName, maxItems) { + const cache = await caches.open(cacheName); + const keys = await cache.keys(); + + if (keys.length > maxItems) { + const keysToDelete = keys.slice(0, keys.length - maxItems); + return Promise.all(keysToDelete.map(key => cache.delete(key))); + } +} + +// Performance monitoring +self.addEventListener('fetch', event => { + if (event.request.url.includes('/api/')) { + const start = performance.now(); + + event.respondWith( + fetch(event.request).then(response => { + const duration = performance.now() - start; + + // Log slow API requests + if (duration > 2000) { + console.warn('Slow API request:', event.request.url, duration + 'ms'); + } + + return response; + }) + ); + } +}); + +// Error tracking +self.addEventListener('error', event => { + console.error('Service Worker error:', event.error); + // Could send to analytics service +}); + +self.addEventListener('unhandledrejection', event => { + console.error('Service Worker unhandled rejection:', event.reason); + // Could send to analytics service +}); \ No newline at end of file diff --git a/.history/routes/admin_20251019202120.js b/.history/routes/admin_20251019202120.js new file mode 100644 index 0000000..adec8e8 --- /dev/null +++ b/.history/routes/admin_20251019202120.js @@ -0,0 +1,383 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ email, isActive: true }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user._id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.countDocuments({ isPublished: true }), + Service.countDocuments({ isActive: true }), + Contact.countDocuments(), + Contact.find().sort({ createdAt: -1 }).limit(5), + Portfolio.find({ isPublished: true }).sort({ createdAt: -1 }).limit(5) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.countDocuments({ isRead: false }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Portfolio.countDocuments() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Service.countDocuments() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findById(req.params.id) + .populate('portfolio', 'title'); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.find({ isPublished: true }) + .select('title category'); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let query = {}; + if (status && status !== 'all') { + query.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.find(query) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Contact.countDocuments(query) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findById(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || new SiteSettings(); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251019202126.js b/.history/routes/admin_20251019202126.js new file mode 100644 index 0000000..273444c --- /dev/null +++ b/.history/routes/admin_20251019202126.js @@ -0,0 +1,388 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user._id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.countDocuments({ isPublished: true }), + Service.countDocuments({ isActive: true }), + Contact.countDocuments(), + Contact.find().sort({ createdAt: -1 }).limit(5), + Portfolio.find({ isPublished: true }).sort({ createdAt: -1 }).limit(5) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.countDocuments({ isRead: false }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Portfolio.countDocuments() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Service.countDocuments() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findById(req.params.id) + .populate('portfolio', 'title'); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.find({ isPublished: true }) + .select('title category'); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let query = {}; + if (status && status !== 'all') { + query.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.find(query) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Contact.countDocuments(query) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findById(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || new SiteSettings(); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251019202132.js b/.history/routes/admin_20251019202132.js new file mode 100644 index 0000000..399d74a --- /dev/null +++ b/.history/routes/admin_20251019202132.js @@ -0,0 +1,388 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.countDocuments({ isPublished: true }), + Service.countDocuments({ isActive: true }), + Contact.countDocuments(), + Contact.find().sort({ createdAt: -1 }).limit(5), + Portfolio.find({ isPublished: true }).sort({ createdAt: -1 }).limit(5) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.countDocuments({ isRead: false }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Portfolio.countDocuments() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Service.countDocuments() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findById(req.params.id) + .populate('portfolio', 'title'); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.find({ isPublished: true }) + .select('title category'); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let query = {}; + if (status && status !== 'all') { + query.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.find(query) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Contact.countDocuments(query) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findById(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || new SiteSettings(); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251019202141.js b/.history/routes/admin_20251019202141.js new file mode 100644 index 0000000..06c7c64 --- /dev/null +++ b/.history/routes/admin_20251019202141.js @@ -0,0 +1,395 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.countDocuments({ isRead: false }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Portfolio.countDocuments() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Service.countDocuments() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findById(req.params.id) + .populate('portfolio', 'title'); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.find({ isPublished: true }) + .select('title category'); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let query = {}; + if (status && status !== 'all') { + query.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.find(query) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Contact.countDocuments(query) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findById(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || new SiteSettings(); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251019202147.js b/.history/routes/admin_20251019202147.js new file mode 100644 index 0000000..65fa613 --- /dev/null +++ b/.history/routes/admin_20251019202147.js @@ -0,0 +1,395 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Portfolio.countDocuments() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Service.countDocuments() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findById(req.params.id) + .populate('portfolio', 'title'); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.find({ isPublished: true }) + .select('title category'); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let query = {}; + if (status && status !== 'all') { + query.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.find(query) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Contact.countDocuments(query) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findById(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || new SiteSettings(); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251019202631.js b/.history/routes/admin_20251019202631.js new file mode 100644 index 0000000..65fa613 --- /dev/null +++ b/.history/routes/admin_20251019202631.js @@ -0,0 +1,395 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Portfolio.countDocuments() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Service.countDocuments() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findById(req.params.id) + .populate('portfolio', 'title'); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.find({ isPublished: true }) + .select('title category'); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let query = {}; + if (status && status !== 'all') { + query.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.find(query) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Contact.countDocuments(query) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findById(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || new SiteSettings(); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251019204203.js b/.history/routes/admin_20251019204203.js new file mode 100644 index 0000000..cff7985 --- /dev/null +++ b/.history/routes/admin_20251019204203.js @@ -0,0 +1,396 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Service.countDocuments() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findById(req.params.id) + .populate('portfolio', 'title'); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.find({ isPublished: true }) + .select('title category'); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let query = {}; + if (status && status !== 'all') { + query.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.find(query) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Contact.countDocuments(query) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findById(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || new SiteSettings(); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251019204212.js b/.history/routes/admin_20251019204212.js new file mode 100644 index 0000000..b15353e --- /dev/null +++ b/.history/routes/admin_20251019204212.js @@ -0,0 +1,396 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.find() + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Service.countDocuments() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findById(req.params.id) + .populate('portfolio', 'title'); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.find({ isPublished: true }) + .select('title category'); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let query = {}; + if (status && status !== 'all') { + query.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.find(query) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Contact.countDocuments(query) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findById(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || new SiteSettings(); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251019204224.js b/.history/routes/admin_20251019204224.js new file mode 100644 index 0000000..e6abba0 --- /dev/null +++ b/.history/routes/admin_20251019204224.js @@ -0,0 +1,397 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findById(req.params.id) + .populate('portfolio', 'title'); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.find({ isPublished: true }) + .select('title category'); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let query = {}; + if (status && status !== 'all') { + query.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.find(query) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Contact.countDocuments(query) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findById(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || new SiteSettings(); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251019204235.js b/.history/routes/admin_20251019204235.js new file mode 100644 index 0000000..1682ba0 --- /dev/null +++ b/.history/routes/admin_20251019204235.js @@ -0,0 +1,396 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.find({ isPublished: true }) + .select('title category'); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let query = {}; + if (status && status !== 'all') { + query.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.find(query) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Contact.countDocuments(query) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findById(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || new SiteSettings(); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251019204245.js b/.history/routes/admin_20251019204245.js new file mode 100644 index 0000000..29bd33c --- /dev/null +++ b/.history/routes/admin_20251019204245.js @@ -0,0 +1,398 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let query = {}; + if (status && status !== 'all') { + query.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.find(query) + .sort({ createdAt: -1 }) + .skip(skip) + .limit(limit), + Contact.countDocuments(query) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findById(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || new SiteSettings(); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251019204257.js b/.history/routes/admin_20251019204257.js new file mode 100644 index 0000000..1c9c434 --- /dev/null +++ b/.history/routes/admin_20251019204257.js @@ -0,0 +1,400 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findById(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || new SiteSettings(); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251019204307.js b/.history/routes/admin_20251019204307.js new file mode 100644 index 0000000..3746273 --- /dev/null +++ b/.history/routes/admin_20251019204307.js @@ -0,0 +1,400 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || new SiteSettings(); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251019204323.js b/.history/routes/admin_20251019204323.js new file mode 100644 index 0000000..d923515 --- /dev/null +++ b/.history/routes/admin_20251019204323.js @@ -0,0 +1,400 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251019204805.js b/.history/routes/admin_20251019204805.js new file mode 100644 index 0000000..d923515 --- /dev/null +++ b/.history/routes/admin_20251019204805.js @@ -0,0 +1,400 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251020044258.js b/.history/routes/admin_20251020044258.js new file mode 100644 index 0000000..8076f4d --- /dev/null +++ b/.history/routes/admin_20251020044258.js @@ -0,0 +1,399 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: req.t('admin.login'), + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'Invalid email or password' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251020044304.js b/.history/routes/admin_20251020044304.js new file mode 100644 index 0000000..5d00fdd --- /dev/null +++ b/.history/routes/admin_20251020044304.js @@ -0,0 +1,398 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: req.t('admin.login'), + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.invalid_credentials') + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login - SmartSolTech', + layout: 'admin/layout', + error: 'An error occurred. Please try again.' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251020044311.js b/.history/routes/admin_20251020044311.js new file mode 100644 index 0000000..a901d2e --- /dev/null +++ b/.history/routes/admin_20251020044311.js @@ -0,0 +1,397 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: req.t('admin.login'), + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.invalid_credentials') + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.server_error') + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251020044317.js b/.history/routes/admin_20251020044317.js new file mode 100644 index 0000000..ab8f4f0 --- /dev/null +++ b/.history/routes/admin_20251020044317.js @@ -0,0 +1,402 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: req.t('admin.login'), + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.invalid_credentials') + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.server_error') + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolio: portfolioCount, + services: servicesCount, + contacts: contactsCount, + unreadContacts: await Contact.count({ where: { isRead: false } }) + }; + + res.render('admin/dashboard', { + title: 'Dashboard - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251020044325.js b/.history/routes/admin_20251020044325.js new file mode 100644 index 0000000..0006b12 --- /dev/null +++ b/.history/routes/admin_20251020044325.js @@ -0,0 +1,402 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: req.t('admin.login'), + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.invalid_credentials') + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.server_error') + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: req.t('admin.dashboard'), + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251020044351.js b/.history/routes/admin_20251020044351.js new file mode 100644 index 0000000..0006b12 --- /dev/null +++ b/.history/routes/admin_20251020044351.js @@ -0,0 +1,402 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: req.t('admin.login'), + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.invalid_credentials') + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.server_error') + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: req.t('admin.dashboard'), + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251020225524.js b/.history/routes/admin_20251020225524.js new file mode 100644 index 0000000..0c488a9 --- /dev/null +++ b/.history/routes/admin_20251020225524.js @@ -0,0 +1,422 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: req.t('admin.login'), + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.invalid_credentials') + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.server_error') + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: req.t('admin.dashboard'), + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner Editor +router.get('/banner-editor', requireAuth, async (req, res) => { + try { + res.render('admin/banner-editor', { + title: 'Редактор Баннеров - Admin Panel', + layout: false, // Отключаем layout для этой страницы + user: req.session.user, + locale: req.getLocale() || 'ko', + theme: req.session.theme || 'light' + }); + } catch (error) { + console.error('Banner editor error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner editor' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251020225526.js b/.history/routes/admin_20251020225526.js new file mode 100644 index 0000000..0c488a9 --- /dev/null +++ b/.history/routes/admin_20251020225526.js @@ -0,0 +1,422 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: req.t('admin.login'), + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.invalid_credentials') + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.server_error') + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: req.t('admin.dashboard'), + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner Editor +router.get('/banner-editor', requireAuth, async (req, res) => { + try { + res.render('admin/banner-editor', { + title: 'Редактор Баннеров - Admin Panel', + layout: false, // Отключаем layout для этой страницы + user: req.session.user, + locale: req.getLocale() || 'ko', + theme: req.session.theme || 'light' + }); + } catch (error) { + console.error('Banner editor error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner editor' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251021214310.js b/.history/routes/admin_20251021214310.js new file mode 100644 index 0000000..f2e1b9f --- /dev/null +++ b/.history/routes/admin_20251021214310.js @@ -0,0 +1,422 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.invalid_credentials') + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.server_error') + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: req.t('admin.dashboard'), + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner Editor +router.get('/banner-editor', requireAuth, async (req, res) => { + try { + res.render('admin/banner-editor', { + title: 'Редактор Баннеров - Admin Panel', + layout: false, // Отключаем layout для этой страницы + user: req.session.user, + locale: req.getLocale() || 'ko', + theme: req.session.theme || 'light' + }); + } catch (error) { + console.error('Banner editor error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner editor' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251021214318.js b/.history/routes/admin_20251021214318.js new file mode 100644 index 0000000..066db72 --- /dev/null +++ b/.history/routes/admin_20251021214318.js @@ -0,0 +1,422 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: req.t('admin.login'), + error: req.t('errors.server_error') + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: req.t('admin.dashboard'), + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner Editor +router.get('/banner-editor', requireAuth, async (req, res) => { + try { + res.render('admin/banner-editor', { + title: 'Редактор Баннеров - Admin Panel', + layout: false, // Отключаем layout для этой страницы + user: req.session.user, + locale: req.getLocale() || 'ko', + theme: req.session.theme || 'light' + }); + } catch (error) { + console.error('Banner editor error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner editor' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251021214326.js b/.history/routes/admin_20251021214326.js new file mode 100644 index 0000000..c9ab8dc --- /dev/null +++ b/.history/routes/admin_20251021214326.js @@ -0,0 +1,422 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: req.t('admin.dashboard'), + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner Editor +router.get('/banner-editor', requireAuth, async (req, res) => { + try { + res.render('admin/banner-editor', { + title: 'Редактор Баннеров - Admin Panel', + layout: false, // Отключаем layout для этой страницы + user: req.session.user, + locale: req.getLocale() || 'ko', + theme: req.session.theme || 'light' + }); + } catch (error) { + console.error('Banner editor error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner editor' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251021214335.js b/.history/routes/admin_20251021214335.js new file mode 100644 index 0000000..219e18e --- /dev/null +++ b/.history/routes/admin_20251021214335.js @@ -0,0 +1,422 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner Editor +router.get('/banner-editor', requireAuth, async (req, res) => { + try { + res.render('admin/banner-editor', { + title: 'Редактор Баннеров - Admin Panel', + layout: false, // Отключаем layout для этой страницы + user: req.session.user, + locale: req.getLocale() || 'ko', + theme: req.session.theme || 'light' + }); + } catch (error) { + console.error('Banner editor error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner editor' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251021214352.js b/.history/routes/admin_20251021214352.js new file mode 100644 index 0000000..219e18e --- /dev/null +++ b/.history/routes/admin_20251021214352.js @@ -0,0 +1,422 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner Editor +router.get('/banner-editor', requireAuth, async (req, res) => { + try { + res.render('admin/banner-editor', { + title: 'Редактор Баннеров - Admin Panel', + layout: false, // Отключаем layout для этой страницы + user: req.session.user, + locale: req.getLocale() || 'ko', + theme: req.session.theme || 'light' + }); + } catch (error) { + console.error('Banner editor error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner editor' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251022052409.js b/.history/routes/admin_20251022052409.js new file mode 100644 index 0000000..ea7dae9 --- /dev/null +++ b/.history/routes/admin_20251022052409.js @@ -0,0 +1,501 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner Editor +router.get('/banner-editor', requireAuth, async (req, res) => { + try { + res.render('admin/banner-editor', { + title: 'Редактор Баннеров - Admin Panel', + layout: false, // Отключаем layout для этой страницы + user: req.session.user, + locale: req.getLocale() || 'ko', + theme: req.session.theme || 'light' + }); + } catch (error) { + console.error('Banner editor error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner editor' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Telegram bot configuration and testing +router.get('/telegram', requireAuth, (req, res) => { + const telegramService = require('../services/telegram'); + res.render('admin/telegram', { + title: 'Telegram Bot - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + botConfigured: telegramService.isEnabled + }); +}); + +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + const testMessage = `🤖 Тест Telegram бота\n\n` + + `✅ Соединение успешно установлено!\n` + + `⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` + + `🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` + + `Бот готов к отправке уведомлений! 🚀`; + + await telegramService.sendMessage(testMessage); + + res.json({ + success: true, + message: 'Test message sent successfully!', + botInfo: result.bot + }); + } else { + res.status(400).json({ + success: false, + message: result.message || 'Failed to connect to Telegram bot' + }); + } + } catch (error) { + console.error('Telegram test error:', error); + res.status(500).json({ + success: false, + message: 'Error testing Telegram bot' + }); + } +}); + +router.post('/telegram/send', requireAuth, async (req, res) => { + try { + const { message } = req.body; + + if (!message || message.trim().length === 0) { + return res.status(400).json({ + success: false, + message: 'Message is required' + }); + } + + const telegramService = require('../services/telegram'); + const result = await telegramService.sendMessage(message); + + if (result.success) { + res.json({ + success: true, + message: 'Message sent successfully!' + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to send message' + }); + } + } catch (error) { + console.error('Send Telegram message error:', error); + res.status(500).json({ + success: false, + message: 'Error sending message' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251022052452.js b/.history/routes/admin_20251022052452.js new file mode 100644 index 0000000..ea7dae9 --- /dev/null +++ b/.history/routes/admin_20251022052452.js @@ -0,0 +1,501 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner Editor +router.get('/banner-editor', requireAuth, async (req, res) => { + try { + res.render('admin/banner-editor', { + title: 'Редактор Баннеров - Admin Panel', + layout: false, // Отключаем layout для этой страницы + user: req.session.user, + locale: req.getLocale() || 'ko', + theme: req.session.theme || 'light' + }); + } catch (error) { + console.error('Banner editor error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner editor' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Telegram bot configuration and testing +router.get('/telegram', requireAuth, (req, res) => { + const telegramService = require('../services/telegram'); + res.render('admin/telegram', { + title: 'Telegram Bot - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + botConfigured: telegramService.isEnabled + }); +}); + +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + const testMessage = `🤖 Тест Telegram бота\n\n` + + `✅ Соединение успешно установлено!\n` + + `⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` + + `🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` + + `Бот готов к отправке уведомлений! 🚀`; + + await telegramService.sendMessage(testMessage); + + res.json({ + success: true, + message: 'Test message sent successfully!', + botInfo: result.bot + }); + } else { + res.status(400).json({ + success: false, + message: result.message || 'Failed to connect to Telegram bot' + }); + } + } catch (error) { + console.error('Telegram test error:', error); + res.status(500).json({ + success: false, + message: 'Error testing Telegram bot' + }); + } +}); + +router.post('/telegram/send', requireAuth, async (req, res) => { + try { + const { message } = req.body; + + if (!message || message.trim().length === 0) { + return res.status(400).json({ + success: false, + message: 'Message is required' + }); + } + + const telegramService = require('../services/telegram'); + const result = await telegramService.sendMessage(message); + + if (result.success) { + res.json({ + success: true, + message: 'Message sent successfully!' + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to send message' + }); + } + } catch (error) { + console.error('Send Telegram message error:', error); + res.status(500).json({ + success: false, + message: 'Error sending message' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251022194710.js b/.history/routes/admin_20251022194710.js new file mode 100644 index 0000000..60e1cdf --- /dev/null +++ b/.history/routes/admin_20251022194710.js @@ -0,0 +1,717 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner Editor +router.get('/banner-editor', requireAuth, async (req, res) => { + try { + res.render('admin/banner-editor', { + title: 'Редактор Баннеров - Admin Panel', + layout: false, // Отключаем layout для этой страницы + user: req.session.user, + locale: req.getLocale() || 'ko', + theme: req.session.theme || 'light' + }); + } catch (error) { + console.error('Banner editor error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner editor' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); +}); + +// Create portfolio item +router.post('/portfolio/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('technologies').isArray({ min: 1 }).withMessage('최소 한 개의 기술을 입력해주세요'), +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished = false, + featured = false + } = req.body; + + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) ? new Date() : null, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 생성되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Update portfolio item +router.put('/portfolio/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished, + featured + } = req.body; + + // Update portfolio + await portfolio.update({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 업데이트되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete portfolio item +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + await portfolio.destroy(); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle portfolio publish status +router.patch('/portfolio/:id/toggle-publish', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const newStatus = !portfolio.isPublished; + await portfolio.update({ + isPublished: newStatus, + publishedAt: newStatus && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: newStatus ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: `포트폴리오가 ${newStatus ? '게시' : '비공개'}되었습니다`, + isPublished: newStatus + }); + } catch (error) { + console.error('Portfolio toggle publish error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Telegram bot configuration and testing +router.get('/telegram', requireAuth, (req, res) => { + const telegramService = require('../services/telegram'); + res.render('admin/telegram', { + title: 'Telegram Bot - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + botConfigured: telegramService.isEnabled + }); +}); + +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + const testMessage = `🤖 Тест Telegram бота\n\n` + + `✅ Соединение успешно установлено!\n` + + `⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` + + `🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` + + `Бот готов к отправке уведомлений! 🚀`; + + await telegramService.sendMessage(testMessage); + + res.json({ + success: true, + message: 'Test message sent successfully!', + botInfo: result.bot + }); + } else { + res.status(400).json({ + success: false, + message: result.message || 'Failed to connect to Telegram bot' + }); + } + } catch (error) { + console.error('Telegram test error:', error); + res.status(500).json({ + success: false, + message: 'Error testing Telegram bot' + }); + } +}); + +router.post('/telegram/send', requireAuth, async (req, res) => { + try { + const { message } = req.body; + + if (!message || message.trim().length === 0) { + return res.status(400).json({ + success: false, + message: 'Message is required' + }); + } + + const telegramService = require('../services/telegram'); + const result = await telegramService.sendMessage(message); + + if (result.success) { + res.json({ + success: true, + message: 'Message sent successfully!' + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to send message' + }); + } + } catch (error) { + console.error('Send Telegram message error:', error); + res.status(500).json({ + success: false, + message: 'Error sending message' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251022194741.js b/.history/routes/admin_20251022194741.js new file mode 100644 index 0000000..5d484dc --- /dev/null +++ b/.history/routes/admin_20251022194741.js @@ -0,0 +1,921 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner Editor +router.get('/banner-editor', requireAuth, async (req, res) => { + try { + res.render('admin/banner-editor', { + title: 'Редактор Баннеров - Admin Panel', + layout: false, // Отключаем layout для этой страницы + user: req.session.user, + locale: req.getLocale() || 'ko', + theme: req.session.theme || 'light' + }); + } catch (error) { + console.error('Banner editor error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner editor' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); +}); + +// Create portfolio item +router.post('/portfolio/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('technologies').isArray({ min: 1 }).withMessage('최소 한 개의 기술을 입력해주세요'), +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished = false, + featured = false + } = req.body; + + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) ? new Date() : null, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 생성되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Update portfolio item +router.put('/portfolio/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished, + featured + } = req.body; + + // Update portfolio + await portfolio.update({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 업데이트되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete portfolio item +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + await portfolio.destroy(); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle portfolio publish status +router.patch('/portfolio/:id/toggle-publish', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const newStatus = !portfolio.isPublished; + await portfolio.update({ + isPublished: newStatus, + publishedAt: newStatus && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: newStatus ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: `포트폴리오가 ${newStatus ? '게시' : '비공개'}되었습니다`, + isPublished: newStatus + }); + } catch (error) { + console.error('Portfolio toggle publish error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); +}); + +// Create service +router.post('/services/add', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive = true, + featured = false + } = req.body; + + const service = await Service.create({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 생성되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service creation error:', error); + res.status(500).json({ + success: false, + message: '서비스 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['id', 'title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Update service +router.put('/services/:id', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const service = await Service.findByPk(req.params.id); + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive, + featured + } = req.body; + + await service.update({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 업데이트되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service update error:', error); + res.status(500).json({ + success: false, + message: '서비스 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete service +router.delete('/services/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + await service.destroy(); + + res.json({ + success: true, + message: '서비스가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Service deletion error:', error); + res.status(500).json({ + success: false, + message: '서비스 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle service active status +router.patch('/services/:id/toggle-active', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const newStatus = !service.isActive; + await service.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `서비스가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Service toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Telegram bot configuration and testing +router.get('/telegram', requireAuth, (req, res) => { + const telegramService = require('../services/telegram'); + res.render('admin/telegram', { + title: 'Telegram Bot - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + botConfigured: telegramService.isEnabled + }); +}); + +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + const testMessage = `🤖 Тест Telegram бота\n\n` + + `✅ Соединение успешно установлено!\n` + + `⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` + + `🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` + + `Бот готов к отправке уведомлений! 🚀`; + + await telegramService.sendMessage(testMessage); + + res.json({ + success: true, + message: 'Test message sent successfully!', + botInfo: result.bot + }); + } else { + res.status(400).json({ + success: false, + message: result.message || 'Failed to connect to Telegram bot' + }); + } + } catch (error) { + console.error('Telegram test error:', error); + res.status(500).json({ + success: false, + message: 'Error testing Telegram bot' + }); + } +}); + +router.post('/telegram/send', requireAuth, async (req, res) => { + try { + const { message } = req.body; + + if (!message || message.trim().length === 0) { + return res.status(400).json({ + success: false, + message: 'Message is required' + }); + } + + const telegramService = require('../services/telegram'); + const result = await telegramService.sendMessage(message); + + if (result.success) { + res.json({ + success: true, + message: 'Message sent successfully!' + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to send message' + }); + } + } catch (error) { + console.error('Send Telegram message error:', error); + res.status(500).json({ + success: false, + message: 'Error sending message' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251022194824.js b/.history/routes/admin_20251022194824.js new file mode 100644 index 0000000..db9d7ba --- /dev/null +++ b/.history/routes/admin_20251022194824.js @@ -0,0 +1,921 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings, Banner } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner Editor +router.get('/banner-editor', requireAuth, async (req, res) => { + try { + res.render('admin/banner-editor', { + title: 'Редактор Баннеров - Admin Panel', + layout: false, // Отключаем layout для этой страницы + user: req.session.user, + locale: req.getLocale() || 'ko', + theme: req.session.theme || 'light' + }); + } catch (error) { + console.error('Banner editor error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner editor' + }); + } +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); +}); + +// Create portfolio item +router.post('/portfolio/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('technologies').isArray({ min: 1 }).withMessage('최소 한 개의 기술을 입력해주세요'), +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished = false, + featured = false + } = req.body; + + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) ? new Date() : null, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 생성되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Update portfolio item +router.put('/portfolio/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished, + featured + } = req.body; + + // Update portfolio + await portfolio.update({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 업데이트되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete portfolio item +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + await portfolio.destroy(); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle portfolio publish status +router.patch('/portfolio/:id/toggle-publish', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const newStatus = !portfolio.isPublished; + await portfolio.update({ + isPublished: newStatus, + publishedAt: newStatus && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: newStatus ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: `포트폴리오가 ${newStatus ? '게시' : '비공개'}되었습니다`, + isPublished: newStatus + }); + } catch (error) { + console.error('Portfolio toggle publish error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); +}); + +// Create service +router.post('/services/add', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive = true, + featured = false + } = req.body; + + const service = await Service.create({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 생성되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service creation error:', error); + res.status(500).json({ + success: false, + message: '서비스 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['id', 'title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Update service +router.put('/services/:id', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const service = await Service.findByPk(req.params.id); + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive, + featured + } = req.body; + + await service.update({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 업데이트되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service update error:', error); + res.status(500).json({ + success: false, + message: '서비스 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete service +router.delete('/services/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + await service.destroy(); + + res.json({ + success: true, + message: '서비스가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Service deletion error:', error); + res.status(500).json({ + success: false, + message: '서비스 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle service active status +router.patch('/services/:id/toggle-active', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const newStatus = !service.isActive; + await service.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `서비스가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Service toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Telegram bot configuration and testing +router.get('/telegram', requireAuth, (req, res) => { + const telegramService = require('../services/telegram'); + res.render('admin/telegram', { + title: 'Telegram Bot - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + botConfigured: telegramService.isEnabled + }); +}); + +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + const testMessage = `🤖 Тест Telegram бота\n\n` + + `✅ Соединение успешно установлено!\n` + + `⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` + + `🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` + + `Бот готов к отправке уведомлений! 🚀`; + + await telegramService.sendMessage(testMessage); + + res.json({ + success: true, + message: 'Test message sent successfully!', + botInfo: result.bot + }); + } else { + res.status(400).json({ + success: false, + message: result.message || 'Failed to connect to Telegram bot' + }); + } + } catch (error) { + console.error('Telegram test error:', error); + res.status(500).json({ + success: false, + message: 'Error testing Telegram bot' + }); + } +}); + +router.post('/telegram/send', requireAuth, async (req, res) => { + try { + const { message } = req.body; + + if (!message || message.trim().length === 0) { + return res.status(400).json({ + success: false, + message: 'Message is required' + }); + } + + const telegramService = require('../services/telegram'); + const result = await telegramService.sendMessage(message); + + if (result.success) { + res.json({ + success: true, + message: 'Message sent successfully!' + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to send message' + }); + } + } catch (error) { + console.error('Send Telegram message error:', error); + res.status(500).json({ + success: false, + message: 'Error sending message' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251022194900.js b/.history/routes/admin_20251022194900.js new file mode 100644 index 0000000..435a517 --- /dev/null +++ b/.history/routes/admin_20251022194900.js @@ -0,0 +1,1241 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings, Banner } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner management +router.get('/banners', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [banners, total] = await Promise.all([ + Banner.findAll({ + order: [['order', 'ASC'], ['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Banner.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/banners/list', { + title: 'Banner Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banners, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Banner list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banners' + }); + } +}); + +// Add banner +router.get('/banners/add', requireAuth, (req, res) => { + res.render('admin/banners/add', { + title: 'Add Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); +}); + +// Create banner +router.post('/banners/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive = true, + startDate, + endDate, + targetAudience = 'all', + backgroundColor, + textColor, + animation = 'none' + } = req.body; + + const banner = await Banner.create({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience, + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation + }); + + res.json({ + success: true, + message: '배너가 성공적으로 생성되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner creation error:', error); + res.status(500).json({ + success: false, + message: '배너 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit banner +router.get('/banners/edit/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Banner not found' + }); + } + + res.render('admin/banners/edit', { + title: 'Edit Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banner, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); + } catch (error) { + console.error('Banner edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner' + }); + } +}); + +// Update banner +router.put('/banners/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const banner = await Banner.findByPk(req.params.id); + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive, + startDate, + endDate, + targetAudience, + backgroundColor, + textColor, + animation + } = req.body; + + await banner.update({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience: targetAudience || 'all', + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation: animation || 'none' + }); + + res.json({ + success: true, + message: '배너가 성공적으로 업데이트되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner update error:', error); + res.status(500).json({ + success: false, + message: '배너 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete banner +router.delete('/banners/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.destroy(); + + res.json({ + success: true, + message: '배너가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Banner deletion error:', error); + res.status(500).json({ + success: false, + message: '배너 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle banner active status +router.patch('/banners/:id/toggle-active', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const newStatus = !banner.isActive; + await banner.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `배너가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Banner toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Record banner click +router.post('/banners/:id/click', async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.recordClick(); + + res.json({ + success: true, + clickCount: banner.clickCount + }); + } catch (error) { + console.error('Banner click record error:', error); + res.status(500).json({ + success: false, + message: '클릭 기록 중 오류가 발생했습니다' + }); + } +}); + +// Banner Editor (legacy route) +router.get('/banner-editor', requireAuth, async (req, res) => { + res.redirect('/admin/banners'); +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); +}); + +// Create portfolio item +router.post('/portfolio/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('technologies').isArray({ min: 1 }).withMessage('최소 한 개의 기술을 입력해주세요'), +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished = false, + featured = false + } = req.body; + + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) ? new Date() : null, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 생성되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Update portfolio item +router.put('/portfolio/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished, + featured + } = req.body; + + // Update portfolio + await portfolio.update({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 업데이트되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete portfolio item +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + await portfolio.destroy(); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle portfolio publish status +router.patch('/portfolio/:id/toggle-publish', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const newStatus = !portfolio.isPublished; + await portfolio.update({ + isPublished: newStatus, + publishedAt: newStatus && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: newStatus ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: `포트폴리오가 ${newStatus ? '게시' : '비공개'}되었습니다`, + isPublished: newStatus + }); + } catch (error) { + console.error('Portfolio toggle publish error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); +}); + +// Create service +router.post('/services/add', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive = true, + featured = false + } = req.body; + + const service = await Service.create({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 생성되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service creation error:', error); + res.status(500).json({ + success: false, + message: '서비스 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['id', 'title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Update service +router.put('/services/:id', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const service = await Service.findByPk(req.params.id); + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive, + featured + } = req.body; + + await service.update({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 업데이트되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service update error:', error); + res.status(500).json({ + success: false, + message: '서비스 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete service +router.delete('/services/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + await service.destroy(); + + res.json({ + success: true, + message: '서비스가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Service deletion error:', error); + res.status(500).json({ + success: false, + message: '서비스 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle service active status +router.patch('/services/:id/toggle-active', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const newStatus = !service.isActive; + await service.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `서비스가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Service toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Telegram bot configuration and testing +router.get('/telegram', requireAuth, (req, res) => { + const telegramService = require('../services/telegram'); + res.render('admin/telegram', { + title: 'Telegram Bot - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + botConfigured: telegramService.isEnabled + }); +}); + +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + const testMessage = `🤖 Тест Telegram бота\n\n` + + `✅ Соединение успешно установлено!\n` + + `⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` + + `🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` + + `Бот готов к отправке уведомлений! 🚀`; + + await telegramService.sendMessage(testMessage); + + res.json({ + success: true, + message: 'Test message sent successfully!', + botInfo: result.bot + }); + } else { + res.status(400).json({ + success: false, + message: result.message || 'Failed to connect to Telegram bot' + }); + } + } catch (error) { + console.error('Telegram test error:', error); + res.status(500).json({ + success: false, + message: 'Error testing Telegram bot' + }); + } +}); + +router.post('/telegram/send', requireAuth, async (req, res) => { + try { + const { message } = req.body; + + if (!message || message.trim().length === 0) { + return res.status(400).json({ + success: false, + message: 'Message is required' + }); + } + + const telegramService = require('../services/telegram'); + const result = await telegramService.sendMessage(message); + + if (result.success) { + res.json({ + success: true, + message: 'Message sent successfully!' + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to send message' + }); + } + } catch (error) { + console.error('Send Telegram message error:', error); + res.status(500).json({ + success: false, + message: 'Error sending message' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251022195039.js b/.history/routes/admin_20251022195039.js new file mode 100644 index 0000000..bb3e05f --- /dev/null +++ b/.history/routes/admin_20251022195039.js @@ -0,0 +1,1426 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings, Banner } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner management +router.get('/banners', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [banners, total] = await Promise.all([ + Banner.findAll({ + order: [['order', 'ASC'], ['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Banner.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/banners/list', { + title: 'Banner Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banners, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Banner list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banners' + }); + } +}); + +// Add banner +router.get('/banners/add', requireAuth, (req, res) => { + res.render('admin/banners/add', { + title: 'Add Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); +}); + +// Create banner +router.post('/banners/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive = true, + startDate, + endDate, + targetAudience = 'all', + backgroundColor, + textColor, + animation = 'none' + } = req.body; + + const banner = await Banner.create({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience, + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation + }); + + res.json({ + success: true, + message: '배너가 성공적으로 생성되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner creation error:', error); + res.status(500).json({ + success: false, + message: '배너 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit banner +router.get('/banners/edit/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Banner not found' + }); + } + + res.render('admin/banners/edit', { + title: 'Edit Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banner, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); + } catch (error) { + console.error('Banner edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner' + }); + } +}); + +// Update banner +router.put('/banners/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const banner = await Banner.findByPk(req.params.id); + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive, + startDate, + endDate, + targetAudience, + backgroundColor, + textColor, + animation + } = req.body; + + await banner.update({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience: targetAudience || 'all', + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation: animation || 'none' + }); + + res.json({ + success: true, + message: '배너가 성공적으로 업데이트되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner update error:', error); + res.status(500).json({ + success: false, + message: '배너 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete banner +router.delete('/banners/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.destroy(); + + res.json({ + success: true, + message: '배너가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Banner deletion error:', error); + res.status(500).json({ + success: false, + message: '배너 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle banner active status +router.patch('/banners/:id/toggle-active', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const newStatus = !banner.isActive; + await banner.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `배너가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Banner toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Record banner click +router.post('/banners/:id/click', async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.recordClick(); + + res.json({ + success: true, + clickCount: banner.clickCount + }); + } catch (error) { + console.error('Banner click record error:', error); + res.status(500).json({ + success: false, + message: '클릭 기록 중 오류가 발생했습니다' + }); + } +}); + +// Banner Editor (legacy route) +router.get('/banner-editor', requireAuth, async (req, res) => { + res.redirect('/admin/banners'); +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); +}); + +// Create portfolio item +router.post('/portfolio/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('technologies').isArray({ min: 1 }).withMessage('최소 한 개의 기술을 입력해주세요'), +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished = false, + featured = false + } = req.body; + + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) ? new Date() : null, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 생성되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Update portfolio item +router.put('/portfolio/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished, + featured + } = req.body; + + // Update portfolio + await portfolio.update({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 업데이트되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete portfolio item +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + await portfolio.destroy(); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle portfolio publish status +router.patch('/portfolio/:id/toggle-publish', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const newStatus = !portfolio.isPublished; + await portfolio.update({ + isPublished: newStatus, + publishedAt: newStatus && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: newStatus ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: `포트폴리오가 ${newStatus ? '게시' : '비공개'}되었습니다`, + isPublished: newStatus + }); + } catch (error) { + console.error('Portfolio toggle publish error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); +}); + +// Create service +router.post('/services/add', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive = true, + featured = false + } = req.body; + + const service = await Service.create({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 생성되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service creation error:', error); + res.status(500).json({ + success: false, + message: '서비스 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['id', 'title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Update service +router.put('/services/:id', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const service = await Service.findByPk(req.params.id); + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive, + featured + } = req.body; + + await service.update({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 업데이트되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service update error:', error); + res.status(500).json({ + success: false, + message: '서비스 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete service +router.delete('/services/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + await service.destroy(); + + res.json({ + success: true, + message: '서비스가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Service deletion error:', error); + res.status(500).json({ + success: false, + message: '서비스 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle service active status +router.patch('/services/:id/toggle-active', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const newStatus = !service.isActive; + await service.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `서비스가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Service toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Telegram bot configuration and testing +router.get('/telegram', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + + // Get bot info and available chats if token is configured + let botInfo = null; + let availableChats = []; + + if (telegramService.botToken) { + const result = await telegramService.getBotInfo(); + if (result.success) { + botInfo = result.bot; + availableChats = telegramService.getAvailableChats(); + } + } + + res.render('admin/telegram', { + title: 'Telegram Bot - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + botConfigured: telegramService.isEnabled, + botToken: telegramService.botToken || '', + chatId: telegramService.chatId || '', + botInfo, + availableChats + }); + } catch (error) { + console.error('Telegram page error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading Telegram settings' + }); + } +}); + +// Update bot token +router.post('/telegram/configure', requireAuth, [ + body('botToken').notEmpty().withMessage('Bot token is required'), + body('chatId').optional().isNumeric().withMessage('Chat ID must be numeric') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { botToken, chatId } = req.body; + const telegramService = require('../services/telegram'); + + // Update bot token + const result = await telegramService.updateBotToken(botToken); + + if (result.success) { + // Update chat ID if provided + if (chatId) { + telegramService.updateChatId(parseInt(chatId)); + } + + // Update environment variables (in production, this should update a config file) + process.env.TELEGRAM_BOT_TOKEN = botToken; + if (chatId) { + process.env.TELEGRAM_CHAT_ID = chatId; + } + + res.json({ + success: true, + message: 'Telegram bot configured successfully', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to configure bot' + }); + } + } catch (error) { + console.error('Configure Telegram bot error:', error); + res.status(500).json({ + success: false, + message: 'Error configuring Telegram bot' + }); + } +}); + +// Get bot info and discover chats +router.get('/telegram/info', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + res.json({ + success: true, + botInfo: result.bot, + availableChats: result.availableChats || [], + isConfigured: telegramService.isEnabled + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to get bot info' + }); + } + } catch (error) { + console.error('Get Telegram info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting bot information' + }); + } +}); + +// Get chat information +router.get('/telegram/chat/:chatId', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.getChat(req.params.chatId); + + if (result.success) { + res.json({ + success: true, + chat: result.chat + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to get chat info' + }); + } + } catch (error) { + console.error('Get chat info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting chat information' + }); + } +}); + +// Test connection +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + const testMessage = `🤖 Тест Telegram бота\n\n` + + `✅ Соединение успешно установлено!\n` + + `🤖 Бот: @${result.bot.username} (${result.bot.first_name})\n` + + `🆔 ID бота: ${result.bot.id}\n` + + `⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` + + `🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` + + `Бот готов к отправке уведомлений! 🚀`; + + const sendResult = await telegramService.sendMessage(testMessage); + + if (sendResult.success) { + res.json({ + success: true, + message: 'Test message sent successfully!', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: 'Bot connection successful, but failed to send test message: ' + (sendResult.error || sendResult.message) + }); + } + } else { + res.status(400).json({ + success: false, + message: result.message || result.error || 'Failed to connect to Telegram bot' + }); + } + } catch (error) { + console.error('Telegram test error:', error); + res.status(500).json({ + success: false, + message: 'Error testing Telegram bot' + }); + } +}); + +// Send custom message +router.post('/telegram/send', requireAuth, [ + body('message').notEmpty().withMessage('Message is required'), + body('chatIds').optional().isArray().withMessage('Chat IDs must be an array'), + body('parseMode').optional().isIn(['HTML', 'Markdown', 'MarkdownV2']).withMessage('Invalid parse mode') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { + message, + chatIds = [], + parseMode = 'HTML', + disableWebPagePreview = false, + disableNotification = false + } = req.body; + + const telegramService = require('../services/telegram'); + + let result; + if (chatIds.length > 0) { + // Send to multiple chats + result = await telegramService.sendCustomMessage({ + text: message, + chatIds: chatIds.map(id => parseInt(id)), + parseMode, + disableWebPagePreview, + disableNotification + }); + + res.json({ + success: result.success, + message: result.success ? + `Message sent to ${result.totalSent} chat(s). ${result.totalFailed} failed.` : + 'Failed to send message', + results: result.results || [], + errors: result.errors || [] + }); + } else { + // Send to default chat + result = await telegramService.sendMessage(message, { + parse_mode: parseMode, + disable_web_page_preview: disableWebPagePreview, + disable_notification: disableNotification + }); + + if (result.success) { + res.json({ + success: true, + message: 'Message sent successfully!' + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to send message' + }); + } + } + } catch (error) { + console.error('Send Telegram message error:', error); + res.status(500).json({ + success: false, + message: 'Error sending message' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251022195541.js b/.history/routes/admin_20251022195541.js new file mode 100644 index 0000000..52890fe --- /dev/null +++ b/.history/routes/admin_20251022195541.js @@ -0,0 +1,1440 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings, Banner } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner management +router.get('/banners', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [banners, total] = await Promise.all([ + Banner.findAll({ + order: [['order', 'ASC'], ['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Banner.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/banners/list', { + title: 'Banner Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banners, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Banner list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banners' + }); + } +}); + +// Add banner +router.get('/banners/add', requireAuth, (req, res) => { + res.render('admin/banners/add', { + title: 'Add Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); +}); + +// Create banner +router.post('/banners/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive = true, + startDate, + endDate, + targetAudience = 'all', + backgroundColor, + textColor, + animation = 'none' + } = req.body; + + const banner = await Banner.create({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience, + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation + }); + + res.json({ + success: true, + message: '배너가 성공적으로 생성되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner creation error:', error); + res.status(500).json({ + success: false, + message: '배너 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit banner +router.get('/banners/edit/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Banner not found' + }); + } + + res.render('admin/banners/edit', { + title: 'Edit Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banner, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); + } catch (error) { + console.error('Banner edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner' + }); + } +}); + +// Update banner +router.put('/banners/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const banner = await Banner.findByPk(req.params.id); + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive, + startDate, + endDate, + targetAudience, + backgroundColor, + textColor, + animation + } = req.body; + + await banner.update({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience: targetAudience || 'all', + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation: animation || 'none' + }); + + res.json({ + success: true, + message: '배너가 성공적으로 업데이트되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner update error:', error); + res.status(500).json({ + success: false, + message: '배너 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete banner +router.delete('/banners/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.destroy(); + + res.json({ + success: true, + message: '배너가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Banner deletion error:', error); + res.status(500).json({ + success: false, + message: '배너 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle banner active status +router.patch('/banners/:id/toggle-active', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const newStatus = !banner.isActive; + await banner.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `배너가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Banner toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Record banner click +router.post('/banners/:id/click', async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.recordClick(); + + res.json({ + success: true, + clickCount: banner.clickCount + }); + } catch (error) { + console.error('Banner click record error:', error); + res.status(500).json({ + success: false, + message: '클릭 기록 중 오류가 발생했습니다' + }); + } +}); + +// Banner Editor (legacy route) +router.get('/banner-editor', requireAuth, async (req, res) => { + res.redirect('/admin/banners'); +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Utility function for category names +const getCategoryName = (category) => { + const categoryNames = { + 'web-development': 'Веб-разработка', + 'mobile-app': 'Мобильные приложения', + 'ui-ux-design': 'UI/UX дизайн', + 'e-commerce': 'Электронная коммерция', + 'enterprise': 'Корпоративное ПО', + 'other': 'Другое' + }; + return categoryNames[category] || category; +}; + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ], + getCategoryName: getCategoryName + }); +}); + +// Create portfolio item +router.post('/portfolio/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('technologies').isArray({ min: 1 }).withMessage('최소 한 개의 기술을 입력해주세요'), +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished = false, + featured = false + } = req.body; + + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) ? new Date() : null, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 생성되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Update portfolio item +router.put('/portfolio/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished, + featured + } = req.body; + + // Update portfolio + await portfolio.update({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 업데이트되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete portfolio item +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + await portfolio.destroy(); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle portfolio publish status +router.patch('/portfolio/:id/toggle-publish', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const newStatus = !portfolio.isPublished; + await portfolio.update({ + isPublished: newStatus, + publishedAt: newStatus && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: newStatus ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: `포트폴리오가 ${newStatus ? '게시' : '비공개'}되었습니다`, + isPublished: newStatus + }); + } catch (error) { + console.error('Portfolio toggle publish error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); +}); + +// Create service +router.post('/services/add', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive = true, + featured = false + } = req.body; + + const service = await Service.create({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 생성되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service creation error:', error); + res.status(500).json({ + success: false, + message: '서비스 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['id', 'title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Update service +router.put('/services/:id', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const service = await Service.findByPk(req.params.id); + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive, + featured + } = req.body; + + await service.update({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 업데이트되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service update error:', error); + res.status(500).json({ + success: false, + message: '서비스 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete service +router.delete('/services/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + await service.destroy(); + + res.json({ + success: true, + message: '서비스가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Service deletion error:', error); + res.status(500).json({ + success: false, + message: '서비스 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle service active status +router.patch('/services/:id/toggle-active', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const newStatus = !service.isActive; + await service.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `서비스가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Service toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Telegram bot configuration and testing +router.get('/telegram', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + + // Get bot info and available chats if token is configured + let botInfo = null; + let availableChats = []; + + if (telegramService.botToken) { + const result = await telegramService.getBotInfo(); + if (result.success) { + botInfo = result.bot; + availableChats = telegramService.getAvailableChats(); + } + } + + res.render('admin/telegram', { + title: 'Telegram Bot - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + botConfigured: telegramService.isEnabled, + botToken: telegramService.botToken || '', + chatId: telegramService.chatId || '', + botInfo, + availableChats + }); + } catch (error) { + console.error('Telegram page error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading Telegram settings' + }); + } +}); + +// Update bot token +router.post('/telegram/configure', requireAuth, [ + body('botToken').notEmpty().withMessage('Bot token is required'), + body('chatId').optional().isNumeric().withMessage('Chat ID must be numeric') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { botToken, chatId } = req.body; + const telegramService = require('../services/telegram'); + + // Update bot token + const result = await telegramService.updateBotToken(botToken); + + if (result.success) { + // Update chat ID if provided + if (chatId) { + telegramService.updateChatId(parseInt(chatId)); + } + + // Update environment variables (in production, this should update a config file) + process.env.TELEGRAM_BOT_TOKEN = botToken; + if (chatId) { + process.env.TELEGRAM_CHAT_ID = chatId; + } + + res.json({ + success: true, + message: 'Telegram bot configured successfully', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to configure bot' + }); + } + } catch (error) { + console.error('Configure Telegram bot error:', error); + res.status(500).json({ + success: false, + message: 'Error configuring Telegram bot' + }); + } +}); + +// Get bot info and discover chats +router.get('/telegram/info', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + res.json({ + success: true, + botInfo: result.bot, + availableChats: result.availableChats || [], + isConfigured: telegramService.isEnabled + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to get bot info' + }); + } + } catch (error) { + console.error('Get Telegram info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting bot information' + }); + } +}); + +// Get chat information +router.get('/telegram/chat/:chatId', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.getChat(req.params.chatId); + + if (result.success) { + res.json({ + success: true, + chat: result.chat + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to get chat info' + }); + } + } catch (error) { + console.error('Get chat info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting chat information' + }); + } +}); + +// Test connection +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + const testMessage = `🤖 Тест Telegram бота\n\n` + + `✅ Соединение успешно установлено!\n` + + `🤖 Бот: @${result.bot.username} (${result.bot.first_name})\n` + + `🆔 ID бота: ${result.bot.id}\n` + + `⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` + + `🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` + + `Бот готов к отправке уведомлений! 🚀`; + + const sendResult = await telegramService.sendMessage(testMessage); + + if (sendResult.success) { + res.json({ + success: true, + message: 'Test message sent successfully!', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: 'Bot connection successful, but failed to send test message: ' + (sendResult.error || sendResult.message) + }); + } + } else { + res.status(400).json({ + success: false, + message: result.message || result.error || 'Failed to connect to Telegram bot' + }); + } + } catch (error) { + console.error('Telegram test error:', error); + res.status(500).json({ + success: false, + message: 'Error testing Telegram bot' + }); + } +}); + +// Send custom message +router.post('/telegram/send', requireAuth, [ + body('message').notEmpty().withMessage('Message is required'), + body('chatIds').optional().isArray().withMessage('Chat IDs must be an array'), + body('parseMode').optional().isIn(['HTML', 'Markdown', 'MarkdownV2']).withMessage('Invalid parse mode') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { + message, + chatIds = [], + parseMode = 'HTML', + disableWebPagePreview = false, + disableNotification = false + } = req.body; + + const telegramService = require('../services/telegram'); + + let result; + if (chatIds.length > 0) { + // Send to multiple chats + result = await telegramService.sendCustomMessage({ + text: message, + chatIds: chatIds.map(id => parseInt(id)), + parseMode, + disableWebPagePreview, + disableNotification + }); + + res.json({ + success: result.success, + message: result.success ? + `Message sent to ${result.totalSent} chat(s). ${result.totalFailed} failed.` : + 'Failed to send message', + results: result.results || [], + errors: result.errors || [] + }); + } else { + // Send to default chat + result = await telegramService.sendMessage(message, { + parse_mode: parseMode, + disable_web_page_preview: disableWebPagePreview, + disable_notification: disableNotification + }); + + if (result.success) { + res.json({ + success: true, + message: 'Message sent successfully!' + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to send message' + }); + } + } + } catch (error) { + console.error('Send Telegram message error:', error); + res.status(500).json({ + success: false, + message: 'Error sending message' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251022195905.js b/.history/routes/admin_20251022195905.js new file mode 100644 index 0000000..52890fe --- /dev/null +++ b/.history/routes/admin_20251022195905.js @@ -0,0 +1,1440 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings, Banner } = require('../models'); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner management +router.get('/banners', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [banners, total] = await Promise.all([ + Banner.findAll({ + order: [['order', 'ASC'], ['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Banner.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/banners/list', { + title: 'Banner Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banners, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Banner list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banners' + }); + } +}); + +// Add banner +router.get('/banners/add', requireAuth, (req, res) => { + res.render('admin/banners/add', { + title: 'Add Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); +}); + +// Create banner +router.post('/banners/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive = true, + startDate, + endDate, + targetAudience = 'all', + backgroundColor, + textColor, + animation = 'none' + } = req.body; + + const banner = await Banner.create({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience, + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation + }); + + res.json({ + success: true, + message: '배너가 성공적으로 생성되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner creation error:', error); + res.status(500).json({ + success: false, + message: '배너 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit banner +router.get('/banners/edit/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Banner not found' + }); + } + + res.render('admin/banners/edit', { + title: 'Edit Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banner, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); + } catch (error) { + console.error('Banner edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner' + }); + } +}); + +// Update banner +router.put('/banners/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const banner = await Banner.findByPk(req.params.id); + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive, + startDate, + endDate, + targetAudience, + backgroundColor, + textColor, + animation + } = req.body; + + await banner.update({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience: targetAudience || 'all', + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation: animation || 'none' + }); + + res.json({ + success: true, + message: '배너가 성공적으로 업데이트되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner update error:', error); + res.status(500).json({ + success: false, + message: '배너 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete banner +router.delete('/banners/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.destroy(); + + res.json({ + success: true, + message: '배너가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Banner deletion error:', error); + res.status(500).json({ + success: false, + message: '배너 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle banner active status +router.patch('/banners/:id/toggle-active', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const newStatus = !banner.isActive; + await banner.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `배너가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Banner toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Record banner click +router.post('/banners/:id/click', async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.recordClick(); + + res.json({ + success: true, + clickCount: banner.clickCount + }); + } catch (error) { + console.error('Banner click record error:', error); + res.status(500).json({ + success: false, + message: '클릭 기록 중 오류가 발생했습니다' + }); + } +}); + +// Banner Editor (legacy route) +router.get('/banner-editor', requireAuth, async (req, res) => { + res.redirect('/admin/banners'); +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Utility function for category names +const getCategoryName = (category) => { + const categoryNames = { + 'web-development': 'Веб-разработка', + 'mobile-app': 'Мобильные приложения', + 'ui-ux-design': 'UI/UX дизайн', + 'e-commerce': 'Электронная коммерция', + 'enterprise': 'Корпоративное ПО', + 'other': 'Другое' + }; + return categoryNames[category] || category; +}; + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ], + getCategoryName: getCategoryName + }); +}); + +// Create portfolio item +router.post('/portfolio/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('technologies').isArray({ min: 1 }).withMessage('최소 한 개의 기술을 입력해주세요'), +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished = false, + featured = false + } = req.body; + + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) ? new Date() : null, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 생성되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Update portfolio item +router.put('/portfolio/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished, + featured + } = req.body; + + // Update portfolio + await portfolio.update({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 업데이트되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete portfolio item +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + await portfolio.destroy(); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle portfolio publish status +router.patch('/portfolio/:id/toggle-publish', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const newStatus = !portfolio.isPublished; + await portfolio.update({ + isPublished: newStatus, + publishedAt: newStatus && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: newStatus ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: `포트폴리오가 ${newStatus ? '게시' : '비공개'}되었습니다`, + isPublished: newStatus + }); + } catch (error) { + console.error('Portfolio toggle publish error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); +}); + +// Create service +router.post('/services/add', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive = true, + featured = false + } = req.body; + + const service = await Service.create({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 생성되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service creation error:', error); + res.status(500).json({ + success: false, + message: '서비스 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['id', 'title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Update service +router.put('/services/:id', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const service = await Service.findByPk(req.params.id); + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive, + featured + } = req.body; + + await service.update({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 업데이트되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service update error:', error); + res.status(500).json({ + success: false, + message: '서비스 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete service +router.delete('/services/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + await service.destroy(); + + res.json({ + success: true, + message: '서비스가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Service deletion error:', error); + res.status(500).json({ + success: false, + message: '서비스 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle service active status +router.patch('/services/:id/toggle-active', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const newStatus = !service.isActive; + await service.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `서비스가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Service toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Telegram bot configuration and testing +router.get('/telegram', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + + // Get bot info and available chats if token is configured + let botInfo = null; + let availableChats = []; + + if (telegramService.botToken) { + const result = await telegramService.getBotInfo(); + if (result.success) { + botInfo = result.bot; + availableChats = telegramService.getAvailableChats(); + } + } + + res.render('admin/telegram', { + title: 'Telegram Bot - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + botConfigured: telegramService.isEnabled, + botToken: telegramService.botToken || '', + chatId: telegramService.chatId || '', + botInfo, + availableChats + }); + } catch (error) { + console.error('Telegram page error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading Telegram settings' + }); + } +}); + +// Update bot token +router.post('/telegram/configure', requireAuth, [ + body('botToken').notEmpty().withMessage('Bot token is required'), + body('chatId').optional().isNumeric().withMessage('Chat ID must be numeric') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { botToken, chatId } = req.body; + const telegramService = require('../services/telegram'); + + // Update bot token + const result = await telegramService.updateBotToken(botToken); + + if (result.success) { + // Update chat ID if provided + if (chatId) { + telegramService.updateChatId(parseInt(chatId)); + } + + // Update environment variables (in production, this should update a config file) + process.env.TELEGRAM_BOT_TOKEN = botToken; + if (chatId) { + process.env.TELEGRAM_CHAT_ID = chatId; + } + + res.json({ + success: true, + message: 'Telegram bot configured successfully', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to configure bot' + }); + } + } catch (error) { + console.error('Configure Telegram bot error:', error); + res.status(500).json({ + success: false, + message: 'Error configuring Telegram bot' + }); + } +}); + +// Get bot info and discover chats +router.get('/telegram/info', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + res.json({ + success: true, + botInfo: result.bot, + availableChats: result.availableChats || [], + isConfigured: telegramService.isEnabled + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to get bot info' + }); + } + } catch (error) { + console.error('Get Telegram info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting bot information' + }); + } +}); + +// Get chat information +router.get('/telegram/chat/:chatId', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.getChat(req.params.chatId); + + if (result.success) { + res.json({ + success: true, + chat: result.chat + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to get chat info' + }); + } + } catch (error) { + console.error('Get chat info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting chat information' + }); + } +}); + +// Test connection +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + const testMessage = `🤖 Тест Telegram бота\n\n` + + `✅ Соединение успешно установлено!\n` + + `🤖 Бот: @${result.bot.username} (${result.bot.first_name})\n` + + `🆔 ID бота: ${result.bot.id}\n` + + `⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` + + `🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` + + `Бот готов к отправке уведомлений! 🚀`; + + const sendResult = await telegramService.sendMessage(testMessage); + + if (sendResult.success) { + res.json({ + success: true, + message: 'Test message sent successfully!', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: 'Bot connection successful, but failed to send test message: ' + (sendResult.error || sendResult.message) + }); + } + } else { + res.status(400).json({ + success: false, + message: result.message || result.error || 'Failed to connect to Telegram bot' + }); + } + } catch (error) { + console.error('Telegram test error:', error); + res.status(500).json({ + success: false, + message: 'Error testing Telegram bot' + }); + } +}); + +// Send custom message +router.post('/telegram/send', requireAuth, [ + body('message').notEmpty().withMessage('Message is required'), + body('chatIds').optional().isArray().withMessage('Chat IDs must be an array'), + body('parseMode').optional().isIn(['HTML', 'Markdown', 'MarkdownV2']).withMessage('Invalid parse mode') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { + message, + chatIds = [], + parseMode = 'HTML', + disableWebPagePreview = false, + disableNotification = false + } = req.body; + + const telegramService = require('../services/telegram'); + + let result; + if (chatIds.length > 0) { + // Send to multiple chats + result = await telegramService.sendCustomMessage({ + text: message, + chatIds: chatIds.map(id => parseInt(id)), + parseMode, + disableWebPagePreview, + disableNotification + }); + + res.json({ + success: result.success, + message: result.success ? + `Message sent to ${result.totalSent} chat(s). ${result.totalFailed} failed.` : + 'Failed to send message', + results: result.results || [], + errors: result.errors || [] + }); + } else { + // Send to default chat + result = await telegramService.sendMessage(message, { + parse_mode: parseMode, + disable_web_page_preview: disableWebPagePreview, + disable_notification: disableNotification + }); + + if (result.success) { + res.json({ + success: true, + message: 'Message sent successfully!' + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to send message' + }); + } + } + } catch (error) { + console.error('Send Telegram message error:', error); + res.status(500).json({ + success: false, + message: 'Error sending message' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251022205401.js b/.history/routes/admin_20251022205401.js new file mode 100644 index 0000000..f9ff0b1 --- /dev/null +++ b/.history/routes/admin_20251022205401.js @@ -0,0 +1,1458 @@ +const express = require('express'); +const router = express.Router(); +const multer = require('multer'); +const sharp = require('sharp'); +const path = require('path'); +const fs = require('fs').promises; +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings, Banner } = require('../models'); + +// Configure multer for file uploads +const storage = multer.memoryStorage(); // Use memory storage to process with sharp +const upload = multer({ + storage, + limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit + fileFilter: (req, file, cb) => { + if (file.mimetype.startsWith('image/')) { + cb(null, true); + } else { + cb(new Error('Only image files are allowed'), false); + } + } +}); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner management +router.get('/banners', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [banners, total] = await Promise.all([ + Banner.findAll({ + order: [['order', 'ASC'], ['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Banner.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/banners/list', { + title: 'Banner Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banners, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Banner list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banners' + }); + } +}); + +// Add banner +router.get('/banners/add', requireAuth, (req, res) => { + res.render('admin/banners/add', { + title: 'Add Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); +}); + +// Create banner +router.post('/banners/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive = true, + startDate, + endDate, + targetAudience = 'all', + backgroundColor, + textColor, + animation = 'none' + } = req.body; + + const banner = await Banner.create({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience, + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation + }); + + res.json({ + success: true, + message: '배너가 성공적으로 생성되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner creation error:', error); + res.status(500).json({ + success: false, + message: '배너 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit banner +router.get('/banners/edit/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Banner not found' + }); + } + + res.render('admin/banners/edit', { + title: 'Edit Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banner, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); + } catch (error) { + console.error('Banner edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner' + }); + } +}); + +// Update banner +router.put('/banners/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const banner = await Banner.findByPk(req.params.id); + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive, + startDate, + endDate, + targetAudience, + backgroundColor, + textColor, + animation + } = req.body; + + await banner.update({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience: targetAudience || 'all', + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation: animation || 'none' + }); + + res.json({ + success: true, + message: '배너가 성공적으로 업데이트되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner update error:', error); + res.status(500).json({ + success: false, + message: '배너 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete banner +router.delete('/banners/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.destroy(); + + res.json({ + success: true, + message: '배너가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Banner deletion error:', error); + res.status(500).json({ + success: false, + message: '배너 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle banner active status +router.patch('/banners/:id/toggle-active', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const newStatus = !banner.isActive; + await banner.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `배너가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Banner toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Record banner click +router.post('/banners/:id/click', async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.recordClick(); + + res.json({ + success: true, + clickCount: banner.clickCount + }); + } catch (error) { + console.error('Banner click record error:', error); + res.status(500).json({ + success: false, + message: '클릭 기록 중 오류가 발생했습니다' + }); + } +}); + +// Banner Editor (legacy route) +router.get('/banner-editor', requireAuth, async (req, res) => { + res.redirect('/admin/banners'); +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Utility function for category names +const getCategoryName = (category) => { + const categoryNames = { + 'web-development': 'Веб-разработка', + 'mobile-app': 'Мобильные приложения', + 'ui-ux-design': 'UI/UX дизайн', + 'e-commerce': 'Электронная коммерция', + 'enterprise': 'Корпоративное ПО', + 'other': 'Другое' + }; + return categoryNames[category] || category; +}; + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ], + getCategoryName: getCategoryName + }); +}); + +// Create portfolio item +router.post('/portfolio/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('technologies').isArray({ min: 1 }).withMessage('최소 한 개의 기술을 입력해주세요'), +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished = false, + featured = false + } = req.body; + + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) ? new Date() : null, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 생성되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Update portfolio item +router.put('/portfolio/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished, + featured + } = req.body; + + // Update portfolio + await portfolio.update({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 업데이트되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete portfolio item +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + await portfolio.destroy(); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle portfolio publish status +router.patch('/portfolio/:id/toggle-publish', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const newStatus = !portfolio.isPublished; + await portfolio.update({ + isPublished: newStatus, + publishedAt: newStatus && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: newStatus ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: `포트폴리오가 ${newStatus ? '게시' : '비공개'}되었습니다`, + isPublished: newStatus + }); + } catch (error) { + console.error('Portfolio toggle publish error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); +}); + +// Create service +router.post('/services/add', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive = true, + featured = false + } = req.body; + + const service = await Service.create({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 생성되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service creation error:', error); + res.status(500).json({ + success: false, + message: '서비스 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['id', 'title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Update service +router.put('/services/:id', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const service = await Service.findByPk(req.params.id); + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive, + featured + } = req.body; + + await service.update({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 업데이트되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service update error:', error); + res.status(500).json({ + success: false, + message: '서비스 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete service +router.delete('/services/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + await service.destroy(); + + res.json({ + success: true, + message: '서비스가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Service deletion error:', error); + res.status(500).json({ + success: false, + message: '서비스 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle service active status +router.patch('/services/:id/toggle-active', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const newStatus = !service.isActive; + await service.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `서비스가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Service toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Telegram bot configuration and testing +router.get('/telegram', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + + // Get bot info and available chats if token is configured + let botInfo = null; + let availableChats = []; + + if (telegramService.botToken) { + const result = await telegramService.getBotInfo(); + if (result.success) { + botInfo = result.bot; + availableChats = telegramService.getAvailableChats(); + } + } + + res.render('admin/telegram', { + title: 'Telegram Bot - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + botConfigured: telegramService.isEnabled, + botToken: telegramService.botToken || '', + chatId: telegramService.chatId || '', + botInfo, + availableChats + }); + } catch (error) { + console.error('Telegram page error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading Telegram settings' + }); + } +}); + +// Update bot token +router.post('/telegram/configure', requireAuth, [ + body('botToken').notEmpty().withMessage('Bot token is required'), + body('chatId').optional().isNumeric().withMessage('Chat ID must be numeric') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { botToken, chatId } = req.body; + const telegramService = require('../services/telegram'); + + // Update bot token + const result = await telegramService.updateBotToken(botToken); + + if (result.success) { + // Update chat ID if provided + if (chatId) { + telegramService.updateChatId(parseInt(chatId)); + } + + // Update environment variables (in production, this should update a config file) + process.env.TELEGRAM_BOT_TOKEN = botToken; + if (chatId) { + process.env.TELEGRAM_CHAT_ID = chatId; + } + + res.json({ + success: true, + message: 'Telegram bot configured successfully', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to configure bot' + }); + } + } catch (error) { + console.error('Configure Telegram bot error:', error); + res.status(500).json({ + success: false, + message: 'Error configuring Telegram bot' + }); + } +}); + +// Get bot info and discover chats +router.get('/telegram/info', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + res.json({ + success: true, + botInfo: result.bot, + availableChats: result.availableChats || [], + isConfigured: telegramService.isEnabled + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to get bot info' + }); + } + } catch (error) { + console.error('Get Telegram info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting bot information' + }); + } +}); + +// Get chat information +router.get('/telegram/chat/:chatId', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.getChat(req.params.chatId); + + if (result.success) { + res.json({ + success: true, + chat: result.chat + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to get chat info' + }); + } + } catch (error) { + console.error('Get chat info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting chat information' + }); + } +}); + +// Test connection +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + const testMessage = `🤖 Тест Telegram бота\n\n` + + `✅ Соединение успешно установлено!\n` + + `🤖 Бот: @${result.bot.username} (${result.bot.first_name})\n` + + `🆔 ID бота: ${result.bot.id}\n` + + `⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` + + `🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` + + `Бот готов к отправке уведомлений! 🚀`; + + const sendResult = await telegramService.sendMessage(testMessage); + + if (sendResult.success) { + res.json({ + success: true, + message: 'Test message sent successfully!', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: 'Bot connection successful, but failed to send test message: ' + (sendResult.error || sendResult.message) + }); + } + } else { + res.status(400).json({ + success: false, + message: result.message || result.error || 'Failed to connect to Telegram bot' + }); + } + } catch (error) { + console.error('Telegram test error:', error); + res.status(500).json({ + success: false, + message: 'Error testing Telegram bot' + }); + } +}); + +// Send custom message +router.post('/telegram/send', requireAuth, [ + body('message').notEmpty().withMessage('Message is required'), + body('chatIds').optional().isArray().withMessage('Chat IDs must be an array'), + body('parseMode').optional().isIn(['HTML', 'Markdown', 'MarkdownV2']).withMessage('Invalid parse mode') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { + message, + chatIds = [], + parseMode = 'HTML', + disableWebPagePreview = false, + disableNotification = false + } = req.body; + + const telegramService = require('../services/telegram'); + + let result; + if (chatIds.length > 0) { + // Send to multiple chats + result = await telegramService.sendCustomMessage({ + text: message, + chatIds: chatIds.map(id => parseInt(id)), + parseMode, + disableWebPagePreview, + disableNotification + }); + + res.json({ + success: result.success, + message: result.success ? + `Message sent to ${result.totalSent} chat(s). ${result.totalFailed} failed.` : + 'Failed to send message', + results: result.results || [], + errors: result.errors || [] + }); + } else { + // Send to default chat + result = await telegramService.sendMessage(message, { + parse_mode: parseMode, + disable_web_page_preview: disableWebPagePreview, + disable_notification: disableNotification + }); + + if (result.success) { + res.json({ + success: true, + message: 'Message sent successfully!' + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to send message' + }); + } + } + } catch (error) { + console.error('Send Telegram message error:', error); + res.status(500).json({ + success: false, + message: 'Error sending message' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251022205440.js b/.history/routes/admin_20251022205440.js new file mode 100644 index 0000000..5b3798a --- /dev/null +++ b/.history/routes/admin_20251022205440.js @@ -0,0 +1,1504 @@ +const express = require('express'); +const router = express.Router(); +const multer = require('multer'); +const sharp = require('sharp'); +const path = require('path'); +const fs = require('fs').promises; +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings, Banner } = require('../models'); + +// Configure multer for file uploads +const storage = multer.memoryStorage(); // Use memory storage to process with sharp +const upload = multer({ + storage, + limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit + fileFilter: (req, file, cb) => { + if (file.mimetype.startsWith('image/')) { + cb(null, true); + } else { + cb(new Error('Only image files are allowed'), false); + } + } +}); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner management +router.get('/banners', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [banners, total] = await Promise.all([ + Banner.findAll({ + order: [['order', 'ASC'], ['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Banner.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/banners/list', { + title: 'Banner Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banners, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Banner list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banners' + }); + } +}); + +// Add banner +router.get('/banners/add', requireAuth, (req, res) => { + res.render('admin/banners/add', { + title: 'Add Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); +}); + +// Create banner +router.post('/banners/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive = true, + startDate, + endDate, + targetAudience = 'all', + backgroundColor, + textColor, + animation = 'none' + } = req.body; + + const banner = await Banner.create({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience, + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation + }); + + res.json({ + success: true, + message: '배너가 성공적으로 생성되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner creation error:', error); + res.status(500).json({ + success: false, + message: '배너 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit banner +router.get('/banners/edit/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Banner not found' + }); + } + + res.render('admin/banners/edit', { + title: 'Edit Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banner, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); + } catch (error) { + console.error('Banner edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner' + }); + } +}); + +// Update banner +router.put('/banners/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const banner = await Banner.findByPk(req.params.id); + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive, + startDate, + endDate, + targetAudience, + backgroundColor, + textColor, + animation + } = req.body; + + await banner.update({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience: targetAudience || 'all', + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation: animation || 'none' + }); + + res.json({ + success: true, + message: '배너가 성공적으로 업데이트되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner update error:', error); + res.status(500).json({ + success: false, + message: '배너 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete banner +router.delete('/banners/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.destroy(); + + res.json({ + success: true, + message: '배너가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Banner deletion error:', error); + res.status(500).json({ + success: false, + message: '배너 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle banner active status +router.patch('/banners/:id/toggle-active', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const newStatus = !banner.isActive; + await banner.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `배너가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Banner toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Record banner click +router.post('/banners/:id/click', async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.recordClick(); + + res.json({ + success: true, + clickCount: banner.clickCount + }); + } catch (error) { + console.error('Banner click record error:', error); + res.status(500).json({ + success: false, + message: '클릭 기록 중 오류가 발생했습니다' + }); + } +}); + +// Banner Editor (legacy route) +router.get('/banner-editor', requireAuth, async (req, res) => { + res.redirect('/admin/banners'); +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Utility function for category names +const getCategoryName = (category) => { + const categoryNames = { + 'web-development': 'Веб-разработка', + 'mobile-app': 'Мобильные приложения', + 'ui-ux-design': 'UI/UX дизайн', + 'e-commerce': 'Электронная коммерция', + 'enterprise': 'Корпоративное ПО', + 'other': 'Другое' + }; + return categoryNames[category] || category; +}; + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ], + getCategoryName: getCategoryName + }); +}); + +// Create portfolio item +router.post('/portfolio/add', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + seoTitle, + seoDescription, + isPublished, + featured + } = req.body; + + // Validate required fields + if (!title || !shortDescription || !description || !category) { + return res.status(400).json({ + success: false, + message: 'Заполните все обязательные поля' + }); + } + + // Parse technologies from JSON string + let parsedTechnologies = []; + try { + parsedTechnologies = JSON.parse(technologies || '[]'); + } catch (e) { + parsedTechnologies = typeof technologies === 'string' ? [technologies] : []; + } + + if (parsedTechnologies.length === 0) { + return res.status(400).json({ + success: false, + message: 'Добавьте хотя бы одну технологию' + }); + } + + // Process uploaded images + const processedImages = []; + if (req.files && req.files.length > 0) { + const uploadDir = path.join(process.cwd(), 'public', 'uploads', 'portfolio'); + + // Ensure directory exists + try { + await fs.access(uploadDir); + } catch { + await fs.mkdir(uploadDir, { recursive: true }); + } + + for (const file of req.files) { + const timestamp = Date.now(); + const filename = `${timestamp}-${Math.random().toString(36).substr(2, 9)}.webp`; + const filepath = path.join(uploadDir, filename); + + // Process image with Sharp + await sharp(file.buffer) + .webp({ quality: 85 }) + .resize(1200, 800, { fit: 'inside', withoutEnlargement: true }) + .toFile(filepath); + + processedImages.push(`/uploads/portfolio/${filename}`); + } + } + + // Create SEO data + const seo = {}; + if (seoTitle) seo.title = seoTitle; + if (seoDescription) seo.description = seoDescription; + + // Create portfolio item + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + technologies: parsedTechnologies, + images: processedImages, + projectUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: isPublished === 'true' || isPublished === true, + featured: featured === 'true' || featured === true, + publishedAt: (isPublished === 'true' || isPublished === true) ? new Date() : null, + status: (isPublished === 'true' || isPublished === true) ? 'published' : 'draft', + seo: Object.keys(seo).length > 0 ? seo : null + }); + + res.json({ + success: true, + message: 'Проект успешно создан!', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category, + isPublished: portfolio.isPublished + } + }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ + success: false, + message: 'Ошибка при создании проекта: ' + error.message + }); + } +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Update portfolio item +router.put('/portfolio/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + isPublished, + featured + } = req.body; + + // Update portfolio + await portfolio.update({ + title, + shortDescription, + description, + category, + technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()), + demoUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: Boolean(isPublished), + featured: Boolean(featured), + publishedAt: Boolean(isPublished) && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: Boolean(isPublished) ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 업데이트되었습니다', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category + } + }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete portfolio item +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + await portfolio.destroy(); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle portfolio publish status +router.patch('/portfolio/:id/toggle-publish', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const newStatus = !portfolio.isPublished; + await portfolio.update({ + isPublished: newStatus, + publishedAt: newStatus && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: newStatus ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: `포트폴리오가 ${newStatus ? '게시' : '비공개'}되었습니다`, + isPublished: newStatus + }); + } catch (error) { + console.error('Portfolio toggle publish error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); +}); + +// Create service +router.post('/services/add', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive = true, + featured = false + } = req.body; + + const service = await Service.create({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 생성되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service creation error:', error); + res.status(500).json({ + success: false, + message: '서비스 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['id', 'title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Update service +router.put('/services/:id', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const service = await Service.findByPk(req.params.id); + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive, + featured + } = req.body; + + await service.update({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 업데이트되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service update error:', error); + res.status(500).json({ + success: false, + message: '서비스 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete service +router.delete('/services/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + await service.destroy(); + + res.json({ + success: true, + message: '서비스가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Service deletion error:', error); + res.status(500).json({ + success: false, + message: '서비스 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle service active status +router.patch('/services/:id/toggle-active', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const newStatus = !service.isActive; + await service.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `서비스가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Service toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Telegram bot configuration and testing +router.get('/telegram', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + + // Get bot info and available chats if token is configured + let botInfo = null; + let availableChats = []; + + if (telegramService.botToken) { + const result = await telegramService.getBotInfo(); + if (result.success) { + botInfo = result.bot; + availableChats = telegramService.getAvailableChats(); + } + } + + res.render('admin/telegram', { + title: 'Telegram Bot - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + botConfigured: telegramService.isEnabled, + botToken: telegramService.botToken || '', + chatId: telegramService.chatId || '', + botInfo, + availableChats + }); + } catch (error) { + console.error('Telegram page error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading Telegram settings' + }); + } +}); + +// Update bot token +router.post('/telegram/configure', requireAuth, [ + body('botToken').notEmpty().withMessage('Bot token is required'), + body('chatId').optional().isNumeric().withMessage('Chat ID must be numeric') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { botToken, chatId } = req.body; + const telegramService = require('../services/telegram'); + + // Update bot token + const result = await telegramService.updateBotToken(botToken); + + if (result.success) { + // Update chat ID if provided + if (chatId) { + telegramService.updateChatId(parseInt(chatId)); + } + + // Update environment variables (in production, this should update a config file) + process.env.TELEGRAM_BOT_TOKEN = botToken; + if (chatId) { + process.env.TELEGRAM_CHAT_ID = chatId; + } + + res.json({ + success: true, + message: 'Telegram bot configured successfully', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to configure bot' + }); + } + } catch (error) { + console.error('Configure Telegram bot error:', error); + res.status(500).json({ + success: false, + message: 'Error configuring Telegram bot' + }); + } +}); + +// Get bot info and discover chats +router.get('/telegram/info', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + res.json({ + success: true, + botInfo: result.bot, + availableChats: result.availableChats || [], + isConfigured: telegramService.isEnabled + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to get bot info' + }); + } + } catch (error) { + console.error('Get Telegram info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting bot information' + }); + } +}); + +// Get chat information +router.get('/telegram/chat/:chatId', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.getChat(req.params.chatId); + + if (result.success) { + res.json({ + success: true, + chat: result.chat + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to get chat info' + }); + } + } catch (error) { + console.error('Get chat info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting chat information' + }); + } +}); + +// Test connection +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + const testMessage = `🤖 Тест Telegram бота\n\n` + + `✅ Соединение успешно установлено!\n` + + `🤖 Бот: @${result.bot.username} (${result.bot.first_name})\n` + + `🆔 ID бота: ${result.bot.id}\n` + + `⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` + + `🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` + + `Бот готов к отправке уведомлений! 🚀`; + + const sendResult = await telegramService.sendMessage(testMessage); + + if (sendResult.success) { + res.json({ + success: true, + message: 'Test message sent successfully!', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: 'Bot connection successful, but failed to send test message: ' + (sendResult.error || sendResult.message) + }); + } + } else { + res.status(400).json({ + success: false, + message: result.message || result.error || 'Failed to connect to Telegram bot' + }); + } + } catch (error) { + console.error('Telegram test error:', error); + res.status(500).json({ + success: false, + message: 'Error testing Telegram bot' + }); + } +}); + +// Send custom message +router.post('/telegram/send', requireAuth, [ + body('message').notEmpty().withMessage('Message is required'), + body('chatIds').optional().isArray().withMessage('Chat IDs must be an array'), + body('parseMode').optional().isIn(['HTML', 'Markdown', 'MarkdownV2']).withMessage('Invalid parse mode') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { + message, + chatIds = [], + parseMode = 'HTML', + disableWebPagePreview = false, + disableNotification = false + } = req.body; + + const telegramService = require('../services/telegram'); + + let result; + if (chatIds.length > 0) { + // Send to multiple chats + result = await telegramService.sendCustomMessage({ + text: message, + chatIds: chatIds.map(id => parseInt(id)), + parseMode, + disableWebPagePreview, + disableNotification + }); + + res.json({ + success: result.success, + message: result.success ? + `Message sent to ${result.totalSent} chat(s). ${result.totalFailed} failed.` : + 'Failed to send message', + results: result.results || [], + errors: result.errors || [] + }); + } else { + // Send to default chat + result = await telegramService.sendMessage(message, { + parse_mode: parseMode, + disable_web_page_preview: disableWebPagePreview, + disable_notification: disableNotification + }); + + if (result.success) { + res.json({ + success: true, + message: 'Message sent successfully!' + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to send message' + }); + } + } + } catch (error) { + console.error('Send Telegram message error:', error); + res.status(500).json({ + success: false, + message: 'Error sending message' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251022205520.js b/.history/routes/admin_20251022205520.js new file mode 100644 index 0000000..c4f4fe6 --- /dev/null +++ b/.history/routes/admin_20251022205520.js @@ -0,0 +1,1552 @@ +const express = require('express'); +const router = express.Router(); +const multer = require('multer'); +const sharp = require('sharp'); +const path = require('path'); +const fs = require('fs').promises; +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings, Banner } = require('../models'); + +// Configure multer for file uploads +const storage = multer.memoryStorage(); // Use memory storage to process with sharp +const upload = multer({ + storage, + limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit + fileFilter: (req, file, cb) => { + if (file.mimetype.startsWith('image/')) { + cb(null, true); + } else { + cb(new Error('Only image files are allowed'), false); + } + } +}); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner management +router.get('/banners', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [banners, total] = await Promise.all([ + Banner.findAll({ + order: [['order', 'ASC'], ['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Banner.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/banners/list', { + title: 'Banner Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banners, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Banner list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banners' + }); + } +}); + +// Add banner +router.get('/banners/add', requireAuth, (req, res) => { + res.render('admin/banners/add', { + title: 'Add Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); +}); + +// Create banner +router.post('/banners/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive = true, + startDate, + endDate, + targetAudience = 'all', + backgroundColor, + textColor, + animation = 'none' + } = req.body; + + const banner = await Banner.create({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience, + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation + }); + + res.json({ + success: true, + message: '배너가 성공적으로 생성되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner creation error:', error); + res.status(500).json({ + success: false, + message: '배너 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit banner +router.get('/banners/edit/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Banner not found' + }); + } + + res.render('admin/banners/edit', { + title: 'Edit Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banner, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); + } catch (error) { + console.error('Banner edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner' + }); + } +}); + +// Update banner +router.put('/banners/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const banner = await Banner.findByPk(req.params.id); + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive, + startDate, + endDate, + targetAudience, + backgroundColor, + textColor, + animation + } = req.body; + + await banner.update({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience: targetAudience || 'all', + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation: animation || 'none' + }); + + res.json({ + success: true, + message: '배너가 성공적으로 업데이트되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner update error:', error); + res.status(500).json({ + success: false, + message: '배너 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete banner +router.delete('/banners/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.destroy(); + + res.json({ + success: true, + message: '배너가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Banner deletion error:', error); + res.status(500).json({ + success: false, + message: '배너 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle banner active status +router.patch('/banners/:id/toggle-active', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const newStatus = !banner.isActive; + await banner.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `배너가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Banner toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Record banner click +router.post('/banners/:id/click', async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.recordClick(); + + res.json({ + success: true, + clickCount: banner.clickCount + }); + } catch (error) { + console.error('Banner click record error:', error); + res.status(500).json({ + success: false, + message: '클릭 기록 중 오류가 발생했습니다' + }); + } +}); + +// Banner Editor (legacy route) +router.get('/banner-editor', requireAuth, async (req, res) => { + res.redirect('/admin/banners'); +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Utility function for category names +const getCategoryName = (category) => { + const categoryNames = { + 'web-development': 'Веб-разработка', + 'mobile-app': 'Мобильные приложения', + 'ui-ux-design': 'UI/UX дизайн', + 'e-commerce': 'Электронная коммерция', + 'enterprise': 'Корпоративное ПО', + 'other': 'Другое' + }; + return categoryNames[category] || category; +}; + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ], + getCategoryName: getCategoryName + }); +}); + +// Create portfolio item +router.post('/portfolio/add', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + seoTitle, + seoDescription, + isPublished, + featured + } = req.body; + + // Validate required fields + if (!title || !shortDescription || !description || !category) { + return res.status(400).json({ + success: false, + message: 'Заполните все обязательные поля' + }); + } + + // Parse technologies from JSON string + let parsedTechnologies = []; + try { + parsedTechnologies = JSON.parse(technologies || '[]'); + } catch (e) { + parsedTechnologies = typeof technologies === 'string' ? [technologies] : []; + } + + if (parsedTechnologies.length === 0) { + return res.status(400).json({ + success: false, + message: 'Добавьте хотя бы одну технологию' + }); + } + + // Process uploaded images + const processedImages = []; + if (req.files && req.files.length > 0) { + const uploadDir = path.join(process.cwd(), 'public', 'uploads', 'portfolio'); + + // Ensure directory exists + try { + await fs.access(uploadDir); + } catch { + await fs.mkdir(uploadDir, { recursive: true }); + } + + for (const file of req.files) { + const timestamp = Date.now(); + const filename = `${timestamp}-${Math.random().toString(36).substr(2, 9)}.webp`; + const filepath = path.join(uploadDir, filename); + + // Process image with Sharp + await sharp(file.buffer) + .webp({ quality: 85 }) + .resize(1200, 800, { fit: 'inside', withoutEnlargement: true }) + .toFile(filepath); + + processedImages.push(`/uploads/portfolio/${filename}`); + } + } + + // Create SEO data + const seo = {}; + if (seoTitle) seo.title = seoTitle; + if (seoDescription) seo.description = seoDescription; + + // Create portfolio item + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + technologies: parsedTechnologies, + images: processedImages, + projectUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: isPublished === 'true' || isPublished === true, + featured: featured === 'true' || featured === true, + publishedAt: (isPublished === 'true' || isPublished === true) ? new Date() : null, + status: (isPublished === 'true' || isPublished === true) ? 'published' : 'draft', + seo: Object.keys(seo).length > 0 ? seo : null + }); + + res.json({ + success: true, + message: 'Проект успешно создан!', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category, + isPublished: portfolio.isPublished + } + }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ + success: false, + message: 'Ошибка при создании проекта: ' + error.message + }); + } +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Update portfolio item +router.put('/portfolio/:id', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ + success: false, + message: 'Проект не найден' + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + seoTitle, + seoDescription, + isPublished, + featured, + existingImages + } = req.body; + + // Validate required fields + if (!title || !shortDescription || !description || !category) { + return res.status(400).json({ + success: false, + message: 'Заполните все обязательные поля' + }); + } + + // Parse technologies from JSON string + let parsedTechnologies = []; + try { + parsedTechnologies = JSON.parse(technologies || '[]'); + } catch (e) { + parsedTechnologies = typeof technologies === 'string' ? [technologies] : []; + } + + // Handle existing images + let finalImages = []; + try { + const existing = JSON.parse(existingImages || '[]'); + finalImages = Array.isArray(existing) ? existing : []; + } catch (e) { + finalImages = portfolio.images || []; + } + + // Process new uploaded images + if (req.files && req.files.length > 0) { + const uploadDir = path.join(process.cwd(), 'public', 'uploads', 'portfolio'); + + // Ensure directory exists + try { + await fs.access(uploadDir); + } catch { + await fs.mkdir(uploadDir, { recursive: true }); + } + + for (const file of req.files) { + const timestamp = Date.now(); + const filename = `${timestamp}-${Math.random().toString(36).substr(2, 9)}.webp`; + const filepath = path.join(uploadDir, filename); + + // Process image with Sharp + await sharp(file.buffer) + .webp({ quality: 85 }) + .resize(1200, 800, { fit: 'inside', withoutEnlargement: true }) + .toFile(filepath); + + finalImages.push(`/uploads/portfolio/${filename}`); + } + } + + // Create SEO data + const seo = portfolio.seo || {}; + if (seoTitle) seo.title = seoTitle; + if (seoDescription) seo.description = seoDescription; + + // Update portfolio + await portfolio.update({ + title, + shortDescription, + description, + category, + technologies: parsedTechnologies, + images: finalImages, + projectUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: isPublished === 'true' || isPublished === true, + featured: featured === 'true' || featured === true, + publishedAt: (isPublished === 'true' || isPublished === true) && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: (isPublished === 'true' || isPublished === true) ? 'published' : 'draft', + seo: Object.keys(seo).length > 0 ? seo : null + }); + + res.json({ + success: true, + message: 'Проект успешно обновлен!', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category, + isPublished: portfolio.isPublished + } + }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ + success: false, + message: 'Ошибка при обновлении проекта: ' + error.message + }); + } +}); + +// Delete portfolio item +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + await portfolio.destroy(); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle portfolio publish status +router.patch('/portfolio/:id/toggle-publish', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const newStatus = !portfolio.isPublished; + await portfolio.update({ + isPublished: newStatus, + publishedAt: newStatus && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: newStatus ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: `포트폴리오가 ${newStatus ? '게시' : '비공개'}되었습니다`, + isPublished: newStatus + }); + } catch (error) { + console.error('Portfolio toggle publish error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); +}); + +// Create service +router.post('/services/add', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive = true, + featured = false + } = req.body; + + const service = await Service.create({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 생성되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service creation error:', error); + res.status(500).json({ + success: false, + message: '서비스 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['id', 'title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Update service +router.put('/services/:id', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const service = await Service.findByPk(req.params.id); + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive, + featured + } = req.body; + + await service.update({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 업데이트되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service update error:', error); + res.status(500).json({ + success: false, + message: '서비스 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete service +router.delete('/services/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + await service.destroy(); + + res.json({ + success: true, + message: '서비스가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Service deletion error:', error); + res.status(500).json({ + success: false, + message: '서비스 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle service active status +router.patch('/services/:id/toggle-active', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const newStatus = !service.isActive; + await service.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `서비스가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Service toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Telegram bot configuration and testing +router.get('/telegram', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + + // Get bot info and available chats if token is configured + let botInfo = null; + let availableChats = []; + + if (telegramService.botToken) { + const result = await telegramService.getBotInfo(); + if (result.success) { + botInfo = result.bot; + availableChats = telegramService.getAvailableChats(); + } + } + + res.render('admin/telegram', { + title: 'Telegram Bot - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + botConfigured: telegramService.isEnabled, + botToken: telegramService.botToken || '', + chatId: telegramService.chatId || '', + botInfo, + availableChats + }); + } catch (error) { + console.error('Telegram page error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading Telegram settings' + }); + } +}); + +// Update bot token +router.post('/telegram/configure', requireAuth, [ + body('botToken').notEmpty().withMessage('Bot token is required'), + body('chatId').optional().isNumeric().withMessage('Chat ID must be numeric') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { botToken, chatId } = req.body; + const telegramService = require('../services/telegram'); + + // Update bot token + const result = await telegramService.updateBotToken(botToken); + + if (result.success) { + // Update chat ID if provided + if (chatId) { + telegramService.updateChatId(parseInt(chatId)); + } + + // Update environment variables (in production, this should update a config file) + process.env.TELEGRAM_BOT_TOKEN = botToken; + if (chatId) { + process.env.TELEGRAM_CHAT_ID = chatId; + } + + res.json({ + success: true, + message: 'Telegram bot configured successfully', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to configure bot' + }); + } + } catch (error) { + console.error('Configure Telegram bot error:', error); + res.status(500).json({ + success: false, + message: 'Error configuring Telegram bot' + }); + } +}); + +// Get bot info and discover chats +router.get('/telegram/info', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + res.json({ + success: true, + botInfo: result.bot, + availableChats: result.availableChats || [], + isConfigured: telegramService.isEnabled + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to get bot info' + }); + } + } catch (error) { + console.error('Get Telegram info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting bot information' + }); + } +}); + +// Get chat information +router.get('/telegram/chat/:chatId', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.getChat(req.params.chatId); + + if (result.success) { + res.json({ + success: true, + chat: result.chat + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to get chat info' + }); + } + } catch (error) { + console.error('Get chat info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting chat information' + }); + } +}); + +// Test connection +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + const testMessage = `🤖 Тест Telegram бота\n\n` + + `✅ Соединение успешно установлено!\n` + + `🤖 Бот: @${result.bot.username} (${result.bot.first_name})\n` + + `🆔 ID бота: ${result.bot.id}\n` + + `⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` + + `🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` + + `Бот готов к отправке уведомлений! 🚀`; + + const sendResult = await telegramService.sendMessage(testMessage); + + if (sendResult.success) { + res.json({ + success: true, + message: 'Test message sent successfully!', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: 'Bot connection successful, but failed to send test message: ' + (sendResult.error || sendResult.message) + }); + } + } else { + res.status(400).json({ + success: false, + message: result.message || result.error || 'Failed to connect to Telegram bot' + }); + } + } catch (error) { + console.error('Telegram test error:', error); + res.status(500).json({ + success: false, + message: 'Error testing Telegram bot' + }); + } +}); + +// Send custom message +router.post('/telegram/send', requireAuth, [ + body('message').notEmpty().withMessage('Message is required'), + body('chatIds').optional().isArray().withMessage('Chat IDs must be an array'), + body('parseMode').optional().isIn(['HTML', 'Markdown', 'MarkdownV2']).withMessage('Invalid parse mode') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { + message, + chatIds = [], + parseMode = 'HTML', + disableWebPagePreview = false, + disableNotification = false + } = req.body; + + const telegramService = require('../services/telegram'); + + let result; + if (chatIds.length > 0) { + // Send to multiple chats + result = await telegramService.sendCustomMessage({ + text: message, + chatIds: chatIds.map(id => parseInt(id)), + parseMode, + disableWebPagePreview, + disableNotification + }); + + res.json({ + success: result.success, + message: result.success ? + `Message sent to ${result.totalSent} chat(s). ${result.totalFailed} failed.` : + 'Failed to send message', + results: result.results || [], + errors: result.errors || [] + }); + } else { + // Send to default chat + result = await telegramService.sendMessage(message, { + parse_mode: parseMode, + disable_web_page_preview: disableWebPagePreview, + disable_notification: disableNotification + }); + + if (result.success) { + res.json({ + success: true, + message: 'Message sent successfully!' + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to send message' + }); + } + } + } catch (error) { + console.error('Send Telegram message error:', error); + res.status(500).json({ + success: false, + message: 'Error sending message' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251022205549.js b/.history/routes/admin_20251022205549.js new file mode 100644 index 0000000..529109a --- /dev/null +++ b/.history/routes/admin_20251022205549.js @@ -0,0 +1,1610 @@ +const express = require('express'); +const router = express.Router(); +const multer = require('multer'); +const sharp = require('sharp'); +const path = require('path'); +const fs = require('fs').promises; +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings, Banner } = require('../models'); + +// Configure multer for file uploads +const storage = multer.memoryStorage(); // Use memory storage to process with sharp +const upload = multer({ + storage, + limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit + fileFilter: (req, file, cb) => { + if (file.mimetype.startsWith('image/')) { + cb(null, true); + } else { + cb(new Error('Only image files are allowed'), false); + } + } +}); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner management +router.get('/banners', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [banners, total] = await Promise.all([ + Banner.findAll({ + order: [['order', 'ASC'], ['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Banner.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/banners/list', { + title: 'Banner Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banners, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Banner list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banners' + }); + } +}); + +// Add banner +router.get('/banners/add', requireAuth, (req, res) => { + res.render('admin/banners/add', { + title: 'Add Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); +}); + +// Create banner +router.post('/banners/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive = true, + startDate, + endDate, + targetAudience = 'all', + backgroundColor, + textColor, + animation = 'none' + } = req.body; + + const banner = await Banner.create({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience, + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation + }); + + res.json({ + success: true, + message: '배너가 성공적으로 생성되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner creation error:', error); + res.status(500).json({ + success: false, + message: '배너 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit banner +router.get('/banners/edit/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Banner not found' + }); + } + + res.render('admin/banners/edit', { + title: 'Edit Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banner, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); + } catch (error) { + console.error('Banner edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner' + }); + } +}); + +// Update banner +router.put('/banners/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const banner = await Banner.findByPk(req.params.id); + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive, + startDate, + endDate, + targetAudience, + backgroundColor, + textColor, + animation + } = req.body; + + await banner.update({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience: targetAudience || 'all', + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation: animation || 'none' + }); + + res.json({ + success: true, + message: '배너가 성공적으로 업데이트되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner update error:', error); + res.status(500).json({ + success: false, + message: '배너 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete banner +router.delete('/banners/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.destroy(); + + res.json({ + success: true, + message: '배너가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Banner deletion error:', error); + res.status(500).json({ + success: false, + message: '배너 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle banner active status +router.patch('/banners/:id/toggle-active', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const newStatus = !banner.isActive; + await banner.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `배너가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Banner toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Record banner click +router.post('/banners/:id/click', async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.recordClick(); + + res.json({ + success: true, + clickCount: banner.clickCount + }); + } catch (error) { + console.error('Banner click record error:', error); + res.status(500).json({ + success: false, + message: '클릭 기록 중 오류가 발생했습니다' + }); + } +}); + +// Banner Editor (legacy route) +router.get('/banner-editor', requireAuth, async (req, res) => { + res.redirect('/admin/banners'); +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Utility function for category names +const getCategoryName = (category) => { + const categoryNames = { + 'web-development': 'Веб-разработка', + 'mobile-app': 'Мобильные приложения', + 'ui-ux-design': 'UI/UX дизайн', + 'e-commerce': 'Электронная коммерция', + 'enterprise': 'Корпоративное ПО', + 'other': 'Другое' + }; + return categoryNames[category] || category; +}; + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ], + getCategoryName: getCategoryName + }); +}); + +// Create portfolio item +router.post('/portfolio/add', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + seoTitle, + seoDescription, + isPublished, + featured + } = req.body; + + // Validate required fields + if (!title || !shortDescription || !description || !category) { + return res.status(400).json({ + success: false, + message: 'Заполните все обязательные поля' + }); + } + + // Parse technologies from JSON string + let parsedTechnologies = []; + try { + parsedTechnologies = JSON.parse(technologies || '[]'); + } catch (e) { + parsedTechnologies = typeof technologies === 'string' ? [technologies] : []; + } + + if (parsedTechnologies.length === 0) { + return res.status(400).json({ + success: false, + message: 'Добавьте хотя бы одну технологию' + }); + } + + // Process uploaded images + const processedImages = []; + if (req.files && req.files.length > 0) { + const uploadDir = path.join(process.cwd(), 'public', 'uploads', 'portfolio'); + + // Ensure directory exists + try { + await fs.access(uploadDir); + } catch { + await fs.mkdir(uploadDir, { recursive: true }); + } + + for (const file of req.files) { + const timestamp = Date.now(); + const filename = `${timestamp}-${Math.random().toString(36).substr(2, 9)}.webp`; + const filepath = path.join(uploadDir, filename); + + // Process image with Sharp + await sharp(file.buffer) + .webp({ quality: 85 }) + .resize(1200, 800, { fit: 'inside', withoutEnlargement: true }) + .toFile(filepath); + + processedImages.push(`/uploads/portfolio/${filename}`); + } + } + + // Create SEO data + const seo = {}; + if (seoTitle) seo.title = seoTitle; + if (seoDescription) seo.description = seoDescription; + + // Create portfolio item + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + technologies: parsedTechnologies, + images: processedImages, + projectUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: isPublished === 'true' || isPublished === true, + featured: featured === 'true' || featured === true, + publishedAt: (isPublished === 'true' || isPublished === true) ? new Date() : null, + status: (isPublished === 'true' || isPublished === true) ? 'published' : 'draft', + seo: Object.keys(seo).length > 0 ? seo : null + }); + + res.json({ + success: true, + message: 'Проект успешно создан!', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category, + isPublished: portfolio.isPublished + } + }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ + success: false, + message: 'Ошибка при создании проекта: ' + error.message + }); + } +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Update portfolio item +router.put('/portfolio/:id', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ + success: false, + message: 'Проект не найден' + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + seoTitle, + seoDescription, + isPublished, + featured, + existingImages + } = req.body; + + // Validate required fields + if (!title || !shortDescription || !description || !category) { + return res.status(400).json({ + success: false, + message: 'Заполните все обязательные поля' + }); + } + + // Parse technologies from JSON string + let parsedTechnologies = []; + try { + parsedTechnologies = JSON.parse(technologies || '[]'); + } catch (e) { + parsedTechnologies = typeof technologies === 'string' ? [technologies] : []; + } + + // Handle existing images + let finalImages = []; + try { + const existing = JSON.parse(existingImages || '[]'); + finalImages = Array.isArray(existing) ? existing : []; + } catch (e) { + finalImages = portfolio.images || []; + } + + // Process new uploaded images + if (req.files && req.files.length > 0) { + const uploadDir = path.join(process.cwd(), 'public', 'uploads', 'portfolio'); + + // Ensure directory exists + try { + await fs.access(uploadDir); + } catch { + await fs.mkdir(uploadDir, { recursive: true }); + } + + for (const file of req.files) { + const timestamp = Date.now(); + const filename = `${timestamp}-${Math.random().toString(36).substr(2, 9)}.webp`; + const filepath = path.join(uploadDir, filename); + + // Process image with Sharp + await sharp(file.buffer) + .webp({ quality: 85 }) + .resize(1200, 800, { fit: 'inside', withoutEnlargement: true }) + .toFile(filepath); + + finalImages.push(`/uploads/portfolio/${filename}`); + } + } + + // Create SEO data + const seo = portfolio.seo || {}; + if (seoTitle) seo.title = seoTitle; + if (seoDescription) seo.description = seoDescription; + + // Update portfolio + await portfolio.update({ + title, + shortDescription, + description, + category, + technologies: parsedTechnologies, + images: finalImages, + projectUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: isPublished === 'true' || isPublished === true, + featured: featured === 'true' || featured === true, + publishedAt: (isPublished === 'true' || isPublished === true) && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: (isPublished === 'true' || isPublished === true) ? 'published' : 'draft', + seo: Object.keys(seo).length > 0 ? seo : null + }); + + res.json({ + success: true, + message: 'Проект успешно обновлен!', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category, + isPublished: portfolio.isPublished + } + }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ + success: false, + message: 'Ошибка при обновлении проекта: ' + error.message + }); + } +}); + +// Delete portfolio item +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + await portfolio.destroy(); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle portfolio publish status +router.patch('/portfolio/:id/toggle-publish', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const newStatus = !portfolio.isPublished; + await portfolio.update({ + isPublished: newStatus, + publishedAt: newStatus && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: newStatus ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: `포트폴리오가 ${newStatus ? '게시' : '비공개'}되었습니다`, + isPublished: newStatus + }); + } catch (error) { + console.error('Portfolio toggle publish error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Portfolio preview +router.post('/portfolio/preview', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName + } = req.body; + + // Parse technologies from JSON string + let parsedTechnologies = []; + try { + parsedTechnologies = JSON.parse(technologies || '[]'); + } catch (e) { + parsedTechnologies = typeof technologies === 'string' ? [technologies] : []; + } + + // Process uploaded images for preview + const previewImages = []; + if (req.files && req.files.length > 0) { + for (const file of req.files) { + // Convert to base64 for preview + const base64 = `data:${file.mimetype};base64,${file.buffer.toString('base64')}`; + previewImages.push(base64); + } + } + + const previewData = { + title, + shortDescription, + description, + category, + technologies: parsedTechnologies, + images: previewImages, + projectUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + createdAt: new Date() + }; + + res.json({ + success: true, + preview: previewData + }); + } catch (error) { + console.error('Portfolio preview error:', error); + res.status(500).json({ + success: false, + message: 'Ошибка при создании предпросмотра: ' + error.message + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); +}); + +// Create service +router.post('/services/add', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive = true, + featured = false + } = req.body; + + const service = await Service.create({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 생성되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service creation error:', error); + res.status(500).json({ + success: false, + message: '서비스 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['id', 'title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Update service +router.put('/services/:id', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const service = await Service.findByPk(req.params.id); + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive, + featured + } = req.body; + + await service.update({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 업데이트되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service update error:', error); + res.status(500).json({ + success: false, + message: '서비스 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete service +router.delete('/services/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + await service.destroy(); + + res.json({ + success: true, + message: '서비스가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Service deletion error:', error); + res.status(500).json({ + success: false, + message: '서비스 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle service active status +router.patch('/services/:id/toggle-active', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const newStatus = !service.isActive; + await service.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `서비스가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Service toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Telegram bot configuration and testing +router.get('/telegram', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + + // Get bot info and available chats if token is configured + let botInfo = null; + let availableChats = []; + + if (telegramService.botToken) { + const result = await telegramService.getBotInfo(); + if (result.success) { + botInfo = result.bot; + availableChats = telegramService.getAvailableChats(); + } + } + + res.render('admin/telegram', { + title: 'Telegram Bot - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + botConfigured: telegramService.isEnabled, + botToken: telegramService.botToken || '', + chatId: telegramService.chatId || '', + botInfo, + availableChats + }); + } catch (error) { + console.error('Telegram page error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading Telegram settings' + }); + } +}); + +// Update bot token +router.post('/telegram/configure', requireAuth, [ + body('botToken').notEmpty().withMessage('Bot token is required'), + body('chatId').optional().isNumeric().withMessage('Chat ID must be numeric') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { botToken, chatId } = req.body; + const telegramService = require('../services/telegram'); + + // Update bot token + const result = await telegramService.updateBotToken(botToken); + + if (result.success) { + // Update chat ID if provided + if (chatId) { + telegramService.updateChatId(parseInt(chatId)); + } + + // Update environment variables (in production, this should update a config file) + process.env.TELEGRAM_BOT_TOKEN = botToken; + if (chatId) { + process.env.TELEGRAM_CHAT_ID = chatId; + } + + res.json({ + success: true, + message: 'Telegram bot configured successfully', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to configure bot' + }); + } + } catch (error) { + console.error('Configure Telegram bot error:', error); + res.status(500).json({ + success: false, + message: 'Error configuring Telegram bot' + }); + } +}); + +// Get bot info and discover chats +router.get('/telegram/info', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + res.json({ + success: true, + botInfo: result.bot, + availableChats: result.availableChats || [], + isConfigured: telegramService.isEnabled + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to get bot info' + }); + } + } catch (error) { + console.error('Get Telegram info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting bot information' + }); + } +}); + +// Get chat information +router.get('/telegram/chat/:chatId', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.getChat(req.params.chatId); + + if (result.success) { + res.json({ + success: true, + chat: result.chat + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to get chat info' + }); + } + } catch (error) { + console.error('Get chat info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting chat information' + }); + } +}); + +// Test connection +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + const testMessage = `🤖 Тест Telegram бота\n\n` + + `✅ Соединение успешно установлено!\n` + + `🤖 Бот: @${result.bot.username} (${result.bot.first_name})\n` + + `🆔 ID бота: ${result.bot.id}\n` + + `⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` + + `🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` + + `Бот готов к отправке уведомлений! 🚀`; + + const sendResult = await telegramService.sendMessage(testMessage); + + if (sendResult.success) { + res.json({ + success: true, + message: 'Test message sent successfully!', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: 'Bot connection successful, but failed to send test message: ' + (sendResult.error || sendResult.message) + }); + } + } else { + res.status(400).json({ + success: false, + message: result.message || result.error || 'Failed to connect to Telegram bot' + }); + } + } catch (error) { + console.error('Telegram test error:', error); + res.status(500).json({ + success: false, + message: 'Error testing Telegram bot' + }); + } +}); + +// Send custom message +router.post('/telegram/send', requireAuth, [ + body('message').notEmpty().withMessage('Message is required'), + body('chatIds').optional().isArray().withMessage('Chat IDs must be an array'), + body('parseMode').optional().isIn(['HTML', 'Markdown', 'MarkdownV2']).withMessage('Invalid parse mode') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { + message, + chatIds = [], + parseMode = 'HTML', + disableWebPagePreview = false, + disableNotification = false + } = req.body; + + const telegramService = require('../services/telegram'); + + let result; + if (chatIds.length > 0) { + // Send to multiple chats + result = await telegramService.sendCustomMessage({ + text: message, + chatIds: chatIds.map(id => parseInt(id)), + parseMode, + disableWebPagePreview, + disableNotification + }); + + res.json({ + success: result.success, + message: result.success ? + `Message sent to ${result.totalSent} chat(s). ${result.totalFailed} failed.` : + 'Failed to send message', + results: result.results || [], + errors: result.errors || [] + }); + } else { + // Send to default chat + result = await telegramService.sendMessage(message, { + parse_mode: parseMode, + disable_web_page_preview: disableWebPagePreview, + disable_notification: disableNotification + }); + + if (result.success) { + res.json({ + success: true, + message: 'Message sent successfully!' + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to send message' + }); + } + } + } catch (error) { + console.error('Send Telegram message error:', error); + res.status(500).json({ + success: false, + message: 'Error sending message' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/admin_20251022211821.js b/.history/routes/admin_20251022211821.js new file mode 100644 index 0000000..529109a --- /dev/null +++ b/.history/routes/admin_20251022211821.js @@ -0,0 +1,1610 @@ +const express = require('express'); +const router = express.Router(); +const multer = require('multer'); +const sharp = require('sharp'); +const path = require('path'); +const fs = require('fs').promises; +const { body, validationResult } = require('express-validator'); +const { User, Portfolio, Service, Contact, SiteSettings, Banner } = require('../models'); + +// Configure multer for file uploads +const storage = multer.memoryStorage(); // Use memory storage to process with sharp +const upload = multer({ + storage, + limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit + fileFilter: (req, file, cb) => { + if (file.mimetype.startsWith('image/')) { + cb(null, true); + } else { + cb(new Error('Only image files are allowed'), false); + } + } +}); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.redirect('/admin/login'); + } + next(); +}; + +// Admin login page +router.get('/login', (req, res) => { + if (req.session.user) { + return res.redirect('/admin/dashboard'); + } + + res.render('admin/login', { + title: 'Admin Login', + error: null + }); +}); + +// Admin login POST +router.post('/login', async (req, res) => { + try { + const { email, password } = req.body; + + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user || !(await user.comparePassword(password))) { + return res.render('admin/login', { + title: 'Admin Login', + error: 'Invalid credentials' + }); + } + + await user.updateLastLogin(); + + req.session.user = { + id: user.id, + email: user.email, + name: user.name, + role: user.role + }; + + res.redirect('/admin/dashboard'); + } catch (error) { + console.error('Admin login error:', error); + res.render('admin/login', { + title: 'Admin Login', + error: 'Server error' + }); + } +}); + +// Admin logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + console.error('Logout error:', err); + } + res.redirect('/admin/login'); + }); +}); + +// Dashboard (default route) +router.get('/', requireAuth, async (req, res) => { + res.redirect('/admin/dashboard'); +}); + +// Dashboard +router.get('/dashboard', requireAuth, async (req, res) => { + try { + const [ + portfolioCount, + servicesCount, + contactsCount, + recentContacts, + recentPortfolio + ] = await Promise.all([ + Portfolio.count({ where: { isPublished: true } }), + Service.count({ where: { isActive: true } }), + Contact.count(), + Contact.findAll({ + order: [['createdAt', 'DESC']], + limit: 5 + }), + Portfolio.findAll({ + where: { isPublished: true }, + order: [['createdAt', 'DESC']], + limit: 5 + }) + ]); + + const stats = { + portfolioCount: portfolioCount, + servicesCount: servicesCount, + contactsCount: contactsCount, + usersCount: await User.count() + }; + + res.render('admin/dashboard', { + title: 'Admin Dashboard', + layout: 'admin/layout', + user: req.session.user, + stats, + recentContacts, + recentPortfolio + }); + } catch (error) { + console.error('Dashboard error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading dashboard' + }); + } +}); + +// Banner management +router.get('/banners', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [banners, total] = await Promise.all([ + Banner.findAll({ + order: [['order', 'ASC'], ['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Banner.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/banners/list', { + title: 'Banner Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banners, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Banner list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banners' + }); + } +}); + +// Add banner +router.get('/banners/add', requireAuth, (req, res) => { + res.render('admin/banners/add', { + title: 'Add Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); +}); + +// Create banner +router.post('/banners/add', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive = true, + startDate, + endDate, + targetAudience = 'all', + backgroundColor, + textColor, + animation = 'none' + } = req.body; + + const banner = await Banner.create({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience, + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation + }); + + res.json({ + success: true, + message: '배너가 성공적으로 생성되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner creation error:', error); + res.status(500).json({ + success: false, + message: '배너 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit banner +router.get('/banners/edit/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Banner not found' + }); + } + + res.render('admin/banners/edit', { + title: 'Edit Banner - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + banner, + positions: [ + { value: 'hero', label: '메인 히어로' }, + { value: 'secondary', label: '보조 배너' }, + { value: 'footer', label: '푸터 배너' } + ], + animations: [ + { value: 'none', label: '없음' }, + { value: 'fade', label: '페이드' }, + { value: 'slide', label: '슬라이드' }, + { value: 'zoom', label: '줌' } + ] + }); + } catch (error) { + console.error('Banner edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading banner' + }); + } +}); + +// Update banner +router.put('/banners/:id', requireAuth, [ + body('title').notEmpty().withMessage('제목을 입력해주세요'), + body('position').isIn(['hero', 'secondary', 'footer']).withMessage('유효한 위치를 선택해주세요'), + body('order').isInt({ min: 0 }).withMessage('유효한 순서를 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const banner = await Banner.findByPk(req.params.id); + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const { + title, + subtitle, + description, + buttonText, + buttonUrl, + image, + mobileImage, + position, + order, + isActive, + startDate, + endDate, + targetAudience, + backgroundColor, + textColor, + animation + } = req.body; + + await banner.update({ + title, + subtitle: subtitle || null, + description: description || null, + buttonText: buttonText || null, + buttonUrl: buttonUrl || null, + image: image || null, + mobileImage: mobileImage || null, + position, + order: parseInt(order), + isActive: Boolean(isActive), + startDate: startDate || null, + endDate: endDate || null, + targetAudience: targetAudience || 'all', + backgroundColor: backgroundColor || null, + textColor: textColor || null, + animation: animation || 'none' + }); + + res.json({ + success: true, + message: '배너가 성공적으로 업데이트되었습니다', + banner: { + id: banner.id, + title: banner.title, + position: banner.position + } + }); + } catch (error) { + console.error('Banner update error:', error); + res.status(500).json({ + success: false, + message: '배너 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete banner +router.delete('/banners/:id', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.destroy(); + + res.json({ + success: true, + message: '배너가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Banner deletion error:', error); + res.status(500).json({ + success: false, + message: '배너 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle banner active status +router.patch('/banners/:id/toggle-active', requireAuth, async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + const newStatus = !banner.isActive; + await banner.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `배너가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Banner toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Record banner click +router.post('/banners/:id/click', async (req, res) => { + try { + const banner = await Banner.findByPk(req.params.id); + + if (!banner) { + return res.status(404).json({ + success: false, + message: '배너를 찾을 수 없습니다' + }); + } + + await banner.recordClick(); + + res.json({ + success: true, + clickCount: banner.clickCount + }); + } catch (error) { + console.error('Banner click record error:', error); + res.status(500).json({ + success: false, + message: '클릭 기록 중 오류가 발생했습니다' + }); + } +}); + +// Banner Editor (legacy route) +router.get('/banner-editor', requireAuth, async (req, res) => { + res.redirect('/admin/banners'); +}); + +// Portfolio management +router.get('/portfolio', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/portfolio/list', { + title: 'Portfolio Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio' + }); + } +}); + +// Utility function for category names +const getCategoryName = (category) => { + const categoryNames = { + 'web-development': 'Веб-разработка', + 'mobile-app': 'Мобильные приложения', + 'ui-ux-design': 'UI/UX дизайн', + 'e-commerce': 'Электронная коммерция', + 'enterprise': 'Корпоративное ПО', + 'other': 'Другое' + }; + return categoryNames[category] || category; +}; + +// Add portfolio item +router.get('/portfolio/add', requireAuth, (req, res) => { + res.render('admin/portfolio/add', { + title: 'Add Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ], + getCategoryName: getCategoryName + }); +}); + +// Create portfolio item +router.post('/portfolio/add', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + seoTitle, + seoDescription, + isPublished, + featured + } = req.body; + + // Validate required fields + if (!title || !shortDescription || !description || !category) { + return res.status(400).json({ + success: false, + message: 'Заполните все обязательные поля' + }); + } + + // Parse technologies from JSON string + let parsedTechnologies = []; + try { + parsedTechnologies = JSON.parse(technologies || '[]'); + } catch (e) { + parsedTechnologies = typeof technologies === 'string' ? [technologies] : []; + } + + if (parsedTechnologies.length === 0) { + return res.status(400).json({ + success: false, + message: 'Добавьте хотя бы одну технологию' + }); + } + + // Process uploaded images + const processedImages = []; + if (req.files && req.files.length > 0) { + const uploadDir = path.join(process.cwd(), 'public', 'uploads', 'portfolio'); + + // Ensure directory exists + try { + await fs.access(uploadDir); + } catch { + await fs.mkdir(uploadDir, { recursive: true }); + } + + for (const file of req.files) { + const timestamp = Date.now(); + const filename = `${timestamp}-${Math.random().toString(36).substr(2, 9)}.webp`; + const filepath = path.join(uploadDir, filename); + + // Process image with Sharp + await sharp(file.buffer) + .webp({ quality: 85 }) + .resize(1200, 800, { fit: 'inside', withoutEnlargement: true }) + .toFile(filepath); + + processedImages.push(`/uploads/portfolio/${filename}`); + } + } + + // Create SEO data + const seo = {}; + if (seoTitle) seo.title = seoTitle; + if (seoDescription) seo.description = seoDescription; + + // Create portfolio item + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + technologies: parsedTechnologies, + images: processedImages, + projectUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: isPublished === 'true' || isPublished === true, + featured: featured === 'true' || featured === true, + publishedAt: (isPublished === 'true' || isPublished === true) ? new Date() : null, + status: (isPublished === 'true' || isPublished === true) ? 'published' : 'draft', + seo: Object.keys(seo).length > 0 ? seo : null + }); + + res.json({ + success: true, + message: 'Проект успешно создан!', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category, + isPublished: portfolio.isPublished + } + }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ + success: false, + message: 'Ошибка при создании проекта: ' + error.message + }); + } +}); + +// Edit portfolio item +router.get('/portfolio/edit/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Portfolio item not found' + }); + } + + res.render('admin/portfolio/edit', { + title: 'Edit Portfolio Item - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + portfolio, + categories: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'enterprise', + 'other' + ] + }); + } catch (error) { + console.error('Portfolio edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading portfolio item' + }); + } +}); + +// Update portfolio item +router.put('/portfolio/:id', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ + success: false, + message: 'Проект не найден' + }); + } + + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName, + duration, + seoTitle, + seoDescription, + isPublished, + featured, + existingImages + } = req.body; + + // Validate required fields + if (!title || !shortDescription || !description || !category) { + return res.status(400).json({ + success: false, + message: 'Заполните все обязательные поля' + }); + } + + // Parse technologies from JSON string + let parsedTechnologies = []; + try { + parsedTechnologies = JSON.parse(technologies || '[]'); + } catch (e) { + parsedTechnologies = typeof technologies === 'string' ? [technologies] : []; + } + + // Handle existing images + let finalImages = []; + try { + const existing = JSON.parse(existingImages || '[]'); + finalImages = Array.isArray(existing) ? existing : []; + } catch (e) { + finalImages = portfolio.images || []; + } + + // Process new uploaded images + if (req.files && req.files.length > 0) { + const uploadDir = path.join(process.cwd(), 'public', 'uploads', 'portfolio'); + + // Ensure directory exists + try { + await fs.access(uploadDir); + } catch { + await fs.mkdir(uploadDir, { recursive: true }); + } + + for (const file of req.files) { + const timestamp = Date.now(); + const filename = `${timestamp}-${Math.random().toString(36).substr(2, 9)}.webp`; + const filepath = path.join(uploadDir, filename); + + // Process image with Sharp + await sharp(file.buffer) + .webp({ quality: 85 }) + .resize(1200, 800, { fit: 'inside', withoutEnlargement: true }) + .toFile(filepath); + + finalImages.push(`/uploads/portfolio/${filename}`); + } + } + + // Create SEO data + const seo = portfolio.seo || {}; + if (seoTitle) seo.title = seoTitle; + if (seoDescription) seo.description = seoDescription; + + // Update portfolio + await portfolio.update({ + title, + shortDescription, + description, + category, + technologies: parsedTechnologies, + images: finalImages, + projectUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + duration: duration ? parseInt(duration) : null, + isPublished: isPublished === 'true' || isPublished === true, + featured: featured === 'true' || featured === true, + publishedAt: (isPublished === 'true' || isPublished === true) && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: (isPublished === 'true' || isPublished === true) ? 'published' : 'draft', + seo: Object.keys(seo).length > 0 ? seo : null + }); + + res.json({ + success: true, + message: 'Проект успешно обновлен!', + portfolio: { + id: portfolio.id, + title: portfolio.title, + category: portfolio.category, + isPublished: portfolio.isPublished + } + }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ + success: false, + message: 'Ошибка при обновлении проекта: ' + error.message + }); + } +}); + +// Delete portfolio item +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + await portfolio.destroy(); + + res.json({ + success: true, + message: '포트폴리오가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ + success: false, + message: '포트폴리오 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle portfolio publish status +router.patch('/portfolio/:id/toggle-publish', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio) { + return res.status(404).json({ + success: false, + message: '포트폴리오를 찾을 수 없습니다' + }); + } + + const newStatus = !portfolio.isPublished; + await portfolio.update({ + isPublished: newStatus, + publishedAt: newStatus && !portfolio.publishedAt ? new Date() : portfolio.publishedAt, + status: newStatus ? 'published' : 'draft' + }); + + res.json({ + success: true, + message: `포트폴리오가 ${newStatus ? '게시' : '비공개'}되었습니다`, + isPublished: newStatus + }); + } catch (error) { + console.error('Portfolio toggle publish error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Portfolio preview +router.post('/portfolio/preview', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const { + title, + shortDescription, + description, + category, + technologies, + demoUrl, + githubUrl, + clientName + } = req.body; + + // Parse technologies from JSON string + let parsedTechnologies = []; + try { + parsedTechnologies = JSON.parse(technologies || '[]'); + } catch (e) { + parsedTechnologies = typeof technologies === 'string' ? [technologies] : []; + } + + // Process uploaded images for preview + const previewImages = []; + if (req.files && req.files.length > 0) { + for (const file of req.files) { + // Convert to base64 for preview + const base64 = `data:${file.mimetype};base64,${file.buffer.toString('base64')}`; + previewImages.push(base64); + } + } + + const previewData = { + title, + shortDescription, + description, + category, + technologies: parsedTechnologies, + images: previewImages, + projectUrl: demoUrl || null, + githubUrl: githubUrl || null, + clientName: clientName || null, + createdAt: new Date() + }; + + res.json({ + success: true, + preview: previewData + }); + } catch (error) { + console.error('Portfolio preview error:', error); + res.status(500).json({ + success: false, + message: 'Ошибка при создании предпросмотра: ' + error.message + }); + } +}); + +// Services management +router.get('/services', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + + const [services, total] = await Promise.all([ + Service.findAll({ + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Service.count() + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/services/list', { + title: 'Services Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + services, + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Services list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading services' + }); + } +}); + +// Add service +router.get('/services/add', requireAuth, (req, res) => { + res.render('admin/services/add', { + title: 'Add Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); +}); + +// Create service +router.post('/services/add', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive = true, + featured = false + } = req.body; + + const service = await Service.create({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 생성되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service creation error:', error); + res.status(500).json({ + success: false, + message: '서비스 생성 중 오류가 발생했습니다' + }); + } +}); + +// Edit service +router.get('/services/edit/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Service not found' + }); + } + + const availablePortfolio = await Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['id', 'title', 'category'] + }); + + res.render('admin/services/edit', { + title: 'Edit Service - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + service, + availablePortfolio, + serviceTypes: [ + 'web-development', + 'mobile-app', + 'ui-ux-design', + 'e-commerce', + 'seo', + 'maintenance', + 'consultation', + 'other' + ] + }); + } catch (error) { + console.error('Service edit error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading service' + }); + } +}); + +// Update service +router.put('/services/:id', requireAuth, [ + body('name').notEmpty().withMessage('서비스명을 입력해주세요'), + body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'), + body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'), + body('category').notEmpty().withMessage('카테고리를 선택해주세요'), + body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: '입력 데이터를 확인해주세요', + errors: errors.array() + }); + } + + const service = await Service.findByPk(req.params.id); + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const { + name, + shortDescription, + description, + category, + basePrice, + features, + duration, + isActive, + featured + } = req.body; + + await service.update({ + name, + shortDescription, + description, + category, + basePrice: parseFloat(basePrice), + features: features || [], + duration: duration ? parseInt(duration) : null, + isActive: Boolean(isActive), + featured: Boolean(featured) + }); + + res.json({ + success: true, + message: '서비스가 성공적으로 업데이트되었습니다', + service: { + id: service.id, + name: service.name, + category: service.category + } + }); + } catch (error) { + console.error('Service update error:', error); + res.status(500).json({ + success: false, + message: '서비스 업데이트 중 오류가 발생했습니다' + }); + } +}); + +// Delete service +router.delete('/services/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + await service.destroy(); + + res.json({ + success: true, + message: '서비스가 성공적으로 삭제되었습니다' + }); + } catch (error) { + console.error('Service deletion error:', error); + res.status(500).json({ + success: false, + message: '서비스 삭제 중 오류가 발생했습니다' + }); + } +}); + +// Toggle service active status +router.patch('/services/:id/toggle-active', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service) { + return res.status(404).json({ + success: false, + message: '서비스를 찾을 수 없습니다' + }); + } + + const newStatus = !service.isActive; + await service.update({ isActive: newStatus }); + + res.json({ + success: true, + message: `서비스가 ${newStatus ? '활성화' : '비활성화'}되었습니다`, + isActive: newStatus + }); + } catch (error) { + console.error('Service toggle active error:', error); + res.status(500).json({ + success: false, + message: '상태 변경 중 오류가 발생했습니다' + }); + } +}); + +// Contacts management +router.get('/contacts', requireAuth, async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 20; + const skip = (page - 1) * limit; + const status = req.query.status; + + let whereClause = {}; + if (status && status !== 'all') { + whereClause.status = status; + } + + const [contacts, total] = await Promise.all([ + Contact.findAll({ + where: whereClause, + order: [['createdAt', 'DESC']], + offset: skip, + limit: limit + }), + Contact.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('admin/contacts/list', { + title: 'Contacts Management - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contacts, + currentStatus: status || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Contacts list error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contacts' + }); + } +}); + +// View contact details +router.get('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + + if (!contact) { + return res.status(404).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Contact not found' + }); + } + + // Mark as read + if (!contact.isRead) { + contact.isRead = true; + await contact.save(); + } + + res.render('admin/contacts/view', { + title: 'Contact Details - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + contact + }); + } catch (error) { + console.error('Contact view error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading contact' + }); + } +}); + +// Settings +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || await SiteSettings.create({}); + + res.render('admin/settings', { + title: 'Site Settings - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + settings + }); + } catch (error) { + console.error('Settings error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading settings' + }); + } +}); + +// Media gallery +router.get('/media', requireAuth, (req, res) => { + res.render('admin/media', { + title: 'Media Gallery - Admin Panel', + layout: 'admin/layout', + user: req.session.user + }); +}); + +// Telegram bot configuration and testing +router.get('/telegram', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + + // Get bot info and available chats if token is configured + let botInfo = null; + let availableChats = []; + + if (telegramService.botToken) { + const result = await telegramService.getBotInfo(); + if (result.success) { + botInfo = result.bot; + availableChats = telegramService.getAvailableChats(); + } + } + + res.render('admin/telegram', { + title: 'Telegram Bot - Admin Panel', + layout: 'admin/layout', + user: req.session.user, + botConfigured: telegramService.isEnabled, + botToken: telegramService.botToken || '', + chatId: telegramService.chatId || '', + botInfo, + availableChats + }); + } catch (error) { + console.error('Telegram page error:', error); + res.status(500).render('admin/error', { + title: 'Error - Admin Panel', + layout: 'admin/layout', + message: 'Error loading Telegram settings' + }); + } +}); + +// Update bot token +router.post('/telegram/configure', requireAuth, [ + body('botToken').notEmpty().withMessage('Bot token is required'), + body('chatId').optional().isNumeric().withMessage('Chat ID must be numeric') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { botToken, chatId } = req.body; + const telegramService = require('../services/telegram'); + + // Update bot token + const result = await telegramService.updateBotToken(botToken); + + if (result.success) { + // Update chat ID if provided + if (chatId) { + telegramService.updateChatId(parseInt(chatId)); + } + + // Update environment variables (in production, this should update a config file) + process.env.TELEGRAM_BOT_TOKEN = botToken; + if (chatId) { + process.env.TELEGRAM_CHAT_ID = chatId; + } + + res.json({ + success: true, + message: 'Telegram bot configured successfully', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to configure bot' + }); + } + } catch (error) { + console.error('Configure Telegram bot error:', error); + res.status(500).json({ + success: false, + message: 'Error configuring Telegram bot' + }); + } +}); + +// Get bot info and discover chats +router.get('/telegram/info', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + res.json({ + success: true, + botInfo: result.bot, + availableChats: result.availableChats || [], + isConfigured: telegramService.isEnabled + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to get bot info' + }); + } + } catch (error) { + console.error('Get Telegram info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting bot information' + }); + } +}); + +// Get chat information +router.get('/telegram/chat/:chatId', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.getChat(req.params.chatId); + + if (result.success) { + res.json({ + success: true, + chat: result.chat + }); + } else { + res.status(400).json({ + success: false, + message: result.error || 'Failed to get chat info' + }); + } + } catch (error) { + console.error('Get chat info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting chat information' + }); + } +}); + +// Test connection +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const telegramService = require('../services/telegram'); + const result = await telegramService.testConnection(); + + if (result.success) { + const testMessage = `🤖 Тест Telegram бота\n\n` + + `✅ Соединение успешно установлено!\n` + + `🤖 Бот: @${result.bot.username} (${result.bot.first_name})\n` + + `🆔 ID бота: ${result.bot.id}\n` + + `⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` + + `🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` + + `Бот готов к отправке уведомлений! 🚀`; + + const sendResult = await telegramService.sendMessage(testMessage); + + if (sendResult.success) { + res.json({ + success: true, + message: 'Test message sent successfully!', + botInfo: result.bot, + availableChats: result.availableChats || [] + }); + } else { + res.status(400).json({ + success: false, + message: 'Bot connection successful, but failed to send test message: ' + (sendResult.error || sendResult.message) + }); + } + } else { + res.status(400).json({ + success: false, + message: result.message || result.error || 'Failed to connect to Telegram bot' + }); + } + } catch (error) { + console.error('Telegram test error:', error); + res.status(500).json({ + success: false, + message: 'Error testing Telegram bot' + }); + } +}); + +// Send custom message +router.post('/telegram/send', requireAuth, [ + body('message').notEmpty().withMessage('Message is required'), + body('chatIds').optional().isArray().withMessage('Chat IDs must be an array'), + body('parseMode').optional().isIn(['HTML', 'Markdown', 'MarkdownV2']).withMessage('Invalid parse mode') +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Validation failed', + errors: errors.array() + }); + } + + const { + message, + chatIds = [], + parseMode = 'HTML', + disableWebPagePreview = false, + disableNotification = false + } = req.body; + + const telegramService = require('../services/telegram'); + + let result; + if (chatIds.length > 0) { + // Send to multiple chats + result = await telegramService.sendCustomMessage({ + text: message, + chatIds: chatIds.map(id => parseInt(id)), + parseMode, + disableWebPagePreview, + disableNotification + }); + + res.json({ + success: result.success, + message: result.success ? + `Message sent to ${result.totalSent} chat(s). ${result.totalFailed} failed.` : + 'Failed to send message', + results: result.results || [], + errors: result.errors || [] + }); + } else { + // Send to default chat + result = await telegramService.sendMessage(message, { + parse_mode: parseMode, + disable_web_page_preview: disableWebPagePreview, + disable_notification: disableNotification + }); + + if (result.success) { + res.json({ + success: true, + message: 'Message sent successfully!' + }); + } else { + res.status(400).json({ + success: false, + message: result.error || result.message || 'Failed to send message' + }); + } + } + } catch (error) { + console.error('Send Telegram message error:', error); + res.status(500).json({ + success: false, + message: 'Error sending message' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/api/admin_20251021213116.js b/.history/routes/api/admin_20251021213116.js new file mode 100644 index 0000000..e0cd2cb --- /dev/null +++ b/.history/routes/api/admin_20251021213116.js @@ -0,0 +1,284 @@ +const express = require('express'); +const router = express.Router(); +const multer = require('multer'); +const path = require('path'); +const { Portfolio, Service, Contact, User } = require('../../models'); + +// Multer configuration for file uploads +const storage = multer.diskStorage({ + destination: (req, file, cb) => { + cb(null, 'public/uploads/'); + }, + filename: (req, file, cb) => { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); + cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname)); + } +}); + +const upload = multer({ + storage: storage, + fileFilter: (req, file, cb) => { + if (file.mimetype.startsWith('image/')) { + cb(null, true); + } else { + cb(new Error('Only image files are allowed!'), false); + } + }, + limits: { + fileSize: 10 * 1024 * 1024 // 10MB + } +}); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.status(401).json({ success: false, message: 'Authentication required' }); + } + next(); +}; + +// Portfolio API Routes +router.post('/portfolio', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const { + title, + shortDescription, + description, + category, + clientName, + projectUrl, + githubUrl, + technologies, + featured, + isPublished + } = req.body; + + // Process uploaded images + const images = req.files ? req.files.map((file, index) => ({ + url: `/uploads/${file.filename}`, + alt: `${title} image ${index + 1}`, + isPrimary: index === 0 + })) : []; + + // Parse technologies + let techArray = []; + if (technologies) { + try { + techArray = JSON.parse(technologies); + } catch (e) { + techArray = technologies.split(',').map(t => t.trim()); + } + } + + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + clientName, + projectUrl: projectUrl || null, + githubUrl: githubUrl || null, + technologies: techArray, + images, + featured: featured === 'on', + isPublished: isPublished === 'on', + status: 'completed', + publishedAt: isPublished === 'on' ? new Date() : null + }); + + res.json({ success: true, portfolio }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +router.patch('/portfolio/:id', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ success: false, message: 'Portfolio not found' }); + } + + const updates = { ...req.body }; + + // Handle checkboxes + updates.featured = updates.featured === 'on'; + updates.isPublished = updates.isPublished === 'on'; + + // Process technologies + if (updates.technologies) { + try { + updates.technologies = JSON.parse(updates.technologies); + } catch (e) { + updates.technologies = updates.technologies.split(',').map(t => t.trim()); + } + } + + // Process new images + if (req.files && req.files.length > 0) { + const newImages = req.files.map((file, index) => ({ + url: `/uploads/${file.filename}`, + alt: `${updates.title || portfolio.title} image ${index + 1}`, + isPrimary: index === 0 && (!portfolio.images || portfolio.images.length === 0) + })); + + updates.images = [...(portfolio.images || []), ...newImages]; + } + + await portfolio.update(updates); + res.json({ success: true, portfolio }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ success: false, message: 'Portfolio not found' }); + } + + await portfolio.destroy(); + res.json({ success: true }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +// Services API Routes +router.post('/services', requireAuth, async (req, res) => { + try { + const { + name, + description, + shortDescription, + icon, + category, + features, + pricing, + estimatedTime, + isActive, + featured, + tags + } = req.body; + + // Parse arrays + let featuresArray = []; + let tagsArray = []; + let pricingObj = {}; + + if (features) { + try { + featuresArray = JSON.parse(features); + } catch (e) { + featuresArray = features.split(',').map(f => f.trim()); + } + } + + if (tags) { + try { + tagsArray = JSON.parse(tags); + } catch (e) { + tagsArray = tags.split(',').map(t => t.trim()); + } + } + + if (pricing) { + try { + pricingObj = JSON.parse(pricing); + } catch (e) { + pricingObj = { basePrice: pricing }; + } + } + + const service = await Service.create({ + name, + description, + shortDescription, + icon, + category, + features: featuresArray, + pricing: pricingObj, + estimatedTime, + isActive: isActive === 'on', + featured: featured === 'on', + tags: tagsArray + }); + + res.json({ success: true, service }); + } catch (error) { + console.error('Service creation error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +router.delete('/services/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + if (!service) { + return res.status(404).json({ success: false, message: 'Service not found' }); + } + + await service.destroy(); + res.json({ success: true }); + } catch (error) { + console.error('Service deletion error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +// Contacts API Routes +router.patch('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + if (!contact) { + return res.status(404).json({ success: false, message: 'Contact not found' }); + } + + await contact.update(req.body); + res.json({ success: true, contact }); + } catch (error) { + console.error('Contact update error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +router.delete('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + if (!contact) { + return res.status(404).json({ success: false, message: 'Contact not found' }); + } + + await contact.destroy(); + res.json({ success: true }); + } catch (error) { + console.error('Contact deletion error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +// Telegram notification for contact +router.post('/contacts/:id/telegram', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + if (!contact) { + return res.status(404).json({ success: false, message: 'Contact not found' }); + } + + // Send Telegram notification (we'll implement this with Telegram bot) + const telegramService = require('../../services/telegram'); + await telegramService.sendContactNotification(contact); + + res.json({ success: true }); + } catch (error) { + console.error('Telegram notification error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/api/admin_20251021213414.js b/.history/routes/api/admin_20251021213414.js new file mode 100644 index 0000000..f7fc941 --- /dev/null +++ b/.history/routes/api/admin_20251021213414.js @@ -0,0 +1,388 @@ +const express = require('express'); +const router = express.Router(); +const multer = require('multer'); +const path = require('path'); +const { Portfolio, Service, Contact, User } = require('../../models'); + +// Multer configuration for file uploads +const storage = multer.diskStorage({ + destination: (req, file, cb) => { + cb(null, 'public/uploads/'); + }, + filename: (req, file, cb) => { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); + cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname)); + } +}); + +const upload = multer({ + storage: storage, + fileFilter: (req, file, cb) => { + if (file.mimetype.startsWith('image/')) { + cb(null, true); + } else { + cb(new Error('Only image files are allowed!'), false); + } + }, + limits: { + fileSize: 10 * 1024 * 1024 // 10MB + } +}); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.status(401).json({ success: false, message: 'Authentication required' }); + } + next(); +}; + +// Portfolio API Routes +router.post('/portfolio', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const { + title, + shortDescription, + description, + category, + clientName, + projectUrl, + githubUrl, + technologies, + featured, + isPublished + } = req.body; + + // Process uploaded images + const images = req.files ? req.files.map((file, index) => ({ + url: `/uploads/${file.filename}`, + alt: `${title} image ${index + 1}`, + isPrimary: index === 0 + })) : []; + + // Parse technologies + let techArray = []; + if (technologies) { + try { + techArray = JSON.parse(technologies); + } catch (e) { + techArray = technologies.split(',').map(t => t.trim()); + } + } + + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + clientName, + projectUrl: projectUrl || null, + githubUrl: githubUrl || null, + technologies: techArray, + images, + featured: featured === 'on', + isPublished: isPublished === 'on', + status: 'completed', + publishedAt: isPublished === 'on' ? new Date() : null + }); + + res.json({ success: true, portfolio }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +router.patch('/portfolio/:id', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ success: false, message: 'Portfolio not found' }); + } + + const updates = { ...req.body }; + + // Handle checkboxes + updates.featured = updates.featured === 'on'; + updates.isPublished = updates.isPublished === 'on'; + + // Process technologies + if (updates.technologies) { + try { + updates.technologies = JSON.parse(updates.technologies); + } catch (e) { + updates.technologies = updates.technologies.split(',').map(t => t.trim()); + } + } + + // Process new images + if (req.files && req.files.length > 0) { + const newImages = req.files.map((file, index) => ({ + url: `/uploads/${file.filename}`, + alt: `${updates.title || portfolio.title} image ${index + 1}`, + isPrimary: index === 0 && (!portfolio.images || portfolio.images.length === 0) + })); + + updates.images = [...(portfolio.images || []), ...newImages]; + } + + await portfolio.update(updates); + res.json({ success: true, portfolio }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ success: false, message: 'Portfolio not found' }); + } + + await portfolio.destroy(); + res.json({ success: true }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +// Services API Routes +router.post('/services', requireAuth, async (req, res) => { + try { + const { + name, + description, + shortDescription, + icon, + category, + features, + pricing, + estimatedTime, + isActive, + featured, + tags + } = req.body; + + // Parse arrays + let featuresArray = []; + let tagsArray = []; + let pricingObj = {}; + + if (features) { + try { + featuresArray = JSON.parse(features); + } catch (e) { + featuresArray = features.split(',').map(f => f.trim()); + } + } + + if (tags) { + try { + tagsArray = JSON.parse(tags); + } catch (e) { + tagsArray = tags.split(',').map(t => t.trim()); + } + } + + if (pricing) { + try { + pricingObj = JSON.parse(pricing); + } catch (e) { + pricingObj = { basePrice: pricing }; + } + } + + const service = await Service.create({ + name, + description, + shortDescription, + icon, + category, + features: featuresArray, + pricing: pricingObj, + estimatedTime, + isActive: isActive === 'on', + featured: featured === 'on', + tags: tagsArray + }); + + res.json({ success: true, service }); + } catch (error) { + console.error('Service creation error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +router.delete('/services/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + if (!service) { + return res.status(404).json({ success: false, message: 'Service not found' }); + } + + await service.destroy(); + res.json({ success: true }); + } catch (error) { + console.error('Service deletion error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +// Contacts API Routes +router.patch('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + if (!contact) { + return res.status(404).json({ success: false, message: 'Contact not found' }); + } + + await contact.update(req.body); + res.json({ success: true, contact }); + } catch (error) { + console.error('Contact update error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +router.delete('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + if (!contact) { + return res.status(404).json({ success: false, message: 'Contact not found' }); + } + + await contact.destroy(); + res.json({ success: true }); + } catch (error) { + console.error('Contact deletion error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +// Telegram notification for contact +router.post('/contacts/:id/telegram', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + if (!contact) { + return res.status(404).json({ success: false, message: 'Contact not found' }); + } + + // Send Telegram notification + const telegramService = require('../../services/telegram'); + const result = await telegramService.sendContactNotification(contact); + + if (result.success) { + res.json({ success: true }); + } else { + res.status(500).json({ success: false, message: result.message || result.error }); + } + } catch (error) { + console.error('Telegram notification error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +// Test Telegram connection +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const { botToken, chatId } = req.body; + + // Temporarily set up telegram service with provided credentials + const axios = require('axios'); + + // Test bot info + const botResponse = await axios.get(`https://api.telegram.org/bot${botToken}/getMe`); + + // Test sending a message + const testMessage = '✅ Telegram bot подключен успешно!\n\nЭто тестовое сообщение от SmartSolTech Admin Panel.'; + await axios.post(`https://api.telegram.org/bot${botToken}/sendMessage`, { + chat_id: chatId, + text: testMessage, + parse_mode: 'Markdown' + }); + + res.json({ + success: true, + bot: botResponse.data.result, + message: 'Test message sent successfully' + }); + } catch (error) { + console.error('Telegram test error:', error); + let message = 'Connection failed'; + + if (error.response?.data?.description) { + message = error.response.data.description; + } else if (error.message) { + message = error.message; + } + + res.status(400).json({ success: false, message }); + } +}); + +// Settings API +const { SiteSettings } = require('../../models'); + +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + res.json({ success: true, settings }); + } catch (error) { + console.error('Settings fetch error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +router.post('/settings', requireAuth, upload.fields([ + { name: 'logo', maxCount: 1 }, + { name: 'favicon', maxCount: 1 } +]), async (req, res) => { + try { + let settings = await SiteSettings.findOne(); + if (!settings) { + settings = await SiteSettings.create({}); + } + + const updates = {}; + + // Handle nested objects + Object.keys(req.body).forEach(key => { + if (key.includes('.')) { + const [parent, child] = key.split('.'); + if (!updates[parent]) updates[parent] = {}; + updates[parent][child] = req.body[key]; + } else { + updates[key] = req.body[key]; + } + }); + + // Handle file uploads + if (req.files.logo) { + updates.logo = `/uploads/${req.files.logo[0].filename}`; + } + if (req.files.favicon) { + updates.favicon = `/uploads/${req.files.favicon[0].filename}`; + } + + // Update existing settings with new values + Object.keys(updates).forEach(key => { + if (typeof updates[key] === 'object' && updates[key] !== null) { + settings[key] = { ...settings[key], ...updates[key] }; + } else { + settings[key] = updates[key]; + } + }); + + await settings.save(); + + res.json({ success: true, settings }); + } catch (error) { + console.error('Settings update error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/api/admin_20251021214113.js b/.history/routes/api/admin_20251021214113.js new file mode 100644 index 0000000..f7fc941 --- /dev/null +++ b/.history/routes/api/admin_20251021214113.js @@ -0,0 +1,388 @@ +const express = require('express'); +const router = express.Router(); +const multer = require('multer'); +const path = require('path'); +const { Portfolio, Service, Contact, User } = require('../../models'); + +// Multer configuration for file uploads +const storage = multer.diskStorage({ + destination: (req, file, cb) => { + cb(null, 'public/uploads/'); + }, + filename: (req, file, cb) => { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); + cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname)); + } +}); + +const upload = multer({ + storage: storage, + fileFilter: (req, file, cb) => { + if (file.mimetype.startsWith('image/')) { + cb(null, true); + } else { + cb(new Error('Only image files are allowed!'), false); + } + }, + limits: { + fileSize: 10 * 1024 * 1024 // 10MB + } +}); + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.status(401).json({ success: false, message: 'Authentication required' }); + } + next(); +}; + +// Portfolio API Routes +router.post('/portfolio', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const { + title, + shortDescription, + description, + category, + clientName, + projectUrl, + githubUrl, + technologies, + featured, + isPublished + } = req.body; + + // Process uploaded images + const images = req.files ? req.files.map((file, index) => ({ + url: `/uploads/${file.filename}`, + alt: `${title} image ${index + 1}`, + isPrimary: index === 0 + })) : []; + + // Parse technologies + let techArray = []; + if (technologies) { + try { + techArray = JSON.parse(technologies); + } catch (e) { + techArray = technologies.split(',').map(t => t.trim()); + } + } + + const portfolio = await Portfolio.create({ + title, + shortDescription, + description, + category, + clientName, + projectUrl: projectUrl || null, + githubUrl: githubUrl || null, + technologies: techArray, + images, + featured: featured === 'on', + isPublished: isPublished === 'on', + status: 'completed', + publishedAt: isPublished === 'on' ? new Date() : null + }); + + res.json({ success: true, portfolio }); + } catch (error) { + console.error('Portfolio creation error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +router.patch('/portfolio/:id', requireAuth, upload.array('images', 10), async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ success: false, message: 'Portfolio not found' }); + } + + const updates = { ...req.body }; + + // Handle checkboxes + updates.featured = updates.featured === 'on'; + updates.isPublished = updates.isPublished === 'on'; + + // Process technologies + if (updates.technologies) { + try { + updates.technologies = JSON.parse(updates.technologies); + } catch (e) { + updates.technologies = updates.technologies.split(',').map(t => t.trim()); + } + } + + // Process new images + if (req.files && req.files.length > 0) { + const newImages = req.files.map((file, index) => ({ + url: `/uploads/${file.filename}`, + alt: `${updates.title || portfolio.title} image ${index + 1}`, + isPrimary: index === 0 && (!portfolio.images || portfolio.images.length === 0) + })); + + updates.images = [...(portfolio.images || []), ...newImages]; + } + + await portfolio.update(updates); + res.json({ success: true, portfolio }); + } catch (error) { + console.error('Portfolio update error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +router.delete('/portfolio/:id', requireAuth, async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + if (!portfolio) { + return res.status(404).json({ success: false, message: 'Portfolio not found' }); + } + + await portfolio.destroy(); + res.json({ success: true }); + } catch (error) { + console.error('Portfolio deletion error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +// Services API Routes +router.post('/services', requireAuth, async (req, res) => { + try { + const { + name, + description, + shortDescription, + icon, + category, + features, + pricing, + estimatedTime, + isActive, + featured, + tags + } = req.body; + + // Parse arrays + let featuresArray = []; + let tagsArray = []; + let pricingObj = {}; + + if (features) { + try { + featuresArray = JSON.parse(features); + } catch (e) { + featuresArray = features.split(',').map(f => f.trim()); + } + } + + if (tags) { + try { + tagsArray = JSON.parse(tags); + } catch (e) { + tagsArray = tags.split(',').map(t => t.trim()); + } + } + + if (pricing) { + try { + pricingObj = JSON.parse(pricing); + } catch (e) { + pricingObj = { basePrice: pricing }; + } + } + + const service = await Service.create({ + name, + description, + shortDescription, + icon, + category, + features: featuresArray, + pricing: pricingObj, + estimatedTime, + isActive: isActive === 'on', + featured: featured === 'on', + tags: tagsArray + }); + + res.json({ success: true, service }); + } catch (error) { + console.error('Service creation error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +router.delete('/services/:id', requireAuth, async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + if (!service) { + return res.status(404).json({ success: false, message: 'Service not found' }); + } + + await service.destroy(); + res.json({ success: true }); + } catch (error) { + console.error('Service deletion error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +// Contacts API Routes +router.patch('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + if (!contact) { + return res.status(404).json({ success: false, message: 'Contact not found' }); + } + + await contact.update(req.body); + res.json({ success: true, contact }); + } catch (error) { + console.error('Contact update error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +router.delete('/contacts/:id', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + if (!contact) { + return res.status(404).json({ success: false, message: 'Contact not found' }); + } + + await contact.destroy(); + res.json({ success: true }); + } catch (error) { + console.error('Contact deletion error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +// Telegram notification for contact +router.post('/contacts/:id/telegram', requireAuth, async (req, res) => { + try { + const contact = await Contact.findByPk(req.params.id); + if (!contact) { + return res.status(404).json({ success: false, message: 'Contact not found' }); + } + + // Send Telegram notification + const telegramService = require('../../services/telegram'); + const result = await telegramService.sendContactNotification(contact); + + if (result.success) { + res.json({ success: true }); + } else { + res.status(500).json({ success: false, message: result.message || result.error }); + } + } catch (error) { + console.error('Telegram notification error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +// Test Telegram connection +router.post('/telegram/test', requireAuth, async (req, res) => { + try { + const { botToken, chatId } = req.body; + + // Temporarily set up telegram service with provided credentials + const axios = require('axios'); + + // Test bot info + const botResponse = await axios.get(`https://api.telegram.org/bot${botToken}/getMe`); + + // Test sending a message + const testMessage = '✅ Telegram bot подключен успешно!\n\nЭто тестовое сообщение от SmartSolTech Admin Panel.'; + await axios.post(`https://api.telegram.org/bot${botToken}/sendMessage`, { + chat_id: chatId, + text: testMessage, + parse_mode: 'Markdown' + }); + + res.json({ + success: true, + bot: botResponse.data.result, + message: 'Test message sent successfully' + }); + } catch (error) { + console.error('Telegram test error:', error); + let message = 'Connection failed'; + + if (error.response?.data?.description) { + message = error.response.data.description; + } else if (error.message) { + message = error.message; + } + + res.status(400).json({ success: false, message }); + } +}); + +// Settings API +const { SiteSettings } = require('../../models'); + +router.get('/settings', requireAuth, async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + res.json({ success: true, settings }); + } catch (error) { + console.error('Settings fetch error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +router.post('/settings', requireAuth, upload.fields([ + { name: 'logo', maxCount: 1 }, + { name: 'favicon', maxCount: 1 } +]), async (req, res) => { + try { + let settings = await SiteSettings.findOne(); + if (!settings) { + settings = await SiteSettings.create({}); + } + + const updates = {}; + + // Handle nested objects + Object.keys(req.body).forEach(key => { + if (key.includes('.')) { + const [parent, child] = key.split('.'); + if (!updates[parent]) updates[parent] = {}; + updates[parent][child] = req.body[key]; + } else { + updates[key] = req.body[key]; + } + }); + + // Handle file uploads + if (req.files.logo) { + updates.logo = `/uploads/${req.files.logo[0].filename}`; + } + if (req.files.favicon) { + updates.favicon = `/uploads/${req.files.favicon[0].filename}`; + } + + // Update existing settings with new values + Object.keys(updates).forEach(key => { + if (typeof updates[key] === 'object' && updates[key] !== null) { + settings[key] = { ...settings[key], ...updates[key] }; + } else { + settings[key] = updates[key]; + } + }); + + await settings.save(); + + res.json({ success: true, settings }); + } catch (error) { + console.error('Settings update error:', error); + res.status(500).json({ success: false, message: error.message }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/auth_20251019204627.js b/.history/routes/auth_20251019204627.js new file mode 100644 index 0000000..7363fcf --- /dev/null +++ b/.history/routes/auth_20251019204627.js @@ -0,0 +1,201 @@ +const express = require('express'); +const router = express.Router(); +const jwt = require('jsonwebtoken'); +const { body, validationResult } = require('express-validator'); +const { User } = require('../models'); + +// Login validation rules +const loginValidation = [ + body('email').isEmail().normalizeEmail(), + body('password').isLength({ min: 6 }) +]; + +// Login +router.post('/login', loginValidation, async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { email, password } = req.body; + + // Find user + const user = await User.findOne({ email, isActive: true }); + if (!user) { + return res.status(401).json({ + success: false, + message: 'Invalid credentials' + }); + } + + // Check password + const isValidPassword = await user.comparePassword(password); + if (!isValidPassword) { + return res.status(401).json({ + success: false, + message: 'Invalid credentials' + }); + } + + // Update last login + await user.updateLastLogin(); + + // Create JWT token + const token = jwt.sign( + { userId: user._id, email: user.email, role: user.role }, + process.env.JWT_SECRET, + { expiresIn: '7d' } + ); + + // Set session + req.session.user = { + id: user._id, + email: user.email, + name: user.name, + role: user.role + }; + + res.json({ + success: true, + message: 'Login successful', + token, + user: { + id: user._id, + email: user.email, + name: user.name, + role: user.role, + avatar: user.avatar + } + }); + } catch (error) { + console.error('Login error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +// Logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + return res.status(500).json({ + success: false, + message: 'Could not log out' + }); + } + + res.clearCookie('connect.sid'); + res.json({ + success: true, + message: 'Logout successful' + }); + }); +}); + +// Check authentication status +router.get('/me', async (req, res) => { + try { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: 'Not authenticated' + }); + } + + const user = await User.findById(req.session.user.id) + .select('-password'); + + if (!user || !user.isActive) { + req.session.destroy(); + return res.status(401).json({ + success: false, + message: 'User not found or inactive' + }); + } + + res.json({ + success: true, + user: { + id: user._id, + email: user.email, + name: user.name, + role: user.role, + avatar: user.avatar, + lastLogin: user.lastLogin + } + }); + } catch (error) { + console.error('Auth check error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +// Change password +router.put('/change-password', [ + body('currentPassword').isLength({ min: 6 }), + body('newPassword').isLength({ min: 6 }) +], async (req, res) => { + try { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: 'Not authenticated' + }); + } + + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { currentPassword, newPassword } = req.body; + const user = await User.findById(req.session.user.id); + + if (!user) { + return res.status(404).json({ + success: false, + message: 'User not found' + }); + } + + // Verify current password + const isValidPassword = await user.comparePassword(currentPassword); + if (!isValidPassword) { + return res.status(400).json({ + success: false, + message: 'Current password is incorrect' + }); + } + + // Update password + user.password = newPassword; + await user.save(); + + res.json({ + success: true, + message: 'Password updated successfully' + }); + } catch (error) { + console.error('Change password error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/auth_20251019204633.js b/.history/routes/auth_20251019204633.js new file mode 100644 index 0000000..790c0b2 --- /dev/null +++ b/.history/routes/auth_20251019204633.js @@ -0,0 +1,206 @@ +const express = require('express'); +const router = express.Router(); +const jwt = require('jsonwebtoken'); +const { body, validationResult } = require('express-validator'); +const { User } = require('../models'); + +// Login validation rules +const loginValidation = [ + body('email').isEmail().normalizeEmail(), + body('password').isLength({ min: 6 }) +]; + +// Login +router.post('/login', loginValidation, async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { email, password } = req.body; + + // Find user + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user) { + return res.status(401).json({ + success: false, + message: 'Invalid credentials' + }); + } + + // Check password + const isValidPassword = await user.comparePassword(password); + if (!isValidPassword) { + return res.status(401).json({ + success: false, + message: 'Invalid credentials' + }); + } + + // Update last login + await user.updateLastLogin(); + + // Create JWT token + const token = jwt.sign( + { userId: user._id, email: user.email, role: user.role }, + process.env.JWT_SECRET, + { expiresIn: '7d' } + ); + + // Set session + req.session.user = { + id: user._id, + email: user.email, + name: user.name, + role: user.role + }; + + res.json({ + success: true, + message: 'Login successful', + token, + user: { + id: user._id, + email: user.email, + name: user.name, + role: user.role, + avatar: user.avatar + } + }); + } catch (error) { + console.error('Login error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +// Logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + return res.status(500).json({ + success: false, + message: 'Could not log out' + }); + } + + res.clearCookie('connect.sid'); + res.json({ + success: true, + message: 'Logout successful' + }); + }); +}); + +// Check authentication status +router.get('/me', async (req, res) => { + try { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: 'Not authenticated' + }); + } + + const user = await User.findById(req.session.user.id) + .select('-password'); + + if (!user || !user.isActive) { + req.session.destroy(); + return res.status(401).json({ + success: false, + message: 'User not found or inactive' + }); + } + + res.json({ + success: true, + user: { + id: user._id, + email: user.email, + name: user.name, + role: user.role, + avatar: user.avatar, + lastLogin: user.lastLogin + } + }); + } catch (error) { + console.error('Auth check error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +// Change password +router.put('/change-password', [ + body('currentPassword').isLength({ min: 6 }), + body('newPassword').isLength({ min: 6 }) +], async (req, res) => { + try { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: 'Not authenticated' + }); + } + + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { currentPassword, newPassword } = req.body; + const user = await User.findById(req.session.user.id); + + if (!user) { + return res.status(404).json({ + success: false, + message: 'User not found' + }); + } + + // Verify current password + const isValidPassword = await user.comparePassword(currentPassword); + if (!isValidPassword) { + return res.status(400).json({ + success: false, + message: 'Current password is incorrect' + }); + } + + // Update password + user.password = newPassword; + await user.save(); + + res.json({ + success: true, + message: 'Password updated successfully' + }); + } catch (error) { + console.error('Change password error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/auth_20251019204648.js b/.history/routes/auth_20251019204648.js new file mode 100644 index 0000000..87df84d --- /dev/null +++ b/.history/routes/auth_20251019204648.js @@ -0,0 +1,207 @@ +const express = require('express'); +const router = express.Router(); +const jwt = require('jsonwebtoken'); +const { body, validationResult } = require('express-validator'); +const { User } = require('../models'); + +// Login validation rules +const loginValidation = [ + body('email').isEmail().normalizeEmail(), + body('password').isLength({ min: 6 }) +]; + +// Login +router.post('/login', loginValidation, async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { email, password } = req.body; + + // Find user + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user) { + return res.status(401).json({ + success: false, + message: 'Invalid credentials' + }); + } + + // Check password + const isValidPassword = await user.comparePassword(password); + if (!isValidPassword) { + return res.status(401).json({ + success: false, + message: 'Invalid credentials' + }); + } + + // Update last login + await user.updateLastLogin(); + + // Create JWT token + const token = jwt.sign( + { userId: user._id, email: user.email, role: user.role }, + process.env.JWT_SECRET, + { expiresIn: '7d' } + ); + + // Set session + req.session.user = { + id: user._id, + email: user.email, + name: user.name, + role: user.role + }; + + res.json({ + success: true, + message: 'Login successful', + token, + user: { + id: user._id, + email: user.email, + name: user.name, + role: user.role, + avatar: user.avatar + } + }); + } catch (error) { + console.error('Login error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +// Logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + return res.status(500).json({ + success: false, + message: 'Could not log out' + }); + } + + res.clearCookie('connect.sid'); + res.json({ + success: true, + message: 'Logout successful' + }); + }); +}); + +// Check authentication status +router.get('/me', async (req, res) => { + try { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: 'Not authenticated' + }); + } + + const user = await User.findByPk(req.session.user.id, { + attributes: { exclude: ['password'] } + }); + + if (!user || !user.isActive) { + req.session.destroy(); + return res.status(401).json({ + success: false, + message: 'User not found or inactive' + }); + } + + res.json({ + success: true, + user: { + id: user._id, + email: user.email, + name: user.name, + role: user.role, + avatar: user.avatar, + lastLogin: user.lastLogin + } + }); + } catch (error) { + console.error('Auth check error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +// Change password +router.put('/change-password', [ + body('currentPassword').isLength({ min: 6 }), + body('newPassword').isLength({ min: 6 }) +], async (req, res) => { + try { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: 'Not authenticated' + }); + } + + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { currentPassword, newPassword } = req.body; + const user = await User.findById(req.session.user.id); + + if (!user) { + return res.status(404).json({ + success: false, + message: 'User not found' + }); + } + + // Verify current password + const isValidPassword = await user.comparePassword(currentPassword); + if (!isValidPassword) { + return res.status(400).json({ + success: false, + message: 'Current password is incorrect' + }); + } + + // Update password + user.password = newPassword; + await user.save(); + + res.json({ + success: true, + message: 'Password updated successfully' + }); + } catch (error) { + console.error('Change password error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/auth_20251019204658.js b/.history/routes/auth_20251019204658.js new file mode 100644 index 0000000..7a71b05 --- /dev/null +++ b/.history/routes/auth_20251019204658.js @@ -0,0 +1,207 @@ +const express = require('express'); +const router = express.Router(); +const jwt = require('jsonwebtoken'); +const { body, validationResult } = require('express-validator'); +const { User } = require('../models'); + +// Login validation rules +const loginValidation = [ + body('email').isEmail().normalizeEmail(), + body('password').isLength({ min: 6 }) +]; + +// Login +router.post('/login', loginValidation, async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { email, password } = req.body; + + // Find user + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user) { + return res.status(401).json({ + success: false, + message: 'Invalid credentials' + }); + } + + // Check password + const isValidPassword = await user.comparePassword(password); + if (!isValidPassword) { + return res.status(401).json({ + success: false, + message: 'Invalid credentials' + }); + } + + // Update last login + await user.updateLastLogin(); + + // Create JWT token + const token = jwt.sign( + { userId: user._id, email: user.email, role: user.role }, + process.env.JWT_SECRET, + { expiresIn: '7d' } + ); + + // Set session + req.session.user = { + id: user._id, + email: user.email, + name: user.name, + role: user.role + }; + + res.json({ + success: true, + message: 'Login successful', + token, + user: { + id: user._id, + email: user.email, + name: user.name, + role: user.role, + avatar: user.avatar + } + }); + } catch (error) { + console.error('Login error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +// Logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + return res.status(500).json({ + success: false, + message: 'Could not log out' + }); + } + + res.clearCookie('connect.sid'); + res.json({ + success: true, + message: 'Logout successful' + }); + }); +}); + +// Check authentication status +router.get('/me', async (req, res) => { + try { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: 'Not authenticated' + }); + } + + const user = await User.findByPk(req.session.user.id, { + attributes: { exclude: ['password'] } + }); + + if (!user || !user.isActive) { + req.session.destroy(); + return res.status(401).json({ + success: false, + message: 'User not found or inactive' + }); + } + + res.json({ + success: true, + user: { + id: user._id, + email: user.email, + name: user.name, + role: user.role, + avatar: user.avatar, + lastLogin: user.lastLogin + } + }); + } catch (error) { + console.error('Auth check error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +// Change password +router.put('/change-password', [ + body('currentPassword').isLength({ min: 6 }), + body('newPassword').isLength({ min: 6 }) +], async (req, res) => { + try { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: 'Not authenticated' + }); + } + + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { currentPassword, newPassword } = req.body; + const user = await User.findByPk(req.session.user.id); + + if (!user) { + return res.status(404).json({ + success: false, + message: 'User not found' + }); + } + + // Verify current password + const isValidPassword = await user.comparePassword(currentPassword); + if (!isValidPassword) { + return res.status(400).json({ + success: false, + message: 'Current password is incorrect' + }); + } + + // Update password + user.password = newPassword; + await user.save(); + + res.json({ + success: true, + message: 'Password updated successfully' + }); + } catch (error) { + console.error('Change password error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/auth_20251019204806.js b/.history/routes/auth_20251019204806.js new file mode 100644 index 0000000..7a71b05 --- /dev/null +++ b/.history/routes/auth_20251019204806.js @@ -0,0 +1,207 @@ +const express = require('express'); +const router = express.Router(); +const jwt = require('jsonwebtoken'); +const { body, validationResult } = require('express-validator'); +const { User } = require('../models'); + +// Login validation rules +const loginValidation = [ + body('email').isEmail().normalizeEmail(), + body('password').isLength({ min: 6 }) +]; + +// Login +router.post('/login', loginValidation, async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { email, password } = req.body; + + // Find user + const user = await User.findOne({ + where: { + email: email, + isActive: true + } + }); + if (!user) { + return res.status(401).json({ + success: false, + message: 'Invalid credentials' + }); + } + + // Check password + const isValidPassword = await user.comparePassword(password); + if (!isValidPassword) { + return res.status(401).json({ + success: false, + message: 'Invalid credentials' + }); + } + + // Update last login + await user.updateLastLogin(); + + // Create JWT token + const token = jwt.sign( + { userId: user._id, email: user.email, role: user.role }, + process.env.JWT_SECRET, + { expiresIn: '7d' } + ); + + // Set session + req.session.user = { + id: user._id, + email: user.email, + name: user.name, + role: user.role + }; + + res.json({ + success: true, + message: 'Login successful', + token, + user: { + id: user._id, + email: user.email, + name: user.name, + role: user.role, + avatar: user.avatar + } + }); + } catch (error) { + console.error('Login error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +// Logout +router.post('/logout', (req, res) => { + req.session.destroy(err => { + if (err) { + return res.status(500).json({ + success: false, + message: 'Could not log out' + }); + } + + res.clearCookie('connect.sid'); + res.json({ + success: true, + message: 'Logout successful' + }); + }); +}); + +// Check authentication status +router.get('/me', async (req, res) => { + try { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: 'Not authenticated' + }); + } + + const user = await User.findByPk(req.session.user.id, { + attributes: { exclude: ['password'] } + }); + + if (!user || !user.isActive) { + req.session.destroy(); + return res.status(401).json({ + success: false, + message: 'User not found or inactive' + }); + } + + res.json({ + success: true, + user: { + id: user._id, + email: user.email, + name: user.name, + role: user.role, + avatar: user.avatar, + lastLogin: user.lastLogin + } + }); + } catch (error) { + console.error('Auth check error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +// Change password +router.put('/change-password', [ + body('currentPassword').isLength({ min: 6 }), + body('newPassword').isLength({ min: 6 }) +], async (req, res) => { + try { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: 'Not authenticated' + }); + } + + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { currentPassword, newPassword } = req.body; + const user = await User.findByPk(req.session.user.id); + + if (!user) { + return res.status(404).json({ + success: false, + message: 'User not found' + }); + } + + // Verify current password + const isValidPassword = await user.comparePassword(currentPassword); + if (!isValidPassword) { + return res.status(400).json({ + success: false, + message: 'Current password is incorrect' + }); + } + + // Update password + user.password = newPassword; + await user.save(); + + res.json({ + success: true, + message: 'Password updated successfully' + }); + } catch (error) { + console.error('Change password error:', error); + res.status(500).json({ + success: false, + message: 'Server error' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/calculator_20251019204545.js b/.history/routes/calculator_20251019204545.js new file mode 100644 index 0000000..38732f0 --- /dev/null +++ b/.history/routes/calculator_20251019204545.js @@ -0,0 +1,312 @@ +const express = require('express'); +const router = express.Router(); +const { Service } = require('../models'); + +// Get all services for calculator +router.get('/services', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .select('name pricing category features estimatedTime') + .sort({ category: 1, name: 1 }); + + const servicesByCategory = services.reduce((acc, service) => { + if (!acc[service.category]) { + acc[service.category] = []; + } + acc[service.category].push(service); + return acc; + }, {}); + + res.json({ + success: true, + services: servicesByCategory, + allServices: services + }); + } catch (error) { + console.error('Calculator services error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching services' + }); + } +}); + +// Calculate project estimate +router.post('/calculate', async (req, res) => { + try { + const { + selectedServices = [], + projectComplexity = 'medium', + timeline = 'standard', + additionalFeatures = [], + customRequirements = '' + } = req.body; + + if (!selectedServices.length) { + return res.status(400).json({ + success: false, + message: 'Please select at least one service' + }); + } + + // Get selected services details + const services = await Service.find({ + _id: { $in: selectedServices }, + isActive: true + }); + + if (services.length !== selectedServices.length) { + return res.status(400).json({ + success: false, + message: 'Some selected services are not available' + }); + } + + // Calculate base cost + let baseCost = 0; + let totalTime = 0; // in days + + services.forEach(service => { + baseCost += service.pricing.basePrice; + totalTime += (service.estimatedTime.min + service.estimatedTime.max) / 2; + }); + + // Apply complexity multiplier + const complexityMultipliers = { + 'simple': 0.7, + 'medium': 1.0, + 'complex': 1.5, + 'enterprise': 2.0 + }; + + const complexityMultiplier = complexityMultipliers[projectComplexity] || 1.0; + + // Apply timeline multiplier + const timelineMultipliers = { + 'rush': 1.8, // Less than 2 weeks + 'fast': 1.4, // 2-4 weeks + 'standard': 1.0, // 1-3 months + 'flexible': 0.8 // 3+ months + }; + + const timelineMultiplier = timelineMultipliers[timeline] || 1.0; + + // Additional features cost + const featureCosts = { + 'seo-optimization': 300000, + 'analytics-setup': 150000, + 'social-integration': 200000, + 'payment-gateway': 500000, + 'multilingual': 400000, + 'admin-panel': 600000, + 'api-integration': 350000, + 'mobile-responsive': 250000, + 'ssl-certificate': 100000, + 'backup-system': 200000 + }; + + let additionalCost = 0; + additionalFeatures.forEach(feature => { + additionalCost += featureCosts[feature] || 0; + }); + + // Custom requirements cost (estimated based on description length and complexity) + let customCost = 0; + if (customRequirements && customRequirements.length > 50) { + customCost = Math.min(customRequirements.length * 1000, 1000000); // Max 1M KRW + } + + // Calculate final estimate + const subtotal = baseCost * complexityMultiplier * timelineMultiplier; + const total = subtotal + additionalCost + customCost; + + // Calculate time estimate + const timeMultiplier = complexityMultiplier * timelineMultiplier; + const estimatedDays = Math.ceil(totalTime * timeMultiplier); + const estimatedWeeks = Math.ceil(estimatedDays / 7); + + // Create price ranges (±20%) + const minPrice = Math.round(total * 0.8); + const maxPrice = Math.round(total * 1.2); + + // Breakdown + const breakdown = { + baseServices: Math.round(baseCost), + complexityAdjustment: Math.round(baseCost * (complexityMultiplier - 1)), + timelineAdjustment: Math.round(baseCost * complexityMultiplier * (timelineMultiplier - 1)), + additionalFeatures: additionalCost, + customRequirements: customCost, + subtotal: Math.round(subtotal), + total: Math.round(total) + }; + + const result = { + success: true, + estimate: { + total: Math.round(total), + range: { + min: minPrice, + max: maxPrice, + formatted: `₩${minPrice.toLocaleString()} - ₩${maxPrice.toLocaleString()}` + }, + breakdown, + timeline: { + days: estimatedDays, + weeks: estimatedWeeks, + formatted: estimatedWeeks === 1 ? '1 week' : `${estimatedWeeks} weeks` + }, + currency: 'KRW', + confidence: calculateConfidence(services.length, projectComplexity, customRequirements), + recommendations: generateRecommendations(projectComplexity, timeline, services) + }, + selectedServices: services.map(s => ({ + id: s._id, + name: s.name, + category: s.category, + basePrice: s.pricing.basePrice + })), + calculatedAt: new Date() + }; + + res.json(result); + } catch (error) { + console.error('Calculator calculation error:', error); + res.status(500).json({ + success: false, + message: 'Error calculating estimate' + }); + } +}); + +// Helper function to calculate confidence level +function calculateConfidence(serviceCount, complexity, customRequirements) { + let confidence = 85; // Base confidence + + // Adjust based on service count + if (serviceCount === 1) confidence += 10; + else if (serviceCount > 5) confidence -= 10; + + // Adjust based on complexity + if (complexity === 'simple') confidence += 10; + else if (complexity === 'enterprise') confidence -= 15; + + // Adjust based on custom requirements + if (customRequirements && customRequirements.length > 200) { + confidence -= 20; + } + + return Math.max(60, Math.min(95, confidence)); +} + +// Helper function to generate recommendations +function generateRecommendations(complexity, timeline, services) { + const recommendations = []; + + if (complexity === 'enterprise' && timeline === 'rush') { + recommendations.push('Consider extending timeline for enterprise-level projects to ensure quality'); + } + + if (services.length > 6) { + recommendations.push('Consider breaking down into phases for better project management'); + } + + if (timeline === 'flexible') { + recommendations.push('Flexible timeline allows for better cost optimization and quality assurance'); + } + + const hasDesign = services.some(s => s.category === 'design'); + const hasDevelopment = services.some(s => s.category === 'development'); + + if (hasDevelopment && !hasDesign) { + recommendations.push('Consider adding UI/UX design services for better user experience'); + } + + return recommendations; +} + +// Get pricing guidelines +router.get('/pricing-guide', async (req, res) => { + try { + const pricingGuide = { + serviceCategories: { + 'development': { + name: 'Web Development', + priceRange: '₩500,000 - ₩5,000,000', + description: 'Custom web applications and websites' + }, + 'design': { + name: 'UI/UX Design', + priceRange: '₩300,000 - ₩2,000,000', + description: 'User interface and experience design' + }, + 'marketing': { + name: 'Digital Marketing', + priceRange: '₩200,000 - ₩1,500,000', + description: 'SEO, social media, and online marketing' + }, + 'consulting': { + name: 'Technical Consulting', + priceRange: '₩150,000 - ₩1,000,000', + description: 'Technology strategy and consultation' + } + }, + complexityFactors: { + 'simple': { + name: 'Simple Project', + multiplier: '0.7x', + description: 'Basic functionality, standard design' + }, + 'medium': { + name: 'Medium Project', + multiplier: '1.0x', + description: 'Moderate complexity, custom features' + }, + 'complex': { + name: 'Complex Project', + multiplier: '1.5x', + description: 'Advanced features, integrations' + }, + 'enterprise': { + name: 'Enterprise Project', + multiplier: '2.0x', + description: 'Large scale, high complexity' + } + }, + timelineImpact: { + 'rush': { + name: 'Rush (< 2 weeks)', + multiplier: '+80%', + description: 'Requires overtime and priority handling' + }, + 'fast': { + name: 'Fast (2-4 weeks)', + multiplier: '+40%', + description: 'Accelerated timeline' + }, + 'standard': { + name: 'Standard (1-3 months)', + multiplier: 'Standard', + description: 'Normal project timeline' + }, + 'flexible': { + name: 'Flexible (3+ months)', + multiplier: '-20%', + description: 'Extended timeline allows optimization' + } + } + }; + + res.json({ + success: true, + pricingGuide + }); + } catch (error) { + console.error('Pricing guide error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching pricing guide' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/calculator_20251019204551.js b/.history/routes/calculator_20251019204551.js new file mode 100644 index 0000000..cb31511 --- /dev/null +++ b/.history/routes/calculator_20251019204551.js @@ -0,0 +1,314 @@ +const express = require('express'); +const router = express.Router(); +const { Service } = require('../models'); + +// Get all services for calculator +router.get('/services', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category', 'features', 'estimatedTime'], + order: [['category', 'ASC'], ['name', 'ASC']] + }); + + const servicesByCategory = services.reduce((acc, service) => { + if (!acc[service.category]) { + acc[service.category] = []; + } + acc[service.category].push(service); + return acc; + }, {}); + + res.json({ + success: true, + services: servicesByCategory, + allServices: services + }); + } catch (error) { + console.error('Calculator services error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching services' + }); + } +}); + +// Calculate project estimate +router.post('/calculate', async (req, res) => { + try { + const { + selectedServices = [], + projectComplexity = 'medium', + timeline = 'standard', + additionalFeatures = [], + customRequirements = '' + } = req.body; + + if (!selectedServices.length) { + return res.status(400).json({ + success: false, + message: 'Please select at least one service' + }); + } + + // Get selected services details + const services = await Service.find({ + _id: { $in: selectedServices }, + isActive: true + }); + + if (services.length !== selectedServices.length) { + return res.status(400).json({ + success: false, + message: 'Some selected services are not available' + }); + } + + // Calculate base cost + let baseCost = 0; + let totalTime = 0; // in days + + services.forEach(service => { + baseCost += service.pricing.basePrice; + totalTime += (service.estimatedTime.min + service.estimatedTime.max) / 2; + }); + + // Apply complexity multiplier + const complexityMultipliers = { + 'simple': 0.7, + 'medium': 1.0, + 'complex': 1.5, + 'enterprise': 2.0 + }; + + const complexityMultiplier = complexityMultipliers[projectComplexity] || 1.0; + + // Apply timeline multiplier + const timelineMultipliers = { + 'rush': 1.8, // Less than 2 weeks + 'fast': 1.4, // 2-4 weeks + 'standard': 1.0, // 1-3 months + 'flexible': 0.8 // 3+ months + }; + + const timelineMultiplier = timelineMultipliers[timeline] || 1.0; + + // Additional features cost + const featureCosts = { + 'seo-optimization': 300000, + 'analytics-setup': 150000, + 'social-integration': 200000, + 'payment-gateway': 500000, + 'multilingual': 400000, + 'admin-panel': 600000, + 'api-integration': 350000, + 'mobile-responsive': 250000, + 'ssl-certificate': 100000, + 'backup-system': 200000 + }; + + let additionalCost = 0; + additionalFeatures.forEach(feature => { + additionalCost += featureCosts[feature] || 0; + }); + + // Custom requirements cost (estimated based on description length and complexity) + let customCost = 0; + if (customRequirements && customRequirements.length > 50) { + customCost = Math.min(customRequirements.length * 1000, 1000000); // Max 1M KRW + } + + // Calculate final estimate + const subtotal = baseCost * complexityMultiplier * timelineMultiplier; + const total = subtotal + additionalCost + customCost; + + // Calculate time estimate + const timeMultiplier = complexityMultiplier * timelineMultiplier; + const estimatedDays = Math.ceil(totalTime * timeMultiplier); + const estimatedWeeks = Math.ceil(estimatedDays / 7); + + // Create price ranges (±20%) + const minPrice = Math.round(total * 0.8); + const maxPrice = Math.round(total * 1.2); + + // Breakdown + const breakdown = { + baseServices: Math.round(baseCost), + complexityAdjustment: Math.round(baseCost * (complexityMultiplier - 1)), + timelineAdjustment: Math.round(baseCost * complexityMultiplier * (timelineMultiplier - 1)), + additionalFeatures: additionalCost, + customRequirements: customCost, + subtotal: Math.round(subtotal), + total: Math.round(total) + }; + + const result = { + success: true, + estimate: { + total: Math.round(total), + range: { + min: minPrice, + max: maxPrice, + formatted: `₩${minPrice.toLocaleString()} - ₩${maxPrice.toLocaleString()}` + }, + breakdown, + timeline: { + days: estimatedDays, + weeks: estimatedWeeks, + formatted: estimatedWeeks === 1 ? '1 week' : `${estimatedWeeks} weeks` + }, + currency: 'KRW', + confidence: calculateConfidence(services.length, projectComplexity, customRequirements), + recommendations: generateRecommendations(projectComplexity, timeline, services) + }, + selectedServices: services.map(s => ({ + id: s._id, + name: s.name, + category: s.category, + basePrice: s.pricing.basePrice + })), + calculatedAt: new Date() + }; + + res.json(result); + } catch (error) { + console.error('Calculator calculation error:', error); + res.status(500).json({ + success: false, + message: 'Error calculating estimate' + }); + } +}); + +// Helper function to calculate confidence level +function calculateConfidence(serviceCount, complexity, customRequirements) { + let confidence = 85; // Base confidence + + // Adjust based on service count + if (serviceCount === 1) confidence += 10; + else if (serviceCount > 5) confidence -= 10; + + // Adjust based on complexity + if (complexity === 'simple') confidence += 10; + else if (complexity === 'enterprise') confidence -= 15; + + // Adjust based on custom requirements + if (customRequirements && customRequirements.length > 200) { + confidence -= 20; + } + + return Math.max(60, Math.min(95, confidence)); +} + +// Helper function to generate recommendations +function generateRecommendations(complexity, timeline, services) { + const recommendations = []; + + if (complexity === 'enterprise' && timeline === 'rush') { + recommendations.push('Consider extending timeline for enterprise-level projects to ensure quality'); + } + + if (services.length > 6) { + recommendations.push('Consider breaking down into phases for better project management'); + } + + if (timeline === 'flexible') { + recommendations.push('Flexible timeline allows for better cost optimization and quality assurance'); + } + + const hasDesign = services.some(s => s.category === 'design'); + const hasDevelopment = services.some(s => s.category === 'development'); + + if (hasDevelopment && !hasDesign) { + recommendations.push('Consider adding UI/UX design services for better user experience'); + } + + return recommendations; +} + +// Get pricing guidelines +router.get('/pricing-guide', async (req, res) => { + try { + const pricingGuide = { + serviceCategories: { + 'development': { + name: 'Web Development', + priceRange: '₩500,000 - ₩5,000,000', + description: 'Custom web applications and websites' + }, + 'design': { + name: 'UI/UX Design', + priceRange: '₩300,000 - ₩2,000,000', + description: 'User interface and experience design' + }, + 'marketing': { + name: 'Digital Marketing', + priceRange: '₩200,000 - ₩1,500,000', + description: 'SEO, social media, and online marketing' + }, + 'consulting': { + name: 'Technical Consulting', + priceRange: '₩150,000 - ₩1,000,000', + description: 'Technology strategy and consultation' + } + }, + complexityFactors: { + 'simple': { + name: 'Simple Project', + multiplier: '0.7x', + description: 'Basic functionality, standard design' + }, + 'medium': { + name: 'Medium Project', + multiplier: '1.0x', + description: 'Moderate complexity, custom features' + }, + 'complex': { + name: 'Complex Project', + multiplier: '1.5x', + description: 'Advanced features, integrations' + }, + 'enterprise': { + name: 'Enterprise Project', + multiplier: '2.0x', + description: 'Large scale, high complexity' + } + }, + timelineImpact: { + 'rush': { + name: 'Rush (< 2 weeks)', + multiplier: '+80%', + description: 'Requires overtime and priority handling' + }, + 'fast': { + name: 'Fast (2-4 weeks)', + multiplier: '+40%', + description: 'Accelerated timeline' + }, + 'standard': { + name: 'Standard (1-3 months)', + multiplier: 'Standard', + description: 'Normal project timeline' + }, + 'flexible': { + name: 'Flexible (3+ months)', + multiplier: '-20%', + description: 'Extended timeline allows optimization' + } + } + }; + + res.json({ + success: true, + pricingGuide + }); + } catch (error) { + console.error('Pricing guide error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching pricing guide' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/calculator_20251019204602.js b/.history/routes/calculator_20251019204602.js new file mode 100644 index 0000000..d701cda --- /dev/null +++ b/.history/routes/calculator_20251019204602.js @@ -0,0 +1,316 @@ +const express = require('express'); +const router = express.Router(); +const { Service } = require('../models'); + +// Get all services for calculator +router.get('/services', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category', 'features', 'estimatedTime'], + order: [['category', 'ASC'], ['name', 'ASC']] + }); + + const servicesByCategory = services.reduce((acc, service) => { + if (!acc[service.category]) { + acc[service.category] = []; + } + acc[service.category].push(service); + return acc; + }, {}); + + res.json({ + success: true, + services: servicesByCategory, + allServices: services + }); + } catch (error) { + console.error('Calculator services error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching services' + }); + } +}); + +// Calculate project estimate +router.post('/calculate', async (req, res) => { + try { + const { + selectedServices = [], + projectComplexity = 'medium', + timeline = 'standard', + additionalFeatures = [], + customRequirements = '' + } = req.body; + + if (!selectedServices.length) { + return res.status(400).json({ + success: false, + message: 'Please select at least one service' + }); + } + + // Get selected services details + const services = await Service.findAll({ + where: { + id: selectedServices, + isActive: true + } + }); + + if (services.length !== selectedServices.length) { + return res.status(400).json({ + success: false, + message: 'Some selected services are not available' + }); + } + + // Calculate base cost + let baseCost = 0; + let totalTime = 0; // in days + + services.forEach(service => { + baseCost += service.pricing.basePrice; + totalTime += (service.estimatedTime.min + service.estimatedTime.max) / 2; + }); + + // Apply complexity multiplier + const complexityMultipliers = { + 'simple': 0.7, + 'medium': 1.0, + 'complex': 1.5, + 'enterprise': 2.0 + }; + + const complexityMultiplier = complexityMultipliers[projectComplexity] || 1.0; + + // Apply timeline multiplier + const timelineMultipliers = { + 'rush': 1.8, // Less than 2 weeks + 'fast': 1.4, // 2-4 weeks + 'standard': 1.0, // 1-3 months + 'flexible': 0.8 // 3+ months + }; + + const timelineMultiplier = timelineMultipliers[timeline] || 1.0; + + // Additional features cost + const featureCosts = { + 'seo-optimization': 300000, + 'analytics-setup': 150000, + 'social-integration': 200000, + 'payment-gateway': 500000, + 'multilingual': 400000, + 'admin-panel': 600000, + 'api-integration': 350000, + 'mobile-responsive': 250000, + 'ssl-certificate': 100000, + 'backup-system': 200000 + }; + + let additionalCost = 0; + additionalFeatures.forEach(feature => { + additionalCost += featureCosts[feature] || 0; + }); + + // Custom requirements cost (estimated based on description length and complexity) + let customCost = 0; + if (customRequirements && customRequirements.length > 50) { + customCost = Math.min(customRequirements.length * 1000, 1000000); // Max 1M KRW + } + + // Calculate final estimate + const subtotal = baseCost * complexityMultiplier * timelineMultiplier; + const total = subtotal + additionalCost + customCost; + + // Calculate time estimate + const timeMultiplier = complexityMultiplier * timelineMultiplier; + const estimatedDays = Math.ceil(totalTime * timeMultiplier); + const estimatedWeeks = Math.ceil(estimatedDays / 7); + + // Create price ranges (±20%) + const minPrice = Math.round(total * 0.8); + const maxPrice = Math.round(total * 1.2); + + // Breakdown + const breakdown = { + baseServices: Math.round(baseCost), + complexityAdjustment: Math.round(baseCost * (complexityMultiplier - 1)), + timelineAdjustment: Math.round(baseCost * complexityMultiplier * (timelineMultiplier - 1)), + additionalFeatures: additionalCost, + customRequirements: customCost, + subtotal: Math.round(subtotal), + total: Math.round(total) + }; + + const result = { + success: true, + estimate: { + total: Math.round(total), + range: { + min: minPrice, + max: maxPrice, + formatted: `₩${minPrice.toLocaleString()} - ₩${maxPrice.toLocaleString()}` + }, + breakdown, + timeline: { + days: estimatedDays, + weeks: estimatedWeeks, + formatted: estimatedWeeks === 1 ? '1 week' : `${estimatedWeeks} weeks` + }, + currency: 'KRW', + confidence: calculateConfidence(services.length, projectComplexity, customRequirements), + recommendations: generateRecommendations(projectComplexity, timeline, services) + }, + selectedServices: services.map(s => ({ + id: s._id, + name: s.name, + category: s.category, + basePrice: s.pricing.basePrice + })), + calculatedAt: new Date() + }; + + res.json(result); + } catch (error) { + console.error('Calculator calculation error:', error); + res.status(500).json({ + success: false, + message: 'Error calculating estimate' + }); + } +}); + +// Helper function to calculate confidence level +function calculateConfidence(serviceCount, complexity, customRequirements) { + let confidence = 85; // Base confidence + + // Adjust based on service count + if (serviceCount === 1) confidence += 10; + else if (serviceCount > 5) confidence -= 10; + + // Adjust based on complexity + if (complexity === 'simple') confidence += 10; + else if (complexity === 'enterprise') confidence -= 15; + + // Adjust based on custom requirements + if (customRequirements && customRequirements.length > 200) { + confidence -= 20; + } + + return Math.max(60, Math.min(95, confidence)); +} + +// Helper function to generate recommendations +function generateRecommendations(complexity, timeline, services) { + const recommendations = []; + + if (complexity === 'enterprise' && timeline === 'rush') { + recommendations.push('Consider extending timeline for enterprise-level projects to ensure quality'); + } + + if (services.length > 6) { + recommendations.push('Consider breaking down into phases for better project management'); + } + + if (timeline === 'flexible') { + recommendations.push('Flexible timeline allows for better cost optimization and quality assurance'); + } + + const hasDesign = services.some(s => s.category === 'design'); + const hasDevelopment = services.some(s => s.category === 'development'); + + if (hasDevelopment && !hasDesign) { + recommendations.push('Consider adding UI/UX design services for better user experience'); + } + + return recommendations; +} + +// Get pricing guidelines +router.get('/pricing-guide', async (req, res) => { + try { + const pricingGuide = { + serviceCategories: { + 'development': { + name: 'Web Development', + priceRange: '₩500,000 - ₩5,000,000', + description: 'Custom web applications and websites' + }, + 'design': { + name: 'UI/UX Design', + priceRange: '₩300,000 - ₩2,000,000', + description: 'User interface and experience design' + }, + 'marketing': { + name: 'Digital Marketing', + priceRange: '₩200,000 - ₩1,500,000', + description: 'SEO, social media, and online marketing' + }, + 'consulting': { + name: 'Technical Consulting', + priceRange: '₩150,000 - ₩1,000,000', + description: 'Technology strategy and consultation' + } + }, + complexityFactors: { + 'simple': { + name: 'Simple Project', + multiplier: '0.7x', + description: 'Basic functionality, standard design' + }, + 'medium': { + name: 'Medium Project', + multiplier: '1.0x', + description: 'Moderate complexity, custom features' + }, + 'complex': { + name: 'Complex Project', + multiplier: '1.5x', + description: 'Advanced features, integrations' + }, + 'enterprise': { + name: 'Enterprise Project', + multiplier: '2.0x', + description: 'Large scale, high complexity' + } + }, + timelineImpact: { + 'rush': { + name: 'Rush (< 2 weeks)', + multiplier: '+80%', + description: 'Requires overtime and priority handling' + }, + 'fast': { + name: 'Fast (2-4 weeks)', + multiplier: '+40%', + description: 'Accelerated timeline' + }, + 'standard': { + name: 'Standard (1-3 months)', + multiplier: 'Standard', + description: 'Normal project timeline' + }, + 'flexible': { + name: 'Flexible (3+ months)', + multiplier: '-20%', + description: 'Extended timeline allows optimization' + } + } + }; + + res.json({ + success: true, + pricingGuide + }); + } catch (error) { + console.error('Pricing guide error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching pricing guide' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/calculator_20251019204805.js b/.history/routes/calculator_20251019204805.js new file mode 100644 index 0000000..d701cda --- /dev/null +++ b/.history/routes/calculator_20251019204805.js @@ -0,0 +1,316 @@ +const express = require('express'); +const router = express.Router(); +const { Service } = require('../models'); + +// Get all services for calculator +router.get('/services', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category', 'features', 'estimatedTime'], + order: [['category', 'ASC'], ['name', 'ASC']] + }); + + const servicesByCategory = services.reduce((acc, service) => { + if (!acc[service.category]) { + acc[service.category] = []; + } + acc[service.category].push(service); + return acc; + }, {}); + + res.json({ + success: true, + services: servicesByCategory, + allServices: services + }); + } catch (error) { + console.error('Calculator services error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching services' + }); + } +}); + +// Calculate project estimate +router.post('/calculate', async (req, res) => { + try { + const { + selectedServices = [], + projectComplexity = 'medium', + timeline = 'standard', + additionalFeatures = [], + customRequirements = '' + } = req.body; + + if (!selectedServices.length) { + return res.status(400).json({ + success: false, + message: 'Please select at least one service' + }); + } + + // Get selected services details + const services = await Service.findAll({ + where: { + id: selectedServices, + isActive: true + } + }); + + if (services.length !== selectedServices.length) { + return res.status(400).json({ + success: false, + message: 'Some selected services are not available' + }); + } + + // Calculate base cost + let baseCost = 0; + let totalTime = 0; // in days + + services.forEach(service => { + baseCost += service.pricing.basePrice; + totalTime += (service.estimatedTime.min + service.estimatedTime.max) / 2; + }); + + // Apply complexity multiplier + const complexityMultipliers = { + 'simple': 0.7, + 'medium': 1.0, + 'complex': 1.5, + 'enterprise': 2.0 + }; + + const complexityMultiplier = complexityMultipliers[projectComplexity] || 1.0; + + // Apply timeline multiplier + const timelineMultipliers = { + 'rush': 1.8, // Less than 2 weeks + 'fast': 1.4, // 2-4 weeks + 'standard': 1.0, // 1-3 months + 'flexible': 0.8 // 3+ months + }; + + const timelineMultiplier = timelineMultipliers[timeline] || 1.0; + + // Additional features cost + const featureCosts = { + 'seo-optimization': 300000, + 'analytics-setup': 150000, + 'social-integration': 200000, + 'payment-gateway': 500000, + 'multilingual': 400000, + 'admin-panel': 600000, + 'api-integration': 350000, + 'mobile-responsive': 250000, + 'ssl-certificate': 100000, + 'backup-system': 200000 + }; + + let additionalCost = 0; + additionalFeatures.forEach(feature => { + additionalCost += featureCosts[feature] || 0; + }); + + // Custom requirements cost (estimated based on description length and complexity) + let customCost = 0; + if (customRequirements && customRequirements.length > 50) { + customCost = Math.min(customRequirements.length * 1000, 1000000); // Max 1M KRW + } + + // Calculate final estimate + const subtotal = baseCost * complexityMultiplier * timelineMultiplier; + const total = subtotal + additionalCost + customCost; + + // Calculate time estimate + const timeMultiplier = complexityMultiplier * timelineMultiplier; + const estimatedDays = Math.ceil(totalTime * timeMultiplier); + const estimatedWeeks = Math.ceil(estimatedDays / 7); + + // Create price ranges (±20%) + const minPrice = Math.round(total * 0.8); + const maxPrice = Math.round(total * 1.2); + + // Breakdown + const breakdown = { + baseServices: Math.round(baseCost), + complexityAdjustment: Math.round(baseCost * (complexityMultiplier - 1)), + timelineAdjustment: Math.round(baseCost * complexityMultiplier * (timelineMultiplier - 1)), + additionalFeatures: additionalCost, + customRequirements: customCost, + subtotal: Math.round(subtotal), + total: Math.round(total) + }; + + const result = { + success: true, + estimate: { + total: Math.round(total), + range: { + min: minPrice, + max: maxPrice, + formatted: `₩${minPrice.toLocaleString()} - ₩${maxPrice.toLocaleString()}` + }, + breakdown, + timeline: { + days: estimatedDays, + weeks: estimatedWeeks, + formatted: estimatedWeeks === 1 ? '1 week' : `${estimatedWeeks} weeks` + }, + currency: 'KRW', + confidence: calculateConfidence(services.length, projectComplexity, customRequirements), + recommendations: generateRecommendations(projectComplexity, timeline, services) + }, + selectedServices: services.map(s => ({ + id: s._id, + name: s.name, + category: s.category, + basePrice: s.pricing.basePrice + })), + calculatedAt: new Date() + }; + + res.json(result); + } catch (error) { + console.error('Calculator calculation error:', error); + res.status(500).json({ + success: false, + message: 'Error calculating estimate' + }); + } +}); + +// Helper function to calculate confidence level +function calculateConfidence(serviceCount, complexity, customRequirements) { + let confidence = 85; // Base confidence + + // Adjust based on service count + if (serviceCount === 1) confidence += 10; + else if (serviceCount > 5) confidence -= 10; + + // Adjust based on complexity + if (complexity === 'simple') confidence += 10; + else if (complexity === 'enterprise') confidence -= 15; + + // Adjust based on custom requirements + if (customRequirements && customRequirements.length > 200) { + confidence -= 20; + } + + return Math.max(60, Math.min(95, confidence)); +} + +// Helper function to generate recommendations +function generateRecommendations(complexity, timeline, services) { + const recommendations = []; + + if (complexity === 'enterprise' && timeline === 'rush') { + recommendations.push('Consider extending timeline for enterprise-level projects to ensure quality'); + } + + if (services.length > 6) { + recommendations.push('Consider breaking down into phases for better project management'); + } + + if (timeline === 'flexible') { + recommendations.push('Flexible timeline allows for better cost optimization and quality assurance'); + } + + const hasDesign = services.some(s => s.category === 'design'); + const hasDevelopment = services.some(s => s.category === 'development'); + + if (hasDevelopment && !hasDesign) { + recommendations.push('Consider adding UI/UX design services for better user experience'); + } + + return recommendations; +} + +// Get pricing guidelines +router.get('/pricing-guide', async (req, res) => { + try { + const pricingGuide = { + serviceCategories: { + 'development': { + name: 'Web Development', + priceRange: '₩500,000 - ₩5,000,000', + description: 'Custom web applications and websites' + }, + 'design': { + name: 'UI/UX Design', + priceRange: '₩300,000 - ₩2,000,000', + description: 'User interface and experience design' + }, + 'marketing': { + name: 'Digital Marketing', + priceRange: '₩200,000 - ₩1,500,000', + description: 'SEO, social media, and online marketing' + }, + 'consulting': { + name: 'Technical Consulting', + priceRange: '₩150,000 - ₩1,000,000', + description: 'Technology strategy and consultation' + } + }, + complexityFactors: { + 'simple': { + name: 'Simple Project', + multiplier: '0.7x', + description: 'Basic functionality, standard design' + }, + 'medium': { + name: 'Medium Project', + multiplier: '1.0x', + description: 'Moderate complexity, custom features' + }, + 'complex': { + name: 'Complex Project', + multiplier: '1.5x', + description: 'Advanced features, integrations' + }, + 'enterprise': { + name: 'Enterprise Project', + multiplier: '2.0x', + description: 'Large scale, high complexity' + } + }, + timelineImpact: { + 'rush': { + name: 'Rush (< 2 weeks)', + multiplier: '+80%', + description: 'Requires overtime and priority handling' + }, + 'fast': { + name: 'Fast (2-4 weeks)', + multiplier: '+40%', + description: 'Accelerated timeline' + }, + 'standard': { + name: 'Standard (1-3 months)', + multiplier: 'Standard', + description: 'Normal project timeline' + }, + 'flexible': { + name: 'Flexible (3+ months)', + multiplier: '-20%', + description: 'Extended timeline allows optimization' + } + } + }; + + res.json({ + success: true, + pricingGuide + }); + } catch (error) { + console.error('Pricing guide error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching pricing guide' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/contact_20251019204616.js b/.history/routes/contact_20251019204616.js new file mode 100644 index 0000000..4bb8161 --- /dev/null +++ b/.history/routes/contact_20251019204616.js @@ -0,0 +1,250 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const nodemailer = require('nodemailer'); +const { Contact } = require('../models'); +const TelegramBot = require('node-telegram-bot-api'); + +// Initialize Telegram bot if token is provided +let bot = null; +if (process.env.TELEGRAM_BOT_TOKEN) { + bot = new TelegramBot(process.env.TELEGRAM_BOT_TOKEN, { polling: false }); +} + +// Contact form validation +const contactValidation = [ + body('name').trim().isLength({ min: 2, max: 100 }), + body('email').isEmail().normalizeEmail(), + body('subject').trim().isLength({ min: 5, max: 200 }), + body('message').trim().isLength({ min: 10, max: 2000 }), + body('phone').optional().isMobilePhone(), + body('company').optional().trim().isLength({ max: 100 }) +]; + +// Submit contact form +router.post('/submit', contactValidation, async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const contactData = { + ...req.body, + ipAddress: req.ip, + userAgent: req.get('User-Agent'), + source: 'website' + }; + + // Save to database + const contact = new Contact(contactData); + await contact.save(); + + // Send email notification + await sendEmailNotification(contact); + + // Send Telegram notification + await sendTelegramNotification(contact); + + res.json({ + success: true, + message: 'Your message has been sent successfully. We will get back to you soon!', + contactId: contact._id + }); + } catch (error) { + console.error('Contact form error:', error); + res.status(500).json({ + success: false, + message: 'Sorry, there was an error sending your message. Please try again.' + }); + } +}); + +// Get project estimate +router.post('/estimate', [ + body('services').isArray().notEmpty(), + body('projectType').notEmpty(), + body('timeline').notEmpty(), + body('budget').notEmpty(), + body('description').trim().isLength({ min: 10, max: 1000 }) +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { services, projectType, timeline, budget, description, contactInfo } = req.body; + + // Calculate estimate (simplified) + const estimate = calculateProjectEstimate(services, projectType, timeline); + + // Save inquiry to database + const contactData = { + name: contactInfo.name, + email: contactInfo.email, + phone: contactInfo.phone, + company: contactInfo.company, + subject: `Project Estimate Request - ${projectType}`, + message: `Project Description: ${description}\n\nServices: ${services.join(', ')}\nTimeline: ${timeline}\nBudget: ${budget}\n\nCalculated Estimate: ${estimate.formatted}`, + serviceInterest: projectType, + budget: budget, + timeline: timeline, + source: 'calculator', + ipAddress: req.ip, + userAgent: req.get('User-Agent') + }; + + const contact = new Contact(contactData); + await contact.save(); + + // Send notifications + await sendEmailNotification(contact); + await sendTelegramNotification(contact); + + res.json({ + success: true, + message: 'Your project estimate request has been submitted successfully!', + estimate: estimate, + contactId: contact._id + }); + } catch (error) { + console.error('Estimate request error:', error); + res.status(500).json({ + success: false, + message: 'Sorry, there was an error processing your request. Please try again.' + }); + } +}); + +// Helper function to send email notification +async function sendEmailNotification(contact) { + if (!process.env.EMAIL_HOST || !process.env.EMAIL_USER) { + console.log('Email configuration not provided, skipping email notification'); + return; + } + + try { + const transporter = nodemailer.createTransporter({ + host: process.env.EMAIL_HOST, + port: process.env.EMAIL_PORT || 587, + secure: false, + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASS + } + }); + + const mailOptions = { + from: process.env.EMAIL_USER, + to: process.env.ADMIN_EMAIL || process.env.EMAIL_USER, + subject: `New Contact Form Submission: ${contact.subject}`, + html: ` +

New Contact Form Submission

+

Name: ${contact.name}

+

Email: ${contact.email}

+

Phone: ${contact.phone || 'Not provided'}

+

Company: ${contact.company || 'Not provided'}

+

Subject: ${contact.subject}

+

Message:

+

${contact.message.replace(/\n/g, '
')}

+

Source: ${contact.source}

+

Submitted: ${contact.createdAt}

+
+

View in Admin Panel

+ ` + }; + + await transporter.sendMail(mailOptions); + console.log('Email notification sent successfully'); + } catch (error) { + console.error('Email notification error:', error); + } +} + +// Helper function to send Telegram notification +async function sendTelegramNotification(contact) { + if (!bot || !process.env.TELEGRAM_CHAT_ID) { + console.log('Telegram configuration not provided, skipping Telegram notification'); + return; + } + + try { + const message = ` +🔔 *New Contact Form Submission* + +👤 *Name:* ${contact.name} +📧 *Email:* ${contact.email} +📱 *Phone:* ${contact.phone || 'Not provided'} +🏢 *Company:* ${contact.company || 'Not provided'} +📝 *Subject:* ${contact.subject} + +💬 *Message:* +${contact.message} + +📍 *Source:* ${contact.source} +🕐 *Time:* ${contact.createdAt.toLocaleString()} + +[View in Admin Panel](${process.env.SITE_URL}/admin/contacts/${contact._id}) + `; + + await bot.sendMessage(process.env.TELEGRAM_CHAT_ID, message, { + parse_mode: 'Markdown', + disable_web_page_preview: true + }); + + console.log('Telegram notification sent successfully'); + } catch (error) { + console.error('Telegram notification error:', error); + } +} + +// Helper function to calculate project estimate +function calculateProjectEstimate(services, projectType, timeline) { + const baseRates = { + 'web-development': 50000, + 'mobile-app': 80000, + 'ui-ux-design': 30000, + 'branding': 20000, + 'e-commerce': 70000, + 'consulting': 40000 + }; + + const timelineMultipliers = { + 'asap': 1.5, + '1-month': 1.2, + '1-3-months': 1.0, + '3-6-months': 0.9, + 'flexible': 0.8 + }; + + let basePrice = baseRates[projectType] || 50000; + let multiplier = timelineMultipliers[timeline] || 1.0; + + // Add service modifiers + let serviceModifier = 1.0; + if (services.length > 3) serviceModifier += 0.3; + if (services.length > 5) serviceModifier += 0.5; + + const totalEstimate = Math.round(basePrice * multiplier * serviceModifier); + const rangeMin = Math.round(totalEstimate * 0.8); + const rangeMax = Math.round(totalEstimate * 1.3); + + return { + base: totalEstimate, + min: rangeMin, + max: rangeMax, + formatted: `₩${rangeMin.toLocaleString()} - ₩${rangeMax.toLocaleString()}`, + currency: 'KRW' + }; +} + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/contact_20251019204806.js b/.history/routes/contact_20251019204806.js new file mode 100644 index 0000000..4bb8161 --- /dev/null +++ b/.history/routes/contact_20251019204806.js @@ -0,0 +1,250 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const nodemailer = require('nodemailer'); +const { Contact } = require('../models'); +const TelegramBot = require('node-telegram-bot-api'); + +// Initialize Telegram bot if token is provided +let bot = null; +if (process.env.TELEGRAM_BOT_TOKEN) { + bot = new TelegramBot(process.env.TELEGRAM_BOT_TOKEN, { polling: false }); +} + +// Contact form validation +const contactValidation = [ + body('name').trim().isLength({ min: 2, max: 100 }), + body('email').isEmail().normalizeEmail(), + body('subject').trim().isLength({ min: 5, max: 200 }), + body('message').trim().isLength({ min: 10, max: 2000 }), + body('phone').optional().isMobilePhone(), + body('company').optional().trim().isLength({ max: 100 }) +]; + +// Submit contact form +router.post('/submit', contactValidation, async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const contactData = { + ...req.body, + ipAddress: req.ip, + userAgent: req.get('User-Agent'), + source: 'website' + }; + + // Save to database + const contact = new Contact(contactData); + await contact.save(); + + // Send email notification + await sendEmailNotification(contact); + + // Send Telegram notification + await sendTelegramNotification(contact); + + res.json({ + success: true, + message: 'Your message has been sent successfully. We will get back to you soon!', + contactId: contact._id + }); + } catch (error) { + console.error('Contact form error:', error); + res.status(500).json({ + success: false, + message: 'Sorry, there was an error sending your message. Please try again.' + }); + } +}); + +// Get project estimate +router.post('/estimate', [ + body('services').isArray().notEmpty(), + body('projectType').notEmpty(), + body('timeline').notEmpty(), + body('budget').notEmpty(), + body('description').trim().isLength({ min: 10, max: 1000 }) +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { services, projectType, timeline, budget, description, contactInfo } = req.body; + + // Calculate estimate (simplified) + const estimate = calculateProjectEstimate(services, projectType, timeline); + + // Save inquiry to database + const contactData = { + name: contactInfo.name, + email: contactInfo.email, + phone: contactInfo.phone, + company: contactInfo.company, + subject: `Project Estimate Request - ${projectType}`, + message: `Project Description: ${description}\n\nServices: ${services.join(', ')}\nTimeline: ${timeline}\nBudget: ${budget}\n\nCalculated Estimate: ${estimate.formatted}`, + serviceInterest: projectType, + budget: budget, + timeline: timeline, + source: 'calculator', + ipAddress: req.ip, + userAgent: req.get('User-Agent') + }; + + const contact = new Contact(contactData); + await contact.save(); + + // Send notifications + await sendEmailNotification(contact); + await sendTelegramNotification(contact); + + res.json({ + success: true, + message: 'Your project estimate request has been submitted successfully!', + estimate: estimate, + contactId: contact._id + }); + } catch (error) { + console.error('Estimate request error:', error); + res.status(500).json({ + success: false, + message: 'Sorry, there was an error processing your request. Please try again.' + }); + } +}); + +// Helper function to send email notification +async function sendEmailNotification(contact) { + if (!process.env.EMAIL_HOST || !process.env.EMAIL_USER) { + console.log('Email configuration not provided, skipping email notification'); + return; + } + + try { + const transporter = nodemailer.createTransporter({ + host: process.env.EMAIL_HOST, + port: process.env.EMAIL_PORT || 587, + secure: false, + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASS + } + }); + + const mailOptions = { + from: process.env.EMAIL_USER, + to: process.env.ADMIN_EMAIL || process.env.EMAIL_USER, + subject: `New Contact Form Submission: ${contact.subject}`, + html: ` +

New Contact Form Submission

+

Name: ${contact.name}

+

Email: ${contact.email}

+

Phone: ${contact.phone || 'Not provided'}

+

Company: ${contact.company || 'Not provided'}

+

Subject: ${contact.subject}

+

Message:

+

${contact.message.replace(/\n/g, '
')}

+

Source: ${contact.source}

+

Submitted: ${contact.createdAt}

+
+

View in Admin Panel

+ ` + }; + + await transporter.sendMail(mailOptions); + console.log('Email notification sent successfully'); + } catch (error) { + console.error('Email notification error:', error); + } +} + +// Helper function to send Telegram notification +async function sendTelegramNotification(contact) { + if (!bot || !process.env.TELEGRAM_CHAT_ID) { + console.log('Telegram configuration not provided, skipping Telegram notification'); + return; + } + + try { + const message = ` +🔔 *New Contact Form Submission* + +👤 *Name:* ${contact.name} +📧 *Email:* ${contact.email} +📱 *Phone:* ${contact.phone || 'Not provided'} +🏢 *Company:* ${contact.company || 'Not provided'} +📝 *Subject:* ${contact.subject} + +💬 *Message:* +${contact.message} + +📍 *Source:* ${contact.source} +🕐 *Time:* ${contact.createdAt.toLocaleString()} + +[View in Admin Panel](${process.env.SITE_URL}/admin/contacts/${contact._id}) + `; + + await bot.sendMessage(process.env.TELEGRAM_CHAT_ID, message, { + parse_mode: 'Markdown', + disable_web_page_preview: true + }); + + console.log('Telegram notification sent successfully'); + } catch (error) { + console.error('Telegram notification error:', error); + } +} + +// Helper function to calculate project estimate +function calculateProjectEstimate(services, projectType, timeline) { + const baseRates = { + 'web-development': 50000, + 'mobile-app': 80000, + 'ui-ux-design': 30000, + 'branding': 20000, + 'e-commerce': 70000, + 'consulting': 40000 + }; + + const timelineMultipliers = { + 'asap': 1.5, + '1-month': 1.2, + '1-3-months': 1.0, + '3-6-months': 0.9, + 'flexible': 0.8 + }; + + let basePrice = baseRates[projectType] || 50000; + let multiplier = timelineMultipliers[timeline] || 1.0; + + // Add service modifiers + let serviceModifier = 1.0; + if (services.length > 3) serviceModifier += 0.3; + if (services.length > 5) serviceModifier += 0.5; + + const totalEstimate = Math.round(basePrice * multiplier * serviceModifier); + const rangeMin = Math.round(totalEstimate * 0.8); + const rangeMax = Math.round(totalEstimate * 1.3); + + return { + base: totalEstimate, + min: rangeMin, + max: rangeMax, + formatted: `₩${rangeMin.toLocaleString()} - ₩${rangeMax.toLocaleString()}`, + currency: 'KRW' + }; +} + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/contact_20251021213241.js b/.history/routes/contact_20251021213241.js new file mode 100644 index 0000000..ef645e4 --- /dev/null +++ b/.history/routes/contact_20251021213241.js @@ -0,0 +1,244 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const nodemailer = require('nodemailer'); +const { Contact } = require('../models'); +const telegramService = require('../services/telegram'); + +// Contact form validation +const contactValidation = [ + body('name').trim().isLength({ min: 2, max: 100 }), + body('email').isEmail().normalizeEmail(), + body('subject').trim().isLength({ min: 5, max: 200 }), + body('message').trim().isLength({ min: 10, max: 2000 }), + body('phone').optional().isMobilePhone(), + body('company').optional().trim().isLength({ max: 100 }) +]; + +// Submit contact form +router.post('/submit', contactValidation, async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const contactData = { + ...req.body, + ipAddress: req.ip, + userAgent: req.get('User-Agent'), + source: 'website' + }; + + // Save to database + const contact = new Contact(contactData); + await contact.save(); + + // Send email notification + await sendEmailNotification(contact); + + // Send Telegram notification + await sendTelegramNotification(contact); + + res.json({ + success: true, + message: 'Your message has been sent successfully. We will get back to you soon!', + contactId: contact._id + }); + } catch (error) { + console.error('Contact form error:', error); + res.status(500).json({ + success: false, + message: 'Sorry, there was an error sending your message. Please try again.' + }); + } +}); + +// Get project estimate +router.post('/estimate', [ + body('services').isArray().notEmpty(), + body('projectType').notEmpty(), + body('timeline').notEmpty(), + body('budget').notEmpty(), + body('description').trim().isLength({ min: 10, max: 1000 }) +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { services, projectType, timeline, budget, description, contactInfo } = req.body; + + // Calculate estimate (simplified) + const estimate = calculateProjectEstimate(services, projectType, timeline); + + // Save inquiry to database + const contactData = { + name: contactInfo.name, + email: contactInfo.email, + phone: contactInfo.phone, + company: contactInfo.company, + subject: `Project Estimate Request - ${projectType}`, + message: `Project Description: ${description}\n\nServices: ${services.join(', ')}\nTimeline: ${timeline}\nBudget: ${budget}\n\nCalculated Estimate: ${estimate.formatted}`, + serviceInterest: projectType, + budget: budget, + timeline: timeline, + source: 'calculator', + ipAddress: req.ip, + userAgent: req.get('User-Agent') + }; + + const contact = new Contact(contactData); + await contact.save(); + + // Send notifications + await sendEmailNotification(contact); + await sendTelegramNotification(contact); + + res.json({ + success: true, + message: 'Your project estimate request has been submitted successfully!', + estimate: estimate, + contactId: contact._id + }); + } catch (error) { + console.error('Estimate request error:', error); + res.status(500).json({ + success: false, + message: 'Sorry, there was an error processing your request. Please try again.' + }); + } +}); + +// Helper function to send email notification +async function sendEmailNotification(contact) { + if (!process.env.EMAIL_HOST || !process.env.EMAIL_USER) { + console.log('Email configuration not provided, skipping email notification'); + return; + } + + try { + const transporter = nodemailer.createTransporter({ + host: process.env.EMAIL_HOST, + port: process.env.EMAIL_PORT || 587, + secure: false, + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASS + } + }); + + const mailOptions = { + from: process.env.EMAIL_USER, + to: process.env.ADMIN_EMAIL || process.env.EMAIL_USER, + subject: `New Contact Form Submission: ${contact.subject}`, + html: ` +

New Contact Form Submission

+

Name: ${contact.name}

+

Email: ${contact.email}

+

Phone: ${contact.phone || 'Not provided'}

+

Company: ${contact.company || 'Not provided'}

+

Subject: ${contact.subject}

+

Message:

+

${contact.message.replace(/\n/g, '
')}

+

Source: ${contact.source}

+

Submitted: ${contact.createdAt}

+
+

View in Admin Panel

+ ` + }; + + await transporter.sendMail(mailOptions); + console.log('Email notification sent successfully'); + } catch (error) { + console.error('Email notification error:', error); + } +} + +// Helper function to send Telegram notification +async function sendTelegramNotification(contact) { + if (!bot || !process.env.TELEGRAM_CHAT_ID) { + console.log('Telegram configuration not provided, skipping Telegram notification'); + return; + } + + try { + const message = ` +🔔 *New Contact Form Submission* + +👤 *Name:* ${contact.name} +📧 *Email:* ${contact.email} +📱 *Phone:* ${contact.phone || 'Not provided'} +🏢 *Company:* ${contact.company || 'Not provided'} +📝 *Subject:* ${contact.subject} + +💬 *Message:* +${contact.message} + +📍 *Source:* ${contact.source} +🕐 *Time:* ${contact.createdAt.toLocaleString()} + +[View in Admin Panel](${process.env.SITE_URL}/admin/contacts/${contact._id}) + `; + + await bot.sendMessage(process.env.TELEGRAM_CHAT_ID, message, { + parse_mode: 'Markdown', + disable_web_page_preview: true + }); + + console.log('Telegram notification sent successfully'); + } catch (error) { + console.error('Telegram notification error:', error); + } +} + +// Helper function to calculate project estimate +function calculateProjectEstimate(services, projectType, timeline) { + const baseRates = { + 'web-development': 50000, + 'mobile-app': 80000, + 'ui-ux-design': 30000, + 'branding': 20000, + 'e-commerce': 70000, + 'consulting': 40000 + }; + + const timelineMultipliers = { + 'asap': 1.5, + '1-month': 1.2, + '1-3-months': 1.0, + '3-6-months': 0.9, + 'flexible': 0.8 + }; + + let basePrice = baseRates[projectType] || 50000; + let multiplier = timelineMultipliers[timeline] || 1.0; + + // Add service modifiers + let serviceModifier = 1.0; + if (services.length > 3) serviceModifier += 0.3; + if (services.length > 5) serviceModifier += 0.5; + + const totalEstimate = Math.round(basePrice * multiplier * serviceModifier); + const rangeMin = Math.round(totalEstimate * 0.8); + const rangeMax = Math.round(totalEstimate * 1.3); + + return { + base: totalEstimate, + min: rangeMin, + max: rangeMax, + formatted: `₩${rangeMin.toLocaleString()} - ₩${rangeMax.toLocaleString()}`, + currency: 'KRW' + }; +} + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/contact_20251021213250.js b/.history/routes/contact_20251021213250.js new file mode 100644 index 0000000..e44e7f0 --- /dev/null +++ b/.history/routes/contact_20251021213250.js @@ -0,0 +1,244 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const nodemailer = require('nodemailer'); +const { Contact } = require('../models'); +const telegramService = require('../services/telegram'); + +// Contact form validation +const contactValidation = [ + body('name').trim().isLength({ min: 2, max: 100 }), + body('email').isEmail().normalizeEmail(), + body('subject').trim().isLength({ min: 5, max: 200 }), + body('message').trim().isLength({ min: 10, max: 2000 }), + body('phone').optional().isMobilePhone(), + body('company').optional().trim().isLength({ max: 100 }) +]; + +// Submit contact form +router.post('/submit', contactValidation, async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const contactData = { + ...req.body, + ipAddress: req.ip, + userAgent: req.get('User-Agent'), + source: 'website' + }; + + // Save to database + const contact = new Contact(contactData); + await contact.save(); + + // Send email notification + await sendEmailNotification(contact); + + // Send Telegram notification + await telegramService.sendNewContactAlert(contact); + + res.json({ + success: true, + message: 'Your message has been sent successfully. We will get back to you soon!', + contactId: contact._id + }); + } catch (error) { + console.error('Contact form error:', error); + res.status(500).json({ + success: false, + message: 'Sorry, there was an error sending your message. Please try again.' + }); + } +}); + +// Get project estimate +router.post('/estimate', [ + body('services').isArray().notEmpty(), + body('projectType').notEmpty(), + body('timeline').notEmpty(), + body('budget').notEmpty(), + body('description').trim().isLength({ min: 10, max: 1000 }) +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { services, projectType, timeline, budget, description, contactInfo } = req.body; + + // Calculate estimate (simplified) + const estimate = calculateProjectEstimate(services, projectType, timeline); + + // Save inquiry to database + const contactData = { + name: contactInfo.name, + email: contactInfo.email, + phone: contactInfo.phone, + company: contactInfo.company, + subject: `Project Estimate Request - ${projectType}`, + message: `Project Description: ${description}\n\nServices: ${services.join(', ')}\nTimeline: ${timeline}\nBudget: ${budget}\n\nCalculated Estimate: ${estimate.formatted}`, + serviceInterest: projectType, + budget: budget, + timeline: timeline, + source: 'calculator', + ipAddress: req.ip, + userAgent: req.get('User-Agent') + }; + + const contact = new Contact(contactData); + await contact.save(); + + // Send notifications + await sendEmailNotification(contact); + await sendTelegramNotification(contact); + + res.json({ + success: true, + message: 'Your project estimate request has been submitted successfully!', + estimate: estimate, + contactId: contact._id + }); + } catch (error) { + console.error('Estimate request error:', error); + res.status(500).json({ + success: false, + message: 'Sorry, there was an error processing your request. Please try again.' + }); + } +}); + +// Helper function to send email notification +async function sendEmailNotification(contact) { + if (!process.env.EMAIL_HOST || !process.env.EMAIL_USER) { + console.log('Email configuration not provided, skipping email notification'); + return; + } + + try { + const transporter = nodemailer.createTransporter({ + host: process.env.EMAIL_HOST, + port: process.env.EMAIL_PORT || 587, + secure: false, + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASS + } + }); + + const mailOptions = { + from: process.env.EMAIL_USER, + to: process.env.ADMIN_EMAIL || process.env.EMAIL_USER, + subject: `New Contact Form Submission: ${contact.subject}`, + html: ` +

New Contact Form Submission

+

Name: ${contact.name}

+

Email: ${contact.email}

+

Phone: ${contact.phone || 'Not provided'}

+

Company: ${contact.company || 'Not provided'}

+

Subject: ${contact.subject}

+

Message:

+

${contact.message.replace(/\n/g, '
')}

+

Source: ${contact.source}

+

Submitted: ${contact.createdAt}

+
+

View in Admin Panel

+ ` + }; + + await transporter.sendMail(mailOptions); + console.log('Email notification sent successfully'); + } catch (error) { + console.error('Email notification error:', error); + } +} + +// Helper function to send Telegram notification +async function sendTelegramNotification(contact) { + if (!bot || !process.env.TELEGRAM_CHAT_ID) { + console.log('Telegram configuration not provided, skipping Telegram notification'); + return; + } + + try { + const message = ` +🔔 *New Contact Form Submission* + +👤 *Name:* ${contact.name} +📧 *Email:* ${contact.email} +📱 *Phone:* ${contact.phone || 'Not provided'} +🏢 *Company:* ${contact.company || 'Not provided'} +📝 *Subject:* ${contact.subject} + +💬 *Message:* +${contact.message} + +📍 *Source:* ${contact.source} +🕐 *Time:* ${contact.createdAt.toLocaleString()} + +[View in Admin Panel](${process.env.SITE_URL}/admin/contacts/${contact._id}) + `; + + await bot.sendMessage(process.env.TELEGRAM_CHAT_ID, message, { + parse_mode: 'Markdown', + disable_web_page_preview: true + }); + + console.log('Telegram notification sent successfully'); + } catch (error) { + console.error('Telegram notification error:', error); + } +} + +// Helper function to calculate project estimate +function calculateProjectEstimate(services, projectType, timeline) { + const baseRates = { + 'web-development': 50000, + 'mobile-app': 80000, + 'ui-ux-design': 30000, + 'branding': 20000, + 'e-commerce': 70000, + 'consulting': 40000 + }; + + const timelineMultipliers = { + 'asap': 1.5, + '1-month': 1.2, + '1-3-months': 1.0, + '3-6-months': 0.9, + 'flexible': 0.8 + }; + + let basePrice = baseRates[projectType] || 50000; + let multiplier = timelineMultipliers[timeline] || 1.0; + + // Add service modifiers + let serviceModifier = 1.0; + if (services.length > 3) serviceModifier += 0.3; + if (services.length > 5) serviceModifier += 0.5; + + const totalEstimate = Math.round(basePrice * multiplier * serviceModifier); + const rangeMin = Math.round(totalEstimate * 0.8); + const rangeMax = Math.round(totalEstimate * 1.3); + + return { + base: totalEstimate, + min: rangeMin, + max: rangeMax, + formatted: `₩${rangeMin.toLocaleString()} - ₩${rangeMax.toLocaleString()}`, + currency: 'KRW' + }; +} + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/contact_20251021213258.js b/.history/routes/contact_20251021213258.js new file mode 100644 index 0000000..53cb066 --- /dev/null +++ b/.history/routes/contact_20251021213258.js @@ -0,0 +1,247 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const nodemailer = require('nodemailer'); +const { Contact } = require('../models'); +const telegramService = require('../services/telegram'); + +// Contact form validation +const contactValidation = [ + body('name').trim().isLength({ min: 2, max: 100 }), + body('email').isEmail().normalizeEmail(), + body('subject').trim().isLength({ min: 5, max: 200 }), + body('message').trim().isLength({ min: 10, max: 2000 }), + body('phone').optional().isMobilePhone(), + body('company').optional().trim().isLength({ max: 100 }) +]; + +// Submit contact form +router.post('/submit', contactValidation, async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const contactData = { + ...req.body, + ipAddress: req.ip, + userAgent: req.get('User-Agent'), + source: 'website' + }; + + // Save to database + const contact = new Contact(contactData); + await contact.save(); + + // Send email notification + await sendEmailNotification(contact); + + // Send Telegram notification + await telegramService.sendNewContactAlert(contact); + + res.json({ + success: true, + message: 'Your message has been sent successfully. We will get back to you soon!', + contactId: contact._id + }); + } catch (error) { + console.error('Contact form error:', error); + res.status(500).json({ + success: false, + message: 'Sorry, there was an error sending your message. Please try again.' + }); + } +}); + +// Get project estimate +router.post('/estimate', [ + body('services').isArray().notEmpty(), + body('projectType').notEmpty(), + body('timeline').notEmpty(), + body('budget').notEmpty(), + body('description').trim().isLength({ min: 10, max: 1000 }) +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { services, projectType, timeline, budget, description, contactInfo } = req.body; + + // Calculate estimate (simplified) + const estimate = calculateProjectEstimate(services, projectType, timeline); + + // Save inquiry to database + const contactData = { + name: contactInfo.name, + email: contactInfo.email, + phone: contactInfo.phone, + company: contactInfo.company, + subject: `Project Estimate Request - ${projectType}`, + message: `Project Description: ${description}\n\nServices: ${services.join(', ')}\nTimeline: ${timeline}\nBudget: ${budget}\n\nCalculated Estimate: ${estimate.formatted}`, + serviceInterest: projectType, + budget: budget, + timeline: timeline, + source: 'calculator', + ipAddress: req.ip, + userAgent: req.get('User-Agent') + }; + + const contact = new Contact(contactData); + await contact.save(); + + // Send notifications + await sendEmailNotification(contact); + await telegramService.sendCalculatorQuote({ + ...contactData, + services: services.map(s => ({ name: s, price: 0 })) // Simplified for now + }); + + res.json({ + success: true, + message: 'Your project estimate request has been submitted successfully!', + estimate: estimate, + contactId: contact._id + }); + } catch (error) { + console.error('Estimate request error:', error); + res.status(500).json({ + success: false, + message: 'Sorry, there was an error processing your request. Please try again.' + }); + } +}); + +// Helper function to send email notification +async function sendEmailNotification(contact) { + if (!process.env.EMAIL_HOST || !process.env.EMAIL_USER) { + console.log('Email configuration not provided, skipping email notification'); + return; + } + + try { + const transporter = nodemailer.createTransporter({ + host: process.env.EMAIL_HOST, + port: process.env.EMAIL_PORT || 587, + secure: false, + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASS + } + }); + + const mailOptions = { + from: process.env.EMAIL_USER, + to: process.env.ADMIN_EMAIL || process.env.EMAIL_USER, + subject: `New Contact Form Submission: ${contact.subject}`, + html: ` +

New Contact Form Submission

+

Name: ${contact.name}

+

Email: ${contact.email}

+

Phone: ${contact.phone || 'Not provided'}

+

Company: ${contact.company || 'Not provided'}

+

Subject: ${contact.subject}

+

Message:

+

${contact.message.replace(/\n/g, '
')}

+

Source: ${contact.source}

+

Submitted: ${contact.createdAt}

+
+

View in Admin Panel

+ ` + }; + + await transporter.sendMail(mailOptions); + console.log('Email notification sent successfully'); + } catch (error) { + console.error('Email notification error:', error); + } +} + +// Helper function to send Telegram notification +async function sendTelegramNotification(contact) { + if (!bot || !process.env.TELEGRAM_CHAT_ID) { + console.log('Telegram configuration not provided, skipping Telegram notification'); + return; + } + + try { + const message = ` +🔔 *New Contact Form Submission* + +👤 *Name:* ${contact.name} +📧 *Email:* ${contact.email} +📱 *Phone:* ${contact.phone || 'Not provided'} +🏢 *Company:* ${contact.company || 'Not provided'} +📝 *Subject:* ${contact.subject} + +💬 *Message:* +${contact.message} + +📍 *Source:* ${contact.source} +🕐 *Time:* ${contact.createdAt.toLocaleString()} + +[View in Admin Panel](${process.env.SITE_URL}/admin/contacts/${contact._id}) + `; + + await bot.sendMessage(process.env.TELEGRAM_CHAT_ID, message, { + parse_mode: 'Markdown', + disable_web_page_preview: true + }); + + console.log('Telegram notification sent successfully'); + } catch (error) { + console.error('Telegram notification error:', error); + } +} + +// Helper function to calculate project estimate +function calculateProjectEstimate(services, projectType, timeline) { + const baseRates = { + 'web-development': 50000, + 'mobile-app': 80000, + 'ui-ux-design': 30000, + 'branding': 20000, + 'e-commerce': 70000, + 'consulting': 40000 + }; + + const timelineMultipliers = { + 'asap': 1.5, + '1-month': 1.2, + '1-3-months': 1.0, + '3-6-months': 0.9, + 'flexible': 0.8 + }; + + let basePrice = baseRates[projectType] || 50000; + let multiplier = timelineMultipliers[timeline] || 1.0; + + // Add service modifiers + let serviceModifier = 1.0; + if (services.length > 3) serviceModifier += 0.3; + if (services.length > 5) serviceModifier += 0.5; + + const totalEstimate = Math.round(basePrice * multiplier * serviceModifier); + const rangeMin = Math.round(totalEstimate * 0.8); + const rangeMax = Math.round(totalEstimate * 1.3); + + return { + base: totalEstimate, + min: rangeMin, + max: rangeMax, + formatted: `₩${rangeMin.toLocaleString()} - ₩${rangeMax.toLocaleString()}`, + currency: 'KRW' + }; +} + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/contact_20251021213310.js b/.history/routes/contact_20251021213310.js new file mode 100644 index 0000000..6f336eb --- /dev/null +++ b/.history/routes/contact_20251021213310.js @@ -0,0 +1,212 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const nodemailer = require('nodemailer'); +const { Contact } = require('../models'); +const telegramService = require('../services/telegram'); + +// Contact form validation +const contactValidation = [ + body('name').trim().isLength({ min: 2, max: 100 }), + body('email').isEmail().normalizeEmail(), + body('subject').trim().isLength({ min: 5, max: 200 }), + body('message').trim().isLength({ min: 10, max: 2000 }), + body('phone').optional().isMobilePhone(), + body('company').optional().trim().isLength({ max: 100 }) +]; + +// Submit contact form +router.post('/submit', contactValidation, async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const contactData = { + ...req.body, + ipAddress: req.ip, + userAgent: req.get('User-Agent'), + source: 'website' + }; + + // Save to database + const contact = new Contact(contactData); + await contact.save(); + + // Send email notification + await sendEmailNotification(contact); + + // Send Telegram notification + await telegramService.sendNewContactAlert(contact); + + res.json({ + success: true, + message: 'Your message has been sent successfully. We will get back to you soon!', + contactId: contact._id + }); + } catch (error) { + console.error('Contact form error:', error); + res.status(500).json({ + success: false, + message: 'Sorry, there was an error sending your message. Please try again.' + }); + } +}); + +// Get project estimate +router.post('/estimate', [ + body('services').isArray().notEmpty(), + body('projectType').notEmpty(), + body('timeline').notEmpty(), + body('budget').notEmpty(), + body('description').trim().isLength({ min: 10, max: 1000 }) +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { services, projectType, timeline, budget, description, contactInfo } = req.body; + + // Calculate estimate (simplified) + const estimate = calculateProjectEstimate(services, projectType, timeline); + + // Save inquiry to database + const contactData = { + name: contactInfo.name, + email: contactInfo.email, + phone: contactInfo.phone, + company: contactInfo.company, + subject: `Project Estimate Request - ${projectType}`, + message: `Project Description: ${description}\n\nServices: ${services.join(', ')}\nTimeline: ${timeline}\nBudget: ${budget}\n\nCalculated Estimate: ${estimate.formatted}`, + serviceInterest: projectType, + budget: budget, + timeline: timeline, + source: 'calculator', + ipAddress: req.ip, + userAgent: req.get('User-Agent') + }; + + const contact = new Contact(contactData); + await contact.save(); + + // Send notifications + await sendEmailNotification(contact); + await telegramService.sendCalculatorQuote({ + ...contactData, + services: services.map(s => ({ name: s, price: 0 })) // Simplified for now + }); + + res.json({ + success: true, + message: 'Your project estimate request has been submitted successfully!', + estimate: estimate, + contactId: contact._id + }); + } catch (error) { + console.error('Estimate request error:', error); + res.status(500).json({ + success: false, + message: 'Sorry, there was an error processing your request. Please try again.' + }); + } +}); + +// Helper function to send email notification +async function sendEmailNotification(contact) { + if (!process.env.EMAIL_HOST || !process.env.EMAIL_USER) { + console.log('Email configuration not provided, skipping email notification'); + return; + } + + try { + const transporter = nodemailer.createTransporter({ + host: process.env.EMAIL_HOST, + port: process.env.EMAIL_PORT || 587, + secure: false, + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASS + } + }); + + const mailOptions = { + from: process.env.EMAIL_USER, + to: process.env.ADMIN_EMAIL || process.env.EMAIL_USER, + subject: `New Contact Form Submission: ${contact.subject}`, + html: ` +

New Contact Form Submission

+

Name: ${contact.name}

+

Email: ${contact.email}

+

Phone: ${contact.phone || 'Not provided'}

+

Company: ${contact.company || 'Not provided'}

+

Subject: ${contact.subject}

+

Message:

+

${contact.message.replace(/\n/g, '
')}

+

Source: ${contact.source}

+

Submitted: ${contact.createdAt}

+
+

View in Admin Panel

+ ` + }; + + await transporter.sendMail(mailOptions); + console.log('Email notification sent successfully'); + } catch (error) { + console.error('Email notification error:', error); + } +} + +// Telegram notifications now handled by telegramService + +// Helper function to calculate project estimate +function calculateProjectEstimate(services, projectType, timeline) { + const baseRates = { + 'web-development': 50000, + 'mobile-app': 80000, + 'ui-ux-design': 30000, + 'branding': 20000, + 'e-commerce': 70000, + 'consulting': 40000 + }; + + const timelineMultipliers = { + 'asap': 1.5, + '1-month': 1.2, + '1-3-months': 1.0, + '3-6-months': 0.9, + 'flexible': 0.8 + }; + + let basePrice = baseRates[projectType] || 50000; + let multiplier = timelineMultipliers[timeline] || 1.0; + + // Add service modifiers + let serviceModifier = 1.0; + if (services.length > 3) serviceModifier += 0.3; + if (services.length > 5) serviceModifier += 0.5; + + const totalEstimate = Math.round(basePrice * multiplier * serviceModifier); + const rangeMin = Math.round(totalEstimate * 0.8); + const rangeMax = Math.round(totalEstimate * 1.3); + + return { + base: totalEstimate, + min: rangeMin, + max: rangeMax, + formatted: `₩${rangeMin.toLocaleString()} - ₩${rangeMax.toLocaleString()}`, + currency: 'KRW' + }; +} + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/contact_20251021214112.js b/.history/routes/contact_20251021214112.js new file mode 100644 index 0000000..6f336eb --- /dev/null +++ b/.history/routes/contact_20251021214112.js @@ -0,0 +1,212 @@ +const express = require('express'); +const router = express.Router(); +const { body, validationResult } = require('express-validator'); +const nodemailer = require('nodemailer'); +const { Contact } = require('../models'); +const telegramService = require('../services/telegram'); + +// Contact form validation +const contactValidation = [ + body('name').trim().isLength({ min: 2, max: 100 }), + body('email').isEmail().normalizeEmail(), + body('subject').trim().isLength({ min: 5, max: 200 }), + body('message').trim().isLength({ min: 10, max: 2000 }), + body('phone').optional().isMobilePhone(), + body('company').optional().trim().isLength({ max: 100 }) +]; + +// Submit contact form +router.post('/submit', contactValidation, async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const contactData = { + ...req.body, + ipAddress: req.ip, + userAgent: req.get('User-Agent'), + source: 'website' + }; + + // Save to database + const contact = new Contact(contactData); + await contact.save(); + + // Send email notification + await sendEmailNotification(contact); + + // Send Telegram notification + await telegramService.sendNewContactAlert(contact); + + res.json({ + success: true, + message: 'Your message has been sent successfully. We will get back to you soon!', + contactId: contact._id + }); + } catch (error) { + console.error('Contact form error:', error); + res.status(500).json({ + success: false, + message: 'Sorry, there was an error sending your message. Please try again.' + }); + } +}); + +// Get project estimate +router.post('/estimate', [ + body('services').isArray().notEmpty(), + body('projectType').notEmpty(), + body('timeline').notEmpty(), + body('budget').notEmpty(), + body('description').trim().isLength({ min: 10, max: 1000 }) +], async (req, res) => { + try { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ + success: false, + message: 'Invalid input data', + errors: errors.array() + }); + } + + const { services, projectType, timeline, budget, description, contactInfo } = req.body; + + // Calculate estimate (simplified) + const estimate = calculateProjectEstimate(services, projectType, timeline); + + // Save inquiry to database + const contactData = { + name: contactInfo.name, + email: contactInfo.email, + phone: contactInfo.phone, + company: contactInfo.company, + subject: `Project Estimate Request - ${projectType}`, + message: `Project Description: ${description}\n\nServices: ${services.join(', ')}\nTimeline: ${timeline}\nBudget: ${budget}\n\nCalculated Estimate: ${estimate.formatted}`, + serviceInterest: projectType, + budget: budget, + timeline: timeline, + source: 'calculator', + ipAddress: req.ip, + userAgent: req.get('User-Agent') + }; + + const contact = new Contact(contactData); + await contact.save(); + + // Send notifications + await sendEmailNotification(contact); + await telegramService.sendCalculatorQuote({ + ...contactData, + services: services.map(s => ({ name: s, price: 0 })) // Simplified for now + }); + + res.json({ + success: true, + message: 'Your project estimate request has been submitted successfully!', + estimate: estimate, + contactId: contact._id + }); + } catch (error) { + console.error('Estimate request error:', error); + res.status(500).json({ + success: false, + message: 'Sorry, there was an error processing your request. Please try again.' + }); + } +}); + +// Helper function to send email notification +async function sendEmailNotification(contact) { + if (!process.env.EMAIL_HOST || !process.env.EMAIL_USER) { + console.log('Email configuration not provided, skipping email notification'); + return; + } + + try { + const transporter = nodemailer.createTransporter({ + host: process.env.EMAIL_HOST, + port: process.env.EMAIL_PORT || 587, + secure: false, + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASS + } + }); + + const mailOptions = { + from: process.env.EMAIL_USER, + to: process.env.ADMIN_EMAIL || process.env.EMAIL_USER, + subject: `New Contact Form Submission: ${contact.subject}`, + html: ` +

New Contact Form Submission

+

Name: ${contact.name}

+

Email: ${contact.email}

+

Phone: ${contact.phone || 'Not provided'}

+

Company: ${contact.company || 'Not provided'}

+

Subject: ${contact.subject}

+

Message:

+

${contact.message.replace(/\n/g, '
')}

+

Source: ${contact.source}

+

Submitted: ${contact.createdAt}

+
+

View in Admin Panel

+ ` + }; + + await transporter.sendMail(mailOptions); + console.log('Email notification sent successfully'); + } catch (error) { + console.error('Email notification error:', error); + } +} + +// Telegram notifications now handled by telegramService + +// Helper function to calculate project estimate +function calculateProjectEstimate(services, projectType, timeline) { + const baseRates = { + 'web-development': 50000, + 'mobile-app': 80000, + 'ui-ux-design': 30000, + 'branding': 20000, + 'e-commerce': 70000, + 'consulting': 40000 + }; + + const timelineMultipliers = { + 'asap': 1.5, + '1-month': 1.2, + '1-3-months': 1.0, + '3-6-months': 0.9, + 'flexible': 0.8 + }; + + let basePrice = baseRates[projectType] || 50000; + let multiplier = timelineMultipliers[timeline] || 1.0; + + // Add service modifiers + let serviceModifier = 1.0; + if (services.length > 3) serviceModifier += 0.3; + if (services.length > 5) serviceModifier += 0.5; + + const totalEstimate = Math.round(basePrice * multiplier * serviceModifier); + const rangeMin = Math.round(totalEstimate * 0.8); + const rangeMax = Math.round(totalEstimate * 1.3); + + return { + base: totalEstimate, + min: rangeMin, + max: rangeMax, + formatted: `₩${rangeMin.toLocaleString()} - ₩${rangeMax.toLocaleString()}`, + currency: 'KRW' + }; +} + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251019203116.js b/.history/routes/index_20251019203116.js new file mode 100644 index 0000000..37ba56c --- /dev/null +++ b/.history/routes/index_20251019203116.js @@ -0,0 +1,201 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.find({ featured: true, isPublished: true }) + .sort({ order: 1, createdAt: -1 }) + .limit(6), + Service.find({ featured: true, isActive: true }) + .sort({ order: 1 }) + .limit(4) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [portfolio, total, categories] = await Promise.all([ + Portfolio.find(query) + .sort({ featured: -1, publishedAt: -1 }) + .skip(skip) + .limit(limit), + Portfolio.countDocuments(query), + Portfolio.distinct('category', { isPublished: true }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.find({ + _id: { $ne: portfolio._id }, + category: portfolio.category, + isPublished: true + }).limit(3); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .sort({ featured: -1, order: 1 }) + .populate('portfolio', 'title images'); + + const categories = await Service.distinct('category', { isActive: true }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .select('name pricing category') + .sort({ category: 1, name: 1 }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251019203123.js b/.history/routes/index_20251019203123.js new file mode 100644 index 0000000..7f9a792 --- /dev/null +++ b/.history/routes/index_20251019203123.js @@ -0,0 +1,205 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [portfolio, total, categories] = await Promise.all([ + Portfolio.find(query) + .sort({ featured: -1, publishedAt: -1 }) + .skip(skip) + .limit(limit), + Portfolio.countDocuments(query), + Portfolio.distinct('category', { isPublished: true }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.find({ + _id: { $ne: portfolio._id }, + category: portfolio.category, + isPublished: true + }).limit(3); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .sort({ featured: -1, order: 1 }) + .populate('portfolio', 'title images'); + + const categories = await Service.distinct('category', { isActive: true }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .select('name pricing category') + .sort({ category: 1, name: 1 }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251019203147.js b/.history/routes/index_20251019203147.js new file mode 100644 index 0000000..7f9a792 --- /dev/null +++ b/.history/routes/index_20251019203147.js @@ -0,0 +1,205 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [portfolio, total, categories] = await Promise.all([ + Portfolio.find(query) + .sort({ featured: -1, publishedAt: -1 }) + .skip(skip) + .limit(limit), + Portfolio.countDocuments(query), + Portfolio.distinct('category', { isPublished: true }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.find({ + _id: { $ne: portfolio._id }, + category: portfolio.category, + isPublished: true + }).limit(3); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .sort({ featured: -1, order: 1 }) + .populate('portfolio', 'title images'); + + const categories = await Service.distinct('category', { isActive: true }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .select('name pricing category') + .sort({ category: 1, name: 1 }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251019203238.js b/.history/routes/index_20251019203238.js new file mode 100644 index 0000000..c7036b7 --- /dev/null +++ b/.history/routes/index_20251019203238.js @@ -0,0 +1,211 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [portfolio, total, categories] = await Promise.all([ + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.find({ + _id: { $ne: portfolio._id }, + category: portfolio.category, + isPublished: true + }).limit(3); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .sort({ featured: -1, order: 1 }) + .populate('portfolio', 'title images'); + + const categories = await Service.distinct('category', { isActive: true }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .select('name pricing category') + .sort({ category: 1, name: 1 }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251019203244.js b/.history/routes/index_20251019203244.js new file mode 100644 index 0000000..c7dfd6f --- /dev/null +++ b/.history/routes/index_20251019203244.js @@ -0,0 +1,211 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [portfolio, total, categories] = await Promise.all([ + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.find({ + _id: { $ne: portfolio._id }, + category: portfolio.category, + isPublished: true + }).limit(3); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .sort({ featured: -1, order: 1 }) + .populate('portfolio', 'title images'); + + const categories = await Service.distinct('category', { isActive: true }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .select('name pricing category') + .sort({ category: 1, name: 1 }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251019203308.js b/.history/routes/index_20251019203308.js new file mode 100644 index 0000000..6b71092 --- /dev/null +++ b/.history/routes/index_20251019203308.js @@ -0,0 +1,215 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [portfolio, total, categories] = await Promise.all([ + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .sort({ featured: -1, order: 1 }) + .populate('portfolio', 'title images'); + + const categories = await Service.distinct('category', { isActive: true }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .select('name pricing category') + .sort({ category: 1, name: 1 }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251019203315.js b/.history/routes/index_20251019203315.js new file mode 100644 index 0000000..565f32f --- /dev/null +++ b/.history/routes/index_20251019203315.js @@ -0,0 +1,216 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [portfolio, total, categories] = await Promise.all([ + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .sort({ featured: -1, order: 1 }) + .populate('portfolio', 'title images'); + + const categories = await Service.distinct('category', { isActive: true }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .select('name pricing category') + .sort({ category: 1, name: 1 }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251019203341.js b/.history/routes/index_20251019203341.js new file mode 100644 index 0000000..565f32f --- /dev/null +++ b/.history/routes/index_20251019203341.js @@ -0,0 +1,216 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [portfolio, total, categories] = await Promise.all([ + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .sort({ featured: -1, order: 1 }) + .populate('portfolio', 'title images'); + + const categories = await Service.distinct('category', { isActive: true }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .select('name pricing category') + .sort({ category: 1, name: 1 }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251019204717.js b/.history/routes/index_20251019204717.js new file mode 100644 index 0000000..e3828a1 --- /dev/null +++ b/.history/routes/index_20251019204717.js @@ -0,0 +1,221 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [portfolio, total, categories] = await Promise.all([ + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }); + + const categories = await Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.find({ isActive: true }) + .select('name pricing category') + .sort({ category: 1, name: 1 }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251019204728.js b/.history/routes/index_20251019204728.js new file mode 100644 index 0000000..5126df1 --- /dev/null +++ b/.history/routes/index_20251019204728.js @@ -0,0 +1,223 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [portfolio, total, categories] = await Promise.all([ + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }); + + const categories = await Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251019204805.js b/.history/routes/index_20251019204805.js new file mode 100644 index 0000000..5126df1 --- /dev/null +++ b/.history/routes/index_20251019204805.js @@ -0,0 +1,223 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [portfolio, total, categories] = await Promise.all([ + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }); + + const categories = await Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251019204839.js b/.history/routes/index_20251019204839.js new file mode 100644 index 0000000..3ee949b --- /dev/null +++ b/.history/routes/index_20251019204839.js @@ -0,0 +1,223 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [portfolio, total, categories] = await Promise.all([ + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + portfolioItems: portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }); + + const categories = await Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251019204914.js b/.history/routes/index_20251019204914.js new file mode 100644 index 0000000..3ee949b --- /dev/null +++ b/.history/routes/index_20251019204914.js @@ -0,0 +1,223 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [portfolio, total, categories] = await Promise.all([ + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + portfolioItems: portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }); + + const categories = await Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251020035435.js b/.history/routes/index_20251020035435.js new file mode 100644 index 0000000..6beb52f --- /dev/null +++ b/.history/routes/index_20251020035435.js @@ -0,0 +1,225 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [settings, portfolio, total, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + settings: settings || {}, + portfolioItems: portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }); + + const categories = await Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251020035447.js b/.history/routes/index_20251020035447.js new file mode 100644 index 0000000..df5cdaf --- /dev/null +++ b/.history/routes/index_20251020035447.js @@ -0,0 +1,230 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [settings, portfolio, total, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + settings: settings || {}, + portfolioItems: portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const [settings, portfolio] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findByPk(req.params.id) + ]); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + settings: settings || {}, + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + settings: settings || {}, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }); + + const categories = await Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }); + + res.render('services', { + title: 'Services - SmartSolTech', + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251020035455.js b/.history/routes/index_20251020035455.js new file mode 100644 index 0000000..2b21a88 --- /dev/null +++ b/.history/routes/index_20251020035455.js @@ -0,0 +1,233 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [settings, portfolio, total, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + settings: settings || {}, + portfolioItems: portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const [settings, portfolio] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findByPk(req.params.id) + ]); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + settings: settings || {}, + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + settings: settings || {}, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const [settings, services, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }), + Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }) + ]); + + res.render('services', { + title: 'Services - SmartSolTech', + settings: settings || {}, + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const services = await Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251020035505.js b/.history/routes/index_20251020035505.js new file mode 100644 index 0000000..c57e2a6 --- /dev/null +++ b/.history/routes/index_20251020035505.js @@ -0,0 +1,237 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [settings, portfolio, total, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + settings: settings || {}, + portfolioItems: portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const [settings, portfolio] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findByPk(req.params.id) + ]); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + settings: settings || {}, + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + settings: settings || {}, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const [settings, services, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }), + Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }) + ]); + + res.render('services', { + title: 'Services - SmartSolTech', + settings: settings || {}, + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const [settings, services] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }) + ]); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + settings: settings || {}, + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251020035544.js b/.history/routes/index_20251020035544.js new file mode 100644 index 0000000..8a6b9a4 --- /dev/null +++ b/.history/routes/index_20251020035544.js @@ -0,0 +1,238 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [settings, portfolio, total, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + settings: settings || {}, + portfolioItems: portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const [settings, portfolio] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findByPk(req.params.id) + ]); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + settings: settings || {}, + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + settings: settings || {}, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const [settings, services, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }), + Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }) + ]); + + res.render('services', { + title: 'Services - SmartSolTech', + settings: settings || {}, + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const [settings, services] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }) + ]); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + settings: settings || {}, + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251020035550.js b/.history/routes/index_20251020035550.js new file mode 100644 index 0000000..b991ee4 --- /dev/null +++ b/.history/routes/index_20251020035550.js @@ -0,0 +1,239 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [settings, portfolio, total, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + settings: settings || {}, + portfolioItems: portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const [settings, portfolio] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findByPk(req.params.id) + ]); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + settings: settings || {}, + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + settings: settings || {}, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const [settings, services, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }), + Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }) + ]); + + res.render('services', { + title: 'Services - SmartSolTech', + settings: settings || {}, + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const [settings, services] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }) + ]); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + settings: settings || {}, + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251020035557.js b/.history/routes/index_20251020035557.js new file mode 100644 index 0000000..21900a4 --- /dev/null +++ b/.history/routes/index_20251020035557.js @@ -0,0 +1,240 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [settings, portfolio, total, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + settings: settings || {}, + portfolioItems: portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const [settings, portfolio] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findByPk(req.params.id) + ]); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + settings: settings || {}, + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + settings: settings || {}, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const [settings, services, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }), + Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }) + ]); + + res.render('services', { + title: 'Services - SmartSolTech', + settings: settings || {}, + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const [settings, services] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }) + ]); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + settings: settings || {}, + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251020035603.js b/.history/routes/index_20251020035603.js new file mode 100644 index 0000000..964220e --- /dev/null +++ b/.history/routes/index_20251020035603.js @@ -0,0 +1,241 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [settings, portfolio, total, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + settings: settings || {}, + portfolioItems: portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const [settings, portfolio] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findByPk(req.params.id) + ]); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + settings: settings || {}, + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + settings: settings || {}, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const [settings, services, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }), + Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }) + ]); + + res.render('services', { + title: 'Services - SmartSolTech', + settings: settings || {}, + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const [settings, services] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }) + ]); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + settings: settings || {}, + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251020035610.js b/.history/routes/index_20251020035610.js new file mode 100644 index 0000000..b62bd2f --- /dev/null +++ b/.history/routes/index_20251020035610.js @@ -0,0 +1,242 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [settings, portfolio, total, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + settings: settings || {}, + portfolioItems: portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const [settings, portfolio] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findByPk(req.params.id) + ]); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + settings: settings || {}, + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + settings: settings || {}, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const [settings, services, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }), + Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }) + ]); + + res.render('services', { + title: 'Services - SmartSolTech', + settings: settings || {}, + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const [settings, services] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }) + ]); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + settings: settings || {}, + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251020035616.js b/.history/routes/index_20251020035616.js new file mode 100644 index 0000000..de42c39 --- /dev/null +++ b/.history/routes/index_20251020035616.js @@ -0,0 +1,243 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [settings, portfolio, total, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + settings: settings || {}, + portfolioItems: portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const [settings, portfolio] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findByPk(req.params.id) + ]); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + settings: settings || {}, + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + settings: settings || {}, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const [settings, services, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }), + Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }) + ]); + + res.render('services', { + title: 'Services - SmartSolTech', + settings: settings || {}, + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const [settings, services] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }) + ]); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + settings: settings || {}, + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251020035622.js b/.history/routes/index_20251020035622.js new file mode 100644 index 0000000..cd5dfb4 --- /dev/null +++ b/.history/routes/index_20251020035622.js @@ -0,0 +1,244 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [settings, portfolio, total, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + settings: settings || {}, + portfolioItems: portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const [settings, portfolio] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findByPk(req.params.id) + ]); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + settings: settings || {}, + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + settings: settings || {}, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const [settings, services, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }), + Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }) + ]); + + res.render('services', { + title: 'Services - SmartSolTech', + settings: settings || {}, + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const [settings, services] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }) + ]); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + settings: settings || {}, + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/index_20251020035856.js b/.history/routes/index_20251020035856.js new file mode 100644 index 0000000..cd5dfb4 --- /dev/null +++ b/.history/routes/index_20251020035856.js @@ -0,0 +1,244 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio, Service, SiteSettings } = require('../models'); +const { Op } = require('sequelize'); + +// Home page +router.get('/', async (req, res) => { + try { + const [settings, featuredPortfolio, featuredServices] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: { featured: true, isPublished: true }, + order: [['order', 'ASC'], ['createdAt', 'DESC']], + limit: 6 + }), + Service.findAll({ + where: { featured: true, isActive: true }, + order: [['order', 'ASC']], + limit: 4 + }) + ]); + + res.render('index', { + title: 'SmartSolTech - Innovative Technology Solutions', + settings: settings || {}, + featuredPortfolio, + featuredServices, + currentPage: 'home' + }); + } catch (error) { + console.error('Home page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// About page +router.get('/about', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('about', { + title: 'About Us - SmartSolTech', + settings, + currentPage: 'about' + }); + } catch (error) { + console.error('About page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Portfolio page +router.get('/portfolio', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = 12; + const skip = (page - 1) * limit; + const category = req.query.category; + + let query = { isPublished: true }; + if (category && category !== 'all') { + query.category = category; + } + + const [settings, portfolio, total, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findAll({ + where: query, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit + }), + Portfolio.count({ where: query }), + Portfolio.findAll({ + where: { isPublished: true }, + attributes: ['category'], + group: ['category'] + }).then(results => results.map(r => r.category)) + ]); + + const totalPages = Math.ceil(total / limit); + + res.render('portfolio', { + title: 'Portfolio - SmartSolTech', + settings: settings || {}, + portfolioItems: portfolio, + categories, + currentCategory: category || 'all', + pagination: { + current: page, + total: totalPages, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Portfolio detail page +router.get('/portfolio/:id', async (req, res) => { + try { + const [settings, portfolio] = await Promise.all([ + SiteSettings.findOne() || {}, + Portfolio.findByPk(req.params.id) + ]); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).render('404', { + title: '404 - Project Not Found', + settings: settings || {}, + message: 'The requested project was not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + order: [['publishedAt', 'DESC']], + limit: 3 + }); + + res.render('portfolio-detail', { + title: `${portfolio.title} - Portfolio - SmartSolTech`, + settings: settings || {}, + portfolio, + relatedProjects, + currentPage: 'portfolio' + }); + } catch (error) { + console.error('Portfolio detail error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Services page +router.get('/services', async (req, res) => { + try { + const [settings, services, categories] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + order: [['featured', 'DESC'], ['order', 'ASC']] + }), + Service.findAll({ + where: { isActive: true }, + attributes: ['category'], + group: ['category'] + }) + ]); + + res.render('services', { + title: 'Services - SmartSolTech', + settings: settings || {}, + services, + categories, + currentPage: 'services' + }); + } catch (error) { + console.error('Services page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Calculator page +router.get('/calculator', async (req, res) => { + try { + const [settings, services] = await Promise.all([ + SiteSettings.findOne() || {}, + Service.findAll({ + where: { isActive: true }, + attributes: ['id', 'name', 'pricing', 'category'], + order: [['category', 'ASC'], ['name', 'ASC']] + }) + ]); + + res.render('calculator', { + title: 'Project Calculator - SmartSolTech', + settings: settings || {}, + services, + currentPage: 'calculator' + }); + } catch (error) { + console.error('Calculator page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +// Contact page +router.get('/contact', async (req, res) => { + try { + const settings = await SiteSettings.findOne() || {}; + + res.render('contact', { + title: 'Contact Us - SmartSolTech', + settings, + currentPage: 'contact' + }); + } catch (error) { + console.error('Contact page error:', error); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: 'Something went wrong' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/media_20251022052143.js b/.history/routes/media_20251022052143.js new file mode 100644 index 0000000..fb4c857 --- /dev/null +++ b/.history/routes/media_20251022052143.js @@ -0,0 +1,415 @@ +const express = require('express'); +const router = express.Router(); +const multer = require('multer'); +const sharp = require('sharp'); +const path = require('path'); +const fs = require('fs').promises; + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: 'Authentication required' + }); + } + next(); +}; + +// Configure multer for file uploads +const storage = multer.diskStorage({ + destination: async (req, file, cb) => { + const uploadPath = path.join(__dirname, '../public/uploads'); + try { + await fs.mkdir(uploadPath, { recursive: true }); + cb(null, uploadPath); + } catch (error) { + cb(error); + } + }, + filename: (req, file, cb) => { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); + const ext = path.extname(file.originalname); + cb(null, file.fieldname + '-' + uniqueSuffix + ext); + } +}); + +const upload = multer({ + storage: storage, + limits: { + fileSize: parseInt(process.env.MAX_FILE_SIZE) || 10 * 1024 * 1024, // 10MB + files: 10 + }, + fileFilter: (req, file, cb) => { + // Allow images only + if (file.mimetype.startsWith('image/')) { + cb(null, true); + } else { + cb(new Error('Only image files are allowed'), false); + } + } +}); + +// Upload single image +router.post('/upload', requireAuth, upload.single('image'), async (req, res) => { + try { + if (!req.file) { + return res.status(400).json({ + success: false, + message: 'No file uploaded' + }); + } + + const originalPath = req.file.path; + const filename = req.file.filename; + const nameWithoutExt = path.parse(filename).name; + + // Create optimized versions + const sizes = { + thumbnail: { width: 300, height: 200 }, + medium: { width: 800, height: 600 }, + large: { width: 1200, height: 900 } + }; + + const optimizedImages = {}; + + for (const [sizeName, dimensions] of Object.entries(sizes)) { + const outputPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-${sizeName}.webp` + ); + + await sharp(originalPath) + .resize(dimensions.width, dimensions.height, { + fit: 'inside', + withoutEnlargement: true + }) + .webp({ quality: 85 }) + .toFile(outputPath); + + optimizedImages[sizeName] = `/uploads/${path.basename(outputPath)}`; + } + + // Keep original as well (converted to webp for better compression) + const originalWebpPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-original.webp` + ); + + await sharp(originalPath) + .webp({ quality: 90 }) + .toFile(originalWebpPath); + + optimizedImages.original = `/uploads/${path.basename(originalWebpPath)}`; + + // Remove the original uploaded file + await fs.unlink(originalPath); + + res.json({ + success: true, + message: 'Image uploaded and optimized successfully', + images: optimizedImages, + metadata: { + originalName: req.file.originalname, + mimeType: req.file.mimetype, + size: req.file.size + } + }); + } catch (error) { + console.error('Image upload error:', error); + + // Clean up files on error + if (req.file && req.file.path) { + try { + await fs.unlink(req.file.path); + } catch (unlinkError) { + console.error('Error removing file:', unlinkError); + } + } + + res.status(500).json({ + success: false, + message: 'Error uploading image' + }); + } +}); + +// Upload multiple images +router.post('/upload-multiple', requireAuth, upload.array('images', 10), async (req, res) => { + try { + if (!req.files || req.files.length === 0) { + return res.status(400).json({ + success: false, + message: 'No files uploaded' + }); + } + + const uploadedImages = []; + + for (const file of req.files) { + try { + const originalPath = file.path; + const filename = file.filename; + const nameWithoutExt = path.parse(filename).name; + + // Create optimized versions + const sizes = { + thumbnail: { width: 300, height: 200 }, + medium: { width: 800, height: 600 }, + large: { width: 1200, height: 900 } + }; + + const optimizedImages = {}; + + for (const [sizeName, dimensions] of Object.entries(sizes)) { + const outputPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-${sizeName}.webp` + ); + + await sharp(originalPath) + .resize(dimensions.width, dimensions.height, { + fit: 'inside', + withoutEnlargement: true + }) + .webp({ quality: 85 }) + .toFile(outputPath); + + optimizedImages[sizeName] = `/uploads/${path.basename(outputPath)}`; + } + + // Original as webp + const originalWebpPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-original.webp` + ); + + await sharp(originalPath) + .webp({ quality: 90 }) + .toFile(originalWebpPath); + + optimizedImages.original = `/uploads/${path.basename(originalWebpPath)}`; + + uploadedImages.push({ + originalName: file.originalname, + images: optimizedImages, + metadata: { + mimeType: file.mimetype, + size: file.size + } + }); + + // Remove original file + await fs.unlink(originalPath); + } catch (fileError) { + console.error(`Error processing file ${file.originalname}:`, fileError); + // Clean up this file and continue with others + try { + await fs.unlink(file.path); + } catch (unlinkError) { + console.error('Error removing file:', unlinkError); + } + } + } + + res.json({ + success: true, + message: `${uploadedImages.length} images uploaded and optimized successfully`, + images: uploadedImages + }); + } catch (error) { + console.error('Multiple images upload error:', error); + + // Clean up files on error + if (req.files) { + for (const file of req.files) { + try { + await fs.unlink(file.path); + } catch (unlinkError) { + console.error('Error removing file:', unlinkError); + } + } + } + + res.status(500).json({ + success: false, + message: 'Error uploading images' + }); + } +}); + +// Delete image +router.delete('/:filename', requireAuth, async (req, res) => { + try { + const filename = req.params.filename; + const uploadPath = path.join(__dirname, '../public/uploads'); + + // Security check - ensure filename doesn't contain path traversal + if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) { + return res.status(400).json({ + success: false, + message: 'Invalid filename' + }); + } + + const filePath = path.join(uploadPath, filename); + + try { + await fs.access(filePath); + await fs.unlink(filePath); + + res.json({ + success: true, + message: 'Image deleted successfully' + }); + } catch (error) { + if (error.code === 'ENOENT') { + return res.status(404).json({ + success: false, + message: 'Image not found' + }); + } + throw error; + } + } catch (error) { + console.error('Image deletion error:', error); + res.status(500).json({ + success: false, + message: 'Error deleting image' + }); + } +}); + +// List uploaded images +router.get('/list', requireAuth, async (req, res) => { + try { + const uploadPath = path.join(__dirname, '../public/uploads'); + + // Create uploads directory if it doesn't exist + try { + await fs.mkdir(uploadPath, { recursive: true }); + } catch (mkdirError) { + // Directory might already exist, continue + } + + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 50; + + let files; + try { + files = await fs.readdir(uploadPath); + } catch (readdirError) { + if (readdirError.code === 'ENOENT') { + // Directory doesn't exist, return empty list + return res.json({ + success: true, + images: [], + pagination: { + current: 1, + total: 0, + limit, + totalItems: 0, + hasNext: false, + hasPrev: false + } + }); + } + throw readdirError; + } + + const imageFiles = files.filter(file => + /\.(jpg|jpeg|png|gif|webp|svg)$/i.test(file) + ); + + // Sort files by modification time (newest first) + const filesWithStats = await Promise.all( + imageFiles.map(async (file) => { + try { + const filePath = path.join(uploadPath, file); + const stats = await fs.stat(filePath); + return { file, stats }; + } catch (error) { + return null; + } + }) + ); + + const validFiles = filesWithStats + .filter(item => item !== null) + .sort((a, b) => b.stats.mtime - a.stats.mtime); + + const total = validFiles.length; + const totalPages = Math.ceil(total / limit); + const start = (page - 1) * limit; + const end = start + limit; + + const paginatedFiles = validFiles.slice(start, end); + + const imagesWithDetails = await Promise.all( + paginatedFiles.map(async ({ file, stats }) => { + try { + const filePath = path.join(uploadPath, file); + + // Try to get image dimensions + let dimensions = null; + try { + const metadata = await sharp(filePath).metadata(); + dimensions = { + width: metadata.width, + height: metadata.height + }; + } catch (sharpError) { + // Not a processable image, skip dimensions + } + + // Determine mime type from extension + const ext = path.extname(file).toLowerCase(); + let mimetype = 'image/jpeg'; + switch (ext) { + case '.png': mimetype = 'image/png'; break; + case '.gif': mimetype = 'image/gif'; break; + case '.webp': mimetype = 'image/webp'; break; + case '.svg': mimetype = 'image/svg+xml'; break; + } + + return { + filename: file, + url: `/uploads/${file}`, + size: stats.size, + mimetype, + uploadedAt: stats.birthtime || stats.mtime, + modifiedAt: stats.mtime, + dimensions, + isImage: true + }; + } catch (error) { + console.error(`Error getting details for ${file}:`, error); + return null; + } + }) + ); + + const validImages = imagesWithDetails.filter(img => img !== null); + + res.json({ + success: true, + images: validImages, + pagination: { + current: page, + total: totalPages, + limit, + totalItems: total, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('List images error:', error); + res.status(500).json({ + success: false, + message: 'Error listing images' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/media_20251022052247.js b/.history/routes/media_20251022052247.js new file mode 100644 index 0000000..fb4c857 --- /dev/null +++ b/.history/routes/media_20251022052247.js @@ -0,0 +1,415 @@ +const express = require('express'); +const router = express.Router(); +const multer = require('multer'); +const sharp = require('sharp'); +const path = require('path'); +const fs = require('fs').promises; + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: 'Authentication required' + }); + } + next(); +}; + +// Configure multer for file uploads +const storage = multer.diskStorage({ + destination: async (req, file, cb) => { + const uploadPath = path.join(__dirname, '../public/uploads'); + try { + await fs.mkdir(uploadPath, { recursive: true }); + cb(null, uploadPath); + } catch (error) { + cb(error); + } + }, + filename: (req, file, cb) => { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); + const ext = path.extname(file.originalname); + cb(null, file.fieldname + '-' + uniqueSuffix + ext); + } +}); + +const upload = multer({ + storage: storage, + limits: { + fileSize: parseInt(process.env.MAX_FILE_SIZE) || 10 * 1024 * 1024, // 10MB + files: 10 + }, + fileFilter: (req, file, cb) => { + // Allow images only + if (file.mimetype.startsWith('image/')) { + cb(null, true); + } else { + cb(new Error('Only image files are allowed'), false); + } + } +}); + +// Upload single image +router.post('/upload', requireAuth, upload.single('image'), async (req, res) => { + try { + if (!req.file) { + return res.status(400).json({ + success: false, + message: 'No file uploaded' + }); + } + + const originalPath = req.file.path; + const filename = req.file.filename; + const nameWithoutExt = path.parse(filename).name; + + // Create optimized versions + const sizes = { + thumbnail: { width: 300, height: 200 }, + medium: { width: 800, height: 600 }, + large: { width: 1200, height: 900 } + }; + + const optimizedImages = {}; + + for (const [sizeName, dimensions] of Object.entries(sizes)) { + const outputPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-${sizeName}.webp` + ); + + await sharp(originalPath) + .resize(dimensions.width, dimensions.height, { + fit: 'inside', + withoutEnlargement: true + }) + .webp({ quality: 85 }) + .toFile(outputPath); + + optimizedImages[sizeName] = `/uploads/${path.basename(outputPath)}`; + } + + // Keep original as well (converted to webp for better compression) + const originalWebpPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-original.webp` + ); + + await sharp(originalPath) + .webp({ quality: 90 }) + .toFile(originalWebpPath); + + optimizedImages.original = `/uploads/${path.basename(originalWebpPath)}`; + + // Remove the original uploaded file + await fs.unlink(originalPath); + + res.json({ + success: true, + message: 'Image uploaded and optimized successfully', + images: optimizedImages, + metadata: { + originalName: req.file.originalname, + mimeType: req.file.mimetype, + size: req.file.size + } + }); + } catch (error) { + console.error('Image upload error:', error); + + // Clean up files on error + if (req.file && req.file.path) { + try { + await fs.unlink(req.file.path); + } catch (unlinkError) { + console.error('Error removing file:', unlinkError); + } + } + + res.status(500).json({ + success: false, + message: 'Error uploading image' + }); + } +}); + +// Upload multiple images +router.post('/upload-multiple', requireAuth, upload.array('images', 10), async (req, res) => { + try { + if (!req.files || req.files.length === 0) { + return res.status(400).json({ + success: false, + message: 'No files uploaded' + }); + } + + const uploadedImages = []; + + for (const file of req.files) { + try { + const originalPath = file.path; + const filename = file.filename; + const nameWithoutExt = path.parse(filename).name; + + // Create optimized versions + const sizes = { + thumbnail: { width: 300, height: 200 }, + medium: { width: 800, height: 600 }, + large: { width: 1200, height: 900 } + }; + + const optimizedImages = {}; + + for (const [sizeName, dimensions] of Object.entries(sizes)) { + const outputPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-${sizeName}.webp` + ); + + await sharp(originalPath) + .resize(dimensions.width, dimensions.height, { + fit: 'inside', + withoutEnlargement: true + }) + .webp({ quality: 85 }) + .toFile(outputPath); + + optimizedImages[sizeName] = `/uploads/${path.basename(outputPath)}`; + } + + // Original as webp + const originalWebpPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-original.webp` + ); + + await sharp(originalPath) + .webp({ quality: 90 }) + .toFile(originalWebpPath); + + optimizedImages.original = `/uploads/${path.basename(originalWebpPath)}`; + + uploadedImages.push({ + originalName: file.originalname, + images: optimizedImages, + metadata: { + mimeType: file.mimetype, + size: file.size + } + }); + + // Remove original file + await fs.unlink(originalPath); + } catch (fileError) { + console.error(`Error processing file ${file.originalname}:`, fileError); + // Clean up this file and continue with others + try { + await fs.unlink(file.path); + } catch (unlinkError) { + console.error('Error removing file:', unlinkError); + } + } + } + + res.json({ + success: true, + message: `${uploadedImages.length} images uploaded and optimized successfully`, + images: uploadedImages + }); + } catch (error) { + console.error('Multiple images upload error:', error); + + // Clean up files on error + if (req.files) { + for (const file of req.files) { + try { + await fs.unlink(file.path); + } catch (unlinkError) { + console.error('Error removing file:', unlinkError); + } + } + } + + res.status(500).json({ + success: false, + message: 'Error uploading images' + }); + } +}); + +// Delete image +router.delete('/:filename', requireAuth, async (req, res) => { + try { + const filename = req.params.filename; + const uploadPath = path.join(__dirname, '../public/uploads'); + + // Security check - ensure filename doesn't contain path traversal + if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) { + return res.status(400).json({ + success: false, + message: 'Invalid filename' + }); + } + + const filePath = path.join(uploadPath, filename); + + try { + await fs.access(filePath); + await fs.unlink(filePath); + + res.json({ + success: true, + message: 'Image deleted successfully' + }); + } catch (error) { + if (error.code === 'ENOENT') { + return res.status(404).json({ + success: false, + message: 'Image not found' + }); + } + throw error; + } + } catch (error) { + console.error('Image deletion error:', error); + res.status(500).json({ + success: false, + message: 'Error deleting image' + }); + } +}); + +// List uploaded images +router.get('/list', requireAuth, async (req, res) => { + try { + const uploadPath = path.join(__dirname, '../public/uploads'); + + // Create uploads directory if it doesn't exist + try { + await fs.mkdir(uploadPath, { recursive: true }); + } catch (mkdirError) { + // Directory might already exist, continue + } + + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 50; + + let files; + try { + files = await fs.readdir(uploadPath); + } catch (readdirError) { + if (readdirError.code === 'ENOENT') { + // Directory doesn't exist, return empty list + return res.json({ + success: true, + images: [], + pagination: { + current: 1, + total: 0, + limit, + totalItems: 0, + hasNext: false, + hasPrev: false + } + }); + } + throw readdirError; + } + + const imageFiles = files.filter(file => + /\.(jpg|jpeg|png|gif|webp|svg)$/i.test(file) + ); + + // Sort files by modification time (newest first) + const filesWithStats = await Promise.all( + imageFiles.map(async (file) => { + try { + const filePath = path.join(uploadPath, file); + const stats = await fs.stat(filePath); + return { file, stats }; + } catch (error) { + return null; + } + }) + ); + + const validFiles = filesWithStats + .filter(item => item !== null) + .sort((a, b) => b.stats.mtime - a.stats.mtime); + + const total = validFiles.length; + const totalPages = Math.ceil(total / limit); + const start = (page - 1) * limit; + const end = start + limit; + + const paginatedFiles = validFiles.slice(start, end); + + const imagesWithDetails = await Promise.all( + paginatedFiles.map(async ({ file, stats }) => { + try { + const filePath = path.join(uploadPath, file); + + // Try to get image dimensions + let dimensions = null; + try { + const metadata = await sharp(filePath).metadata(); + dimensions = { + width: metadata.width, + height: metadata.height + }; + } catch (sharpError) { + // Not a processable image, skip dimensions + } + + // Determine mime type from extension + const ext = path.extname(file).toLowerCase(); + let mimetype = 'image/jpeg'; + switch (ext) { + case '.png': mimetype = 'image/png'; break; + case '.gif': mimetype = 'image/gif'; break; + case '.webp': mimetype = 'image/webp'; break; + case '.svg': mimetype = 'image/svg+xml'; break; + } + + return { + filename: file, + url: `/uploads/${file}`, + size: stats.size, + mimetype, + uploadedAt: stats.birthtime || stats.mtime, + modifiedAt: stats.mtime, + dimensions, + isImage: true + }; + } catch (error) { + console.error(`Error getting details for ${file}:`, error); + return null; + } + }) + ); + + const validImages = imagesWithDetails.filter(img => img !== null); + + res.json({ + success: true, + images: validImages, + pagination: { + current: page, + total: totalPages, + limit, + totalItems: total, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('List images error:', error); + res.status(500).json({ + success: false, + message: 'Error listing images' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/media_20251022195139.js b/.history/routes/media_20251022195139.js new file mode 100644 index 0000000..dd066ec --- /dev/null +++ b/.history/routes/media_20251022195139.js @@ -0,0 +1,733 @@ +const express = require('express'); +const router = express.Router(); +const multer = require('multer'); +const sharp = require('sharp'); +const path = require('path'); +const fs = require('fs').promises; + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: 'Authentication required' + }); + } + next(); +}; + +// Configure multer for file uploads +const storage = multer.diskStorage({ + destination: async (req, file, cb) => { + const uploadPath = path.join(__dirname, '../public/uploads'); + try { + await fs.mkdir(uploadPath, { recursive: true }); + cb(null, uploadPath); + } catch (error) { + cb(error); + } + }, + filename: (req, file, cb) => { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); + const ext = path.extname(file.originalname); + cb(null, file.fieldname + '-' + uniqueSuffix + ext); + } +}); + +const upload = multer({ + storage: storage, + limits: { + fileSize: parseInt(process.env.MAX_FILE_SIZE) || 10 * 1024 * 1024, // 10MB + files: 10 + }, + fileFilter: (req, file, cb) => { + // Allow images only + if (file.mimetype.startsWith('image/')) { + cb(null, true); + } else { + cb(new Error('Only image files are allowed'), false); + } + } +}); + +// Upload single image +router.post('/upload', requireAuth, upload.single('image'), async (req, res) => { + try { + if (!req.file) { + return res.status(400).json({ + success: false, + message: 'No file uploaded' + }); + } + + const originalPath = req.file.path; + const filename = req.file.filename; + const nameWithoutExt = path.parse(filename).name; + + // Create optimized versions + const sizes = { + thumbnail: { width: 300, height: 200 }, + medium: { width: 800, height: 600 }, + large: { width: 1200, height: 900 } + }; + + const optimizedImages = {}; + + for (const [sizeName, dimensions] of Object.entries(sizes)) { + const outputPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-${sizeName}.webp` + ); + + await sharp(originalPath) + .resize(dimensions.width, dimensions.height, { + fit: 'inside', + withoutEnlargement: true + }) + .webp({ quality: 85 }) + .toFile(outputPath); + + optimizedImages[sizeName] = `/uploads/${path.basename(outputPath)}`; + } + + // Keep original as well (converted to webp for better compression) + const originalWebpPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-original.webp` + ); + + await sharp(originalPath) + .webp({ quality: 90 }) + .toFile(originalWebpPath); + + optimizedImages.original = `/uploads/${path.basename(originalWebpPath)}`; + + // Remove the original uploaded file + await fs.unlink(originalPath); + + res.json({ + success: true, + message: 'Image uploaded and optimized successfully', + images: optimizedImages, + metadata: { + originalName: req.file.originalname, + mimeType: req.file.mimetype, + size: req.file.size + } + }); + } catch (error) { + console.error('Image upload error:', error); + + // Clean up files on error + if (req.file && req.file.path) { + try { + await fs.unlink(req.file.path); + } catch (unlinkError) { + console.error('Error removing file:', unlinkError); + } + } + + res.status(500).json({ + success: false, + message: 'Error uploading image' + }); + } +}); + +// Upload multiple images +router.post('/upload-multiple', requireAuth, upload.array('images', 10), async (req, res) => { + try { + if (!req.files || req.files.length === 0) { + return res.status(400).json({ + success: false, + message: 'No files uploaded' + }); + } + + const uploadedImages = []; + + for (const file of req.files) { + try { + const originalPath = file.path; + const filename = file.filename; + const nameWithoutExt = path.parse(filename).name; + + // Create optimized versions + const sizes = { + thumbnail: { width: 300, height: 200 }, + medium: { width: 800, height: 600 }, + large: { width: 1200, height: 900 } + }; + + const optimizedImages = {}; + + for (const [sizeName, dimensions] of Object.entries(sizes)) { + const outputPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-${sizeName}.webp` + ); + + await sharp(originalPath) + .resize(dimensions.width, dimensions.height, { + fit: 'inside', + withoutEnlargement: true + }) + .webp({ quality: 85 }) + .toFile(outputPath); + + optimizedImages[sizeName] = `/uploads/${path.basename(outputPath)}`; + } + + // Original as webp + const originalWebpPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-original.webp` + ); + + await sharp(originalPath) + .webp({ quality: 90 }) + .toFile(originalWebpPath); + + optimizedImages.original = `/uploads/${path.basename(originalWebpPath)}`; + + uploadedImages.push({ + originalName: file.originalname, + images: optimizedImages, + metadata: { + mimeType: file.mimetype, + size: file.size + } + }); + + // Remove original file + await fs.unlink(originalPath); + } catch (fileError) { + console.error(`Error processing file ${file.originalname}:`, fileError); + // Clean up this file and continue with others + try { + await fs.unlink(file.path); + } catch (unlinkError) { + console.error('Error removing file:', unlinkError); + } + } + } + + res.json({ + success: true, + message: `${uploadedImages.length} images uploaded and optimized successfully`, + images: uploadedImages + }); + } catch (error) { + console.error('Multiple images upload error:', error); + + // Clean up files on error + if (req.files) { + for (const file of req.files) { + try { + await fs.unlink(file.path); + } catch (unlinkError) { + console.error('Error removing file:', unlinkError); + } + } + } + + res.status(500).json({ + success: false, + message: 'Error uploading images' + }); + } +}); + +// Delete image +router.delete('/:filename', requireAuth, async (req, res) => { + try { + const filename = req.params.filename; + const uploadPath = path.join(__dirname, '../public/uploads'); + + // Security check - ensure filename doesn't contain path traversal + if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) { + return res.status(400).json({ + success: false, + message: 'Invalid filename' + }); + } + + const filePath = path.join(uploadPath, filename); + + try { + await fs.access(filePath); + await fs.unlink(filePath); + + res.json({ + success: true, + message: 'Image deleted successfully' + }); + } catch (error) { + if (error.code === 'ENOENT') { + return res.status(404).json({ + success: false, + message: 'Image not found' + }); + } + throw error; + } + } catch (error) { + console.error('Image deletion error:', error); + res.status(500).json({ + success: false, + message: 'Error deleting image' + }); + } +}); + +// List uploaded images with advanced filtering and search +router.get('/list', requireAuth, async (req, res) => { + try { + const uploadPath = path.join(__dirname, '../public/uploads'); + + // Create uploads directory if it doesn't exist + try { + await fs.mkdir(uploadPath, { recursive: true }); + } catch (mkdirError) { + // Directory might already exist, continue + } + + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 24; + const search = req.query.search?.toLowerCase() || ''; + const sortBy = req.query.sortBy || 'date'; // date, name, size + const sortOrder = req.query.sortOrder || 'desc'; // asc, desc + const fileType = req.query.fileType || 'all'; // all, image, video, document + + let files; + try { + files = await fs.readdir(uploadPath); + } catch (readdirError) { + if (readdirError.code === 'ENOENT') { + return res.json({ + success: true, + images: [], + pagination: { + current: 1, + total: 0, + limit, + totalItems: 0, + hasNext: false, + hasPrev: false + }, + filters: { + search: '', + sortBy: 'date', + sortOrder: 'desc', + fileType: 'all' + } + }); + } + throw readdirError; + } + + // Filter by file type + let filteredFiles = files; + if (fileType !== 'all') { + const typePatterns = { + image: /\.(jpg|jpeg|png|gif|webp|svg|bmp|tiff?)$/i, + video: /\.(mp4|webm|avi|mov|mkv|wmv|flv)$/i, + document: /\.(pdf|doc|docx|txt|rtf|odt)$/i + }; + + const pattern = typePatterns[fileType]; + if (pattern) { + filteredFiles = files.filter(file => pattern.test(file)); + } + } else { + // Only show supported media files + filteredFiles = files.filter(file => + /\.(jpg|jpeg|png|gif|webp|svg|bmp|tiff?|mp4|webm|avi|mov|mkv|pdf|doc|docx)$/i.test(file) + ); + } + + // Apply search filter + if (search) { + filteredFiles = filteredFiles.filter(file => + file.toLowerCase().includes(search) + ); + } + + // Get file stats and create file objects + const filesWithStats = await Promise.all( + filteredFiles.map(async (file) => { + try { + const filePath = path.join(uploadPath, file); + const stats = await fs.stat(filePath); + return { file, stats, filePath }; + } catch (error) { + return null; + } + }) + ); + + const validFiles = filesWithStats.filter(item => item !== null); + + // Sort files + validFiles.sort((a, b) => { + let aValue, bValue; + + switch (sortBy) { + case 'name': + aValue = a.file.toLowerCase(); + bValue = b.file.toLowerCase(); + break; + case 'size': + aValue = a.stats.size; + bValue = b.stats.size; + break; + case 'date': + default: + aValue = a.stats.mtime; + bValue = b.stats.mtime; + break; + } + + if (sortOrder === 'asc') { + return aValue > bValue ? 1 : -1; + } else { + return aValue < bValue ? 1 : -1; + } + }); + + const total = validFiles.length; + const totalPages = Math.ceil(total / limit); + const start = (page - 1) * limit; + const end = start + limit; + + const paginatedFiles = validFiles.slice(start, end); + + const filesWithDetails = await Promise.all( + paginatedFiles.map(async ({ file, stats, filePath }) => { + try { + const ext = path.extname(file).toLowerCase(); + let fileDetails = { + filename: file, + url: `/uploads/${file}`, + size: stats.size, + uploadedAt: stats.birthtime || stats.mtime, + modifiedAt: stats.mtime, + extension: ext, + isImage: false, + isVideo: false, + isDocument: false + }; + + // Determine file type and get additional info + if (/\.(jpg|jpeg|png|gif|webp|svg|bmp|tiff?)$/i.test(file)) { + fileDetails.isImage = true; + fileDetails.mimetype = `image/${ext.replace('.', '')}`; + + // Get image dimensions + try { + const metadata = await sharp(filePath).metadata(); + fileDetails.dimensions = { + width: metadata.width, + height: metadata.height + }; + fileDetails.format = metadata.format; + } catch (sharpError) { + console.warn(`Could not get image metadata for ${file}`); + } + } else if (/\.(mp4|webm|avi|mov|mkv|wmv|flv)$/i.test(file)) { + fileDetails.isVideo = true; + fileDetails.mimetype = `video/${ext.replace('.', '')}`; + } else if (/\.(pdf|doc|docx|txt|rtf|odt)$/i.test(file)) { + fileDetails.isDocument = true; + fileDetails.mimetype = `application/${ext.replace('.', '')}`; + } + + // Generate thumbnail for images + if (fileDetails.isImage && !file.includes('-thumbnail.')) { + const thumbnailPath = path.join(uploadPath, `${path.parse(file).name}-thumbnail.webp`); + try { + await fs.access(thumbnailPath); + fileDetails.thumbnail = `/uploads/${path.basename(thumbnailPath)}`; + } catch { + // Thumbnail doesn't exist, create it + try { + await sharp(filePath) + .resize(200, 150, { + fit: 'cover', + withoutEnlargement: false + }) + .webp({ quality: 80 }) + .toFile(thumbnailPath); + fileDetails.thumbnail = `/uploads/${path.basename(thumbnailPath)}`; + } catch (thumbError) { + console.warn(`Could not create thumbnail for ${file}`); + } + } + } + + return fileDetails; + } catch (error) { + console.error(`Error getting details for ${file}:`, error); + return null; + } + }) + ); + + const validMedia = filesWithDetails.filter(item => item !== null); + + // Calculate storage stats + const totalSize = validFiles.reduce((sum, file) => sum + file.stats.size, 0); + const imageCount = validMedia.filter(f => f.isImage).length; + const videoCount = validMedia.filter(f => f.isVideo).length; + const documentCount = validMedia.filter(f => f.isDocument).length; + + res.json({ + success: true, + files: validMedia, + pagination: { + current: page, + total: totalPages, + limit, + totalItems: total, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + filters: { + search, + sortBy, + sortOrder, + fileType + }, + stats: { + totalFiles: total, + totalSize, + imageCount, + videoCount, + documentCount, + formattedSize: formatFileSize(totalSize) + } + }); + } catch (error) { + console.error('List media error:', error); + res.status(500).json({ + success: false, + message: 'Error listing media files' + }); + } +}); + +// Create folder structure +router.post('/folder', requireAuth, async (req, res) => { + try { + const { folderName } = req.body; + + if (!folderName || !folderName.trim()) { + return res.status(400).json({ + success: false, + message: 'Folder name is required' + }); + } + + // Sanitize folder name + const sanitizedName = folderName.trim().replace(/[^a-zA-Z0-9-_]/g, '-'); + const folderPath = path.join(__dirname, '../public/uploads', sanitizedName); + + try { + await fs.mkdir(folderPath, { recursive: true }); + + res.json({ + success: true, + message: 'Folder created successfully', + folderName: sanitizedName, + folderPath: `/uploads/${sanitizedName}` + }); + } catch (error) { + if (error.code === 'EEXIST') { + return res.status(400).json({ + success: false, + message: 'Folder already exists' + }); + } + throw error; + } + } catch (error) { + console.error('Create folder error:', error); + res.status(500).json({ + success: false, + message: 'Error creating folder' + }); + } +}); + +// Get media file info +router.get('/info/:filename', requireAuth, async (req, res) => { + try { + const filename = req.params.filename; + + // Security check + if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) { + return res.status(400).json({ + success: false, + message: 'Invalid filename' + }); + } + + const filePath = path.join(__dirname, '../public/uploads', filename); + + try { + const stats = await fs.stat(filePath); + const ext = path.extname(filename).toLowerCase(); + + let fileInfo = { + filename, + url: `/uploads/${filename}`, + size: stats.size, + formattedSize: formatFileSize(stats.size), + uploadedAt: stats.birthtime || stats.mtime, + modifiedAt: stats.mtime, + extension: ext, + mimetype: getMimeType(ext) + }; + + // Get additional info for images + if (/\.(jpg|jpeg|png|gif|webp|svg|bmp|tiff?)$/i.test(filename)) { + try { + const metadata = await sharp(filePath).metadata(); + fileInfo.dimensions = { + width: metadata.width, + height: metadata.height + }; + fileInfo.format = metadata.format; + fileInfo.hasAlpha = metadata.hasAlpha; + fileInfo.density = metadata.density; + } catch (sharpError) { + console.warn(`Could not get image metadata for ${filename}`); + } + } + + res.json({ + success: true, + fileInfo + }); + } catch (error) { + if (error.code === 'ENOENT') { + return res.status(404).json({ + success: false, + message: 'File not found' + }); + } + throw error; + } + } catch (error) { + console.error('Get file info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting file information' + }); + } +}); + +// Resize image +router.post('/resize/:filename', requireAuth, async (req, res) => { + try { + const filename = req.params.filename; + const { width, height, quality = 85 } = req.body; + + if (!width && !height) { + return res.status(400).json({ + success: false, + message: 'Width or height must be specified' + }); + } + + // Security check + if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) { + return res.status(400).json({ + success: false, + message: 'Invalid filename' + }); + } + + const originalPath = path.join(__dirname, '../public/uploads', filename); + const nameWithoutExt = path.parse(filename).name; + const resizedPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-${width || 'auto'}x${height || 'auto'}.webp` + ); + + try { + let sharpInstance = sharp(originalPath); + + if (width && height) { + sharpInstance = sharpInstance.resize(parseInt(width), parseInt(height), { + fit: 'cover' + }); + } else if (width) { + sharpInstance = sharpInstance.resize(parseInt(width)); + } else { + sharpInstance = sharpInstance.resize(null, parseInt(height)); + } + + await sharpInstance + .webp({ quality: parseInt(quality) }) + .toFile(resizedPath); + + res.json({ + success: true, + message: 'Image resized successfully', + originalFile: filename, + resizedFile: path.basename(resizedPath), + resizedUrl: `/uploads/${path.basename(resizedPath)}` + }); + } catch (error) { + if (error.code === 'ENOENT') { + return res.status(404).json({ + success: false, + message: 'Original file not found' + }); + } + throw error; + } + } catch (error) { + console.error('Resize image error:', error); + res.status(500).json({ + success: false, + message: 'Error resizing image' + }); + } +}); + +// Utility functions +function formatFileSize(bytes) { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; +} + +function getMimeType(ext) { + const mimeTypes = { + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.png': 'image/png', + '.gif': 'image/gif', + '.webp': 'image/webp', + '.svg': 'image/svg+xml', + '.bmp': 'image/bmp', + '.tiff': 'image/tiff', + '.tif': 'image/tiff', + '.mp4': 'video/mp4', + '.webm': 'video/webm', + '.avi': 'video/x-msvideo', + '.mov': 'video/quicktime', + '.mkv': 'video/x-matroska', + '.pdf': 'application/pdf', + '.doc': 'application/msword', + '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' + }; + + return mimeTypes[ext.toLowerCase()] || 'application/octet-stream'; +} + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/media_20251022195905.js b/.history/routes/media_20251022195905.js new file mode 100644 index 0000000..dd066ec --- /dev/null +++ b/.history/routes/media_20251022195905.js @@ -0,0 +1,733 @@ +const express = require('express'); +const router = express.Router(); +const multer = require('multer'); +const sharp = require('sharp'); +const path = require('path'); +const fs = require('fs').promises; + +// Authentication middleware +const requireAuth = (req, res, next) => { + if (!req.session.user) { + return res.status(401).json({ + success: false, + message: 'Authentication required' + }); + } + next(); +}; + +// Configure multer for file uploads +const storage = multer.diskStorage({ + destination: async (req, file, cb) => { + const uploadPath = path.join(__dirname, '../public/uploads'); + try { + await fs.mkdir(uploadPath, { recursive: true }); + cb(null, uploadPath); + } catch (error) { + cb(error); + } + }, + filename: (req, file, cb) => { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); + const ext = path.extname(file.originalname); + cb(null, file.fieldname + '-' + uniqueSuffix + ext); + } +}); + +const upload = multer({ + storage: storage, + limits: { + fileSize: parseInt(process.env.MAX_FILE_SIZE) || 10 * 1024 * 1024, // 10MB + files: 10 + }, + fileFilter: (req, file, cb) => { + // Allow images only + if (file.mimetype.startsWith('image/')) { + cb(null, true); + } else { + cb(new Error('Only image files are allowed'), false); + } + } +}); + +// Upload single image +router.post('/upload', requireAuth, upload.single('image'), async (req, res) => { + try { + if (!req.file) { + return res.status(400).json({ + success: false, + message: 'No file uploaded' + }); + } + + const originalPath = req.file.path; + const filename = req.file.filename; + const nameWithoutExt = path.parse(filename).name; + + // Create optimized versions + const sizes = { + thumbnail: { width: 300, height: 200 }, + medium: { width: 800, height: 600 }, + large: { width: 1200, height: 900 } + }; + + const optimizedImages = {}; + + for (const [sizeName, dimensions] of Object.entries(sizes)) { + const outputPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-${sizeName}.webp` + ); + + await sharp(originalPath) + .resize(dimensions.width, dimensions.height, { + fit: 'inside', + withoutEnlargement: true + }) + .webp({ quality: 85 }) + .toFile(outputPath); + + optimizedImages[sizeName] = `/uploads/${path.basename(outputPath)}`; + } + + // Keep original as well (converted to webp for better compression) + const originalWebpPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-original.webp` + ); + + await sharp(originalPath) + .webp({ quality: 90 }) + .toFile(originalWebpPath); + + optimizedImages.original = `/uploads/${path.basename(originalWebpPath)}`; + + // Remove the original uploaded file + await fs.unlink(originalPath); + + res.json({ + success: true, + message: 'Image uploaded and optimized successfully', + images: optimizedImages, + metadata: { + originalName: req.file.originalname, + mimeType: req.file.mimetype, + size: req.file.size + } + }); + } catch (error) { + console.error('Image upload error:', error); + + // Clean up files on error + if (req.file && req.file.path) { + try { + await fs.unlink(req.file.path); + } catch (unlinkError) { + console.error('Error removing file:', unlinkError); + } + } + + res.status(500).json({ + success: false, + message: 'Error uploading image' + }); + } +}); + +// Upload multiple images +router.post('/upload-multiple', requireAuth, upload.array('images', 10), async (req, res) => { + try { + if (!req.files || req.files.length === 0) { + return res.status(400).json({ + success: false, + message: 'No files uploaded' + }); + } + + const uploadedImages = []; + + for (const file of req.files) { + try { + const originalPath = file.path; + const filename = file.filename; + const nameWithoutExt = path.parse(filename).name; + + // Create optimized versions + const sizes = { + thumbnail: { width: 300, height: 200 }, + medium: { width: 800, height: 600 }, + large: { width: 1200, height: 900 } + }; + + const optimizedImages = {}; + + for (const [sizeName, dimensions] of Object.entries(sizes)) { + const outputPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-${sizeName}.webp` + ); + + await sharp(originalPath) + .resize(dimensions.width, dimensions.height, { + fit: 'inside', + withoutEnlargement: true + }) + .webp({ quality: 85 }) + .toFile(outputPath); + + optimizedImages[sizeName] = `/uploads/${path.basename(outputPath)}`; + } + + // Original as webp + const originalWebpPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-original.webp` + ); + + await sharp(originalPath) + .webp({ quality: 90 }) + .toFile(originalWebpPath); + + optimizedImages.original = `/uploads/${path.basename(originalWebpPath)}`; + + uploadedImages.push({ + originalName: file.originalname, + images: optimizedImages, + metadata: { + mimeType: file.mimetype, + size: file.size + } + }); + + // Remove original file + await fs.unlink(originalPath); + } catch (fileError) { + console.error(`Error processing file ${file.originalname}:`, fileError); + // Clean up this file and continue with others + try { + await fs.unlink(file.path); + } catch (unlinkError) { + console.error('Error removing file:', unlinkError); + } + } + } + + res.json({ + success: true, + message: `${uploadedImages.length} images uploaded and optimized successfully`, + images: uploadedImages + }); + } catch (error) { + console.error('Multiple images upload error:', error); + + // Clean up files on error + if (req.files) { + for (const file of req.files) { + try { + await fs.unlink(file.path); + } catch (unlinkError) { + console.error('Error removing file:', unlinkError); + } + } + } + + res.status(500).json({ + success: false, + message: 'Error uploading images' + }); + } +}); + +// Delete image +router.delete('/:filename', requireAuth, async (req, res) => { + try { + const filename = req.params.filename; + const uploadPath = path.join(__dirname, '../public/uploads'); + + // Security check - ensure filename doesn't contain path traversal + if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) { + return res.status(400).json({ + success: false, + message: 'Invalid filename' + }); + } + + const filePath = path.join(uploadPath, filename); + + try { + await fs.access(filePath); + await fs.unlink(filePath); + + res.json({ + success: true, + message: 'Image deleted successfully' + }); + } catch (error) { + if (error.code === 'ENOENT') { + return res.status(404).json({ + success: false, + message: 'Image not found' + }); + } + throw error; + } + } catch (error) { + console.error('Image deletion error:', error); + res.status(500).json({ + success: false, + message: 'Error deleting image' + }); + } +}); + +// List uploaded images with advanced filtering and search +router.get('/list', requireAuth, async (req, res) => { + try { + const uploadPath = path.join(__dirname, '../public/uploads'); + + // Create uploads directory if it doesn't exist + try { + await fs.mkdir(uploadPath, { recursive: true }); + } catch (mkdirError) { + // Directory might already exist, continue + } + + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 24; + const search = req.query.search?.toLowerCase() || ''; + const sortBy = req.query.sortBy || 'date'; // date, name, size + const sortOrder = req.query.sortOrder || 'desc'; // asc, desc + const fileType = req.query.fileType || 'all'; // all, image, video, document + + let files; + try { + files = await fs.readdir(uploadPath); + } catch (readdirError) { + if (readdirError.code === 'ENOENT') { + return res.json({ + success: true, + images: [], + pagination: { + current: 1, + total: 0, + limit, + totalItems: 0, + hasNext: false, + hasPrev: false + }, + filters: { + search: '', + sortBy: 'date', + sortOrder: 'desc', + fileType: 'all' + } + }); + } + throw readdirError; + } + + // Filter by file type + let filteredFiles = files; + if (fileType !== 'all') { + const typePatterns = { + image: /\.(jpg|jpeg|png|gif|webp|svg|bmp|tiff?)$/i, + video: /\.(mp4|webm|avi|mov|mkv|wmv|flv)$/i, + document: /\.(pdf|doc|docx|txt|rtf|odt)$/i + }; + + const pattern = typePatterns[fileType]; + if (pattern) { + filteredFiles = files.filter(file => pattern.test(file)); + } + } else { + // Only show supported media files + filteredFiles = files.filter(file => + /\.(jpg|jpeg|png|gif|webp|svg|bmp|tiff?|mp4|webm|avi|mov|mkv|pdf|doc|docx)$/i.test(file) + ); + } + + // Apply search filter + if (search) { + filteredFiles = filteredFiles.filter(file => + file.toLowerCase().includes(search) + ); + } + + // Get file stats and create file objects + const filesWithStats = await Promise.all( + filteredFiles.map(async (file) => { + try { + const filePath = path.join(uploadPath, file); + const stats = await fs.stat(filePath); + return { file, stats, filePath }; + } catch (error) { + return null; + } + }) + ); + + const validFiles = filesWithStats.filter(item => item !== null); + + // Sort files + validFiles.sort((a, b) => { + let aValue, bValue; + + switch (sortBy) { + case 'name': + aValue = a.file.toLowerCase(); + bValue = b.file.toLowerCase(); + break; + case 'size': + aValue = a.stats.size; + bValue = b.stats.size; + break; + case 'date': + default: + aValue = a.stats.mtime; + bValue = b.stats.mtime; + break; + } + + if (sortOrder === 'asc') { + return aValue > bValue ? 1 : -1; + } else { + return aValue < bValue ? 1 : -1; + } + }); + + const total = validFiles.length; + const totalPages = Math.ceil(total / limit); + const start = (page - 1) * limit; + const end = start + limit; + + const paginatedFiles = validFiles.slice(start, end); + + const filesWithDetails = await Promise.all( + paginatedFiles.map(async ({ file, stats, filePath }) => { + try { + const ext = path.extname(file).toLowerCase(); + let fileDetails = { + filename: file, + url: `/uploads/${file}`, + size: stats.size, + uploadedAt: stats.birthtime || stats.mtime, + modifiedAt: stats.mtime, + extension: ext, + isImage: false, + isVideo: false, + isDocument: false + }; + + // Determine file type and get additional info + if (/\.(jpg|jpeg|png|gif|webp|svg|bmp|tiff?)$/i.test(file)) { + fileDetails.isImage = true; + fileDetails.mimetype = `image/${ext.replace('.', '')}`; + + // Get image dimensions + try { + const metadata = await sharp(filePath).metadata(); + fileDetails.dimensions = { + width: metadata.width, + height: metadata.height + }; + fileDetails.format = metadata.format; + } catch (sharpError) { + console.warn(`Could not get image metadata for ${file}`); + } + } else if (/\.(mp4|webm|avi|mov|mkv|wmv|flv)$/i.test(file)) { + fileDetails.isVideo = true; + fileDetails.mimetype = `video/${ext.replace('.', '')}`; + } else if (/\.(pdf|doc|docx|txt|rtf|odt)$/i.test(file)) { + fileDetails.isDocument = true; + fileDetails.mimetype = `application/${ext.replace('.', '')}`; + } + + // Generate thumbnail for images + if (fileDetails.isImage && !file.includes('-thumbnail.')) { + const thumbnailPath = path.join(uploadPath, `${path.parse(file).name}-thumbnail.webp`); + try { + await fs.access(thumbnailPath); + fileDetails.thumbnail = `/uploads/${path.basename(thumbnailPath)}`; + } catch { + // Thumbnail doesn't exist, create it + try { + await sharp(filePath) + .resize(200, 150, { + fit: 'cover', + withoutEnlargement: false + }) + .webp({ quality: 80 }) + .toFile(thumbnailPath); + fileDetails.thumbnail = `/uploads/${path.basename(thumbnailPath)}`; + } catch (thumbError) { + console.warn(`Could not create thumbnail for ${file}`); + } + } + } + + return fileDetails; + } catch (error) { + console.error(`Error getting details for ${file}:`, error); + return null; + } + }) + ); + + const validMedia = filesWithDetails.filter(item => item !== null); + + // Calculate storage stats + const totalSize = validFiles.reduce((sum, file) => sum + file.stats.size, 0); + const imageCount = validMedia.filter(f => f.isImage).length; + const videoCount = validMedia.filter(f => f.isVideo).length; + const documentCount = validMedia.filter(f => f.isDocument).length; + + res.json({ + success: true, + files: validMedia, + pagination: { + current: page, + total: totalPages, + limit, + totalItems: total, + hasNext: page < totalPages, + hasPrev: page > 1 + }, + filters: { + search, + sortBy, + sortOrder, + fileType + }, + stats: { + totalFiles: total, + totalSize, + imageCount, + videoCount, + documentCount, + formattedSize: formatFileSize(totalSize) + } + }); + } catch (error) { + console.error('List media error:', error); + res.status(500).json({ + success: false, + message: 'Error listing media files' + }); + } +}); + +// Create folder structure +router.post('/folder', requireAuth, async (req, res) => { + try { + const { folderName } = req.body; + + if (!folderName || !folderName.trim()) { + return res.status(400).json({ + success: false, + message: 'Folder name is required' + }); + } + + // Sanitize folder name + const sanitizedName = folderName.trim().replace(/[^a-zA-Z0-9-_]/g, '-'); + const folderPath = path.join(__dirname, '../public/uploads', sanitizedName); + + try { + await fs.mkdir(folderPath, { recursive: true }); + + res.json({ + success: true, + message: 'Folder created successfully', + folderName: sanitizedName, + folderPath: `/uploads/${sanitizedName}` + }); + } catch (error) { + if (error.code === 'EEXIST') { + return res.status(400).json({ + success: false, + message: 'Folder already exists' + }); + } + throw error; + } + } catch (error) { + console.error('Create folder error:', error); + res.status(500).json({ + success: false, + message: 'Error creating folder' + }); + } +}); + +// Get media file info +router.get('/info/:filename', requireAuth, async (req, res) => { + try { + const filename = req.params.filename; + + // Security check + if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) { + return res.status(400).json({ + success: false, + message: 'Invalid filename' + }); + } + + const filePath = path.join(__dirname, '../public/uploads', filename); + + try { + const stats = await fs.stat(filePath); + const ext = path.extname(filename).toLowerCase(); + + let fileInfo = { + filename, + url: `/uploads/${filename}`, + size: stats.size, + formattedSize: formatFileSize(stats.size), + uploadedAt: stats.birthtime || stats.mtime, + modifiedAt: stats.mtime, + extension: ext, + mimetype: getMimeType(ext) + }; + + // Get additional info for images + if (/\.(jpg|jpeg|png|gif|webp|svg|bmp|tiff?)$/i.test(filename)) { + try { + const metadata = await sharp(filePath).metadata(); + fileInfo.dimensions = { + width: metadata.width, + height: metadata.height + }; + fileInfo.format = metadata.format; + fileInfo.hasAlpha = metadata.hasAlpha; + fileInfo.density = metadata.density; + } catch (sharpError) { + console.warn(`Could not get image metadata for ${filename}`); + } + } + + res.json({ + success: true, + fileInfo + }); + } catch (error) { + if (error.code === 'ENOENT') { + return res.status(404).json({ + success: false, + message: 'File not found' + }); + } + throw error; + } + } catch (error) { + console.error('Get file info error:', error); + res.status(500).json({ + success: false, + message: 'Error getting file information' + }); + } +}); + +// Resize image +router.post('/resize/:filename', requireAuth, async (req, res) => { + try { + const filename = req.params.filename; + const { width, height, quality = 85 } = req.body; + + if (!width && !height) { + return res.status(400).json({ + success: false, + message: 'Width or height must be specified' + }); + } + + // Security check + if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) { + return res.status(400).json({ + success: false, + message: 'Invalid filename' + }); + } + + const originalPath = path.join(__dirname, '../public/uploads', filename); + const nameWithoutExt = path.parse(filename).name; + const resizedPath = path.join( + path.dirname(originalPath), + `${nameWithoutExt}-${width || 'auto'}x${height || 'auto'}.webp` + ); + + try { + let sharpInstance = sharp(originalPath); + + if (width && height) { + sharpInstance = sharpInstance.resize(parseInt(width), parseInt(height), { + fit: 'cover' + }); + } else if (width) { + sharpInstance = sharpInstance.resize(parseInt(width)); + } else { + sharpInstance = sharpInstance.resize(null, parseInt(height)); + } + + await sharpInstance + .webp({ quality: parseInt(quality) }) + .toFile(resizedPath); + + res.json({ + success: true, + message: 'Image resized successfully', + originalFile: filename, + resizedFile: path.basename(resizedPath), + resizedUrl: `/uploads/${path.basename(resizedPath)}` + }); + } catch (error) { + if (error.code === 'ENOENT') { + return res.status(404).json({ + success: false, + message: 'Original file not found' + }); + } + throw error; + } + } catch (error) { + console.error('Resize image error:', error); + res.status(500).json({ + success: false, + message: 'Error resizing image' + }); + } +}); + +// Utility functions +function formatFileSize(bytes) { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; +} + +function getMimeType(ext) { + const mimeTypes = { + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.png': 'image/png', + '.gif': 'image/gif', + '.webp': 'image/webp', + '.svg': 'image/svg+xml', + '.bmp': 'image/bmp', + '.tiff': 'image/tiff', + '.tif': 'image/tiff', + '.mp4': 'video/mp4', + '.webm': 'video/webm', + '.avi': 'video/x-msvideo', + '.mov': 'video/quicktime', + '.mkv': 'video/x-matroska', + '.pdf': 'application/pdf', + '.doc': 'application/msword', + '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' + }; + + return mimeTypes[ext.toLowerCase()] || 'application/octet-stream'; +} + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/portfolio_20251019203104.js b/.history/routes/portfolio_20251019203104.js new file mode 100644 index 0000000..c86551b --- /dev/null +++ b/.history/routes/portfolio_20251019203104.js @@ -0,0 +1,197 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio } = require('../models'); +const { Op } = require('sequelize'); + +// Get all portfolio items +router.get('/', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 12; + const skip = (page - 1) * limit; + const category = req.query.category; + const search = req.query.search; + const featured = req.query.featured; + + // Build query + let query = { isPublished: true }; + + if (category && category !== 'all') { + query.category = category; + } + + if (featured === 'true') { + query.featured = true; + } + + if (search) { + query.$text = { $search: search }; + } + + // Get portfolio items + const [portfolio, total] = await Promise.all([ + Portfolio.find(query) + .sort({ featured: -1, publishedAt: -1 }) + .skip(skip) + .limit(limit) + .select('title shortDescription category technologies images status publishedAt viewCount'), + Portfolio.countDocuments(query) + ]); + + const totalPages = Math.ceil(total / limit); + + res.json({ + success: true, + portfolio, + pagination: { + current: page, + total: totalPages, + limit, + totalItems: total, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio' + }); + } +}); + +// Get single portfolio item +router.get('/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.find({ + _id: { $ne: portfolio._id }, + category: portfolio.category, + isPublished: true + }) + .select('title shortDescription images') + .limit(4); + + res.json({ + success: true, + portfolio, + relatedProjects + }); + } catch (error) { + console.error('Portfolio detail API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio item' + }); + } +}); + +// Get portfolio categories +router.get('/meta/categories', async (req, res) => { + try { + const categories = await Portfolio.distinct('category', { isPublished: true }); + + // Get count for each category + const categoriesWithCount = await Promise.all( + categories.map(async (category) => { + const count = await Portfolio.countDocuments({ + category, + isPublished: true + }); + return { category, count }; + }) + ); + + res.json({ + success: true, + categories: categoriesWithCount + }); + } catch (error) { + console.error('Portfolio categories API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching categories' + }); + } +}); + +// Like portfolio item +router.post('/:id/like', async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + portfolio.likes += 1; + await portfolio.save(); + + res.json({ + success: true, + likes: portfolio.likes + }); + } catch (error) { + console.error('Portfolio like API error:', error); + res.status(500).json({ + success: false, + message: 'Error liking portfolio item' + }); + } +}); + +// Search portfolio +router.get('/search/:term', async (req, res) => { + try { + const searchTerm = req.params.term; + const limit = parseInt(req.query.limit) || 10; + + const portfolio = await Portfolio.find({ + $and: [ + { isPublished: true }, + { + $or: [ + { title: { $regex: searchTerm, $options: 'i' } }, + { description: { $regex: searchTerm, $options: 'i' } }, + { technologies: { $in: [new RegExp(searchTerm, 'i')] } } + ] + } + ] + }) + .select('title shortDescription category images') + .sort({ featured: -1, publishedAt: -1 }) + .limit(limit); + + res.json({ + success: true, + portfolio, + searchTerm, + count: portfolio.length + }); + } catch (error) { + console.error('Portfolio search API error:', error); + res.status(500).json({ + success: false, + message: 'Error searching portfolio' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/portfolio_20251019203147.js b/.history/routes/portfolio_20251019203147.js new file mode 100644 index 0000000..c86551b --- /dev/null +++ b/.history/routes/portfolio_20251019203147.js @@ -0,0 +1,197 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio } = require('../models'); +const { Op } = require('sequelize'); + +// Get all portfolio items +router.get('/', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 12; + const skip = (page - 1) * limit; + const category = req.query.category; + const search = req.query.search; + const featured = req.query.featured; + + // Build query + let query = { isPublished: true }; + + if (category && category !== 'all') { + query.category = category; + } + + if (featured === 'true') { + query.featured = true; + } + + if (search) { + query.$text = { $search: search }; + } + + // Get portfolio items + const [portfolio, total] = await Promise.all([ + Portfolio.find(query) + .sort({ featured: -1, publishedAt: -1 }) + .skip(skip) + .limit(limit) + .select('title shortDescription category technologies images status publishedAt viewCount'), + Portfolio.countDocuments(query) + ]); + + const totalPages = Math.ceil(total / limit); + + res.json({ + success: true, + portfolio, + pagination: { + current: page, + total: totalPages, + limit, + totalItems: total, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio' + }); + } +}); + +// Get single portfolio item +router.get('/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.find({ + _id: { $ne: portfolio._id }, + category: portfolio.category, + isPublished: true + }) + .select('title shortDescription images') + .limit(4); + + res.json({ + success: true, + portfolio, + relatedProjects + }); + } catch (error) { + console.error('Portfolio detail API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio item' + }); + } +}); + +// Get portfolio categories +router.get('/meta/categories', async (req, res) => { + try { + const categories = await Portfolio.distinct('category', { isPublished: true }); + + // Get count for each category + const categoriesWithCount = await Promise.all( + categories.map(async (category) => { + const count = await Portfolio.countDocuments({ + category, + isPublished: true + }); + return { category, count }; + }) + ); + + res.json({ + success: true, + categories: categoriesWithCount + }); + } catch (error) { + console.error('Portfolio categories API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching categories' + }); + } +}); + +// Like portfolio item +router.post('/:id/like', async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + portfolio.likes += 1; + await portfolio.save(); + + res.json({ + success: true, + likes: portfolio.likes + }); + } catch (error) { + console.error('Portfolio like API error:', error); + res.status(500).json({ + success: false, + message: 'Error liking portfolio item' + }); + } +}); + +// Search portfolio +router.get('/search/:term', async (req, res) => { + try { + const searchTerm = req.params.term; + const limit = parseInt(req.query.limit) || 10; + + const portfolio = await Portfolio.find({ + $and: [ + { isPublished: true }, + { + $or: [ + { title: { $regex: searchTerm, $options: 'i' } }, + { description: { $regex: searchTerm, $options: 'i' } }, + { technologies: { $in: [new RegExp(searchTerm, 'i')] } } + ] + } + ] + }) + .select('title shortDescription category images') + .sort({ featured: -1, publishedAt: -1 }) + .limit(limit); + + res.json({ + success: true, + portfolio, + searchTerm, + count: portfolio.length + }); + } catch (error) { + console.error('Portfolio search API error:', error); + res.status(500).json({ + success: false, + message: 'Error searching portfolio' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/portfolio_20251019204338.js b/.history/routes/portfolio_20251019204338.js new file mode 100644 index 0000000..8000d4d --- /dev/null +++ b/.history/routes/portfolio_20251019204338.js @@ -0,0 +1,206 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio } = require('../models'); +const { Op } = require('sequelize'); + +// Get all portfolio items +router.get('/', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 12; + const skip = (page - 1) * limit; + const category = req.query.category; + const search = req.query.search; + const featured = req.query.featured; + + // Build query + let whereClause = { isPublished: true }; + + if (category && category !== 'all') { + whereClause.category = category; + } + + if (featured === 'true') { + whereClause.featured = true; + } + + if (search) { + whereClause = { + ...whereClause, + [Op.or]: [ + { title: { [Op.iLike]: `%${search}%` } }, + { description: { [Op.iLike]: `%${search}%` } }, + { shortDescription: { [Op.iLike]: `%${search}%` } } + ] + }; + } + + // Get portfolio items + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + where: whereClause, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit, + attributes: ['id', 'title', 'shortDescription', 'category', 'technologies', 'images', 'status', 'publishedAt', 'viewCount'] + }), + Portfolio.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.json({ + success: true, + portfolio, + pagination: { + current: page, + total: totalPages, + limit, + totalItems: total, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio' + }); + } +}); + +// Get single portfolio item +router.get('/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.find({ + _id: { $ne: portfolio._id }, + category: portfolio.category, + isPublished: true + }) + .select('title shortDescription images') + .limit(4); + + res.json({ + success: true, + portfolio, + relatedProjects + }); + } catch (error) { + console.error('Portfolio detail API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio item' + }); + } +}); + +// Get portfolio categories +router.get('/meta/categories', async (req, res) => { + try { + const categories = await Portfolio.distinct('category', { isPublished: true }); + + // Get count for each category + const categoriesWithCount = await Promise.all( + categories.map(async (category) => { + const count = await Portfolio.countDocuments({ + category, + isPublished: true + }); + return { category, count }; + }) + ); + + res.json({ + success: true, + categories: categoriesWithCount + }); + } catch (error) { + console.error('Portfolio categories API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching categories' + }); + } +}); + +// Like portfolio item +router.post('/:id/like', async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + portfolio.likes += 1; + await portfolio.save(); + + res.json({ + success: true, + likes: portfolio.likes + }); + } catch (error) { + console.error('Portfolio like API error:', error); + res.status(500).json({ + success: false, + message: 'Error liking portfolio item' + }); + } +}); + +// Search portfolio +router.get('/search/:term', async (req, res) => { + try { + const searchTerm = req.params.term; + const limit = parseInt(req.query.limit) || 10; + + const portfolio = await Portfolio.find({ + $and: [ + { isPublished: true }, + { + $or: [ + { title: { $regex: searchTerm, $options: 'i' } }, + { description: { $regex: searchTerm, $options: 'i' } }, + { technologies: { $in: [new RegExp(searchTerm, 'i')] } } + ] + } + ] + }) + .select('title shortDescription category images') + .sort({ featured: -1, publishedAt: -1 }) + .limit(limit); + + res.json({ + success: true, + portfolio, + searchTerm, + count: portfolio.length + }); + } catch (error) { + console.error('Portfolio search API error:', error); + res.status(500).json({ + success: false, + message: 'Error searching portfolio' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/portfolio_20251019204352.js b/.history/routes/portfolio_20251019204352.js new file mode 100644 index 0000000..44dba44 --- /dev/null +++ b/.history/routes/portfolio_20251019204352.js @@ -0,0 +1,206 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio } = require('../models'); +const { Op } = require('sequelize'); + +// Get all portfolio items +router.get('/', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 12; + const skip = (page - 1) * limit; + const category = req.query.category; + const search = req.query.search; + const featured = req.query.featured; + + // Build query + let whereClause = { isPublished: true }; + + if (category && category !== 'all') { + whereClause.category = category; + } + + if (featured === 'true') { + whereClause.featured = true; + } + + if (search) { + whereClause = { + ...whereClause, + [Op.or]: [ + { title: { [Op.iLike]: `%${search}%` } }, + { description: { [Op.iLike]: `%${search}%` } }, + { shortDescription: { [Op.iLike]: `%${search}%` } } + ] + }; + } + + // Get portfolio items + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + where: whereClause, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit, + attributes: ['id', 'title', 'shortDescription', 'category', 'technologies', 'images', 'status', 'publishedAt', 'viewCount'] + }), + Portfolio.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.json({ + success: true, + portfolio, + pagination: { + current: page, + total: totalPages, + limit, + totalItems: total, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio' + }); + } +}); + +// Get single portfolio item +router.get('/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.find({ + _id: { $ne: portfolio._id }, + category: portfolio.category, + isPublished: true + }) + .select('title shortDescription images') + .limit(4); + + res.json({ + success: true, + portfolio, + relatedProjects + }); + } catch (error) { + console.error('Portfolio detail API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio item' + }); + } +}); + +// Get portfolio categories +router.get('/meta/categories', async (req, res) => { + try { + const categories = await Portfolio.distinct('category', { isPublished: true }); + + // Get count for each category + const categoriesWithCount = await Promise.all( + categories.map(async (category) => { + const count = await Portfolio.countDocuments({ + category, + isPublished: true + }); + return { category, count }; + }) + ); + + res.json({ + success: true, + categories: categoriesWithCount + }); + } catch (error) { + console.error('Portfolio categories API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching categories' + }); + } +}); + +// Like portfolio item +router.post('/:id/like', async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + portfolio.likes += 1; + await portfolio.save(); + + res.json({ + success: true, + likes: portfolio.likes + }); + } catch (error) { + console.error('Portfolio like API error:', error); + res.status(500).json({ + success: false, + message: 'Error liking portfolio item' + }); + } +}); + +// Search portfolio +router.get('/search/:term', async (req, res) => { + try { + const searchTerm = req.params.term; + const limit = parseInt(req.query.limit) || 10; + + const portfolio = await Portfolio.find({ + $and: [ + { isPublished: true }, + { + $or: [ + { title: { $regex: searchTerm, $options: 'i' } }, + { description: { $regex: searchTerm, $options: 'i' } }, + { technologies: { $in: [new RegExp(searchTerm, 'i')] } } + ] + } + ] + }) + .select('title shortDescription category images') + .sort({ featured: -1, publishedAt: -1 }) + .limit(limit); + + res.json({ + success: true, + portfolio, + searchTerm, + count: portfolio.length + }); + } catch (error) { + console.error('Portfolio search API error:', error); + res.status(500).json({ + success: false, + message: 'Error searching portfolio' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/portfolio_20251019204403.js b/.history/routes/portfolio_20251019204403.js new file mode 100644 index 0000000..cfde0d6 --- /dev/null +++ b/.history/routes/portfolio_20251019204403.js @@ -0,0 +1,208 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio } = require('../models'); +const { Op } = require('sequelize'); + +// Get all portfolio items +router.get('/', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 12; + const skip = (page - 1) * limit; + const category = req.query.category; + const search = req.query.search; + const featured = req.query.featured; + + // Build query + let whereClause = { isPublished: true }; + + if (category && category !== 'all') { + whereClause.category = category; + } + + if (featured === 'true') { + whereClause.featured = true; + } + + if (search) { + whereClause = { + ...whereClause, + [Op.or]: [ + { title: { [Op.iLike]: `%${search}%` } }, + { description: { [Op.iLike]: `%${search}%` } }, + { shortDescription: { [Op.iLike]: `%${search}%` } } + ] + }; + } + + // Get portfolio items + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + where: whereClause, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit, + attributes: ['id', 'title', 'shortDescription', 'category', 'technologies', 'images', 'status', 'publishedAt', 'viewCount'] + }), + Portfolio.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.json({ + success: true, + portfolio, + pagination: { + current: page, + total: totalPages, + limit, + totalItems: total, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio' + }); + } +}); + +// Get single portfolio item +router.get('/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + attributes: ['id', 'title', 'shortDescription', 'images'], + limit: 3 + }); + + res.json({ + success: true, + portfolio, + relatedProjects + }); + } catch (error) { + console.error('Portfolio detail API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio item' + }); + } +}); + +// Get portfolio categories +router.get('/meta/categories', async (req, res) => { + try { + const categories = await Portfolio.distinct('category', { isPublished: true }); + + // Get count for each category + const categoriesWithCount = await Promise.all( + categories.map(async (category) => { + const count = await Portfolio.countDocuments({ + category, + isPublished: true + }); + return { category, count }; + }) + ); + + res.json({ + success: true, + categories: categoriesWithCount + }); + } catch (error) { + console.error('Portfolio categories API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching categories' + }); + } +}); + +// Like portfolio item +router.post('/:id/like', async (req, res) => { + try { + const portfolio = await Portfolio.findById(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + portfolio.likes += 1; + await portfolio.save(); + + res.json({ + success: true, + likes: portfolio.likes + }); + } catch (error) { + console.error('Portfolio like API error:', error); + res.status(500).json({ + success: false, + message: 'Error liking portfolio item' + }); + } +}); + +// Search portfolio +router.get('/search/:term', async (req, res) => { + try { + const searchTerm = req.params.term; + const limit = parseInt(req.query.limit) || 10; + + const portfolio = await Portfolio.find({ + $and: [ + { isPublished: true }, + { + $or: [ + { title: { $regex: searchTerm, $options: 'i' } }, + { description: { $regex: searchTerm, $options: 'i' } }, + { technologies: { $in: [new RegExp(searchTerm, 'i')] } } + ] + } + ] + }) + .select('title shortDescription category images') + .sort({ featured: -1, publishedAt: -1 }) + .limit(limit); + + res.json({ + success: true, + portfolio, + searchTerm, + count: portfolio.length + }); + } catch (error) { + console.error('Portfolio search API error:', error); + res.status(500).json({ + success: false, + message: 'Error searching portfolio' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/portfolio_20251019204418.js b/.history/routes/portfolio_20251019204418.js new file mode 100644 index 0000000..504cfa4 --- /dev/null +++ b/.history/routes/portfolio_20251019204418.js @@ -0,0 +1,208 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio } = require('../models'); +const { Op } = require('sequelize'); + +// Get all portfolio items +router.get('/', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 12; + const skip = (page - 1) * limit; + const category = req.query.category; + const search = req.query.search; + const featured = req.query.featured; + + // Build query + let whereClause = { isPublished: true }; + + if (category && category !== 'all') { + whereClause.category = category; + } + + if (featured === 'true') { + whereClause.featured = true; + } + + if (search) { + whereClause = { + ...whereClause, + [Op.or]: [ + { title: { [Op.iLike]: `%${search}%` } }, + { description: { [Op.iLike]: `%${search}%` } }, + { shortDescription: { [Op.iLike]: `%${search}%` } } + ] + }; + } + + // Get portfolio items + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + where: whereClause, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit, + attributes: ['id', 'title', 'shortDescription', 'category', 'technologies', 'images', 'status', 'publishedAt', 'viewCount'] + }), + Portfolio.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.json({ + success: true, + portfolio, + pagination: { + current: page, + total: totalPages, + limit, + totalItems: total, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio' + }); + } +}); + +// Get single portfolio item +router.get('/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + attributes: ['id', 'title', 'shortDescription', 'images'], + limit: 3 + }); + + res.json({ + success: true, + portfolio, + relatedProjects + }); + } catch (error) { + console.error('Portfolio detail API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio item' + }); + } +}); + +// Get portfolio categories +router.get('/meta/categories', async (req, res) => { + try { + const categories = await Portfolio.distinct('category', { isPublished: true }); + + // Get count for each category + const categoriesWithCount = await Promise.all( + categories.map(async (category) => { + const count = await Portfolio.countDocuments({ + category, + isPublished: true + }); + return { category, count }; + }) + ); + + res.json({ + success: true, + categories: categoriesWithCount + }); + } catch (error) { + console.error('Portfolio categories API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching categories' + }); + } +}); + +// Like portfolio item +router.post('/:id/like', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + portfolio.likes += 1; + await portfolio.save(); + + res.json({ + success: true, + likes: portfolio.likes + }); + } catch (error) { + console.error('Portfolio like API error:', error); + res.status(500).json({ + success: false, + message: 'Error liking portfolio item' + }); + } +}); + +// Search portfolio +router.get('/search/:term', async (req, res) => { + try { + const searchTerm = req.params.term; + const limit = parseInt(req.query.limit) || 10; + + const portfolio = await Portfolio.find({ + $and: [ + { isPublished: true }, + { + $or: [ + { title: { $regex: searchTerm, $options: 'i' } }, + { description: { $regex: searchTerm, $options: 'i' } }, + { technologies: { $in: [new RegExp(searchTerm, 'i')] } } + ] + } + ] + }) + .select('title shortDescription category images') + .sort({ featured: -1, publishedAt: -1 }) + .limit(limit); + + res.json({ + success: true, + portfolio, + searchTerm, + count: portfolio.length + }); + } catch (error) { + console.error('Portfolio search API error:', error); + res.status(500).json({ + success: false, + message: 'Error searching portfolio' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/portfolio_20251019204435.js b/.history/routes/portfolio_20251019204435.js new file mode 100644 index 0000000..7c637ae --- /dev/null +++ b/.history/routes/portfolio_20251019204435.js @@ -0,0 +1,209 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio } = require('../models'); +const { Op } = require('sequelize'); + +// Get all portfolio items +router.get('/', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 12; + const skip = (page - 1) * limit; + const category = req.query.category; + const search = req.query.search; + const featured = req.query.featured; + + // Build query + let whereClause = { isPublished: true }; + + if (category && category !== 'all') { + whereClause.category = category; + } + + if (featured === 'true') { + whereClause.featured = true; + } + + if (search) { + whereClause = { + ...whereClause, + [Op.or]: [ + { title: { [Op.iLike]: `%${search}%` } }, + { description: { [Op.iLike]: `%${search}%` } }, + { shortDescription: { [Op.iLike]: `%${search}%` } } + ] + }; + } + + // Get portfolio items + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + where: whereClause, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit, + attributes: ['id', 'title', 'shortDescription', 'category', 'technologies', 'images', 'status', 'publishedAt', 'viewCount'] + }), + Portfolio.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.json({ + success: true, + portfolio, + pagination: { + current: page, + total: totalPages, + limit, + totalItems: total, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio' + }); + } +}); + +// Get single portfolio item +router.get('/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + attributes: ['id', 'title', 'shortDescription', 'images'], + limit: 3 + }); + + res.json({ + success: true, + portfolio, + relatedProjects + }); + } catch (error) { + console.error('Portfolio detail API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio item' + }); + } +}); + +// Get portfolio categories +router.get('/meta/categories', async (req, res) => { + try { + const categories = await Portfolio.distinct('category', { isPublished: true }); + + // Get count for each category + const categoriesWithCount = await Promise.all( + categories.map(async (category) => { + const count = await Portfolio.countDocuments({ + category, + isPublished: true + }); + return { category, count }; + }) + ); + + res.json({ + success: true, + categories: categoriesWithCount + }); + } catch (error) { + console.error('Portfolio categories API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching categories' + }); + } +}); + +// Like portfolio item +router.post('/:id/like', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + portfolio.likes += 1; + await portfolio.save(); + + res.json({ + success: true, + likes: portfolio.likes + }); + } catch (error) { + console.error('Portfolio like API error:', error); + res.status(500).json({ + success: false, + message: 'Error liking portfolio item' + }); + } +}); + +// Search portfolio +router.get('/search/:term', async (req, res) => { + try { + const searchTerm = req.params.term; + const limit = parseInt(req.query.limit) || 10; + + const portfolio = await Portfolio.findAll({ + where: { + [Op.and]: [ + { isPublished: true }, + { + [Op.or]: [ + { title: { [Op.iLike]: `%${searchTerm}%` } }, + { description: { [Op.iLike]: `%${searchTerm}%` } }, + { technologies: { [Op.contains]: [searchTerm] } } + ] + } + ] + }, + attributes: ['id', 'title', 'shortDescription', 'images', 'category'], + limit: limit + }); + + res.json({ + success: true, + portfolio, + searchTerm, + count: portfolio.length + }); + } catch (error) { + console.error('Portfolio search API error:', error); + res.status(500).json({ + success: false, + message: 'Error searching portfolio' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/portfolio_20251019204805.js b/.history/routes/portfolio_20251019204805.js new file mode 100644 index 0000000..7c637ae --- /dev/null +++ b/.history/routes/portfolio_20251019204805.js @@ -0,0 +1,209 @@ +const express = require('express'); +const router = express.Router(); +const { Portfolio } = require('../models'); +const { Op } = require('sequelize'); + +// Get all portfolio items +router.get('/', async (req, res) => { + try { + const page = parseInt(req.query.page) || 1; + const limit = parseInt(req.query.limit) || 12; + const skip = (page - 1) * limit; + const category = req.query.category; + const search = req.query.search; + const featured = req.query.featured; + + // Build query + let whereClause = { isPublished: true }; + + if (category && category !== 'all') { + whereClause.category = category; + } + + if (featured === 'true') { + whereClause.featured = true; + } + + if (search) { + whereClause = { + ...whereClause, + [Op.or]: [ + { title: { [Op.iLike]: `%${search}%` } }, + { description: { [Op.iLike]: `%${search}%` } }, + { shortDescription: { [Op.iLike]: `%${search}%` } } + ] + }; + } + + // Get portfolio items + const [portfolio, total] = await Promise.all([ + Portfolio.findAll({ + where: whereClause, + order: [['featured', 'DESC'], ['publishedAt', 'DESC']], + offset: skip, + limit: limit, + attributes: ['id', 'title', 'shortDescription', 'category', 'technologies', 'images', 'status', 'publishedAt', 'viewCount'] + }), + Portfolio.count({ where: whereClause }) + ]); + + const totalPages = Math.ceil(total / limit); + + res.json({ + success: true, + portfolio, + pagination: { + current: page, + total: totalPages, + limit, + totalItems: total, + hasNext: page < totalPages, + hasPrev: page > 1 + } + }); + } catch (error) { + console.error('Portfolio API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio' + }); + } +}); + +// Get single portfolio item +router.get('/:id', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + // Increment view count + portfolio.viewCount += 1; + await portfolio.save(); + + // Get related projects + const relatedProjects = await Portfolio.findAll({ + where: { + id: { [Op.ne]: portfolio.id }, + category: portfolio.category, + isPublished: true + }, + attributes: ['id', 'title', 'shortDescription', 'images'], + limit: 3 + }); + + res.json({ + success: true, + portfolio, + relatedProjects + }); + } catch (error) { + console.error('Portfolio detail API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching portfolio item' + }); + } +}); + +// Get portfolio categories +router.get('/meta/categories', async (req, res) => { + try { + const categories = await Portfolio.distinct('category', { isPublished: true }); + + // Get count for each category + const categoriesWithCount = await Promise.all( + categories.map(async (category) => { + const count = await Portfolio.countDocuments({ + category, + isPublished: true + }); + return { category, count }; + }) + ); + + res.json({ + success: true, + categories: categoriesWithCount + }); + } catch (error) { + console.error('Portfolio categories API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching categories' + }); + } +}); + +// Like portfolio item +router.post('/:id/like', async (req, res) => { + try { + const portfolio = await Portfolio.findByPk(req.params.id); + + if (!portfolio || !portfolio.isPublished) { + return res.status(404).json({ + success: false, + message: 'Portfolio item not found' + }); + } + + portfolio.likes += 1; + await portfolio.save(); + + res.json({ + success: true, + likes: portfolio.likes + }); + } catch (error) { + console.error('Portfolio like API error:', error); + res.status(500).json({ + success: false, + message: 'Error liking portfolio item' + }); + } +}); + +// Search portfolio +router.get('/search/:term', async (req, res) => { + try { + const searchTerm = req.params.term; + const limit = parseInt(req.query.limit) || 10; + + const portfolio = await Portfolio.findAll({ + where: { + [Op.and]: [ + { isPublished: true }, + { + [Op.or]: [ + { title: { [Op.iLike]: `%${searchTerm}%` } }, + { description: { [Op.iLike]: `%${searchTerm}%` } }, + { technologies: { [Op.contains]: [searchTerm] } } + ] + } + ] + }, + attributes: ['id', 'title', 'shortDescription', 'images', 'category'], + limit: limit + }); + + res.json({ + success: true, + portfolio, + searchTerm, + count: portfolio.length + }); + } catch (error) { + console.error('Portfolio search API error:', error); + res.status(500).json({ + success: false, + message: 'Error searching portfolio' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/services_20251019204450.js b/.history/routes/services_20251019204450.js new file mode 100644 index 0000000..3e3221c --- /dev/null +++ b/.history/routes/services_20251019204450.js @@ -0,0 +1,141 @@ +const express = require('express'); +const router = express.Router(); +const { Service, Portfolio } = require('../models'); +const { Op } = require('sequelize'); + +// Get all services +router.get('/', async (req, res) => { + try { + const category = req.query.category; + const featured = req.query.featured; + + let query = { isActive: true }; + + if (category && category !== 'all') { + query.category = category; + } + + if (featured === 'true') { + query.featured = true; + } + + const services = await Service.find(query) + .populate('portfolio', 'title images') + .sort({ featured: -1, order: 1 }); + + res.json({ + success: true, + services + }); + } catch (error) { + console.error('Services API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching services' + }); + } +}); + +// Get single service +router.get('/:id', async (req, res) => { + try { + const service = await Service.findById(req.params.id) + .populate('portfolio', 'title shortDescription images category'); + + if (!service || !service.isActive) { + return res.status(404).json({ + success: false, + message: 'Service not found' + }); + } + + // Get related services + const relatedServices = await Service.find({ + _id: { $ne: service._id }, + category: service.category, + isActive: true + }) + .select('name shortDescription icon pricing') + .limit(3); + + res.json({ + success: true, + service, + relatedServices + }); + } catch (error) { + console.error('Service detail API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching service' + }); + } +}); + +// Get service categories +router.get('/meta/categories', async (req, res) => { + try { + const categories = await Service.distinct('category', { isActive: true }); + + // Get count for each category + const categoriesWithCount = await Promise.all( + categories.map(async (category) => { + const count = await Service.countDocuments({ + category, + isActive: true + }); + return { category, count }; + }) + ); + + res.json({ + success: true, + categories: categoriesWithCount + }); + } catch (error) { + console.error('Service categories API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching categories' + }); + } +}); + +// Search services +router.get('/search/:term', async (req, res) => { + try { + const searchTerm = req.params.term; + const limit = parseInt(req.query.limit) || 10; + + const services = await Service.find({ + $and: [ + { isActive: true }, + { + $or: [ + { name: { $regex: searchTerm, $options: 'i' } }, + { description: { $regex: searchTerm, $options: 'i' } }, + { tags: { $in: [new RegExp(searchTerm, 'i')] } } + ] + } + ] + }) + .select('name shortDescription icon pricing category') + .sort({ featured: -1, order: 1 }) + .limit(limit); + + res.json({ + success: true, + services, + searchTerm, + count: services.length + }); + } catch (error) { + console.error('Service search API error:', error); + res.status(500).json({ + success: false, + message: 'Error searching services' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/services_20251019204458.js b/.history/routes/services_20251019204458.js new file mode 100644 index 0000000..da8d2cc --- /dev/null +++ b/.history/routes/services_20251019204458.js @@ -0,0 +1,142 @@ +const express = require('express'); +const router = express.Router(); +const { Service, Portfolio } = require('../models'); +const { Op } = require('sequelize'); + +// Get all services +router.get('/', async (req, res) => { + try { + const category = req.query.category; + const featured = req.query.featured; + + let whereClause = { isActive: true }; + + if (category && category !== 'all') { + whereClause.category = category; + } + + if (featured === 'true') { + whereClause.featured = true; + } + + const services = await Service.findAll({ + where: whereClause, + order: [['featured', 'DESC'], ['order', 'ASC']] + }); + + res.json({ + success: true, + services + }); + } catch (error) { + console.error('Services API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching services' + }); + } +}); + +// Get single service +router.get('/:id', async (req, res) => { + try { + const service = await Service.findById(req.params.id) + .populate('portfolio', 'title shortDescription images category'); + + if (!service || !service.isActive) { + return res.status(404).json({ + success: false, + message: 'Service not found' + }); + } + + // Get related services + const relatedServices = await Service.find({ + _id: { $ne: service._id }, + category: service.category, + isActive: true + }) + .select('name shortDescription icon pricing') + .limit(3); + + res.json({ + success: true, + service, + relatedServices + }); + } catch (error) { + console.error('Service detail API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching service' + }); + } +}); + +// Get service categories +router.get('/meta/categories', async (req, res) => { + try { + const categories = await Service.distinct('category', { isActive: true }); + + // Get count for each category + const categoriesWithCount = await Promise.all( + categories.map(async (category) => { + const count = await Service.countDocuments({ + category, + isActive: true + }); + return { category, count }; + }) + ); + + res.json({ + success: true, + categories: categoriesWithCount + }); + } catch (error) { + console.error('Service categories API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching categories' + }); + } +}); + +// Search services +router.get('/search/:term', async (req, res) => { + try { + const searchTerm = req.params.term; + const limit = parseInt(req.query.limit) || 10; + + const services = await Service.find({ + $and: [ + { isActive: true }, + { + $or: [ + { name: { $regex: searchTerm, $options: 'i' } }, + { description: { $regex: searchTerm, $options: 'i' } }, + { tags: { $in: [new RegExp(searchTerm, 'i')] } } + ] + } + ] + }) + .select('name shortDescription icon pricing category') + .sort({ featured: -1, order: 1 }) + .limit(limit); + + res.json({ + success: true, + services, + searchTerm, + count: services.length + }); + } catch (error) { + console.error('Service search API error:', error); + res.status(500).json({ + success: false, + message: 'Error searching services' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/services_20251019204511.js b/.history/routes/services_20251019204511.js new file mode 100644 index 0000000..4e5bd09 --- /dev/null +++ b/.history/routes/services_20251019204511.js @@ -0,0 +1,143 @@ +const express = require('express'); +const router = express.Router(); +const { Service, Portfolio } = require('../models'); +const { Op } = require('sequelize'); + +// Get all services +router.get('/', async (req, res) => { + try { + const category = req.query.category; + const featured = req.query.featured; + + let whereClause = { isActive: true }; + + if (category && category !== 'all') { + whereClause.category = category; + } + + if (featured === 'true') { + whereClause.featured = true; + } + + const services = await Service.findAll({ + where: whereClause, + order: [['featured', 'DESC'], ['order', 'ASC']] + }); + + res.json({ + success: true, + services + }); + } catch (error) { + console.error('Services API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching services' + }); + } +}); + +// Get single service +router.get('/:id', async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service || !service.isActive) { + return res.status(404).json({ + success: false, + message: 'Service not found' + }); + } + + // Get related services + const relatedServices = await Service.findAll({ + where: { + id: { [Op.ne]: service.id }, + category: service.category, + isActive: true + }, + limit: 3 + }); + .limit(3); + + res.json({ + success: true, + service, + relatedServices + }); + } catch (error) { + console.error('Service detail API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching service' + }); + } +}); + +// Get service categories +router.get('/meta/categories', async (req, res) => { + try { + const categories = await Service.distinct('category', { isActive: true }); + + // Get count for each category + const categoriesWithCount = await Promise.all( + categories.map(async (category) => { + const count = await Service.countDocuments({ + category, + isActive: true + }); + return { category, count }; + }) + ); + + res.json({ + success: true, + categories: categoriesWithCount + }); + } catch (error) { + console.error('Service categories API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching categories' + }); + } +}); + +// Search services +router.get('/search/:term', async (req, res) => { + try { + const searchTerm = req.params.term; + const limit = parseInt(req.query.limit) || 10; + + const services = await Service.find({ + $and: [ + { isActive: true }, + { + $or: [ + { name: { $regex: searchTerm, $options: 'i' } }, + { description: { $regex: searchTerm, $options: 'i' } }, + { tags: { $in: [new RegExp(searchTerm, 'i')] } } + ] + } + ] + }) + .select('name shortDescription icon pricing category') + .sort({ featured: -1, order: 1 }) + .limit(limit); + + res.json({ + success: true, + services, + searchTerm, + count: services.length + }); + } catch (error) { + console.error('Service search API error:', error); + res.status(500).json({ + success: false, + message: 'Error searching services' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/services_20251019204521.js b/.history/routes/services_20251019204521.js new file mode 100644 index 0000000..441b1d2 --- /dev/null +++ b/.history/routes/services_20251019204521.js @@ -0,0 +1,142 @@ +const express = require('express'); +const router = express.Router(); +const { Service, Portfolio } = require('../models'); +const { Op } = require('sequelize'); + +// Get all services +router.get('/', async (req, res) => { + try { + const category = req.query.category; + const featured = req.query.featured; + + let whereClause = { isActive: true }; + + if (category && category !== 'all') { + whereClause.category = category; + } + + if (featured === 'true') { + whereClause.featured = true; + } + + const services = await Service.findAll({ + where: whereClause, + order: [['featured', 'DESC'], ['order', 'ASC']] + }); + + res.json({ + success: true, + services + }); + } catch (error) { + console.error('Services API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching services' + }); + } +}); + +// Get single service +router.get('/:id', async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service || !service.isActive) { + return res.status(404).json({ + success: false, + message: 'Service not found' + }); + } + + // Get related services + const relatedServices = await Service.findAll({ + where: { + id: { [Op.ne]: service.id }, + category: service.category, + isActive: true + }, + limit: 3 + }); + + res.json({ + success: true, + service, + relatedServices + }); + } catch (error) { + console.error('Service detail API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching service' + }); + } +}); + +// Get service categories +router.get('/meta/categories', async (req, res) => { + try { + const categories = await Service.distinct('category', { isActive: true }); + + // Get count for each category + const categoriesWithCount = await Promise.all( + categories.map(async (category) => { + const count = await Service.countDocuments({ + category, + isActive: true + }); + return { category, count }; + }) + ); + + res.json({ + success: true, + categories: categoriesWithCount + }); + } catch (error) { + console.error('Service categories API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching categories' + }); + } +}); + +// Search services +router.get('/search/:term', async (req, res) => { + try { + const searchTerm = req.params.term; + const limit = parseInt(req.query.limit) || 10; + + const services = await Service.find({ + $and: [ + { isActive: true }, + { + $or: [ + { name: { $regex: searchTerm, $options: 'i' } }, + { description: { $regex: searchTerm, $options: 'i' } }, + { tags: { $in: [new RegExp(searchTerm, 'i')] } } + ] + } + ] + }) + .select('name shortDescription icon pricing category') + .sort({ featured: -1, order: 1 }) + .limit(limit); + + res.json({ + success: true, + services, + searchTerm, + count: services.length + }); + } catch (error) { + console.error('Service search API error:', error); + res.status(500).json({ + success: false, + message: 'Error searching services' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/services_20251019204534.js b/.history/routes/services_20251019204534.js new file mode 100644 index 0000000..601fa05 --- /dev/null +++ b/.history/routes/services_20251019204534.js @@ -0,0 +1,142 @@ +const express = require('express'); +const router = express.Router(); +const { Service, Portfolio } = require('../models'); +const { Op } = require('sequelize'); + +// Get all services +router.get('/', async (req, res) => { + try { + const category = req.query.category; + const featured = req.query.featured; + + let whereClause = { isActive: true }; + + if (category && category !== 'all') { + whereClause.category = category; + } + + if (featured === 'true') { + whereClause.featured = true; + } + + const services = await Service.findAll({ + where: whereClause, + order: [['featured', 'DESC'], ['order', 'ASC']] + }); + + res.json({ + success: true, + services + }); + } catch (error) { + console.error('Services API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching services' + }); + } +}); + +// Get single service +router.get('/:id', async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service || !service.isActive) { + return res.status(404).json({ + success: false, + message: 'Service not found' + }); + } + + // Get related services + const relatedServices = await Service.findAll({ + where: { + id: { [Op.ne]: service.id }, + category: service.category, + isActive: true + }, + limit: 3 + }); + + res.json({ + success: true, + service, + relatedServices + }); + } catch (error) { + console.error('Service detail API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching service' + }); + } +}); + +// Get service categories +router.get('/meta/categories', async (req, res) => { + try { + const categories = await Service.distinct('category', { isActive: true }); + + // Get count for each category + const categoriesWithCount = await Promise.all( + categories.map(async (category) => { + const count = await Service.countDocuments({ + category, + isActive: true + }); + return { category, count }; + }) + ); + + res.json({ + success: true, + categories: categoriesWithCount + }); + } catch (error) { + console.error('Service categories API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching categories' + }); + } +}); + +// Search services +router.get('/search/:term', async (req, res) => { + try { + const searchTerm = req.params.term; + const limit = parseInt(req.query.limit) || 10; + + const services = await Service.findAll({ + where: { + [Op.and]: [ + { isActive: true }, + { + [Op.or]: [ + { name: { [Op.iLike]: `%${searchTerm}%` } }, + { description: { [Op.iLike]: `%${searchTerm}%` } }, + { tags: { [Op.contains]: [searchTerm] } } + ] + } + ] + }, + limit: limit + }); + + res.json({ + success: true, + services, + searchTerm, + count: services.length + }); + } catch (error) { + console.error('Service search API error:', error); + res.status(500).json({ + success: false, + message: 'Error searching services' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/routes/services_20251019204805.js b/.history/routes/services_20251019204805.js new file mode 100644 index 0000000..601fa05 --- /dev/null +++ b/.history/routes/services_20251019204805.js @@ -0,0 +1,142 @@ +const express = require('express'); +const router = express.Router(); +const { Service, Portfolio } = require('../models'); +const { Op } = require('sequelize'); + +// Get all services +router.get('/', async (req, res) => { + try { + const category = req.query.category; + const featured = req.query.featured; + + let whereClause = { isActive: true }; + + if (category && category !== 'all') { + whereClause.category = category; + } + + if (featured === 'true') { + whereClause.featured = true; + } + + const services = await Service.findAll({ + where: whereClause, + order: [['featured', 'DESC'], ['order', 'ASC']] + }); + + res.json({ + success: true, + services + }); + } catch (error) { + console.error('Services API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching services' + }); + } +}); + +// Get single service +router.get('/:id', async (req, res) => { + try { + const service = await Service.findByPk(req.params.id); + + if (!service || !service.isActive) { + return res.status(404).json({ + success: false, + message: 'Service not found' + }); + } + + // Get related services + const relatedServices = await Service.findAll({ + where: { + id: { [Op.ne]: service.id }, + category: service.category, + isActive: true + }, + limit: 3 + }); + + res.json({ + success: true, + service, + relatedServices + }); + } catch (error) { + console.error('Service detail API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching service' + }); + } +}); + +// Get service categories +router.get('/meta/categories', async (req, res) => { + try { + const categories = await Service.distinct('category', { isActive: true }); + + // Get count for each category + const categoriesWithCount = await Promise.all( + categories.map(async (category) => { + const count = await Service.countDocuments({ + category, + isActive: true + }); + return { category, count }; + }) + ); + + res.json({ + success: true, + categories: categoriesWithCount + }); + } catch (error) { + console.error('Service categories API error:', error); + res.status(500).json({ + success: false, + message: 'Error fetching categories' + }); + } +}); + +// Search services +router.get('/search/:term', async (req, res) => { + try { + const searchTerm = req.params.term; + const limit = parseInt(req.query.limit) || 10; + + const services = await Service.findAll({ + where: { + [Op.and]: [ + { isActive: true }, + { + [Op.or]: [ + { name: { [Op.iLike]: `%${searchTerm}%` } }, + { description: { [Op.iLike]: `%${searchTerm}%` } }, + { tags: { [Op.contains]: [searchTerm] } } + ] + } + ] + }, + limit: limit + }); + + res.json({ + success: true, + services, + searchTerm, + count: services.length + }); + } catch (error) { + console.error('Service search API error:', error); + res.status(500).json({ + success: false, + message: 'Error searching services' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/.history/scripts/dev_20251019203008.js b/.history/scripts/dev_20251019203008.js new file mode 100644 index 0000000..3c1a5fc --- /dev/null +++ b/.history/scripts/dev_20251019203008.js @@ -0,0 +1,73 @@ +#!/usr/bin/env node + +/** + * Development server with hot reload for SmartSolTech + * Uses nodemon for automatic server restart on file changes + */ + +const path = require('path'); +const { spawn } = require('child_process'); + +// Configuration +const SERVER_FILE = path.join(__dirname, '..', 'server.js'); +const NODEMON_CONFIG = { + script: SERVER_FILE, + ext: 'js,json,ejs', + ignore: ['node_modules/', 'public/', '.git/'], + watch: ['models/', 'routes/', 'views/', 'server.js'], + env: { + NODE_ENV: 'development', + DEBUG: 'app:*' + }, + delay: 1000 +}; + +function startDevelopmentServer() { + console.log('🚀 Starting SmartSolTech development server...'); + console.log('📁 Watching files for changes...'); + console.log('🔄 Server will automatically restart on file changes'); + console.log(''); + + const nodemonArgs = [ + NODEMON_CONFIG.script, + '--ext', NODEMON_CONFIG.ext, + '--ignore', NODEMON_CONFIG.ignore.join(','), + '--watch', NODEMON_CONFIG.watch.join(','), + '--delay', NODEMON_CONFIG.delay.toString() + ]; + + const nodemon = spawn('npx', ['nodemon', ...nodemonArgs], { + stdio: 'inherit', + env: { + ...process.env, + ...NODEMON_CONFIG.env + } + }); + + nodemon.on('close', (code) => { + console.log(`\n🛑 Development server exited with code ${code}`); + process.exit(code); + }); + + nodemon.on('error', (error) => { + console.error('❌ Error starting development server:', error); + process.exit(1); + }); + + // Handle graceful shutdown + process.on('SIGINT', () => { + console.log('\n🛑 Shutting down development server...'); + nodemon.kill('SIGINT'); + }); + + process.on('SIGTERM', () => { + console.log('\n🛑 Shutting down development server...'); + nodemon.kill('SIGTERM'); + }); +} + +if (require.main === module) { + startDevelopmentServer(); +} + +module.exports = { startDevelopmentServer }; \ No newline at end of file diff --git a/.history/scripts/dev_20251019203023.js b/.history/scripts/dev_20251019203023.js new file mode 100644 index 0000000..3c1a5fc --- /dev/null +++ b/.history/scripts/dev_20251019203023.js @@ -0,0 +1,73 @@ +#!/usr/bin/env node + +/** + * Development server with hot reload for SmartSolTech + * Uses nodemon for automatic server restart on file changes + */ + +const path = require('path'); +const { spawn } = require('child_process'); + +// Configuration +const SERVER_FILE = path.join(__dirname, '..', 'server.js'); +const NODEMON_CONFIG = { + script: SERVER_FILE, + ext: 'js,json,ejs', + ignore: ['node_modules/', 'public/', '.git/'], + watch: ['models/', 'routes/', 'views/', 'server.js'], + env: { + NODE_ENV: 'development', + DEBUG: 'app:*' + }, + delay: 1000 +}; + +function startDevelopmentServer() { + console.log('🚀 Starting SmartSolTech development server...'); + console.log('📁 Watching files for changes...'); + console.log('🔄 Server will automatically restart on file changes'); + console.log(''); + + const nodemonArgs = [ + NODEMON_CONFIG.script, + '--ext', NODEMON_CONFIG.ext, + '--ignore', NODEMON_CONFIG.ignore.join(','), + '--watch', NODEMON_CONFIG.watch.join(','), + '--delay', NODEMON_CONFIG.delay.toString() + ]; + + const nodemon = spawn('npx', ['nodemon', ...nodemonArgs], { + stdio: 'inherit', + env: { + ...process.env, + ...NODEMON_CONFIG.env + } + }); + + nodemon.on('close', (code) => { + console.log(`\n🛑 Development server exited with code ${code}`); + process.exit(code); + }); + + nodemon.on('error', (error) => { + console.error('❌ Error starting development server:', error); + process.exit(1); + }); + + // Handle graceful shutdown + process.on('SIGINT', () => { + console.log('\n🛑 Shutting down development server...'); + nodemon.kill('SIGINT'); + }); + + process.on('SIGTERM', () => { + console.log('\n🛑 Shutting down development server...'); + nodemon.kill('SIGTERM'); + }); +} + +if (require.main === module) { + startDevelopmentServer(); +} + +module.exports = { startDevelopmentServer }; \ No newline at end of file diff --git a/.history/scripts/init-db_20251019202228.js b/.history/scripts/init-db_20251019202228.js new file mode 100644 index 0000000..6118a9d --- /dev/null +++ b/.history/scripts/init-db_20251019202228.js @@ -0,0 +1,495 @@ +#!/usr/bin/env node + +/** + * Database initialization script for SmartSolTech + * Creates initial admin user and sample data for PostgreSQL + */ + +const { sequelize } = require('../config/database'); +require('dotenv').config(); + +// Import models +const { User, Service, Portfolio, SiteSettings } = require('../models'); + +// Configuration +const ADMIN_EMAIL = process.env.ADMIN_EMAIL || 'admin@smartsoltech.kr'; +const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'admin123456'; + +async function initializeDatabase() { + try { + console.log('🔄 Connecting to PostgreSQL...'); + await sequelize.authenticate(); + console.log('✅ Connected to PostgreSQL'); + + // Sync database (create tables) + console.log('🔄 Syncing database schema...'); + await sequelize.sync({ force: false }); + console.log('✅ Database schema synchronized'); + + // Create admin user + await createAdminUser(); + + // Create sample services + await createSampleServices(); + + // Create sample portfolio items + await createSamplePortfolio(); + + // Create site settings + await createSiteSettings(); + + console.log('🎉 Database initialization completed successfully!'); + console.log(`📧 Admin login: ${ADMIN_EMAIL}`); + console.log(`🔑 Admin password: ${ADMIN_PASSWORD}`); + console.log('⚠️ Please change the admin password after first login!'); + + } catch (error) { + console.error('❌ Database initialization failed:', error); + process.exit(1); + } finally { + await sequelize.close(); + console.log('🔌 Database connection closed'); + process.exit(0); + } +} + +async function createAdminUser() { + try { + const existingAdmin = await User.findOne({ email: ADMIN_EMAIL }); + + if (existingAdmin) { + console.log('👤 Admin user already exists, skipping...'); + return; + } + + const adminUser = new User({ + name: 'Administrator', + email: ADMIN_EMAIL, + password: ADMIN_PASSWORD, + role: 'admin', + isActive: true + }); + + await adminUser.save(); + console.log('✅ Admin user created successfully'); + } catch (error) { + console.error('❌ Error creating admin user:', error); + throw error; + } +} + +async function createSampleServices() { + try { + const existingServices = await Service.countDocuments(); + + if (existingServices > 0) { + console.log('🛠️ Services already exist, skipping...'); + return; + } + + const services = [ + { + name: '웹 개발', + description: '현대적이고 반응형인 웹사이트와 웹 애플리케이션을 개발합니다. React, Node.js, MongoDB 등 최신 기술 스택을 활용하여 성능과 사용자 경험을 최적화합니다.', + shortDescription: '현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발', + icon: 'fas fa-code', + category: 'development', + features: [ + { name: '반응형 디자인', description: '모든 디바이스에서 완벽하게 작동', included: true }, + { name: 'SEO 최적화', description: '검색엔진 최적화로 더 많은 방문자 유치', included: true }, + { name: '성능 최적화', description: '빠른 로딩 속도와 원활한 사용자 경험', included: true }, + { name: '보안 강화', description: '최신 보안 기술로 안전한 웹사이트', included: true }, + { name: '유지보수', description: '6개월 무료 유지보수 지원', included: true } + ], + pricing: { + basePrice: 500000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 500000, max: 5000000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: true, + isActive: true, + order: 1, + tags: ['React', 'Node.js', 'MongoDB', 'Express', 'JavaScript', 'HTML', 'CSS'] + }, + { + name: '모바일 앱 개발', + description: 'iOS와 Android 플랫폼을 위한 네이티브 및 크로스플랫폼 모바일 애플리케이션을 개발합니다. React Native, Flutter 등을 활용하여 효율적인 개발을 진행합니다.', + shortDescription: 'iOS/Android 네이티브 및 크로스플랫폼 앱 개발', + icon: 'fas fa-mobile-alt', + category: 'development', + features: [ + { name: '크로스플랫폼 개발', description: 'iOS와 Android 동시 지원', included: true }, + { name: '네이티브 성능', description: '최적화된 성능과 사용자 경험', included: true }, + { name: '푸시 알림', description: '실시간 푸시 알림 시스템', included: true }, + { name: '오프라인 지원', description: '인터넷 연결 없이도 기본 기능 사용 가능', included: false }, + { name: '앱스토어 등록', description: '앱스토어 및 플레이스토어 등록 지원', included: true } + ], + pricing: { + basePrice: 800000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 800000, max: 8000000 } + }, + estimatedTime: { min: 4, max: 16, unit: 'weeks' }, + featured: true, + isActive: true, + order: 2, + tags: ['React Native', 'Flutter', 'iOS', 'Android', 'Swift', 'Kotlin', 'JavaScript'] + }, + { + name: 'UI/UX 디자인', + description: '사용자 중심의 직관적이고 아름다운 인터페이스를 디자인합니다. 사용자 경험 연구와 프로토타이핑을 통해 최적의 디자인 솔루션을 제공합니다.', + shortDescription: '사용자 중심의 직관적이고 아름다운 인터페이스 디자인', + icon: 'fas fa-palette', + category: 'design', + features: [ + { name: '사용자 경험 연구', description: '타겟 사용자 분석 및 페르소나 설정', included: true }, + { name: '와이어프레임', description: '정보 구조와 레이아웃 설계', included: true }, + { name: '프로토타이핑', description: '인터랙티브 프로토타입 제작', included: true }, + { name: '비주얼 디자인', description: '브랜드에 맞는 시각적 디자인', included: true }, + { name: '디자인 시스템', description: '일관된 디자인을 위한 가이드라인', included: false } + ], + pricing: { + basePrice: 300000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 300000, max: 2000000 } + }, + estimatedTime: { min: 1, max: 6, unit: 'weeks' }, + featured: true, + isActive: true, + order: 3, + tags: ['Figma', 'Sketch', 'Adobe XD', 'Photoshop', 'Illustrator', 'Prototyping'] + }, + { + name: '디지털 마케팅', + description: 'SEO, 소셜미디어 마케팅, 온라인 광고를 통해 디지털 마케팅 전략을 수립하고 실행합니다. 데이터 분석을 통한 지속적인 최적화를 제공합니다.', + shortDescription: 'SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅', + icon: 'fas fa-chart-line', + category: 'marketing', + features: [ + { name: 'SEO 최적화', description: '검색엔진 상위 노출을 위한 최적화', included: true }, + { name: '소셜미디어 관리', description: '페이스북, 인스타그램, 유튜브 관리', included: true }, + { name: '구글 광고', description: '구글 애즈를 통한 타겟 광고', included: true }, + { name: '콘텐츠 마케팅', description: '블로그 및 콘텐츠 제작', included: false }, + { name: '분석 및 리포팅', description: '마케팅 성과 분석 및 보고서', included: true } + ], + pricing: { + basePrice: 200000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 200000, max: 1500000 } + }, + estimatedTime: { min: 2, max: 12, unit: 'weeks' }, + featured: true, + isActive: true, + order: 4, + tags: ['SEO', 'Google Ads', 'Facebook Ads', 'Analytics', 'Social Media', 'Content Marketing'] + }, + { + name: '브랜딩', + description: '기업의 정체성을 반영하는 로고, 브랜드 가이드라인, 마케팅 자료를 디자인합니다. 일관된 브랜드 이미지로 브랜드 가치를 높입니다.', + shortDescription: '로고, 브랜드 가이드라인, 마케팅 자료 디자인', + icon: 'fas fa-copyright', + category: 'design', + features: [ + { name: '로고 디자인', description: '기업 정체성을 반영한 로고 제작', included: true }, + { name: '브랜드 가이드라인', description: '색상, 폰트, 사용법 가이드', included: true }, + { name: '명함 디자인', description: '브랜드에 맞는 명함 디자인', included: true }, + { name: '브로슈어 디자인', description: '회사 소개 브로슈어 제작', included: false }, + { name: '웹 브랜딩', description: '웹사이트 브랜딩 요소 적용', included: false } + ], + pricing: { + basePrice: 400000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 400000, max: 2500000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: false, + isActive: true, + order: 5, + tags: ['Logo Design', 'Brand Identity', 'Graphic Design', 'Corporate Design'] + }, + { + name: '기술 컨설팅', + description: '기업의 디지털 전환과 기술 도입을 위한 전문적인 컨설팅을 제공합니다. 기술 아키텍처 설계부터 구현 전략까지 포괄적으로 지원합니다.', + shortDescription: '디지털 전환 및 기술 도입을 위한 전문 컨설팅', + icon: 'fas fa-lightbulb', + category: 'consulting', + features: [ + { name: '기술 분석', description: '현재 기술 스택 분석 및 개선안 제시', included: true }, + { name: '아키텍처 설계', description: '확장 가능한 시스템 아키텍처 설계', included: true }, + { name: '개발 프로세스 개선', description: '효율적인 개발 워크플로우 구축', included: true }, + { name: '팀 교육', description: '개발팀 대상 기술 교육', included: false }, + { name: '지속적인 지원', description: '프로젝트 완료 후 지속적인 기술 지원', included: false } + ], + pricing: { + basePrice: 150000, + currency: 'KRW', + priceType: 'hourly', + priceRange: { min: 150000, max: 500000 } + }, + estimatedTime: { min: 1, max: 4, unit: 'weeks' }, + featured: false, + isActive: true, + order: 6, + tags: ['Architecture', 'Strategy', 'Process', 'Training', 'Consultation'] + } + ]; + + await Service.insertMany(services); + console.log('✅ Sample services created successfully'); + } catch (error) { + console.error('❌ Error creating sample services:', error); + throw error; + } +} + +async function createSamplePortfolio() { + try { + const existingPortfolio = await Portfolio.countDocuments(); + + if (existingPortfolio > 0) { + console.log('🎨 Portfolio items already exist, skipping...'); + return; + } + + const portfolioItems = [ + { + title: 'E-commerce 플랫폼', + description: '현대적인 온라인 쇼핑몰 플랫폼을 개발했습니다. React와 Node.js를 활용하여 높은 성능과 사용자 경험을 제공하며, 결제 시스템과 재고 관리 기능을 포함합니다.', + shortDescription: '반응형 온라인 쇼핑몰 플랫폼 개발', + category: 'web-development', + technologies: ['React', 'Node.js', 'MongoDB', 'Express', 'Stripe', 'AWS'], + images: [ + { url: '/images/portfolio/ecommerce-1.jpg', alt: 'E-commerce 메인페이지', isPrimary: true }, + { url: '/images/portfolio/ecommerce-2.jpg', alt: '상품 상세페이지', isPrimary: false }, + { url: '/images/portfolio/ecommerce-3.jpg', alt: '장바구니 페이지', isPrimary: false } + ], + clientName: '패션 브랜드 ABC', + projectUrl: 'https://example-ecommerce.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-01-15'), + completedAt: new Date('2024-01-10'), + isPublished: true, + viewCount: 150, + likes: 25, + order: 1, + seo: { + metaTitle: 'E-commerce 플랫폼 개발 프로젝트', + metaDescription: '현대적인 온라인 쇼핑몰 플랫폼 개발 사례', + keywords: ['E-commerce', 'React', 'Node.js', '온라인쇼핑몰'] + } + }, + { + title: '모바일 피트니스 앱', + description: 'React Native를 사용하여 크로스플랫폼 피트니스 애플리케이션을 개발했습니다. 운동 계획, 칼로리 추적, 소셜 기능을 포함하여 사용자들의 건강한 라이프스타일을 지원합니다.', + shortDescription: '건강 관리를 위한 크로스플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['React Native', 'Redux', 'Firebase', 'Node.js', 'PostgreSQL'], + images: [ + { url: '/images/portfolio/fitness-1.jpg', alt: '피트니스 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/fitness-2.jpg', alt: '운동 계획 화면', isPrimary: false }, + { url: '/images/portfolio/fitness-3.jpg', alt: '통계 화면', isPrimary: false } + ], + clientName: '헬스케어 스타트업 FIT', + status: 'completed', + featured: true, + publishedAt: new Date('2024-02-20'), + completedAt: new Date('2024-02-15'), + isPublished: true, + viewCount: 200, + likes: 35, + order: 2, + seo: { + metaTitle: '모바일 피트니스 앱 개발 프로젝트', + metaDescription: 'React Native로 개발한 크로스플랫폼 피트니스 앱', + keywords: ['Mobile App', 'React Native', 'Fitness', '헬스케어'] + } + }, + { + title: '기업 웹사이트 리뉴얼', + description: '기업의 브랜드 아이덴티티를 반영한 웹사이트 리뉴얼 프로젝트입니다. 사용자 경험을 개선하고 모던한 디자인을 적용하여 브랜드 가치를 높였습니다.', + shortDescription: '브랜드 아이덴티티를 반영한 기업 웹사이트 리뉴얼', + category: 'ui-ux-design', + technologies: ['Figma', 'React', 'Sass', 'Framer Motion', 'Contentful'], + images: [ + { url: '/images/portfolio/corporate-1.jpg', alt: '기업 웹사이트 메인페이지', isPrimary: true }, + { url: '/images/portfolio/corporate-2.jpg', alt: '회사소개 페이지', isPrimary: false }, + { url: '/images/portfolio/corporate-3.jpg', alt: '서비스 페이지', isPrimary: false } + ], + clientName: '기술 기업 TechCorp', + projectUrl: 'https://example-corp.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-03-10'), + completedAt: new Date('2024-03-05'), + isPublished: true, + viewCount: 120, + likes: 18, + order: 3, + seo: { + metaTitle: '기업 웹사이트 리뉴얼 프로젝트', + metaDescription: '모던한 디자인과 향상된 UX의 기업 웹사이트', + keywords: ['Web Design', 'Corporate', 'UI/UX', '웹사이트리뉴얼'] + } + }, + { + title: '레스토랑 예약 시스템', + description: '레스토랑을 위한 온라인 예약 시스템을 개발했습니다. 실시간 테이블 현황, 예약 관리, 고객 관리 기능을 포함하여 레스토랑 운영 효율성을 높였습니다.', + shortDescription: '실시간 테이블 예약 및 관리 시스템', + category: 'web-development', + technologies: ['Vue.js', 'Laravel', 'MySQL', 'Socket.io', 'Bootstrap'], + images: [ + { url: '/images/portfolio/restaurant-1.jpg', alt: '예약 시스템 메인', isPrimary: true }, + { url: '/images/portfolio/restaurant-2.jpg', alt: '예약 현황 관리', isPrimary: false } + ], + clientName: '레스토랑 델리셔스', + status: 'completed', + featured: false, + publishedAt: new Date('2024-04-05'), + completedAt: new Date('2024-04-01'), + isPublished: true, + viewCount: 85, + likes: 12, + order: 4, + seo: { + metaTitle: '레스토랑 예약 시스템 개발', + metaDescription: '실시간 테이블 예약 및 관리 웹 시스템', + keywords: ['Reservation System', 'Vue.js', 'Laravel', '예약시스템'] + } + }, + { + title: '교육 플랫폼 앱', + description: '온라인 교육을 위한 모바일 애플리케이션입니다. 동영상 강의, 퀴즈, 진도 관리 기능을 포함하여 효과적인 학습 환경을 제공합니다.', + shortDescription: '온라인 학습을 위한 교육 플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['Flutter', 'Dart', 'Firebase', 'FFmpeg', 'AWS S3'], + images: [ + { url: '/images/portfolio/education-1.jpg', alt: '교육 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/education-2.jpg', alt: '강의 재생 화면', isPrimary: false } + ], + clientName: '온라인 교육 기업 EduTech', + status: 'completed', + featured: false, + publishedAt: new Date('2024-05-12'), + completedAt: new Date('2024-05-08'), + isPublished: true, + viewCount: 95, + likes: 20, + order: 5, + seo: { + metaTitle: '교육 플랫폼 모바일 앱 개발', + metaDescription: 'Flutter로 개발한 온라인 교육 모바일 앱', + keywords: ['Education App', 'Flutter', 'E-learning', '교육앱'] + } + }, + { + title: 'IoT 대시보드', + description: 'IoT 디바이스들을 모니터링하고 제어할 수 있는 웹 대시보드를 개발했습니다. 실시간 데이터 시각화와 알림 기능을 포함합니다.', + shortDescription: 'IoT 디바이스 모니터링 및 제어 웹 대시보드', + category: 'web-development', + technologies: ['React', 'D3.js', 'Node.js', 'MQTT', 'InfluxDB', 'Docker'], + images: [ + { url: '/images/portfolio/iot-1.jpg', alt: 'IoT 대시보드 메인', isPrimary: true } + ], + clientName: 'IoT 솔루션 기업 SmartDevice', + status: 'in-progress', + featured: false, + publishedAt: new Date('2024-06-01'), + isPublished: true, + viewCount: 45, + likes: 8, + order: 6, + seo: { + metaTitle: 'IoT 대시보드 개발 프로젝트', + metaDescription: '실시간 IoT 디바이스 모니터링 웹 대시보드', + keywords: ['IoT', 'Dashboard', 'Real-time', 'Monitoring'] + } + } + ]; + + await Portfolio.insertMany(portfolioItems); + console.log('✅ Sample portfolio items created successfully'); + } catch (error) { + console.error('❌ Error creating sample portfolio:', error); + throw error; + } +} + +async function createSiteSettings() { + try { + const existingSettings = await SiteSettings.findOne(); + + if (existingSettings) { + console.log('⚙️ Site settings already exist, skipping...'); + return; + } + + const settings = new SiteSettings({ + siteName: 'SmartSolTech', + siteDescription: '혁신적인 기술 솔루션으로 비즈니스의 성장을 지원합니다', + logo: '/images/logo.png', + favicon: '/images/favicon.ico', + contact: { + email: 'info@smartsoltech.kr', + phone: '+82-10-1234-5678', + address: 'Seoul, South Korea' + }, + social: { + facebook: 'https://facebook.com/smartsoltech', + twitter: 'https://twitter.com/smartsoltech', + linkedin: 'https://linkedin.com/company/smartsoltech', + instagram: 'https://instagram.com/smartsoltech', + github: 'https://github.com/smartsoltech' + }, + telegram: { + isEnabled: false + }, + seo: { + metaTitle: 'SmartSolTech - 혁신적인 기술 솔루션', + metaDescription: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅 전문 기업. 한국에서 최고의 기술 솔루션을 제공합니다.', + keywords: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅, 기술 솔루션, 한국, SmartSolTech' + }, + hero: { + title: 'Smart Technology Solutions', + subtitle: '혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비즈니스의 디지털 전환을 이끌어갑니다', + backgroundImage: '/images/hero-bg.jpg', + ctaText: '프로젝트 시작하기', + ctaLink: '/contact' + }, + about: { + title: 'SmartSolTech 소개', + description: '우리는 최신 기술과 창의적인 아이디어로 고객의 비즈니스 성장을 지원하는 전문 개발팀입니다. 웹 개발부터 모바일 앱, UI/UX 디자인까지 포괄적인 디지털 솔루션을 제공합니다.', + image: '/images/about.jpg' + }, + maintenance: { + isEnabled: false, + message: '현재 시스템 점검 중입니다. 잠시 후 다시 접속해 주세요.' + } + }); + + await settings.save(); + console.log('✅ Site settings created successfully'); + } catch (error) { + console.error('❌ Error creating site settings:', error); + throw error; + } +} + +// Run the initialization +if (require.main === module) { + initializeDatabase(); +} + +module.exports = { + initializeDatabase, + createAdminUser, + createSampleServices, + createSamplePortfolio, + createSiteSettings +}; \ No newline at end of file diff --git a/.history/scripts/init-db_20251019202238.js b/.history/scripts/init-db_20251019202238.js new file mode 100644 index 0000000..40ef93d --- /dev/null +++ b/.history/scripts/init-db_20251019202238.js @@ -0,0 +1,496 @@ +#!/usr/bin/env node + +/** + * Database initialization script for SmartSolTech + * Creates initial admin user and sample data for PostgreSQL + */ + +const { sequelize } = require('../config/database'); +require('dotenv').config(); + +// Import models +const { User, Service, Portfolio, SiteSettings } = require('../models'); + +// Configuration +const ADMIN_EMAIL = process.env.ADMIN_EMAIL || 'admin@smartsoltech.kr'; +const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'admin123456'; + +async function initializeDatabase() { + try { + console.log('🔄 Connecting to PostgreSQL...'); + await sequelize.authenticate(); + console.log('✅ Connected to PostgreSQL'); + + // Sync database (create tables) + console.log('🔄 Syncing database schema...'); + await sequelize.sync({ force: false }); + console.log('✅ Database schema synchronized'); + + // Create admin user + await createAdminUser(); + + // Create sample services + await createSampleServices(); + + // Create sample portfolio items + await createSamplePortfolio(); + + // Create site settings + await createSiteSettings(); + + console.log('🎉 Database initialization completed successfully!'); + console.log(`📧 Admin login: ${ADMIN_EMAIL}`); + console.log(`🔑 Admin password: ${ADMIN_PASSWORD}`); + console.log('⚠️ Please change the admin password after first login!'); + + } catch (error) { + console.error('❌ Database initialization failed:', error); + process.exit(1); + } finally { + await sequelize.close(); + console.log('🔌 Database connection closed'); + process.exit(0); + } +} + +async function createAdminUser() { + try { + const existingAdmin = await User.findOne({ + where: { email: ADMIN_EMAIL } + }); + + if (existingAdmin) { + console.log('👤 Admin user already exists, skipping...'); + return; + } + + const adminUser = await User.create({ + name: 'Administrator', + email: ADMIN_EMAIL, + password: ADMIN_PASSWORD, + role: 'admin', + isActive: true + }); + + console.log('✅ Admin user created successfully'); + } catch (error) { + console.error('❌ Error creating admin user:', error); + throw error; + } +} + +async function createSampleServices() { + try { + const existingServices = await Service.countDocuments(); + + if (existingServices > 0) { + console.log('🛠️ Services already exist, skipping...'); + return; + } + + const services = [ + { + name: '웹 개발', + description: '현대적이고 반응형인 웹사이트와 웹 애플리케이션을 개발합니다. React, Node.js, MongoDB 등 최신 기술 스택을 활용하여 성능과 사용자 경험을 최적화합니다.', + shortDescription: '현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발', + icon: 'fas fa-code', + category: 'development', + features: [ + { name: '반응형 디자인', description: '모든 디바이스에서 완벽하게 작동', included: true }, + { name: 'SEO 최적화', description: '검색엔진 최적화로 더 많은 방문자 유치', included: true }, + { name: '성능 최적화', description: '빠른 로딩 속도와 원활한 사용자 경험', included: true }, + { name: '보안 강화', description: '최신 보안 기술로 안전한 웹사이트', included: true }, + { name: '유지보수', description: '6개월 무료 유지보수 지원', included: true } + ], + pricing: { + basePrice: 500000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 500000, max: 5000000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: true, + isActive: true, + order: 1, + tags: ['React', 'Node.js', 'MongoDB', 'Express', 'JavaScript', 'HTML', 'CSS'] + }, + { + name: '모바일 앱 개발', + description: 'iOS와 Android 플랫폼을 위한 네이티브 및 크로스플랫폼 모바일 애플리케이션을 개발합니다. React Native, Flutter 등을 활용하여 효율적인 개발을 진행합니다.', + shortDescription: 'iOS/Android 네이티브 및 크로스플랫폼 앱 개발', + icon: 'fas fa-mobile-alt', + category: 'development', + features: [ + { name: '크로스플랫폼 개발', description: 'iOS와 Android 동시 지원', included: true }, + { name: '네이티브 성능', description: '최적화된 성능과 사용자 경험', included: true }, + { name: '푸시 알림', description: '실시간 푸시 알림 시스템', included: true }, + { name: '오프라인 지원', description: '인터넷 연결 없이도 기본 기능 사용 가능', included: false }, + { name: '앱스토어 등록', description: '앱스토어 및 플레이스토어 등록 지원', included: true } + ], + pricing: { + basePrice: 800000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 800000, max: 8000000 } + }, + estimatedTime: { min: 4, max: 16, unit: 'weeks' }, + featured: true, + isActive: true, + order: 2, + tags: ['React Native', 'Flutter', 'iOS', 'Android', 'Swift', 'Kotlin', 'JavaScript'] + }, + { + name: 'UI/UX 디자인', + description: '사용자 중심의 직관적이고 아름다운 인터페이스를 디자인합니다. 사용자 경험 연구와 프로토타이핑을 통해 최적의 디자인 솔루션을 제공합니다.', + shortDescription: '사용자 중심의 직관적이고 아름다운 인터페이스 디자인', + icon: 'fas fa-palette', + category: 'design', + features: [ + { name: '사용자 경험 연구', description: '타겟 사용자 분석 및 페르소나 설정', included: true }, + { name: '와이어프레임', description: '정보 구조와 레이아웃 설계', included: true }, + { name: '프로토타이핑', description: '인터랙티브 프로토타입 제작', included: true }, + { name: '비주얼 디자인', description: '브랜드에 맞는 시각적 디자인', included: true }, + { name: '디자인 시스템', description: '일관된 디자인을 위한 가이드라인', included: false } + ], + pricing: { + basePrice: 300000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 300000, max: 2000000 } + }, + estimatedTime: { min: 1, max: 6, unit: 'weeks' }, + featured: true, + isActive: true, + order: 3, + tags: ['Figma', 'Sketch', 'Adobe XD', 'Photoshop', 'Illustrator', 'Prototyping'] + }, + { + name: '디지털 마케팅', + description: 'SEO, 소셜미디어 마케팅, 온라인 광고를 통해 디지털 마케팅 전략을 수립하고 실행합니다. 데이터 분석을 통한 지속적인 최적화를 제공합니다.', + shortDescription: 'SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅', + icon: 'fas fa-chart-line', + category: 'marketing', + features: [ + { name: 'SEO 최적화', description: '검색엔진 상위 노출을 위한 최적화', included: true }, + { name: '소셜미디어 관리', description: '페이스북, 인스타그램, 유튜브 관리', included: true }, + { name: '구글 광고', description: '구글 애즈를 통한 타겟 광고', included: true }, + { name: '콘텐츠 마케팅', description: '블로그 및 콘텐츠 제작', included: false }, + { name: '분석 및 리포팅', description: '마케팅 성과 분석 및 보고서', included: true } + ], + pricing: { + basePrice: 200000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 200000, max: 1500000 } + }, + estimatedTime: { min: 2, max: 12, unit: 'weeks' }, + featured: true, + isActive: true, + order: 4, + tags: ['SEO', 'Google Ads', 'Facebook Ads', 'Analytics', 'Social Media', 'Content Marketing'] + }, + { + name: '브랜딩', + description: '기업의 정체성을 반영하는 로고, 브랜드 가이드라인, 마케팅 자료를 디자인합니다. 일관된 브랜드 이미지로 브랜드 가치를 높입니다.', + shortDescription: '로고, 브랜드 가이드라인, 마케팅 자료 디자인', + icon: 'fas fa-copyright', + category: 'design', + features: [ + { name: '로고 디자인', description: '기업 정체성을 반영한 로고 제작', included: true }, + { name: '브랜드 가이드라인', description: '색상, 폰트, 사용법 가이드', included: true }, + { name: '명함 디자인', description: '브랜드에 맞는 명함 디자인', included: true }, + { name: '브로슈어 디자인', description: '회사 소개 브로슈어 제작', included: false }, + { name: '웹 브랜딩', description: '웹사이트 브랜딩 요소 적용', included: false } + ], + pricing: { + basePrice: 400000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 400000, max: 2500000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: false, + isActive: true, + order: 5, + tags: ['Logo Design', 'Brand Identity', 'Graphic Design', 'Corporate Design'] + }, + { + name: '기술 컨설팅', + description: '기업의 디지털 전환과 기술 도입을 위한 전문적인 컨설팅을 제공합니다. 기술 아키텍처 설계부터 구현 전략까지 포괄적으로 지원합니다.', + shortDescription: '디지털 전환 및 기술 도입을 위한 전문 컨설팅', + icon: 'fas fa-lightbulb', + category: 'consulting', + features: [ + { name: '기술 분석', description: '현재 기술 스택 분석 및 개선안 제시', included: true }, + { name: '아키텍처 설계', description: '확장 가능한 시스템 아키텍처 설계', included: true }, + { name: '개발 프로세스 개선', description: '효율적인 개발 워크플로우 구축', included: true }, + { name: '팀 교육', description: '개발팀 대상 기술 교육', included: false }, + { name: '지속적인 지원', description: '프로젝트 완료 후 지속적인 기술 지원', included: false } + ], + pricing: { + basePrice: 150000, + currency: 'KRW', + priceType: 'hourly', + priceRange: { min: 150000, max: 500000 } + }, + estimatedTime: { min: 1, max: 4, unit: 'weeks' }, + featured: false, + isActive: true, + order: 6, + tags: ['Architecture', 'Strategy', 'Process', 'Training', 'Consultation'] + } + ]; + + await Service.insertMany(services); + console.log('✅ Sample services created successfully'); + } catch (error) { + console.error('❌ Error creating sample services:', error); + throw error; + } +} + +async function createSamplePortfolio() { + try { + const existingPortfolio = await Portfolio.countDocuments(); + + if (existingPortfolio > 0) { + console.log('🎨 Portfolio items already exist, skipping...'); + return; + } + + const portfolioItems = [ + { + title: 'E-commerce 플랫폼', + description: '현대적인 온라인 쇼핑몰 플랫폼을 개발했습니다. React와 Node.js를 활용하여 높은 성능과 사용자 경험을 제공하며, 결제 시스템과 재고 관리 기능을 포함합니다.', + shortDescription: '반응형 온라인 쇼핑몰 플랫폼 개발', + category: 'web-development', + technologies: ['React', 'Node.js', 'MongoDB', 'Express', 'Stripe', 'AWS'], + images: [ + { url: '/images/portfolio/ecommerce-1.jpg', alt: 'E-commerce 메인페이지', isPrimary: true }, + { url: '/images/portfolio/ecommerce-2.jpg', alt: '상품 상세페이지', isPrimary: false }, + { url: '/images/portfolio/ecommerce-3.jpg', alt: '장바구니 페이지', isPrimary: false } + ], + clientName: '패션 브랜드 ABC', + projectUrl: 'https://example-ecommerce.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-01-15'), + completedAt: new Date('2024-01-10'), + isPublished: true, + viewCount: 150, + likes: 25, + order: 1, + seo: { + metaTitle: 'E-commerce 플랫폼 개발 프로젝트', + metaDescription: '현대적인 온라인 쇼핑몰 플랫폼 개발 사례', + keywords: ['E-commerce', 'React', 'Node.js', '온라인쇼핑몰'] + } + }, + { + title: '모바일 피트니스 앱', + description: 'React Native를 사용하여 크로스플랫폼 피트니스 애플리케이션을 개발했습니다. 운동 계획, 칼로리 추적, 소셜 기능을 포함하여 사용자들의 건강한 라이프스타일을 지원합니다.', + shortDescription: '건강 관리를 위한 크로스플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['React Native', 'Redux', 'Firebase', 'Node.js', 'PostgreSQL'], + images: [ + { url: '/images/portfolio/fitness-1.jpg', alt: '피트니스 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/fitness-2.jpg', alt: '운동 계획 화면', isPrimary: false }, + { url: '/images/portfolio/fitness-3.jpg', alt: '통계 화면', isPrimary: false } + ], + clientName: '헬스케어 스타트업 FIT', + status: 'completed', + featured: true, + publishedAt: new Date('2024-02-20'), + completedAt: new Date('2024-02-15'), + isPublished: true, + viewCount: 200, + likes: 35, + order: 2, + seo: { + metaTitle: '모바일 피트니스 앱 개발 프로젝트', + metaDescription: 'React Native로 개발한 크로스플랫폼 피트니스 앱', + keywords: ['Mobile App', 'React Native', 'Fitness', '헬스케어'] + } + }, + { + title: '기업 웹사이트 리뉴얼', + description: '기업의 브랜드 아이덴티티를 반영한 웹사이트 리뉴얼 프로젝트입니다. 사용자 경험을 개선하고 모던한 디자인을 적용하여 브랜드 가치를 높였습니다.', + shortDescription: '브랜드 아이덴티티를 반영한 기업 웹사이트 리뉴얼', + category: 'ui-ux-design', + technologies: ['Figma', 'React', 'Sass', 'Framer Motion', 'Contentful'], + images: [ + { url: '/images/portfolio/corporate-1.jpg', alt: '기업 웹사이트 메인페이지', isPrimary: true }, + { url: '/images/portfolio/corporate-2.jpg', alt: '회사소개 페이지', isPrimary: false }, + { url: '/images/portfolio/corporate-3.jpg', alt: '서비스 페이지', isPrimary: false } + ], + clientName: '기술 기업 TechCorp', + projectUrl: 'https://example-corp.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-03-10'), + completedAt: new Date('2024-03-05'), + isPublished: true, + viewCount: 120, + likes: 18, + order: 3, + seo: { + metaTitle: '기업 웹사이트 리뉴얼 프로젝트', + metaDescription: '모던한 디자인과 향상된 UX의 기업 웹사이트', + keywords: ['Web Design', 'Corporate', 'UI/UX', '웹사이트리뉴얼'] + } + }, + { + title: '레스토랑 예약 시스템', + description: '레스토랑을 위한 온라인 예약 시스템을 개발했습니다. 실시간 테이블 현황, 예약 관리, 고객 관리 기능을 포함하여 레스토랑 운영 효율성을 높였습니다.', + shortDescription: '실시간 테이블 예약 및 관리 시스템', + category: 'web-development', + technologies: ['Vue.js', 'Laravel', 'MySQL', 'Socket.io', 'Bootstrap'], + images: [ + { url: '/images/portfolio/restaurant-1.jpg', alt: '예약 시스템 메인', isPrimary: true }, + { url: '/images/portfolio/restaurant-2.jpg', alt: '예약 현황 관리', isPrimary: false } + ], + clientName: '레스토랑 델리셔스', + status: 'completed', + featured: false, + publishedAt: new Date('2024-04-05'), + completedAt: new Date('2024-04-01'), + isPublished: true, + viewCount: 85, + likes: 12, + order: 4, + seo: { + metaTitle: '레스토랑 예약 시스템 개발', + metaDescription: '실시간 테이블 예약 및 관리 웹 시스템', + keywords: ['Reservation System', 'Vue.js', 'Laravel', '예약시스템'] + } + }, + { + title: '교육 플랫폼 앱', + description: '온라인 교육을 위한 모바일 애플리케이션입니다. 동영상 강의, 퀴즈, 진도 관리 기능을 포함하여 효과적인 학습 환경을 제공합니다.', + shortDescription: '온라인 학습을 위한 교육 플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['Flutter', 'Dart', 'Firebase', 'FFmpeg', 'AWS S3'], + images: [ + { url: '/images/portfolio/education-1.jpg', alt: '교육 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/education-2.jpg', alt: '강의 재생 화면', isPrimary: false } + ], + clientName: '온라인 교육 기업 EduTech', + status: 'completed', + featured: false, + publishedAt: new Date('2024-05-12'), + completedAt: new Date('2024-05-08'), + isPublished: true, + viewCount: 95, + likes: 20, + order: 5, + seo: { + metaTitle: '교육 플랫폼 모바일 앱 개발', + metaDescription: 'Flutter로 개발한 온라인 교육 모바일 앱', + keywords: ['Education App', 'Flutter', 'E-learning', '교육앱'] + } + }, + { + title: 'IoT 대시보드', + description: 'IoT 디바이스들을 모니터링하고 제어할 수 있는 웹 대시보드를 개발했습니다. 실시간 데이터 시각화와 알림 기능을 포함합니다.', + shortDescription: 'IoT 디바이스 모니터링 및 제어 웹 대시보드', + category: 'web-development', + technologies: ['React', 'D3.js', 'Node.js', 'MQTT', 'InfluxDB', 'Docker'], + images: [ + { url: '/images/portfolio/iot-1.jpg', alt: 'IoT 대시보드 메인', isPrimary: true } + ], + clientName: 'IoT 솔루션 기업 SmartDevice', + status: 'in-progress', + featured: false, + publishedAt: new Date('2024-06-01'), + isPublished: true, + viewCount: 45, + likes: 8, + order: 6, + seo: { + metaTitle: 'IoT 대시보드 개발 프로젝트', + metaDescription: '실시간 IoT 디바이스 모니터링 웹 대시보드', + keywords: ['IoT', 'Dashboard', 'Real-time', 'Monitoring'] + } + } + ]; + + await Portfolio.insertMany(portfolioItems); + console.log('✅ Sample portfolio items created successfully'); + } catch (error) { + console.error('❌ Error creating sample portfolio:', error); + throw error; + } +} + +async function createSiteSettings() { + try { + const existingSettings = await SiteSettings.findOne(); + + if (existingSettings) { + console.log('⚙️ Site settings already exist, skipping...'); + return; + } + + const settings = new SiteSettings({ + siteName: 'SmartSolTech', + siteDescription: '혁신적인 기술 솔루션으로 비즈니스의 성장을 지원합니다', + logo: '/images/logo.png', + favicon: '/images/favicon.ico', + contact: { + email: 'info@smartsoltech.kr', + phone: '+82-10-1234-5678', + address: 'Seoul, South Korea' + }, + social: { + facebook: 'https://facebook.com/smartsoltech', + twitter: 'https://twitter.com/smartsoltech', + linkedin: 'https://linkedin.com/company/smartsoltech', + instagram: 'https://instagram.com/smartsoltech', + github: 'https://github.com/smartsoltech' + }, + telegram: { + isEnabled: false + }, + seo: { + metaTitle: 'SmartSolTech - 혁신적인 기술 솔루션', + metaDescription: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅 전문 기업. 한국에서 최고의 기술 솔루션을 제공합니다.', + keywords: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅, 기술 솔루션, 한국, SmartSolTech' + }, + hero: { + title: 'Smart Technology Solutions', + subtitle: '혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비즈니스의 디지털 전환을 이끌어갑니다', + backgroundImage: '/images/hero-bg.jpg', + ctaText: '프로젝트 시작하기', + ctaLink: '/contact' + }, + about: { + title: 'SmartSolTech 소개', + description: '우리는 최신 기술과 창의적인 아이디어로 고객의 비즈니스 성장을 지원하는 전문 개발팀입니다. 웹 개발부터 모바일 앱, UI/UX 디자인까지 포괄적인 디지털 솔루션을 제공합니다.', + image: '/images/about.jpg' + }, + maintenance: { + isEnabled: false, + message: '현재 시스템 점검 중입니다. 잠시 후 다시 접속해 주세요.' + } + }); + + await settings.save(); + console.log('✅ Site settings created successfully'); + } catch (error) { + console.error('❌ Error creating site settings:', error); + throw error; + } +} + +// Run the initialization +if (require.main === module) { + initializeDatabase(); +} + +module.exports = { + initializeDatabase, + createAdminUser, + createSampleServices, + createSamplePortfolio, + createSiteSettings +}; \ No newline at end of file diff --git a/.history/scripts/init-db_20251019202245.js b/.history/scripts/init-db_20251019202245.js new file mode 100644 index 0000000..f08fb5a --- /dev/null +++ b/.history/scripts/init-db_20251019202245.js @@ -0,0 +1,496 @@ +#!/usr/bin/env node + +/** + * Database initialization script for SmartSolTech + * Creates initial admin user and sample data for PostgreSQL + */ + +const { sequelize } = require('../config/database'); +require('dotenv').config(); + +// Import models +const { User, Service, Portfolio, SiteSettings } = require('../models'); + +// Configuration +const ADMIN_EMAIL = process.env.ADMIN_EMAIL || 'admin@smartsoltech.kr'; +const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'admin123456'; + +async function initializeDatabase() { + try { + console.log('🔄 Connecting to PostgreSQL...'); + await sequelize.authenticate(); + console.log('✅ Connected to PostgreSQL'); + + // Sync database (create tables) + console.log('🔄 Syncing database schema...'); + await sequelize.sync({ force: false }); + console.log('✅ Database schema synchronized'); + + // Create admin user + await createAdminUser(); + + // Create sample services + await createSampleServices(); + + // Create sample portfolio items + await createSamplePortfolio(); + + // Create site settings + await createSiteSettings(); + + console.log('🎉 Database initialization completed successfully!'); + console.log(`📧 Admin login: ${ADMIN_EMAIL}`); + console.log(`🔑 Admin password: ${ADMIN_PASSWORD}`); + console.log('⚠️ Please change the admin password after first login!'); + + } catch (error) { + console.error('❌ Database initialization failed:', error); + process.exit(1); + } finally { + await sequelize.close(); + console.log('🔌 Database connection closed'); + process.exit(0); + } +} + +async function createAdminUser() { + try { + const existingAdmin = await User.findOne({ + where: { email: ADMIN_EMAIL } + }); + + if (existingAdmin) { + console.log('👤 Admin user already exists, skipping...'); + return; + } + + const adminUser = await User.create({ + name: 'Administrator', + email: ADMIN_EMAIL, + password: ADMIN_PASSWORD, + role: 'admin', + isActive: true + }); + + console.log('✅ Admin user created successfully'); + } catch (error) { + console.error('❌ Error creating admin user:', error); + throw error; + } +} + +async function createSampleServices() { + try { + const existingServices = await Service.count(); + + if (existingServices > 0) { + console.log('🛠️ Services already exist, skipping...'); + return; + } + + const services = [ + { + name: '웹 개발', + description: '현대적이고 반응형인 웹사이트와 웹 애플리케이션을 개발합니다. React, Node.js, MongoDB 등 최신 기술 스택을 활용하여 성능과 사용자 경험을 최적화합니다.', + shortDescription: '현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발', + icon: 'fas fa-code', + category: 'development', + features: [ + { name: '반응형 디자인', description: '모든 디바이스에서 완벽하게 작동', included: true }, + { name: 'SEO 최적화', description: '검색엔진 최적화로 더 많은 방문자 유치', included: true }, + { name: '성능 최적화', description: '빠른 로딩 속도와 원활한 사용자 경험', included: true }, + { name: '보안 강화', description: '최신 보안 기술로 안전한 웹사이트', included: true }, + { name: '유지보수', description: '6개월 무료 유지보수 지원', included: true } + ], + pricing: { + basePrice: 500000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 500000, max: 5000000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: true, + isActive: true, + order: 1, + tags: ['React', 'Node.js', 'MongoDB', 'Express', 'JavaScript', 'HTML', 'CSS'] + }, + { + name: '모바일 앱 개발', + description: 'iOS와 Android 플랫폼을 위한 네이티브 및 크로스플랫폼 모바일 애플리케이션을 개발합니다. React Native, Flutter 등을 활용하여 효율적인 개발을 진행합니다.', + shortDescription: 'iOS/Android 네이티브 및 크로스플랫폼 앱 개발', + icon: 'fas fa-mobile-alt', + category: 'development', + features: [ + { name: '크로스플랫폼 개발', description: 'iOS와 Android 동시 지원', included: true }, + { name: '네이티브 성능', description: '최적화된 성능과 사용자 경험', included: true }, + { name: '푸시 알림', description: '실시간 푸시 알림 시스템', included: true }, + { name: '오프라인 지원', description: '인터넷 연결 없이도 기본 기능 사용 가능', included: false }, + { name: '앱스토어 등록', description: '앱스토어 및 플레이스토어 등록 지원', included: true } + ], + pricing: { + basePrice: 800000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 800000, max: 8000000 } + }, + estimatedTime: { min: 4, max: 16, unit: 'weeks' }, + featured: true, + isActive: true, + order: 2, + tags: ['React Native', 'Flutter', 'iOS', 'Android', 'Swift', 'Kotlin', 'JavaScript'] + }, + { + name: 'UI/UX 디자인', + description: '사용자 중심의 직관적이고 아름다운 인터페이스를 디자인합니다. 사용자 경험 연구와 프로토타이핑을 통해 최적의 디자인 솔루션을 제공합니다.', + shortDescription: '사용자 중심의 직관적이고 아름다운 인터페이스 디자인', + icon: 'fas fa-palette', + category: 'design', + features: [ + { name: '사용자 경험 연구', description: '타겟 사용자 분석 및 페르소나 설정', included: true }, + { name: '와이어프레임', description: '정보 구조와 레이아웃 설계', included: true }, + { name: '프로토타이핑', description: '인터랙티브 프로토타입 제작', included: true }, + { name: '비주얼 디자인', description: '브랜드에 맞는 시각적 디자인', included: true }, + { name: '디자인 시스템', description: '일관된 디자인을 위한 가이드라인', included: false } + ], + pricing: { + basePrice: 300000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 300000, max: 2000000 } + }, + estimatedTime: { min: 1, max: 6, unit: 'weeks' }, + featured: true, + isActive: true, + order: 3, + tags: ['Figma', 'Sketch', 'Adobe XD', 'Photoshop', 'Illustrator', 'Prototyping'] + }, + { + name: '디지털 마케팅', + description: 'SEO, 소셜미디어 마케팅, 온라인 광고를 통해 디지털 마케팅 전략을 수립하고 실행합니다. 데이터 분석을 통한 지속적인 최적화를 제공합니다.', + shortDescription: 'SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅', + icon: 'fas fa-chart-line', + category: 'marketing', + features: [ + { name: 'SEO 최적화', description: '검색엔진 상위 노출을 위한 최적화', included: true }, + { name: '소셜미디어 관리', description: '페이스북, 인스타그램, 유튜브 관리', included: true }, + { name: '구글 광고', description: '구글 애즈를 통한 타겟 광고', included: true }, + { name: '콘텐츠 마케팅', description: '블로그 및 콘텐츠 제작', included: false }, + { name: '분석 및 리포팅', description: '마케팅 성과 분석 및 보고서', included: true } + ], + pricing: { + basePrice: 200000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 200000, max: 1500000 } + }, + estimatedTime: { min: 2, max: 12, unit: 'weeks' }, + featured: true, + isActive: true, + order: 4, + tags: ['SEO', 'Google Ads', 'Facebook Ads', 'Analytics', 'Social Media', 'Content Marketing'] + }, + { + name: '브랜딩', + description: '기업의 정체성을 반영하는 로고, 브랜드 가이드라인, 마케팅 자료를 디자인합니다. 일관된 브랜드 이미지로 브랜드 가치를 높입니다.', + shortDescription: '로고, 브랜드 가이드라인, 마케팅 자료 디자인', + icon: 'fas fa-copyright', + category: 'design', + features: [ + { name: '로고 디자인', description: '기업 정체성을 반영한 로고 제작', included: true }, + { name: '브랜드 가이드라인', description: '색상, 폰트, 사용법 가이드', included: true }, + { name: '명함 디자인', description: '브랜드에 맞는 명함 디자인', included: true }, + { name: '브로슈어 디자인', description: '회사 소개 브로슈어 제작', included: false }, + { name: '웹 브랜딩', description: '웹사이트 브랜딩 요소 적용', included: false } + ], + pricing: { + basePrice: 400000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 400000, max: 2500000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: false, + isActive: true, + order: 5, + tags: ['Logo Design', 'Brand Identity', 'Graphic Design', 'Corporate Design'] + }, + { + name: '기술 컨설팅', + description: '기업의 디지털 전환과 기술 도입을 위한 전문적인 컨설팅을 제공합니다. 기술 아키텍처 설계부터 구현 전략까지 포괄적으로 지원합니다.', + shortDescription: '디지털 전환 및 기술 도입을 위한 전문 컨설팅', + icon: 'fas fa-lightbulb', + category: 'consulting', + features: [ + { name: '기술 분석', description: '현재 기술 스택 분석 및 개선안 제시', included: true }, + { name: '아키텍처 설계', description: '확장 가능한 시스템 아키텍처 설계', included: true }, + { name: '개발 프로세스 개선', description: '효율적인 개발 워크플로우 구축', included: true }, + { name: '팀 교육', description: '개발팀 대상 기술 교육', included: false }, + { name: '지속적인 지원', description: '프로젝트 완료 후 지속적인 기술 지원', included: false } + ], + pricing: { + basePrice: 150000, + currency: 'KRW', + priceType: 'hourly', + priceRange: { min: 150000, max: 500000 } + }, + estimatedTime: { min: 1, max: 4, unit: 'weeks' }, + featured: false, + isActive: true, + order: 6, + tags: ['Architecture', 'Strategy', 'Process', 'Training', 'Consultation'] + } + ]; + + await Service.insertMany(services); + console.log('✅ Sample services created successfully'); + } catch (error) { + console.error('❌ Error creating sample services:', error); + throw error; + } +} + +async function createSamplePortfolio() { + try { + const existingPortfolio = await Portfolio.countDocuments(); + + if (existingPortfolio > 0) { + console.log('🎨 Portfolio items already exist, skipping...'); + return; + } + + const portfolioItems = [ + { + title: 'E-commerce 플랫폼', + description: '현대적인 온라인 쇼핑몰 플랫폼을 개발했습니다. React와 Node.js를 활용하여 높은 성능과 사용자 경험을 제공하며, 결제 시스템과 재고 관리 기능을 포함합니다.', + shortDescription: '반응형 온라인 쇼핑몰 플랫폼 개발', + category: 'web-development', + technologies: ['React', 'Node.js', 'MongoDB', 'Express', 'Stripe', 'AWS'], + images: [ + { url: '/images/portfolio/ecommerce-1.jpg', alt: 'E-commerce 메인페이지', isPrimary: true }, + { url: '/images/portfolio/ecommerce-2.jpg', alt: '상품 상세페이지', isPrimary: false }, + { url: '/images/portfolio/ecommerce-3.jpg', alt: '장바구니 페이지', isPrimary: false } + ], + clientName: '패션 브랜드 ABC', + projectUrl: 'https://example-ecommerce.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-01-15'), + completedAt: new Date('2024-01-10'), + isPublished: true, + viewCount: 150, + likes: 25, + order: 1, + seo: { + metaTitle: 'E-commerce 플랫폼 개발 프로젝트', + metaDescription: '현대적인 온라인 쇼핑몰 플랫폼 개발 사례', + keywords: ['E-commerce', 'React', 'Node.js', '온라인쇼핑몰'] + } + }, + { + title: '모바일 피트니스 앱', + description: 'React Native를 사용하여 크로스플랫폼 피트니스 애플리케이션을 개발했습니다. 운동 계획, 칼로리 추적, 소셜 기능을 포함하여 사용자들의 건강한 라이프스타일을 지원합니다.', + shortDescription: '건강 관리를 위한 크로스플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['React Native', 'Redux', 'Firebase', 'Node.js', 'PostgreSQL'], + images: [ + { url: '/images/portfolio/fitness-1.jpg', alt: '피트니스 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/fitness-2.jpg', alt: '운동 계획 화면', isPrimary: false }, + { url: '/images/portfolio/fitness-3.jpg', alt: '통계 화면', isPrimary: false } + ], + clientName: '헬스케어 스타트업 FIT', + status: 'completed', + featured: true, + publishedAt: new Date('2024-02-20'), + completedAt: new Date('2024-02-15'), + isPublished: true, + viewCount: 200, + likes: 35, + order: 2, + seo: { + metaTitle: '모바일 피트니스 앱 개발 프로젝트', + metaDescription: 'React Native로 개발한 크로스플랫폼 피트니스 앱', + keywords: ['Mobile App', 'React Native', 'Fitness', '헬스케어'] + } + }, + { + title: '기업 웹사이트 리뉴얼', + description: '기업의 브랜드 아이덴티티를 반영한 웹사이트 리뉴얼 프로젝트입니다. 사용자 경험을 개선하고 모던한 디자인을 적용하여 브랜드 가치를 높였습니다.', + shortDescription: '브랜드 아이덴티티를 반영한 기업 웹사이트 리뉴얼', + category: 'ui-ux-design', + technologies: ['Figma', 'React', 'Sass', 'Framer Motion', 'Contentful'], + images: [ + { url: '/images/portfolio/corporate-1.jpg', alt: '기업 웹사이트 메인페이지', isPrimary: true }, + { url: '/images/portfolio/corporate-2.jpg', alt: '회사소개 페이지', isPrimary: false }, + { url: '/images/portfolio/corporate-3.jpg', alt: '서비스 페이지', isPrimary: false } + ], + clientName: '기술 기업 TechCorp', + projectUrl: 'https://example-corp.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-03-10'), + completedAt: new Date('2024-03-05'), + isPublished: true, + viewCount: 120, + likes: 18, + order: 3, + seo: { + metaTitle: '기업 웹사이트 리뉴얼 프로젝트', + metaDescription: '모던한 디자인과 향상된 UX의 기업 웹사이트', + keywords: ['Web Design', 'Corporate', 'UI/UX', '웹사이트리뉴얼'] + } + }, + { + title: '레스토랑 예약 시스템', + description: '레스토랑을 위한 온라인 예약 시스템을 개발했습니다. 실시간 테이블 현황, 예약 관리, 고객 관리 기능을 포함하여 레스토랑 운영 효율성을 높였습니다.', + shortDescription: '실시간 테이블 예약 및 관리 시스템', + category: 'web-development', + technologies: ['Vue.js', 'Laravel', 'MySQL', 'Socket.io', 'Bootstrap'], + images: [ + { url: '/images/portfolio/restaurant-1.jpg', alt: '예약 시스템 메인', isPrimary: true }, + { url: '/images/portfolio/restaurant-2.jpg', alt: '예약 현황 관리', isPrimary: false } + ], + clientName: '레스토랑 델리셔스', + status: 'completed', + featured: false, + publishedAt: new Date('2024-04-05'), + completedAt: new Date('2024-04-01'), + isPublished: true, + viewCount: 85, + likes: 12, + order: 4, + seo: { + metaTitle: '레스토랑 예약 시스템 개발', + metaDescription: '실시간 테이블 예약 및 관리 웹 시스템', + keywords: ['Reservation System', 'Vue.js', 'Laravel', '예약시스템'] + } + }, + { + title: '교육 플랫폼 앱', + description: '온라인 교육을 위한 모바일 애플리케이션입니다. 동영상 강의, 퀴즈, 진도 관리 기능을 포함하여 효과적인 학습 환경을 제공합니다.', + shortDescription: '온라인 학습을 위한 교육 플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['Flutter', 'Dart', 'Firebase', 'FFmpeg', 'AWS S3'], + images: [ + { url: '/images/portfolio/education-1.jpg', alt: '교육 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/education-2.jpg', alt: '강의 재생 화면', isPrimary: false } + ], + clientName: '온라인 교육 기업 EduTech', + status: 'completed', + featured: false, + publishedAt: new Date('2024-05-12'), + completedAt: new Date('2024-05-08'), + isPublished: true, + viewCount: 95, + likes: 20, + order: 5, + seo: { + metaTitle: '교육 플랫폼 모바일 앱 개발', + metaDescription: 'Flutter로 개발한 온라인 교육 모바일 앱', + keywords: ['Education App', 'Flutter', 'E-learning', '교육앱'] + } + }, + { + title: 'IoT 대시보드', + description: 'IoT 디바이스들을 모니터링하고 제어할 수 있는 웹 대시보드를 개발했습니다. 실시간 데이터 시각화와 알림 기능을 포함합니다.', + shortDescription: 'IoT 디바이스 모니터링 및 제어 웹 대시보드', + category: 'web-development', + technologies: ['React', 'D3.js', 'Node.js', 'MQTT', 'InfluxDB', 'Docker'], + images: [ + { url: '/images/portfolio/iot-1.jpg', alt: 'IoT 대시보드 메인', isPrimary: true } + ], + clientName: 'IoT 솔루션 기업 SmartDevice', + status: 'in-progress', + featured: false, + publishedAt: new Date('2024-06-01'), + isPublished: true, + viewCount: 45, + likes: 8, + order: 6, + seo: { + metaTitle: 'IoT 대시보드 개발 프로젝트', + metaDescription: '실시간 IoT 디바이스 모니터링 웹 대시보드', + keywords: ['IoT', 'Dashboard', 'Real-time', 'Monitoring'] + } + } + ]; + + await Portfolio.insertMany(portfolioItems); + console.log('✅ Sample portfolio items created successfully'); + } catch (error) { + console.error('❌ Error creating sample portfolio:', error); + throw error; + } +} + +async function createSiteSettings() { + try { + const existingSettings = await SiteSettings.findOne(); + + if (existingSettings) { + console.log('⚙️ Site settings already exist, skipping...'); + return; + } + + const settings = new SiteSettings({ + siteName: 'SmartSolTech', + siteDescription: '혁신적인 기술 솔루션으로 비즈니스의 성장을 지원합니다', + logo: '/images/logo.png', + favicon: '/images/favicon.ico', + contact: { + email: 'info@smartsoltech.kr', + phone: '+82-10-1234-5678', + address: 'Seoul, South Korea' + }, + social: { + facebook: 'https://facebook.com/smartsoltech', + twitter: 'https://twitter.com/smartsoltech', + linkedin: 'https://linkedin.com/company/smartsoltech', + instagram: 'https://instagram.com/smartsoltech', + github: 'https://github.com/smartsoltech' + }, + telegram: { + isEnabled: false + }, + seo: { + metaTitle: 'SmartSolTech - 혁신적인 기술 솔루션', + metaDescription: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅 전문 기업. 한국에서 최고의 기술 솔루션을 제공합니다.', + keywords: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅, 기술 솔루션, 한국, SmartSolTech' + }, + hero: { + title: 'Smart Technology Solutions', + subtitle: '혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비즈니스의 디지털 전환을 이끌어갑니다', + backgroundImage: '/images/hero-bg.jpg', + ctaText: '프로젝트 시작하기', + ctaLink: '/contact' + }, + about: { + title: 'SmartSolTech 소개', + description: '우리는 최신 기술과 창의적인 아이디어로 고객의 비즈니스 성장을 지원하는 전문 개발팀입니다. 웹 개발부터 모바일 앱, UI/UX 디자인까지 포괄적인 디지털 솔루션을 제공합니다.', + image: '/images/about.jpg' + }, + maintenance: { + isEnabled: false, + message: '현재 시스템 점검 중입니다. 잠시 후 다시 접속해 주세요.' + } + }); + + await settings.save(); + console.log('✅ Site settings created successfully'); + } catch (error) { + console.error('❌ Error creating site settings:', error); + throw error; + } +} + +// Run the initialization +if (require.main === module) { + initializeDatabase(); +} + +module.exports = { + initializeDatabase, + createAdminUser, + createSampleServices, + createSamplePortfolio, + createSiteSettings +}; \ No newline at end of file diff --git a/.history/scripts/init-db_20251019202251.js b/.history/scripts/init-db_20251019202251.js new file mode 100644 index 0000000..a21c857 --- /dev/null +++ b/.history/scripts/init-db_20251019202251.js @@ -0,0 +1,496 @@ +#!/usr/bin/env node + +/** + * Database initialization script for SmartSolTech + * Creates initial admin user and sample data for PostgreSQL + */ + +const { sequelize } = require('../config/database'); +require('dotenv').config(); + +// Import models +const { User, Service, Portfolio, SiteSettings } = require('../models'); + +// Configuration +const ADMIN_EMAIL = process.env.ADMIN_EMAIL || 'admin@smartsoltech.kr'; +const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'admin123456'; + +async function initializeDatabase() { + try { + console.log('🔄 Connecting to PostgreSQL...'); + await sequelize.authenticate(); + console.log('✅ Connected to PostgreSQL'); + + // Sync database (create tables) + console.log('🔄 Syncing database schema...'); + await sequelize.sync({ force: false }); + console.log('✅ Database schema synchronized'); + + // Create admin user + await createAdminUser(); + + // Create sample services + await createSampleServices(); + + // Create sample portfolio items + await createSamplePortfolio(); + + // Create site settings + await createSiteSettings(); + + console.log('🎉 Database initialization completed successfully!'); + console.log(`📧 Admin login: ${ADMIN_EMAIL}`); + console.log(`🔑 Admin password: ${ADMIN_PASSWORD}`); + console.log('⚠️ Please change the admin password after first login!'); + + } catch (error) { + console.error('❌ Database initialization failed:', error); + process.exit(1); + } finally { + await sequelize.close(); + console.log('🔌 Database connection closed'); + process.exit(0); + } +} + +async function createAdminUser() { + try { + const existingAdmin = await User.findOne({ + where: { email: ADMIN_EMAIL } + }); + + if (existingAdmin) { + console.log('👤 Admin user already exists, skipping...'); + return; + } + + const adminUser = await User.create({ + name: 'Administrator', + email: ADMIN_EMAIL, + password: ADMIN_PASSWORD, + role: 'admin', + isActive: true + }); + + console.log('✅ Admin user created successfully'); + } catch (error) { + console.error('❌ Error creating admin user:', error); + throw error; + } +} + +async function createSampleServices() { + try { + const existingServices = await Service.count(); + + if (existingServices > 0) { + console.log('🛠️ Services already exist, skipping...'); + return; + } + + const services = [ + { + name: '웹 개발', + description: '현대적이고 반응형인 웹사이트와 웹 애플리케이션을 개발합니다. React, Node.js, MongoDB 등 최신 기술 스택을 활용하여 성능과 사용자 경험을 최적화합니다.', + shortDescription: '현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발', + icon: 'fas fa-code', + category: 'development', + features: [ + { name: '반응형 디자인', description: '모든 디바이스에서 완벽하게 작동', included: true }, + { name: 'SEO 최적화', description: '검색엔진 최적화로 더 많은 방문자 유치', included: true }, + { name: '성능 최적화', description: '빠른 로딩 속도와 원활한 사용자 경험', included: true }, + { name: '보안 강화', description: '최신 보안 기술로 안전한 웹사이트', included: true }, + { name: '유지보수', description: '6개월 무료 유지보수 지원', included: true } + ], + pricing: { + basePrice: 500000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 500000, max: 5000000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: true, + isActive: true, + order: 1, + tags: ['React', 'Node.js', 'MongoDB', 'Express', 'JavaScript', 'HTML', 'CSS'] + }, + { + name: '모바일 앱 개발', + description: 'iOS와 Android 플랫폼을 위한 네이티브 및 크로스플랫폼 모바일 애플리케이션을 개발합니다. React Native, Flutter 등을 활용하여 효율적인 개발을 진행합니다.', + shortDescription: 'iOS/Android 네이티브 및 크로스플랫폼 앱 개발', + icon: 'fas fa-mobile-alt', + category: 'development', + features: [ + { name: '크로스플랫폼 개발', description: 'iOS와 Android 동시 지원', included: true }, + { name: '네이티브 성능', description: '최적화된 성능과 사용자 경험', included: true }, + { name: '푸시 알림', description: '실시간 푸시 알림 시스템', included: true }, + { name: '오프라인 지원', description: '인터넷 연결 없이도 기본 기능 사용 가능', included: false }, + { name: '앱스토어 등록', description: '앱스토어 및 플레이스토어 등록 지원', included: true } + ], + pricing: { + basePrice: 800000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 800000, max: 8000000 } + }, + estimatedTime: { min: 4, max: 16, unit: 'weeks' }, + featured: true, + isActive: true, + order: 2, + tags: ['React Native', 'Flutter', 'iOS', 'Android', 'Swift', 'Kotlin', 'JavaScript'] + }, + { + name: 'UI/UX 디자인', + description: '사용자 중심의 직관적이고 아름다운 인터페이스를 디자인합니다. 사용자 경험 연구와 프로토타이핑을 통해 최적의 디자인 솔루션을 제공합니다.', + shortDescription: '사용자 중심의 직관적이고 아름다운 인터페이스 디자인', + icon: 'fas fa-palette', + category: 'design', + features: [ + { name: '사용자 경험 연구', description: '타겟 사용자 분석 및 페르소나 설정', included: true }, + { name: '와이어프레임', description: '정보 구조와 레이아웃 설계', included: true }, + { name: '프로토타이핑', description: '인터랙티브 프로토타입 제작', included: true }, + { name: '비주얼 디자인', description: '브랜드에 맞는 시각적 디자인', included: true }, + { name: '디자인 시스템', description: '일관된 디자인을 위한 가이드라인', included: false } + ], + pricing: { + basePrice: 300000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 300000, max: 2000000 } + }, + estimatedTime: { min: 1, max: 6, unit: 'weeks' }, + featured: true, + isActive: true, + order: 3, + tags: ['Figma', 'Sketch', 'Adobe XD', 'Photoshop', 'Illustrator', 'Prototyping'] + }, + { + name: '디지털 마케팅', + description: 'SEO, 소셜미디어 마케팅, 온라인 광고를 통해 디지털 마케팅 전략을 수립하고 실행합니다. 데이터 분석을 통한 지속적인 최적화를 제공합니다.', + shortDescription: 'SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅', + icon: 'fas fa-chart-line', + category: 'marketing', + features: [ + { name: 'SEO 최적화', description: '검색엔진 상위 노출을 위한 최적화', included: true }, + { name: '소셜미디어 관리', description: '페이스북, 인스타그램, 유튜브 관리', included: true }, + { name: '구글 광고', description: '구글 애즈를 통한 타겟 광고', included: true }, + { name: '콘텐츠 마케팅', description: '블로그 및 콘텐츠 제작', included: false }, + { name: '분석 및 리포팅', description: '마케팅 성과 분석 및 보고서', included: true } + ], + pricing: { + basePrice: 200000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 200000, max: 1500000 } + }, + estimatedTime: { min: 2, max: 12, unit: 'weeks' }, + featured: true, + isActive: true, + order: 4, + tags: ['SEO', 'Google Ads', 'Facebook Ads', 'Analytics', 'Social Media', 'Content Marketing'] + }, + { + name: '브랜딩', + description: '기업의 정체성을 반영하는 로고, 브랜드 가이드라인, 마케팅 자료를 디자인합니다. 일관된 브랜드 이미지로 브랜드 가치를 높입니다.', + shortDescription: '로고, 브랜드 가이드라인, 마케팅 자료 디자인', + icon: 'fas fa-copyright', + category: 'design', + features: [ + { name: '로고 디자인', description: '기업 정체성을 반영한 로고 제작', included: true }, + { name: '브랜드 가이드라인', description: '색상, 폰트, 사용법 가이드', included: true }, + { name: '명함 디자인', description: '브랜드에 맞는 명함 디자인', included: true }, + { name: '브로슈어 디자인', description: '회사 소개 브로슈어 제작', included: false }, + { name: '웹 브랜딩', description: '웹사이트 브랜딩 요소 적용', included: false } + ], + pricing: { + basePrice: 400000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 400000, max: 2500000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: false, + isActive: true, + order: 5, + tags: ['Logo Design', 'Brand Identity', 'Graphic Design', 'Corporate Design'] + }, + { + name: '기술 컨설팅', + description: '기업의 디지털 전환과 기술 도입을 위한 전문적인 컨설팅을 제공합니다. 기술 아키텍처 설계부터 구현 전략까지 포괄적으로 지원합니다.', + shortDescription: '디지털 전환 및 기술 도입을 위한 전문 컨설팅', + icon: 'fas fa-lightbulb', + category: 'consulting', + features: [ + { name: '기술 분석', description: '현재 기술 스택 분석 및 개선안 제시', included: true }, + { name: '아키텍처 설계', description: '확장 가능한 시스템 아키텍처 설계', included: true }, + { name: '개발 프로세스 개선', description: '효율적인 개발 워크플로우 구축', included: true }, + { name: '팀 교육', description: '개발팀 대상 기술 교육', included: false }, + { name: '지속적인 지원', description: '프로젝트 완료 후 지속적인 기술 지원', included: false } + ], + pricing: { + basePrice: 150000, + currency: 'KRW', + priceType: 'hourly', + priceRange: { min: 150000, max: 500000 } + }, + estimatedTime: { min: 1, max: 4, unit: 'weeks' }, + featured: false, + isActive: true, + order: 6, + tags: ['Architecture', 'Strategy', 'Process', 'Training', 'Consultation'] + } + ]; + + await Service.bulkCreate(services); + console.log('✅ Sample services created successfully'); + } catch (error) { + console.error('❌ Error creating sample services:', error); + throw error; + } +} + +async function createSamplePortfolio() { + try { + const existingPortfolio = await Portfolio.countDocuments(); + + if (existingPortfolio > 0) { + console.log('🎨 Portfolio items already exist, skipping...'); + return; + } + + const portfolioItems = [ + { + title: 'E-commerce 플랫폼', + description: '현대적인 온라인 쇼핑몰 플랫폼을 개발했습니다. React와 Node.js를 활용하여 높은 성능과 사용자 경험을 제공하며, 결제 시스템과 재고 관리 기능을 포함합니다.', + shortDescription: '반응형 온라인 쇼핑몰 플랫폼 개발', + category: 'web-development', + technologies: ['React', 'Node.js', 'MongoDB', 'Express', 'Stripe', 'AWS'], + images: [ + { url: '/images/portfolio/ecommerce-1.jpg', alt: 'E-commerce 메인페이지', isPrimary: true }, + { url: '/images/portfolio/ecommerce-2.jpg', alt: '상품 상세페이지', isPrimary: false }, + { url: '/images/portfolio/ecommerce-3.jpg', alt: '장바구니 페이지', isPrimary: false } + ], + clientName: '패션 브랜드 ABC', + projectUrl: 'https://example-ecommerce.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-01-15'), + completedAt: new Date('2024-01-10'), + isPublished: true, + viewCount: 150, + likes: 25, + order: 1, + seo: { + metaTitle: 'E-commerce 플랫폼 개발 프로젝트', + metaDescription: '현대적인 온라인 쇼핑몰 플랫폼 개발 사례', + keywords: ['E-commerce', 'React', 'Node.js', '온라인쇼핑몰'] + } + }, + { + title: '모바일 피트니스 앱', + description: 'React Native를 사용하여 크로스플랫폼 피트니스 애플리케이션을 개발했습니다. 운동 계획, 칼로리 추적, 소셜 기능을 포함하여 사용자들의 건강한 라이프스타일을 지원합니다.', + shortDescription: '건강 관리를 위한 크로스플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['React Native', 'Redux', 'Firebase', 'Node.js', 'PostgreSQL'], + images: [ + { url: '/images/portfolio/fitness-1.jpg', alt: '피트니스 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/fitness-2.jpg', alt: '운동 계획 화면', isPrimary: false }, + { url: '/images/portfolio/fitness-3.jpg', alt: '통계 화면', isPrimary: false } + ], + clientName: '헬스케어 스타트업 FIT', + status: 'completed', + featured: true, + publishedAt: new Date('2024-02-20'), + completedAt: new Date('2024-02-15'), + isPublished: true, + viewCount: 200, + likes: 35, + order: 2, + seo: { + metaTitle: '모바일 피트니스 앱 개발 프로젝트', + metaDescription: 'React Native로 개발한 크로스플랫폼 피트니스 앱', + keywords: ['Mobile App', 'React Native', 'Fitness', '헬스케어'] + } + }, + { + title: '기업 웹사이트 리뉴얼', + description: '기업의 브랜드 아이덴티티를 반영한 웹사이트 리뉴얼 프로젝트입니다. 사용자 경험을 개선하고 모던한 디자인을 적용하여 브랜드 가치를 높였습니다.', + shortDescription: '브랜드 아이덴티티를 반영한 기업 웹사이트 리뉴얼', + category: 'ui-ux-design', + technologies: ['Figma', 'React', 'Sass', 'Framer Motion', 'Contentful'], + images: [ + { url: '/images/portfolio/corporate-1.jpg', alt: '기업 웹사이트 메인페이지', isPrimary: true }, + { url: '/images/portfolio/corporate-2.jpg', alt: '회사소개 페이지', isPrimary: false }, + { url: '/images/portfolio/corporate-3.jpg', alt: '서비스 페이지', isPrimary: false } + ], + clientName: '기술 기업 TechCorp', + projectUrl: 'https://example-corp.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-03-10'), + completedAt: new Date('2024-03-05'), + isPublished: true, + viewCount: 120, + likes: 18, + order: 3, + seo: { + metaTitle: '기업 웹사이트 리뉴얼 프로젝트', + metaDescription: '모던한 디자인과 향상된 UX의 기업 웹사이트', + keywords: ['Web Design', 'Corporate', 'UI/UX', '웹사이트리뉴얼'] + } + }, + { + title: '레스토랑 예약 시스템', + description: '레스토랑을 위한 온라인 예약 시스템을 개발했습니다. 실시간 테이블 현황, 예약 관리, 고객 관리 기능을 포함하여 레스토랑 운영 효율성을 높였습니다.', + shortDescription: '실시간 테이블 예약 및 관리 시스템', + category: 'web-development', + technologies: ['Vue.js', 'Laravel', 'MySQL', 'Socket.io', 'Bootstrap'], + images: [ + { url: '/images/portfolio/restaurant-1.jpg', alt: '예약 시스템 메인', isPrimary: true }, + { url: '/images/portfolio/restaurant-2.jpg', alt: '예약 현황 관리', isPrimary: false } + ], + clientName: '레스토랑 델리셔스', + status: 'completed', + featured: false, + publishedAt: new Date('2024-04-05'), + completedAt: new Date('2024-04-01'), + isPublished: true, + viewCount: 85, + likes: 12, + order: 4, + seo: { + metaTitle: '레스토랑 예약 시스템 개발', + metaDescription: '실시간 테이블 예약 및 관리 웹 시스템', + keywords: ['Reservation System', 'Vue.js', 'Laravel', '예약시스템'] + } + }, + { + title: '교육 플랫폼 앱', + description: '온라인 교육을 위한 모바일 애플리케이션입니다. 동영상 강의, 퀴즈, 진도 관리 기능을 포함하여 효과적인 학습 환경을 제공합니다.', + shortDescription: '온라인 학습을 위한 교육 플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['Flutter', 'Dart', 'Firebase', 'FFmpeg', 'AWS S3'], + images: [ + { url: '/images/portfolio/education-1.jpg', alt: '교육 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/education-2.jpg', alt: '강의 재생 화면', isPrimary: false } + ], + clientName: '온라인 교육 기업 EduTech', + status: 'completed', + featured: false, + publishedAt: new Date('2024-05-12'), + completedAt: new Date('2024-05-08'), + isPublished: true, + viewCount: 95, + likes: 20, + order: 5, + seo: { + metaTitle: '교육 플랫폼 모바일 앱 개발', + metaDescription: 'Flutter로 개발한 온라인 교육 모바일 앱', + keywords: ['Education App', 'Flutter', 'E-learning', '교육앱'] + } + }, + { + title: 'IoT 대시보드', + description: 'IoT 디바이스들을 모니터링하고 제어할 수 있는 웹 대시보드를 개발했습니다. 실시간 데이터 시각화와 알림 기능을 포함합니다.', + shortDescription: 'IoT 디바이스 모니터링 및 제어 웹 대시보드', + category: 'web-development', + technologies: ['React', 'D3.js', 'Node.js', 'MQTT', 'InfluxDB', 'Docker'], + images: [ + { url: '/images/portfolio/iot-1.jpg', alt: 'IoT 대시보드 메인', isPrimary: true } + ], + clientName: 'IoT 솔루션 기업 SmartDevice', + status: 'in-progress', + featured: false, + publishedAt: new Date('2024-06-01'), + isPublished: true, + viewCount: 45, + likes: 8, + order: 6, + seo: { + metaTitle: 'IoT 대시보드 개발 프로젝트', + metaDescription: '실시간 IoT 디바이스 모니터링 웹 대시보드', + keywords: ['IoT', 'Dashboard', 'Real-time', 'Monitoring'] + } + } + ]; + + await Portfolio.insertMany(portfolioItems); + console.log('✅ Sample portfolio items created successfully'); + } catch (error) { + console.error('❌ Error creating sample portfolio:', error); + throw error; + } +} + +async function createSiteSettings() { + try { + const existingSettings = await SiteSettings.findOne(); + + if (existingSettings) { + console.log('⚙️ Site settings already exist, skipping...'); + return; + } + + const settings = new SiteSettings({ + siteName: 'SmartSolTech', + siteDescription: '혁신적인 기술 솔루션으로 비즈니스의 성장을 지원합니다', + logo: '/images/logo.png', + favicon: '/images/favicon.ico', + contact: { + email: 'info@smartsoltech.kr', + phone: '+82-10-1234-5678', + address: 'Seoul, South Korea' + }, + social: { + facebook: 'https://facebook.com/smartsoltech', + twitter: 'https://twitter.com/smartsoltech', + linkedin: 'https://linkedin.com/company/smartsoltech', + instagram: 'https://instagram.com/smartsoltech', + github: 'https://github.com/smartsoltech' + }, + telegram: { + isEnabled: false + }, + seo: { + metaTitle: 'SmartSolTech - 혁신적인 기술 솔루션', + metaDescription: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅 전문 기업. 한국에서 최고의 기술 솔루션을 제공합니다.', + keywords: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅, 기술 솔루션, 한국, SmartSolTech' + }, + hero: { + title: 'Smart Technology Solutions', + subtitle: '혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비즈니스의 디지털 전환을 이끌어갑니다', + backgroundImage: '/images/hero-bg.jpg', + ctaText: '프로젝트 시작하기', + ctaLink: '/contact' + }, + about: { + title: 'SmartSolTech 소개', + description: '우리는 최신 기술과 창의적인 아이디어로 고객의 비즈니스 성장을 지원하는 전문 개발팀입니다. 웹 개발부터 모바일 앱, UI/UX 디자인까지 포괄적인 디지털 솔루션을 제공합니다.', + image: '/images/about.jpg' + }, + maintenance: { + isEnabled: false, + message: '현재 시스템 점검 중입니다. 잠시 후 다시 접속해 주세요.' + } + }); + + await settings.save(); + console.log('✅ Site settings created successfully'); + } catch (error) { + console.error('❌ Error creating site settings:', error); + throw error; + } +} + +// Run the initialization +if (require.main === module) { + initializeDatabase(); +} + +module.exports = { + initializeDatabase, + createAdminUser, + createSampleServices, + createSamplePortfolio, + createSiteSettings +}; \ No newline at end of file diff --git a/.history/scripts/init-db_20251019202258.js b/.history/scripts/init-db_20251019202258.js new file mode 100644 index 0000000..bd874b5 --- /dev/null +++ b/.history/scripts/init-db_20251019202258.js @@ -0,0 +1,496 @@ +#!/usr/bin/env node + +/** + * Database initialization script for SmartSolTech + * Creates initial admin user and sample data for PostgreSQL + */ + +const { sequelize } = require('../config/database'); +require('dotenv').config(); + +// Import models +const { User, Service, Portfolio, SiteSettings } = require('../models'); + +// Configuration +const ADMIN_EMAIL = process.env.ADMIN_EMAIL || 'admin@smartsoltech.kr'; +const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'admin123456'; + +async function initializeDatabase() { + try { + console.log('🔄 Connecting to PostgreSQL...'); + await sequelize.authenticate(); + console.log('✅ Connected to PostgreSQL'); + + // Sync database (create tables) + console.log('🔄 Syncing database schema...'); + await sequelize.sync({ force: false }); + console.log('✅ Database schema synchronized'); + + // Create admin user + await createAdminUser(); + + // Create sample services + await createSampleServices(); + + // Create sample portfolio items + await createSamplePortfolio(); + + // Create site settings + await createSiteSettings(); + + console.log('🎉 Database initialization completed successfully!'); + console.log(`📧 Admin login: ${ADMIN_EMAIL}`); + console.log(`🔑 Admin password: ${ADMIN_PASSWORD}`); + console.log('⚠️ Please change the admin password after first login!'); + + } catch (error) { + console.error('❌ Database initialization failed:', error); + process.exit(1); + } finally { + await sequelize.close(); + console.log('🔌 Database connection closed'); + process.exit(0); + } +} + +async function createAdminUser() { + try { + const existingAdmin = await User.findOne({ + where: { email: ADMIN_EMAIL } + }); + + if (existingAdmin) { + console.log('👤 Admin user already exists, skipping...'); + return; + } + + const adminUser = await User.create({ + name: 'Administrator', + email: ADMIN_EMAIL, + password: ADMIN_PASSWORD, + role: 'admin', + isActive: true + }); + + console.log('✅ Admin user created successfully'); + } catch (error) { + console.error('❌ Error creating admin user:', error); + throw error; + } +} + +async function createSampleServices() { + try { + const existingServices = await Service.count(); + + if (existingServices > 0) { + console.log('🛠️ Services already exist, skipping...'); + return; + } + + const services = [ + { + name: '웹 개발', + description: '현대적이고 반응형인 웹사이트와 웹 애플리케이션을 개발합니다. React, Node.js, MongoDB 등 최신 기술 스택을 활용하여 성능과 사용자 경험을 최적화합니다.', + shortDescription: '현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발', + icon: 'fas fa-code', + category: 'development', + features: [ + { name: '반응형 디자인', description: '모든 디바이스에서 완벽하게 작동', included: true }, + { name: 'SEO 최적화', description: '검색엔진 최적화로 더 많은 방문자 유치', included: true }, + { name: '성능 최적화', description: '빠른 로딩 속도와 원활한 사용자 경험', included: true }, + { name: '보안 강화', description: '최신 보안 기술로 안전한 웹사이트', included: true }, + { name: '유지보수', description: '6개월 무료 유지보수 지원', included: true } + ], + pricing: { + basePrice: 500000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 500000, max: 5000000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: true, + isActive: true, + order: 1, + tags: ['React', 'Node.js', 'MongoDB', 'Express', 'JavaScript', 'HTML', 'CSS'] + }, + { + name: '모바일 앱 개발', + description: 'iOS와 Android 플랫폼을 위한 네이티브 및 크로스플랫폼 모바일 애플리케이션을 개발합니다. React Native, Flutter 등을 활용하여 효율적인 개발을 진행합니다.', + shortDescription: 'iOS/Android 네이티브 및 크로스플랫폼 앱 개발', + icon: 'fas fa-mobile-alt', + category: 'development', + features: [ + { name: '크로스플랫폼 개발', description: 'iOS와 Android 동시 지원', included: true }, + { name: '네이티브 성능', description: '최적화된 성능과 사용자 경험', included: true }, + { name: '푸시 알림', description: '실시간 푸시 알림 시스템', included: true }, + { name: '오프라인 지원', description: '인터넷 연결 없이도 기본 기능 사용 가능', included: false }, + { name: '앱스토어 등록', description: '앱스토어 및 플레이스토어 등록 지원', included: true } + ], + pricing: { + basePrice: 800000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 800000, max: 8000000 } + }, + estimatedTime: { min: 4, max: 16, unit: 'weeks' }, + featured: true, + isActive: true, + order: 2, + tags: ['React Native', 'Flutter', 'iOS', 'Android', 'Swift', 'Kotlin', 'JavaScript'] + }, + { + name: 'UI/UX 디자인', + description: '사용자 중심의 직관적이고 아름다운 인터페이스를 디자인합니다. 사용자 경험 연구와 프로토타이핑을 통해 최적의 디자인 솔루션을 제공합니다.', + shortDescription: '사용자 중심의 직관적이고 아름다운 인터페이스 디자인', + icon: 'fas fa-palette', + category: 'design', + features: [ + { name: '사용자 경험 연구', description: '타겟 사용자 분석 및 페르소나 설정', included: true }, + { name: '와이어프레임', description: '정보 구조와 레이아웃 설계', included: true }, + { name: '프로토타이핑', description: '인터랙티브 프로토타입 제작', included: true }, + { name: '비주얼 디자인', description: '브랜드에 맞는 시각적 디자인', included: true }, + { name: '디자인 시스템', description: '일관된 디자인을 위한 가이드라인', included: false } + ], + pricing: { + basePrice: 300000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 300000, max: 2000000 } + }, + estimatedTime: { min: 1, max: 6, unit: 'weeks' }, + featured: true, + isActive: true, + order: 3, + tags: ['Figma', 'Sketch', 'Adobe XD', 'Photoshop', 'Illustrator', 'Prototyping'] + }, + { + name: '디지털 마케팅', + description: 'SEO, 소셜미디어 마케팅, 온라인 광고를 통해 디지털 마케팅 전략을 수립하고 실행합니다. 데이터 분석을 통한 지속적인 최적화를 제공합니다.', + shortDescription: 'SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅', + icon: 'fas fa-chart-line', + category: 'marketing', + features: [ + { name: 'SEO 최적화', description: '검색엔진 상위 노출을 위한 최적화', included: true }, + { name: '소셜미디어 관리', description: '페이스북, 인스타그램, 유튜브 관리', included: true }, + { name: '구글 광고', description: '구글 애즈를 통한 타겟 광고', included: true }, + { name: '콘텐츠 마케팅', description: '블로그 및 콘텐츠 제작', included: false }, + { name: '분석 및 리포팅', description: '마케팅 성과 분석 및 보고서', included: true } + ], + pricing: { + basePrice: 200000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 200000, max: 1500000 } + }, + estimatedTime: { min: 2, max: 12, unit: 'weeks' }, + featured: true, + isActive: true, + order: 4, + tags: ['SEO', 'Google Ads', 'Facebook Ads', 'Analytics', 'Social Media', 'Content Marketing'] + }, + { + name: '브랜딩', + description: '기업의 정체성을 반영하는 로고, 브랜드 가이드라인, 마케팅 자료를 디자인합니다. 일관된 브랜드 이미지로 브랜드 가치를 높입니다.', + shortDescription: '로고, 브랜드 가이드라인, 마케팅 자료 디자인', + icon: 'fas fa-copyright', + category: 'design', + features: [ + { name: '로고 디자인', description: '기업 정체성을 반영한 로고 제작', included: true }, + { name: '브랜드 가이드라인', description: '색상, 폰트, 사용법 가이드', included: true }, + { name: '명함 디자인', description: '브랜드에 맞는 명함 디자인', included: true }, + { name: '브로슈어 디자인', description: '회사 소개 브로슈어 제작', included: false }, + { name: '웹 브랜딩', description: '웹사이트 브랜딩 요소 적용', included: false } + ], + pricing: { + basePrice: 400000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 400000, max: 2500000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: false, + isActive: true, + order: 5, + tags: ['Logo Design', 'Brand Identity', 'Graphic Design', 'Corporate Design'] + }, + { + name: '기술 컨설팅', + description: '기업의 디지털 전환과 기술 도입을 위한 전문적인 컨설팅을 제공합니다. 기술 아키텍처 설계부터 구현 전략까지 포괄적으로 지원합니다.', + shortDescription: '디지털 전환 및 기술 도입을 위한 전문 컨설팅', + icon: 'fas fa-lightbulb', + category: 'consulting', + features: [ + { name: '기술 분석', description: '현재 기술 스택 분석 및 개선안 제시', included: true }, + { name: '아키텍처 설계', description: '확장 가능한 시스템 아키텍처 설계', included: true }, + { name: '개발 프로세스 개선', description: '효율적인 개발 워크플로우 구축', included: true }, + { name: '팀 교육', description: '개발팀 대상 기술 교육', included: false }, + { name: '지속적인 지원', description: '프로젝트 완료 후 지속적인 기술 지원', included: false } + ], + pricing: { + basePrice: 150000, + currency: 'KRW', + priceType: 'hourly', + priceRange: { min: 150000, max: 500000 } + }, + estimatedTime: { min: 1, max: 4, unit: 'weeks' }, + featured: false, + isActive: true, + order: 6, + tags: ['Architecture', 'Strategy', 'Process', 'Training', 'Consultation'] + } + ]; + + await Service.bulkCreate(services); + console.log('✅ Sample services created successfully'); + } catch (error) { + console.error('❌ Error creating sample services:', error); + throw error; + } +} + +async function createSamplePortfolio() { + try { + const existingPortfolio = await Portfolio.count(); + + if (existingPortfolio > 0) { + console.log('🎨 Portfolio items already exist, skipping...'); + return; + } + + const portfolioItems = [ + { + title: 'E-commerce 플랫폼', + description: '현대적인 온라인 쇼핑몰 플랫폼을 개발했습니다. React와 Node.js를 활용하여 높은 성능과 사용자 경험을 제공하며, 결제 시스템과 재고 관리 기능을 포함합니다.', + shortDescription: '반응형 온라인 쇼핑몰 플랫폼 개발', + category: 'web-development', + technologies: ['React', 'Node.js', 'MongoDB', 'Express', 'Stripe', 'AWS'], + images: [ + { url: '/images/portfolio/ecommerce-1.jpg', alt: 'E-commerce 메인페이지', isPrimary: true }, + { url: '/images/portfolio/ecommerce-2.jpg', alt: '상품 상세페이지', isPrimary: false }, + { url: '/images/portfolio/ecommerce-3.jpg', alt: '장바구니 페이지', isPrimary: false } + ], + clientName: '패션 브랜드 ABC', + projectUrl: 'https://example-ecommerce.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-01-15'), + completedAt: new Date('2024-01-10'), + isPublished: true, + viewCount: 150, + likes: 25, + order: 1, + seo: { + metaTitle: 'E-commerce 플랫폼 개발 프로젝트', + metaDescription: '현대적인 온라인 쇼핑몰 플랫폼 개발 사례', + keywords: ['E-commerce', 'React', 'Node.js', '온라인쇼핑몰'] + } + }, + { + title: '모바일 피트니스 앱', + description: 'React Native를 사용하여 크로스플랫폼 피트니스 애플리케이션을 개발했습니다. 운동 계획, 칼로리 추적, 소셜 기능을 포함하여 사용자들의 건강한 라이프스타일을 지원합니다.', + shortDescription: '건강 관리를 위한 크로스플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['React Native', 'Redux', 'Firebase', 'Node.js', 'PostgreSQL'], + images: [ + { url: '/images/portfolio/fitness-1.jpg', alt: '피트니스 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/fitness-2.jpg', alt: '운동 계획 화면', isPrimary: false }, + { url: '/images/portfolio/fitness-3.jpg', alt: '통계 화면', isPrimary: false } + ], + clientName: '헬스케어 스타트업 FIT', + status: 'completed', + featured: true, + publishedAt: new Date('2024-02-20'), + completedAt: new Date('2024-02-15'), + isPublished: true, + viewCount: 200, + likes: 35, + order: 2, + seo: { + metaTitle: '모바일 피트니스 앱 개발 프로젝트', + metaDescription: 'React Native로 개발한 크로스플랫폼 피트니스 앱', + keywords: ['Mobile App', 'React Native', 'Fitness', '헬스케어'] + } + }, + { + title: '기업 웹사이트 리뉴얼', + description: '기업의 브랜드 아이덴티티를 반영한 웹사이트 리뉴얼 프로젝트입니다. 사용자 경험을 개선하고 모던한 디자인을 적용하여 브랜드 가치를 높였습니다.', + shortDescription: '브랜드 아이덴티티를 반영한 기업 웹사이트 리뉴얼', + category: 'ui-ux-design', + technologies: ['Figma', 'React', 'Sass', 'Framer Motion', 'Contentful'], + images: [ + { url: '/images/portfolio/corporate-1.jpg', alt: '기업 웹사이트 메인페이지', isPrimary: true }, + { url: '/images/portfolio/corporate-2.jpg', alt: '회사소개 페이지', isPrimary: false }, + { url: '/images/portfolio/corporate-3.jpg', alt: '서비스 페이지', isPrimary: false } + ], + clientName: '기술 기업 TechCorp', + projectUrl: 'https://example-corp.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-03-10'), + completedAt: new Date('2024-03-05'), + isPublished: true, + viewCount: 120, + likes: 18, + order: 3, + seo: { + metaTitle: '기업 웹사이트 리뉴얼 프로젝트', + metaDescription: '모던한 디자인과 향상된 UX의 기업 웹사이트', + keywords: ['Web Design', 'Corporate', 'UI/UX', '웹사이트리뉴얼'] + } + }, + { + title: '레스토랑 예약 시스템', + description: '레스토랑을 위한 온라인 예약 시스템을 개발했습니다. 실시간 테이블 현황, 예약 관리, 고객 관리 기능을 포함하여 레스토랑 운영 효율성을 높였습니다.', + shortDescription: '실시간 테이블 예약 및 관리 시스템', + category: 'web-development', + technologies: ['Vue.js', 'Laravel', 'MySQL', 'Socket.io', 'Bootstrap'], + images: [ + { url: '/images/portfolio/restaurant-1.jpg', alt: '예약 시스템 메인', isPrimary: true }, + { url: '/images/portfolio/restaurant-2.jpg', alt: '예약 현황 관리', isPrimary: false } + ], + clientName: '레스토랑 델리셔스', + status: 'completed', + featured: false, + publishedAt: new Date('2024-04-05'), + completedAt: new Date('2024-04-01'), + isPublished: true, + viewCount: 85, + likes: 12, + order: 4, + seo: { + metaTitle: '레스토랑 예약 시스템 개발', + metaDescription: '실시간 테이블 예약 및 관리 웹 시스템', + keywords: ['Reservation System', 'Vue.js', 'Laravel', '예약시스템'] + } + }, + { + title: '교육 플랫폼 앱', + description: '온라인 교육을 위한 모바일 애플리케이션입니다. 동영상 강의, 퀴즈, 진도 관리 기능을 포함하여 효과적인 학습 환경을 제공합니다.', + shortDescription: '온라인 학습을 위한 교육 플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['Flutter', 'Dart', 'Firebase', 'FFmpeg', 'AWS S3'], + images: [ + { url: '/images/portfolio/education-1.jpg', alt: '교육 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/education-2.jpg', alt: '강의 재생 화면', isPrimary: false } + ], + clientName: '온라인 교육 기업 EduTech', + status: 'completed', + featured: false, + publishedAt: new Date('2024-05-12'), + completedAt: new Date('2024-05-08'), + isPublished: true, + viewCount: 95, + likes: 20, + order: 5, + seo: { + metaTitle: '교육 플랫폼 모바일 앱 개발', + metaDescription: 'Flutter로 개발한 온라인 교육 모바일 앱', + keywords: ['Education App', 'Flutter', 'E-learning', '교육앱'] + } + }, + { + title: 'IoT 대시보드', + description: 'IoT 디바이스들을 모니터링하고 제어할 수 있는 웹 대시보드를 개발했습니다. 실시간 데이터 시각화와 알림 기능을 포함합니다.', + shortDescription: 'IoT 디바이스 모니터링 및 제어 웹 대시보드', + category: 'web-development', + technologies: ['React', 'D3.js', 'Node.js', 'MQTT', 'InfluxDB', 'Docker'], + images: [ + { url: '/images/portfolio/iot-1.jpg', alt: 'IoT 대시보드 메인', isPrimary: true } + ], + clientName: 'IoT 솔루션 기업 SmartDevice', + status: 'in-progress', + featured: false, + publishedAt: new Date('2024-06-01'), + isPublished: true, + viewCount: 45, + likes: 8, + order: 6, + seo: { + metaTitle: 'IoT 대시보드 개발 프로젝트', + metaDescription: '실시간 IoT 디바이스 모니터링 웹 대시보드', + keywords: ['IoT', 'Dashboard', 'Real-time', 'Monitoring'] + } + } + ]; + + await Portfolio.insertMany(portfolioItems); + console.log('✅ Sample portfolio items created successfully'); + } catch (error) { + console.error('❌ Error creating sample portfolio:', error); + throw error; + } +} + +async function createSiteSettings() { + try { + const existingSettings = await SiteSettings.findOne(); + + if (existingSettings) { + console.log('⚙️ Site settings already exist, skipping...'); + return; + } + + const settings = new SiteSettings({ + siteName: 'SmartSolTech', + siteDescription: '혁신적인 기술 솔루션으로 비즈니스의 성장을 지원합니다', + logo: '/images/logo.png', + favicon: '/images/favicon.ico', + contact: { + email: 'info@smartsoltech.kr', + phone: '+82-10-1234-5678', + address: 'Seoul, South Korea' + }, + social: { + facebook: 'https://facebook.com/smartsoltech', + twitter: 'https://twitter.com/smartsoltech', + linkedin: 'https://linkedin.com/company/smartsoltech', + instagram: 'https://instagram.com/smartsoltech', + github: 'https://github.com/smartsoltech' + }, + telegram: { + isEnabled: false + }, + seo: { + metaTitle: 'SmartSolTech - 혁신적인 기술 솔루션', + metaDescription: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅 전문 기업. 한국에서 최고의 기술 솔루션을 제공합니다.', + keywords: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅, 기술 솔루션, 한국, SmartSolTech' + }, + hero: { + title: 'Smart Technology Solutions', + subtitle: '혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비즈니스의 디지털 전환을 이끌어갑니다', + backgroundImage: '/images/hero-bg.jpg', + ctaText: '프로젝트 시작하기', + ctaLink: '/contact' + }, + about: { + title: 'SmartSolTech 소개', + description: '우리는 최신 기술과 창의적인 아이디어로 고객의 비즈니스 성장을 지원하는 전문 개발팀입니다. 웹 개발부터 모바일 앱, UI/UX 디자인까지 포괄적인 디지털 솔루션을 제공합니다.', + image: '/images/about.jpg' + }, + maintenance: { + isEnabled: false, + message: '현재 시스템 점검 중입니다. 잠시 후 다시 접속해 주세요.' + } + }); + + await settings.save(); + console.log('✅ Site settings created successfully'); + } catch (error) { + console.error('❌ Error creating site settings:', error); + throw error; + } +} + +// Run the initialization +if (require.main === module) { + initializeDatabase(); +} + +module.exports = { + initializeDatabase, + createAdminUser, + createSampleServices, + createSamplePortfolio, + createSiteSettings +}; \ No newline at end of file diff --git a/.history/scripts/init-db_20251019202305.js b/.history/scripts/init-db_20251019202305.js new file mode 100644 index 0000000..9c2c5ce --- /dev/null +++ b/.history/scripts/init-db_20251019202305.js @@ -0,0 +1,496 @@ +#!/usr/bin/env node + +/** + * Database initialization script for SmartSolTech + * Creates initial admin user and sample data for PostgreSQL + */ + +const { sequelize } = require('../config/database'); +require('dotenv').config(); + +// Import models +const { User, Service, Portfolio, SiteSettings } = require('../models'); + +// Configuration +const ADMIN_EMAIL = process.env.ADMIN_EMAIL || 'admin@smartsoltech.kr'; +const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'admin123456'; + +async function initializeDatabase() { + try { + console.log('🔄 Connecting to PostgreSQL...'); + await sequelize.authenticate(); + console.log('✅ Connected to PostgreSQL'); + + // Sync database (create tables) + console.log('🔄 Syncing database schema...'); + await sequelize.sync({ force: false }); + console.log('✅ Database schema synchronized'); + + // Create admin user + await createAdminUser(); + + // Create sample services + await createSampleServices(); + + // Create sample portfolio items + await createSamplePortfolio(); + + // Create site settings + await createSiteSettings(); + + console.log('🎉 Database initialization completed successfully!'); + console.log(`📧 Admin login: ${ADMIN_EMAIL}`); + console.log(`🔑 Admin password: ${ADMIN_PASSWORD}`); + console.log('⚠️ Please change the admin password after first login!'); + + } catch (error) { + console.error('❌ Database initialization failed:', error); + process.exit(1); + } finally { + await sequelize.close(); + console.log('🔌 Database connection closed'); + process.exit(0); + } +} + +async function createAdminUser() { + try { + const existingAdmin = await User.findOne({ + where: { email: ADMIN_EMAIL } + }); + + if (existingAdmin) { + console.log('👤 Admin user already exists, skipping...'); + return; + } + + const adminUser = await User.create({ + name: 'Administrator', + email: ADMIN_EMAIL, + password: ADMIN_PASSWORD, + role: 'admin', + isActive: true + }); + + console.log('✅ Admin user created successfully'); + } catch (error) { + console.error('❌ Error creating admin user:', error); + throw error; + } +} + +async function createSampleServices() { + try { + const existingServices = await Service.count(); + + if (existingServices > 0) { + console.log('🛠️ Services already exist, skipping...'); + return; + } + + const services = [ + { + name: '웹 개발', + description: '현대적이고 반응형인 웹사이트와 웹 애플리케이션을 개발합니다. React, Node.js, MongoDB 등 최신 기술 스택을 활용하여 성능과 사용자 경험을 최적화합니다.', + shortDescription: '현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발', + icon: 'fas fa-code', + category: 'development', + features: [ + { name: '반응형 디자인', description: '모든 디바이스에서 완벽하게 작동', included: true }, + { name: 'SEO 최적화', description: '검색엔진 최적화로 더 많은 방문자 유치', included: true }, + { name: '성능 최적화', description: '빠른 로딩 속도와 원활한 사용자 경험', included: true }, + { name: '보안 강화', description: '최신 보안 기술로 안전한 웹사이트', included: true }, + { name: '유지보수', description: '6개월 무료 유지보수 지원', included: true } + ], + pricing: { + basePrice: 500000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 500000, max: 5000000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: true, + isActive: true, + order: 1, + tags: ['React', 'Node.js', 'MongoDB', 'Express', 'JavaScript', 'HTML', 'CSS'] + }, + { + name: '모바일 앱 개발', + description: 'iOS와 Android 플랫폼을 위한 네이티브 및 크로스플랫폼 모바일 애플리케이션을 개발합니다. React Native, Flutter 등을 활용하여 효율적인 개발을 진행합니다.', + shortDescription: 'iOS/Android 네이티브 및 크로스플랫폼 앱 개발', + icon: 'fas fa-mobile-alt', + category: 'development', + features: [ + { name: '크로스플랫폼 개발', description: 'iOS와 Android 동시 지원', included: true }, + { name: '네이티브 성능', description: '최적화된 성능과 사용자 경험', included: true }, + { name: '푸시 알림', description: '실시간 푸시 알림 시스템', included: true }, + { name: '오프라인 지원', description: '인터넷 연결 없이도 기본 기능 사용 가능', included: false }, + { name: '앱스토어 등록', description: '앱스토어 및 플레이스토어 등록 지원', included: true } + ], + pricing: { + basePrice: 800000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 800000, max: 8000000 } + }, + estimatedTime: { min: 4, max: 16, unit: 'weeks' }, + featured: true, + isActive: true, + order: 2, + tags: ['React Native', 'Flutter', 'iOS', 'Android', 'Swift', 'Kotlin', 'JavaScript'] + }, + { + name: 'UI/UX 디자인', + description: '사용자 중심의 직관적이고 아름다운 인터페이스를 디자인합니다. 사용자 경험 연구와 프로토타이핑을 통해 최적의 디자인 솔루션을 제공합니다.', + shortDescription: '사용자 중심의 직관적이고 아름다운 인터페이스 디자인', + icon: 'fas fa-palette', + category: 'design', + features: [ + { name: '사용자 경험 연구', description: '타겟 사용자 분석 및 페르소나 설정', included: true }, + { name: '와이어프레임', description: '정보 구조와 레이아웃 설계', included: true }, + { name: '프로토타이핑', description: '인터랙티브 프로토타입 제작', included: true }, + { name: '비주얼 디자인', description: '브랜드에 맞는 시각적 디자인', included: true }, + { name: '디자인 시스템', description: '일관된 디자인을 위한 가이드라인', included: false } + ], + pricing: { + basePrice: 300000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 300000, max: 2000000 } + }, + estimatedTime: { min: 1, max: 6, unit: 'weeks' }, + featured: true, + isActive: true, + order: 3, + tags: ['Figma', 'Sketch', 'Adobe XD', 'Photoshop', 'Illustrator', 'Prototyping'] + }, + { + name: '디지털 마케팅', + description: 'SEO, 소셜미디어 마케팅, 온라인 광고를 통해 디지털 마케팅 전략을 수립하고 실행합니다. 데이터 분석을 통한 지속적인 최적화를 제공합니다.', + shortDescription: 'SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅', + icon: 'fas fa-chart-line', + category: 'marketing', + features: [ + { name: 'SEO 최적화', description: '검색엔진 상위 노출을 위한 최적화', included: true }, + { name: '소셜미디어 관리', description: '페이스북, 인스타그램, 유튜브 관리', included: true }, + { name: '구글 광고', description: '구글 애즈를 통한 타겟 광고', included: true }, + { name: '콘텐츠 마케팅', description: '블로그 및 콘텐츠 제작', included: false }, + { name: '분석 및 리포팅', description: '마케팅 성과 분석 및 보고서', included: true } + ], + pricing: { + basePrice: 200000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 200000, max: 1500000 } + }, + estimatedTime: { min: 2, max: 12, unit: 'weeks' }, + featured: true, + isActive: true, + order: 4, + tags: ['SEO', 'Google Ads', 'Facebook Ads', 'Analytics', 'Social Media', 'Content Marketing'] + }, + { + name: '브랜딩', + description: '기업의 정체성을 반영하는 로고, 브랜드 가이드라인, 마케팅 자료를 디자인합니다. 일관된 브랜드 이미지로 브랜드 가치를 높입니다.', + shortDescription: '로고, 브랜드 가이드라인, 마케팅 자료 디자인', + icon: 'fas fa-copyright', + category: 'design', + features: [ + { name: '로고 디자인', description: '기업 정체성을 반영한 로고 제작', included: true }, + { name: '브랜드 가이드라인', description: '색상, 폰트, 사용법 가이드', included: true }, + { name: '명함 디자인', description: '브랜드에 맞는 명함 디자인', included: true }, + { name: '브로슈어 디자인', description: '회사 소개 브로슈어 제작', included: false }, + { name: '웹 브랜딩', description: '웹사이트 브랜딩 요소 적용', included: false } + ], + pricing: { + basePrice: 400000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 400000, max: 2500000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: false, + isActive: true, + order: 5, + tags: ['Logo Design', 'Brand Identity', 'Graphic Design', 'Corporate Design'] + }, + { + name: '기술 컨설팅', + description: '기업의 디지털 전환과 기술 도입을 위한 전문적인 컨설팅을 제공합니다. 기술 아키텍처 설계부터 구현 전략까지 포괄적으로 지원합니다.', + shortDescription: '디지털 전환 및 기술 도입을 위한 전문 컨설팅', + icon: 'fas fa-lightbulb', + category: 'consulting', + features: [ + { name: '기술 분석', description: '현재 기술 스택 분석 및 개선안 제시', included: true }, + { name: '아키텍처 설계', description: '확장 가능한 시스템 아키텍처 설계', included: true }, + { name: '개발 프로세스 개선', description: '효율적인 개발 워크플로우 구축', included: true }, + { name: '팀 교육', description: '개발팀 대상 기술 교육', included: false }, + { name: '지속적인 지원', description: '프로젝트 완료 후 지속적인 기술 지원', included: false } + ], + pricing: { + basePrice: 150000, + currency: 'KRW', + priceType: 'hourly', + priceRange: { min: 150000, max: 500000 } + }, + estimatedTime: { min: 1, max: 4, unit: 'weeks' }, + featured: false, + isActive: true, + order: 6, + tags: ['Architecture', 'Strategy', 'Process', 'Training', 'Consultation'] + } + ]; + + await Service.bulkCreate(services); + console.log('✅ Sample services created successfully'); + } catch (error) { + console.error('❌ Error creating sample services:', error); + throw error; + } +} + +async function createSamplePortfolio() { + try { + const existingPortfolio = await Portfolio.count(); + + if (existingPortfolio > 0) { + console.log('🎨 Portfolio items already exist, skipping...'); + return; + } + + const portfolioItems = [ + { + title: 'E-commerce 플랫폼', + description: '현대적인 온라인 쇼핑몰 플랫폼을 개발했습니다. React와 Node.js를 활용하여 높은 성능과 사용자 경험을 제공하며, 결제 시스템과 재고 관리 기능을 포함합니다.', + shortDescription: '반응형 온라인 쇼핑몰 플랫폼 개발', + category: 'web-development', + technologies: ['React', 'Node.js', 'MongoDB', 'Express', 'Stripe', 'AWS'], + images: [ + { url: '/images/portfolio/ecommerce-1.jpg', alt: 'E-commerce 메인페이지', isPrimary: true }, + { url: '/images/portfolio/ecommerce-2.jpg', alt: '상품 상세페이지', isPrimary: false }, + { url: '/images/portfolio/ecommerce-3.jpg', alt: '장바구니 페이지', isPrimary: false } + ], + clientName: '패션 브랜드 ABC', + projectUrl: 'https://example-ecommerce.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-01-15'), + completedAt: new Date('2024-01-10'), + isPublished: true, + viewCount: 150, + likes: 25, + order: 1, + seo: { + metaTitle: 'E-commerce 플랫폼 개발 프로젝트', + metaDescription: '현대적인 온라인 쇼핑몰 플랫폼 개발 사례', + keywords: ['E-commerce', 'React', 'Node.js', '온라인쇼핑몰'] + } + }, + { + title: '모바일 피트니스 앱', + description: 'React Native를 사용하여 크로스플랫폼 피트니스 애플리케이션을 개발했습니다. 운동 계획, 칼로리 추적, 소셜 기능을 포함하여 사용자들의 건강한 라이프스타일을 지원합니다.', + shortDescription: '건강 관리를 위한 크로스플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['React Native', 'Redux', 'Firebase', 'Node.js', 'PostgreSQL'], + images: [ + { url: '/images/portfolio/fitness-1.jpg', alt: '피트니스 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/fitness-2.jpg', alt: '운동 계획 화면', isPrimary: false }, + { url: '/images/portfolio/fitness-3.jpg', alt: '통계 화면', isPrimary: false } + ], + clientName: '헬스케어 스타트업 FIT', + status: 'completed', + featured: true, + publishedAt: new Date('2024-02-20'), + completedAt: new Date('2024-02-15'), + isPublished: true, + viewCount: 200, + likes: 35, + order: 2, + seo: { + metaTitle: '모바일 피트니스 앱 개발 프로젝트', + metaDescription: 'React Native로 개발한 크로스플랫폼 피트니스 앱', + keywords: ['Mobile App', 'React Native', 'Fitness', '헬스케어'] + } + }, + { + title: '기업 웹사이트 리뉴얼', + description: '기업의 브랜드 아이덴티티를 반영한 웹사이트 리뉴얼 프로젝트입니다. 사용자 경험을 개선하고 모던한 디자인을 적용하여 브랜드 가치를 높였습니다.', + shortDescription: '브랜드 아이덴티티를 반영한 기업 웹사이트 리뉴얼', + category: 'ui-ux-design', + technologies: ['Figma', 'React', 'Sass', 'Framer Motion', 'Contentful'], + images: [ + { url: '/images/portfolio/corporate-1.jpg', alt: '기업 웹사이트 메인페이지', isPrimary: true }, + { url: '/images/portfolio/corporate-2.jpg', alt: '회사소개 페이지', isPrimary: false }, + { url: '/images/portfolio/corporate-3.jpg', alt: '서비스 페이지', isPrimary: false } + ], + clientName: '기술 기업 TechCorp', + projectUrl: 'https://example-corp.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-03-10'), + completedAt: new Date('2024-03-05'), + isPublished: true, + viewCount: 120, + likes: 18, + order: 3, + seo: { + metaTitle: '기업 웹사이트 리뉴얼 프로젝트', + metaDescription: '모던한 디자인과 향상된 UX의 기업 웹사이트', + keywords: ['Web Design', 'Corporate', 'UI/UX', '웹사이트리뉴얼'] + } + }, + { + title: '레스토랑 예약 시스템', + description: '레스토랑을 위한 온라인 예약 시스템을 개발했습니다. 실시간 테이블 현황, 예약 관리, 고객 관리 기능을 포함하여 레스토랑 운영 효율성을 높였습니다.', + shortDescription: '실시간 테이블 예약 및 관리 시스템', + category: 'web-development', + technologies: ['Vue.js', 'Laravel', 'MySQL', 'Socket.io', 'Bootstrap'], + images: [ + { url: '/images/portfolio/restaurant-1.jpg', alt: '예약 시스템 메인', isPrimary: true }, + { url: '/images/portfolio/restaurant-2.jpg', alt: '예약 현황 관리', isPrimary: false } + ], + clientName: '레스토랑 델리셔스', + status: 'completed', + featured: false, + publishedAt: new Date('2024-04-05'), + completedAt: new Date('2024-04-01'), + isPublished: true, + viewCount: 85, + likes: 12, + order: 4, + seo: { + metaTitle: '레스토랑 예약 시스템 개발', + metaDescription: '실시간 테이블 예약 및 관리 웹 시스템', + keywords: ['Reservation System', 'Vue.js', 'Laravel', '예약시스템'] + } + }, + { + title: '교육 플랫폼 앱', + description: '온라인 교육을 위한 모바일 애플리케이션입니다. 동영상 강의, 퀴즈, 진도 관리 기능을 포함하여 효과적인 학습 환경을 제공합니다.', + shortDescription: '온라인 학습을 위한 교육 플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['Flutter', 'Dart', 'Firebase', 'FFmpeg', 'AWS S3'], + images: [ + { url: '/images/portfolio/education-1.jpg', alt: '교육 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/education-2.jpg', alt: '강의 재생 화면', isPrimary: false } + ], + clientName: '온라인 교육 기업 EduTech', + status: 'completed', + featured: false, + publishedAt: new Date('2024-05-12'), + completedAt: new Date('2024-05-08'), + isPublished: true, + viewCount: 95, + likes: 20, + order: 5, + seo: { + metaTitle: '교육 플랫폼 모바일 앱 개발', + metaDescription: 'Flutter로 개발한 온라인 교육 모바일 앱', + keywords: ['Education App', 'Flutter', 'E-learning', '교육앱'] + } + }, + { + title: 'IoT 대시보드', + description: 'IoT 디바이스들을 모니터링하고 제어할 수 있는 웹 대시보드를 개발했습니다. 실시간 데이터 시각화와 알림 기능을 포함합니다.', + shortDescription: 'IoT 디바이스 모니터링 및 제어 웹 대시보드', + category: 'web-development', + technologies: ['React', 'D3.js', 'Node.js', 'MQTT', 'InfluxDB', 'Docker'], + images: [ + { url: '/images/portfolio/iot-1.jpg', alt: 'IoT 대시보드 메인', isPrimary: true } + ], + clientName: 'IoT 솔루션 기업 SmartDevice', + status: 'in-progress', + featured: false, + publishedAt: new Date('2024-06-01'), + isPublished: true, + viewCount: 45, + likes: 8, + order: 6, + seo: { + metaTitle: 'IoT 대시보드 개발 프로젝트', + metaDescription: '실시간 IoT 디바이스 모니터링 웹 대시보드', + keywords: ['IoT', 'Dashboard', 'Real-time', 'Monitoring'] + } + } + ]; + + await Portfolio.bulkCreate(portfolioItems); + console.log('✅ Sample portfolio items created successfully'); + } catch (error) { + console.error('❌ Error creating sample portfolio:', error); + throw error; + } +} + +async function createSiteSettings() { + try { + const existingSettings = await SiteSettings.findOne(); + + if (existingSettings) { + console.log('⚙️ Site settings already exist, skipping...'); + return; + } + + const settings = new SiteSettings({ + siteName: 'SmartSolTech', + siteDescription: '혁신적인 기술 솔루션으로 비즈니스의 성장을 지원합니다', + logo: '/images/logo.png', + favicon: '/images/favicon.ico', + contact: { + email: 'info@smartsoltech.kr', + phone: '+82-10-1234-5678', + address: 'Seoul, South Korea' + }, + social: { + facebook: 'https://facebook.com/smartsoltech', + twitter: 'https://twitter.com/smartsoltech', + linkedin: 'https://linkedin.com/company/smartsoltech', + instagram: 'https://instagram.com/smartsoltech', + github: 'https://github.com/smartsoltech' + }, + telegram: { + isEnabled: false + }, + seo: { + metaTitle: 'SmartSolTech - 혁신적인 기술 솔루션', + metaDescription: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅 전문 기업. 한국에서 최고의 기술 솔루션을 제공합니다.', + keywords: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅, 기술 솔루션, 한국, SmartSolTech' + }, + hero: { + title: 'Smart Technology Solutions', + subtitle: '혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비즈니스의 디지털 전환을 이끌어갑니다', + backgroundImage: '/images/hero-bg.jpg', + ctaText: '프로젝트 시작하기', + ctaLink: '/contact' + }, + about: { + title: 'SmartSolTech 소개', + description: '우리는 최신 기술과 창의적인 아이디어로 고객의 비즈니스 성장을 지원하는 전문 개발팀입니다. 웹 개발부터 모바일 앱, UI/UX 디자인까지 포괄적인 디지털 솔루션을 제공합니다.', + image: '/images/about.jpg' + }, + maintenance: { + isEnabled: false, + message: '현재 시스템 점검 중입니다. 잠시 후 다시 접속해 주세요.' + } + }); + + await settings.save(); + console.log('✅ Site settings created successfully'); + } catch (error) { + console.error('❌ Error creating site settings:', error); + throw error; + } +} + +// Run the initialization +if (require.main === module) { + initializeDatabase(); +} + +module.exports = { + initializeDatabase, + createAdminUser, + createSampleServices, + createSamplePortfolio, + createSiteSettings +}; \ No newline at end of file diff --git a/.history/scripts/init-db_20251019202312.js b/.history/scripts/init-db_20251019202312.js new file mode 100644 index 0000000..15c017d --- /dev/null +++ b/.history/scripts/init-db_20251019202312.js @@ -0,0 +1,496 @@ +#!/usr/bin/env node + +/** + * Database initialization script for SmartSolTech + * Creates initial admin user and sample data for PostgreSQL + */ + +const { sequelize } = require('../config/database'); +require('dotenv').config(); + +// Import models +const { User, Service, Portfolio, SiteSettings } = require('../models'); + +// Configuration +const ADMIN_EMAIL = process.env.ADMIN_EMAIL || 'admin@smartsoltech.kr'; +const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'admin123456'; + +async function initializeDatabase() { + try { + console.log('🔄 Connecting to PostgreSQL...'); + await sequelize.authenticate(); + console.log('✅ Connected to PostgreSQL'); + + // Sync database (create tables) + console.log('🔄 Syncing database schema...'); + await sequelize.sync({ force: false }); + console.log('✅ Database schema synchronized'); + + // Create admin user + await createAdminUser(); + + // Create sample services + await createSampleServices(); + + // Create sample portfolio items + await createSamplePortfolio(); + + // Create site settings + await createSiteSettings(); + + console.log('🎉 Database initialization completed successfully!'); + console.log(`📧 Admin login: ${ADMIN_EMAIL}`); + console.log(`🔑 Admin password: ${ADMIN_PASSWORD}`); + console.log('⚠️ Please change the admin password after first login!'); + + } catch (error) { + console.error('❌ Database initialization failed:', error); + process.exit(1); + } finally { + await sequelize.close(); + console.log('🔌 Database connection closed'); + process.exit(0); + } +} + +async function createAdminUser() { + try { + const existingAdmin = await User.findOne({ + where: { email: ADMIN_EMAIL } + }); + + if (existingAdmin) { + console.log('👤 Admin user already exists, skipping...'); + return; + } + + const adminUser = await User.create({ + name: 'Administrator', + email: ADMIN_EMAIL, + password: ADMIN_PASSWORD, + role: 'admin', + isActive: true + }); + + console.log('✅ Admin user created successfully'); + } catch (error) { + console.error('❌ Error creating admin user:', error); + throw error; + } +} + +async function createSampleServices() { + try { + const existingServices = await Service.count(); + + if (existingServices > 0) { + console.log('🛠️ Services already exist, skipping...'); + return; + } + + const services = [ + { + name: '웹 개발', + description: '현대적이고 반응형인 웹사이트와 웹 애플리케이션을 개발합니다. React, Node.js, MongoDB 등 최신 기술 스택을 활용하여 성능과 사용자 경험을 최적화합니다.', + shortDescription: '현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발', + icon: 'fas fa-code', + category: 'development', + features: [ + { name: '반응형 디자인', description: '모든 디바이스에서 완벽하게 작동', included: true }, + { name: 'SEO 최적화', description: '검색엔진 최적화로 더 많은 방문자 유치', included: true }, + { name: '성능 최적화', description: '빠른 로딩 속도와 원활한 사용자 경험', included: true }, + { name: '보안 강화', description: '최신 보안 기술로 안전한 웹사이트', included: true }, + { name: '유지보수', description: '6개월 무료 유지보수 지원', included: true } + ], + pricing: { + basePrice: 500000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 500000, max: 5000000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: true, + isActive: true, + order: 1, + tags: ['React', 'Node.js', 'MongoDB', 'Express', 'JavaScript', 'HTML', 'CSS'] + }, + { + name: '모바일 앱 개발', + description: 'iOS와 Android 플랫폼을 위한 네이티브 및 크로스플랫폼 모바일 애플리케이션을 개발합니다. React Native, Flutter 등을 활용하여 효율적인 개발을 진행합니다.', + shortDescription: 'iOS/Android 네이티브 및 크로스플랫폼 앱 개발', + icon: 'fas fa-mobile-alt', + category: 'development', + features: [ + { name: '크로스플랫폼 개발', description: 'iOS와 Android 동시 지원', included: true }, + { name: '네이티브 성능', description: '최적화된 성능과 사용자 경험', included: true }, + { name: '푸시 알림', description: '실시간 푸시 알림 시스템', included: true }, + { name: '오프라인 지원', description: '인터넷 연결 없이도 기본 기능 사용 가능', included: false }, + { name: '앱스토어 등록', description: '앱스토어 및 플레이스토어 등록 지원', included: true } + ], + pricing: { + basePrice: 800000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 800000, max: 8000000 } + }, + estimatedTime: { min: 4, max: 16, unit: 'weeks' }, + featured: true, + isActive: true, + order: 2, + tags: ['React Native', 'Flutter', 'iOS', 'Android', 'Swift', 'Kotlin', 'JavaScript'] + }, + { + name: 'UI/UX 디자인', + description: '사용자 중심의 직관적이고 아름다운 인터페이스를 디자인합니다. 사용자 경험 연구와 프로토타이핑을 통해 최적의 디자인 솔루션을 제공합니다.', + shortDescription: '사용자 중심의 직관적이고 아름다운 인터페이스 디자인', + icon: 'fas fa-palette', + category: 'design', + features: [ + { name: '사용자 경험 연구', description: '타겟 사용자 분석 및 페르소나 설정', included: true }, + { name: '와이어프레임', description: '정보 구조와 레이아웃 설계', included: true }, + { name: '프로토타이핑', description: '인터랙티브 프로토타입 제작', included: true }, + { name: '비주얼 디자인', description: '브랜드에 맞는 시각적 디자인', included: true }, + { name: '디자인 시스템', description: '일관된 디자인을 위한 가이드라인', included: false } + ], + pricing: { + basePrice: 300000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 300000, max: 2000000 } + }, + estimatedTime: { min: 1, max: 6, unit: 'weeks' }, + featured: true, + isActive: true, + order: 3, + tags: ['Figma', 'Sketch', 'Adobe XD', 'Photoshop', 'Illustrator', 'Prototyping'] + }, + { + name: '디지털 마케팅', + description: 'SEO, 소셜미디어 마케팅, 온라인 광고를 통해 디지털 마케팅 전략을 수립하고 실행합니다. 데이터 분석을 통한 지속적인 최적화를 제공합니다.', + shortDescription: 'SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅', + icon: 'fas fa-chart-line', + category: 'marketing', + features: [ + { name: 'SEO 최적화', description: '검색엔진 상위 노출을 위한 최적화', included: true }, + { name: '소셜미디어 관리', description: '페이스북, 인스타그램, 유튜브 관리', included: true }, + { name: '구글 광고', description: '구글 애즈를 통한 타겟 광고', included: true }, + { name: '콘텐츠 마케팅', description: '블로그 및 콘텐츠 제작', included: false }, + { name: '분석 및 리포팅', description: '마케팅 성과 분석 및 보고서', included: true } + ], + pricing: { + basePrice: 200000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 200000, max: 1500000 } + }, + estimatedTime: { min: 2, max: 12, unit: 'weeks' }, + featured: true, + isActive: true, + order: 4, + tags: ['SEO', 'Google Ads', 'Facebook Ads', 'Analytics', 'Social Media', 'Content Marketing'] + }, + { + name: '브랜딩', + description: '기업의 정체성을 반영하는 로고, 브랜드 가이드라인, 마케팅 자료를 디자인합니다. 일관된 브랜드 이미지로 브랜드 가치를 높입니다.', + shortDescription: '로고, 브랜드 가이드라인, 마케팅 자료 디자인', + icon: 'fas fa-copyright', + category: 'design', + features: [ + { name: '로고 디자인', description: '기업 정체성을 반영한 로고 제작', included: true }, + { name: '브랜드 가이드라인', description: '색상, 폰트, 사용법 가이드', included: true }, + { name: '명함 디자인', description: '브랜드에 맞는 명함 디자인', included: true }, + { name: '브로슈어 디자인', description: '회사 소개 브로슈어 제작', included: false }, + { name: '웹 브랜딩', description: '웹사이트 브랜딩 요소 적용', included: false } + ], + pricing: { + basePrice: 400000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 400000, max: 2500000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: false, + isActive: true, + order: 5, + tags: ['Logo Design', 'Brand Identity', 'Graphic Design', 'Corporate Design'] + }, + { + name: '기술 컨설팅', + description: '기업의 디지털 전환과 기술 도입을 위한 전문적인 컨설팅을 제공합니다. 기술 아키텍처 설계부터 구현 전략까지 포괄적으로 지원합니다.', + shortDescription: '디지털 전환 및 기술 도입을 위한 전문 컨설팅', + icon: 'fas fa-lightbulb', + category: 'consulting', + features: [ + { name: '기술 분석', description: '현재 기술 스택 분석 및 개선안 제시', included: true }, + { name: '아키텍처 설계', description: '확장 가능한 시스템 아키텍처 설계', included: true }, + { name: '개발 프로세스 개선', description: '효율적인 개발 워크플로우 구축', included: true }, + { name: '팀 교육', description: '개발팀 대상 기술 교육', included: false }, + { name: '지속적인 지원', description: '프로젝트 완료 후 지속적인 기술 지원', included: false } + ], + pricing: { + basePrice: 150000, + currency: 'KRW', + priceType: 'hourly', + priceRange: { min: 150000, max: 500000 } + }, + estimatedTime: { min: 1, max: 4, unit: 'weeks' }, + featured: false, + isActive: true, + order: 6, + tags: ['Architecture', 'Strategy', 'Process', 'Training', 'Consultation'] + } + ]; + + await Service.bulkCreate(services); + console.log('✅ Sample services created successfully'); + } catch (error) { + console.error('❌ Error creating sample services:', error); + throw error; + } +} + +async function createSamplePortfolio() { + try { + const existingPortfolio = await Portfolio.count(); + + if (existingPortfolio > 0) { + console.log('🎨 Portfolio items already exist, skipping...'); + return; + } + + const portfolioItems = [ + { + title: 'E-commerce 플랫폼', + description: '현대적인 온라인 쇼핑몰 플랫폼을 개발했습니다. React와 Node.js를 활용하여 높은 성능과 사용자 경험을 제공하며, 결제 시스템과 재고 관리 기능을 포함합니다.', + shortDescription: '반응형 온라인 쇼핑몰 플랫폼 개발', + category: 'web-development', + technologies: ['React', 'Node.js', 'MongoDB', 'Express', 'Stripe', 'AWS'], + images: [ + { url: '/images/portfolio/ecommerce-1.jpg', alt: 'E-commerce 메인페이지', isPrimary: true }, + { url: '/images/portfolio/ecommerce-2.jpg', alt: '상품 상세페이지', isPrimary: false }, + { url: '/images/portfolio/ecommerce-3.jpg', alt: '장바구니 페이지', isPrimary: false } + ], + clientName: '패션 브랜드 ABC', + projectUrl: 'https://example-ecommerce.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-01-15'), + completedAt: new Date('2024-01-10'), + isPublished: true, + viewCount: 150, + likes: 25, + order: 1, + seo: { + metaTitle: 'E-commerce 플랫폼 개발 프로젝트', + metaDescription: '현대적인 온라인 쇼핑몰 플랫폼 개발 사례', + keywords: ['E-commerce', 'React', 'Node.js', '온라인쇼핑몰'] + } + }, + { + title: '모바일 피트니스 앱', + description: 'React Native를 사용하여 크로스플랫폼 피트니스 애플리케이션을 개발했습니다. 운동 계획, 칼로리 추적, 소셜 기능을 포함하여 사용자들의 건강한 라이프스타일을 지원합니다.', + shortDescription: '건강 관리를 위한 크로스플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['React Native', 'Redux', 'Firebase', 'Node.js', 'PostgreSQL'], + images: [ + { url: '/images/portfolio/fitness-1.jpg', alt: '피트니스 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/fitness-2.jpg', alt: '운동 계획 화면', isPrimary: false }, + { url: '/images/portfolio/fitness-3.jpg', alt: '통계 화면', isPrimary: false } + ], + clientName: '헬스케어 스타트업 FIT', + status: 'completed', + featured: true, + publishedAt: new Date('2024-02-20'), + completedAt: new Date('2024-02-15'), + isPublished: true, + viewCount: 200, + likes: 35, + order: 2, + seo: { + metaTitle: '모바일 피트니스 앱 개발 프로젝트', + metaDescription: 'React Native로 개발한 크로스플랫폼 피트니스 앱', + keywords: ['Mobile App', 'React Native', 'Fitness', '헬스케어'] + } + }, + { + title: '기업 웹사이트 리뉴얼', + description: '기업의 브랜드 아이덴티티를 반영한 웹사이트 리뉴얼 프로젝트입니다. 사용자 경험을 개선하고 모던한 디자인을 적용하여 브랜드 가치를 높였습니다.', + shortDescription: '브랜드 아이덴티티를 반영한 기업 웹사이트 리뉴얼', + category: 'ui-ux-design', + technologies: ['Figma', 'React', 'Sass', 'Framer Motion', 'Contentful'], + images: [ + { url: '/images/portfolio/corporate-1.jpg', alt: '기업 웹사이트 메인페이지', isPrimary: true }, + { url: '/images/portfolio/corporate-2.jpg', alt: '회사소개 페이지', isPrimary: false }, + { url: '/images/portfolio/corporate-3.jpg', alt: '서비스 페이지', isPrimary: false } + ], + clientName: '기술 기업 TechCorp', + projectUrl: 'https://example-corp.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-03-10'), + completedAt: new Date('2024-03-05'), + isPublished: true, + viewCount: 120, + likes: 18, + order: 3, + seo: { + metaTitle: '기업 웹사이트 리뉴얼 프로젝트', + metaDescription: '모던한 디자인과 향상된 UX의 기업 웹사이트', + keywords: ['Web Design', 'Corporate', 'UI/UX', '웹사이트리뉴얼'] + } + }, + { + title: '레스토랑 예약 시스템', + description: '레스토랑을 위한 온라인 예약 시스템을 개발했습니다. 실시간 테이블 현황, 예약 관리, 고객 관리 기능을 포함하여 레스토랑 운영 효율성을 높였습니다.', + shortDescription: '실시간 테이블 예약 및 관리 시스템', + category: 'web-development', + technologies: ['Vue.js', 'Laravel', 'MySQL', 'Socket.io', 'Bootstrap'], + images: [ + { url: '/images/portfolio/restaurant-1.jpg', alt: '예약 시스템 메인', isPrimary: true }, + { url: '/images/portfolio/restaurant-2.jpg', alt: '예약 현황 관리', isPrimary: false } + ], + clientName: '레스토랑 델리셔스', + status: 'completed', + featured: false, + publishedAt: new Date('2024-04-05'), + completedAt: new Date('2024-04-01'), + isPublished: true, + viewCount: 85, + likes: 12, + order: 4, + seo: { + metaTitle: '레스토랑 예약 시스템 개발', + metaDescription: '실시간 테이블 예약 및 관리 웹 시스템', + keywords: ['Reservation System', 'Vue.js', 'Laravel', '예약시스템'] + } + }, + { + title: '교육 플랫폼 앱', + description: '온라인 교육을 위한 모바일 애플리케이션입니다. 동영상 강의, 퀴즈, 진도 관리 기능을 포함하여 효과적인 학습 환경을 제공합니다.', + shortDescription: '온라인 학습을 위한 교육 플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['Flutter', 'Dart', 'Firebase', 'FFmpeg', 'AWS S3'], + images: [ + { url: '/images/portfolio/education-1.jpg', alt: '교육 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/education-2.jpg', alt: '강의 재생 화면', isPrimary: false } + ], + clientName: '온라인 교육 기업 EduTech', + status: 'completed', + featured: false, + publishedAt: new Date('2024-05-12'), + completedAt: new Date('2024-05-08'), + isPublished: true, + viewCount: 95, + likes: 20, + order: 5, + seo: { + metaTitle: '교육 플랫폼 모바일 앱 개발', + metaDescription: 'Flutter로 개발한 온라인 교육 모바일 앱', + keywords: ['Education App', 'Flutter', 'E-learning', '교육앱'] + } + }, + { + title: 'IoT 대시보드', + description: 'IoT 디바이스들을 모니터링하고 제어할 수 있는 웹 대시보드를 개발했습니다. 실시간 데이터 시각화와 알림 기능을 포함합니다.', + shortDescription: 'IoT 디바이스 모니터링 및 제어 웹 대시보드', + category: 'web-development', + technologies: ['React', 'D3.js', 'Node.js', 'MQTT', 'InfluxDB', 'Docker'], + images: [ + { url: '/images/portfolio/iot-1.jpg', alt: 'IoT 대시보드 메인', isPrimary: true } + ], + clientName: 'IoT 솔루션 기업 SmartDevice', + status: 'in-progress', + featured: false, + publishedAt: new Date('2024-06-01'), + isPublished: true, + viewCount: 45, + likes: 8, + order: 6, + seo: { + metaTitle: 'IoT 대시보드 개발 프로젝트', + metaDescription: '실시간 IoT 디바이스 모니터링 웹 대시보드', + keywords: ['IoT', 'Dashboard', 'Real-time', 'Monitoring'] + } + } + ]; + + await Portfolio.bulkCreate(portfolioItems); + console.log('✅ Sample portfolio items created successfully'); + } catch (error) { + console.error('❌ Error creating sample portfolio:', error); + throw error; + } +} + +async function createSiteSettings() { + try { + const existingSettings = await SiteSettings.findOne(); + + if (existingSettings) { + console.log('⚙️ Site settings already exist, skipping...'); + return; + } + + const settings = await SiteSettings.create({ + siteName: 'SmartSolTech', + siteDescription: '혁신적인 기술 솔루션으로 비즈니스의 성장을 지원합니다', + logo: '/images/logo.png', + favicon: '/images/favicon.ico', + contact: { + email: 'info@smartsoltech.kr', + phone: '+82-10-1234-5678', + address: 'Seoul, South Korea' + }, + social: { + facebook: 'https://facebook.com/smartsoltech', + twitter: 'https://twitter.com/smartsoltech', + linkedin: 'https://linkedin.com/company/smartsoltech', + instagram: 'https://instagram.com/smartsoltech', + github: 'https://github.com/smartsoltech' + }, + telegram: { + isEnabled: false + }, + seo: { + metaTitle: 'SmartSolTech - 혁신적인 기술 솔루션', + metaDescription: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅 전문 기업. 한국에서 최고의 기술 솔루션을 제공합니다.', + keywords: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅, 기술 솔루션, 한국, SmartSolTech' + }, + hero: { + title: 'Smart Technology Solutions', + subtitle: '혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비즈니스의 디지털 전환을 이끌어갑니다', + backgroundImage: '/images/hero-bg.jpg', + ctaText: '프로젝트 시작하기', + ctaLink: '/contact' + }, + about: { + title: 'SmartSolTech 소개', + description: '우리는 최신 기술과 창의적인 아이디어로 고객의 비즈니스 성장을 지원하는 전문 개발팀입니다. 웹 개발부터 모바일 앱, UI/UX 디자인까지 포괄적인 디지털 솔루션을 제공합니다.', + image: '/images/about.jpg' + }, + maintenance: { + isEnabled: false, + message: '현재 시스템 점검 중입니다. 잠시 후 다시 접속해 주세요.' + } + }); + + await settings.save(); + console.log('✅ Site settings created successfully'); + } catch (error) { + console.error('❌ Error creating site settings:', error); + throw error; + } +} + +// Run the initialization +if (require.main === module) { + initializeDatabase(); +} + +module.exports = { + initializeDatabase, + createAdminUser, + createSampleServices, + createSamplePortfolio, + createSiteSettings +}; \ No newline at end of file diff --git a/.history/scripts/init-db_20251019202318.js b/.history/scripts/init-db_20251019202318.js new file mode 100644 index 0000000..6ee7fc9 --- /dev/null +++ b/.history/scripts/init-db_20251019202318.js @@ -0,0 +1,495 @@ +#!/usr/bin/env node + +/** + * Database initialization script for SmartSolTech + * Creates initial admin user and sample data for PostgreSQL + */ + +const { sequelize } = require('../config/database'); +require('dotenv').config(); + +// Import models +const { User, Service, Portfolio, SiteSettings } = require('../models'); + +// Configuration +const ADMIN_EMAIL = process.env.ADMIN_EMAIL || 'admin@smartsoltech.kr'; +const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'admin123456'; + +async function initializeDatabase() { + try { + console.log('🔄 Connecting to PostgreSQL...'); + await sequelize.authenticate(); + console.log('✅ Connected to PostgreSQL'); + + // Sync database (create tables) + console.log('🔄 Syncing database schema...'); + await sequelize.sync({ force: false }); + console.log('✅ Database schema synchronized'); + + // Create admin user + await createAdminUser(); + + // Create sample services + await createSampleServices(); + + // Create sample portfolio items + await createSamplePortfolio(); + + // Create site settings + await createSiteSettings(); + + console.log('🎉 Database initialization completed successfully!'); + console.log(`📧 Admin login: ${ADMIN_EMAIL}`); + console.log(`🔑 Admin password: ${ADMIN_PASSWORD}`); + console.log('⚠️ Please change the admin password after first login!'); + + } catch (error) { + console.error('❌ Database initialization failed:', error); + process.exit(1); + } finally { + await sequelize.close(); + console.log('🔌 Database connection closed'); + process.exit(0); + } +} + +async function createAdminUser() { + try { + const existingAdmin = await User.findOne({ + where: { email: ADMIN_EMAIL } + }); + + if (existingAdmin) { + console.log('👤 Admin user already exists, skipping...'); + return; + } + + const adminUser = await User.create({ + name: 'Administrator', + email: ADMIN_EMAIL, + password: ADMIN_PASSWORD, + role: 'admin', + isActive: true + }); + + console.log('✅ Admin user created successfully'); + } catch (error) { + console.error('❌ Error creating admin user:', error); + throw error; + } +} + +async function createSampleServices() { + try { + const existingServices = await Service.count(); + + if (existingServices > 0) { + console.log('🛠️ Services already exist, skipping...'); + return; + } + + const services = [ + { + name: '웹 개발', + description: '현대적이고 반응형인 웹사이트와 웹 애플리케이션을 개발합니다. React, Node.js, MongoDB 등 최신 기술 스택을 활용하여 성능과 사용자 경험을 최적화합니다.', + shortDescription: '현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발', + icon: 'fas fa-code', + category: 'development', + features: [ + { name: '반응형 디자인', description: '모든 디바이스에서 완벽하게 작동', included: true }, + { name: 'SEO 최적화', description: '검색엔진 최적화로 더 많은 방문자 유치', included: true }, + { name: '성능 최적화', description: '빠른 로딩 속도와 원활한 사용자 경험', included: true }, + { name: '보안 강화', description: '최신 보안 기술로 안전한 웹사이트', included: true }, + { name: '유지보수', description: '6개월 무료 유지보수 지원', included: true } + ], + pricing: { + basePrice: 500000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 500000, max: 5000000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: true, + isActive: true, + order: 1, + tags: ['React', 'Node.js', 'MongoDB', 'Express', 'JavaScript', 'HTML', 'CSS'] + }, + { + name: '모바일 앱 개발', + description: 'iOS와 Android 플랫폼을 위한 네이티브 및 크로스플랫폼 모바일 애플리케이션을 개발합니다. React Native, Flutter 등을 활용하여 효율적인 개발을 진행합니다.', + shortDescription: 'iOS/Android 네이티브 및 크로스플랫폼 앱 개발', + icon: 'fas fa-mobile-alt', + category: 'development', + features: [ + { name: '크로스플랫폼 개발', description: 'iOS와 Android 동시 지원', included: true }, + { name: '네이티브 성능', description: '최적화된 성능과 사용자 경험', included: true }, + { name: '푸시 알림', description: '실시간 푸시 알림 시스템', included: true }, + { name: '오프라인 지원', description: '인터넷 연결 없이도 기본 기능 사용 가능', included: false }, + { name: '앱스토어 등록', description: '앱스토어 및 플레이스토어 등록 지원', included: true } + ], + pricing: { + basePrice: 800000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 800000, max: 8000000 } + }, + estimatedTime: { min: 4, max: 16, unit: 'weeks' }, + featured: true, + isActive: true, + order: 2, + tags: ['React Native', 'Flutter', 'iOS', 'Android', 'Swift', 'Kotlin', 'JavaScript'] + }, + { + name: 'UI/UX 디자인', + description: '사용자 중심의 직관적이고 아름다운 인터페이스를 디자인합니다. 사용자 경험 연구와 프로토타이핑을 통해 최적의 디자인 솔루션을 제공합니다.', + shortDescription: '사용자 중심의 직관적이고 아름다운 인터페이스 디자인', + icon: 'fas fa-palette', + category: 'design', + features: [ + { name: '사용자 경험 연구', description: '타겟 사용자 분석 및 페르소나 설정', included: true }, + { name: '와이어프레임', description: '정보 구조와 레이아웃 설계', included: true }, + { name: '프로토타이핑', description: '인터랙티브 프로토타입 제작', included: true }, + { name: '비주얼 디자인', description: '브랜드에 맞는 시각적 디자인', included: true }, + { name: '디자인 시스템', description: '일관된 디자인을 위한 가이드라인', included: false } + ], + pricing: { + basePrice: 300000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 300000, max: 2000000 } + }, + estimatedTime: { min: 1, max: 6, unit: 'weeks' }, + featured: true, + isActive: true, + order: 3, + tags: ['Figma', 'Sketch', 'Adobe XD', 'Photoshop', 'Illustrator', 'Prototyping'] + }, + { + name: '디지털 마케팅', + description: 'SEO, 소셜미디어 마케팅, 온라인 광고를 통해 디지털 마케팅 전략을 수립하고 실행합니다. 데이터 분석을 통한 지속적인 최적화를 제공합니다.', + shortDescription: 'SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅', + icon: 'fas fa-chart-line', + category: 'marketing', + features: [ + { name: 'SEO 최적화', description: '검색엔진 상위 노출을 위한 최적화', included: true }, + { name: '소셜미디어 관리', description: '페이스북, 인스타그램, 유튜브 관리', included: true }, + { name: '구글 광고', description: '구글 애즈를 통한 타겟 광고', included: true }, + { name: '콘텐츠 마케팅', description: '블로그 및 콘텐츠 제작', included: false }, + { name: '분석 및 리포팅', description: '마케팅 성과 분석 및 보고서', included: true } + ], + pricing: { + basePrice: 200000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 200000, max: 1500000 } + }, + estimatedTime: { min: 2, max: 12, unit: 'weeks' }, + featured: true, + isActive: true, + order: 4, + tags: ['SEO', 'Google Ads', 'Facebook Ads', 'Analytics', 'Social Media', 'Content Marketing'] + }, + { + name: '브랜딩', + description: '기업의 정체성을 반영하는 로고, 브랜드 가이드라인, 마케팅 자료를 디자인합니다. 일관된 브랜드 이미지로 브랜드 가치를 높입니다.', + shortDescription: '로고, 브랜드 가이드라인, 마케팅 자료 디자인', + icon: 'fas fa-copyright', + category: 'design', + features: [ + { name: '로고 디자인', description: '기업 정체성을 반영한 로고 제작', included: true }, + { name: '브랜드 가이드라인', description: '색상, 폰트, 사용법 가이드', included: true }, + { name: '명함 디자인', description: '브랜드에 맞는 명함 디자인', included: true }, + { name: '브로슈어 디자인', description: '회사 소개 브로슈어 제작', included: false }, + { name: '웹 브랜딩', description: '웹사이트 브랜딩 요소 적용', included: false } + ], + pricing: { + basePrice: 400000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 400000, max: 2500000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: false, + isActive: true, + order: 5, + tags: ['Logo Design', 'Brand Identity', 'Graphic Design', 'Corporate Design'] + }, + { + name: '기술 컨설팅', + description: '기업의 디지털 전환과 기술 도입을 위한 전문적인 컨설팅을 제공합니다. 기술 아키텍처 설계부터 구현 전략까지 포괄적으로 지원합니다.', + shortDescription: '디지털 전환 및 기술 도입을 위한 전문 컨설팅', + icon: 'fas fa-lightbulb', + category: 'consulting', + features: [ + { name: '기술 분석', description: '현재 기술 스택 분석 및 개선안 제시', included: true }, + { name: '아키텍처 설계', description: '확장 가능한 시스템 아키텍처 설계', included: true }, + { name: '개발 프로세스 개선', description: '효율적인 개발 워크플로우 구축', included: true }, + { name: '팀 교육', description: '개발팀 대상 기술 교육', included: false }, + { name: '지속적인 지원', description: '프로젝트 완료 후 지속적인 기술 지원', included: false } + ], + pricing: { + basePrice: 150000, + currency: 'KRW', + priceType: 'hourly', + priceRange: { min: 150000, max: 500000 } + }, + estimatedTime: { min: 1, max: 4, unit: 'weeks' }, + featured: false, + isActive: true, + order: 6, + tags: ['Architecture', 'Strategy', 'Process', 'Training', 'Consultation'] + } + ]; + + await Service.bulkCreate(services); + console.log('✅ Sample services created successfully'); + } catch (error) { + console.error('❌ Error creating sample services:', error); + throw error; + } +} + +async function createSamplePortfolio() { + try { + const existingPortfolio = await Portfolio.count(); + + if (existingPortfolio > 0) { + console.log('🎨 Portfolio items already exist, skipping...'); + return; + } + + const portfolioItems = [ + { + title: 'E-commerce 플랫폼', + description: '현대적인 온라인 쇼핑몰 플랫폼을 개발했습니다. React와 Node.js를 활용하여 높은 성능과 사용자 경험을 제공하며, 결제 시스템과 재고 관리 기능을 포함합니다.', + shortDescription: '반응형 온라인 쇼핑몰 플랫폼 개발', + category: 'web-development', + technologies: ['React', 'Node.js', 'MongoDB', 'Express', 'Stripe', 'AWS'], + images: [ + { url: '/images/portfolio/ecommerce-1.jpg', alt: 'E-commerce 메인페이지', isPrimary: true }, + { url: '/images/portfolio/ecommerce-2.jpg', alt: '상품 상세페이지', isPrimary: false }, + { url: '/images/portfolio/ecommerce-3.jpg', alt: '장바구니 페이지', isPrimary: false } + ], + clientName: '패션 브랜드 ABC', + projectUrl: 'https://example-ecommerce.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-01-15'), + completedAt: new Date('2024-01-10'), + isPublished: true, + viewCount: 150, + likes: 25, + order: 1, + seo: { + metaTitle: 'E-commerce 플랫폼 개발 프로젝트', + metaDescription: '현대적인 온라인 쇼핑몰 플랫폼 개발 사례', + keywords: ['E-commerce', 'React', 'Node.js', '온라인쇼핑몰'] + } + }, + { + title: '모바일 피트니스 앱', + description: 'React Native를 사용하여 크로스플랫폼 피트니스 애플리케이션을 개발했습니다. 운동 계획, 칼로리 추적, 소셜 기능을 포함하여 사용자들의 건강한 라이프스타일을 지원합니다.', + shortDescription: '건강 관리를 위한 크로스플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['React Native', 'Redux', 'Firebase', 'Node.js', 'PostgreSQL'], + images: [ + { url: '/images/portfolio/fitness-1.jpg', alt: '피트니스 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/fitness-2.jpg', alt: '운동 계획 화면', isPrimary: false }, + { url: '/images/portfolio/fitness-3.jpg', alt: '통계 화면', isPrimary: false } + ], + clientName: '헬스케어 스타트업 FIT', + status: 'completed', + featured: true, + publishedAt: new Date('2024-02-20'), + completedAt: new Date('2024-02-15'), + isPublished: true, + viewCount: 200, + likes: 35, + order: 2, + seo: { + metaTitle: '모바일 피트니스 앱 개발 프로젝트', + metaDescription: 'React Native로 개발한 크로스플랫폼 피트니스 앱', + keywords: ['Mobile App', 'React Native', 'Fitness', '헬스케어'] + } + }, + { + title: '기업 웹사이트 리뉴얼', + description: '기업의 브랜드 아이덴티티를 반영한 웹사이트 리뉴얼 프로젝트입니다. 사용자 경험을 개선하고 모던한 디자인을 적용하여 브랜드 가치를 높였습니다.', + shortDescription: '브랜드 아이덴티티를 반영한 기업 웹사이트 리뉴얼', + category: 'ui-ux-design', + technologies: ['Figma', 'React', 'Sass', 'Framer Motion', 'Contentful'], + images: [ + { url: '/images/portfolio/corporate-1.jpg', alt: '기업 웹사이트 메인페이지', isPrimary: true }, + { url: '/images/portfolio/corporate-2.jpg', alt: '회사소개 페이지', isPrimary: false }, + { url: '/images/portfolio/corporate-3.jpg', alt: '서비스 페이지', isPrimary: false } + ], + clientName: '기술 기업 TechCorp', + projectUrl: 'https://example-corp.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-03-10'), + completedAt: new Date('2024-03-05'), + isPublished: true, + viewCount: 120, + likes: 18, + order: 3, + seo: { + metaTitle: '기업 웹사이트 리뉴얼 프로젝트', + metaDescription: '모던한 디자인과 향상된 UX의 기업 웹사이트', + keywords: ['Web Design', 'Corporate', 'UI/UX', '웹사이트리뉴얼'] + } + }, + { + title: '레스토랑 예약 시스템', + description: '레스토랑을 위한 온라인 예약 시스템을 개발했습니다. 실시간 테이블 현황, 예약 관리, 고객 관리 기능을 포함하여 레스토랑 운영 효율성을 높였습니다.', + shortDescription: '실시간 테이블 예약 및 관리 시스템', + category: 'web-development', + technologies: ['Vue.js', 'Laravel', 'MySQL', 'Socket.io', 'Bootstrap'], + images: [ + { url: '/images/portfolio/restaurant-1.jpg', alt: '예약 시스템 메인', isPrimary: true }, + { url: '/images/portfolio/restaurant-2.jpg', alt: '예약 현황 관리', isPrimary: false } + ], + clientName: '레스토랑 델리셔스', + status: 'completed', + featured: false, + publishedAt: new Date('2024-04-05'), + completedAt: new Date('2024-04-01'), + isPublished: true, + viewCount: 85, + likes: 12, + order: 4, + seo: { + metaTitle: '레스토랑 예약 시스템 개발', + metaDescription: '실시간 테이블 예약 및 관리 웹 시스템', + keywords: ['Reservation System', 'Vue.js', 'Laravel', '예약시스템'] + } + }, + { + title: '교육 플랫폼 앱', + description: '온라인 교육을 위한 모바일 애플리케이션입니다. 동영상 강의, 퀴즈, 진도 관리 기능을 포함하여 효과적인 학습 환경을 제공합니다.', + shortDescription: '온라인 학습을 위한 교육 플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['Flutter', 'Dart', 'Firebase', 'FFmpeg', 'AWS S3'], + images: [ + { url: '/images/portfolio/education-1.jpg', alt: '교육 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/education-2.jpg', alt: '강의 재생 화면', isPrimary: false } + ], + clientName: '온라인 교육 기업 EduTech', + status: 'completed', + featured: false, + publishedAt: new Date('2024-05-12'), + completedAt: new Date('2024-05-08'), + isPublished: true, + viewCount: 95, + likes: 20, + order: 5, + seo: { + metaTitle: '교육 플랫폼 모바일 앱 개발', + metaDescription: 'Flutter로 개발한 온라인 교육 모바일 앱', + keywords: ['Education App', 'Flutter', 'E-learning', '교육앱'] + } + }, + { + title: 'IoT 대시보드', + description: 'IoT 디바이스들을 모니터링하고 제어할 수 있는 웹 대시보드를 개발했습니다. 실시간 데이터 시각화와 알림 기능을 포함합니다.', + shortDescription: 'IoT 디바이스 모니터링 및 제어 웹 대시보드', + category: 'web-development', + technologies: ['React', 'D3.js', 'Node.js', 'MQTT', 'InfluxDB', 'Docker'], + images: [ + { url: '/images/portfolio/iot-1.jpg', alt: 'IoT 대시보드 메인', isPrimary: true } + ], + clientName: 'IoT 솔루션 기업 SmartDevice', + status: 'in-progress', + featured: false, + publishedAt: new Date('2024-06-01'), + isPublished: true, + viewCount: 45, + likes: 8, + order: 6, + seo: { + metaTitle: 'IoT 대시보드 개발 프로젝트', + metaDescription: '실시간 IoT 디바이스 모니터링 웹 대시보드', + keywords: ['IoT', 'Dashboard', 'Real-time', 'Monitoring'] + } + } + ]; + + await Portfolio.bulkCreate(portfolioItems); + console.log('✅ Sample portfolio items created successfully'); + } catch (error) { + console.error('❌ Error creating sample portfolio:', error); + throw error; + } +} + +async function createSiteSettings() { + try { + const existingSettings = await SiteSettings.findOne(); + + if (existingSettings) { + console.log('⚙️ Site settings already exist, skipping...'); + return; + } + + const settings = await SiteSettings.create({ + siteName: 'SmartSolTech', + siteDescription: '혁신적인 기술 솔루션으로 비즈니스의 성장을 지원합니다', + logo: '/images/logo.png', + favicon: '/images/favicon.ico', + contact: { + email: 'info@smartsoltech.kr', + phone: '+82-10-1234-5678', + address: 'Seoul, South Korea' + }, + social: { + facebook: 'https://facebook.com/smartsoltech', + twitter: 'https://twitter.com/smartsoltech', + linkedin: 'https://linkedin.com/company/smartsoltech', + instagram: 'https://instagram.com/smartsoltech', + github: 'https://github.com/smartsoltech' + }, + telegram: { + isEnabled: false + }, + seo: { + metaTitle: 'SmartSolTech - 혁신적인 기술 솔루션', + metaDescription: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅 전문 기업. 한국에서 최고의 기술 솔루션을 제공합니다.', + keywords: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅, 기술 솔루션, 한국, SmartSolTech' + }, + hero: { + title: 'Smart Technology Solutions', + subtitle: '혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비즈니스의 디지털 전환을 이끌어갑니다', + backgroundImage: '/images/hero-bg.jpg', + ctaText: '프로젝트 시작하기', + ctaLink: '/contact' + }, + about: { + title: 'SmartSolTech 소개', + description: '우리는 최신 기술과 창의적인 아이디어로 고객의 비즈니스 성장을 지원하는 전문 개발팀입니다. 웹 개발부터 모바일 앱, UI/UX 디자인까지 포괄적인 디지털 솔루션을 제공합니다.', + image: '/images/about.jpg' + }, + maintenance: { + isEnabled: false, + message: '현재 시스템 점검 중입니다. 잠시 후 다시 접속해 주세요.' + } + }); + + console.log('✅ Site settings created successfully'); + } catch (error) { + console.error('❌ Error creating site settings:', error); + throw error; + } +} + +// Run the initialization +if (require.main === module) { + initializeDatabase(); +} + +module.exports = { + initializeDatabase, + createAdminUser, + createSampleServices, + createSamplePortfolio, + createSiteSettings +}; \ No newline at end of file diff --git a/.history/scripts/init-db_20251019202631.js b/.history/scripts/init-db_20251019202631.js new file mode 100644 index 0000000..6ee7fc9 --- /dev/null +++ b/.history/scripts/init-db_20251019202631.js @@ -0,0 +1,495 @@ +#!/usr/bin/env node + +/** + * Database initialization script for SmartSolTech + * Creates initial admin user and sample data for PostgreSQL + */ + +const { sequelize } = require('../config/database'); +require('dotenv').config(); + +// Import models +const { User, Service, Portfolio, SiteSettings } = require('../models'); + +// Configuration +const ADMIN_EMAIL = process.env.ADMIN_EMAIL || 'admin@smartsoltech.kr'; +const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'admin123456'; + +async function initializeDatabase() { + try { + console.log('🔄 Connecting to PostgreSQL...'); + await sequelize.authenticate(); + console.log('✅ Connected to PostgreSQL'); + + // Sync database (create tables) + console.log('🔄 Syncing database schema...'); + await sequelize.sync({ force: false }); + console.log('✅ Database schema synchronized'); + + // Create admin user + await createAdminUser(); + + // Create sample services + await createSampleServices(); + + // Create sample portfolio items + await createSamplePortfolio(); + + // Create site settings + await createSiteSettings(); + + console.log('🎉 Database initialization completed successfully!'); + console.log(`📧 Admin login: ${ADMIN_EMAIL}`); + console.log(`🔑 Admin password: ${ADMIN_PASSWORD}`); + console.log('⚠️ Please change the admin password after first login!'); + + } catch (error) { + console.error('❌ Database initialization failed:', error); + process.exit(1); + } finally { + await sequelize.close(); + console.log('🔌 Database connection closed'); + process.exit(0); + } +} + +async function createAdminUser() { + try { + const existingAdmin = await User.findOne({ + where: { email: ADMIN_EMAIL } + }); + + if (existingAdmin) { + console.log('👤 Admin user already exists, skipping...'); + return; + } + + const adminUser = await User.create({ + name: 'Administrator', + email: ADMIN_EMAIL, + password: ADMIN_PASSWORD, + role: 'admin', + isActive: true + }); + + console.log('✅ Admin user created successfully'); + } catch (error) { + console.error('❌ Error creating admin user:', error); + throw error; + } +} + +async function createSampleServices() { + try { + const existingServices = await Service.count(); + + if (existingServices > 0) { + console.log('🛠️ Services already exist, skipping...'); + return; + } + + const services = [ + { + name: '웹 개발', + description: '현대적이고 반응형인 웹사이트와 웹 애플리케이션을 개발합니다. React, Node.js, MongoDB 등 최신 기술 스택을 활용하여 성능과 사용자 경험을 최적화합니다.', + shortDescription: '현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발', + icon: 'fas fa-code', + category: 'development', + features: [ + { name: '반응형 디자인', description: '모든 디바이스에서 완벽하게 작동', included: true }, + { name: 'SEO 최적화', description: '검색엔진 최적화로 더 많은 방문자 유치', included: true }, + { name: '성능 최적화', description: '빠른 로딩 속도와 원활한 사용자 경험', included: true }, + { name: '보안 강화', description: '최신 보안 기술로 안전한 웹사이트', included: true }, + { name: '유지보수', description: '6개월 무료 유지보수 지원', included: true } + ], + pricing: { + basePrice: 500000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 500000, max: 5000000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: true, + isActive: true, + order: 1, + tags: ['React', 'Node.js', 'MongoDB', 'Express', 'JavaScript', 'HTML', 'CSS'] + }, + { + name: '모바일 앱 개발', + description: 'iOS와 Android 플랫폼을 위한 네이티브 및 크로스플랫폼 모바일 애플리케이션을 개발합니다. React Native, Flutter 등을 활용하여 효율적인 개발을 진행합니다.', + shortDescription: 'iOS/Android 네이티브 및 크로스플랫폼 앱 개발', + icon: 'fas fa-mobile-alt', + category: 'development', + features: [ + { name: '크로스플랫폼 개발', description: 'iOS와 Android 동시 지원', included: true }, + { name: '네이티브 성능', description: '최적화된 성능과 사용자 경험', included: true }, + { name: '푸시 알림', description: '실시간 푸시 알림 시스템', included: true }, + { name: '오프라인 지원', description: '인터넷 연결 없이도 기본 기능 사용 가능', included: false }, + { name: '앱스토어 등록', description: '앱스토어 및 플레이스토어 등록 지원', included: true } + ], + pricing: { + basePrice: 800000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 800000, max: 8000000 } + }, + estimatedTime: { min: 4, max: 16, unit: 'weeks' }, + featured: true, + isActive: true, + order: 2, + tags: ['React Native', 'Flutter', 'iOS', 'Android', 'Swift', 'Kotlin', 'JavaScript'] + }, + { + name: 'UI/UX 디자인', + description: '사용자 중심의 직관적이고 아름다운 인터페이스를 디자인합니다. 사용자 경험 연구와 프로토타이핑을 통해 최적의 디자인 솔루션을 제공합니다.', + shortDescription: '사용자 중심의 직관적이고 아름다운 인터페이스 디자인', + icon: 'fas fa-palette', + category: 'design', + features: [ + { name: '사용자 경험 연구', description: '타겟 사용자 분석 및 페르소나 설정', included: true }, + { name: '와이어프레임', description: '정보 구조와 레이아웃 설계', included: true }, + { name: '프로토타이핑', description: '인터랙티브 프로토타입 제작', included: true }, + { name: '비주얼 디자인', description: '브랜드에 맞는 시각적 디자인', included: true }, + { name: '디자인 시스템', description: '일관된 디자인을 위한 가이드라인', included: false } + ], + pricing: { + basePrice: 300000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 300000, max: 2000000 } + }, + estimatedTime: { min: 1, max: 6, unit: 'weeks' }, + featured: true, + isActive: true, + order: 3, + tags: ['Figma', 'Sketch', 'Adobe XD', 'Photoshop', 'Illustrator', 'Prototyping'] + }, + { + name: '디지털 마케팅', + description: 'SEO, 소셜미디어 마케팅, 온라인 광고를 통해 디지털 마케팅 전략을 수립하고 실행합니다. 데이터 분석을 통한 지속적인 최적화를 제공합니다.', + shortDescription: 'SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅', + icon: 'fas fa-chart-line', + category: 'marketing', + features: [ + { name: 'SEO 최적화', description: '검색엔진 상위 노출을 위한 최적화', included: true }, + { name: '소셜미디어 관리', description: '페이스북, 인스타그램, 유튜브 관리', included: true }, + { name: '구글 광고', description: '구글 애즈를 통한 타겟 광고', included: true }, + { name: '콘텐츠 마케팅', description: '블로그 및 콘텐츠 제작', included: false }, + { name: '분석 및 리포팅', description: '마케팅 성과 분석 및 보고서', included: true } + ], + pricing: { + basePrice: 200000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 200000, max: 1500000 } + }, + estimatedTime: { min: 2, max: 12, unit: 'weeks' }, + featured: true, + isActive: true, + order: 4, + tags: ['SEO', 'Google Ads', 'Facebook Ads', 'Analytics', 'Social Media', 'Content Marketing'] + }, + { + name: '브랜딩', + description: '기업의 정체성을 반영하는 로고, 브랜드 가이드라인, 마케팅 자료를 디자인합니다. 일관된 브랜드 이미지로 브랜드 가치를 높입니다.', + shortDescription: '로고, 브랜드 가이드라인, 마케팅 자료 디자인', + icon: 'fas fa-copyright', + category: 'design', + features: [ + { name: '로고 디자인', description: '기업 정체성을 반영한 로고 제작', included: true }, + { name: '브랜드 가이드라인', description: '색상, 폰트, 사용법 가이드', included: true }, + { name: '명함 디자인', description: '브랜드에 맞는 명함 디자인', included: true }, + { name: '브로슈어 디자인', description: '회사 소개 브로슈어 제작', included: false }, + { name: '웹 브랜딩', description: '웹사이트 브랜딩 요소 적용', included: false } + ], + pricing: { + basePrice: 400000, + currency: 'KRW', + priceType: 'project', + priceRange: { min: 400000, max: 2500000 } + }, + estimatedTime: { min: 2, max: 8, unit: 'weeks' }, + featured: false, + isActive: true, + order: 5, + tags: ['Logo Design', 'Brand Identity', 'Graphic Design', 'Corporate Design'] + }, + { + name: '기술 컨설팅', + description: '기업의 디지털 전환과 기술 도입을 위한 전문적인 컨설팅을 제공합니다. 기술 아키텍처 설계부터 구현 전략까지 포괄적으로 지원합니다.', + shortDescription: '디지털 전환 및 기술 도입을 위한 전문 컨설팅', + icon: 'fas fa-lightbulb', + category: 'consulting', + features: [ + { name: '기술 분석', description: '현재 기술 스택 분석 및 개선안 제시', included: true }, + { name: '아키텍처 설계', description: '확장 가능한 시스템 아키텍처 설계', included: true }, + { name: '개발 프로세스 개선', description: '효율적인 개발 워크플로우 구축', included: true }, + { name: '팀 교육', description: '개발팀 대상 기술 교육', included: false }, + { name: '지속적인 지원', description: '프로젝트 완료 후 지속적인 기술 지원', included: false } + ], + pricing: { + basePrice: 150000, + currency: 'KRW', + priceType: 'hourly', + priceRange: { min: 150000, max: 500000 } + }, + estimatedTime: { min: 1, max: 4, unit: 'weeks' }, + featured: false, + isActive: true, + order: 6, + tags: ['Architecture', 'Strategy', 'Process', 'Training', 'Consultation'] + } + ]; + + await Service.bulkCreate(services); + console.log('✅ Sample services created successfully'); + } catch (error) { + console.error('❌ Error creating sample services:', error); + throw error; + } +} + +async function createSamplePortfolio() { + try { + const existingPortfolio = await Portfolio.count(); + + if (existingPortfolio > 0) { + console.log('🎨 Portfolio items already exist, skipping...'); + return; + } + + const portfolioItems = [ + { + title: 'E-commerce 플랫폼', + description: '현대적인 온라인 쇼핑몰 플랫폼을 개발했습니다. React와 Node.js를 활용하여 높은 성능과 사용자 경험을 제공하며, 결제 시스템과 재고 관리 기능을 포함합니다.', + shortDescription: '반응형 온라인 쇼핑몰 플랫폼 개발', + category: 'web-development', + technologies: ['React', 'Node.js', 'MongoDB', 'Express', 'Stripe', 'AWS'], + images: [ + { url: '/images/portfolio/ecommerce-1.jpg', alt: 'E-commerce 메인페이지', isPrimary: true }, + { url: '/images/portfolio/ecommerce-2.jpg', alt: '상품 상세페이지', isPrimary: false }, + { url: '/images/portfolio/ecommerce-3.jpg', alt: '장바구니 페이지', isPrimary: false } + ], + clientName: '패션 브랜드 ABC', + projectUrl: 'https://example-ecommerce.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-01-15'), + completedAt: new Date('2024-01-10'), + isPublished: true, + viewCount: 150, + likes: 25, + order: 1, + seo: { + metaTitle: 'E-commerce 플랫폼 개발 프로젝트', + metaDescription: '현대적인 온라인 쇼핑몰 플랫폼 개발 사례', + keywords: ['E-commerce', 'React', 'Node.js', '온라인쇼핑몰'] + } + }, + { + title: '모바일 피트니스 앱', + description: 'React Native를 사용하여 크로스플랫폼 피트니스 애플리케이션을 개발했습니다. 운동 계획, 칼로리 추적, 소셜 기능을 포함하여 사용자들의 건강한 라이프스타일을 지원합니다.', + shortDescription: '건강 관리를 위한 크로스플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['React Native', 'Redux', 'Firebase', 'Node.js', 'PostgreSQL'], + images: [ + { url: '/images/portfolio/fitness-1.jpg', alt: '피트니스 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/fitness-2.jpg', alt: '운동 계획 화면', isPrimary: false }, + { url: '/images/portfolio/fitness-3.jpg', alt: '통계 화면', isPrimary: false } + ], + clientName: '헬스케어 스타트업 FIT', + status: 'completed', + featured: true, + publishedAt: new Date('2024-02-20'), + completedAt: new Date('2024-02-15'), + isPublished: true, + viewCount: 200, + likes: 35, + order: 2, + seo: { + metaTitle: '모바일 피트니스 앱 개발 프로젝트', + metaDescription: 'React Native로 개발한 크로스플랫폼 피트니스 앱', + keywords: ['Mobile App', 'React Native', 'Fitness', '헬스케어'] + } + }, + { + title: '기업 웹사이트 리뉴얼', + description: '기업의 브랜드 아이덴티티를 반영한 웹사이트 리뉴얼 프로젝트입니다. 사용자 경험을 개선하고 모던한 디자인을 적용하여 브랜드 가치를 높였습니다.', + shortDescription: '브랜드 아이덴티티를 반영한 기업 웹사이트 리뉴얼', + category: 'ui-ux-design', + technologies: ['Figma', 'React', 'Sass', 'Framer Motion', 'Contentful'], + images: [ + { url: '/images/portfolio/corporate-1.jpg', alt: '기업 웹사이트 메인페이지', isPrimary: true }, + { url: '/images/portfolio/corporate-2.jpg', alt: '회사소개 페이지', isPrimary: false }, + { url: '/images/portfolio/corporate-3.jpg', alt: '서비스 페이지', isPrimary: false } + ], + clientName: '기술 기업 TechCorp', + projectUrl: 'https://example-corp.com', + status: 'completed', + featured: true, + publishedAt: new Date('2024-03-10'), + completedAt: new Date('2024-03-05'), + isPublished: true, + viewCount: 120, + likes: 18, + order: 3, + seo: { + metaTitle: '기업 웹사이트 리뉴얼 프로젝트', + metaDescription: '모던한 디자인과 향상된 UX의 기업 웹사이트', + keywords: ['Web Design', 'Corporate', 'UI/UX', '웹사이트리뉴얼'] + } + }, + { + title: '레스토랑 예약 시스템', + description: '레스토랑을 위한 온라인 예약 시스템을 개발했습니다. 실시간 테이블 현황, 예약 관리, 고객 관리 기능을 포함하여 레스토랑 운영 효율성을 높였습니다.', + shortDescription: '실시간 테이블 예약 및 관리 시스템', + category: 'web-development', + technologies: ['Vue.js', 'Laravel', 'MySQL', 'Socket.io', 'Bootstrap'], + images: [ + { url: '/images/portfolio/restaurant-1.jpg', alt: '예약 시스템 메인', isPrimary: true }, + { url: '/images/portfolio/restaurant-2.jpg', alt: '예약 현황 관리', isPrimary: false } + ], + clientName: '레스토랑 델리셔스', + status: 'completed', + featured: false, + publishedAt: new Date('2024-04-05'), + completedAt: new Date('2024-04-01'), + isPublished: true, + viewCount: 85, + likes: 12, + order: 4, + seo: { + metaTitle: '레스토랑 예약 시스템 개발', + metaDescription: '실시간 테이블 예약 및 관리 웹 시스템', + keywords: ['Reservation System', 'Vue.js', 'Laravel', '예약시스템'] + } + }, + { + title: '교육 플랫폼 앱', + description: '온라인 교육을 위한 모바일 애플리케이션입니다. 동영상 강의, 퀴즈, 진도 관리 기능을 포함하여 효과적인 학습 환경을 제공합니다.', + shortDescription: '온라인 학습을 위한 교육 플랫폼 모바일 앱', + category: 'mobile-app', + technologies: ['Flutter', 'Dart', 'Firebase', 'FFmpeg', 'AWS S3'], + images: [ + { url: '/images/portfolio/education-1.jpg', alt: '교육 앱 메인화면', isPrimary: true }, + { url: '/images/portfolio/education-2.jpg', alt: '강의 재생 화면', isPrimary: false } + ], + clientName: '온라인 교육 기업 EduTech', + status: 'completed', + featured: false, + publishedAt: new Date('2024-05-12'), + completedAt: new Date('2024-05-08'), + isPublished: true, + viewCount: 95, + likes: 20, + order: 5, + seo: { + metaTitle: '교육 플랫폼 모바일 앱 개발', + metaDescription: 'Flutter로 개발한 온라인 교육 모바일 앱', + keywords: ['Education App', 'Flutter', 'E-learning', '교육앱'] + } + }, + { + title: 'IoT 대시보드', + description: 'IoT 디바이스들을 모니터링하고 제어할 수 있는 웹 대시보드를 개발했습니다. 실시간 데이터 시각화와 알림 기능을 포함합니다.', + shortDescription: 'IoT 디바이스 모니터링 및 제어 웹 대시보드', + category: 'web-development', + technologies: ['React', 'D3.js', 'Node.js', 'MQTT', 'InfluxDB', 'Docker'], + images: [ + { url: '/images/portfolio/iot-1.jpg', alt: 'IoT 대시보드 메인', isPrimary: true } + ], + clientName: 'IoT 솔루션 기업 SmartDevice', + status: 'in-progress', + featured: false, + publishedAt: new Date('2024-06-01'), + isPublished: true, + viewCount: 45, + likes: 8, + order: 6, + seo: { + metaTitle: 'IoT 대시보드 개발 프로젝트', + metaDescription: '실시간 IoT 디바이스 모니터링 웹 대시보드', + keywords: ['IoT', 'Dashboard', 'Real-time', 'Monitoring'] + } + } + ]; + + await Portfolio.bulkCreate(portfolioItems); + console.log('✅ Sample portfolio items created successfully'); + } catch (error) { + console.error('❌ Error creating sample portfolio:', error); + throw error; + } +} + +async function createSiteSettings() { + try { + const existingSettings = await SiteSettings.findOne(); + + if (existingSettings) { + console.log('⚙️ Site settings already exist, skipping...'); + return; + } + + const settings = await SiteSettings.create({ + siteName: 'SmartSolTech', + siteDescription: '혁신적인 기술 솔루션으로 비즈니스의 성장을 지원합니다', + logo: '/images/logo.png', + favicon: '/images/favicon.ico', + contact: { + email: 'info@smartsoltech.kr', + phone: '+82-10-1234-5678', + address: 'Seoul, South Korea' + }, + social: { + facebook: 'https://facebook.com/smartsoltech', + twitter: 'https://twitter.com/smartsoltech', + linkedin: 'https://linkedin.com/company/smartsoltech', + instagram: 'https://instagram.com/smartsoltech', + github: 'https://github.com/smartsoltech' + }, + telegram: { + isEnabled: false + }, + seo: { + metaTitle: 'SmartSolTech - 혁신적인 기술 솔루션', + metaDescription: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅 전문 기업. 한국에서 최고의 기술 솔루션을 제공합니다.', + keywords: '웹 개발, 모바일 앱, UI/UX 디자인, 디지털 마케팅, 기술 솔루션, 한국, SmartSolTech' + }, + hero: { + title: 'Smart Technology Solutions', + subtitle: '혁신적인 웹 개발, 모바일 앱, UI/UX 디자인으로 비즈니스의 디지털 전환을 이끌어갑니다', + backgroundImage: '/images/hero-bg.jpg', + ctaText: '프로젝트 시작하기', + ctaLink: '/contact' + }, + about: { + title: 'SmartSolTech 소개', + description: '우리는 최신 기술과 창의적인 아이디어로 고객의 비즈니스 성장을 지원하는 전문 개발팀입니다. 웹 개발부터 모바일 앱, UI/UX 디자인까지 포괄적인 디지털 솔루션을 제공합니다.', + image: '/images/about.jpg' + }, + maintenance: { + isEnabled: false, + message: '현재 시스템 점검 중입니다. 잠시 후 다시 접속해 주세요.' + } + }); + + console.log('✅ Site settings created successfully'); + } catch (error) { + console.error('❌ Error creating site settings:', error); + throw error; + } +} + +// Run the initialization +if (require.main === module) { + initializeDatabase(); +} + +module.exports = { + initializeDatabase, + createAdminUser, + createSampleServices, + createSamplePortfolio, + createSiteSettings +}; \ No newline at end of file diff --git a/.history/scripts/sync-locales_20251021172132.js b/.history/scripts/sync-locales_20251021172132.js new file mode 100644 index 0000000..e3f4eee --- /dev/null +++ b/.history/scripts/sync-locales_20251021172132.js @@ -0,0 +1,115 @@ +const fs = require('fs'); +const path = require('path'); + +const localesDir = path.join(__dirname, '..', 'locales'); +const files = fs.readdirSync(localesDir).filter(f => f.endsWith('.json')); + +// priority order for falling back when filling missing translations +const priority = ['en.json', 'ko.json', 'ru.json', 'kk.json']; + +function readJSON(file) { + try { + return JSON.parse(fs.readFileSync(path.join(localesDir, file), 'utf8')); + } catch (e) { + console.error('Failed to read', file, e.message); + return {}; + } +} + +function flatten(obj, prefix = '') { + const res = {}; + for (const k of Object.keys(obj)) { + const val = obj[k]; + const key = prefix ? `${prefix}.${k}` : k; + if (val && typeof val === 'object' && !Array.isArray(val)) { + Object.assign(res, flatten(val, key)); + } else { + res[key] = val; + } + } + return res; +} + +function unflatten(flat) { + const res = {}; + for (const flatKey of Object.keys(flat)) { + const parts = flatKey.split('.'); + let cur = res; + for (let i = 0; i < parts.length; i++) { + const p = parts[i]; + if (i === parts.length - 1) { + cur[p] = flat[flatKey]; + } else { + cur[p] = cur[p] || {}; + cur = cur[p]; + } + } + } + return res; +} + +// load all +const data = {}; +for (const f of files) { + data[f] = readJSON(f); +} + +// use en.json as canonical key set if exists, else merge all keys +let canonical = {}; +if (files.includes('en.json')) { + canonical = flatten(data['en.json']); +} else { + for (const f of files) { + canonical = Object.assign(canonical, flatten(data[f])); + } +} + +function getFallback(key, currentFile) { + for (const p of priority) { + if (p === currentFile) continue; + if (!files.includes(p)) continue; + const flat = flatten(data[p]); + if (flat[key] && typeof flat[key] === 'string' && flat[key].trim()) return flat[key]; + } + return null; +} + +let report = {}; +for (const f of files) { + const flat = flatten(data[f]); + report[f] = { added: 0, marked: 0 }; + const out = {}; + for (const key of Object.keys(canonical)) { + if (flat.hasOwnProperty(key)) { + out[key] = flat[key]; + } else { + const fb = getFallback(key, f); + if (fb) { + out[key] = fb; + } else { + out[key] = '[TRANSLATE] ' + canonical[key]; + report[f].marked++; + } + report[f].added++; + } + } + // also keep any extra keys present in this file but not in canonical + for (const key of Object.keys(flat)) { + if (!out.hasOwnProperty(key)) { + out[key] = flat[key]; + } + } + // write back + const nested = unflatten(out); + try { + fs.writeFileSync(path.join(localesDir, f), JSON.stringify(nested, null, 2), 'utf8'); + } catch (e) { + console.error('Failed to write', f, e.message); + } +} + +console.log('Locale sync report:'); +for (const f of files) { + console.log(`- ${f}: added ${report[f].added} keys, ${report[f].marked} marked for translation`); +} +console.log('Done. Review files in locales/ and replace [TRANSLATE] placeholders with correct translations.'); diff --git a/.history/scripts/sync-locales_20251021172239.js b/.history/scripts/sync-locales_20251021172239.js new file mode 100644 index 0000000..e3f4eee --- /dev/null +++ b/.history/scripts/sync-locales_20251021172239.js @@ -0,0 +1,115 @@ +const fs = require('fs'); +const path = require('path'); + +const localesDir = path.join(__dirname, '..', 'locales'); +const files = fs.readdirSync(localesDir).filter(f => f.endsWith('.json')); + +// priority order for falling back when filling missing translations +const priority = ['en.json', 'ko.json', 'ru.json', 'kk.json']; + +function readJSON(file) { + try { + return JSON.parse(fs.readFileSync(path.join(localesDir, file), 'utf8')); + } catch (e) { + console.error('Failed to read', file, e.message); + return {}; + } +} + +function flatten(obj, prefix = '') { + const res = {}; + for (const k of Object.keys(obj)) { + const val = obj[k]; + const key = prefix ? `${prefix}.${k}` : k; + if (val && typeof val === 'object' && !Array.isArray(val)) { + Object.assign(res, flatten(val, key)); + } else { + res[key] = val; + } + } + return res; +} + +function unflatten(flat) { + const res = {}; + for (const flatKey of Object.keys(flat)) { + const parts = flatKey.split('.'); + let cur = res; + for (let i = 0; i < parts.length; i++) { + const p = parts[i]; + if (i === parts.length - 1) { + cur[p] = flat[flatKey]; + } else { + cur[p] = cur[p] || {}; + cur = cur[p]; + } + } + } + return res; +} + +// load all +const data = {}; +for (const f of files) { + data[f] = readJSON(f); +} + +// use en.json as canonical key set if exists, else merge all keys +let canonical = {}; +if (files.includes('en.json')) { + canonical = flatten(data['en.json']); +} else { + for (const f of files) { + canonical = Object.assign(canonical, flatten(data[f])); + } +} + +function getFallback(key, currentFile) { + for (const p of priority) { + if (p === currentFile) continue; + if (!files.includes(p)) continue; + const flat = flatten(data[p]); + if (flat[key] && typeof flat[key] === 'string' && flat[key].trim()) return flat[key]; + } + return null; +} + +let report = {}; +for (const f of files) { + const flat = flatten(data[f]); + report[f] = { added: 0, marked: 0 }; + const out = {}; + for (const key of Object.keys(canonical)) { + if (flat.hasOwnProperty(key)) { + out[key] = flat[key]; + } else { + const fb = getFallback(key, f); + if (fb) { + out[key] = fb; + } else { + out[key] = '[TRANSLATE] ' + canonical[key]; + report[f].marked++; + } + report[f].added++; + } + } + // also keep any extra keys present in this file but not in canonical + for (const key of Object.keys(flat)) { + if (!out.hasOwnProperty(key)) { + out[key] = flat[key]; + } + } + // write back + const nested = unflatten(out); + try { + fs.writeFileSync(path.join(localesDir, f), JSON.stringify(nested, null, 2), 'utf8'); + } catch (e) { + console.error('Failed to write', f, e.message); + } +} + +console.log('Locale sync report:'); +for (const f of files) { + console.log(`- ${f}: added ${report[f].added} keys, ${report[f].marked} marked for translation`); +} +console.log('Done. Review files in locales/ and replace [TRANSLATE] placeholders with correct translations.'); diff --git a/.history/server_20251019201946.js b/.history/server_20251019201946.js new file mode 100644 index 0000000..9b233cc --- /dev/null +++ b/.history/server_20251019201946.js @@ -0,0 +1,121 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +require('dotenv').config(); + +const app = express(); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection +mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/smartsoltech', { + useNewUrlParser: true, + useUnifiedTopology: true, +}) +.then(() => console.log('✓ MongoDB connected')) +.catch(err => console.error('✗ MongoDB connection error:', err)); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: MongoStore.create({ + mongoUrl: process.env.MONGODB_URI || 'mongodb://localhost:27017/smartsoltech', + touchAfter: 24 * 3600 // lazy session update + }), + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).json({ + success: false, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('404', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена' + }); +}); + +const PORT = process.env.PORT || 3000; + +app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); +}); + +module.exports = app; \ No newline at end of file diff --git a/.history/server_20251019201954.js b/.history/server_20251019201954.js new file mode 100644 index 0000000..9f5dc0b --- /dev/null +++ b/.history/server_20251019201954.js @@ -0,0 +1,124 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +require('dotenv').config(); + +const app = express(); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: MongoStore.create({ + mongoUrl: process.env.MONGODB_URI || 'mongodb://localhost:27017/smartsoltech', + touchAfter: 24 * 3600 // lazy session update + }), + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).json({ + success: false, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('404', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена' + }); +}); + +const PORT = process.env.PORT || 3000; + +app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); +}); + +module.exports = app; \ No newline at end of file diff --git a/.history/server_20251019202002.js b/.history/server_20251019202002.js new file mode 100644 index 0000000..2a9d835 --- /dev/null +++ b/.history/server_20251019202002.js @@ -0,0 +1,121 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +require('dotenv').config(); + +const app = express(); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).json({ + success: false, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('404', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена' + }); +}); + +const PORT = process.env.PORT || 3000; + +app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); +}); + +module.exports = app; \ No newline at end of file diff --git a/.history/server_20251019202011.js b/.history/server_20251019202011.js new file mode 100644 index 0000000..a5313b2 --- /dev/null +++ b/.history/server_20251019202011.js @@ -0,0 +1,137 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +require('dotenv').config(); + +const app = express(); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).json({ + success: false, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('404', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251019202631.js b/.history/server_20251019202631.js new file mode 100644 index 0000000..a5313b2 --- /dev/null +++ b/.history/server_20251019202631.js @@ -0,0 +1,137 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +require('dotenv').config(); + +const app = express(); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).json({ + success: false, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('404', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251019203324.js b/.history/server_20251019203324.js new file mode 100644 index 0000000..f916b5e --- /dev/null +++ b/.history/server_20251019203324.js @@ -0,0 +1,138 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +require('dotenv').config(); + +const app = express(); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('404', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251019203331.js b/.history/server_20251019203331.js new file mode 100644 index 0000000..e0b53bf --- /dev/null +++ b/.history/server_20251019203331.js @@ -0,0 +1,139 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +require('dotenv').config(); + +const app = express(); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251019203341.js b/.history/server_20251019203341.js new file mode 100644 index 0000000..e0b53bf --- /dev/null +++ b/.history/server_20251019203341.js @@ -0,0 +1,139 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +require('dotenv').config(); + +const app = express(); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251019203555.js b/.history/server_20251019203555.js new file mode 100644 index 0000000..210a4f7 --- /dev/null +++ b/.history/server_20251019203555.js @@ -0,0 +1,153 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251019203603.js b/.history/server_20251019203603.js new file mode 100644 index 0000000..4c06e0a --- /dev/null +++ b/.history/server_20251019203603.js @@ -0,0 +1,161 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + res.locals.locale = req.getLocale(); + res.locals.__ = res.__; + res.locals.theme = req.session.theme || 'light'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251019203613.js b/.history/server_20251019203613.js new file mode 100644 index 0000000..0bccad5 --- /dev/null +++ b/.history/server_20251019203613.js @@ -0,0 +1,167 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + res.locals.locale = req.getLocale(); + res.locals.__ = res.__; + res.locals.theme = req.session.theme || 'light'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена', + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251019203723.js b/.history/server_20251019203723.js new file mode 100644 index 0000000..0bccad5 --- /dev/null +++ b/.history/server_20251019203723.js @@ -0,0 +1,167 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + res.locals.locale = req.getLocale(); + res.locals.__ = res.__; + res.locals.theme = req.session.theme || 'light'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена', + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251019204032.js b/.history/server_20251019204032.js new file mode 100644 index 0000000..fc6cb35 --- /dev/null +++ b/.history/server_20251019204032.js @@ -0,0 +1,168 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + res.locals.locale = req.getLocale(); + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = req.getLocale(); + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена', + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251019204034.js b/.history/server_20251019204034.js new file mode 100644 index 0000000..fc6cb35 --- /dev/null +++ b/.history/server_20251019204034.js @@ -0,0 +1,168 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + res.locals.locale = req.getLocale(); + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = req.getLocale(); + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена', + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251019204044.js b/.history/server_20251019204044.js new file mode 100644 index 0000000..c7239ee --- /dev/null +++ b/.history/server_20251019204044.js @@ -0,0 +1,170 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + res.locals.locale = req.getLocale(); + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = req.getLocale(); + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light', + currentLanguage: req.getLocale ? req.getLocale() : 'ko' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена', + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light', + currentLanguage: req.getLocale ? req.getLocale() : 'ko' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251019204050.js b/.history/server_20251019204050.js new file mode 100644 index 0000000..c7239ee --- /dev/null +++ b/.history/server_20251019204050.js @@ -0,0 +1,170 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + res.locals.locale = req.getLocale(); + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = req.getLocale(); + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light', + currentLanguage: req.getLocale ? req.getLocale() : 'ko' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена', + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light', + currentLanguage: req.getLocale ? req.getLocale() : 'ko' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251020040200.js b/.history/server_20251020040200.js new file mode 100644 index 0000000..1dde76f --- /dev/null +++ b/.history/server_20251020040200.js @@ -0,0 +1,193 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + res.locals.locale = req.getLocale(); + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = req.getLocale(); + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light', + currentLanguage: req.getLocale ? req.getLocale() : 'ko' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена', + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light', + currentLanguage: req.getLocale ? req.getLocale() : 'ko' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251020040210.js b/.history/server_20251020040210.js new file mode 100644 index 0000000..80454d0 --- /dev/null +++ b/.history/server_20251020040210.js @@ -0,0 +1,197 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ko'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light', + currentLanguage: req.getLocale ? req.getLocale() : 'ko' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - Страница не найдена', + message: 'Запрашиваемая страница не найдена', + currentPage: 'error', + locale: req.getLocale ? req.getLocale() : 'ko', + __: res.__ || ((key) => key), + theme: req.session?.theme || 'light', + currentLanguage: req.getLocale ? req.getLocale() : 'ko' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251020040221.js b/.history/server_20251020040221.js new file mode 100644 index 0000000..bda65fc --- /dev/null +++ b/.history/server_20251020040221.js @@ -0,0 +1,191 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ko'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251020040538.js b/.history/server_20251020040538.js new file mode 100644 index 0000000..bda65fc --- /dev/null +++ b/.history/server_20251020040538.js @@ -0,0 +1,191 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ko'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251020042047.js b/.history/server_20251020042047.js new file mode 100644 index 0000000..6b8d520 --- /dev/null +++ b/.history/server_20251020042047.js @@ -0,0 +1,191 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ko'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251020042121.js b/.history/server_20251020042121.js new file mode 100644 index 0000000..6b8d520 --- /dev/null +++ b/.history/server_20251020042121.js @@ -0,0 +1,191 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ko'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251020042412.js b/.history/server_20251020042412.js new file mode 100644 index 0000000..0f99a8d --- /dev/null +++ b/.history/server_20251020042412.js @@ -0,0 +1,191 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ko'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251020042429.js b/.history/server_20251020042429.js new file mode 100644 index 0000000..0f99a8d --- /dev/null +++ b/.history/server_20251020042429.js @@ -0,0 +1,191 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ko', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ko'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251020230447.js b/.history/server_20251020230447.js new file mode 100644 index 0000000..e9992e9 --- /dev/null +++ b/.history/server_20251020230447.js @@ -0,0 +1,191 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ru', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ko'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251020230451.js b/.history/server_20251020230451.js new file mode 100644 index 0000000..e9992e9 --- /dev/null +++ b/.history/server_20251020230451.js @@ -0,0 +1,191 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ru', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ko'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251020230454.js b/.history/server_20251020230454.js new file mode 100644 index 0000000..9a9762a --- /dev/null +++ b/.history/server_20251020230454.js @@ -0,0 +1,191 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ru', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ru'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251020230455.js b/.history/server_20251020230455.js new file mode 100644 index 0000000..9a9762a --- /dev/null +++ b/.history/server_20251020230455.js @@ -0,0 +1,191 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ru', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ru'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251021172422.js b/.history/server_20251021172422.js new file mode 100644 index 0000000..bd58cd8 --- /dev/null +++ b/.history/server_20251021172422.js @@ -0,0 +1,191 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ru', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ru'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com", "https://cdn.tailwindcss.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com", "https://cdn.tailwindcss.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com", "https://fonts.googleapis.com", "https://fonts.gstatic.com", "https://cdn.tailwindcss.com"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251021172601.js b/.history/server_20251021172601.js new file mode 100644 index 0000000..bd58cd8 --- /dev/null +++ b/.history/server_20251021172601.js @@ -0,0 +1,191 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ru', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ru'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com", "https://cdn.tailwindcss.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com", "https://cdn.tailwindcss.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com", "https://fonts.googleapis.com", "https://fonts.gstatic.com", "https://cdn.tailwindcss.com"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251021213142.js b/.history/server_20251021213142.js new file mode 100644 index 0000000..1cfb86f --- /dev/null +++ b/.history/server_20251021213142.js @@ -0,0 +1,192 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ru', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ru'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com", "https://cdn.tailwindcss.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com", "https://cdn.tailwindcss.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com", "https://fonts.googleapis.com", "https://fonts.gstatic.com", "https://cdn.tailwindcss.com"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/api/admin', require('./routes/api/admin')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251021214112.js b/.history/server_20251021214112.js new file mode 100644 index 0000000..1cfb86f --- /dev/null +++ b/.history/server_20251021214112.js @@ -0,0 +1,192 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ru', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ru'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com", "https://cdn.tailwindcss.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com", "https://cdn.tailwindcss.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com", "https://fonts.googleapis.com", "https://fonts.gstatic.com", "https://cdn.tailwindcss.com"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/api/admin', require('./routes/api/admin')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251022052951.js b/.history/server_20251022052951.js new file mode 100644 index 0000000..4c12c09 --- /dev/null +++ b/.history/server_20251022052951.js @@ -0,0 +1,199 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ru', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ru'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com", "https://cdn.tailwindcss.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com", "https://cdn.tailwindcss.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com", "https://fonts.googleapis.com", "https://fonts.gstatic.com", "https://cdn.tailwindcss.com"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Layout engine +const expressLayouts = require('express-ejs-layouts'); +app.use(expressLayouts); +app.set('layout', 'layout'); // Default layout for main site +app.set('layout extractScripts', true); +app.set('layout extractStyles', true); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/api/admin', require('./routes/api/admin')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/server_20251022052954.js b/.history/server_20251022052954.js new file mode 100644 index 0000000..4c12c09 --- /dev/null +++ b/.history/server_20251022052954.js @@ -0,0 +1,199 @@ +const express = require('express'); +const { sequelize, testConnection } = require('./config/database'); +const session = require('express-session'); +const SequelizeStore = require('connect-session-sequelize')(session.Store); +const path = require('path'); +const helmet = require('helmet'); +const compression = require('compression'); +const cors = require('cors'); +const morgan = require('morgan'); +const rateLimit = require('express-rate-limit'); +const i18n = require('i18n'); +require('dotenv').config(); + +const app = express(); + +// Настройка i18n +i18n.configure({ + locales: ['ko', 'en', 'ru', 'kk'], + defaultLocale: 'ru', + directory: path.join(__dirname, 'locales'), + objectNotation: true, + updateFiles: false, + syncFiles: false +}); + +// i18n middleware +app.use(i18n.init); + +// Middleware для передачи переменных в шаблоны +app.use((req, res, next) => { + const currentLang = req.session?.language || req.getLocale() || 'ru'; + req.setLocale(currentLang); + + res.locals.locale = currentLang; + res.locals.__ = res.__; + res.locals.theme = req.session?.theme || 'light'; + res.locals.currentLanguage = currentLang; + res.locals.currentPage = req.path.split('/')[1] || 'home'; + next(); +}); + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com", "https://cdn.tailwindcss.com"], + fontSrc: ["'self'", "https://fonts.gstatic.com", "https://cdnjs.cloudflare.com"], + scriptSrc: ["'self'", "'unsafe-inline'", "https://cdnjs.cloudflare.com", "https://unpkg.com", "https://cdn.tailwindcss.com"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:", "https://cdnjs.cloudflare.com", "https://cdn.jsdelivr.net", "https://unpkg.com", "https://fonts.googleapis.com", "https://fonts.gstatic.com", "https://cdn.tailwindcss.com"] + } + } +})); + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100 // limit each IP to 100 requests per windowMs +}); +app.use('/api/', limiter); + +// Middleware +app.use(compression()); +app.use(cors()); +app.use(morgan('combined')); +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Static files +app.use(express.static(path.join(__dirname, 'public'))); +app.use('/uploads', express.static(path.join(__dirname, 'public/uploads'))); + +// View engine +app.set('view engine', 'ejs'); +app.set('views', path.join(__dirname, 'views')); + +// Layout engine +const expressLayouts = require('express-ejs-layouts'); +app.use(expressLayouts); +app.set('layout', 'layout'); // Default layout for main site +app.set('layout extractScripts', true); +app.set('layout extractStyles', true); + +// Database connection and testing +testConnection(); + +// Session store configuration +const sessionStore = new SequelizeStore({ + db: sequelize, + tableName: 'sessions', + checkExpirationInterval: 15 * 60 * 1000, // 15 minutes + expiration: 7 * 24 * 60 * 60 * 1000 // 7 days +}); + +// Session configuration +app.use(session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + store: sessionStore, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days + } +})); + +// Routes +app.use('/', require('./routes/index')); +app.use('/api/auth', require('./routes/auth')); +app.use('/api/portfolio', require('./routes/portfolio')); +app.use('/api/services', require('./routes/services')); +app.use('/api/calculator', require('./routes/calculator')); +app.use('/api/contact', require('./routes/contact')); +app.use('/api/media', require('./routes/media')); +app.use('/api/admin', require('./routes/api/admin')); +app.use('/admin', require('./routes/admin')); + +// Language switching routes +app.get('/lang/:language', (req, res) => { + const { language } = req.params; + const supportedLanguages = ['ko', 'en', 'ru', 'kk']; + + if (supportedLanguages.includes(language)) { + req.setLocale(language); + req.session.language = language; + } + + const referer = req.get('Referer') || '/'; + res.redirect(referer); +}); + +// Theme switching routes +app.get('/theme/:theme', (req, res) => { + const { theme } = req.params; + if (['light', 'dark'].includes(theme)) { + req.session.theme = theme; + } + res.json({ success: true, theme: req.session.theme }); +}); + +// PWA Service Worker +app.get('/sw.js', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'sw.js')); +}); + +// PWA Manifest +app.get('/manifest.json', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'manifest.json')); +}); + +// Error handling middleware +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).render('error', { + title: 'Error', + settings: {}, + message: process.env.NODE_ENV === 'production' + ? 'Something went wrong!' + : err.message, + currentPage: 'error' + }); +}); + +// 404 handler +app.use((req, res) => { + res.status(404).render('error', { + title: '404 - 페이지를 찾을 수 없습니다', + settings: {}, + message: '요청하신 페이지를 찾을 수 없습니다', + currentPage: 'error' + }); +}); + +const PORT = process.env.PORT || 3000; + +// Sync database and start server +async function startServer() { + try { + // Sync all models with database + await sequelize.sync({ force: false }); + console.log('✓ Database synchronized'); + + // Create session table + await sessionStore.sync(); + console.log('✓ Session store synchronized'); + + app.listen(PORT, () => { + console.log(`🚀 Server running on port ${PORT}`); + console.log(`🌐 Visit: http://localhost:${PORT}`); + }); + } catch (error) { + console.error('✗ Failed to start server:', error); + process.exit(1); + } +} + +startServer(); \ No newline at end of file diff --git a/.history/services/telegram_20251021213227.js b/.history/services/telegram_20251021213227.js new file mode 100644 index 0000000..687d0b6 --- /dev/null +++ b/.history/services/telegram_20251021213227.js @@ -0,0 +1,163 @@ +const axios = require('axios'); + +class TelegramService { + constructor() { + this.botToken = process.env.TELEGRAM_BOT_TOKEN; + this.chatId = process.env.TELEGRAM_CHAT_ID; + this.baseUrl = `https://api.telegram.org/bot${this.botToken}`; + this.isEnabled = !!(this.botToken && this.chatId); + } + + async sendMessage(text, options = {}) { + if (!this.isEnabled) { + console.warn('Telegram bot is not configured. Missing TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID'); + return { success: false, message: 'Telegram bot not configured' }; + } + + try { + const response = await axios.post(`${this.baseUrl}/sendMessage`, { + chat_id: this.chatId, + text: text, + parse_mode: 'HTML', + ...options + }); + + return { success: true, data: response.data }; + } catch (error) { + console.error('Telegram send message error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + async sendContactNotification(contact) { + const message = this.formatContactMessage(contact); + return await this.sendMessage(message); + } + + async sendNewContactAlert(contact) { + const message = `🔔 Новый запрос с сайта!\n\n` + + `👤 Клиент: ${contact.name}\n` + + `📧 Email: ${contact.email}\n` + + `📱 Телефон: ${contact.phone || 'Не указан'}\n` + + `💼 Услуга: ${contact.serviceInterest || 'Общий запрос'}\n` + + `💰 Бюджет: ${contact.budget || 'Не указан'}\n` + + `⏱️ Сроки: ${contact.timeline || 'Не указаны'}\n\n` + + `💬 Сообщение:\n${contact.message}\n\n` + + `🕐 Время: ${new Date(contact.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Открыть в админ-панели`; + + return await this.sendMessage(message); + } + + async sendPortfolioNotification(portfolio) { + const message = `📁 Новый проект добавлен в портфолио\n\n` + + `🏷️ Название: ${portfolio.title}\n` + + `📂 Категория: ${portfolio.category}\n` + + `👤 Клиент: ${portfolio.clientName || 'Не указан'}\n` + + `🌐 URL: ${portfolio.projectUrl || 'Не указан'}\n` + + `⭐ Рекомендуемый: ${portfolio.featured ? 'Да' : 'Нет'}\n` + + `📅 Время: ${new Date(portfolio.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Посмотреть проект`; + + return await this.sendMessage(message); + } + + async sendServiceNotification(service) { + const message = `⚙️ Новая услуга добавлена\n\n` + + `🏷️ Название: ${service.name}\n` + + `📂 Категория: ${service.category}\n` + + `💰 Стоимость: ${service.pricing?.basePrice ? `от $${service.pricing.basePrice}` : 'По запросу'}\n` + + `⏱️ Время выполнения: ${service.estimatedTime || 'Не указано'}\n` + + `⭐ Рекомендуемая: ${service.featured ? 'Да' : 'Нет'}\n` + + `📅 Время: ${new Date(service.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Посмотреть услуги`; + + return await this.sendMessage(message); + } + + async sendCalculatorQuote(calculatorData) { + const totalCost = calculatorData.services?.reduce((sum, service) => sum + (service.price || 0), 0) || 0; + + const message = `💰 Новый расчет стоимости\n\n` + + `👤 Клиент: ${calculatorData.name || 'Не указан'}\n` + + `📧 Email: ${calculatorData.email || 'Не указан'}\n` + + `📱 Телефон: ${calculatorData.phone || 'Не указан'}\n\n` + + `🛠️ Выбранные услуги:\n${this.formatServices(calculatorData.services)}\n` + + `💵 Общая стоимость: $${totalCost}\n\n` + + `📅 Время: ${new Date().toLocaleString('ru-RU')}`; + + return await this.sendMessage(message); + } + + formatContactMessage(contact) { + return `📞 Уведомление о контакте\n\n` + + `👤 Клиент: ${contact.name}\n` + + `📧 Email: ${contact.email}\n` + + `📱 Телефон: ${contact.phone || 'Не указан'}\n` + + `💼 Услуга: ${contact.serviceInterest || 'Общий запрос'}\n` + + `📊 Статус: ${this.getStatusText(contact.status)}\n` + + `⚡ Приоритет: ${this.getPriorityText(contact.priority)}\n\n` + + `💬 Сообщение:\n${contact.message}\n\n` + + `🔗 Открыть в админ-панели`; + } + + formatServices(services) { + if (!services || services.length === 0) return 'Не выбрано'; + + return services.map(service => + `• ${service.name} - $${service.price || 0}` + ).join('\n'); + } + + getStatusText(status) { + const statusMap = { + 'new': '🆕 Новое', + 'in_progress': '⏳ В работе', + 'completed': '✅ Завершено' + }; + return statusMap[status] || status; + } + + getPriorityText(priority) { + const priorityMap = { + 'low': '🟢 Низкий', + 'medium': '🟡 Средний', + 'high': '🔴 Высокий' + }; + return priorityMap[priority] || priority; + } + + // Test connection + async testConnection() { + if (!this.isEnabled) { + return { success: false, message: 'Telegram bot not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getMe`); + return { success: true, bot: response.data.result }; + } catch (error) { + console.error('Telegram connection test error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Webhook setup (for future use) + async setWebhook(webhookUrl) { + if (!this.isEnabled) { + return { success: false, message: 'Telegram bot not configured' }; + } + + try { + const response = await axios.post(`${this.baseUrl}/setWebhook`, { + url: webhookUrl + }); + return { success: true, data: response.data }; + } catch (error) { + console.error('Telegram webhook setup error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } +} + +module.exports = new TelegramService(); \ No newline at end of file diff --git a/.history/services/telegram_20251021214113.js b/.history/services/telegram_20251021214113.js new file mode 100644 index 0000000..687d0b6 --- /dev/null +++ b/.history/services/telegram_20251021214113.js @@ -0,0 +1,163 @@ +const axios = require('axios'); + +class TelegramService { + constructor() { + this.botToken = process.env.TELEGRAM_BOT_TOKEN; + this.chatId = process.env.TELEGRAM_CHAT_ID; + this.baseUrl = `https://api.telegram.org/bot${this.botToken}`; + this.isEnabled = !!(this.botToken && this.chatId); + } + + async sendMessage(text, options = {}) { + if (!this.isEnabled) { + console.warn('Telegram bot is not configured. Missing TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID'); + return { success: false, message: 'Telegram bot not configured' }; + } + + try { + const response = await axios.post(`${this.baseUrl}/sendMessage`, { + chat_id: this.chatId, + text: text, + parse_mode: 'HTML', + ...options + }); + + return { success: true, data: response.data }; + } catch (error) { + console.error('Telegram send message error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + async sendContactNotification(contact) { + const message = this.formatContactMessage(contact); + return await this.sendMessage(message); + } + + async sendNewContactAlert(contact) { + const message = `🔔 Новый запрос с сайта!\n\n` + + `👤 Клиент: ${contact.name}\n` + + `📧 Email: ${contact.email}\n` + + `📱 Телефон: ${contact.phone || 'Не указан'}\n` + + `💼 Услуга: ${contact.serviceInterest || 'Общий запрос'}\n` + + `💰 Бюджет: ${contact.budget || 'Не указан'}\n` + + `⏱️ Сроки: ${contact.timeline || 'Не указаны'}\n\n` + + `💬 Сообщение:\n${contact.message}\n\n` + + `🕐 Время: ${new Date(contact.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Открыть в админ-панели`; + + return await this.sendMessage(message); + } + + async sendPortfolioNotification(portfolio) { + const message = `📁 Новый проект добавлен в портфолио\n\n` + + `🏷️ Название: ${portfolio.title}\n` + + `📂 Категория: ${portfolio.category}\n` + + `👤 Клиент: ${portfolio.clientName || 'Не указан'}\n` + + `🌐 URL: ${portfolio.projectUrl || 'Не указан'}\n` + + `⭐ Рекомендуемый: ${portfolio.featured ? 'Да' : 'Нет'}\n` + + `📅 Время: ${new Date(portfolio.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Посмотреть проект`; + + return await this.sendMessage(message); + } + + async sendServiceNotification(service) { + const message = `⚙️ Новая услуга добавлена\n\n` + + `🏷️ Название: ${service.name}\n` + + `📂 Категория: ${service.category}\n` + + `💰 Стоимость: ${service.pricing?.basePrice ? `от $${service.pricing.basePrice}` : 'По запросу'}\n` + + `⏱️ Время выполнения: ${service.estimatedTime || 'Не указано'}\n` + + `⭐ Рекомендуемая: ${service.featured ? 'Да' : 'Нет'}\n` + + `📅 Время: ${new Date(service.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Посмотреть услуги`; + + return await this.sendMessage(message); + } + + async sendCalculatorQuote(calculatorData) { + const totalCost = calculatorData.services?.reduce((sum, service) => sum + (service.price || 0), 0) || 0; + + const message = `💰 Новый расчет стоимости\n\n` + + `👤 Клиент: ${calculatorData.name || 'Не указан'}\n` + + `📧 Email: ${calculatorData.email || 'Не указан'}\n` + + `📱 Телефон: ${calculatorData.phone || 'Не указан'}\n\n` + + `🛠️ Выбранные услуги:\n${this.formatServices(calculatorData.services)}\n` + + `💵 Общая стоимость: $${totalCost}\n\n` + + `📅 Время: ${new Date().toLocaleString('ru-RU')}`; + + return await this.sendMessage(message); + } + + formatContactMessage(contact) { + return `📞 Уведомление о контакте\n\n` + + `👤 Клиент: ${contact.name}\n` + + `📧 Email: ${contact.email}\n` + + `📱 Телефон: ${contact.phone || 'Не указан'}\n` + + `💼 Услуга: ${contact.serviceInterest || 'Общий запрос'}\n` + + `📊 Статус: ${this.getStatusText(contact.status)}\n` + + `⚡ Приоритет: ${this.getPriorityText(contact.priority)}\n\n` + + `💬 Сообщение:\n${contact.message}\n\n` + + `🔗 Открыть в админ-панели`; + } + + formatServices(services) { + if (!services || services.length === 0) return 'Не выбрано'; + + return services.map(service => + `• ${service.name} - $${service.price || 0}` + ).join('\n'); + } + + getStatusText(status) { + const statusMap = { + 'new': '🆕 Новое', + 'in_progress': '⏳ В работе', + 'completed': '✅ Завершено' + }; + return statusMap[status] || status; + } + + getPriorityText(priority) { + const priorityMap = { + 'low': '🟢 Низкий', + 'medium': '🟡 Средний', + 'high': '🔴 Высокий' + }; + return priorityMap[priority] || priority; + } + + // Test connection + async testConnection() { + if (!this.isEnabled) { + return { success: false, message: 'Telegram bot not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getMe`); + return { success: true, bot: response.data.result }; + } catch (error) { + console.error('Telegram connection test error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Webhook setup (for future use) + async setWebhook(webhookUrl) { + if (!this.isEnabled) { + return { success: false, message: 'Telegram bot not configured' }; + } + + try { + const response = await axios.post(`${this.baseUrl}/setWebhook`, { + url: webhookUrl + }); + return { success: true, data: response.data }; + } catch (error) { + console.error('Telegram webhook setup error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } +} + +module.exports = new TelegramService(); \ No newline at end of file diff --git a/.history/services/telegram_20251022194926.js b/.history/services/telegram_20251022194926.js new file mode 100644 index 0000000..3b641ae --- /dev/null +++ b/.history/services/telegram_20251022194926.js @@ -0,0 +1,295 @@ +const axios = require('axios'); + +class TelegramService { + constructor() { + this.botToken = process.env.TELEGRAM_BOT_TOKEN; + this.chatId = process.env.TELEGRAM_CHAT_ID; + this.baseUrl = `https://api.telegram.org/bot${this.botToken}`; + this.isEnabled = !!(this.botToken && this.chatId); + this.chats = new Map(); // Store chat information + this.botInfo = null; // Store bot information + } + + // Update bot token and reinitialize + updateBotToken(newToken) { + this.botToken = newToken; + this.baseUrl = `https://api.telegram.org/bot${this.botToken}`; + this.isEnabled = !!(this.botToken && this.chatId); + this.botInfo = null; // Reset bot info + return this.testConnection(); + } + + // Update default chat ID + updateChatId(newChatId) { + this.chatId = newChatId; + this.isEnabled = !!(this.botToken && this.chatId); + } + + // Get bot information + async getBotInfo() { + if (this.botInfo) return { success: true, bot: this.botInfo }; + + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getMe`); + this.botInfo = response.data.result; + return { success: true, bot: this.botInfo }; + } catch (error) { + console.error('Get bot info error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get updates (for getting chat IDs and managing chats) + async getUpdates() { + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getUpdates`); + const updates = response.data.result; + + // Extract unique chats + const chats = new Map(); + updates.forEach(update => { + if (update.message) { + const chat = update.message.chat; + chats.set(chat.id, { + id: chat.id, + type: chat.type, + title: chat.title || `${chat.first_name || ''} ${chat.last_name || ''}`.trim(), + username: chat.username || null, + first_name: chat.first_name || null, + last_name: chat.last_name || null, + description: chat.description || null, + invite_link: chat.invite_link || null, + pinned_message: chat.pinned_message || null, + permissions: chat.permissions || null, + slow_mode_delay: chat.slow_mode_delay || null + }); + } + }); + + // Update internal chat storage + chats.forEach((chat, id) => { + this.chats.set(id, chat); + }); + + return { + success: true, + updates, + chats: Array.from(chats.values()), + totalUpdates: updates.length + }; + } catch (error) { + console.error('Get updates error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get chat information + async getChat(chatId) { + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getChat`, { + params: { chat_id: chatId } + }); + const chat = response.data.result; + + // Store in local cache + this.chats.set(chatId, chat); + + return { success: true, chat }; + } catch (error) { + console.error('Get chat error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get chat administrators + async getChatAdministrators(chatId) { + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getChatAdministrators`, { + params: { chat_id: chatId } + }); + return { success: true, administrators: response.data.result }; + } catch (error) { + console.error('Get chat administrators error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get available chats (cached) + getAvailableChats() { + return Array.from(this.chats.values()).map(chat => ({ + id: chat.id, + title: chat.title || chat.username || `${chat.first_name || ''} ${chat.last_name || ''}`.trim(), + type: chat.type, + username: chat.username + })); + } + + async sendMessage(text, options = {}) { + if (!this.isEnabled) { + console.warn('Telegram bot is not configured. Missing TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID'); + return { success: false, message: 'Telegram bot not configured' }; + } + + try { + const response = await axios.post(`${this.baseUrl}/sendMessage`, { + chat_id: this.chatId, + text: text, + parse_mode: 'HTML', + ...options + }); + + return { success: true, data: response.data }; + } catch (error) { + console.error('Telegram send message error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + async sendContactNotification(contact) { + const message = this.formatContactMessage(contact); + return await this.sendMessage(message); + } + + async sendNewContactAlert(contact) { + const message = `🔔 Новый запрос с сайта!\n\n` + + `👤 Клиент: ${contact.name}\n` + + `📧 Email: ${contact.email}\n` + + `📱 Телефон: ${contact.phone || 'Не указан'}\n` + + `💼 Услуга: ${contact.serviceInterest || 'Общий запрос'}\n` + + `💰 Бюджет: ${contact.budget || 'Не указан'}\n` + + `⏱️ Сроки: ${contact.timeline || 'Не указаны'}\n\n` + + `💬 Сообщение:\n${contact.message}\n\n` + + `🕐 Время: ${new Date(contact.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Открыть в админ-панели`; + + return await this.sendMessage(message); + } + + async sendPortfolioNotification(portfolio) { + const message = `📁 Новый проект добавлен в портфолио\n\n` + + `🏷️ Название: ${portfolio.title}\n` + + `📂 Категория: ${portfolio.category}\n` + + `👤 Клиент: ${portfolio.clientName || 'Не указан'}\n` + + `🌐 URL: ${portfolio.projectUrl || 'Не указан'}\n` + + `⭐ Рекомендуемый: ${portfolio.featured ? 'Да' : 'Нет'}\n` + + `📅 Время: ${new Date(portfolio.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Посмотреть проект`; + + return await this.sendMessage(message); + } + + async sendServiceNotification(service) { + const message = `⚙️ Новая услуга добавлена\n\n` + + `🏷️ Название: ${service.name}\n` + + `📂 Категория: ${service.category}\n` + + `💰 Стоимость: ${service.pricing?.basePrice ? `от $${service.pricing.basePrice}` : 'По запросу'}\n` + + `⏱️ Время выполнения: ${service.estimatedTime || 'Не указано'}\n` + + `⭐ Рекомендуемая: ${service.featured ? 'Да' : 'Нет'}\n` + + `📅 Время: ${new Date(service.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Посмотреть услуги`; + + return await this.sendMessage(message); + } + + async sendCalculatorQuote(calculatorData) { + const totalCost = calculatorData.services?.reduce((sum, service) => sum + (service.price || 0), 0) || 0; + + const message = `💰 Новый расчет стоимости\n\n` + + `👤 Клиент: ${calculatorData.name || 'Не указан'}\n` + + `📧 Email: ${calculatorData.email || 'Не указан'}\n` + + `📱 Телефон: ${calculatorData.phone || 'Не указан'}\n\n` + + `🛠️ Выбранные услуги:\n${this.formatServices(calculatorData.services)}\n` + + `💵 Общая стоимость: $${totalCost}\n\n` + + `📅 Время: ${new Date().toLocaleString('ru-RU')}`; + + return await this.sendMessage(message); + } + + formatContactMessage(contact) { + return `📞 Уведомление о контакте\n\n` + + `👤 Клиент: ${contact.name}\n` + + `📧 Email: ${contact.email}\n` + + `📱 Телефон: ${contact.phone || 'Не указан'}\n` + + `💼 Услуга: ${contact.serviceInterest || 'Общий запрос'}\n` + + `📊 Статус: ${this.getStatusText(contact.status)}\n` + + `⚡ Приоритет: ${this.getPriorityText(contact.priority)}\n\n` + + `💬 Сообщение:\n${contact.message}\n\n` + + `🔗 Открыть в админ-панели`; + } + + formatServices(services) { + if (!services || services.length === 0) return 'Не выбрано'; + + return services.map(service => + `• ${service.name} - $${service.price || 0}` + ).join('\n'); + } + + getStatusText(status) { + const statusMap = { + 'new': '🆕 Новое', + 'in_progress': '⏳ В работе', + 'completed': '✅ Завершено' + }; + return statusMap[status] || status; + } + + getPriorityText(priority) { + const priorityMap = { + 'low': '🟢 Низкий', + 'medium': '🟡 Средний', + 'high': '🔴 Высокий' + }; + return priorityMap[priority] || priority; + } + + // Test connection + async testConnection() { + if (!this.isEnabled) { + return { success: false, message: 'Telegram bot not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getMe`); + return { success: true, bot: response.data.result }; + } catch (error) { + console.error('Telegram connection test error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Webhook setup (for future use) + async setWebhook(webhookUrl) { + if (!this.isEnabled) { + return { success: false, message: 'Telegram bot not configured' }; + } + + try { + const response = await axios.post(`${this.baseUrl}/setWebhook`, { + url: webhookUrl + }); + return { success: true, data: response.data }; + } catch (error) { + console.error('Telegram webhook setup error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } +} + +module.exports = new TelegramService(); \ No newline at end of file diff --git a/.history/services/telegram_20251022194946.js b/.history/services/telegram_20251022194946.js new file mode 100644 index 0000000..e806e7d --- /dev/null +++ b/.history/services/telegram_20251022194946.js @@ -0,0 +1,365 @@ +const axios = require('axios'); + +class TelegramService { + constructor() { + this.botToken = process.env.TELEGRAM_BOT_TOKEN; + this.chatId = process.env.TELEGRAM_CHAT_ID; + this.baseUrl = `https://api.telegram.org/bot${this.botToken}`; + this.isEnabled = !!(this.botToken && this.chatId); + this.chats = new Map(); // Store chat information + this.botInfo = null; // Store bot information + } + + // Update bot token and reinitialize + updateBotToken(newToken) { + this.botToken = newToken; + this.baseUrl = `https://api.telegram.org/bot${this.botToken}`; + this.isEnabled = !!(this.botToken && this.chatId); + this.botInfo = null; // Reset bot info + return this.testConnection(); + } + + // Update default chat ID + updateChatId(newChatId) { + this.chatId = newChatId; + this.isEnabled = !!(this.botToken && this.chatId); + } + + // Get bot information + async getBotInfo() { + if (this.botInfo) return { success: true, bot: this.botInfo }; + + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getMe`); + this.botInfo = response.data.result; + return { success: true, bot: this.botInfo }; + } catch (error) { + console.error('Get bot info error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get updates (for getting chat IDs and managing chats) + async getUpdates() { + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getUpdates`); + const updates = response.data.result; + + // Extract unique chats + const chats = new Map(); + updates.forEach(update => { + if (update.message) { + const chat = update.message.chat; + chats.set(chat.id, { + id: chat.id, + type: chat.type, + title: chat.title || `${chat.first_name || ''} ${chat.last_name || ''}`.trim(), + username: chat.username || null, + first_name: chat.first_name || null, + last_name: chat.last_name || null, + description: chat.description || null, + invite_link: chat.invite_link || null, + pinned_message: chat.pinned_message || null, + permissions: chat.permissions || null, + slow_mode_delay: chat.slow_mode_delay || null + }); + } + }); + + // Update internal chat storage + chats.forEach((chat, id) => { + this.chats.set(id, chat); + }); + + return { + success: true, + updates, + chats: Array.from(chats.values()), + totalUpdates: updates.length + }; + } catch (error) { + console.error('Get updates error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get chat information + async getChat(chatId) { + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getChat`, { + params: { chat_id: chatId } + }); + const chat = response.data.result; + + // Store in local cache + this.chats.set(chatId, chat); + + return { success: true, chat }; + } catch (error) { + console.error('Get chat error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get chat administrators + async getChatAdministrators(chatId) { + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getChatAdministrators`, { + params: { chat_id: chatId } + }); + return { success: true, administrators: response.data.result }; + } catch (error) { + console.error('Get chat administrators error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get available chats (cached) + getAvailableChats() { + return Array.from(this.chats.values()).map(chat => ({ + id: chat.id, + title: chat.title || chat.username || `${chat.first_name || ''} ${chat.last_name || ''}`.trim(), + type: chat.type, + username: chat.username + })); + } + + async sendMessage(text, options = {}) { + const chatId = options.chat_id || this.chatId; + + if (!this.botToken) { + console.warn('Telegram bot token is not configured'); + return { success: false, message: 'Telegram bot token not configured' }; + } + + if (!chatId) { + console.warn('Telegram chat ID is not specified'); + return { success: false, message: 'Telegram chat ID not specified' }; + } + + try { + const response = await axios.post(`${this.baseUrl}/sendMessage`, { + chat_id: chatId, + text: text, + parse_mode: 'HTML', + disable_web_page_preview: false, + disable_notification: false, + ...options + }); + + return { success: true, data: response.data }; + } catch (error) { + console.error('Telegram send message error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Send message to multiple chats + async sendBroadcastMessage(text, chatIds = [], options = {}) { + if (!this.botToken) { + return { success: false, message: 'Telegram bot token not configured' }; + } + + if (!chatIds || chatIds.length === 0) { + return { success: false, message: 'No chat IDs specified' }; + } + + const results = []; + const errors = []; + + for (const chatId of chatIds) { + try { + const result = await this.sendMessage(text, { + ...options, + chat_id: chatId + }); + + if (result.success) { + results.push({ chatId, success: true, messageId: result.data.result.message_id }); + } else { + errors.push({ chatId, error: result.error || result.message }); + } + + // Add delay between messages to avoid rate limiting + await new Promise(resolve => setTimeout(resolve, 100)); + } catch (error) { + errors.push({ chatId, error: error.message }); + } + } + + return { + success: errors.length === 0, + results, + errors, + totalSent: results.length, + totalFailed: errors.length + }; + } + + // Send custom message with advanced options + async sendCustomMessage({ + text, + chatIds = [], + parseMode = 'HTML', + disableWebPagePreview = false, + disableNotification = false, + replyMarkup = null + }) { + const targetChats = chatIds.length > 0 ? chatIds : [this.chatId]; + + return await this.sendBroadcastMessage(text, targetChats, { + parse_mode: parseMode, + disable_web_page_preview: disableWebPagePreview, + disable_notification: disableNotification, + reply_markup: replyMarkup + }); + } + + async sendContactNotification(contact) { + const message = this.formatContactMessage(contact); + return await this.sendMessage(message); + } + + async sendNewContactAlert(contact) { + const message = `🔔 Новый запрос с сайта!\n\n` + + `👤 Клиент: ${contact.name}\n` + + `📧 Email: ${contact.email}\n` + + `📱 Телефон: ${contact.phone || 'Не указан'}\n` + + `💼 Услуга: ${contact.serviceInterest || 'Общий запрос'}\n` + + `💰 Бюджет: ${contact.budget || 'Не указан'}\n` + + `⏱️ Сроки: ${contact.timeline || 'Не указаны'}\n\n` + + `💬 Сообщение:\n${contact.message}\n\n` + + `🕐 Время: ${new Date(contact.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Открыть в админ-панели`; + + return await this.sendMessage(message); + } + + async sendPortfolioNotification(portfolio) { + const message = `📁 Новый проект добавлен в портфолио\n\n` + + `🏷️ Название: ${portfolio.title}\n` + + `📂 Категория: ${portfolio.category}\n` + + `👤 Клиент: ${portfolio.clientName || 'Не указан'}\n` + + `🌐 URL: ${portfolio.projectUrl || 'Не указан'}\n` + + `⭐ Рекомендуемый: ${portfolio.featured ? 'Да' : 'Нет'}\n` + + `📅 Время: ${new Date(portfolio.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Посмотреть проект`; + + return await this.sendMessage(message); + } + + async sendServiceNotification(service) { + const message = `⚙️ Новая услуга добавлена\n\n` + + `🏷️ Название: ${service.name}\n` + + `📂 Категория: ${service.category}\n` + + `💰 Стоимость: ${service.pricing?.basePrice ? `от $${service.pricing.basePrice}` : 'По запросу'}\n` + + `⏱️ Время выполнения: ${service.estimatedTime || 'Не указано'}\n` + + `⭐ Рекомендуемая: ${service.featured ? 'Да' : 'Нет'}\n` + + `📅 Время: ${new Date(service.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Посмотреть услуги`; + + return await this.sendMessage(message); + } + + async sendCalculatorQuote(calculatorData) { + const totalCost = calculatorData.services?.reduce((sum, service) => sum + (service.price || 0), 0) || 0; + + const message = `💰 Новый расчет стоимости\n\n` + + `👤 Клиент: ${calculatorData.name || 'Не указан'}\n` + + `📧 Email: ${calculatorData.email || 'Не указан'}\n` + + `📱 Телефон: ${calculatorData.phone || 'Не указан'}\n\n` + + `🛠️ Выбранные услуги:\n${this.formatServices(calculatorData.services)}\n` + + `💵 Общая стоимость: $${totalCost}\n\n` + + `📅 Время: ${new Date().toLocaleString('ru-RU')}`; + + return await this.sendMessage(message); + } + + formatContactMessage(contact) { + return `📞 Уведомление о контакте\n\n` + + `👤 Клиент: ${contact.name}\n` + + `📧 Email: ${contact.email}\n` + + `📱 Телефон: ${contact.phone || 'Не указан'}\n` + + `💼 Услуга: ${contact.serviceInterest || 'Общий запрос'}\n` + + `📊 Статус: ${this.getStatusText(contact.status)}\n` + + `⚡ Приоритет: ${this.getPriorityText(contact.priority)}\n\n` + + `💬 Сообщение:\n${contact.message}\n\n` + + `🔗 Открыть в админ-панели`; + } + + formatServices(services) { + if (!services || services.length === 0) return 'Не выбрано'; + + return services.map(service => + `• ${service.name} - $${service.price || 0}` + ).join('\n'); + } + + getStatusText(status) { + const statusMap = { + 'new': '🆕 Новое', + 'in_progress': '⏳ В работе', + 'completed': '✅ Завершено' + }; + return statusMap[status] || status; + } + + getPriorityText(priority) { + const priorityMap = { + 'low': '🟢 Низкий', + 'medium': '🟡 Средний', + 'high': '🔴 Высокий' + }; + return priorityMap[priority] || priority; + } + + // Test connection + async testConnection() { + if (!this.isEnabled) { + return { success: false, message: 'Telegram bot not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getMe`); + return { success: true, bot: response.data.result }; + } catch (error) { + console.error('Telegram connection test error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Webhook setup (for future use) + async setWebhook(webhookUrl) { + if (!this.isEnabled) { + return { success: false, message: 'Telegram bot not configured' }; + } + + try { + const response = await axios.post(`${this.baseUrl}/setWebhook`, { + url: webhookUrl + }); + return { success: true, data: response.data }; + } catch (error) { + console.error('Telegram webhook setup error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } +} + +module.exports = new TelegramService(); \ No newline at end of file diff --git a/.history/services/telegram_20251022194959.js b/.history/services/telegram_20251022194959.js new file mode 100644 index 0000000..1b5cbac --- /dev/null +++ b/.history/services/telegram_20251022194959.js @@ -0,0 +1,374 @@ +const axios = require('axios'); + +class TelegramService { + constructor() { + this.botToken = process.env.TELEGRAM_BOT_TOKEN; + this.chatId = process.env.TELEGRAM_CHAT_ID; + this.baseUrl = `https://api.telegram.org/bot${this.botToken}`; + this.isEnabled = !!(this.botToken && this.chatId); + this.chats = new Map(); // Store chat information + this.botInfo = null; // Store bot information + } + + // Update bot token and reinitialize + updateBotToken(newToken) { + this.botToken = newToken; + this.baseUrl = `https://api.telegram.org/bot${this.botToken}`; + this.isEnabled = !!(this.botToken && this.chatId); + this.botInfo = null; // Reset bot info + return this.testConnection(); + } + + // Update default chat ID + updateChatId(newChatId) { + this.chatId = newChatId; + this.isEnabled = !!(this.botToken && this.chatId); + } + + // Get bot information + async getBotInfo() { + if (this.botInfo) return { success: true, bot: this.botInfo }; + + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getMe`); + this.botInfo = response.data.result; + return { success: true, bot: this.botInfo }; + } catch (error) { + console.error('Get bot info error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get updates (for getting chat IDs and managing chats) + async getUpdates() { + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getUpdates`); + const updates = response.data.result; + + // Extract unique chats + const chats = new Map(); + updates.forEach(update => { + if (update.message) { + const chat = update.message.chat; + chats.set(chat.id, { + id: chat.id, + type: chat.type, + title: chat.title || `${chat.first_name || ''} ${chat.last_name || ''}`.trim(), + username: chat.username || null, + first_name: chat.first_name || null, + last_name: chat.last_name || null, + description: chat.description || null, + invite_link: chat.invite_link || null, + pinned_message: chat.pinned_message || null, + permissions: chat.permissions || null, + slow_mode_delay: chat.slow_mode_delay || null + }); + } + }); + + // Update internal chat storage + chats.forEach((chat, id) => { + this.chats.set(id, chat); + }); + + return { + success: true, + updates, + chats: Array.from(chats.values()), + totalUpdates: updates.length + }; + } catch (error) { + console.error('Get updates error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get chat information + async getChat(chatId) { + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getChat`, { + params: { chat_id: chatId } + }); + const chat = response.data.result; + + // Store in local cache + this.chats.set(chatId, chat); + + return { success: true, chat }; + } catch (error) { + console.error('Get chat error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get chat administrators + async getChatAdministrators(chatId) { + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getChatAdministrators`, { + params: { chat_id: chatId } + }); + return { success: true, administrators: response.data.result }; + } catch (error) { + console.error('Get chat administrators error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get available chats (cached) + getAvailableChats() { + return Array.from(this.chats.values()).map(chat => ({ + id: chat.id, + title: chat.title || chat.username || `${chat.first_name || ''} ${chat.last_name || ''}`.trim(), + type: chat.type, + username: chat.username + })); + } + + async sendMessage(text, options = {}) { + const chatId = options.chat_id || this.chatId; + + if (!this.botToken) { + console.warn('Telegram bot token is not configured'); + return { success: false, message: 'Telegram bot token not configured' }; + } + + if (!chatId) { + console.warn('Telegram chat ID is not specified'); + return { success: false, message: 'Telegram chat ID not specified' }; + } + + try { + const response = await axios.post(`${this.baseUrl}/sendMessage`, { + chat_id: chatId, + text: text, + parse_mode: 'HTML', + disable_web_page_preview: false, + disable_notification: false, + ...options + }); + + return { success: true, data: response.data }; + } catch (error) { + console.error('Telegram send message error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Send message to multiple chats + async sendBroadcastMessage(text, chatIds = [], options = {}) { + if (!this.botToken) { + return { success: false, message: 'Telegram bot token not configured' }; + } + + if (!chatIds || chatIds.length === 0) { + return { success: false, message: 'No chat IDs specified' }; + } + + const results = []; + const errors = []; + + for (const chatId of chatIds) { + try { + const result = await this.sendMessage(text, { + ...options, + chat_id: chatId + }); + + if (result.success) { + results.push({ chatId, success: true, messageId: result.data.result.message_id }); + } else { + errors.push({ chatId, error: result.error || result.message }); + } + + // Add delay between messages to avoid rate limiting + await new Promise(resolve => setTimeout(resolve, 100)); + } catch (error) { + errors.push({ chatId, error: error.message }); + } + } + + return { + success: errors.length === 0, + results, + errors, + totalSent: results.length, + totalFailed: errors.length + }; + } + + // Send custom message with advanced options + async sendCustomMessage({ + text, + chatIds = [], + parseMode = 'HTML', + disableWebPagePreview = false, + disableNotification = false, + replyMarkup = null + }) { + const targetChats = chatIds.length > 0 ? chatIds : [this.chatId]; + + return await this.sendBroadcastMessage(text, targetChats, { + parse_mode: parseMode, + disable_web_page_preview: disableWebPagePreview, + disable_notification: disableNotification, + reply_markup: replyMarkup + }); + } + + async sendContactNotification(contact) { + const message = this.formatContactMessage(contact); + return await this.sendMessage(message); + } + + async sendNewContactAlert(contact) { + const message = `🔔 Новый запрос с сайта!\n\n` + + `👤 Клиент: ${contact.name}\n` + + `📧 Email: ${contact.email}\n` + + `📱 Телефон: ${contact.phone || 'Не указан'}\n` + + `💼 Услуга: ${contact.serviceInterest || 'Общий запрос'}\n` + + `💰 Бюджет: ${contact.budget || 'Не указан'}\n` + + `⏱️ Сроки: ${contact.timeline || 'Не указаны'}\n\n` + + `💬 Сообщение:\n${contact.message}\n\n` + + `🕐 Время: ${new Date(contact.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Открыть в админ-панели`; + + return await this.sendMessage(message); + } + + async sendPortfolioNotification(portfolio) { + const message = `📁 Новый проект добавлен в портфолио\n\n` + + `🏷️ Название: ${portfolio.title}\n` + + `📂 Категория: ${portfolio.category}\n` + + `👤 Клиент: ${portfolio.clientName || 'Не указан'}\n` + + `🌐 URL: ${portfolio.projectUrl || 'Не указан'}\n` + + `⭐ Рекомендуемый: ${portfolio.featured ? 'Да' : 'Нет'}\n` + + `📅 Время: ${new Date(portfolio.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Посмотреть проект`; + + return await this.sendMessage(message); + } + + async sendServiceNotification(service) { + const message = `⚙️ Новая услуга добавлена\n\n` + + `🏷️ Название: ${service.name}\n` + + `📂 Категория: ${service.category}\n` + + `💰 Стоимость: ${service.pricing?.basePrice ? `от $${service.pricing.basePrice}` : 'По запросу'}\n` + + `⏱️ Время выполнения: ${service.estimatedTime || 'Не указано'}\n` + + `⭐ Рекомендуемая: ${service.featured ? 'Да' : 'Нет'}\n` + + `📅 Время: ${new Date(service.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Посмотреть услуги`; + + return await this.sendMessage(message); + } + + async sendCalculatorQuote(calculatorData) { + const totalCost = calculatorData.services?.reduce((sum, service) => sum + (service.price || 0), 0) || 0; + + const message = `💰 Новый расчет стоимости\n\n` + + `👤 Клиент: ${calculatorData.name || 'Не указан'}\n` + + `📧 Email: ${calculatorData.email || 'Не указан'}\n` + + `📱 Телефон: ${calculatorData.phone || 'Не указан'}\n\n` + + `🛠️ Выбранные услуги:\n${this.formatServices(calculatorData.services)}\n` + + `💵 Общая стоимость: $${totalCost}\n\n` + + `📅 Время: ${new Date().toLocaleString('ru-RU')}`; + + return await this.sendMessage(message); + } + + formatContactMessage(contact) { + return `📞 Уведомление о контакте\n\n` + + `👤 Клиент: ${contact.name}\n` + + `📧 Email: ${contact.email}\n` + + `📱 Телефон: ${contact.phone || 'Не указан'}\n` + + `💼 Услуга: ${contact.serviceInterest || 'Общий запрос'}\n` + + `📊 Статус: ${this.getStatusText(contact.status)}\n` + + `⚡ Приоритет: ${this.getPriorityText(contact.priority)}\n\n` + + `💬 Сообщение:\n${contact.message}\n\n` + + `🔗 Открыть в админ-панели`; + } + + formatServices(services) { + if (!services || services.length === 0) return 'Не выбрано'; + + return services.map(service => + `• ${service.name} - $${service.price || 0}` + ).join('\n'); + } + + getStatusText(status) { + const statusMap = { + 'new': '🆕 Новое', + 'in_progress': '⏳ В работе', + 'completed': '✅ Завершено' + }; + return statusMap[status] || status; + } + + getPriorityText(priority) { + const priorityMap = { + 'low': '🟢 Низкий', + 'medium': '🟡 Средний', + 'high': '🔴 Высокий' + }; + return priorityMap[priority] || priority; + } + + // Test connection and update bot info + async testConnection() { + if (!this.botToken) { + return { success: false, message: 'Telegram bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getMe`); + this.botInfo = response.data.result; + + // Also get updates to discover available chats + await this.getUpdates(); + + return { + success: true, + bot: this.botInfo, + availableChats: this.getAvailableChats() + }; + } catch (error) { + console.error('Telegram connection test error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Webhook setup (for future use) + async setWebhook(webhookUrl) { + if (!this.isEnabled) { + return { success: false, message: 'Telegram bot not configured' }; + } + + try { + const response = await axios.post(`${this.baseUrl}/setWebhook`, { + url: webhookUrl + }); + return { success: true, data: response.data }; + } catch (error) { + console.error('Telegram webhook setup error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } +} + +module.exports = new TelegramService(); \ No newline at end of file diff --git a/.history/services/telegram_20251022195905.js b/.history/services/telegram_20251022195905.js new file mode 100644 index 0000000..1b5cbac --- /dev/null +++ b/.history/services/telegram_20251022195905.js @@ -0,0 +1,374 @@ +const axios = require('axios'); + +class TelegramService { + constructor() { + this.botToken = process.env.TELEGRAM_BOT_TOKEN; + this.chatId = process.env.TELEGRAM_CHAT_ID; + this.baseUrl = `https://api.telegram.org/bot${this.botToken}`; + this.isEnabled = !!(this.botToken && this.chatId); + this.chats = new Map(); // Store chat information + this.botInfo = null; // Store bot information + } + + // Update bot token and reinitialize + updateBotToken(newToken) { + this.botToken = newToken; + this.baseUrl = `https://api.telegram.org/bot${this.botToken}`; + this.isEnabled = !!(this.botToken && this.chatId); + this.botInfo = null; // Reset bot info + return this.testConnection(); + } + + // Update default chat ID + updateChatId(newChatId) { + this.chatId = newChatId; + this.isEnabled = !!(this.botToken && this.chatId); + } + + // Get bot information + async getBotInfo() { + if (this.botInfo) return { success: true, bot: this.botInfo }; + + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getMe`); + this.botInfo = response.data.result; + return { success: true, bot: this.botInfo }; + } catch (error) { + console.error('Get bot info error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get updates (for getting chat IDs and managing chats) + async getUpdates() { + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getUpdates`); + const updates = response.data.result; + + // Extract unique chats + const chats = new Map(); + updates.forEach(update => { + if (update.message) { + const chat = update.message.chat; + chats.set(chat.id, { + id: chat.id, + type: chat.type, + title: chat.title || `${chat.first_name || ''} ${chat.last_name || ''}`.trim(), + username: chat.username || null, + first_name: chat.first_name || null, + last_name: chat.last_name || null, + description: chat.description || null, + invite_link: chat.invite_link || null, + pinned_message: chat.pinned_message || null, + permissions: chat.permissions || null, + slow_mode_delay: chat.slow_mode_delay || null + }); + } + }); + + // Update internal chat storage + chats.forEach((chat, id) => { + this.chats.set(id, chat); + }); + + return { + success: true, + updates, + chats: Array.from(chats.values()), + totalUpdates: updates.length + }; + } catch (error) { + console.error('Get updates error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get chat information + async getChat(chatId) { + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getChat`, { + params: { chat_id: chatId } + }); + const chat = response.data.result; + + // Store in local cache + this.chats.set(chatId, chat); + + return { success: true, chat }; + } catch (error) { + console.error('Get chat error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get chat administrators + async getChatAdministrators(chatId) { + if (!this.botToken) { + return { success: false, message: 'Bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getChatAdministrators`, { + params: { chat_id: chatId } + }); + return { success: true, administrators: response.data.result }; + } catch (error) { + console.error('Get chat administrators error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Get available chats (cached) + getAvailableChats() { + return Array.from(this.chats.values()).map(chat => ({ + id: chat.id, + title: chat.title || chat.username || `${chat.first_name || ''} ${chat.last_name || ''}`.trim(), + type: chat.type, + username: chat.username + })); + } + + async sendMessage(text, options = {}) { + const chatId = options.chat_id || this.chatId; + + if (!this.botToken) { + console.warn('Telegram bot token is not configured'); + return { success: false, message: 'Telegram bot token not configured' }; + } + + if (!chatId) { + console.warn('Telegram chat ID is not specified'); + return { success: false, message: 'Telegram chat ID not specified' }; + } + + try { + const response = await axios.post(`${this.baseUrl}/sendMessage`, { + chat_id: chatId, + text: text, + parse_mode: 'HTML', + disable_web_page_preview: false, + disable_notification: false, + ...options + }); + + return { success: true, data: response.data }; + } catch (error) { + console.error('Telegram send message error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Send message to multiple chats + async sendBroadcastMessage(text, chatIds = [], options = {}) { + if (!this.botToken) { + return { success: false, message: 'Telegram bot token not configured' }; + } + + if (!chatIds || chatIds.length === 0) { + return { success: false, message: 'No chat IDs specified' }; + } + + const results = []; + const errors = []; + + for (const chatId of chatIds) { + try { + const result = await this.sendMessage(text, { + ...options, + chat_id: chatId + }); + + if (result.success) { + results.push({ chatId, success: true, messageId: result.data.result.message_id }); + } else { + errors.push({ chatId, error: result.error || result.message }); + } + + // Add delay between messages to avoid rate limiting + await new Promise(resolve => setTimeout(resolve, 100)); + } catch (error) { + errors.push({ chatId, error: error.message }); + } + } + + return { + success: errors.length === 0, + results, + errors, + totalSent: results.length, + totalFailed: errors.length + }; + } + + // Send custom message with advanced options + async sendCustomMessage({ + text, + chatIds = [], + parseMode = 'HTML', + disableWebPagePreview = false, + disableNotification = false, + replyMarkup = null + }) { + const targetChats = chatIds.length > 0 ? chatIds : [this.chatId]; + + return await this.sendBroadcastMessage(text, targetChats, { + parse_mode: parseMode, + disable_web_page_preview: disableWebPagePreview, + disable_notification: disableNotification, + reply_markup: replyMarkup + }); + } + + async sendContactNotification(contact) { + const message = this.formatContactMessage(contact); + return await this.sendMessage(message); + } + + async sendNewContactAlert(contact) { + const message = `🔔 Новый запрос с сайта!\n\n` + + `👤 Клиент: ${contact.name}\n` + + `📧 Email: ${contact.email}\n` + + `📱 Телефон: ${contact.phone || 'Не указан'}\n` + + `💼 Услуга: ${contact.serviceInterest || 'Общий запрос'}\n` + + `💰 Бюджет: ${contact.budget || 'Не указан'}\n` + + `⏱️ Сроки: ${contact.timeline || 'Не указаны'}\n\n` + + `💬 Сообщение:\n${contact.message}\n\n` + + `🕐 Время: ${new Date(contact.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Открыть в админ-панели`; + + return await this.sendMessage(message); + } + + async sendPortfolioNotification(portfolio) { + const message = `📁 Новый проект добавлен в портфолио\n\n` + + `🏷️ Название: ${portfolio.title}\n` + + `📂 Категория: ${portfolio.category}\n` + + `👤 Клиент: ${portfolio.clientName || 'Не указан'}\n` + + `🌐 URL: ${portfolio.projectUrl || 'Не указан'}\n` + + `⭐ Рекомендуемый: ${portfolio.featured ? 'Да' : 'Нет'}\n` + + `📅 Время: ${new Date(portfolio.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Посмотреть проект`; + + return await this.sendMessage(message); + } + + async sendServiceNotification(service) { + const message = `⚙️ Новая услуга добавлена\n\n` + + `🏷️ Название: ${service.name}\n` + + `📂 Категория: ${service.category}\n` + + `💰 Стоимость: ${service.pricing?.basePrice ? `от $${service.pricing.basePrice}` : 'По запросу'}\n` + + `⏱️ Время выполнения: ${service.estimatedTime || 'Не указано'}\n` + + `⭐ Рекомендуемая: ${service.featured ? 'Да' : 'Нет'}\n` + + `📅 Время: ${new Date(service.createdAt).toLocaleString('ru-RU')}\n\n` + + `🔗 Посмотреть услуги`; + + return await this.sendMessage(message); + } + + async sendCalculatorQuote(calculatorData) { + const totalCost = calculatorData.services?.reduce((sum, service) => sum + (service.price || 0), 0) || 0; + + const message = `💰 Новый расчет стоимости\n\n` + + `👤 Клиент: ${calculatorData.name || 'Не указан'}\n` + + `📧 Email: ${calculatorData.email || 'Не указан'}\n` + + `📱 Телефон: ${calculatorData.phone || 'Не указан'}\n\n` + + `🛠️ Выбранные услуги:\n${this.formatServices(calculatorData.services)}\n` + + `💵 Общая стоимость: $${totalCost}\n\n` + + `📅 Время: ${new Date().toLocaleString('ru-RU')}`; + + return await this.sendMessage(message); + } + + formatContactMessage(contact) { + return `📞 Уведомление о контакте\n\n` + + `👤 Клиент: ${contact.name}\n` + + `📧 Email: ${contact.email}\n` + + `📱 Телефон: ${contact.phone || 'Не указан'}\n` + + `💼 Услуга: ${contact.serviceInterest || 'Общий запрос'}\n` + + `📊 Статус: ${this.getStatusText(contact.status)}\n` + + `⚡ Приоритет: ${this.getPriorityText(contact.priority)}\n\n` + + `💬 Сообщение:\n${contact.message}\n\n` + + `🔗 Открыть в админ-панели`; + } + + formatServices(services) { + if (!services || services.length === 0) return 'Не выбрано'; + + return services.map(service => + `• ${service.name} - $${service.price || 0}` + ).join('\n'); + } + + getStatusText(status) { + const statusMap = { + 'new': '🆕 Новое', + 'in_progress': '⏳ В работе', + 'completed': '✅ Завершено' + }; + return statusMap[status] || status; + } + + getPriorityText(priority) { + const priorityMap = { + 'low': '🟢 Низкий', + 'medium': '🟡 Средний', + 'high': '🔴 Высокий' + }; + return priorityMap[priority] || priority; + } + + // Test connection and update bot info + async testConnection() { + if (!this.botToken) { + return { success: false, message: 'Telegram bot token not configured' }; + } + + try { + const response = await axios.get(`${this.baseUrl}/getMe`); + this.botInfo = response.data.result; + + // Also get updates to discover available chats + await this.getUpdates(); + + return { + success: true, + bot: this.botInfo, + availableChats: this.getAvailableChats() + }; + } catch (error) { + console.error('Telegram connection test error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } + + // Webhook setup (for future use) + async setWebhook(webhookUrl) { + if (!this.isEnabled) { + return { success: false, message: 'Telegram bot not configured' }; + } + + try { + const response = await axios.post(`${this.baseUrl}/setWebhook`, { + url: webhookUrl + }); + return { success: true, data: response.data }; + } catch (error) { + console.error('Telegram webhook setup error:', error.response?.data || error.message); + return { success: false, error: error.message }; + } + } +} + +module.exports = new TelegramService(); \ No newline at end of file diff --git a/.history/views/about_20251020225314.ejs b/.history/views/about_20251020225314.ejs new file mode 100644 index 0000000..77a2b0c --- /dev/null +++ b/.history/views/about_20251020225314.ejs @@ -0,0 +1,160 @@ + + + + + + <%- __('about.meta.title') %> - SmartSolTech + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+

+ <%- __('about.hero.title') %> +

+

+ <%- __('about.hero.subtitle') %> +

+
+
+ + +
+
+
+
+

+ <%- __('about.company.title') %> +

+

+ <%- __('about.company.description1') %> +

+

+ <%- __('about.company.description2') %> +

+ + +
+
+
50+
+
<%- __('about.stats.projects') %>
+
+
+
3+
+
<%- __('about.stats.experience') %>
+
+
+
30+
+
<%- __('about.stats.clients') %>
+
+
+
+ + +
+
+ +
+
+
+
+
+ + +
+
+
+

+ <%- __('about.mission.title') %> +

+

+ <%- __('about.mission.description') %> +

+
+ +
+ +
+
+ +
+

+ <%- __('about.values.innovation.title') %> +

+

+ <%- __('about.values.innovation.description') %> +

+
+ + +
+
+ +
+

+ <%- __('about.values.quality.title') %> +

+

+ <%- __('about.values.quality.description') %> +

+
+ + +
+
+ +
+

+ <%- __('about.values.partnership.title') %> +

+

+ <%- __('about.values.partnership.description') %> +

+
+
+
+
+ + +
+
+

+ <%- __('about.cta.title') %> +

+

+ <%- __('about.cta.subtitle') %> +

+ + <%- __('about.cta.button') %> + + + + +
+
+ + <%- include('partials/footer') %> + + + + + \ No newline at end of file diff --git a/.history/views/about_20251020225419.ejs b/.history/views/about_20251020225419.ejs new file mode 100644 index 0000000..77a2b0c --- /dev/null +++ b/.history/views/about_20251020225419.ejs @@ -0,0 +1,160 @@ + + + + + + <%- __('about.meta.title') %> - SmartSolTech + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+

+ <%- __('about.hero.title') %> +

+

+ <%- __('about.hero.subtitle') %> +

+
+
+ + +
+
+
+
+

+ <%- __('about.company.title') %> +

+

+ <%- __('about.company.description1') %> +

+

+ <%- __('about.company.description2') %> +

+ + +
+
+
50+
+
<%- __('about.stats.projects') %>
+
+
+
3+
+
<%- __('about.stats.experience') %>
+
+
+
30+
+
<%- __('about.stats.clients') %>
+
+
+
+ + +
+
+ +
+
+
+
+
+ + +
+
+
+

+ <%- __('about.mission.title') %> +

+

+ <%- __('about.mission.description') %> +

+
+ +
+ +
+
+ +
+

+ <%- __('about.values.innovation.title') %> +

+

+ <%- __('about.values.innovation.description') %> +

+
+ + +
+
+ +
+

+ <%- __('about.values.quality.title') %> +

+

+ <%- __('about.values.quality.description') %> +

+
+ + +
+
+ +
+

+ <%- __('about.values.partnership.title') %> +

+

+ <%- __('about.values.partnership.description') %> +

+
+
+
+
+ + +
+
+

+ <%- __('about.cta.title') %> +

+

+ <%- __('about.cta.subtitle') %> +

+ + <%- __('about.cta.button') %> + + + + +
+
+ + <%- include('partials/footer') %> + + + + + \ No newline at end of file diff --git a/.history/views/about_20251021212419.ejs b/.history/views/about_20251021212419.ejs new file mode 100644 index 0000000..8a673aa --- /dev/null +++ b/.history/views/about_20251021212419.ejs @@ -0,0 +1,160 @@ + + + + + + <%- __('about.meta.title') %> - SmartSolTech + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+

+ <%- __('about.hero.title') %> +

+

+ <%- __('about.hero.subtitle') %> +

+
+
+ + +
+
+
+
+

+ <%- __('about.company.title') %> +

+

+ <%- __('about.company.description1') %> +

+

+ <%- __('about.company.description2') %> +

+ + +
+
+
50+
+
<%- __('about.stats.projects') %>
+
+
+
3+
+
<%- __('about.stats.experience') %>
+
+
+
30+
+
<%- __('about.stats.clients') %>
+
+
+
+ + +
+
+ +
+
+
+
+
+ + +
+
+
+

+ <%- __('about.mission.title') %> +

+

+ <%- __('about.mission.description') %> +

+
+ +
+ +
+
+ +
+

+ <%- __('about.values.innovation.title') %> +

+

+ <%- __('about.values.innovation.description') %> +

+
+ + +
+
+ +
+

+ <%- __('about.values.quality.title') %> +

+

+ <%- __('about.values.quality.description') %> +

+
+ + +
+
+ +
+

+ <%- __('about.values.partnership.title') %> +

+

+ <%- __('about.values.partnership.description') %> +

+
+
+
+
+ + +
+
+

+ <%- __('about.cta.title') %> +

+

+ <%- __('about.cta.subtitle') %> +

+ + <%- __('about.cta.button') %> + + + + +
+
+ + <%- include('partials/footer') %> + + + + + \ No newline at end of file diff --git a/.history/views/about_20251021212532.ejs b/.history/views/about_20251021212532.ejs new file mode 100644 index 0000000..8a673aa --- /dev/null +++ b/.history/views/about_20251021212532.ejs @@ -0,0 +1,160 @@ + + + + + + <%- __('about.meta.title') %> - SmartSolTech + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+

+ <%- __('about.hero.title') %> +

+

+ <%- __('about.hero.subtitle') %> +

+
+
+ + +
+
+
+
+

+ <%- __('about.company.title') %> +

+

+ <%- __('about.company.description1') %> +

+

+ <%- __('about.company.description2') %> +

+ + +
+
+
50+
+
<%- __('about.stats.projects') %>
+
+
+
3+
+
<%- __('about.stats.experience') %>
+
+
+
30+
+
<%- __('about.stats.clients') %>
+
+
+
+ + +
+
+ +
+
+
+
+
+ + +
+
+
+

+ <%- __('about.mission.title') %> +

+

+ <%- __('about.mission.description') %> +

+
+ +
+ +
+
+ +
+

+ <%- __('about.values.innovation.title') %> +

+

+ <%- __('about.values.innovation.description') %> +

+
+ + +
+
+ +
+

+ <%- __('about.values.quality.title') %> +

+

+ <%- __('about.values.quality.description') %> +

+
+ + +
+
+ +
+

+ <%- __('about.values.partnership.title') %> +

+

+ <%- __('about.values.partnership.description') %> +

+
+
+
+
+ + +
+
+

+ <%- __('about.cta.title') %> +

+

+ <%- __('about.cta.subtitle') %> +

+ + <%- __('about.cta.button') %> + + + + +
+
+ + <%- include('partials/footer') %> + + + + + \ No newline at end of file diff --git a/.history/views/admin/banner-editor_20251020225453.ejs b/.history/views/admin/banner-editor_20251020225453.ejs new file mode 100644 index 0000000..d416c48 --- /dev/null +++ b/.history/views/admin/banner-editor_20251020225453.ejs @@ -0,0 +1,596 @@ + + + + + + Редактор Баннеров - SmartSolTech Admin + + + + + + + + + +
+ +
+ +
+ + +
+
+
+

+ + Редактор Баннеров +

+
+ + +
+
+ + +
+
+ +
+
+ + +
+

+ Текущий баннер: Главная страница +

+
+
+
+

Текущий Баннер

+

Нажмите на изображение ниже, чтобы заменить

+
+
+
+
+ +
+ +
+
+
+ + +
+

+ Галерея изображений +

+ + + +
+ +
+ + +
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/.history/views/admin/banner-editor_20251020225526.ejs b/.history/views/admin/banner-editor_20251020225526.ejs new file mode 100644 index 0000000..d416c48 --- /dev/null +++ b/.history/views/admin/banner-editor_20251020225526.ejs @@ -0,0 +1,596 @@ + + + + + + Редактор Баннеров - SmartSolTech Admin + + + + + + + + + +
+ +
+ +
+ + +
+
+
+

+ + Редактор Баннеров +

+
+ + +
+
+ + +
+
+ +
+
+ + +
+

+ Текущий баннер: Главная страница +

+
+
+
+

Текущий Баннер

+

Нажмите на изображение ниже, чтобы заменить

+
+
+
+
+ +
+ +
+
+
+ + +
+

+ Галерея изображений +

+ + + +
+ +
+ + +
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/.history/views/admin/banner-editor_20251022193520.ejs b/.history/views/admin/banner-editor_20251022193520.ejs new file mode 100644 index 0000000..6276981 --- /dev/null +++ b/.history/views/admin/banner-editor_20251022193520.ejs @@ -0,0 +1,601 @@ + + + + + + Редактор Баннеров - SmartSolTech Admin + + + + + + + + + + + + + + +
+ +
+ +
+ + +
+
+
+

+ + Редактор Баннеров +

+
+ + +
+
+ + +
+
+ +
+
+ + +
+

+ Текущий баннер: Главная страница +

+
+
+
+

Текущий Баннер

+

Нажмите на изображение ниже, чтобы заменить

+
+
+
+
+ +
+ +
+
+
+ + +
+

+ Галерея изображений +

+ + + +
+ +
+ + +
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/.history/views/admin/banner-editor_20251022193602.ejs b/.history/views/admin/banner-editor_20251022193602.ejs new file mode 100644 index 0000000..3f2ba90 --- /dev/null +++ b/.history/views/admin/banner-editor_20251022193602.ejs @@ -0,0 +1,652 @@ + + + + + + Редактор Баннеров - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Редактор Баннеров +

+

Создание и редактирование баннеров для сайта

+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+

+ Текущий баннер: Главная страница +

+
+
+
+

Текущий Баннер

+

Нажмите на изображение ниже, чтобы заменить

+
+
+
+
+ +
+ +
+
+
+ + +
+

+ Галерея изображений +

+ + + +
+ +
+ + +
+
+ + + + + + + + + + \ No newline at end of file diff --git a/.history/views/admin/banner-editor_20251022193627.ejs b/.history/views/admin/banner-editor_20251022193627.ejs new file mode 100644 index 0000000..3f2ba90 --- /dev/null +++ b/.history/views/admin/banner-editor_20251022193627.ejs @@ -0,0 +1,652 @@ + + + + + + Редактор Баннеров - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Редактор Баннеров +

+

Создание и редактирование баннеров для сайта

+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+

+ Текущий баннер: Главная страница +

+
+
+
+

Текущий Баннер

+

Нажмите на изображение ниже, чтобы заменить

+
+
+
+
+ +
+ +
+
+
+ + +
+

+ Галерея изображений +

+ + + +
+ +
+ + +
+
+ + + + + + + + + + \ No newline at end of file diff --git a/.history/views/admin/banner-editor_20251022193634.ejs b/.history/views/admin/banner-editor_20251022193634.ejs new file mode 100644 index 0000000..379f7ff --- /dev/null +++ b/.history/views/admin/banner-editor_20251022193634.ejs @@ -0,0 +1,658 @@ + + + + + + Редактор Баннеров - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Редактор Баннеров +

+

Создание и редактирование баннеров для сайта

+
+ + +
+
+

Инструменты

+
+ + +
+
+
+ + +
+
+ +
+
+ + +
+

+ Текущий баннер: Главная страница +

+
+
+
+

Текущий Баннер

+

Нажмите на изображение ниже, чтобы заменить

+
+
+
+
+ +
+ +
+
+
+ + +
+

+ Галерея изображений +

+ + + +
+ +
+ + +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/.history/views/admin/banner-editor_20251022193641.ejs b/.history/views/admin/banner-editor_20251022193641.ejs new file mode 100644 index 0000000..6c09e74 --- /dev/null +++ b/.history/views/admin/banner-editor_20251022193641.ejs @@ -0,0 +1,660 @@ + + + + + + Редактор Баннеров - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Редактор Баннеров +

+

Создание и редактирование баннеров для сайта

+
+ + +
+
+

Инструменты

+
+ + +
+
+
+ + +
+ +
+
+ +
+
+ + +
+

+ Текущий баннер: Главная страница +

+
+
+
+

Текущий Баннер

+

Нажмите на изображение ниже, чтобы заменить

+
+
+
+
+ +
+ +
+
+
+ + +
+

+ Галерея изображений +

+ + + +
+ +
+ + +
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/.history/views/admin/banner-editor_20251022193752.ejs b/.history/views/admin/banner-editor_20251022193752.ejs new file mode 100644 index 0000000..b0853ff --- /dev/null +++ b/.history/views/admin/banner-editor_20251022193752.ejs @@ -0,0 +1,664 @@ + + + + + + Редактор Баннеров - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Редактор Баннеров +

+

Создание и редактирование баннеров для сайта

+
+ + +
+
+

Инструменты

+
+ + +
+
+
+ + +
+ +
+
+ +
+
+ + +
+

+ Текущий баннер: Главная страница +

+
+
+
+

Текущий Баннер

+

Нажмите на изображение ниже, чтобы заменить

+
+
+
+
+ +
+ +
+
+
+ + +
+

+ Галерея изображений +

+ + + +
+ +
+ + +
+
+
+
+ + + + + + + + + + + + \ No newline at end of file diff --git a/.history/views/admin/banner-editor_20251022194039.ejs b/.history/views/admin/banner-editor_20251022194039.ejs new file mode 100644 index 0000000..b0853ff --- /dev/null +++ b/.history/views/admin/banner-editor_20251022194039.ejs @@ -0,0 +1,664 @@ + + + + + + Редактор Баннеров - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Редактор Баннеров +

+

Создание и редактирование баннеров для сайта

+
+ + +
+
+

Инструменты

+
+ + +
+
+
+ + +
+ +
+
+ +
+
+ + +
+

+ Текущий баннер: Главная страница +

+
+
+
+

Текущий Баннер

+

Нажмите на изображение ниже, чтобы заменить

+
+
+
+
+ +
+ +
+
+
+ + +
+

+ Галерея изображений +

+ + + +
+ +
+ + +
+
+
+
+ + + + + + + + + + + + \ No newline at end of file diff --git a/.history/views/admin/contacts/list_20251021213012.ejs b/.history/views/admin/contacts/list_20251021213012.ejs new file mode 100644 index 0000000..430dffd --- /dev/null +++ b/.history/views/admin/contacts/list_20251021213012.ejs @@ -0,0 +1,117 @@ + +
+
+
+

+ + Управление сообщениями +

+
+ + +
+
+
+ +
+ +
+ + + <% if (pagination && pagination.total > 1) { %> +
+
+ <% if (pagination.hasPrev) { %> + + Предыдущая + + <% } %> + <% if (pagination.hasNext) { %> + + Следующая + + <% } %> +
+
+ <% } %> +
+ + \ No newline at end of file diff --git a/.history/views/admin/contacts/list_20251021214113.ejs b/.history/views/admin/contacts/list_20251021214113.ejs new file mode 100644 index 0000000..430dffd --- /dev/null +++ b/.history/views/admin/contacts/list_20251021214113.ejs @@ -0,0 +1,117 @@ + +
+
+
+

+ + Управление сообщениями +

+
+ + +
+
+
+ +
+ +
+ + + <% if (pagination && pagination.total > 1) { %> +
+
+ <% if (pagination.hasPrev) { %> + + Предыдущая + + <% } %> + <% if (pagination.hasNext) { %> + + Следующая + + <% } %> +
+
+ <% } %> +
+ + \ No newline at end of file diff --git a/.history/views/admin/contacts/view_20251021213046.ejs b/.history/views/admin/contacts/view_20251021213046.ejs new file mode 100644 index 0000000..f24bee0 --- /dev/null +++ b/.history/views/admin/contacts/view_20251021213046.ejs @@ -0,0 +1,219 @@ + +
+
+
+

+ + Детали сообщения +

+ + + Назад к списку + +
+
+ +
+
+ +
+

Информация о контакте

+ +
+
+
Имя
+
<%= contact.name %>
+
+ + + + <% if (contact.phone) { %> +
+
Телефон
+
+ + <%= contact.phone %> + +
+
+ <% } %> + +
+
Дата создания
+
+ <%= new Date(contact.createdAt).toLocaleString('ru-RU') %> +
+
+
+
+ + +
+

Детали проекта

+ +
+ <% if (contact.serviceInterest) { %> +
+
Интересующая услуга
+
+ + <%= contact.serviceInterest %> + +
+
+ <% } %> + + <% if (contact.budget) { %> +
+
Бюджет
+
<%= contact.budget %>
+
+ <% } %> + + <% if (contact.timeline) { %> +
+
Временные рамки
+
<%= contact.timeline %>
+
+ <% } %> + +
+
Статус
+
+ +
+
+ +
+
Приоритет
+
+ +
+
+
+
+
+ + +
+

Сообщение

+
+

<%= contact.message %>

+
+
+ + +
+ + + + + Ответить по email + + + +
+
+
+ + \ No newline at end of file diff --git a/.history/views/admin/contacts/view_20251021214113.ejs b/.history/views/admin/contacts/view_20251021214113.ejs new file mode 100644 index 0000000..f24bee0 --- /dev/null +++ b/.history/views/admin/contacts/view_20251021214113.ejs @@ -0,0 +1,219 @@ + +
+
+
+

+ + Детали сообщения +

+ + + Назад к списку + +
+
+ +
+
+ +
+

Информация о контакте

+ +
+
+
Имя
+
<%= contact.name %>
+
+ + + + <% if (contact.phone) { %> +
+
Телефон
+
+ + <%= contact.phone %> + +
+
+ <% } %> + +
+
Дата создания
+
+ <%= new Date(contact.createdAt).toLocaleString('ru-RU') %> +
+
+
+
+ + +
+

Детали проекта

+ +
+ <% if (contact.serviceInterest) { %> +
+
Интересующая услуга
+
+ + <%= contact.serviceInterest %> + +
+
+ <% } %> + + <% if (contact.budget) { %> +
+
Бюджет
+
<%= contact.budget %>
+
+ <% } %> + + <% if (contact.timeline) { %> +
+
Временные рамки
+
<%= contact.timeline %>
+
+ <% } %> + +
+
Статус
+
+ +
+
+ +
+
Приоритет
+
+ +
+
+
+
+
+ + +
+

Сообщение

+
+

<%= contact.message %>

+
+
+ + +
+ + + + + Ответить по email + + + +
+
+
+ + \ No newline at end of file diff --git a/.history/views/admin/dashboard_20251020043151.ejs b/.history/views/admin/dashboard_20251020043151.ejs new file mode 100644 index 0000000..a893db9 --- /dev/null +++ b/.history/views/admin/dashboard_20251020043151.ejs @@ -0,0 +1,228 @@ +<% layout('admin/layout') -%> + +
+ +
+

+ + <%= t('admin.dashboard') %> +

+

<%= t('admin.dashboard_subtitle') %>

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ <%= t('admin.portfolio_projects') %> +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.services') %> +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.contact_messages') %> +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.users') %> +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ <%= t('admin.recent_portfolio') %> +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_portfolio') %>

+ <% } %> +
+
+ + +
+
+

+ <%= t('admin.recent_contacts') %> +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_contacts') %>

+ <% } %> +
+
+
+ + +
+

+ <%= t('admin.quick_actions') %> +

+ +
+
\ No newline at end of file diff --git a/.history/views/admin/dashboard_20251020044146.ejs b/.history/views/admin/dashboard_20251020044146.ejs new file mode 100644 index 0000000..a893db9 --- /dev/null +++ b/.history/views/admin/dashboard_20251020044146.ejs @@ -0,0 +1,228 @@ +<% layout('admin/layout') -%> + +
+ +
+

+ + <%= t('admin.dashboard') %> +

+

<%= t('admin.dashboard_subtitle') %>

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ <%= t('admin.portfolio_projects') %> +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.services') %> +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.contact_messages') %> +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.users') %> +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ <%= t('admin.recent_portfolio') %> +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_portfolio') %>

+ <% } %> +
+
+ + +
+
+

+ <%= t('admin.recent_contacts') %> +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_contacts') %>

+ <% } %> +
+
+
+ + +
+

+ <%= t('admin.quick_actions') %> +

+ +
+
\ No newline at end of file diff --git a/.history/views/admin/dashboard_20251020044644.ejs b/.history/views/admin/dashboard_20251020044644.ejs new file mode 100644 index 0000000..b26e261 --- /dev/null +++ b/.history/views/admin/dashboard_20251020044644.ejs @@ -0,0 +1,228 @@ +<% layout('admin/layout') -%> + +
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ <%= t('admin.portfolio_projects') %> +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.services') %> +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.contact_messages') %> +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.users') %> +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ <%= t('admin.recent_portfolio') %> +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_portfolio') %>

+ <% } %> +
+
+ + +
+
+

+ <%= t('admin.recent_contacts') %> +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_contacts') %>

+ <% } %> +
+
+
+ + +
+

+ <%= t('admin.quick_actions') %> +

+ +
+
\ No newline at end of file diff --git a/.history/views/admin/dashboard_20251020044653.ejs b/.history/views/admin/dashboard_20251020044653.ejs new file mode 100644 index 0000000..45164ff --- /dev/null +++ b/.history/views/admin/dashboard_20251020044653.ejs @@ -0,0 +1,228 @@ +<% layout('admin/layout') -%> + +
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.services') %> +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.contact_messages') %> +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.users') %> +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ <%= t('admin.recent_portfolio') %> +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_portfolio') %>

+ <% } %> +
+
+ + +
+
+

+ <%= t('admin.recent_contacts') %> +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_contacts') %>

+ <% } %> +
+
+
+ + +
+

+ <%= t('admin.quick_actions') %> +

+ +
+
\ No newline at end of file diff --git a/.history/views/admin/dashboard_20251020044659.ejs b/.history/views/admin/dashboard_20251020044659.ejs new file mode 100644 index 0000000..c4a6f77 --- /dev/null +++ b/.history/views/admin/dashboard_20251020044659.ejs @@ -0,0 +1,228 @@ +<% layout('admin/layout') -%> + +
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.services') %> +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.contact_messages') %> +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ <%= t('admin.users') %> +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ <%= t('admin.recent_portfolio') %> +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_portfolio') %>

+ <% } %> +
+
+ + +
+
+

+ <%= t('admin.recent_contacts') %> +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_contacts') %>

+ <% } %> +
+
+
+ + +
+

+ <%= t('admin.quick_actions') %> +

+ +
+
\ No newline at end of file diff --git a/.history/views/admin/dashboard_20251020044717.ejs b/.history/views/admin/dashboard_20251020044717.ejs new file mode 100644 index 0000000..909ca0d --- /dev/null +++ b/.history/views/admin/dashboard_20251020044717.ejs @@ -0,0 +1,228 @@ +<% layout('admin/layout') -%> + +
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Услуги +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Сообщения +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Пользователи +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ <%= t('admin.recent_portfolio') %> +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_portfolio') %>

+ <% } %> +
+
+ + +
+
+

+ <%= t('admin.recent_contacts') %> +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_contacts') %>

+ <% } %> +
+
+
+ + +
+

+ <%= t('admin.quick_actions') %> +

+ +
+
\ No newline at end of file diff --git a/.history/views/admin/dashboard_20251020044727.ejs b/.history/views/admin/dashboard_20251020044727.ejs new file mode 100644 index 0000000..befbb1c --- /dev/null +++ b/.history/views/admin/dashboard_20251020044727.ejs @@ -0,0 +1,228 @@ +<% layout('admin/layout') -%> + +
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Услуги +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Сообщения +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Пользователи +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ Последние проекты +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_portfolio') %>

+ <% } %> +
+
+ + +
+
+

+ <%= t('admin.recent_contacts') %> +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_contacts') %>

+ <% } %> +
+
+
+ + +
+

+ <%= t('admin.quick_actions') %> +

+ +
+
\ No newline at end of file diff --git a/.history/views/admin/dashboard_20251020044735.ejs b/.history/views/admin/dashboard_20251020044735.ejs new file mode 100644 index 0000000..94e8eac --- /dev/null +++ b/.history/views/admin/dashboard_20251020044735.ejs @@ -0,0 +1,228 @@ +<% layout('admin/layout') -%> + +
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Услуги +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Сообщения +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Пользователи +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ Последние проекты +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних проектов

+ <% } %> +
+
+ + +
+
+

+ Последние сообщения +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

<%= t('admin.no_recent_contacts') %>

+ <% } %> +
+
+
+ + +
+

+ <%= t('admin.quick_actions') %> +

+ +
+
\ No newline at end of file diff --git a/.history/views/admin/dashboard_20251020044748.ejs b/.history/views/admin/dashboard_20251020044748.ejs new file mode 100644 index 0000000..22d0b5e --- /dev/null +++ b/.history/views/admin/dashboard_20251020044748.ejs @@ -0,0 +1,228 @@ +<% layout('admin/layout') -%> + +
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Услуги +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Сообщения +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Пользователи +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ Последние проекты +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних проектов

+ <% } %> +
+
+ + +
+
+

+ Последние сообщения +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних сообщений

+ <% } %> +
+
+
+ + +
+

+ Быстрые действия +

+ +
+
\ No newline at end of file diff --git a/.history/views/admin/dashboard_20251020044814.ejs b/.history/views/admin/dashboard_20251020044814.ejs new file mode 100644 index 0000000..22d0b5e --- /dev/null +++ b/.history/views/admin/dashboard_20251020044814.ejs @@ -0,0 +1,228 @@ +<% layout('admin/layout') -%> + +
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Услуги +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Сообщения +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Пользователи +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ Последние проекты +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних проектов

+ <% } %> +
+
+ + +
+
+

+ Последние сообщения +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних сообщений

+ <% } %> +
+
+
+ + +
+

+ Быстрые действия +

+ +
+
\ No newline at end of file diff --git a/.history/views/admin/dashboard_20251022051556.ejs b/.history/views/admin/dashboard_20251022051556.ejs new file mode 100644 index 0000000..d4a4f05 --- /dev/null +++ b/.history/views/admin/dashboard_20251022051556.ejs @@ -0,0 +1,228 @@ +<%- include('layout', { title: title, user: user }) %> + +
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Услуги +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Сообщения +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Пользователи +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ Последние проекты +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних проектов

+ <% } %> +
+
+ + +
+
+

+ Последние сообщения +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних сообщений

+ <% } %> +
+
+
+ + +
+

+ Быстрые действия +

+ +
+
\ No newline at end of file diff --git a/.history/views/admin/dashboard_20251022051559.ejs b/.history/views/admin/dashboard_20251022051559.ejs new file mode 100644 index 0000000..d4a4f05 --- /dev/null +++ b/.history/views/admin/dashboard_20251022051559.ejs @@ -0,0 +1,228 @@ +<%- include('layout', { title: title, user: user }) %> + +
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Услуги +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Сообщения +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Пользователи +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ Последние проекты +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних проектов

+ <% } %> +
+
+ + +
+
+

+ Последние сообщения +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних сообщений

+ <% } %> +
+
+
+ + +
+

+ Быстрые действия +

+ +
+
\ No newline at end of file diff --git a/.history/views/admin/dashboard_20251022051633.ejs b/.history/views/admin/dashboard_20251022051633.ejs new file mode 100644 index 0000000..4be8997 --- /dev/null +++ b/.history/views/admin/dashboard_20251022051633.ejs @@ -0,0 +1,312 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Услуги +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Сообщения +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Пользователи +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ Последние проекты +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних проектов

+ <% } %> +
+
+ + +
+
+

+ Последние сообщения +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних сообщений

+ <% } %> +
+
+
+ + + +
\ No newline at end of file diff --git a/.history/views/admin/dashboard_20251022051635.ejs b/.history/views/admin/dashboard_20251022051635.ejs new file mode 100644 index 0000000..4be8997 --- /dev/null +++ b/.history/views/admin/dashboard_20251022051635.ejs @@ -0,0 +1,312 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Услуги +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Сообщения +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Пользователи +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ Последние проекты +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних проектов

+ <% } %> +
+
+ + +
+
+

+ Последние сообщения +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних сообщений

+ <% } %> +
+
+
+ + + +
\ No newline at end of file diff --git a/.history/views/admin/dashboard_20251022051641.ejs b/.history/views/admin/dashboard_20251022051641.ejs new file mode 100644 index 0000000..db36e51 --- /dev/null +++ b/.history/views/admin/dashboard_20251022051641.ejs @@ -0,0 +1,319 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Услуги +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Сообщения +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Пользователи +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ Последние проекты +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних проектов

+ <% } %> +
+
+ + +
+
+

+ Последние сообщения +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних сообщений

+ <% } %> +
+
+
+ + + +
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/admin/dashboard_20251022051755.ejs b/.history/views/admin/dashboard_20251022051755.ejs new file mode 100644 index 0000000..db36e51 --- /dev/null +++ b/.history/views/admin/dashboard_20251022051755.ejs @@ -0,0 +1,319 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Услуги +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Сообщения +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Пользователи +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ Последние проекты +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних проектов

+ <% } %> +
+
+ + +
+
+

+ Последние сообщения +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних сообщений

+ <% } %> +
+
+
+ + + +
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/admin/dashboard_20251022052515.ejs b/.history/views/admin/dashboard_20251022052515.ejs new file mode 100644 index 0000000..a72cc3c --- /dev/null +++ b/.history/views/admin/dashboard_20251022052515.ejs @@ -0,0 +1,323 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Услуги +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Сообщения +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Пользователи +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ Последние проекты +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних проектов

+ <% } %> +
+
+ + +
+
+

+ Последние сообщения +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних сообщений

+ <% } %> +
+
+
+ + + +
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/admin/dashboard_20251022052523.ejs b/.history/views/admin/dashboard_20251022052523.ejs new file mode 100644 index 0000000..a72cc3c --- /dev/null +++ b/.history/views/admin/dashboard_20251022052523.ejs @@ -0,0 +1,323 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Панель управления +

+

Обзор основных показателей сайта

+
+ + +
+ +
+
+
+
+ +
+
+
+
+ Проекты +
+
+ <%= stats.portfolioCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Услуги +
+
+ <%= stats.servicesCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Сообщения +
+
+ <%= stats.contactsCount || 0 %> +
+
+
+
+
+ +
+ + +
+
+
+
+ +
+
+
+
+ Пользователи +
+
+ <%= stats.usersCount || 0 %> +
+
+
+
+
+ +
+
+ + +
+ +
+
+

+ Последние проекты +

+
+
+ <% if (recentPortfolio && recentPortfolio.length > 0) { %> +
+ <% recentPortfolio.forEach(function(project) { %> +
+
+ +
+
+

+ <%= project.title %> +

+

+ <%= project.category %> +

+
+
+ + <%= project.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних проектов

+ <% } %> +
+
+ + +
+
+

+ Последние сообщения +

+
+
+ <% if (recentContacts && recentContacts.length > 0) { %> +
+ <% recentContacts.forEach(function(contact) { %> +
+
+ +
+
+

+ <%= contact.name %> +

+

+ <%= contact.email %> +

+
+
+ + <%= contact.status %> + +
+
+ <% }); %> +
+ <% } else { %> +

Нет недавних сообщений

+ <% } %> +
+
+
+ + + +
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/admin/error_20251022050350.ejs b/.history/views/admin/error_20251022050350.ejs new file mode 100644 index 0000000..d044398 --- /dev/null +++ b/.history/views/admin/error_20251022050350.ejs @@ -0,0 +1,23 @@ +<% layout('admin/layout') -%> + + \ No newline at end of file diff --git a/.history/views/admin/error_20251022050417.ejs b/.history/views/admin/error_20251022050417.ejs new file mode 100644 index 0000000..d044398 --- /dev/null +++ b/.history/views/admin/error_20251022050417.ejs @@ -0,0 +1,23 @@ +<% layout('admin/layout') -%> + + \ No newline at end of file diff --git a/.history/views/admin/error_20251022051604.ejs b/.history/views/admin/error_20251022051604.ejs new file mode 100644 index 0000000..d238c31 --- /dev/null +++ b/.history/views/admin/error_20251022051604.ejs @@ -0,0 +1,23 @@ +<%- include('layout', { title: title, user: user }) %> + + \ No newline at end of file diff --git a/.history/views/admin/error_20251022051635.ejs b/.history/views/admin/error_20251022051635.ejs new file mode 100644 index 0000000..d238c31 --- /dev/null +++ b/.history/views/admin/error_20251022051635.ejs @@ -0,0 +1,23 @@ +<%- include('layout', { title: title, user: user }) %> + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251020043105.ejs b/.history/views/admin/layout_20251020043105.ejs new file mode 100644 index 0000000..bc71cd8 --- /dev/null +++ b/.history/views/admin/layout_20251020043105.ejs @@ -0,0 +1,89 @@ + + + + + + <%= title %> - <%= t('site.name') %> Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + <%= t('site.name') %> Admin +

+
+
+ + + <%= t('admin.view_site') %> + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251020043142.ejs b/.history/views/admin/layout_20251020043142.ejs new file mode 100644 index 0000000..bc71cd8 --- /dev/null +++ b/.history/views/admin/layout_20251020043142.ejs @@ -0,0 +1,89 @@ + + + + + + <%= title %> - <%= t('site.name') %> Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + <%= t('site.name') %> Admin +

+
+
+ + + <%= t('admin.view_site') %> + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251020044615.ejs b/.history/views/admin/layout_20251020044615.ejs new file mode 100644 index 0000000..d5fb4f4 --- /dev/null +++ b/.history/views/admin/layout_20251020044615.ejs @@ -0,0 +1,89 @@ + + + + + + <%= title %> - <%= t('site.name') %> Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + + <%= t('admin.view_site') %> + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251020044623.ejs b/.history/views/admin/layout_20251020044623.ejs new file mode 100644 index 0000000..4bb97e7 --- /dev/null +++ b/.history/views/admin/layout_20251020044623.ejs @@ -0,0 +1,89 @@ + + + + + + <%= title %> - <%= t('site.name') %> Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + + Посмотреть сайт + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251020044636.ejs b/.history/views/admin/layout_20251020044636.ejs new file mode 100644 index 0000000..4fa0570 --- /dev/null +++ b/.history/views/admin/layout_20251020044636.ejs @@ -0,0 +1,89 @@ + + + + + + <%= title %> - <%= t('site.name') %> Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + + Посмотреть сайт + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251020044814.ejs b/.history/views/admin/layout_20251020044814.ejs new file mode 100644 index 0000000..4fa0570 --- /dev/null +++ b/.history/views/admin/layout_20251020044814.ejs @@ -0,0 +1,89 @@ + + + + + + <%= title %> - <%= t('site.name') %> Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + + Посмотреть сайт + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251022050302.ejs b/.history/views/admin/layout_20251022050302.ejs new file mode 100644 index 0000000..9609e49 --- /dev/null +++ b/.history/views/admin/layout_20251022050302.ejs @@ -0,0 +1,89 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + + Посмотреть сайт + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251022050311.ejs b/.history/views/admin/layout_20251022050311.ejs new file mode 100644 index 0000000..9609e49 --- /dev/null +++ b/.history/views/admin/layout_20251022050311.ejs @@ -0,0 +1,89 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + + Посмотреть сайт + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251022051612.ejs b/.history/views/admin/layout_20251022051612.ejs new file mode 100644 index 0000000..e21d31a --- /dev/null +++ b/.history/views/admin/layout_20251022051612.ejs @@ -0,0 +1,89 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + + Посмотреть сайт + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251022051635.ejs b/.history/views/admin/layout_20251022051635.ejs new file mode 100644 index 0000000..e21d31a --- /dev/null +++ b/.history/views/admin/layout_20251022051635.ejs @@ -0,0 +1,89 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + + Посмотреть сайт + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251022052659.ejs b/.history/views/admin/layout_20251022052659.ejs new file mode 100644 index 0000000..9609e49 --- /dev/null +++ b/.history/views/admin/layout_20251022052659.ejs @@ -0,0 +1,89 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + + Посмотреть сайт + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251022052711.ejs b/.history/views/admin/layout_20251022052711.ejs new file mode 100644 index 0000000..151b76f --- /dev/null +++ b/.history/views/admin/layout_20251022052711.ejs @@ -0,0 +1,101 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + + Посмотреть сайт + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251022052722.ejs b/.history/views/admin/layout_20251022052722.ejs new file mode 100644 index 0000000..b09e7a6 --- /dev/null +++ b/.history/views/admin/layout_20251022052722.ejs @@ -0,0 +1,104 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251022052731.ejs b/.history/views/admin/layout_20251022052731.ejs new file mode 100644 index 0000000..dab51d1 --- /dev/null +++ b/.history/views/admin/layout_20251022052731.ejs @@ -0,0 +1,104 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/layout_20251022052852.ejs b/.history/views/admin/layout_20251022052852.ejs new file mode 100644 index 0000000..dab51d1 --- /dev/null +++ b/.history/views/admin/layout_20251022052852.ejs @@ -0,0 +1,104 @@ + + + + + + <%= title %> - SmartSolTech Admin + + + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/login_20251020043121.ejs b/.history/views/admin/login_20251020043121.ejs new file mode 100644 index 0000000..95a1a25 --- /dev/null +++ b/.history/views/admin/login_20251020043121.ejs @@ -0,0 +1,81 @@ + + + + + + <%= t('admin.login') %> - <%= t('site.name') %> + + + + + + + + + + + + +
+
+
+ +
+

+ <%= t('admin.login_title') %> +

+

+ <%= t('admin.login_subtitle') %> +

+
+ +
+ <% if (typeof error !== 'undefined') { %> +
+ + <%= error %> +
+ <% } %> + +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+ + + + + \ No newline at end of file diff --git a/.history/views/admin/login_20251020043142.ejs b/.history/views/admin/login_20251020043142.ejs new file mode 100644 index 0000000..95a1a25 --- /dev/null +++ b/.history/views/admin/login_20251020043142.ejs @@ -0,0 +1,81 @@ + + + + + + <%= t('admin.login') %> - <%= t('site.name') %> + + + + + + + + + + + + +
+
+
+ +
+

+ <%= t('admin.login_title') %> +

+

+ <%= t('admin.login_subtitle') %> +

+
+ +
+ <% if (typeof error !== 'undefined') { %> +
+ + <%= error %> +
+ <% } %> + +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+ + + + + \ No newline at end of file diff --git a/.history/views/admin/login_20251020044542.ejs b/.history/views/admin/login_20251020044542.ejs new file mode 100644 index 0000000..576634c --- /dev/null +++ b/.history/views/admin/login_20251020044542.ejs @@ -0,0 +1,81 @@ + + + + + + <%= t('admin.login') %> - <%= t('site.name') %> + + + + + + + + + + + + +
+
+
+ +
+

+ Вход в админ панель +

+

+ Войдите в свой аккаунт для управления сайтом +

+
+ +
+ <% if (typeof error !== 'undefined') { %> +
+ + <%= error %> +
+ <% } %> + +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+ + + + + \ No newline at end of file diff --git a/.history/views/admin/login_20251020044553.ejs b/.history/views/admin/login_20251020044553.ejs new file mode 100644 index 0000000..483d894 --- /dev/null +++ b/.history/views/admin/login_20251020044553.ejs @@ -0,0 +1,81 @@ + + + + + + <%= t('admin.login') %> - <%= t('site.name') %> + + + + + + + + + + + + +
+
+
+ +
+

+ Вход в админ панель +

+

+ Войдите в свой аккаунт для управления сайтом +

+
+ +
+ <% if (typeof error !== 'undefined') { %> +
+ + <%= error %> +
+ <% } %> + +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+ + + + + \ No newline at end of file diff --git a/.history/views/admin/login_20251020044602.ejs b/.history/views/admin/login_20251020044602.ejs new file mode 100644 index 0000000..0502977 --- /dev/null +++ b/.history/views/admin/login_20251020044602.ejs @@ -0,0 +1,81 @@ + + + + + + <%= t('admin.login') %> - <%= t('site.name') %> + + + + + + + + + + + + +
+
+
+ +
+

+ Вход в админ панель +

+

+ Войдите в свой аккаунт для управления сайтом +

+
+ +
+ <% if (typeof error !== 'undefined') { %> +
+ + <%= error %> +
+ <% } %> + +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+ + + + + \ No newline at end of file diff --git a/.history/views/admin/login_20251020044608.ejs b/.history/views/admin/login_20251020044608.ejs new file mode 100644 index 0000000..ee8f3ba --- /dev/null +++ b/.history/views/admin/login_20251020044608.ejs @@ -0,0 +1,81 @@ + + + + + + Вход в админ панель - SmartSolTech + + + + + + + + + + + + +
+
+
+ +
+

+ Вход в админ панель +

+

+ Войдите в свой аккаунт для управления сайтом +

+
+ +
+ <% if (typeof error !== 'undefined') { %> +
+ + <%= error %> +
+ <% } %> + +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+ + + + + \ No newline at end of file diff --git a/.history/views/admin/login_20251020044813.ejs b/.history/views/admin/login_20251020044813.ejs new file mode 100644 index 0000000..5509b45 --- /dev/null +++ b/.history/views/admin/login_20251020044813.ejs @@ -0,0 +1,81 @@ +о + + + + + Вход в админ панель - SmartSolTech + + + + + + + + + + + + +
+
+
+ +
+

+ Вход в админ панель +

+

+ Войдите в свой аккаунт для управления сайтом +

+
+ +
+ <% if (typeof error !== 'undefined') { %> +
+ + <%= error %> +
+ <% } %> + +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+ + + + + \ No newline at end of file diff --git a/.history/views/admin/login_20251020044814.ejs b/.history/views/admin/login_20251020044814.ejs new file mode 100644 index 0000000..5509b45 --- /dev/null +++ b/.history/views/admin/login_20251020044814.ejs @@ -0,0 +1,81 @@ +о + + + + + Вход в админ панель - SmartSolTech + + + + + + + + + + + + +
+
+
+ +
+

+ Вход в админ панель +

+

+ Войдите в свой аккаунт для управления сайтом +

+
+ +
+ <% if (typeof error !== 'undefined') { %> +
+ + <%= error %> +
+ <% } %> + +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+ + + + + \ No newline at end of file diff --git a/.history/views/admin/media_20251022052001.ejs b/.history/views/admin/media_20251022052001.ejs new file mode 100644 index 0000000..b27d34e --- /dev/null +++ b/.history/views/admin/media_20251022052001.ejs @@ -0,0 +1,848 @@ + + + + + + Медиа Галерея - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+
+
+

+ + Медиа Галерея +

+

Управление изображениями и файлами сайта

+
+
+ + +
+
+
+ + + + + + + + +
+
+
+
+ + +
+
+ + +
+
+
+ +
+ + +
+
+
+
+ + +
+
+

Файлы

+
+ Загрузка... +
+ + +
+
+
+ + +
+
+

Загрузка медиа файлов...

+
+ + + + + +
+ +
+ + + + + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/.history/views/admin/media_20251022052056.ejs b/.history/views/admin/media_20251022052056.ejs new file mode 100644 index 0000000..b27d34e --- /dev/null +++ b/.history/views/admin/media_20251022052056.ejs @@ -0,0 +1,848 @@ + + + + + + Медиа Галерея - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+
+
+

+ + Медиа Галерея +

+

Управление изображениями и файлами сайта

+
+
+ + +
+
+
+ + + + + + + + +
+
+
+
+ + +
+
+ + +
+
+
+ +
+ + +
+
+
+
+ + +
+
+

Файлы

+
+ Загрузка... +
+ + +
+
+
+ + +
+
+

Загрузка медиа файлов...

+
+ + + + + +
+ +
+ + + + + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/.history/views/admin/portfolio/add_20251021212914.ejs b/.history/views/admin/portfolio/add_20251021212914.ejs new file mode 100644 index 0000000..33e70a8 --- /dev/null +++ b/.history/views/admin/portfolio/add_20251021212914.ejs @@ -0,0 +1,186 @@ + +
+
+

+ + Добавить проект +

+
+ +
+
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+
+ + + +
+ +

или перетащите сюда

+
+

PNG, JPG, WEBP до 10MB

+
+
+
+
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ + Отмена + + +
+
+
+ + \ No newline at end of file diff --git a/.history/views/admin/portfolio/add_20251021214113.ejs b/.history/views/admin/portfolio/add_20251021214113.ejs new file mode 100644 index 0000000..33e70a8 --- /dev/null +++ b/.history/views/admin/portfolio/add_20251021214113.ejs @@ -0,0 +1,186 @@ + +
+
+

+ + Добавить проект +

+
+ +
+
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+
+ + + +
+ +

или перетащите сюда

+
+

PNG, JPG, WEBP до 10MB

+
+
+
+
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ + Отмена + + +
+
+
+ + \ No newline at end of file diff --git a/.history/views/admin/portfolio/add_20251022195336.ejs b/.history/views/admin/portfolio/add_20251022195336.ejs new file mode 100644 index 0000000..6152e64 --- /dev/null +++ b/.history/views/admin/portfolio/add_20251022195336.ejs @@ -0,0 +1,223 @@ + +
+
+
+
+
+

+ + Добавить новый проект +

+

Заполните информацию о проекте для добавления в портфолио

+
+
+ + + + Назад к списку + +
+
+
+ +
+ +
+
+
+
1
+ Основная информация +
+
+
+
2
+ Медиа и изображения +
+
+
+
3
+ Публикация +
+
+
+ +
+
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+
+ + + +
+ +

или перетащите сюда

+
+

PNG, JPG, WEBP до 10MB

+
+
+
+
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ + Отмена + + +
+ +
+ + \ No newline at end of file diff --git a/.history/views/admin/portfolio/add_20251022195407.ejs b/.history/views/admin/portfolio/add_20251022195407.ejs new file mode 100644 index 0000000..73b92cd --- /dev/null +++ b/.history/views/admin/portfolio/add_20251022195407.ejs @@ -0,0 +1,266 @@ + +
+
+
+
+
+

+ + Добавить новый проект +

+

Заполните информацию о проекте для добавления в портфолио

+
+
+ + + + Назад к списку + +
+
+
+ +
+ +
+
+
+
1
+ Основная информация +
+
+
+
2
+ Медиа и изображения +
+
+
+
3
+ Публикация +
+
+
+ +
+ +
+

+ + Основная информация о проекте +

+ +
+ +
+ + +

Используйте описательное название, которое четко передает суть проекта

+
+ + +
+ + +
+ Отображается в превью проекта + 0/200 +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+
+ + + +
+ +

или перетащите сюда

+
+

PNG, JPG, WEBP до 10MB

+
+
+
+
+ + +
+
+ + +
+ +
+ + +
+
+ + +
+ + Отмена + + +
+ +
+ + \ No newline at end of file diff --git a/.history/views/admin/portfolio/add_20251022195457.ejs b/.history/views/admin/portfolio/add_20251022195457.ejs new file mode 100644 index 0000000..998b387 --- /dev/null +++ b/.history/views/admin/portfolio/add_20251022195457.ejs @@ -0,0 +1,385 @@ + +
+
+
+
+
+

+ + Добавить новый проект +

+

Заполните информацию о проекте для добавления в портфолио

+
+
+ + + + Назад к списку + +
+
+
+ +
+ +
+
+
+
1
+ Основная информация +
+
+
+
2
+ Медиа и изображения +
+
+
+
3
+ Публикация +
+
+
+ +
+ +
+

+ + Основная информация о проекте +

+ +
+ +
+ + +

Используйте описательное название, которое четко передает суть проекта

+
+ + +
+ + +
+ Отображается в превью проекта + 0/200 +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+
+ + +
+

+ + Описание и технические детали +

+ + +
+ +
+ +

Опишите задачи, решения и результаты проекта

+
+ + +
+ +
+ +
+ +
+
+
+ + + +
+

Популярные технологии:

+
+ <% const popularTechs = ['React', 'Vue.js', 'Node.js', 'Express.js', 'MongoDB', 'PostgreSQL', 'MySQL', 'JavaScript', 'TypeScript', 'HTML5', 'CSS3', 'Tailwind CSS', 'Bootstrap', 'Webpack', 'Docker', 'Git', 'AWS', 'Figma', 'Photoshop']; %> + <% popularTechs.forEach(tech => { %> + + <% }); %> +
+
+
+
+ + +
+

+ + Изображения и медиа контент +

+ +
+ +
+ +
+
+
+ + + +
+
+ + или перетащите файлы сюда +
+

+ PNG, JPG, WEBP до 10MB каждый. Первое изображение будет использоваться как главное. +

+
+
+
+ + + +
+
+ + +
+

+ + Настройки публикации +

+ +
+ +
+
+
+ +
+
+ +

Проект будет виден посетителям сайта

+
+
+ +
+
+ +
+
+ +

Проект будет показан в топе портфолио

+
+
+
+ + +
+
SEO настройки (необязательно)
+
+
+ + +

Рекомендуется до 60 символов

+
+ +
+ + +

Рекомендуется до 160 символов

+
+
+
+
+
+ + +
+
+ +
+ +
+ + Отмена + + + +
+
+ +
+ + \ No newline at end of file diff --git a/.history/views/admin/portfolio/add_20251022195641.ejs b/.history/views/admin/portfolio/add_20251022195641.ejs new file mode 100644 index 0000000..6032e4d --- /dev/null +++ b/.history/views/admin/portfolio/add_20251022195641.ejs @@ -0,0 +1,776 @@ + +
+
+
+
+
+

+ + Добавить новый проект +

+

Заполните информацию о проекте для добавления в портфолио

+
+
+ + + + Назад к списку + +
+
+
+ +
+ +
+
+
+
1
+ Основная информация +
+
+
+
2
+ Медиа и изображения +
+
+
+
3
+ Публикация +
+
+
+ +
+ +
+

+ + Основная информация о проекте +

+ +
+ +
+ + +

Используйте описательное название, которое четко передает суть проекта

+
+ + +
+ + +
+ Отображается в превью проекта + 0/200 +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+
+ + +
+

+ + Описание и технические детали +

+ + +
+ +
+ +

Опишите задачи, решения и результаты проекта

+
+ + +
+ +
+ +
+ +
+
+
+ + + +
+

Популярные технологии:

+
+ <% const popularTechs = ['React', 'Vue.js', 'Node.js', 'Express.js', 'MongoDB', 'PostgreSQL', 'MySQL', 'JavaScript', 'TypeScript', 'HTML5', 'CSS3', 'Tailwind CSS', 'Bootstrap', 'Webpack', 'Docker', 'Git', 'AWS', 'Figma', 'Photoshop']; %> + <% popularTechs.forEach(tech => { %> + + <% }); %> +
+
+
+
+ + +
+

+ + Изображения и медиа контент +

+ +
+ +
+ +
+
+
+ + + +
+
+ + или перетащите файлы сюда +
+

+ PNG, JPG, WEBP до 10MB каждый. Первое изображение будет использоваться как главное. +

+
+
+
+ + + +
+
+ + +
+

+ + Настройки публикации +

+ +
+ +
+
+
+ +
+
+ +

Проект будет виден посетителям сайта

+
+
+ +
+
+ +
+
+ +

Проект будет показан в топе портфолио

+
+
+
+ + +
+
SEO настройки (необязательно)
+
+
+ + +

Рекомендуется до 60 символов

+
+ +
+ + +

Рекомендуется до 160 символов

+
+
+
+
+
+ + +
+
+ +
+ +
+ + Отмена + + + +
+
+ +
+ + + + + + \ No newline at end of file diff --git a/.history/views/admin/portfolio/add_20251022195905.ejs b/.history/views/admin/portfolio/add_20251022195905.ejs new file mode 100644 index 0000000..6032e4d --- /dev/null +++ b/.history/views/admin/portfolio/add_20251022195905.ejs @@ -0,0 +1,776 @@ + +
+
+
+
+
+

+ + Добавить новый проект +

+

Заполните информацию о проекте для добавления в портфолио

+
+
+ + + + Назад к списку + +
+
+
+ +
+ +
+
+
+
1
+ Основная информация +
+
+
+
2
+ Медиа и изображения +
+
+
+
3
+ Публикация +
+
+
+ +
+ +
+

+ + Основная информация о проекте +

+ +
+ +
+ + +

Используйте описательное название, которое четко передает суть проекта

+
+ + +
+ + +
+ Отображается в превью проекта + 0/200 +
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+
+ + +
+

+ + Описание и технические детали +

+ + +
+ +
+ +

Опишите задачи, решения и результаты проекта

+
+ + +
+ +
+ +
+ +
+
+
+ + + +
+

Популярные технологии:

+
+ <% const popularTechs = ['React', 'Vue.js', 'Node.js', 'Express.js', 'MongoDB', 'PostgreSQL', 'MySQL', 'JavaScript', 'TypeScript', 'HTML5', 'CSS3', 'Tailwind CSS', 'Bootstrap', 'Webpack', 'Docker', 'Git', 'AWS', 'Figma', 'Photoshop']; %> + <% popularTechs.forEach(tech => { %> + + <% }); %> +
+
+
+
+ + +
+

+ + Изображения и медиа контент +

+ +
+ +
+ +
+
+
+ + + +
+
+ + или перетащите файлы сюда +
+

+ PNG, JPG, WEBP до 10MB каждый. Первое изображение будет использоваться как главное. +

+
+
+
+ + + +
+
+ + +
+

+ + Настройки публикации +

+ +
+ +
+
+
+ +
+
+ +

Проект будет виден посетителям сайта

+
+
+ +
+
+ +
+
+ +

Проект будет показан в топе портфолио

+
+
+
+ + +
+
SEO настройки (необязательно)
+
+
+ + +

Рекомендуется до 60 символов

+
+ +
+ + +

Рекомендуется до 160 символов

+
+
+
+
+
+ + +
+
+ +
+ +
+ + Отмена + + + +
+
+ +
+ + + + + + \ No newline at end of file diff --git a/.history/views/admin/portfolio/list_20251021212843.ejs b/.history/views/admin/portfolio/list_20251021212843.ejs new file mode 100644 index 0000000..b54e8e7 --- /dev/null +++ b/.history/views/admin/portfolio/list_20251021212843.ejs @@ -0,0 +1,123 @@ + +
+
+
+

+ + Управление портфолио +

+ + + Добавить проект + +
+
+ +
+
    + <% if (portfolio && portfolio.length > 0) { %> + <% portfolio.forEach(item => { %> +
  • +
    +
    +
    + <% if (item.images && item.images.length > 0) { %> + + <% } else { %> +
    + +
    + <% } %> +
    +
    +
    +
    <%= item.title %>
    + <% if (item.featured) { %> + + + Рекомендуемое + + <% } %> + <% if (!item.isPublished) { %> + + Черновик + + <% } %> +
    +
    + <%= item.category %> • + <%= new Date(item.createdAt).toLocaleDateString('ru-RU') %> +
    +
    +
    +
    + + + + + + + +
    +
    +
  • + <% }) %> + <% } else { %> +
  • +
    + +

    Проекты не найдены

    + + Добавить первый проект + +
    +
  • + <% } %> +
+
+ + + <% if (pagination && pagination.total > 1) { %> +
+
+ <% if (pagination.hasPrev) { %> + + Предыдущая + + <% } %> + <% if (pagination.hasNext) { %> + + Следующая + + <% } %> +
+
+ <% } %> +
+ + \ No newline at end of file diff --git a/.history/views/admin/portfolio/list_20251021214113.ejs b/.history/views/admin/portfolio/list_20251021214113.ejs new file mode 100644 index 0000000..b54e8e7 --- /dev/null +++ b/.history/views/admin/portfolio/list_20251021214113.ejs @@ -0,0 +1,123 @@ + +
+
+
+

+ + Управление портфолио +

+ + + Добавить проект + +
+
+ +
+
    + <% if (portfolio && portfolio.length > 0) { %> + <% portfolio.forEach(item => { %> +
  • +
    +
    +
    + <% if (item.images && item.images.length > 0) { %> + + <% } else { %> +
    + +
    + <% } %> +
    +
    +
    +
    <%= item.title %>
    + <% if (item.featured) { %> + + + Рекомендуемое + + <% } %> + <% if (!item.isPublished) { %> + + Черновик + + <% } %> +
    +
    + <%= item.category %> • + <%= new Date(item.createdAt).toLocaleDateString('ru-RU') %> +
    +
    +
    +
    + + + + + + + +
    +
    +
  • + <% }) %> + <% } else { %> +
  • +
    + +

    Проекты не найдены

    + + Добавить первый проект + +
    +
  • + <% } %> +
+
+ + + <% if (pagination && pagination.total > 1) { %> +
+
+ <% if (pagination.hasPrev) { %> + + Предыдущая + + <% } %> + <% if (pagination.hasNext) { %> + + Следующая + + <% } %> +
+
+ <% } %> +
+ + \ No newline at end of file diff --git a/.history/views/admin/portfolio/list_20251022195202.ejs b/.history/views/admin/portfolio/list_20251022195202.ejs new file mode 100644 index 0000000..bd7f666 --- /dev/null +++ b/.history/views/admin/portfolio/list_20251022195202.ejs @@ -0,0 +1,147 @@ + +
+
+
+
+

+ + Управление портфолио +

+

+ Всего проектов: <%= portfolio ? portfolio.length : 0 %> +

+
+
+
+ + +
+ + + + Добавить проект + +
+
+
+ +
+
    + <% if (portfolio && portfolio.length > 0) { %> + <% portfolio.forEach(item => { %> +
  • +
    +
    +
    + <% if (item.images && item.images.length > 0) { %> + + <% } else { %> +
    + +
    + <% } %> +
    +
    +
    +
    <%= item.title %>
    + <% if (item.featured) { %> + + + Рекомендуемое + + <% } %> + <% if (!item.isPublished) { %> + + Черновик + + <% } %> +
    +
    + <%= item.category %> • + <%= new Date(item.createdAt).toLocaleDateString('ru-RU') %> +
    +
    +
    +
    + + + + + + + +
    +
    +
  • + <% }) %> + <% } else { %> +
  • +
    + +

    Проекты не найдены

    + + Добавить первый проект + +
    +
  • + <% } %> +
+
+ + + <% if (pagination && pagination.total > 1) { %> +
+
+ <% if (pagination.hasPrev) { %> + + Предыдущая + + <% } %> + <% if (pagination.hasNext) { %> + + Следующая + + <% } %> +
+
+ <% } %> +
+ + \ No newline at end of file diff --git a/.history/views/admin/portfolio/list_20251022195234.ejs b/.history/views/admin/portfolio/list_20251022195234.ejs new file mode 100644 index 0000000..c556764 --- /dev/null +++ b/.history/views/admin/portfolio/list_20251022195234.ejs @@ -0,0 +1,186 @@ + +
+
+
+
+

+ + Управление портфолио +

+

+ Всего проектов: <%= portfolio ? portfolio.length : 0 %> +

+
+
+
+ + +
+ + + + Добавить проект + +
+
+
+ +
+
    + <% if (portfolio && portfolio.length > 0) { %> + <% portfolio.forEach(item => { %> +
  • +
    +
    +
    +
    + <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
    + +
    + <% } %> +
    +
    +
    +

    <%= item.title %>

    +
    + <% if (item.featured) { %> + + + Рекомендуемое + + <% } %> + + + <%= item.isPublished ? 'Опубликовано' : 'Черновик' %> + +
    +
    +

    <%= item.shortDescription || 'Описание не указано' %>

    +
    +
    + + <%= item.category.replace('-', ' ') %> +
    +
    + + <%= new Date(item.createdAt).toLocaleDateString('ru-RU') %> +
    + <% if (item.viewCount && item.viewCount > 0) { %> +
    + + <%= item.viewCount %> просмотров +
    + <% } %> + <% if (item.technologies && item.technologies.length > 0) { %> +
    + + <%= item.technologies.slice(0, 2).join(', ') %><%= item.technologies.length > 2 ? '...' : '' %> +
    + <% } %> +
    +
    +
    +
    + <% if (item.isPublished) { %> + + + + <% } %> + + + + + + +
    +
    +
    +
  • + <% }) %> + <% } else { %> +
  • +
    + +

    Проекты не найдены

    + + Добавить первый проект + +
    +
  • + <% } %> +
+
+ + + <% if (pagination && pagination.total > 1) { %> +
+
+ <% if (pagination.hasPrev) { %> + + Предыдущая + + <% } %> + <% if (pagination.hasNext) { %> + + Следующая + + <% } %> +
+
+ <% } %> +
+ + \ No newline at end of file diff --git a/.history/views/admin/portfolio/list_20251022195245.ejs b/.history/views/admin/portfolio/list_20251022195245.ejs new file mode 100644 index 0000000..b4386d2 --- /dev/null +++ b/.history/views/admin/portfolio/list_20251022195245.ejs @@ -0,0 +1,186 @@ + +
+
+
+
+

+ + Управление портфолио +

+

+ Всего проектов: <%= portfolio ? portfolio.length : 0 %> +

+
+
+
+ + +
+ + + + Добавить проект + +
+
+
+ +
+
    + <% if (portfolio && portfolio.length > 0) { %> + <% portfolio.forEach(item => { %> +
  • +
    +
    +
    +
    + <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
    + +
    + <% } %> +
    +
    +
    +

    <%= item.title %>

    +
    + <% if (item.featured) { %> + + + Рекомендуемое + + <% } %> + + + <%= item.isPublished ? 'Опубликовано' : 'Черновик' %> + +
    +
    +

    <%= item.shortDescription || 'Описание не указано' %>

    +
    +
    + + <%= item.category.replace('-', ' ') %> +
    +
    + + <%= new Date(item.createdAt).toLocaleDateString('ru-RU') %> +
    + <% if (item.viewCount && item.viewCount > 0) { %> +
    + + <%= item.viewCount %> просмотров +
    + <% } %> + <% if (item.technologies && item.technologies.length > 0) { %> +
    + + <%= item.technologies.slice(0, 2).join(', ') %><%= item.technologies.length > 2 ? '...' : '' %> +
    + <% } %> +
    +
    +
    +
    + <% if (item.isPublished) { %> + + + + <% } %> + + + + + + +
    +
    +
    +
  • + <% }) %> + <% } else { %> +
  • +
    + +

    Проекты не найдены

    + + Добавить первый проект + +
    +
  • + <% } %> +
+
+ + + <% if (pagination && pagination.total > 1) { %> +
+
+ <% if (pagination.hasPrev) { %> + + Предыдущая + + <% } %> + <% if (pagination.hasNext) { %> + + Следующая + + <% } %> +
+
+ <% } %> +
+ + \ No newline at end of file diff --git a/.history/views/admin/portfolio/list_20251022195313.ejs b/.history/views/admin/portfolio/list_20251022195313.ejs new file mode 100644 index 0000000..7c7031a --- /dev/null +++ b/.history/views/admin/portfolio/list_20251022195313.ejs @@ -0,0 +1,358 @@ + +
+
+
+
+

+ + Управление портфолио +

+

+ Всего проектов: <%= portfolio ? portfolio.length : 0 %> +

+
+
+
+ + +
+ + + + Добавить проект + +
+
+
+ +
+
    + <% if (portfolio && portfolio.length > 0) { %> + <% portfolio.forEach(item => { %> +
  • +
    +
    +
    +
    + <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
    + +
    + <% } %> +
    +
    +
    +

    <%= item.title %>

    +
    + <% if (item.featured) { %> + + + Рекомендуемое + + <% } %> + + + <%= item.isPublished ? 'Опубликовано' : 'Черновик' %> + +
    +
    +

    <%= item.shortDescription || 'Описание не указано' %>

    +
    +
    + + <%= item.category.replace('-', ' ') %> +
    +
    + + <%= new Date(item.createdAt).toLocaleDateString('ru-RU') %> +
    + <% if (item.viewCount && item.viewCount > 0) { %> +
    + + <%= item.viewCount %> просмотров +
    + <% } %> + <% if (item.technologies && item.technologies.length > 0) { %> +
    + + <%= item.technologies.slice(0, 2).join(', ') %><%= item.technologies.length > 2 ? '...' : '' %> +
    + <% } %> +
    +
    +
    +
    + <% if (item.isPublished) { %> + + + + <% } %> + + + + + + +
    +
    +
    +
  • + <% }) %> + <% } else { %> +
  • +
    + +

    Проекты не найдены

    + + Добавить первый проект + +
    +
  • + <% } %> +
+
+ + + <% if (pagination && pagination.total > 1) { %> +
+
+ <% if (pagination.hasPrev) { %> + + Предыдущая + + <% } %> + <% if (pagination.hasNext) { %> + + Следующая + + <% } %> +
+
+ <% } %> +
+ + \ No newline at end of file diff --git a/.history/views/admin/portfolio/list_20251022195905.ejs b/.history/views/admin/portfolio/list_20251022195905.ejs new file mode 100644 index 0000000..7c7031a --- /dev/null +++ b/.history/views/admin/portfolio/list_20251022195905.ejs @@ -0,0 +1,358 @@ + +
+
+
+
+

+ + Управление портфолио +

+

+ Всего проектов: <%= portfolio ? portfolio.length : 0 %> +

+
+
+
+ + +
+ + + + Добавить проект + +
+
+
+ +
+
    + <% if (portfolio && portfolio.length > 0) { %> + <% portfolio.forEach(item => { %> +
  • +
    +
    +
    +
    + <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
    + +
    + <% } %> +
    +
    +
    +

    <%= item.title %>

    +
    + <% if (item.featured) { %> + + + Рекомендуемое + + <% } %> + + + <%= item.isPublished ? 'Опубликовано' : 'Черновик' %> + +
    +
    +

    <%= item.shortDescription || 'Описание не указано' %>

    +
    +
    + + <%= item.category.replace('-', ' ') %> +
    +
    + + <%= new Date(item.createdAt).toLocaleDateString('ru-RU') %> +
    + <% if (item.viewCount && item.viewCount > 0) { %> +
    + + <%= item.viewCount %> просмотров +
    + <% } %> + <% if (item.technologies && item.technologies.length > 0) { %> +
    + + <%= item.technologies.slice(0, 2).join(', ') %><%= item.technologies.length > 2 ? '...' : '' %> +
    + <% } %> +
    +
    +
    +
    + <% if (item.isPublished) { %> + + + + <% } %> + + + + + + +
    +
    +
    +
  • + <% }) %> + <% } else { %> +
  • +
    + +

    Проекты не найдены

    + + Добавить первый проект + +
    +
  • + <% } %> +
+
+ + + <% if (pagination && pagination.total > 1) { %> +
+
+ <% if (pagination.hasPrev) { %> + + Предыдущая + + <% } %> + <% if (pagination.hasNext) { %> + + Следующая + + <% } %> +
+
+ <% } %> +
+ + \ No newline at end of file diff --git a/.history/views/admin/services/list_20251021212943.ejs b/.history/views/admin/services/list_20251021212943.ejs new file mode 100644 index 0000000..5d2add4 --- /dev/null +++ b/.history/views/admin/services/list_20251021212943.ejs @@ -0,0 +1,121 @@ + +
+
+
+

+ + Управление услугами +

+ + + Добавить услугу + +
+
+ +
+
    + <% if (services && services.length > 0) { %> + <% services.forEach(service => { %> +
  • +
    +
    +
    +
    + +
    +
    +
    +
    +
    <%= service.name %>
    + <% if (service.featured) { %> + + + Рекомендуемая + + <% } %> + <% if (!service.isActive) { %> + + Неактивна + + <% } %> +
    +
    + <%= service.category %> • + <% if (service.pricing && service.pricing.basePrice) { %> + от $<%= service.pricing.basePrice %> + <% } %> +
    +
    +
    +
    + + + + + + + +
    +
    +
  • + <% }) %> + <% } else { %> +
  • +
    + +

    Услуги не найдены

    + + Добавить первую услугу + +
    +
  • + <% } %> +
+
+ + + <% if (pagination && pagination.total > 1) { %> +
+
+ <% if (pagination.hasPrev) { %> + + Предыдущая + + <% } %> + <% if (pagination.hasNext) { %> + + Следующая + + <% } %> +
+
+ <% } %> +
+ + \ No newline at end of file diff --git a/.history/views/admin/services/list_20251021214113.ejs b/.history/views/admin/services/list_20251021214113.ejs new file mode 100644 index 0000000..5d2add4 --- /dev/null +++ b/.history/views/admin/services/list_20251021214113.ejs @@ -0,0 +1,121 @@ + +
+
+
+

+ + Управление услугами +

+ + + Добавить услугу + +
+
+ +
+
    + <% if (services && services.length > 0) { %> + <% services.forEach(service => { %> +
  • +
    +
    +
    +
    + +
    +
    +
    +
    +
    <%= service.name %>
    + <% if (service.featured) { %> + + + Рекомендуемая + + <% } %> + <% if (!service.isActive) { %> + + Неактивна + + <% } %> +
    +
    + <%= service.category %> • + <% if (service.pricing && service.pricing.basePrice) { %> + от $<%= service.pricing.basePrice %> + <% } %> +
    +
    +
    +
    + + + + + + + +
    +
    +
  • + <% }) %> + <% } else { %> +
  • +
    + +

    Услуги не найдены

    + + Добавить первую услугу + +
    +
  • + <% } %> +
+
+ + + <% if (pagination && pagination.total > 1) { %> +
+
+ <% if (pagination.hasPrev) { %> + + Предыдущая + + <% } %> + <% if (pagination.hasNext) { %> + + Следующая + + <% } %> +
+
+ <% } %> +
+ + \ No newline at end of file diff --git a/.history/views/admin/settings_20251021213351.ejs b/.history/views/admin/settings_20251021213351.ejs new file mode 100644 index 0000000..61ea517 --- /dev/null +++ b/.history/views/admin/settings_20251021213351.ejs @@ -0,0 +1,242 @@ + +
+
+

+ + Настройки сайта +

+
+ +
+
+ +
+

Основные настройки

+
+
+ + +
+ +
+ + +
+ +
+ + + <% if (settings.logo) { %> + Current logo + <% } %> +
+ +
+ + + <% if (settings.favicon) { %> + Current favicon + <% } %> +
+
+
+ + +
+

Контактная информация

+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

Социальные сети

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

Telegram Bot

+
+
+ + +

Получите токен у @BotFather

+
+ +
+ + +

ID чата для уведомлений

+
+ +
+ +
+
+
+
+ + +
+

SEO настройки

+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+ + +
+ +
+
+
+ + \ No newline at end of file diff --git a/.history/views/admin/settings_20251021214113.ejs b/.history/views/admin/settings_20251021214113.ejs new file mode 100644 index 0000000..61ea517 --- /dev/null +++ b/.history/views/admin/settings_20251021214113.ejs @@ -0,0 +1,242 @@ + +
+
+

+ + Настройки сайта +

+
+ +
+
+ +
+

Основные настройки

+
+
+ + +
+ +
+ + +
+ +
+ + + <% if (settings.logo) { %> + Current logo + <% } %> +
+ +
+ + + <% if (settings.favicon) { %> + Current favicon + <% } %> +
+
+
+ + +
+

Контактная информация

+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

Социальные сети

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

Telegram Bot

+
+
+ + +

Получите токен у @BotFather

+
+ +
+ + +

ID чата для уведомлений

+
+ +
+ +
+
+
+
+ + +
+

SEO настройки

+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+ + +
+ +
+
+
+ + \ No newline at end of file diff --git a/.history/views/admin/settings_20251022052811.ejs b/.history/views/admin/settings_20251022052811.ejs new file mode 100644 index 0000000..15885e1 --- /dev/null +++ b/.history/views/admin/settings_20251022052811.ejs @@ -0,0 +1,342 @@ + + + + + + Настройки сайта - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Настройки сайта +

+

Управление основными параметрами сайта

+
+ + +
+
+

+ + Настройки сайта +

+
+ +
+
+ +
+

Основные настройки

+
+
+ + +
+ +
+ + +
+ +
+ + + <% if (settings.logo) { %> + Current logo + <% } %> +
+ +
+ + + <% if (settings.favicon) { %> + Current favicon + <% } %> +
+
+
+ + +
+

Контактная информация

+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

Социальные сети

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

Telegram Bot

+
+
+ + +

Получите токен у @BotFather

+
+ +
+ + +

ID чата для уведомлений

+
+ +
+ +
+
+
+
+ + +
+

SEO настройки

+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+ + +
+ +
+
+
+ + \ No newline at end of file diff --git a/.history/views/admin/settings_20251022052834.ejs b/.history/views/admin/settings_20251022052834.ejs new file mode 100644 index 0000000..581ae50 --- /dev/null +++ b/.history/views/admin/settings_20251022052834.ejs @@ -0,0 +1,350 @@ + + + + + + Настройки сайта - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Настройки сайта +

+

Управление основными параметрами сайта

+
+ + +
+
+

+ + Настройки сайта +

+
+ +
+
+ +
+

Основные настройки

+
+
+ + +
+ +
+ + +
+ +
+ + + <% if (settings.logo) { %> + Current logo + <% } %> +
+ +
+ + + <% if (settings.favicon) { %> + Current favicon + <% } %> +
+
+
+ + +
+

Контактная информация

+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

Социальные сети

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

Telegram Bot

+
+
+ + +

Получите токен у @BotFather

+
+ +
+ + +

ID чата для уведомлений

+
+ +
+ +
+
+
+
+ + +
+

SEO настройки

+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+ + +
+ +
+
+
+ + +
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/admin/settings_20251022052852.ejs b/.history/views/admin/settings_20251022052852.ejs new file mode 100644 index 0000000..581ae50 --- /dev/null +++ b/.history/views/admin/settings_20251022052852.ejs @@ -0,0 +1,350 @@ + + + + + + Настройки сайта - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+

+ + Настройки сайта +

+

Управление основными параметрами сайта

+
+ + +
+
+

+ + Настройки сайта +

+
+ +
+
+ +
+

Основные настройки

+
+
+ + +
+ +
+ + +
+ +
+ + + <% if (settings.logo) { %> + Current logo + <% } %> +
+ +
+ + + <% if (settings.favicon) { %> + Current favicon + <% } %> +
+
+
+ + +
+

Контактная информация

+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

Социальные сети

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

Telegram Bot

+
+
+ + +

Получите токен у @BotFather

+
+ +
+ + +

ID чата для уведомлений

+
+ +
+ +
+
+
+
+ + +
+

SEO настройки

+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+ + +
+ +
+
+
+ + +
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/admin/telegram_20251022052503.ejs b/.history/views/admin/telegram_20251022052503.ejs new file mode 100644 index 0000000..a3226bc --- /dev/null +++ b/.history/views/admin/telegram_20251022052503.ejs @@ -0,0 +1,347 @@ + + + + + + Telegram Bot - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+
+
+

+ + Telegram Bot +

+

Настройка и управление уведомлениями через Telegram

+
+
+
+
+ + <%= botConfigured ? 'Подключен' : 'Не настроен' %> + +
+
+
+
+ + <% if (!botConfigured) { %> + +
+
+
+

Настройка Telegram бота

+
+

Для настройки Telegram бота выполните следующие шаги:

+
    +
  1. Создайте бота через @BotFather в Telegram
  2. +
  3. Получите токен бота от BotFather
  4. +
  5. Создайте группу или используйте личный чат для получения уведомлений
  6. +
  7. Получите Chat ID группы или чата
  8. +
  9. Добавьте в файл .env следующие переменные:
  10. +
+
+ TELEGRAM_BOT_TOKEN=your_bot_token_here
+ TELEGRAM_CHAT_ID=your_chat_id_here +
+

После добавления переменных перезапустите сервер.

+
+
+
+
+ <% } else { %> + +
+ +
+

+ + Проверка подключения +

+

+ Отправить тестовое сообщение для проверки работоспособности бота. +

+ + +
+ + +
+

+ + Отправить сообщение +

+
+
+ + +
+ +
+ +
+
+ + +
+

+ + Настройки уведомлений +

+ +
+ +
+

Типы уведомлений

+
+
+
+ + Новые обращения +
+
+
+
+
+ + Расчеты стоимости +
+
+
+
+
+ + Новые проекты +
+
+
+
+
+ + Новые услуги +
+
+
+
+
+ + +
+

Информация о боте

+
+
+ Статус: + Активен +
+
+ Токен: + •••••••••• +
+
+ Chat ID: + •••••••••• +
+
+ Последнее уведомление: + Недавно +
+
+
+
+
+ + +
+

+ + Недавние уведомления +

+ +
+ +

Уведомления будут отображаться здесь после отправки

+
+
+ <% } %> +
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/telegram_20251022052523.ejs b/.history/views/admin/telegram_20251022052523.ejs new file mode 100644 index 0000000..a3226bc --- /dev/null +++ b/.history/views/admin/telegram_20251022052523.ejs @@ -0,0 +1,347 @@ + + + + + + Telegram Bot - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+
+
+

+ + Telegram Bot +

+

Настройка и управление уведомлениями через Telegram

+
+
+
+
+ + <%= botConfigured ? 'Подключен' : 'Не настроен' %> + +
+
+
+
+ + <% if (!botConfigured) { %> + +
+
+
+

Настройка Telegram бота

+
+

Для настройки Telegram бота выполните следующие шаги:

+
    +
  1. Создайте бота через @BotFather в Telegram
  2. +
  3. Получите токен бота от BotFather
  4. +
  5. Создайте группу или используйте личный чат для получения уведомлений
  6. +
  7. Получите Chat ID группы или чата
  8. +
  9. Добавьте в файл .env следующие переменные:
  10. +
+
+ TELEGRAM_BOT_TOKEN=your_bot_token_here
+ TELEGRAM_CHAT_ID=your_chat_id_here +
+

После добавления переменных перезапустите сервер.

+
+
+
+
+ <% } else { %> + +
+ +
+

+ + Проверка подключения +

+

+ Отправить тестовое сообщение для проверки работоспособности бота. +

+ + +
+ + +
+

+ + Отправить сообщение +

+
+
+ + +
+ +
+ +
+
+ + +
+

+ + Настройки уведомлений +

+ +
+ +
+

Типы уведомлений

+
+
+
+ + Новые обращения +
+
+
+
+
+ + Расчеты стоимости +
+
+
+
+
+ + Новые проекты +
+
+
+
+
+ + Новые услуги +
+
+
+
+
+ + +
+

Информация о боте

+
+
+ Статус: + Активен +
+
+ Токен: + •••••••••• +
+
+ Chat ID: + •••••••••• +
+
+ Последнее уведомление: + Недавно +
+
+
+
+
+ + +
+

+ + Недавние уведомления +

+ +
+ +

Уведомления будут отображаться здесь после отправки

+
+
+ <% } %> +
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/telegram_20251022195740.ejs b/.history/views/admin/telegram_20251022195740.ejs new file mode 100644 index 0000000..e1b424c --- /dev/null +++ b/.history/views/admin/telegram_20251022195740.ejs @@ -0,0 +1,570 @@ + + + + + + Telegram Bot - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+
+
+

+ + Telegram Bot +

+

Настройка и управление уведомлениями через Telegram

+
+
+
+
+ + <%= botConfigured ? 'Подключен' : 'Не настроен' %> + +
+
+
+
+ + +
+

+ + Конфигурация бота +

+ +
+
+ +
+ +
+ + +
+

+ Получите токен от @BotFather +

+
+ + +
+ + +

+ Оставьте пустым, если будете выбирать чат из списка +

+
+
+ +
+ + +
+
+ + +
+ + <% if (botConfigured) { %> + +
+
+

+ + Информация о боте +

+ +
+ +
+ <% if (botInfo) { %> +
+
+
Имя бота
+
@<%= botInfo.username %>
+
+
+
Отображаемое имя
+
<%= botInfo.first_name %>
+
+
+
ID бота
+
<%= botInfo.id %>
+
+
+
Может читать сообщения
+
+ <%= botInfo.can_read_all_group_messages ? 'Да' : 'Нет' %> +
+
+
+ <% } else { %> +
+ +

Настройте токен бота для получения информации

+
+ <% } %> +
+
+ + +
+
+

+ + Доступные чаты +

+ +
+ +
+ <% if (availableChats && availableChats.length > 0) { %> +
+ <% availableChats.forEach(chat => { %> +
+
+
+ +
+
+
<%= chat.title %>
+
+ <%= chat.type %> • ID: <%= chat.id %> + <% if (chat.username) { %>• @<%= chat.username %><% } %> +
+
+
+ +
+ <% }); %> +
+ <% } else { %> +
+ +

Чаты не найдены

+

Отправьте боту сообщение или добавьте его в группу, затем нажмите "Найти чаты"

+
+ <% } %> +
+
+ <% } %> + + +
+

+ + Отправить сообщение +

+ +
+ +
+ + +
+ + +
+ +
+ <% if (availableChats && availableChats.length > 0) { %> + <% availableChats.forEach(chat => { %> + + <% }); %> + <% } else { %> +
+ + Сообщение будет отправлено в чат по умолчанию +
+ <% } %> +
+
+ + +
+

Настройки сообщения

+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+
+
+ + +
+ +
+ +
+

+ + Проверка подключения +

+

+ Отправить тестовое сообщение для проверки работоспособности бота. +

+ + +
+ + +
+

+ + Отправить сообщение +

+
+
+ + +
+ +
+ +
+
+ + +
+

+ + Настройки уведомлений +

+ +
+ +
+

Типы уведомлений

+
+
+
+ + Новые обращения +
+
+
+
+
+ + Расчеты стоимости +
+
+
+
+
+ + Новые проекты +
+
+
+
+
+ + Новые услуги +
+
+
+
+
+ + +
+

Информация о боте

+
+
+ Статус: + Активен +
+
+ Токен: + •••••••••• +
+
+ Chat ID: + •••••••••• +
+
+ Последнее уведомление: + Недавно +
+
+
+
+
+ + +
+

+ + Недавние уведомления +

+ +
+ +

Уведомления будут отображаться здесь после отправки

+
+
+ <% } %> +
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/telegram_20251022195839.ejs b/.history/views/admin/telegram_20251022195839.ejs new file mode 100644 index 0000000..5a53154 --- /dev/null +++ b/.history/views/admin/telegram_20251022195839.ejs @@ -0,0 +1,885 @@ + + + + + + Telegram Bot - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+
+
+

+ + Telegram Bot +

+

Настройка и управление уведомлениями через Telegram

+
+
+
+
+ + <%= botConfigured ? 'Подключен' : 'Не настроен' %> + +
+
+
+
+ + +
+

+ + Конфигурация бота +

+ +
+
+ +
+ +
+ + +
+

+ Получите токен от @BotFather +

+
+ + +
+ + +

+ Оставьте пустым, если будете выбирать чат из списка +

+
+
+ +
+ + +
+
+ + +
+ + <% if (botConfigured) { %> + +
+
+

+ + Информация о боте +

+ +
+ +
+ <% if (botInfo) { %> +
+
+
Имя бота
+
@<%= botInfo.username %>
+
+
+
Отображаемое имя
+
<%= botInfo.first_name %>
+
+
+
ID бота
+
<%= botInfo.id %>
+
+
+
Может читать сообщения
+
+ <%= botInfo.can_read_all_group_messages ? 'Да' : 'Нет' %> +
+
+
+ <% } else { %> +
+ +

Настройте токен бота для получения информации

+
+ <% } %> +
+
+ + +
+
+

+ + Доступные чаты +

+ +
+ +
+ <% if (availableChats && availableChats.length > 0) { %> +
+ <% availableChats.forEach(chat => { %> +
+
+
+ +
+
+
<%= chat.title %>
+
+ <%= chat.type %> • ID: <%= chat.id %> + <% if (chat.username) { %>• @<%= chat.username %><% } %> +
+
+
+ +
+ <% }); %> +
+ <% } else { %> +
+ +

Чаты не найдены

+

Отправьте боту сообщение или добавьте его в группу, затем нажмите "Найти чаты"

+
+ <% } %> +
+
+ <% } %> + + +
+

+ + Отправить сообщение +

+ +
+ +
+ + +
+ + +
+ +
+ <% if (availableChats && availableChats.length > 0) { %> + <% availableChats.forEach(chat => { %> + + <% }); %> + <% } else { %> +
+ + Сообщение будет отправлено в чат по умолчанию +
+ <% } %> +
+
+ + +
+

Настройки сообщения

+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+
+
+ + +
+ +
+ +
+

+ + Проверка подключения +

+

+ Отправить тестовое сообщение для проверки работоспособности бота. +

+ + +
+ + +
+

+ + Отправить сообщение +

+
+
+ + +
+ +
+ +
+
+ + +
+

+ + Настройки уведомлений +

+ +
+ +
+

Типы уведомлений

+
+
+
+ + Новые обращения +
+
+
+
+
+ + Расчеты стоимости +
+
+
+
+
+ + Новые проекты +
+
+
+
+
+ + Новые услуги +
+
+
+
+
+ + +
+

Информация о боте

+
+
+ Статус: + Активен +
+
+ Токен: + •••••••••• +
+
+ Chat ID: + •••••••••• +
+
+ Последнее уведомление: + Недавно +
+
+
+
+
+ + +
+

+ + Недавние уведомления +

+ +
+ +

Уведомления будут отображаться здесь после отправки

+
+
+ <% } %> +
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/admin/telegram_20251022195905.ejs b/.history/views/admin/telegram_20251022195905.ejs new file mode 100644 index 0000000..5a53154 --- /dev/null +++ b/.history/views/admin/telegram_20251022195905.ejs @@ -0,0 +1,885 @@ + + + + + + Telegram Bot - SmartSolTech Admin + + + + + + + + + + + + + +
+
+
+
+

+ + SmartSolTech Admin +

+
+
+ + Добро пожаловать, <%= user ? user.name : 'Admin' %>! + + + + Посмотреть сайт + +
+ +
+
+
+
+
+ +
+ + + + +
+
+ +
+
+
+

+ + Telegram Bot +

+

Настройка и управление уведомлениями через Telegram

+
+
+
+
+ + <%= botConfigured ? 'Подключен' : 'Не настроен' %> + +
+
+
+
+ + +
+

+ + Конфигурация бота +

+ +
+
+ +
+ +
+ + +
+

+ Получите токен от @BotFather +

+
+ + +
+ + +

+ Оставьте пустым, если будете выбирать чат из списка +

+
+
+ +
+ + +
+
+ + +
+ + <% if (botConfigured) { %> + +
+
+

+ + Информация о боте +

+ +
+ +
+ <% if (botInfo) { %> +
+
+
Имя бота
+
@<%= botInfo.username %>
+
+
+
Отображаемое имя
+
<%= botInfo.first_name %>
+
+
+
ID бота
+
<%= botInfo.id %>
+
+
+
Может читать сообщения
+
+ <%= botInfo.can_read_all_group_messages ? 'Да' : 'Нет' %> +
+
+
+ <% } else { %> +
+ +

Настройте токен бота для получения информации

+
+ <% } %> +
+
+ + +
+
+

+ + Доступные чаты +

+ +
+ +
+ <% if (availableChats && availableChats.length > 0) { %> +
+ <% availableChats.forEach(chat => { %> +
+
+
+ +
+
+
<%= chat.title %>
+
+ <%= chat.type %> • ID: <%= chat.id %> + <% if (chat.username) { %>• @<%= chat.username %><% } %> +
+
+
+ +
+ <% }); %> +
+ <% } else { %> +
+ +

Чаты не найдены

+

Отправьте боту сообщение или добавьте его в группу, затем нажмите "Найти чаты"

+
+ <% } %> +
+
+ <% } %> + + +
+

+ + Отправить сообщение +

+ +
+ +
+ + +
+ + +
+ +
+ <% if (availableChats && availableChats.length > 0) { %> + <% availableChats.forEach(chat => { %> + + <% }); %> + <% } else { %> +
+ + Сообщение будет отправлено в чат по умолчанию +
+ <% } %> +
+
+ + +
+

Настройки сообщения

+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+
+
+ + +
+ +
+ +
+

+ + Проверка подключения +

+

+ Отправить тестовое сообщение для проверки работоспособности бота. +

+ + +
+ + +
+

+ + Отправить сообщение +

+
+
+ + +
+ +
+ +
+
+ + +
+

+ + Настройки уведомлений +

+ +
+ +
+

Типы уведомлений

+
+
+
+ + Новые обращения +
+
+
+
+
+ + Расчеты стоимости +
+
+
+
+
+ + Новые проекты +
+
+
+
+
+ + Новые услуги +
+
+
+
+
+ + +
+

Информация о боте

+
+
+ Статус: + Активен +
+
+ Токен: + •••••••••• +
+
+ Chat ID: + •••••••••• +
+
+ Последнее уведомление: + Недавно +
+
+
+
+
+ + +
+

+ + Недавние уведомления +

+ +
+ +

Уведомления будут отображаться здесь после отправки

+
+
+ <% } %> +
+
+
+ + + + + + + \ No newline at end of file diff --git a/.history/views/calculator_20251021212440.ejs b/.history/views/calculator_20251021212440.ejs new file mode 100644 index 0000000..1b57f80 --- /dev/null +++ b/.history/views/calculator_20251021212440.ejs @@ -0,0 +1,302 @@ + + + + + + <%- __('calculator.meta.title') %> - SmartSolTech + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+

+ <%- __('calculator.title') %> +

+

+ <%- __('calculator.subtitle') %> +

+
+
+ + +
+
+
+
+ +
+
+
+ +
+ +
+
+

+ <%- __('calculator.step1.title') %> +

+

+ <%- __('calculator.step1.subtitle') %> +

+
+ +
+ +
+
+
+ +
+
+

+ <%- __('services.web.title') %> +

+
<%- __('services.web.price') %>
+
+
+

+ <%- __('services.web.description') %> +

+
+ + +
+
+
+ +
+
+

+ <%- __('services.mobile.title') %> +

+
<%- __('services.mobile.price') %>
+
+
+

+ <%- __('services.mobile.description') %> +

+
+ + +
+
+
+ +
+
+

+ <%- __('services.design.title') %> +

+
<%- __('services.design.price') %>
+
+
+

+ <%- __('services.design.description') %> +

+
+ + +
+
+
+ +
+
+

+ <%- __('services.marketing.title') %> +

+
<%- __('services.marketing.price') %>
+
+
+

+ <%- __('services.marketing.description') %> +

+
+
+ +
+ +
+
+ + + + + + +
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/calculator_20251021212532.ejs b/.history/views/calculator_20251021212532.ejs new file mode 100644 index 0000000..1b57f80 --- /dev/null +++ b/.history/views/calculator_20251021212532.ejs @@ -0,0 +1,302 @@ + + + + + + <%- __('calculator.meta.title') %> - SmartSolTech + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+

+ <%- __('calculator.title') %> +

+

+ <%- __('calculator.subtitle') %> +

+
+
+ + +
+
+
+
+ +
+
+
+ +
+ +
+
+

+ <%- __('calculator.step1.title') %> +

+

+ <%- __('calculator.step1.subtitle') %> +

+
+ +
+ +
+
+
+ +
+
+

+ <%- __('services.web.title') %> +

+
<%- __('services.web.price') %>
+
+
+

+ <%- __('services.web.description') %> +

+
+ + +
+
+
+ +
+
+

+ <%- __('services.mobile.title') %> +

+
<%- __('services.mobile.price') %>
+
+
+

+ <%- __('services.mobile.description') %> +

+
+ + +
+
+
+ +
+
+

+ <%- __('services.design.title') %> +

+
<%- __('services.design.price') %>
+
+
+

+ <%- __('services.design.description') %> +

+
+ + +
+
+
+ +
+
+

+ <%- __('services.marketing.title') %> +

+
<%- __('services.marketing.price') %>
+
+
+

+ <%- __('services.marketing.description') %> +

+
+
+ +
+ +
+
+ + + + + + +
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/contact_20251021212428.ejs b/.history/views/contact_20251021212428.ejs new file mode 100644 index 0000000..cd4dbb3 --- /dev/null +++ b/.history/views/contact_20251021212428.ejs @@ -0,0 +1,211 @@ + + + + + + <%- __('contact.meta.title') %> - SmartSolTech + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+

+ <%- __('contact.hero.title') %> +

+

+ <%- __('contact.hero.subtitle') %> +

+
+
+ + +
+
+
+ +
+

+ <%- __('contact.form.title') %> +

+
+
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+ + +
+

+ <%- __('contact.info.title') %> +

+ +
+ +
+
+ +
+
+

+ <%- __('contact.phone.title') %> +

+

+ <%- __('contact.phone.number') %> +

+

+ <%- __('contact.phone.hours') %> +

+
+
+ + +
+
+ +
+
+

+ <%- __('contact.email.title') %> +

+

+ <%- __('contact.email.address') %> +

+

+ <%- __('contact.email.response') %> +

+
+
+ + +
+
+ +
+
+

+ <%- __('contact.telegram.title') %> +

+

+ @smartsoltech +

+

+ <%- __('contact.telegram.subtitle') %> +

+
+
+ + +
+
+ +
+
+

+ <%- __('contact.address.title') %> +

+

+ <%- __('contact.address.line1') %>
+ <%- __('contact.address.line2') %> +

+
+
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + \ No newline at end of file diff --git a/.history/views/contact_20251021212532.ejs b/.history/views/contact_20251021212532.ejs new file mode 100644 index 0000000..cd4dbb3 --- /dev/null +++ b/.history/views/contact_20251021212532.ejs @@ -0,0 +1,211 @@ + + + + + + <%- __('contact.meta.title') %> - SmartSolTech + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+

+ <%- __('contact.hero.title') %> +

+

+ <%- __('contact.hero.subtitle') %> +

+
+
+ + +
+
+
+ +
+

+ <%- __('contact.form.title') %> +

+
+
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+
+ + +
+

+ <%- __('contact.info.title') %> +

+ +
+ +
+
+ +
+
+

+ <%- __('contact.phone.title') %> +

+

+ <%- __('contact.phone.number') %> +

+

+ <%- __('contact.phone.hours') %> +

+
+
+ + +
+
+ +
+
+

+ <%- __('contact.email.title') %> +

+

+ <%- __('contact.email.address') %> +

+

+ <%- __('contact.email.response') %> +

+
+
+ + +
+
+ +
+
+

+ <%- __('contact.telegram.title') %> +

+

+ @smartsoltech +

+

+ <%- __('contact.telegram.subtitle') %> +

+
+
+ + +
+
+ +
+
+

+ <%- __('contact.address.title') %> +

+

+ <%- __('contact.address.line1') %>
+ <%- __('contact.address.line2') %> +

+
+
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + \ No newline at end of file diff --git a/.history/views/error_20251020035536.ejs b/.history/views/error_20251020035536.ejs new file mode 100644 index 0000000..6c427dc --- /dev/null +++ b/.history/views/error_20251020035536.ejs @@ -0,0 +1,102 @@ + + + + + + <%= title || '오류 - SmartSolTech' %> + + + + + + + + + + + + + <%- include('partials/navigation', { settings: settings || {}, currentPage: 'error' }) %> + + +
+
+
+ + +
+ +
+ + +

+ <%= title || '오류가 발생했습니다' %> +

+ + +

+ <%= message || '요청을 처리하는 중 문제가 발생했습니다.' %> +

+ + +
+ + + 홈으로 돌아가기 + + +
+ + +
+

도움이 필요하신가요?

+

+ 문제가 지속되면 언제든지 저희에게 연락해 주세요. +

+
+ + + 문의하기 + + <% if (settings && settings.contact && settings.contact.email) { %> + + + <%= settings.contact.email %> + + <% } else { %> + + + info@smartsoltech.kr + + <% } %> + <% if (settings && settings.contact && settings.contact.phone) { %> + + + <%= settings.contact.phone %> + + <% } else { %> + + + +82-10-1234-5678 + + <% } %> +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/error_20251020035856.ejs b/.history/views/error_20251020035856.ejs new file mode 100644 index 0000000..6c427dc --- /dev/null +++ b/.history/views/error_20251020035856.ejs @@ -0,0 +1,102 @@ + + + + + + <%= title || '오류 - SmartSolTech' %> + + + + + + + + + + + + + <%- include('partials/navigation', { settings: settings || {}, currentPage: 'error' }) %> + + +
+
+
+ + +
+ +
+ + +

+ <%= title || '오류가 발생했습니다' %> +

+ + +

+ <%= message || '요청을 처리하는 중 문제가 발생했습니다.' %> +

+ + +
+ + + 홈으로 돌아가기 + + +
+ + +
+

도움이 필요하신가요?

+

+ 문제가 지속되면 언제든지 저희에게 연락해 주세요. +

+
+ + + 문의하기 + + <% if (settings && settings.contact && settings.contact.email) { %> + + + <%= settings.contact.email %> + + <% } else { %> + + + info@smartsoltech.kr + + <% } %> + <% if (settings && settings.contact && settings.contact.phone) { %> + + + <%= settings.contact.phone %> + + <% } else { %> + + + +82-10-1234-5678 + + <% } %> +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/error_20251020041605.ejs b/.history/views/error_20251020041605.ejs new file mode 100644 index 0000000..e5e44f2 --- /dev/null +++ b/.history/views/error_20251020041605.ejs @@ -0,0 +1,104 @@ + + + + + + <%= title || '오류 - SmartSolTech' %> + + + + + + + + + + + + + + + <%- include('partials/navigation', { settings: settings || {}, currentPage: 'error' }) %> + + +
+
+
+ + +
+ +
+ + +

+ <%= title || '오류가 발생했습니다' %> +

+ + +

+ <%= message || '요청을 처리하는 중 문제가 발생했습니다.' %> +

+ + +
+ + + 홈으로 돌아가기 + + +
+ + +
+

도움이 필요하신가요?

+

+ 문제가 지속되면 언제든지 저희에게 연락해 주세요. +

+
+ + + 문의하기 + + <% if (settings && settings.contact && settings.contact.email) { %> + + + <%= settings.contact.email %> + + <% } else { %> + + + info@smartsoltech.kr + + <% } %> + <% if (settings && settings.contact && settings.contact.phone) { %> + + + <%= settings.contact.phone %> + + <% } else { %> + + + +82-10-1234-5678 + + <% } %> +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/error_20251020041613.ejs b/.history/views/error_20251020041613.ejs new file mode 100644 index 0000000..17e1d74 --- /dev/null +++ b/.history/views/error_20251020041613.ejs @@ -0,0 +1,104 @@ + + + + + + <%= title || '오류 - SmartSolTech' %> + + + + + + + + + + + + + + + <%- include('partials/navigation', { settings: settings || {}, currentPage: 'error' }) %> + + +
+
+
+ + +
+ +
+ + +

+ <%= title || '오류가 발생했습니다' %> +

+ + +

+ <%= message || '요청을 처리하는 중 문제가 발생했습니다.' %> +

+ + +
+ + + 홈으로 돌아가기 + + +
+ + +
+

도움이 필요하신가요?

+

+ 문제가 지속되면 언제든지 저희에게 연락해 주세요. +

+
+ + + 문의하기 + + <% if (settings && settings.contact && settings.contact.email) { %> + + + <%= settings.contact.email %> + + <% } else { %> + + + info@smartsoltech.kr + + <% } %> + <% if (settings && settings.contact && settings.contact.phone) { %> + + + <%= settings.contact.phone %> + + <% } else { %> + + + +82-10-1234-5678 + + <% } %> +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/error_20251020041618.ejs b/.history/views/error_20251020041618.ejs new file mode 100644 index 0000000..f7da80d --- /dev/null +++ b/.history/views/error_20251020041618.ejs @@ -0,0 +1,104 @@ + + + + + + <%= title || '오류 - SmartSolTech' %> + + + + + + + + + + + + + + + <%- include('partials/navigation', { settings: settings || {}, currentPage: 'error' }) %> + + +
+
+
+ + +
+ +
+ + +

+ <%= title || '오류가 발생했습니다' %> +

+ + +

+ <%= message || '요청을 처리하는 중 문제가 발생했습니다.' %> +

+ + +
+ + + 홈으로 돌아가기 + + +
+ + +
+

도움이 필요하신가요?

+

+ 문제가 지속되면 언제든지 저희에게 연락해 주세요. +

+
+ + + 문의하기 + + <% if (settings && settings.contact && settings.contact.email) { %> + + + <%= settings.contact.email %> + + <% } else { %> + + + info@smartsoltech.kr + + <% } %> + <% if (settings && settings.contact && settings.contact.phone) { %> + + + <%= settings.contact.phone %> + + <% } else { %> + + + +82-10-1234-5678 + + <% } %> +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/error_20251020041711.ejs b/.history/views/error_20251020041711.ejs new file mode 100644 index 0000000..f7da80d --- /dev/null +++ b/.history/views/error_20251020041711.ejs @@ -0,0 +1,104 @@ + + + + + + <%= title || '오류 - SmartSolTech' %> + + + + + + + + + + + + + + + <%- include('partials/navigation', { settings: settings || {}, currentPage: 'error' }) %> + + +
+
+
+ + +
+ +
+ + +

+ <%= title || '오류가 발생했습니다' %> +

+ + +

+ <%= message || '요청을 처리하는 중 문제가 발생했습니다.' %> +

+ + +
+ + + 홈으로 돌아가기 + + +
+ + +
+

도움이 필요하신가요?

+

+ 문제가 지속되면 언제든지 저희에게 연락해 주세요. +

+
+ + + 문의하기 + + <% if (settings && settings.contact && settings.contact.email) { %> + + + <%= settings.contact.email %> + + <% } else { %> + + + info@smartsoltech.kr + + <% } %> + <% if (settings && settings.contact && settings.contact.phone) { %> + + + <%= settings.contact.phone %> + + <% } else { %> + + + +82-10-1234-5678 + + <% } %> +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/error_20251021184031.ejs b/.history/views/error_20251021184031.ejs new file mode 100644 index 0000000..23094bd --- /dev/null +++ b/.history/views/error_20251021184031.ejs @@ -0,0 +1,104 @@ + + + + + + <%= title || __('errors.title') %> + + + + + + + + + + + + + + + <%- include('partials/navigation', { settings: settings || {}, currentPage: 'error' }) %> + + +
+
+
+ + +
+ +
+ + +

+ <%= title || '오류가 발생했습니다' %> +

+ + +

+ <%= message || '요청을 처리하는 중 문제가 발생했습니다.' %> +

+ + +
+ + + 홈으로 돌아가기 + + +
+ + +
+

도움이 필요하신가요?

+

+ 문제가 지속되면 언제든지 저희에게 연락해 주세요. +

+
+ + + 문의하기 + + <% if (settings && settings.contact && settings.contact.email) { %> + + + <%= settings.contact.email %> + + <% } else { %> + + + info@smartsoltech.kr + + <% } %> + <% if (settings && settings.contact && settings.contact.phone) { %> + + + <%= settings.contact.phone %> + + <% } else { %> + + + +82-10-1234-5678 + + <% } %> +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/error_20251021184045.ejs b/.history/views/error_20251021184045.ejs new file mode 100644 index 0000000..63c7d3f --- /dev/null +++ b/.history/views/error_20251021184045.ejs @@ -0,0 +1,104 @@ + + + + + + <%= title || __('errors.title') %> + + + + + + + + + + + + + + + <%- include('partials/navigation', { settings: settings || {}, currentPage: 'error' }) %> + + +
+
+
+ + +
+ +
+ + +

+ <%= title || __('errors.default_title') %> +

+ + +

+ <%= message || __('errors.default_message') %> +

+ + +
+ + + 홈으로 돌아가기 + + +
+ + +
+

도움이 필요하신가요?

+

+ 문제가 지속되면 언제든지 저희에게 연락해 주세요. +

+
+ + + 문의하기 + + <% if (settings && settings.contact && settings.contact.email) { %> + + + <%= settings.contact.email %> + + <% } else { %> + + + info@smartsoltech.kr + + <% } %> + <% if (settings && settings.contact && settings.contact.phone) { %> + + + <%= settings.contact.phone %> + + <% } else { %> + + + +82-10-1234-5678 + + <% } %> +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/error_20251021184055.ejs b/.history/views/error_20251021184055.ejs new file mode 100644 index 0000000..97b9db4 --- /dev/null +++ b/.history/views/error_20251021184055.ejs @@ -0,0 +1,104 @@ + + + + + + <%= title || __('errors.title') %> + + + + + + + + + + + + + + + <%- include('partials/navigation', { settings: settings || {}, currentPage: 'error' }) %> + + +
+
+
+ + +
+ +
+ + +

+ <%= title || __('errors.default_title') %> +

+ + +

+ <%= message || __('errors.default_message') %> +

+ + +
+ + + <%= __('errors.back_home') %> + + +
+ + +
+

도움이 필요하신가요?

+

+ 문제가 지속되면 언제든지 저희에게 연락해 주세요. +

+
+ + + 문의하기 + + <% if (settings && settings.contact && settings.contact.email) { %> + + + <%= settings.contact.email %> + + <% } else { %> + + + info@smartsoltech.kr + + <% } %> + <% if (settings && settings.contact && settings.contact.phone) { %> + + + <%= settings.contact.phone %> + + <% } else { %> + + + +82-10-1234-5678 + + <% } %> +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/error_20251021184106.ejs b/.history/views/error_20251021184106.ejs new file mode 100644 index 0000000..772066c --- /dev/null +++ b/.history/views/error_20251021184106.ejs @@ -0,0 +1,104 @@ + + + + + + <%= title || __('errors.title') %> + + + + + + + + + + + + + + + <%- include('partials/navigation', { settings: settings || {}, currentPage: 'error' }) %> + + +
+
+
+ + +
+ +
+ + +

+ <%= title || __('errors.default_title') %> +

+ + +

+ <%= message || __('errors.default_message') %> +

+ + +
+ + + <%= __('errors.back_home') %> + + +
+ + +
+

<%= __('errors.need_help') %>

+

+ <%= __('errors.help_message') %> +

+
+ + + <%= __('errors.contact_support') %> + + <% if (settings && settings.contact && settings.contact.email) { %> + + + <%= settings.contact.email %> + + <% } else { %> + + + info@smartsoltech.kr + + <% } %> + <% if (settings && settings.contact && settings.contact.phone) { %> + + + <%= settings.contact.phone %> + + <% } else { %> + + + +82-10-1234-5678 + + <% } %> +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/error_20251021184308.ejs b/.history/views/error_20251021184308.ejs new file mode 100644 index 0000000..772066c --- /dev/null +++ b/.history/views/error_20251021184308.ejs @@ -0,0 +1,104 @@ + + + + + + <%= title || __('errors.title') %> + + + + + + + + + + + + + + + <%- include('partials/navigation', { settings: settings || {}, currentPage: 'error' }) %> + + +
+
+
+ + +
+ +
+ + +

+ <%= title || __('errors.default_title') %> +

+ + +

+ <%= message || __('errors.default_message') %> +

+ + +
+ + + <%= __('errors.back_home') %> + + +
+ + +
+

<%= __('errors.need_help') %>

+

+ <%= __('errors.help_message') %> +

+
+ + + <%= __('errors.contact_support') %> + + <% if (settings && settings.contact && settings.contact.email) { %> + + + <%= settings.contact.email %> + + <% } else { %> + + + info@smartsoltech.kr + + <% } %> + <% if (settings && settings.contact && settings.contact.phone) { %> + + + <%= settings.contact.phone %> + + <% } else { %> + + + +82-10-1234-5678 + + <% } %> +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/.history/views/index_20251020045441.ejs b/.history/views/index_20251020045441.ejs new file mode 100644 index 0000000..1852428 --- /dev/null +++ b/.history/views/index_20251020045441.ejs @@ -0,0 +1,371 @@ +<% layout('layout') -%> +<%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251020045457.ejs b/.history/views/index_20251020045457.ejs new file mode 100644 index 0000000..1852428 --- /dev/null +++ b/.history/views/index_20251020045457.ejs @@ -0,0 +1,371 @@ +<% layout('layout') -%> +<%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251020045504.ejs b/.history/views/index_20251020045504.ejs new file mode 100644 index 0000000..18511de --- /dev/null +++ b/.history/views/index_20251020045504.ejs @@ -0,0 +1,382 @@ +<% layout('layout') -%> +<%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251020045528.ejs b/.history/views/index_20251020045528.ejs new file mode 100644 index 0000000..18511de --- /dev/null +++ b/.history/views/index_20251020045528.ejs @@ -0,0 +1,382 @@ +<% layout('layout') -%> +<%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251020045539.ejs b/.history/views/index_20251020045539.ejs new file mode 100644 index 0000000..2e4a8ce --- /dev/null +++ b/.history/views/index_20251020045539.ejs @@ -0,0 +1,381 @@ +<%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251020045545.ejs b/.history/views/index_20251020045545.ejs new file mode 100644 index 0000000..2e4a8ce --- /dev/null +++ b/.history/views/index_20251020045545.ejs @@ -0,0 +1,381 @@ +<%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251020045552.ejs b/.history/views/index_20251020045552.ejs new file mode 100644 index 0000000..16f57b7 --- /dev/null +++ b/.history/views/index_20251020045552.ejs @@ -0,0 +1,435 @@ + + + + + + <%- __(title || 'meta.title') %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251020045603.ejs b/.history/views/index_20251020045603.ejs new file mode 100644 index 0000000..848454a --- /dev/null +++ b/.history/views/index_20251020045603.ejs @@ -0,0 +1,457 @@ + + + + + + <%- __(title || 'meta.title') %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + <%- include('partials/footer') %> + + + + + + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251020045707.ejs b/.history/views/index_20251020045707.ejs new file mode 100644 index 0000000..848454a --- /dev/null +++ b/.history/views/index_20251020045707.ejs @@ -0,0 +1,457 @@ + + + + + + <%- __(title || 'meta.title') %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + <%- include('partials/footer') %> + + + + + + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251020045942.ejs b/.history/views/index_20251020045942.ejs new file mode 100644 index 0000000..94a025c --- /dev/null +++ b/.history/views/index_20251020045942.ejs @@ -0,0 +1,457 @@ + + + + + + <%- title || 'SmartSolTech - Innovative Technology Solutions' %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + <%- include('partials/footer') %> + + + + + + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251020045946.ejs b/.history/views/index_20251020045946.ejs new file mode 100644 index 0000000..94a025c --- /dev/null +++ b/.history/views/index_20251020045946.ejs @@ -0,0 +1,457 @@ + + + + + + <%- title || 'SmartSolTech - Innovative Technology Solutions' %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + <%- include('partials/footer') %> + + + + + + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251020045954.ejs b/.history/views/index_20251020045954.ejs new file mode 100644 index 0000000..66d917d --- /dev/null +++ b/.history/views/index_20251020045954.ejs @@ -0,0 +1,457 @@ + + + + + + <%- title || 'SmartSolTech - Innovative Technology Solutions' %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + <%- include('partials/footer') %> + + + + + + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251020050006.ejs b/.history/views/index_20251020050006.ejs new file mode 100644 index 0000000..66d917d --- /dev/null +++ b/.history/views/index_20251020050006.ejs @@ -0,0 +1,457 @@ + + + + + + <%- title || 'SmartSolTech - Innovative Technology Solutions' %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + <%- include('partials/footer') %> + + + + + + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251021172735.ejs b/.history/views/index_20251021172735.ejs new file mode 100644 index 0000000..9d9e6dd --- /dev/null +++ b/.history/views/index_20251021172735.ejs @@ -0,0 +1,457 @@ + + + + + + <%- title || 'SmartSolTech - Innovative Technology Solutions' %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + <%- include('partials/footer') %> + + + + + + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251021172757.ejs b/.history/views/index_20251021172757.ejs new file mode 100644 index 0000000..3fdc222 --- /dev/null +++ b/.history/views/index_20251021172757.ejs @@ -0,0 +1,441 @@ + + + + + + <%- title || 'SmartSolTech - Innovative Technology Solutions' %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + 0% { transform: translate(0px, 0px) scale(1); } + 33% { transform: translate(30px, -50px) scale(1.1); } + 66% { transform: translate(-20px, 20px) scale(0.9); } + 100% { transform: translate(0px, 0px) scale(1); } + } + .animate-blob { + animation: blob 7s infinite; + } + .animation-delay-2000 { + animation-delay: 2s; + } + .animation-delay-4000 { + animation-delay: 4s; + } + `; + document.head.appendChild(style); + + // Contact form handler + document.getElementById('quick-contact-form').addEventListener('submit', function(e) { + e.preventDefault(); + const formData = new FormData(this); + + fetch('/contact', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + alert('<%- __("contact.form.success") %>'); + this.reset(); + } else { + alert('<%- __("contact.form.error") %>'); + } + }) + .catch(error => { + console.error('Error:', error); + alert('<%- __("contact.form.error") %>'); + }); + }); + + // Theme initialization + document.addEventListener('DOMContentLoaded', function() { + const theme = localStorage.getItem('theme') || 'light'; + document.documentElement.className = theme === 'dark' ? 'dark' : ''; + }); + + <%- include('partials/footer') %> + + + + + + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251021172802.ejs b/.history/views/index_20251021172802.ejs new file mode 100644 index 0000000..3fdc222 --- /dev/null +++ b/.history/views/index_20251021172802.ejs @@ -0,0 +1,441 @@ + + + + + + <%- title || 'SmartSolTech - Innovative Technology Solutions' %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + 0% { transform: translate(0px, 0px) scale(1); } + 33% { transform: translate(30px, -50px) scale(1.1); } + 66% { transform: translate(-20px, 20px) scale(0.9); } + 100% { transform: translate(0px, 0px) scale(1); } + } + .animate-blob { + animation: blob 7s infinite; + } + .animation-delay-2000 { + animation-delay: 2s; + } + .animation-delay-4000 { + animation-delay: 4s; + } + `; + document.head.appendChild(style); + + // Contact form handler + document.getElementById('quick-contact-form').addEventListener('submit', function(e) { + e.preventDefault(); + const formData = new FormData(this); + + fetch('/contact', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + alert('<%- __("contact.form.success") %>'); + this.reset(); + } else { + alert('<%- __("contact.form.error") %>'); + } + }) + .catch(error => { + console.error('Error:', error); + alert('<%- __("contact.form.error") %>'); + }); + }); + + // Theme initialization + document.addEventListener('DOMContentLoaded', function() { + const theme = localStorage.getItem('theme') || 'light'; + document.documentElement.className = theme === 'dark' ? 'dark' : ''; + }); + + <%- include('partials/footer') %> + + + + + + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251021172824.ejs b/.history/views/index_20251021172824.ejs new file mode 100644 index 0000000..f6fcb0c --- /dev/null +++ b/.history/views/index_20251021172824.ejs @@ -0,0 +1,394 @@ + + + + + + <%- title || 'SmartSolTech - Innovative Technology Solutions' %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + + + + + + + + \ No newline at end of file diff --git a/.history/views/index_20251021172830.ejs b/.history/views/index_20251021172830.ejs new file mode 100644 index 0000000..f6fcb0c --- /dev/null +++ b/.history/views/index_20251021172830.ejs @@ -0,0 +1,394 @@ + + + + + + <%- title || 'SmartSolTech - Innovative Technology Solutions' %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+
+ + +
+
+
+
+
+ +
+
+

+ <%- __('hero.title.smart') %> + <%- __('hero.title.solutions') %> +

+

+ <%- __('hero.subtitle') %> +

+ +
+ + +
+ + + +
+
+
+ + +
+
+
+

+ <%- __('services.title.our') %> <%- __('services.title.services') %> +

+

+ <%- __('services.subtitle') %> +

+
+ +
+ +
+
+ +
+

<%- __('services.web.title') %>

+

<%- __('services.web.description') %>

+
<%- __('services.web.price') %>
+
+ + +
+
+ +
+

<%- __('services.mobile.title') %>

+

<%- __('services.mobile.description') %>

+
<%- __('services.mobile.price') %>
+
+ + +
+
+ +
+

<%- __('services.design.title') %>

+

<%- __('services.design.description') %>

+
<%- __('services.design.price') %>
+
+ + +
+
+ +
+

<%- __('services.marketing.title') %>

+

<%- __('services.marketing.description') %>

+
<%- __('services.marketing.price') %>
+
+
+ + +
+
+ + +
+
+
+

+ <%- __('portfolio.title.recent') %> <%- __('portfolio.title.projects') %> +

+

+ <%- __('portfolio.subtitle') %> +

+
+ +
+ <% if (featuredPortfolio && featuredPortfolio.length > 0) { %> + <% featuredPortfolio.forEach((project, index) => { %> +
+
+ <% if (project.images && project.images.length > 0) { %> + <%= project.images.find(img => img.isPrimary)?.alt || project.title %> + <% } else { %> +
+ <%= project.title.charAt(0) %> +
+ <% } %> +
+
+
+ <% if (project.technologies && project.technologies.length > 0) { %> + <% project.technologies.slice(0, 3).forEach(tech => { %> + <%= tech %> + <% }) %> + <% } %> +
+
+
+
+
+ <%= project.category %> + + <%= project.viewCount || 0 %> + +
+

<%= project.title %>

+

<%= project.shortDescription || project.description %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% }) %> + <% } else { %> + +
+
+
+ E +
+
+
+
+ React + Node.js +
+
+
+
+
+ <%- __('portfolio.default.ecommerce') %> + + 1,234 + +
+

<%- __('portfolio.default.title') %>

+

<%- __('portfolio.default.description') %>

+ + <%- __('common.view_details') %> + + + + +
+
+ <% } %> +
+ + +
+
+ + +
+
+
+

+ <%- __('calculator.cta.title') %> +

+

+ <%- __('calculator.cta.subtitle') %> +

+ + + <%- __('calculator.cta.button') %> + +
+
+
+ + +
+
+
+
+

+ <%- __('contact.cta.ready') %> <%- __('contact.cta.start') %><%- __('contact.cta.question') %> +

+

+ <%- __('contact.cta.subtitle') %> +

+
+
+
+ +
+
+
<%- __('contact.phone.title') %>
+
<%- __('contact.phone.number') %>
+
+
+
+
+ +
+
+
<%- __('contact.email.title') %>
+
<%- __('contact.email.address') %>
+
+
+
+
+ +
+
+
<%- __('contact.telegram.title') %>
+
<%- __('contact.telegram.subtitle') %>
+
+
+
+
+
+
+

<%- __('contact.form.title') %>

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+
+
+
+ + <%- include('partials/footer') %> + + + + + + + + + + + + + \ No newline at end of file diff --git a/.history/views/layout_20251020041345.ejs b/.history/views/layout_20251020041345.ejs new file mode 100644 index 0000000..0f39444 --- /dev/null +++ b/.history/views/layout_20251020041345.ejs @@ -0,0 +1,90 @@ + + + + + + <%= title %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+ <%- body %> +
+ + + <%- include('partials/footer') %> + + + + + + + + + + <% if (settings && settings.seo && settings.seo.googleAnalytics) { %> + + + + <% } %> + + \ No newline at end of file diff --git a/.history/views/layout_20251020041353.ejs b/.history/views/layout_20251020041353.ejs new file mode 100644 index 0000000..64bab5e --- /dev/null +++ b/.history/views/layout_20251020041353.ejs @@ -0,0 +1,90 @@ + + + + + + <%= title %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+ <%- body %> +
+ + + <%- include('partials/footer') %> + + + + + + + + + + <% if (settings && settings.seo && settings.seo.googleAnalytics) { %> + + + + <% } %> + + \ No newline at end of file diff --git a/.history/views/layout_20251020041359.ejs b/.history/views/layout_20251020041359.ejs new file mode 100644 index 0000000..0a84c58 --- /dev/null +++ b/.history/views/layout_20251020041359.ejs @@ -0,0 +1,90 @@ + + + + + + <%= title %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+ <%- body %> +
+ + + <%- include('partials/footer') %> + + + + + + + + + + <% if (settings && settings.seo && settings.seo.googleAnalytics) { %> + + + + <% } %> + + \ No newline at end of file diff --git a/.history/views/layout_20251020041711.ejs b/.history/views/layout_20251020041711.ejs new file mode 100644 index 0000000..0a84c58 --- /dev/null +++ b/.history/views/layout_20251020041711.ejs @@ -0,0 +1,90 @@ + + + + + + <%= title %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+ <%- body %> +
+ + + <%- include('partials/footer') %> + + + + + + + + + + <% if (settings && settings.seo && settings.seo.googleAnalytics) { %> + + + + <% } %> + + \ No newline at end of file diff --git a/.history/views/layout_20251020045220.ejs b/.history/views/layout_20251020045220.ejs new file mode 100644 index 0000000..66704be --- /dev/null +++ b/.history/views/layout_20251020045220.ejs @@ -0,0 +1,112 @@ + + + + + + <%= title %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+ <%- body %> +
+ + + <%- include('partials/footer') %> + + + + + + + + + + <% if (settings && settings.seo && settings.seo.googleAnalytics) { %> + + + + <% } %> + + \ No newline at end of file diff --git a/.history/views/layout_20251020045222.ejs b/.history/views/layout_20251020045222.ejs new file mode 100644 index 0000000..66704be --- /dev/null +++ b/.history/views/layout_20251020045222.ejs @@ -0,0 +1,112 @@ + + + + + + <%= title %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+ <%- body %> +
+ + + <%- include('partials/footer') %> + + + + + + + + + + <% if (settings && settings.seo && settings.seo.googleAnalytics) { %> + + + + <% } %> + + \ No newline at end of file diff --git a/.history/views/layout_20251020045413.ejs b/.history/views/layout_20251020045413.ejs new file mode 100644 index 0000000..93d1e0b --- /dev/null +++ b/.history/views/layout_20251020045413.ejs @@ -0,0 +1,113 @@ + + + + + + <%= title %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+ <%- body %> +
+ + + <%- include('partials/footer') %> + + + + + + + + + + <% if (settings && settings.seo && settings.seo.googleAnalytics) { %> + + + + <% } %> + + \ No newline at end of file diff --git a/.history/views/layout_20251020045457.ejs b/.history/views/layout_20251020045457.ejs new file mode 100644 index 0000000..93d1e0b --- /dev/null +++ b/.history/views/layout_20251020045457.ejs @@ -0,0 +1,113 @@ + + + + + + <%= title %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+ <%- body %> +
+ + + <%- include('partials/footer') %> + + + + + + + + + + <% if (settings && settings.seo && settings.seo.googleAnalytics) { %> + + + + <% } %> + + \ No newline at end of file diff --git a/.history/views/partials/footer_20251020035751.ejs b/.history/views/partials/footer_20251020035751.ejs new file mode 100644 index 0000000..8094e67 --- /dev/null +++ b/.history/views/partials/footer_20251020035751.ejs @@ -0,0 +1,125 @@ + \ No newline at end of file diff --git a/.history/views/partials/footer_20251020035803.ejs b/.history/views/partials/footer_20251020035803.ejs new file mode 100644 index 0000000..3efd1d7 --- /dev/null +++ b/.history/views/partials/footer_20251020035803.ejs @@ -0,0 +1,125 @@ + \ No newline at end of file diff --git a/.history/views/partials/footer_20251020035824.ejs b/.history/views/partials/footer_20251020035824.ejs new file mode 100644 index 0000000..7a57269 --- /dev/null +++ b/.history/views/partials/footer_20251020035824.ejs @@ -0,0 +1,125 @@ + \ No newline at end of file diff --git a/.history/views/partials/footer_20251020035835.ejs b/.history/views/partials/footer_20251020035835.ejs new file mode 100644 index 0000000..d3d422c --- /dev/null +++ b/.history/views/partials/footer_20251020035835.ejs @@ -0,0 +1,125 @@ + \ No newline at end of file diff --git a/.history/views/partials/footer_20251020035857.ejs b/.history/views/partials/footer_20251020035857.ejs new file mode 100644 index 0000000..d3d422c --- /dev/null +++ b/.history/views/partials/footer_20251020035857.ejs @@ -0,0 +1,125 @@ + \ No newline at end of file diff --git a/.history/views/partials/footer_20251020040336.ejs b/.history/views/partials/footer_20251020040336.ejs new file mode 100644 index 0000000..e053a41 --- /dev/null +++ b/.history/views/partials/footer_20251020040336.ejs @@ -0,0 +1,125 @@ + \ No newline at end of file diff --git a/.history/views/partials/footer_20251020040347.ejs b/.history/views/partials/footer_20251020040347.ejs new file mode 100644 index 0000000..c660555 --- /dev/null +++ b/.history/views/partials/footer_20251020040347.ejs @@ -0,0 +1,125 @@ + \ No newline at end of file diff --git a/.history/views/partials/footer_20251020040409.ejs b/.history/views/partials/footer_20251020040409.ejs new file mode 100644 index 0000000..d7da06b --- /dev/null +++ b/.history/views/partials/footer_20251020040409.ejs @@ -0,0 +1,125 @@ + \ No newline at end of file diff --git a/.history/views/partials/footer_20251020040420.ejs b/.history/views/partials/footer_20251020040420.ejs new file mode 100644 index 0000000..1e0ae0a --- /dev/null +++ b/.history/views/partials/footer_20251020040420.ejs @@ -0,0 +1,125 @@ + \ No newline at end of file diff --git a/.history/views/partials/footer_20251020040538.ejs b/.history/views/partials/footer_20251020040538.ejs new file mode 100644 index 0000000..1e0ae0a --- /dev/null +++ b/.history/views/partials/footer_20251020040538.ejs @@ -0,0 +1,125 @@ + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251020035643.ejs b/.history/views/partials/navigation_20251020035643.ejs new file mode 100644 index 0000000..e894b95 --- /dev/null +++ b/.history/views/partials/navigation_20251020035643.ejs @@ -0,0 +1,207 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251020035702.ejs b/.history/views/partials/navigation_20251020035702.ejs new file mode 100644 index 0000000..4a98f1e --- /dev/null +++ b/.history/views/partials/navigation_20251020035702.ejs @@ -0,0 +1,207 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251020035714.ejs b/.history/views/partials/navigation_20251020035714.ejs new file mode 100644 index 0000000..bd3883c --- /dev/null +++ b/.history/views/partials/navigation_20251020035714.ejs @@ -0,0 +1,207 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251020035734.ejs b/.history/views/partials/navigation_20251020035734.ejs new file mode 100644 index 0000000..89f8511 --- /dev/null +++ b/.history/views/partials/navigation_20251020035734.ejs @@ -0,0 +1,207 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251020035857.ejs b/.history/views/partials/navigation_20251020035857.ejs new file mode 100644 index 0000000..89f8511 --- /dev/null +++ b/.history/views/partials/navigation_20251020035857.ejs @@ -0,0 +1,207 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251020040238.ejs b/.history/views/partials/navigation_20251020040238.ejs new file mode 100644 index 0000000..dc6bee2 --- /dev/null +++ b/.history/views/partials/navigation_20251020040238.ejs @@ -0,0 +1,207 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251020040256.ejs b/.history/views/partials/navigation_20251020040256.ejs new file mode 100644 index 0000000..73ec4de --- /dev/null +++ b/.history/views/partials/navigation_20251020040256.ejs @@ -0,0 +1,207 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251020040308.ejs b/.history/views/partials/navigation_20251020040308.ejs new file mode 100644 index 0000000..df8c1e4 --- /dev/null +++ b/.history/views/partials/navigation_20251020040308.ejs @@ -0,0 +1,207 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251020040327.ejs b/.history/views/partials/navigation_20251020040327.ejs new file mode 100644 index 0000000..3cf4b8b --- /dev/null +++ b/.history/views/partials/navigation_20251020040327.ejs @@ -0,0 +1,207 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251020040538.ejs b/.history/views/partials/navigation_20251020040538.ejs new file mode 100644 index 0000000..3cf4b8b --- /dev/null +++ b/.history/views/partials/navigation_20251020040538.ejs @@ -0,0 +1,207 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251021173840.ejs b/.history/views/partials/navigation_20251021173840.ejs new file mode 100644 index 0000000..fe1930e --- /dev/null +++ b/.history/views/partials/navigation_20251021173840.ejs @@ -0,0 +1,181 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251021173853.ejs b/.history/views/partials/navigation_20251021173853.ejs new file mode 100644 index 0000000..e4043e7 --- /dev/null +++ b/.history/views/partials/navigation_20251021173853.ejs @@ -0,0 +1,174 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251021173904.ejs b/.history/views/partials/navigation_20251021173904.ejs new file mode 100644 index 0000000..b1f4b77 --- /dev/null +++ b/.history/views/partials/navigation_20251021173904.ejs @@ -0,0 +1,157 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251021173947.ejs b/.history/views/partials/navigation_20251021173947.ejs new file mode 100644 index 0000000..b1f4b77 --- /dev/null +++ b/.history/views/partials/navigation_20251021173947.ejs @@ -0,0 +1,157 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251021174141.ejs b/.history/views/partials/navigation_20251021174141.ejs new file mode 100644 index 0000000..2e39190 --- /dev/null +++ b/.history/views/partials/navigation_20251021174141.ejs @@ -0,0 +1,177 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251021175225.ejs b/.history/views/partials/navigation_20251021175225.ejs new file mode 100644 index 0000000..2e39190 --- /dev/null +++ b/.history/views/partials/navigation_20251021175225.ejs @@ -0,0 +1,177 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251021185219.ejs b/.history/views/partials/navigation_20251021185219.ejs new file mode 100644 index 0000000..76a622b --- /dev/null +++ b/.history/views/partials/navigation_20251021185219.ejs @@ -0,0 +1,190 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251021185230.ejs b/.history/views/partials/navigation_20251021185230.ejs new file mode 100644 index 0000000..977e552 --- /dev/null +++ b/.history/views/partials/navigation_20251021185230.ejs @@ -0,0 +1,184 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251021185245.ejs b/.history/views/partials/navigation_20251021185245.ejs new file mode 100644 index 0000000..669611d --- /dev/null +++ b/.history/views/partials/navigation_20251021185245.ejs @@ -0,0 +1,197 @@ + + + \ No newline at end of file diff --git a/.history/views/partials/navigation_20251021190951.ejs b/.history/views/partials/navigation_20251021190951.ejs new file mode 100644 index 0000000..669611d --- /dev/null +++ b/.history/views/partials/navigation_20251021190951.ejs @@ -0,0 +1,197 @@ + + + \ No newline at end of file diff --git a/.history/views/portfolio-detail_20251020041641.ejs b/.history/views/portfolio-detail_20251020041641.ejs new file mode 100644 index 0000000..8dc3167 --- /dev/null +++ b/.history/views/portfolio-detail_20251020041641.ejs @@ -0,0 +1,474 @@ + + + + + + <%= portfolio.title %> - SmartSolTech 포트폴리오 + + + + + + + + + + <% if (portfolio.images && portfolio.images.length > 0) { %> + + <% } %> + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+ + + +
+ +
+
+ + <%= getCategoryName(portfolio.category) %> + + <% if (portfolio.featured) { %> + + FEATURED + + <% } %> + <% if (portfolio.status === 'completed') { %> + + 완료 + + <% } else if (portfolio.status === 'in-progress') { %> + + 진행 중 + + <% } %> +
+ +

+ <%= portfolio.title %> +

+ +

+ <%= portfolio.description %> +

+ + +
+
+
<%= portfolio.viewCount || 0 %>
+
조회수
+
+
+
<%= portfolio.likes || 0 %>
+
좋아요
+
+
+
+ <%= portfolio.completedAt ? new Date(portfolio.completedAt).getFullYear() : new Date().getFullYear() %> +
+
완료년도
+
+
+ + +
+ <% if (portfolio.projectUrl) { %> + + + 프로젝트 보기 + + <% } %> + +
+
+ + +
+ <% if (portfolio.images && portfolio.images.length > 0) { %> +
+ <%= portfolio.title %> +
+
+ <% } else { %> +
+ +
+ <% } %> +
+
+
+
+ + +
+
+
+ + +
+ + + <% if (portfolio.images && portfolio.images.length > 1) { %> +
+

프로젝트 갤러리

+ + +
+
+ <% portfolio.images.forEach(image => { %> +
+
+ <%= image.alt || portfolio.title %> +
+

<%= image.alt || portfolio.title %>

+
+
+
+ <% }) %> +
+
+
+
+
+
+ <% } %> + + +
+

프로젝트 개요

+
+ <%= portfolio.description %> +
+
+ + + <% if (portfolio.technologies && portfolio.technologies.length > 0) { %> +
+

사용된 기술

+
+ <% portfolio.technologies.forEach(tech => { %> +
+
<%= tech %>
+
+ <% }) %> +
+
+ <% } %> + + +
+

이 프로젝트 공유하기

+
+ + + + +
+
+
+ + +
+ + +
+

프로젝트 정보

+ +
+ <% if (portfolio.clientName) { %> +
+
클라이언트
+
<%= portfolio.clientName %>
+
+ <% } %> + +
+
카테고리
+
<%= getCategoryName(portfolio.category) %>
+
+ + <% if (portfolio.completedAt) { %> +
+
완료일
+
+ <%= new Date(portfolio.completedAt).toLocaleDateString('ko-KR') %> +
+
+ <% } %> + + <% if (portfolio.projectUrl) { %> +
+
프로젝트 URL
+ + <%= portfolio.projectUrl %> + +
+ <% } %> +
+
+ + +
+

비슷한 프로젝트가 필요하신가요?

+

+ 이런 프로젝트에 관심이 있으시다면 언제든 연락주세요. + 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다. +

+ +
+ +
+
+
+
+ + +
+
+
+

+ 관련 프로젝트 +

+

+ 비슷한 카테고리의 다른 프로젝트들도 확인해보세요 +

+
+ +
+ <% if (relatedProjects && relatedProjects.length > 0) { %> + <% relatedProjects.forEach((project, index) => { %> +
+ +
+ <% if (project.images && project.images.length > 0) { %> + <%= project.title %> + <% } else { %> +
+ +
+ <% } %> +
+ +
+

+ <%= project.title %> +

+

+ <%= project.shortDescription || project.description %> +

+ + 자세히 보기 → + +
+
+ <% }) %> + <% } else { %> +
+

관련 프로젝트가 없습니다.

+
+ <% } %> +
+
+
+ + <%- include('partials/footer') %> + + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio-detail_20251020041649.ejs b/.history/views/portfolio-detail_20251020041649.ejs new file mode 100644 index 0000000..2b97f47 --- /dev/null +++ b/.history/views/portfolio-detail_20251020041649.ejs @@ -0,0 +1,474 @@ + + + + + + <%= portfolio.title %> - SmartSolTech 포트폴리오 + + + + + + + + + + <% if (portfolio.images && portfolio.images.length > 0) { %> + + <% } %> + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+ + + +
+ +
+
+ + <%= getCategoryName(portfolio.category) %> + + <% if (portfolio.featured) { %> + + FEATURED + + <% } %> + <% if (portfolio.status === 'completed') { %> + + 완료 + + <% } else if (portfolio.status === 'in-progress') { %> + + 진행 중 + + <% } %> +
+ +

+ <%= portfolio.title %> +

+ +

+ <%= portfolio.description %> +

+ + +
+
+
<%= portfolio.viewCount || 0 %>
+
조회수
+
+
+
<%= portfolio.likes || 0 %>
+
좋아요
+
+
+
+ <%= portfolio.completedAt ? new Date(portfolio.completedAt).getFullYear() : new Date().getFullYear() %> +
+
완료년도
+
+
+ + +
+ <% if (portfolio.projectUrl) { %> + + + 프로젝트 보기 + + <% } %> + +
+
+ + +
+ <% if (portfolio.images && portfolio.images.length > 0) { %> +
+ <%= portfolio.title %> +
+
+ <% } else { %> +
+ +
+ <% } %> +
+
+
+
+ + +
+
+
+ + +
+ + + <% if (portfolio.images && portfolio.images.length > 1) { %> +
+

프로젝트 갤러리

+ + +
+
+ <% portfolio.images.forEach(image => { %> +
+
+ <%= image.alt || portfolio.title %> +
+

<%= image.alt || portfolio.title %>

+
+
+
+ <% }) %> +
+
+
+
+
+
+ <% } %> + + +
+

프로젝트 개요

+
+ <%= portfolio.description %> +
+
+ + + <% if (portfolio.technologies && portfolio.technologies.length > 0) { %> +
+

사용된 기술

+
+ <% portfolio.technologies.forEach(tech => { %> +
+
<%= tech %>
+
+ <% }) %> +
+
+ <% } %> + + +
+

이 프로젝트 공유하기

+
+ + + + +
+
+
+ + +
+ + +
+

프로젝트 정보

+ +
+ <% if (portfolio.clientName) { %> +
+
클라이언트
+
<%= portfolio.clientName %>
+
+ <% } %> + +
+
카테고리
+
<%= getCategoryName(portfolio.category) %>
+
+ + <% if (portfolio.completedAt) { %> +
+
완료일
+
+ <%= new Date(portfolio.completedAt).toLocaleDateString('ko-KR') %> +
+
+ <% } %> + + <% if (portfolio.projectUrl) { %> +
+
프로젝트 URL
+ + <%= portfolio.projectUrl %> + +
+ <% } %> +
+
+ + +
+

비슷한 프로젝트가 필요하신가요?

+

+ 이런 프로젝트에 관심이 있으시다면 언제든 연락주세요. + 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다. +

+ +
+ +
+
+
+
+ + +
+
+
+

+ 관련 프로젝트 +

+

+ 비슷한 카테고리의 다른 프로젝트들도 확인해보세요 +

+
+ +
+ <% if (relatedProjects && relatedProjects.length > 0) { %> + <% relatedProjects.forEach((project, index) => { %> +
+ +
+ <% if (project.images && project.images.length > 0) { %> + <%= project.title %> + <% } else { %> +
+ +
+ <% } %> +
+ +
+

+ <%= project.title %> +

+

+ <%= project.shortDescription || project.description %> +

+ + 자세히 보기 → + +
+
+ <% }) %> + <% } else { %> +
+

관련 프로젝트가 없습니다.

+
+ <% } %> +
+
+
+ + <%- include('partials/footer') %> + + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio-detail_20251020041655.ejs b/.history/views/portfolio-detail_20251020041655.ejs new file mode 100644 index 0000000..01a16ef --- /dev/null +++ b/.history/views/portfolio-detail_20251020041655.ejs @@ -0,0 +1,474 @@ + + + + + + <%= portfolio.title %> - SmartSolTech 포트폴리오 + + + + + + + + + + <% if (portfolio.images && portfolio.images.length > 0) { %> + + <% } %> + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+ + + +
+ +
+
+ + <%= getCategoryName(portfolio.category) %> + + <% if (portfolio.featured) { %> + + FEATURED + + <% } %> + <% if (portfolio.status === 'completed') { %> + + 완료 + + <% } else if (portfolio.status === 'in-progress') { %> + + 진행 중 + + <% } %> +
+ +

+ <%= portfolio.title %> +

+ +

+ <%= portfolio.description %> +

+ + +
+
+
<%= portfolio.viewCount || 0 %>
+
조회수
+
+
+
<%= portfolio.likes || 0 %>
+
좋아요
+
+
+
+ <%= portfolio.completedAt ? new Date(portfolio.completedAt).getFullYear() : new Date().getFullYear() %> +
+
완료년도
+
+
+ + +
+ <% if (portfolio.projectUrl) { %> + + + 프로젝트 보기 + + <% } %> + +
+
+ + +
+ <% if (portfolio.images && portfolio.images.length > 0) { %> +
+ <%= portfolio.title %> +
+
+ <% } else { %> +
+ +
+ <% } %> +
+
+
+
+ + +
+
+
+ + +
+ + + <% if (portfolio.images && portfolio.images.length > 1) { %> +
+

프로젝트 갤러리

+ + +
+
+ <% portfolio.images.forEach(image => { %> +
+
+ <%= image.alt || portfolio.title %> +
+

<%= image.alt || portfolio.title %>

+
+
+
+ <% }) %> +
+
+
+
+
+
+ <% } %> + + +
+

프로젝트 개요

+
+ <%= portfolio.description %> +
+
+ + + <% if (portfolio.technologies && portfolio.technologies.length > 0) { %> +
+

사용된 기술

+
+ <% portfolio.technologies.forEach(tech => { %> +
+
<%= tech %>
+
+ <% }) %> +
+
+ <% } %> + + +
+

이 프로젝트 공유하기

+
+ + + + +
+
+
+ + +
+ + +
+

프로젝트 정보

+ +
+ <% if (portfolio.clientName) { %> +
+
클라이언트
+
<%= portfolio.clientName %>
+
+ <% } %> + +
+
카테고리
+
<%= getCategoryName(portfolio.category) %>
+
+ + <% if (portfolio.completedAt) { %> +
+
완료일
+
+ <%= new Date(portfolio.completedAt).toLocaleDateString('ko-KR') %> +
+
+ <% } %> + + <% if (portfolio.projectUrl) { %> +
+
프로젝트 URL
+ + <%= portfolio.projectUrl %> + +
+ <% } %> +
+
+ + +
+

비슷한 프로젝트가 필요하신가요?

+

+ 이런 프로젝트에 관심이 있으시다면 언제든 연락주세요. + 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다. +

+ +
+ +
+
+
+
+ + +
+
+
+

+ 관련 프로젝트 +

+

+ 비슷한 카테고리의 다른 프로젝트들도 확인해보세요 +

+
+ +
+ <% if (relatedProjects && relatedProjects.length > 0) { %> + <% relatedProjects.forEach((project, index) => { %> +
+ +
+ <% if (project.images && project.images.length > 0) { %> + <%= project.title %> + <% } else { %> +
+ +
+ <% } %> +
+ +
+

+ <%= project.title %> +

+

+ <%= project.shortDescription || project.description %> +

+ + 자세히 보기 → + +
+
+ <% }) %> + <% } else { %> +
+

관련 프로젝트가 없습니다.

+
+ <% } %> +
+
+
+ + <%- include('partials/footer') %> + + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio-detail_20251020041711.ejs b/.history/views/portfolio-detail_20251020041711.ejs new file mode 100644 index 0000000..01a16ef --- /dev/null +++ b/.history/views/portfolio-detail_20251020041711.ejs @@ -0,0 +1,474 @@ + + + + + + <%= portfolio.title %> - SmartSolTech 포트폴리오 + + + + + + + + + + <% if (portfolio.images && portfolio.images.length > 0) { %> + + <% } %> + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+ + + +
+ +
+
+ + <%= getCategoryName(portfolio.category) %> + + <% if (portfolio.featured) { %> + + FEATURED + + <% } %> + <% if (portfolio.status === 'completed') { %> + + 완료 + + <% } else if (portfolio.status === 'in-progress') { %> + + 진행 중 + + <% } %> +
+ +

+ <%= portfolio.title %> +

+ +

+ <%= portfolio.description %> +

+ + +
+
+
<%= portfolio.viewCount || 0 %>
+
조회수
+
+
+
<%= portfolio.likes || 0 %>
+
좋아요
+
+
+
+ <%= portfolio.completedAt ? new Date(portfolio.completedAt).getFullYear() : new Date().getFullYear() %> +
+
완료년도
+
+
+ + +
+ <% if (portfolio.projectUrl) { %> + + + 프로젝트 보기 + + <% } %> + +
+
+ + +
+ <% if (portfolio.images && portfolio.images.length > 0) { %> +
+ <%= portfolio.title %> +
+
+ <% } else { %> +
+ +
+ <% } %> +
+
+
+
+ + +
+
+
+ + +
+ + + <% if (portfolio.images && portfolio.images.length > 1) { %> +
+

프로젝트 갤러리

+ + +
+
+ <% portfolio.images.forEach(image => { %> +
+
+ <%= image.alt || portfolio.title %> +
+

<%= image.alt || portfolio.title %>

+
+
+
+ <% }) %> +
+
+
+
+
+
+ <% } %> + + +
+

프로젝트 개요

+
+ <%= portfolio.description %> +
+
+ + + <% if (portfolio.technologies && portfolio.technologies.length > 0) { %> +
+

사용된 기술

+
+ <% portfolio.technologies.forEach(tech => { %> +
+
<%= tech %>
+
+ <% }) %> +
+
+ <% } %> + + +
+

이 프로젝트 공유하기

+
+ + + + +
+
+
+ + +
+ + +
+

프로젝트 정보

+ +
+ <% if (portfolio.clientName) { %> +
+
클라이언트
+
<%= portfolio.clientName %>
+
+ <% } %> + +
+
카테고리
+
<%= getCategoryName(portfolio.category) %>
+
+ + <% if (portfolio.completedAt) { %> +
+
완료일
+
+ <%= new Date(portfolio.completedAt).toLocaleDateString('ko-KR') %> +
+
+ <% } %> + + <% if (portfolio.projectUrl) { %> +
+
프로젝트 URL
+ + <%= portfolio.projectUrl %> + +
+ <% } %> +
+
+ + +
+

비슷한 프로젝트가 필요하신가요?

+

+ 이런 프로젝트에 관심이 있으시다면 언제든 연락주세요. + 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다. +

+ +
+ +
+
+
+
+ + +
+
+
+

+ 관련 프로젝트 +

+

+ 비슷한 카테고리의 다른 프로젝트들도 확인해보세요 +

+
+ +
+ <% if (relatedProjects && relatedProjects.length > 0) { %> + <% relatedProjects.forEach((project, index) => { %> +
+ +
+ <% if (project.images && project.images.length > 0) { %> + <%= project.title %> + <% } else { %> +
+ +
+ <% } %> +
+ +
+

+ <%= project.title %> +

+

+ <%= project.shortDescription || project.description %> +

+ + 자세히 보기 → + +
+
+ <% }) %> + <% } else { %> +
+

관련 프로젝트가 없습니다.

+
+ <% } %> +
+
+
+ + <%- include('partials/footer') %> + + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251020041534.ejs b/.history/views/portfolio_20251020041534.ejs new file mode 100644 index 0000000..de68188 --- /dev/null +++ b/.history/views/portfolio_20251020041534.ejs @@ -0,0 +1,298 @@ + + + + + + 포트폴리오 - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ 우리의 포트폴리오 +

+

+ 혁신적인 프로젝트와 창의적인 솔루션들을 만나보세요 +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

아직 포트폴리오가 없습니다

+

곧 멋진 프로젝트들을 공개할 예정입니다!

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ 다음 프로젝트의 주인공이 되어보세요 +

+

+ 우리와 함께 혁신적인 디지털 솔루션을 만들어보세요 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251020041542.ejs b/.history/views/portfolio_20251020041542.ejs new file mode 100644 index 0000000..56d5684 --- /dev/null +++ b/.history/views/portfolio_20251020041542.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.title') || '포트폴리오 - SmartSolTech' %> + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ 우리의 포트폴리오 +

+

+ 혁신적인 프로젝트와 창의적인 솔루션들을 만나보세요 +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

아직 포트폴리오가 없습니다

+

곧 멋진 프로젝트들을 공개할 예정입니다!

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ 다음 프로젝트의 주인공이 되어보세요 +

+

+ 우리와 함께 혁신적인 디지털 솔루션을 만들어보세요 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251020041547.ejs b/.history/views/portfolio_20251020041547.ejs new file mode 100644 index 0000000..9002a1e --- /dev/null +++ b/.history/views/portfolio_20251020041547.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.title') || '포트폴리오 - SmartSolTech' %> + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ 우리의 포트폴리오 +

+

+ 혁신적인 프로젝트와 창의적인 솔루션들을 만나보세요 +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

아직 포트폴리오가 없습니다

+

곧 멋진 프로젝트들을 공개할 예정입니다!

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ 다음 프로젝트의 주인공이 되어보세요 +

+

+ 우리와 함께 혁신적인 디지털 솔루션을 만들어보세요 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251020041711.ejs b/.history/views/portfolio_20251020041711.ejs new file mode 100644 index 0000000..9002a1e --- /dev/null +++ b/.history/views/portfolio_20251020041711.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.title') || '포트폴리오 - SmartSolTech' %> + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ 우리의 포트폴리오 +

+

+ 혁신적인 프로젝트와 창의적인 솔루션들을 만나보세요 +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

아직 포트폴리오가 없습니다

+

곧 멋진 프로젝트들을 공개할 예정입니다!

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ 다음 프로젝트의 주인공이 되어보세요 +

+

+ 우리와 함께 혁신적인 디지털 솔루션을 만들어보세요 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021184538.ejs b/.history/views/portfolio_20251021184538.ejs new file mode 100644 index 0000000..d4eaa47 --- /dev/null +++ b/.history/views/portfolio_20251021184538.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ 우리의 포트폴리오 +

+

+ 혁신적인 프로젝트와 창의적인 솔루션들을 만나보세요 +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

아직 포트폴리오가 없습니다

+

곧 멋진 프로젝트들을 공개할 예정입니다!

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ 다음 프로젝트의 주인공이 되어보세요 +

+

+ 우리와 함께 혁신적인 디지털 솔루션을 만들어보세요 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021184547.ejs b/.history/views/portfolio_20251021184547.ejs new file mode 100644 index 0000000..d4eaa47 --- /dev/null +++ b/.history/views/portfolio_20251021184547.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ 우리의 포트폴리오 +

+

+ 혁신적인 프로젝트와 창의적인 솔루션들을 만나보세요 +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

아직 포트폴리오가 없습니다

+

곧 멋진 프로젝트들을 공개할 예정입니다!

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ 다음 프로젝트의 주인공이 되어보세요 +

+

+ 우리와 함께 혁신적인 디지털 솔루션을 만들어보세요 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021211541.ejs b/.history/views/portfolio_20251021211541.ejs new file mode 100644 index 0000000..0c29b0c --- /dev/null +++ b/.history/views/portfolio_20251021211541.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

아직 포트폴리오가 없습니다

+

곧 멋진 프로젝트들을 공개할 예정입니다!

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ 다음 프로젝트의 주인공이 되어보세요 +

+

+ 우리와 함께 혁신적인 디지털 솔루션을 만들어보세요 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021211552.ejs b/.history/views/portfolio_20251021211552.ejs new file mode 100644 index 0000000..f336ce8 --- /dev/null +++ b/.history/views/portfolio_20251021211552.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

아직 포트폴리오가 없습니다

+

곧 멋진 프로젝트들을 공개할 예정입니다!

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ 다음 프로젝트의 주인공이 되어보세요 +

+

+ 우리와 함께 혁신적인 디지털 솔루션을 만들어보세요 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021211553.ejs b/.history/views/portfolio_20251021211553.ejs new file mode 100644 index 0000000..f336ce8 --- /dev/null +++ b/.history/views/portfolio_20251021211553.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

아직 포트폴리오가 없습니다

+

곧 멋진 프로젝트들을 공개할 예정입니다!

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ 다음 프로젝트의 주인공이 되어보세요 +

+

+ 우리와 함께 혁신적인 디지털 솔루션을 만들어보세요 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021211600.ejs b/.history/views/portfolio_20251021211600.ejs new file mode 100644 index 0000000..bd6b34b --- /dev/null +++ b/.history/views/portfolio_20251021211600.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

아직 포트폴리오가 없습니다

+

곧 멋진 프로젝트들을 공개할 예정입니다!

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ 다음 프로젝트의 주인공이 되어보세요 +

+

+ 우리와 함께 혁신적인 디지털 솔루션을 만들어보세요 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021211608.ejs b/.history/views/portfolio_20251021211608.ejs new file mode 100644 index 0000000..cb596c4 --- /dev/null +++ b/.history/views/portfolio_20251021211608.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

아직 포트폴리오가 없습니다

+

곧 멋진 프로젝트들을 공개할 예정입니다!

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ 다음 프로젝트의 주인공이 되어보세요 +

+

+ 우리와 함께 혁신적인 디지털 솔루션을 만들어보세요 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021211616.ejs b/.history/views/portfolio_20251021211616.ejs new file mode 100644 index 0000000..02a3ef1 --- /dev/null +++ b/.history/views/portfolio_20251021211616.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

<%- __('portfolio_page.empty.title') %>

+

<%- __('portfolio_page.empty.subtitle') %>

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ 다음 프로젝트의 주인공이 되어보세요 +

+

+ 우리와 함께 혁신적인 디지털 솔루션을 만들어보세요 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021211626.ejs b/.history/views/portfolio_20251021211626.ejs new file mode 100644 index 0000000..ed6d86e --- /dev/null +++ b/.history/views/portfolio_20251021211626.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

<%- __('portfolio_page.empty.title') %>

+

<%- __('portfolio_page.empty.subtitle') %>

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ 다음 프로젝트의 주인공이 되어보세요 +

+

+ 우리와 함께 혁신적인 디지털 솔루션을 만들어보세요 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021211634.ejs b/.history/views/portfolio_20251021211634.ejs new file mode 100644 index 0000000..e999ab4 --- /dev/null +++ b/.history/views/portfolio_20251021211634.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

<%- __('portfolio_page.empty.title') %>

+

<%- __('portfolio_page.empty.subtitle') %>

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ <%- __('portfolio_page.cta.title') %> +

+

+ <%- __('portfolio_page.cta.subtitle') %> +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021211644.ejs b/.history/views/portfolio_20251021211644.ejs new file mode 100644 index 0000000..2957682 --- /dev/null +++ b/.history/views/portfolio_20251021211644.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

<%- __('portfolio_page.empty.title') %>

+

<%- __('portfolio_page.empty.subtitle') %>

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ <%- __('portfolio_page.cta.title') %> +

+

+ <%- __('portfolio_page.cta.subtitle') %> +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021211654.ejs b/.history/views/portfolio_20251021211654.ejs new file mode 100644 index 0000000..c6f1001 --- /dev/null +++ b/.history/views/portfolio_20251021211654.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

<%- __('portfolio_page.empty.title') %>

+

<%- __('portfolio_page.empty.subtitle') %>

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ <%- __('portfolio_page.cta.title') %> +

+

+ <%- __('portfolio_page.cta.subtitle') %> +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names + function getCategoryName(category) { + const categoryNames = { + 'web-development': '웹 개발', + 'mobile-app': '모바일 앱', + 'ui-ux-design': 'UI/UX 디자인', + 'branding': '브랜딩', + 'marketing': '디지털 마케팅' + }; + return categoryNames[category] || category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021211703.ejs b/.history/views/portfolio_20251021211703.ejs new file mode 100644 index 0000000..b9fae10 --- /dev/null +++ b/.history/views/portfolio_20251021211703.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + FEATURED + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

<%- __('portfolio_page.empty.title') %>

+

<%- __('portfolio_page.empty.subtitle') %>

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ <%- __('portfolio_page.cta.title') %> +

+

+ <%- __('portfolio_page.cta.subtitle') %> +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names - uses i18n + function getCategoryName(category) { + const categoryMap = { + 'web-development': 'portfolio_page.categories.web-development', + 'mobile-app': 'portfolio_page.categories.mobile-app', + 'ui-ux-design': 'portfolio_page.categories.ui-ux-design', + 'branding': 'portfolio_page.categories.branding', + 'marketing': 'portfolio_page.categories.marketing' + }; + return categoryMap[category] ? __(categoryMap[category]) : category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021211710.ejs b/.history/views/portfolio_20251021211710.ejs new file mode 100644 index 0000000..98dedc6 --- /dev/null +++ b/.history/views/portfolio_20251021211710.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + <%- __('portfolio_page.labels.featured') %> + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

<%- __('portfolio_page.empty.title') %>

+

<%- __('portfolio_page.empty.subtitle') %>

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ <%- __('portfolio_page.cta.title') %> +

+

+ <%- __('portfolio_page.cta.subtitle') %> +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names - uses i18n + function getCategoryName(category) { + const categoryMap = { + 'web-development': 'portfolio_page.categories.web-development', + 'mobile-app': 'portfolio_page.categories.mobile-app', + 'ui-ux-design': 'portfolio_page.categories.ui-ux-design', + 'branding': 'portfolio_page.categories.branding', + 'marketing': 'portfolio_page.categories.marketing' + }; + return categoryMap[category] ? __(categoryMap[category]) : category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021211719.ejs b/.history/views/portfolio_20251021211719.ejs new file mode 100644 index 0000000..98dedc6 --- /dev/null +++ b/.history/views/portfolio_20251021211719.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + <%- __('portfolio_page.labels.featured') %> + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

<%- __('portfolio_page.empty.title') %>

+

<%- __('portfolio_page.empty.subtitle') %>

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ <%- __('portfolio_page.cta.title') %> +

+

+ <%- __('portfolio_page.cta.subtitle') %> +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names - uses i18n + function getCategoryName(category) { + const categoryMap = { + 'web-development': 'portfolio_page.categories.web-development', + 'mobile-app': 'portfolio_page.categories.mobile-app', + 'ui-ux-design': 'portfolio_page.categories.ui-ux-design', + 'branding': 'portfolio_page.categories.branding', + 'marketing': 'portfolio_page.categories.marketing' + }; + return categoryMap[category] ? __(categoryMap[category]) : category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021212403.ejs b/.history/views/portfolio_20251021212403.ejs new file mode 100644 index 0000000..cabef5e --- /dev/null +++ b/.history/views/portfolio_20251021212403.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + <%- __('portfolio_page.labels.featured') %> + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

<%- __('portfolio_page.empty.title') %>

+

<%- __('portfolio_page.empty.subtitle') %>

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ <%- __('portfolio_page.cta.title') %> +

+

+ <%- __('portfolio_page.cta.subtitle') %> +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names - uses i18n + function getCategoryName(category) { + const categoryMap = { + 'web-development': 'portfolio_page.categories.web-development', + 'mobile-app': 'portfolio_page.categories.mobile-app', + 'ui-ux-design': 'portfolio_page.categories.ui-ux-design', + 'branding': 'portfolio_page.categories.branding', + 'marketing': 'portfolio_page.categories.marketing' + }; + return categoryMap[category] ? __(categoryMap[category]) : category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021212447.ejs b/.history/views/portfolio_20251021212447.ejs new file mode 100644 index 0000000..4a16fd9 --- /dev/null +++ b/.history/views/portfolio_20251021212447.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + <%- __('portfolio_page.labels.featured') %> + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

<%- __('portfolio_page.empty.title') %>

+

<%- __('portfolio_page.empty.subtitle') %>

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ <%- __('portfolio_page.cta.title') %> +

+

+ <%- __('portfolio_page.cta.subtitle') %> +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names - uses i18n + function getCategoryName(category) { + const categoryMap = { + 'web-development': 'portfolio_page.categories.web-development', + 'mobile-app': 'portfolio_page.categories.mobile-app', + 'ui-ux-design': 'portfolio_page.categories.ui-ux-design', + 'branding': 'portfolio_page.categories.branding', + 'marketing': 'portfolio_page.categories.marketing' + }; + return categoryMap[category] ? __(categoryMap[category]) : category; + } + %> + + \ No newline at end of file diff --git a/.history/views/portfolio_20251021212532.ejs b/.history/views/portfolio_20251021212532.ejs new file mode 100644 index 0000000..4a16fd9 --- /dev/null +++ b/.history/views/portfolio_20251021212532.ejs @@ -0,0 +1,298 @@ + + + + + + <%- __('portfolio.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('portfolio_page.title') %> +

+

+ <%- __('portfolio_page.subtitle') %> +

+
+ + + + +
+
+
+ + +
+
+
+ <% if (portfolioItems && portfolioItems.length > 0) { %> + <% portfolioItems.forEach((item, index) => { %> +
+ + +
+ <% if (item.images && item.images.length > 0) { %> + <%= item.title %> + <% } else { %> +
+ +
+ <% } %> + + +
+ + <%= getCategoryName(item.category) %> + +
+ + + <% if (item.featured) { %> +
+ + <%- __('portfolio_page.labels.featured') %> + +
+ <% } %> + + + +
+ + +
+

+ <%= item.title %> +

+

+ <%= item.shortDescription || item.description %> +

+ + +
+ <% if (item.technologies && item.technologies.length > 0) { %> + <% item.technologies.slice(0, 3).forEach(tech => { %> + + <%= tech %> + + <% }) %> + <% if (item.technologies.length > 3) { %> + + +<%= item.technologies.length - 3 %> + + <% } %> + <% } %> +
+ + +
+
+ <% if (item.clientName) { %> + + <%= item.clientName %> + <% } %> +
+
+ + + <%= item.viewCount || 0 %> + + + + <%= item.likes || 0 %> + +
+
+ + + +
+
+ <% }) %> + <% } else { %> +
+ +

<%- __('portfolio_page.empty.title') %>

+

<%- __('portfolio_page.empty.subtitle') %>

+
+ <% } %> +
+ + + <% if (portfolioItems && portfolioItems.length >= 9) { %> +
+ +
+ <% } %> +
+
+ + +
+
+

+ <%- __('portfolio_page.cta.title') %> +

+

+ <%- __('portfolio_page.cta.subtitle') %> +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + + + <% + // Helper function for category names - uses i18n + function getCategoryName(category) { + const categoryMap = { + 'web-development': 'portfolio_page.categories.web-development', + 'mobile-app': 'portfolio_page.categories.mobile-app', + 'ui-ux-design': 'portfolio_page.categories.ui-ux-design', + 'branding': 'portfolio_page.categories.branding', + 'marketing': 'portfolio_page.categories.marketing' + }; + return categoryMap[category] ? __(categoryMap[category]) : category; + } + %> + + \ No newline at end of file diff --git a/.history/views/services_20251020041507.ejs b/.history/views/services_20251020041507.ejs new file mode 100644 index 0000000..092e75b --- /dev/null +++ b/.history/views/services_20251020041507.ejs @@ -0,0 +1,295 @@ + + + + + + 서비스 - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ 우리의 서비스 +

+

+ 혁신적인 기술로 비즈니스의 성장을 지원합니다 +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
시작가격
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : '상담' %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251020041515.ejs b/.history/views/services_20251020041515.ejs new file mode 100644 index 0000000..3fc5b02 --- /dev/null +++ b/.history/views/services_20251020041515.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.title') || '서비스 - SmartSolTech' %> + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ 우리의 서비스 +

+

+ 혁신적인 기술로 비즈니스의 성장을 지원합니다 +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
시작가격
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : '상담' %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251020041521.ejs b/.history/views/services_20251020041521.ejs new file mode 100644 index 0000000..6e04bbe --- /dev/null +++ b/.history/views/services_20251020041521.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.title') || '서비스 - SmartSolTech' %> + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ 우리의 서비스 +

+

+ 혁신적인 기술로 비즈니스의 성장을 지원합니다 +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
시작가격
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : '상담' %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251020041711.ejs b/.history/views/services_20251020041711.ejs new file mode 100644 index 0000000..6e04bbe --- /dev/null +++ b/.history/views/services_20251020041711.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.title') || '서비스 - SmartSolTech' %> + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ 우리의 서비스 +

+

+ 혁신적인 기술로 비즈니스의 성장을 지원합니다 +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
시작가격
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : '상담' %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251020225327.ejs b/.history/views/services_20251020225327.ejs new file mode 100644 index 0000000..c48265c --- /dev/null +++ b/.history/views/services_20251020225327.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.title') || '서비스 - SmartSolTech' %> + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ 우리의 서비스 +

+

+ 혁신적인 기술로 비즈니스의 성장을 지원합니다 +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
시작가격
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : '상담' %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251020225419.ejs b/.history/views/services_20251020225419.ejs new file mode 100644 index 0000000..c48265c --- /dev/null +++ b/.history/views/services_20251020225419.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.title') || '서비스 - SmartSolTech' %> + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ 우리의 서비스 +

+

+ 혁신적인 기술로 비즈니스의 성장을 지원합니다 +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
시작가격
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : '상담' %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021184120.ejs b/.history/views/services_20251021184120.ejs new file mode 100644 index 0000000..e7703cc --- /dev/null +++ b/.history/views/services_20251021184120.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ 우리의 서비스 +

+

+ 혁신적인 기술로 비즈니스의 성장을 지원합니다 +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
시작가격
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : '상담' %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021184308.ejs b/.history/views/services_20251021184308.ejs new file mode 100644 index 0000000..e7703cc --- /dev/null +++ b/.history/views/services_20251021184308.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ 우리의 서비스 +

+

+ 혁신적인 기술로 비즈니스의 성장을 지원합니다 +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
시작가격
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : '상담' %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021184341.ejs b/.history/views/services_20251021184341.ejs new file mode 100644 index 0000000..5d933e7 --- /dev/null +++ b/.history/views/services_20251021184341.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('services.hero.title') %> <%- __('services.hero.title_highlight') %> +

+

+ <%- __('services.hero.subtitle') %> +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
시작가격
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : '상담' %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021184342.ejs b/.history/views/services_20251021184342.ejs new file mode 100644 index 0000000..5d933e7 --- /dev/null +++ b/.history/views/services_20251021184342.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('services.hero.title') %> <%- __('services.hero.title_highlight') %> +

+

+ <%- __('services.hero.subtitle') %> +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
시작가격
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : '상담' %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021184349.ejs b/.history/views/services_20251021184349.ejs new file mode 100644 index 0000000..78efb4f --- /dev/null +++ b/.history/views/services_20251021184349.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('services.hero.title') %> <%- __('services.hero.title_highlight') %> +

+

+ <%- __('services.hero.subtitle') %> +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
<%- __('services.cards.starting_price') %>
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : __('services.cards.consultation') %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021184350.ejs b/.history/views/services_20251021184350.ejs new file mode 100644 index 0000000..78efb4f --- /dev/null +++ b/.history/views/services_20251021184350.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('services.hero.title') %> <%- __('services.hero.title_highlight') %> +

+

+ <%- __('services.hero.subtitle') %> +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
<%- __('services.cards.starting_price') %>
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : __('services.cards.consultation') %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021184356.ejs b/.history/views/services_20251021184356.ejs new file mode 100644 index 0000000..ccc8a17 --- /dev/null +++ b/.history/views/services_20251021184356.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('services.hero.title') %> <%- __('services.hero.title_highlight') %> +

+

+ <%- __('services.hero.subtitle') %> +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
<%- __('services.cards.starting_price') %>
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : __('services.cards.consultation') %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021184357.ejs b/.history/views/services_20251021184357.ejs new file mode 100644 index 0000000..ccc8a17 --- /dev/null +++ b/.history/views/services_20251021184357.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('services.hero.title') %> <%- __('services.hero.title_highlight') %> +

+

+ <%- __('services.hero.subtitle') %> +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
<%- __('services.cards.starting_price') %>
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : __('services.cards.consultation') %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021184402.ejs b/.history/views/services_20251021184402.ejs new file mode 100644 index 0000000..30f0b04 --- /dev/null +++ b/.history/views/services_20251021184402.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('services.hero.title') %> <%- __('services.hero.title_highlight') %> +

+

+ <%- __('services.hero.subtitle') %> +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
<%- __('services.cards.starting_price') %>
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : __('services.cards.consultation') %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + 인기 + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021184408.ejs b/.history/views/services_20251021184408.ejs new file mode 100644 index 0000000..02a7863 --- /dev/null +++ b/.history/views/services_20251021184408.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('services.hero.title') %> <%- __('services.hero.title_highlight') %> +

+

+ <%- __('services.hero.subtitle') %> +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
<%- __('services.cards.starting_price') %>
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : __('services.cards.consultation') %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + <%- __('services.cards.popular') %> + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

서비스 준비 중

+

곧 다양한 서비스를 제공할 예정입니다!

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021184415.ejs b/.history/views/services_20251021184415.ejs new file mode 100644 index 0000000..0947a3e --- /dev/null +++ b/.history/views/services_20251021184415.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('services.hero.title') %> <%- __('services.hero.title_highlight') %> +

+

+ <%- __('services.hero.subtitle') %> +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
<%- __('services.cards.starting_price') %>
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : __('services.cards.consultation') %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + <%- __('services.cards.popular') %> + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

<%- __('services.cards.coming_soon') %>

+

<%- __('services.cards.coming_soon_desc') %>

+
+ <% } %> +
+
+
+ + +
+
+
+

+ 프로젝트 진행 과정 +

+

+ 체계적이고 전문적인 프로세스로 프로젝트를 진행합니다 +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021184425.ejs b/.history/views/services_20251021184425.ejs new file mode 100644 index 0000000..4e9d2da --- /dev/null +++ b/.history/views/services_20251021184425.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('services.hero.title') %> <%- __('services.hero.title_highlight') %> +

+

+ <%- __('services.hero.subtitle') %> +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
<%- __('services.cards.starting_price') %>
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : __('services.cards.consultation') %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + <%- __('services.cards.popular') %> + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

<%- __('services.cards.coming_soon') %>

+

<%- __('services.cards.coming_soon_desc') %>

+
+ <% } %> +
+
+
+ + +
+
+
+

+ <%- __('services.process.title') %> +

+

+ <%- __('services.process.subtitle') %> +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021184446.ejs b/.history/views/services_20251021184446.ejs new file mode 100644 index 0000000..4e9d2da --- /dev/null +++ b/.history/views/services_20251021184446.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('services.hero.title') %> <%- __('services.hero.title_highlight') %> +

+

+ <%- __('services.hero.subtitle') %> +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
<%- __('services.cards.starting_price') %>
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : __('services.cards.consultation') %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + <%- __('services.cards.popular') %> + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

<%- __('services.cards.coming_soon') %>

+

<%- __('services.cards.coming_soon_desc') %>

+
+ <% } %> +
+
+
+ + +
+
+
+

+ <%- __('services.process.title') %> +

+

+ <%- __('services.process.subtitle') %> +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021212411.ejs b/.history/views/services_20251021212411.ejs new file mode 100644 index 0000000..4cf1f8d --- /dev/null +++ b/.history/views/services_20251021212411.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('services.hero.title') %> <%- __('services.hero.title_highlight') %> +

+

+ <%- __('services.hero.subtitle') %> +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
<%- __('services.cards.starting_price') %>
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : __('services.cards.consultation') %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + <%- __('services.cards.popular') %> + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

<%- __('services.cards.coming_soon') %>

+

<%- __('services.cards.coming_soon_desc') %>

+
+ <% } %> +
+
+
+ + +
+
+
+

+ <%- __('services.process.title') %> +

+

+ <%- __('services.process.subtitle') %> +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file diff --git a/.history/views/services_20251021212532.ejs b/.history/views/services_20251021212532.ejs new file mode 100644 index 0000000..4cf1f8d --- /dev/null +++ b/.history/views/services_20251021212532.ejs @@ -0,0 +1,295 @@ + + + + + + <%- __('services.meta.title') %> - SmartSolTech + + + + + + + + + + + + + + + + + + + + <%- include('partials/navigation') %> + + +
+
+

+ <%- __('services.hero.title') %> <%- __('services.hero.title_highlight') %> +

+

+ <%- __('services.hero.subtitle') %> +

+
+
+ + +
+
+
+ <% if (services && services.length > 0) { %> + <% services.forEach((service, index) => { %> +
+ + +
+ +
+ + +

+ <%= service.name %> +

+ +

+ <%= service.shortDescription || service.description %> +

+ + + <% if (service.pricing) { %> +
+
<%- __('services.cards.starting_price') %>
+
+ <%= service.pricing.basePrice ? service.pricing.basePrice.toLocaleString() : __('services.cards.consultation') %> + <% if (service.pricing.basePrice) { %> + 원~ + <% } %> +
+ <% if (service.pricing.priceRange) { %> +
+ <%= service.pricing.priceRange.min.toLocaleString() %>원 - + <%= service.pricing.priceRange.max.toLocaleString() %>원 +
+ <% } %> +
+ <% } %> + + + + + + <% if (service.featured) { %> +
+ + <%- __('services.cards.popular') %> + +
+ <% } %> +
+ <% }) %> + <% } else { %> +
+ +

<%- __('services.cards.coming_soon') %>

+

<%- __('services.cards.coming_soon_desc') %>

+
+ <% } %> +
+
+
+ + +
+
+
+

+ <%- __('services.process.title') %> +

+

+ <%- __('services.process.subtitle') %> +

+
+ +
+ +
+
+ 1 +
+

상담 및 기획

+

+ 고객의 요구사항을 정확히 파악하고 + 최적의 솔루션을 기획합니다 +

+
+ + +
+
+ 2 +
+

디자인 및 설계

+

+ 사용자 중심의 직관적인 디자인과 + 견고한 시스템 아키텍처를 설계합니다 +

+
+ + +
+
+ 3 +
+

개발 및 구현

+

+ 최신 기술과 모범 사례를 활용하여 + 효율적이고 확장 가능한 솔루션을 개발합니다 +

+
+ + +
+
+ 4 +
+

테스트 및 배포

+

+ 철저한 테스트를 통해 품질을 보장하고 + 안정적인 배포를 진행합니다 +

+
+
+
+
+ + +
+
+
+ +
+

+ 왜 SmartSolTech를 선택해야 할까요? +

+ +
+ +
+
+ +
+
+

최신 기술 활용

+

+ 항상 최신 기술 트렌드를 파악하고, 검증된 기술 스택을 활용하여 + 미래 지향적인 솔루션을 제공합니다. +

+
+
+ + +
+
+ +
+
+

전문가 팀

+

+ 각 분야의 전문가들로 구성된 팀이 협력하여 + 최고 품질의 결과물을 보장합니다. +

+
+
+ + +
+
+ +
+
+

빠른 대응

+

+ 신속한 커뮤니케이션과 효율적인 프로젝트 관리로 + 정해진 일정 내에 프로젝트를 완료합니다. +

+
+
+ + +
+
+ +
+
+

지속적인 지원

+

+ 프로젝트 완료 후에도 지속적인 유지보수와 기술 지원을 + 통해 장기적인 파트너십을 유지합니다. +

+
+
+
+
+ + +
+
+
+ +

품질 보장

+

+ 고객 만족을 위한
+ 최고 품질의 서비스 +

+
+
+
+
+
+
+ + +
+
+

+ 프로젝트를 시작할 준비가 되셨나요? +

+

+ 무료 상담을 통해 최적의 솔루션을 제안해드리겠습니다 +

+ +
+
+ + <%- include('partials/footer') %> + + + + + + \ No newline at end of file