Compare commits
10 Commits
c53f174959
...
Lite
| Author | SHA1 | Date | |
|---|---|---|---|
| 96cbfc69b5 | |||
| ec159f33f4 | |||
| f06d0f97aa | |||
| 78f7494790 | |||
| de32f37c97 | |||
| ca11fb9ac9 | |||
| 601a0082a0 | |||
| 49c5dd0035 | |||
| 70702268fb | |||
| 99e0abf45a |
@@ -1,101 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
Все значимые изменения в проекте Izipus документируются в этом файле.
|
|
||||||
|
|
||||||
Формат основан на [Keep a Changelog](https://keepachangelog.com/ru/1.0.0/),
|
|
||||||
версионирование следует [Semantic Versioning](https://semver.org/lang/ru/).
|
|
||||||
|
|
||||||
## [0.4.0] - 2025-10-26
|
|
||||||
|
|
||||||
### Добавлено
|
|
||||||
- 🎉 **Toast уведомления**: Визуальное подтверждение при копировании шаблонов с отображением счётчика использований
|
|
||||||
- 🔍 **Поиск по шаблонам**: Мгновенный поиск по названию и содержимому шаблонов (RU/EN)
|
|
||||||
- Автоматическое открытие категорий с найденными шаблонами
|
|
||||||
- Скрытие категорий без результатов
|
|
||||||
- 📤 **Экспорт шаблонов**: Сохранение пользовательских шаблонов в JSON файл
|
|
||||||
- Формат имени файла: `izipus-templates-YYYY-MM-DD.json`
|
|
||||||
- Валидация данных перед экспортом
|
|
||||||
- 📥 **Импорт шаблонов**: Загрузка шаблонов из JSON файла
|
|
||||||
- Автоматическая валидация структуры
|
|
||||||
- Предотвращение дубликатов по названию
|
|
||||||
- Информирование о количестве импортированных шаблонов
|
|
||||||
- 📊 **Счётчик использования**: Отслеживание частоты использования каждого шаблона
|
|
||||||
- Отображение в toast уведомлениях
|
|
||||||
- Раздельная статистика для встроенных и пользовательских шаблонов
|
|
||||||
- 🖊️ **Редактирование встроенных шаблонов**: Возможность переопределять встроенные шаблоны
|
|
||||||
- Система overrides с сохранением в chrome.storage
|
|
||||||
- Выбор языка при редактировании (RU/EN)
|
|
||||||
- 💾 **Chrome Storage Sync API**: Синхронизация данных между устройствами
|
|
||||||
- Автоматическая миграция из localStorage
|
|
||||||
- Fallback на localStorage для локального тестирования
|
|
||||||
- Синхронизация: шаблонов, статистики, темы, состояния категорий
|
|
||||||
- 🎨 **Улучшенный UI заголовка**: Группировка кнопок управления
|
|
||||||
- Кнопки добавления, импорта и экспорта объединены в header-buttons
|
|
||||||
|
|
||||||
### Изменено
|
|
||||||
- 📦 **Версия**: Обновлена с 0.3.1 до 0.4.0
|
|
||||||
- 🔄 **Storage API**: Все операции с хранилищем переведены на chrome.storage.sync
|
|
||||||
- `templates` - пользовательские шаблоны
|
|
||||||
- `builtinOverrides` - переопределения встроенных шаблонов
|
|
||||||
- `usageStats` - статистика использования
|
|
||||||
- `theme` - текущая тема
|
|
||||||
- `categoryStates` - состояния категорий
|
|
||||||
- ⚡ **Асинхронные операции**: Переход на async/await для всех операций хранения
|
|
||||||
- 🎯 **Header layout**: Изменён с `justify-content: center` на `space-between`
|
|
||||||
|
|
||||||
### Исправлено
|
|
||||||
- 🐛 **HTML опечатка**: Исправлен тег `</1--div>` на корректный `</div>` (строка 280 в popup.html)
|
|
||||||
- ✨ **Анимации**: Улучшены анимации toast уведомлений
|
|
||||||
- slideIn: плавное появление справа
|
|
||||||
- fadeOut: постепенное исчезновение через 2.7 секунды
|
|
||||||
|
|
||||||
### Технические детали
|
|
||||||
- Добавлен `StorageHelper` - универсальная обёртка для работы с хранилищем
|
|
||||||
- Функция `migrateFromLocalStorage()` для одноразовой миграции данных
|
|
||||||
- Новые CSS классы: `.toast-container`, `.toast`, `.search-wrapper`, `.hidden-by-search`, `.header-buttons`
|
|
||||||
- Улучшена структура кода с использованием async/await
|
|
||||||
|
|
||||||
### Производительность
|
|
||||||
- Оптимизирован поиск с использованием `includes()` вместо регулярных выражений
|
|
||||||
- Кэширование состояния категорий для быстрого доступа
|
|
||||||
- Ленивая загрузка шаблонов при импорте
|
|
||||||
|
|
||||||
## [0.3.1] - Предыдущая версия
|
|
||||||
|
|
||||||
### Возможности
|
|
||||||
- Базовая библиотека встроенных шаблонов (60+ шаблонов)
|
|
||||||
- Создание пользовательских шаблонов
|
|
||||||
- Организация по категориям:
|
|
||||||
- Промежуточные ответы
|
|
||||||
- Запросы партнерам
|
|
||||||
- Ответы клиентам
|
|
||||||
- Даофис (специфичные шаблоны)
|
|
||||||
- Тёмная/светлая тема
|
|
||||||
- Сворачивание категорий с сохранением состояния
|
|
||||||
- Копирование в буфер обмена одним кликом
|
|
||||||
- Редактирование и удаление пользовательских шаблонов
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Планы на будущее
|
|
||||||
|
|
||||||
### [0.5.0] - Фаза 2: Переменные и история
|
|
||||||
- Система переменных: `{{client_name}}`, `{{ticket_id}}`, `{{date}}`
|
|
||||||
- История последних использованных шаблонов
|
|
||||||
- Категория "Недавние" для быстрого доступа
|
|
||||||
- Графики и статистика популярности шаблонов
|
|
||||||
|
|
||||||
### [0.6.0] - Фаза 2: Интеграция
|
|
||||||
- Context menu для быстрого доступа
|
|
||||||
- Content script для вставки в текстовые поля
|
|
||||||
- Горячие клавиши для избранных шаблонов
|
|
||||||
|
|
||||||
### [1.0.0] - Стабильный релиз
|
|
||||||
- Полная локализация интерфейса
|
|
||||||
- Интеграция с Zendesk/Jira
|
|
||||||
- Облачная синхронизация для команд
|
|
||||||
- Полное тестовое покрытие
|
|
||||||
|
|
||||||
[0.4.0]: https://git.gorshenin.info/Dgors03/Answer_Templates/compare/v0.3.1...v0.4.0
|
|
||||||
[0.3.1]: https://git.gorshenin.info/Dgors03/Answer_Templates/releases/tag/v0.3.1
|
|
||||||
|
|
||||||
166
lzipus/README.md
166
lzipus/README.md
@@ -1,166 +0,0 @@
|
|||||||
# Izipus - SMS Support Templates Manager
|
|
||||||
|
|
||||||
**Version:** 0.4.0
|
|
||||||
**Type:** Browser Extension (Firefox/Chrome)
|
|
||||||
|
|
||||||
## 🎯 Описание
|
|
||||||
|
|
||||||
Izipus - браузерное расширение для специалистов технической поддержки SMS/телеком-операторов. Упрощает работу с шаблонами ответов на типовые запросы клиентов и партнёров.
|
|
||||||
|
|
||||||
## ✨ Основные возможности
|
|
||||||
|
|
||||||
### Фаза 1 (v0.4.0) - ГОТОВО ✅
|
|
||||||
|
|
||||||
#### 📝 Управление шаблонами
|
|
||||||
- **Библиотека готовых шаблонов** на русском и английском языках
|
|
||||||
- **Создание пользовательских шаблонов** с RU/EN версиями
|
|
||||||
- **Редактирование встроенных шаблонов** с сохранением переопределений
|
|
||||||
- **Импорт/экспорт шаблонов** в JSON для обмена с коллегами
|
|
||||||
- **Поиск по шаблонам** по названию и содержимому
|
|
||||||
|
|
||||||
#### 💾 Синхронизация
|
|
||||||
- **Chrome Storage Sync API** - автоматическая синхронизация между устройствами
|
|
||||||
- **Автоматическая миграция** данных из localStorage
|
|
||||||
- **Fallback на localStorage** для локального тестирования
|
|
||||||
|
|
||||||
#### 🎨 Пользовательский интерфейс
|
|
||||||
- **Toast уведомления** при копировании в буфер обмена
|
|
||||||
- **Тёмная/светлая тема** с сохранением выбора
|
|
||||||
- **Организация по категориям** с возможностью сворачивания
|
|
||||||
- **Счётчик использования** для каждого шаблона
|
|
||||||
- **Адаптивный дизайн**
|
|
||||||
|
|
||||||
## 🚀 Установка
|
|
||||||
|
|
||||||
### Firefox
|
|
||||||
1. Скачайте расширение
|
|
||||||
2. Откройте `about:debugging#/runtime/this-firefox`
|
|
||||||
3. Нажмите "Загрузить временное дополнение"
|
|
||||||
4. Выберите файл `manifest.json`
|
|
||||||
|
|
||||||
### Chrome/Edge
|
|
||||||
1. Скачайте расширение
|
|
||||||
2. Откройте `chrome://extensions/`
|
|
||||||
3. Включите "Режим разработчика"
|
|
||||||
4. Нажмите "Загрузить распакованное расширение"
|
|
||||||
5. Выберите папку с расширением
|
|
||||||
|
|
||||||
## 📖 Использование
|
|
||||||
|
|
||||||
### Основные действия
|
|
||||||
|
|
||||||
1. **Копирование шаблона**: Нажмите кнопку RU или ENG рядом с нужным шаблоном
|
|
||||||
2. **Создание шаблона**: Нажмите кнопку "+" в заголовке
|
|
||||||
3. **Редактирование**: Нажмите иконку карандаша рядом с шаблоном
|
|
||||||
4. **Удаление**: Нажмите иконку корзины
|
|
||||||
5. **Поиск**: Используйте строку поиска в верхней части окна
|
|
||||||
|
|
||||||
### Импорт/Экспорт
|
|
||||||
|
|
||||||
**Экспорт:**
|
|
||||||
- Нажмите кнопку экспорта (📤)
|
|
||||||
- Файл сохранится как `izipus-templates-YYYY-MM-DD.json`
|
|
||||||
|
|
||||||
**Импорт:**
|
|
||||||
- Нажмите кнопку импорта (📥)
|
|
||||||
- Выберите JSON файл с шаблонами
|
|
||||||
- Дубликаты (по названию) будут автоматически пропущены
|
|
||||||
|
|
||||||
### Редактирование встроенных шаблонов
|
|
||||||
|
|
||||||
1. Нажмите кнопку редактирования (🖊️) на встроенном шаблоне
|
|
||||||
2. Выберите язык (OK - RU, Отмена - EN)
|
|
||||||
3. Введите новый текст
|
|
||||||
4. Изменения сохраняются автоматически
|
|
||||||
|
|
||||||
## 📂 Структура проекта
|
|
||||||
|
|
||||||
```
|
|
||||||
lzipus/
|
|
||||||
├── manifest.json # Конфигурация расширения
|
|
||||||
├── icons/ # Иконки расширения
|
|
||||||
│ ├── icon16.png
|
|
||||||
│ ├── icon48.png
|
|
||||||
│ └── icon128.png
|
|
||||||
└── src/
|
|
||||||
├── popup.html # Интерфейс расширения
|
|
||||||
├── popup.css # Стили
|
|
||||||
└── popup.js # Логика приложения
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 Технологии
|
|
||||||
|
|
||||||
- **Manifest V3** - современная версия API расширений
|
|
||||||
- **Chrome Storage Sync API** - синхронизация данных
|
|
||||||
- **Bootstrap 5.3.1** - UI компоненты
|
|
||||||
- **Font Awesome 6.1.2** - иконки
|
|
||||||
- **Vanilla JavaScript** - без фреймворков
|
|
||||||
|
|
||||||
## 📊 Хранение данных
|
|
||||||
|
|
||||||
Все данные хранятся в `chrome.storage.sync`:
|
|
||||||
|
|
||||||
- `templates` - пользовательские шаблоны
|
|
||||||
- `builtinOverrides` - переопределения встроенных шаблонов
|
|
||||||
- `usageStats` - статистика использования
|
|
||||||
- `theme` - выбранная тема
|
|
||||||
- `categoryStates` - состояния категорий (открыто/закрыто)
|
|
||||||
|
|
||||||
## 🗺️ Дорожная карта
|
|
||||||
|
|
||||||
### Фаза 2: Новая функциональность
|
|
||||||
- [ ] Система переменных в шаблонах (`{{client_name}}`, `{{date}}`)
|
|
||||||
- [ ] Контекстное меню для быстрого доступа
|
|
||||||
- [ ] История использования и статистика
|
|
||||||
- [ ] Мультиязычность (ES, DE, FR)
|
|
||||||
|
|
||||||
### Фаза 3: Интеграция
|
|
||||||
- [ ] Интеграция с CRM/тикет-системами (Zendesk, Jira)
|
|
||||||
- [ ] AI-ассистент для предложения шаблонов
|
|
||||||
- [ ] Командные возможности (облачное хранилище)
|
|
||||||
|
|
||||||
### Фаза 4: Качество
|
|
||||||
- [ ] Рефакторинг (модули, TypeScript)
|
|
||||||
- [ ] Unit и E2E тесты
|
|
||||||
- [ ] CI/CD pipeline
|
|
||||||
- [ ] Полная документация
|
|
||||||
|
|
||||||
## 🤝 Вклад в проект
|
|
||||||
|
|
||||||
Проект открыт для улучшений! Если у вас есть идеи или вы нашли баг:
|
|
||||||
|
|
||||||
1. Создайте issue на GitLab
|
|
||||||
2. Предложите pull request
|
|
||||||
3. Опишите изменения и их необходимость
|
|
||||||
|
|
||||||
## 📝 Changelog
|
|
||||||
|
|
||||||
### v0.4.0 (2025-10-26) - Фаза 1
|
|
||||||
- ✅ Toast уведомления при копировании
|
|
||||||
- ✅ Поиск по шаблонам
|
|
||||||
- ✅ Импорт/экспорт в JSON
|
|
||||||
- ✅ Счётчик использования шаблонов
|
|
||||||
- ✅ Миграция на chrome.storage.sync
|
|
||||||
- ✅ Редактирование встроенных шаблонов
|
|
||||||
- 🐛 Исправлена опечатка в HTML
|
|
||||||
|
|
||||||
### v0.3.1
|
|
||||||
- Базовая функциональность
|
|
||||||
- Встроенные шаблоны
|
|
||||||
- Пользовательские шаблоны
|
|
||||||
- Тёмная тема
|
|
||||||
|
|
||||||
## 📄 Лицензия
|
|
||||||
|
|
||||||
Проект распространяется "как есть" для внутреннего использования.
|
|
||||||
|
|
||||||
## 👤 Автор
|
|
||||||
|
|
||||||
**Dmitriy Gorshenin**
|
|
||||||
Email: dmitriy.gorshenin1@gmail.com
|
|
||||||
GitLab: https://git.gorshenin.info/Dgors03/Answer_Templates
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Made with ❤️ for SMS Support Teams
|
|
||||||
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "Izipus",
|
"name": "Izipus Lite",
|
||||||
"version": "0.4.0",
|
"version": "0.3.1",
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"description": "Lzipus - assists your workflow by simplifying interactions.",
|
"description": "Lzipus Lite - assists your workflow by simplifying interactions.",
|
||||||
"homepage_url": "https://git.gorshenin.info/Dgors03/Answer_Templates",
|
"homepage_url": "https://git.gorshenin.info/Dgors03/Answer_Templates",
|
||||||
"icons": {
|
"icons": {
|
||||||
"16": "icons/icon16.png",
|
"16": "icons/icon16.png",
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
},
|
},
|
||||||
"action": {
|
"action": {
|
||||||
"default_icon": "icons/icon48.png",
|
"default_icon": "icons/icon48.png",
|
||||||
"default_title": "Izipus - Quick Actions",
|
"default_title": "Izipus Lite- Quick Actions",
|
||||||
"default_popup": "src/popup.html"
|
"default_popup": "src/popup.html"
|
||||||
},
|
},
|
||||||
"permissions": [
|
"permissions": [
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
],
|
],
|
||||||
"browser_specific_settings": {
|
"browser_specific_settings": {
|
||||||
"gecko": {
|
"gecko": {
|
||||||
"id": "dmitriy.gorshenin1@gmail.com"
|
"id": "dmitriy.gorshenin@gmail.com"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
165
lzipus/src/popup.css
Normal file → Executable file
165
lzipus/src/popup.css
Normal file → Executable file
@@ -8,28 +8,6 @@ body {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Плавное появление элементов */
|
|
||||||
@keyframes fadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Заголовки */
|
|
||||||
h1, h3 {
|
|
||||||
padding: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 16px; /* Уменьшен размер шрифта */
|
|
||||||
font-weight: normal; /* Уменьшен вес шрифта для минималистичного вида */
|
|
||||||
color: #333; /* Цвет шрифта для категорий */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Контейнеры */
|
|
||||||
div.container {
|
div.container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -40,17 +18,26 @@ div.container {
|
|||||||
div.header {
|
div.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: center;
|
||||||
align-items: center;
|
padding: 10px;
|
||||||
padding: 6px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-buttons {
|
h1 {
|
||||||
display: flex;
|
padding: 10px;
|
||||||
gap: 5px;
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
padding: 10px;
|
||||||
|
font-family: Helvetica, Arial, sans-serif; /* Более минималистичный шрифт для категорий */
|
||||||
|
font-size: 16px; /* Уменьшен размер шрифта */
|
||||||
|
font-weight: normal; /* Уменьшен вес шрифта для минималистичного вида */
|
||||||
|
color: #333; /* Цвет шрифта для категорий */
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
width: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Кнопки */
|
|
||||||
button.template-add,
|
button.template-add,
|
||||||
button.template-edit,
|
button.template-edit,
|
||||||
button.template-delete {
|
button.template-delete {
|
||||||
@@ -62,19 +49,19 @@ button.template-delete {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 14px; /* Меньший размер текста */
|
font-size: 14px; /* Меньший размер текста */
|
||||||
border-radius: 5px; /* Скругленные углы */
|
border-radius: 5px; /* Скругленные углы */
|
||||||
transition: background-color 0.3s, color 0.3s, border-color 0.3s, transform 0.3s; /* Плавные переходы */
|
transition: background-color 0.3s, color 0.3s, border-color 0.3s; /* Плавные переходы */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Эффекты для кнопок */
|
/* Эффект при наведении */
|
||||||
button.template-add:hover,
|
button.template-add:hover,
|
||||||
button.template-edit:hover,
|
button.template-edit:hover,
|
||||||
button.template-delete:hover {
|
button.template-delete:hover {
|
||||||
background-color: #abcef5; /* Подсветка фоном */
|
background-color: #abcef5; /* Подсветка фоном */
|
||||||
color: #fff; /* Белый цвет текста */
|
color: #fff; /* Белый цвет текста */
|
||||||
border-color: #88aee5; /* Немного темнее рамка */
|
border-color: #88aee5; /* Немного темнее рамка */
|
||||||
transform: scale(1.05); /* Увеличение кнопки при наведении */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Эффект при фокусе */
|
||||||
button.template-add:focus,
|
button.template-add:focus,
|
||||||
button.template-edit:focus,
|
button.template-edit:focus,
|
||||||
button.template-delete:focus {
|
button.template-delete:focus {
|
||||||
@@ -82,7 +69,6 @@ button.template-delete:focus {
|
|||||||
box-shadow: 0 0 3px rgba(0, 0, 0, 0.2); /* Легкая тень для фокуса */
|
box-shadow: 0 0 3px rgba(0, 0, 0, 0.2); /* Легкая тень для фокуса */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Шаблоны */
|
|
||||||
div.templates {
|
div.templates {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -97,16 +83,12 @@ span.template {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
padding: 8px 7px;
|
padding: 8px 7px;
|
||||||
justify-content: space-between;
|
|
||||||
height: fit-content;
|
|
||||||
padding: 8px 7px;
|
|
||||||
border: 1px solid #8888c6; /* Цвет рамки совпадает с рамкой категории */
|
border: 1px solid #8888c6; /* Цвет рамки совпадает с рамкой категории */
|
||||||
border-radius: 8px; /* Скругленные углы */
|
border-radius: 8px; /* Скругленные углы */
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%; /* Шаблоны должны растягиваться на всю ширину */
|
width: 100%; /* Шаблоны должны растягиваться на всю ширину */
|
||||||
box-sizing: border-box; /* Чтобы паддинги не влияли на размер */
|
box-sizing: border-box; /* Чтобы паддинги не влияли на размер */
|
||||||
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; /* Плавный переход при наведении */
|
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease; /* Плавный переход при наведении */
|
||||||
animation: fadeIn 0.5s ease-in; /* Применяем анимацию к элементам шаблона */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Подсветка шаблонов при наведении */
|
/* Подсветка шаблонов при наведении */
|
||||||
@@ -283,112 +265,3 @@ textarea {
|
|||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Toast уведомления */
|
|
||||||
.toast-container {
|
|
||||||
position: fixed;
|
|
||||||
top: 20px;
|
|
||||||
right: 20px;
|
|
||||||
z-index: 9999;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast {
|
|
||||||
background: #4CAF50;
|
|
||||||
color: white;
|
|
||||||
padding: 12px 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
min-width: 250px;
|
|
||||||
animation: slideIn 0.3s ease-out, fadeOut 0.3s ease-in 2.7s;
|
|
||||||
opacity: 0;
|
|
||||||
animation-fill-mode: forwards;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast.show {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast i {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast-message {
|
|
||||||
flex: 1;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slideIn {
|
|
||||||
from {
|
|
||||||
transform: translateX(400px);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: translateX(0);
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeOut {
|
|
||||||
from {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-theme .toast {
|
|
||||||
background: #388E3C;
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Поиск */
|
|
||||||
.search-wrapper {
|
|
||||||
position: relative;
|
|
||||||
margin: 10px 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-wrapper input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px 40px 10px 15px;
|
|
||||||
border: 1px solid #8888c6;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 14px;
|
|
||||||
transition: border-color 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-wrapper input:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: #a56fbf;
|
|
||||||
box-shadow: 0 0 5px rgba(165, 111, 191, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-icon {
|
|
||||||
position: absolute;
|
|
||||||
right: 15px;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
color: #8888c6;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-theme .search-wrapper input {
|
|
||||||
background: #444;
|
|
||||||
color: #fff;
|
|
||||||
border-color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark-theme .search-icon {
|
|
||||||
color: #aaa;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Скрытие элементов при поиске */
|
|
||||||
.hidden-by-search {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|||||||
136
lzipus/src/popup.html
Normal file → Executable file
136
lzipus/src/popup.html
Normal file → Executable file
@@ -11,101 +11,88 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="toast-container" id="toast-container"></div>
|
|
||||||
<button id="theme-toggle" class="theme-btn">
|
<button id="theme-toggle" class="theme-btn">
|
||||||
<i id="theme-icon" class="fa-moon fa-solid"></i>
|
<i id="theme-icon" class="fa-moon fa-solid"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1 class="name fs-4 fw-lighter font-monospace">Твои шаблоны</h1>
|
<h1 class="name fs-4 fw-lighter font-monospace">Твои шаблоны</h1>
|
||||||
<div class="header-buttons">
|
|
||||||
<button class="template-add btn btn-primary" type="button" title="Добавить шаблон">
|
|
||||||
<i class="fa-solid fa-plus"></i>
|
|
||||||
</button>
|
|
||||||
<button class="import-btn btn btn-secondary" type="button" title="Импортировать шаблоны">
|
|
||||||
<i class="fa-solid fa-file-import"></i>
|
|
||||||
</button>
|
|
||||||
<button class="export-btn btn btn-secondary" type="button" title="Экспортировать шаблоны">
|
|
||||||
<i class="fa-solid fa-file-export"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id="template-list">
|
||||||
|
|
||||||
<input type="file" id="import-file-input" accept=".json" style="display: none;">
|
|
||||||
|
|
||||||
<div class="search-wrapper">
|
|
||||||
<input type="text" id="search-input" class="form-control" placeholder="Поиск по шаблонам...">
|
|
||||||
<i class="fa-solid fa-search search-icon"></i>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="templates" id="template-list">
|
|
||||||
<div class="category">
|
<div class="category">
|
||||||
<h3 class="font-monospace">
|
<h3 class="font-monospace">
|
||||||
Промежуточные ответы
|
Промежуточные ответы
|
||||||
<i class="fa fa-chevron-down toggle-icon"></i>
|
|
||||||
|
|
||||||
</h3>
|
</h3>
|
||||||
<div class="category-content">
|
<div class="template-group">
|
||||||
<span class="template" data-builtin-id="inWork">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Приняли в работу</span>
|
<span class="template-title">Приняли в работу</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span class="btn btn-light" id="inWorkRu">RU</span>
|
<span class="btn btn-light" id="inWorkRu">RU</span>
|
||||||
<span class="btn btn-light" id="inWorkEn">ENG</span>
|
<span class="btn btn-light" id="inWorkEn">ENG</span>
|
||||||
<button class="template-edit-builtin btn"><i class="fa-solid fa-pen-to-square"></i></button>
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Начали диалог с ОС</span>
|
<span class="template-title">Начали диалог с ОС</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span class="btn btn-light" id="dialogueRu">RU</span>
|
<span class="btn btn-light" id="dialogueRu">RU</span>
|
||||||
<span class="btn btn-light" id="dialogueEn">ENG</span>
|
<span class="btn btn-light" id="dialogueEn">ENG</span>
|
||||||
<!-- <button class="template-edit-existed btn"><i class="fa-solid fa-pen-to-square"></i></button> -->
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Работаем над запросом</span>
|
<span class="template-title">Работаем над запросом</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="weworkRu" class="btn btn-light">RU</span>
|
<span id="weworkRu" class="btn btn-light">RU</span>
|
||||||
<span id="weworkEn" class="btn btn-light">ENG</span>
|
<span id="weworkEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Ждем ответа от оператора</span>
|
<span class="template-title">Ждем ответа от оператора</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="wework2Ru" class="btn btn-light">RU</span>
|
<span id="wework2Ru" class="btn btn-light">RU</span>
|
||||||
<span id="wework2En" class="btn btn-light">ENG</span>
|
<span id="wework2En" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Всё еще ждем ответа от оператора</span>
|
<span class="template-title">Всё еще ждем ответа от оператора</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="wework3Ru" class="btn btn-light">RU</span>
|
<span id="wework3Ru" class="btn btn-light">RU</span>
|
||||||
<span id="wework3En" class="btn btn-light">ENG</span>
|
<span id="wework3En" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Появилась ли информация?</span>
|
<span class="template-title">Появилась ли информация?</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="theyworkRu" class="btn btn-light">RU</span>
|
<span id="theyworkRu" class="btn btn-light">RU</span>
|
||||||
<span id="theyworkEn" class="btn btn-light">ENG</span>
|
<span id="theyworkEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Появился ли ответ?</span>
|
<span class="template-title">Появился ли ответ?</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="theywork2Ru" class="btn btn-light">RU</span>
|
<span id="theywork2Ru" class="btn btn-light">RU</span>
|
||||||
<span id="theywork2En" class="btn btn-light">ENG</span>
|
<span id="theywork2En" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Появились ли новости?</span>
|
<span class="template-title">Появились ли новости?</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="theywork3Ru" class="btn btn-light">RU</span>
|
<span id="theywork3Ru" class="btn btn-light">RU</span>
|
||||||
<span id="theywork3En" class="btn btn-light">ENG</span>
|
<span id="theywork3En" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Передали ответственной команде</span>
|
<span class="template-title">Передали ответственной команде</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="fwd2treadRU" class="btn btn-light">RU</span>
|
<span id="fwd2treadRU" class="btn btn-light">RU</span>
|
||||||
<span id="fwd2treadEn" class="btn btn-light">ENG</span>
|
<span id="fwd2treadEn" class="btn btn-light">ENG</span>
|
||||||
@@ -114,42 +101,46 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="category">
|
<div class="category">
|
||||||
<h3 class="font-monospace">
|
<h3 class="font-monospace">
|
||||||
Запросы партнерам
|
Запросы партнерам
|
||||||
<i class="fa fa-chevron-down toggle-icon"></i>
|
|
||||||
</h3>
|
</h3>
|
||||||
<div class="category-content">
|
<div class="template-group">
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Запрос: Недоставка</span>
|
<span class="template-title">Запрос: Недоставка</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="nonDelRu" class="btn btn-light">RU</span>
|
<span id="nonDelRu" class="btn btn-light">RU</span>
|
||||||
<span id="nonDelEn" class="btn btn-light">ENG</span>
|
<span id="nonDelEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Запрос: Фейк доставка</span>
|
<span class="template-title">Запрос: Фейк доставка</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="fakeRu" class="btn btn-light">RU</span>
|
<span id="fakeRu" class="btn btn-light">RU</span>
|
||||||
<span id="fakeEn" class="btn btn-light">ENG</span>
|
<span id="fakeEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Запрос: Задержки</span>
|
<span class="template-title">Запрос: Задержки</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="delayRu" class="btn btn-light">RU</span>
|
<span id="delayRu" class="btn btn-light">RU</span>
|
||||||
<span id="delayEn" class="btn btn-light">ENG</span>
|
<span id="delayEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Разрыв соединения</span>
|
<span class="template-title">Разрыв соединения</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="conbreRu" class="btn btn-light">RU</span>
|
<span id="conbreRu" class="btn btn-light">RU</span>
|
||||||
<span id="conbreEn" class="btn btn-light">ENG</span>
|
<span id="conbreEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Запрос: Корректность HLR</span>
|
<span class="template-title">Запрос: Корректность HLR</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="HLRRu" class="btn btn-light">RU</span>
|
<span id="HLRRu" class="btn btn-light">RU</span>
|
||||||
<span id="HLREn" class="btn btn-light">ENG</span>
|
<span id="HLREn" class="btn btn-light">ENG</span>
|
||||||
@@ -158,108 +149,122 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="category">
|
<div class="category">
|
||||||
<h3 class="font-monospace">
|
<h3 class="font-monospace">
|
||||||
Ответы клиентам
|
Ответы клиентам
|
||||||
<i class="fa fa-chevron-down toggle-icon"></i>
|
|
||||||
</h3>
|
</h3>
|
||||||
<div class="category-content">
|
<div class="template-group">
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Не фиксируем на платформе</span>
|
<span class="template-title">Не фиксируем на платформе</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="fixItRu" class="btn btn-light">RU</span>
|
<span id="fixItRu" class="btn btn-light">RU</span>
|
||||||
<span id="fixItEn" class="btn btn-light">ENG</span>
|
<span id="fixItEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Все доставлено</span>
|
<span class="template-title">Все доставлено</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="delivedRu" class="btn btn-light">RU</span>
|
<span id="delivedRu" class="btn btn-light">RU</span>
|
||||||
<span id="delivedEn" class="btn btn-light">ENG</span>
|
<span id="delivedEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Проблемы с ТА</span>
|
<span class="template-title">Проблемы с ТА</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="problphRu" class="btn btn-light">RU</span>
|
<span id="problphRu" class="btn btn-light">RU</span>
|
||||||
<span id="problphEn" class="btn btn-light">ENG</span>
|
<span id="problphEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">501: Не обслуживается</span>
|
<span class="template-title">501: Не обслуживается</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="notservRu" class="btn btn-light">RU</span>
|
<span id="notservRu" class="btn btn-light">RU</span>
|
||||||
<span id="notservEn" class="btn btn-light">ENG</span>
|
<span id="notservEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">В статусе отправлено</span>
|
<span class="template-title">В статусе отправлено</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="insentRu" class="btn btn-light">RU</span>
|
<span id="insentRu" class="btn btn-light">RU</span>
|
||||||
<span id="insentEn" class="btn btn-light">ENG</span>
|
<span id="insentEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">132: Дубликаты</span>
|
<span class="template-title">132: Дубликаты</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="dubleRu" class="btn btn-light">RU</span>
|
<span id="dubleRu" class="btn btn-light">RU</span>
|
||||||
<span id="dubleEn" class="btn btn-light">ENG</span>
|
<span id="dubleEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">508: Недоступен</span>
|
<span class="template-title">508: Недоступен</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="unvelbRu" class="btn btn-light">RU</span>
|
<span id="unvelbRu" class="btn btn-light">RU</span>
|
||||||
<span id="unvelbEn" class="btn btn-light">ENG</span>
|
<span id="unvelbEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Корректировка маршрутизации</span>
|
<span class="template-title">Корректировка маршрутизации</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="corroutRu" class="btn btn-light">RU</span>
|
<span id="corroutRu" class="btn btn-light">RU</span>
|
||||||
<span id="corroutEn" class="btn btn-light">ENG</span>
|
<span id="corroutEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Технические неполадки</span>
|
<span class="template-title">Технические неполадки</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="tehtroubRu" class="btn btn-light">RU</span>
|
<span id="tehtroubRu" class="btn btn-light">RU</span>
|
||||||
<span id="tehtroubEn" class="btn btn-light">ENG</span>
|
<span id="tehtroubEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Регистрации сендеров</span>
|
<span class="template-title">Регистрации сендеров</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="senderRu" class="btn btn-light">RU</span>
|
<span id="senderRu" class="btn btn-light">RU</span>
|
||||||
<span id="senderEn" class="btn btn-light">ENG</span>
|
<span id="senderEn" class="btn btn-light">ENG</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Block list by MSISDN</span>
|
<span class="template-title">Block list by MSISDN</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="blockMsisdnRu" class="btn btn-light">RU</span>
|
<span id="blockMsisdnRu" class="btn btn-light">RU</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Общее имя</span>
|
<span class="template-title">Общее имя</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="commonSenderRu" class="btn btn-light">RU</span>
|
<span id="commonSenderRu" class="btn btn-light">RU</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Нет ответа</span>
|
<span class="template-title">Нет ответа</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="noResponse" class="btn btn-light">RU</span>
|
<span id="noResponse" class="btn btn-light">RU</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Провели тестирование</span>
|
<span class="template-title">Провели тестирование</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="conductedTesting" class="btn btn-light">RU</span>
|
<span id="conductedTesting" class="btn btn-light">RU</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Информация менеджеру</span>
|
<span class="template-title">Информация менеджеру</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="managerTemplate" class="btn btn-light">RU</span>
|
<span id="managerTemplate" class="btn btn-light">RU</span>
|
||||||
</span>
|
</span>
|
||||||
@@ -267,26 +272,27 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="category">
|
<div class="category">
|
||||||
<h3 class="font-monospace">
|
<h3 class="font-monospace">
|
||||||
Даофис
|
Даофис
|
||||||
<i class="fa fa-chevron-down toggle-icon"></i>
|
|
||||||
</h3>
|
</h3>
|
||||||
<div class="category-content">
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Внести номер телефона в аксапту</span>
|
<span class="template-title">Внести номер телефона в аксапту</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="daoffice1RU" class="btn btn-light">RU</span>
|
<span id="daoffice1RU" class="btn btn-light">RU</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Учетка в порядке</span>
|
<span class="template-title">Учетка в порядке</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="officeRU" class="btn btn-light">RU</span>
|
<span id="officeRU" class="btn btn-light">RU</span>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span class="template">
|
<span class="template">
|
||||||
<span class="template-title font-monospace">Нет ответа</span>
|
<span class="template-title">Нет ответа</span>
|
||||||
<span class="buttons">
|
<span class="buttons">
|
||||||
<span id="notResponseRU" class="btn btn-light">RU</span>
|
<span id="notResponseRU" class="btn btn-light">RU</span>
|
||||||
<span id="notResponseEn" class="btn btn-light">ENG</span>
|
<span id="notResponseEn" class="btn btn-light">ENG</span>
|
||||||
@@ -295,7 +301,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<script src="popup.js"></script>
|
<script src="popup.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
503
lzipus/src/popup.js
Normal file → Executable file
503
lzipus/src/popup.js
Normal file → Executable file
@@ -1,124 +1,6 @@
|
|||||||
document.addEventListener("DOMContentLoaded", async () => {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
// Storage Helper - обёртка для chrome.storage.sync с fallback на localStorage
|
|
||||||
const StorageHelper = {
|
|
||||||
async get(key) {
|
|
||||||
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.sync) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
chrome.storage.sync.get([key], (result) => {
|
|
||||||
resolve(result[key]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Fallback для локального тестирования
|
|
||||||
const value = localStorage.getItem(key);
|
|
||||||
return value ? JSON.parse(value) : null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async set(key, value) {
|
|
||||||
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.sync) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
chrome.storage.sync.set({ [key]: value }, resolve);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Fallback для локального тестирования
|
|
||||||
localStorage.setItem(key, JSON.stringify(value));
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async remove(key) {
|
|
||||||
if (typeof chrome !== 'undefined' && chrome.storage && chrome.storage.sync) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
chrome.storage.sync.remove(key, resolve);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem(key);
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Миграция данных из localStorage в chrome.storage.sync
|
|
||||||
async function migrateFromLocalStorage() {
|
|
||||||
const migrationKey = 'migrated_to_chrome_storage';
|
|
||||||
const alreadyMigrated = await StorageHelper.get(migrationKey);
|
|
||||||
|
|
||||||
if (!alreadyMigrated && typeof chrome !== 'undefined' && chrome.storage && chrome.storage.sync) {
|
|
||||||
console.log('Начинаем миграцию данных из localStorage в chrome.storage.sync...');
|
|
||||||
|
|
||||||
const keysToMigrate = ['templates', 'theme', 'usageStats'];
|
|
||||||
|
|
||||||
for (const key of keysToMigrate) {
|
|
||||||
const localValue = localStorage.getItem(key);
|
|
||||||
if (localValue) {
|
|
||||||
try {
|
|
||||||
const parsedValue = JSON.parse(localValue);
|
|
||||||
await StorageHelper.set(key, parsedValue);
|
|
||||||
console.log(`Мигрирован ключ: ${key}`);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`Ошибка при миграции ключа ${key}:`, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Миграция состояний категорий
|
|
||||||
const categoryStates = {};
|
|
||||||
for (let i = 0; i < localStorage.length; i++) {
|
|
||||||
const key = localStorage.key(i);
|
|
||||||
if (key && !keysToMigrate.includes(key)) {
|
|
||||||
const value = localStorage.getItem(key);
|
|
||||||
if (value === 'true' || value === 'false') {
|
|
||||||
categoryStates[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(categoryStates).length > 0) {
|
|
||||||
await StorageHelper.set('categoryStates', categoryStates);
|
|
||||||
console.log('Мигрированы состояния категорий');
|
|
||||||
}
|
|
||||||
|
|
||||||
await StorageHelper.set(migrationKey, true);
|
|
||||||
console.log('Миграция завершена!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await migrateFromLocalStorage();
|
|
||||||
|
|
||||||
// Статистика использования шаблонов
|
|
||||||
let usageStats = await StorageHelper.get('usageStats') || {};
|
|
||||||
|
|
||||||
async function updateUsageStats(templateId) {
|
|
||||||
usageStats[templateId] = (usageStats[templateId] || 0) + 1;
|
|
||||||
await StorageHelper.set('usageStats', usageStats);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getUsageCount(templateId) {
|
|
||||||
return usageStats[templateId] || 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toast уведомления
|
|
||||||
function showToast(message, duration = 3000) {
|
|
||||||
const toastContainer = document.getElementById('toast-container');
|
|
||||||
const toast = document.createElement('div');
|
|
||||||
toast.className = 'toast show';
|
|
||||||
toast.innerHTML = `
|
|
||||||
<i class="fa-solid fa-check-circle"></i>
|
|
||||||
<span class="toast-message">${message}</span>
|
|
||||||
`;
|
|
||||||
toastContainer.appendChild(toast);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
toast.remove();
|
|
||||||
}, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Загружаем переопределения встроенных шаблонов
|
|
||||||
let builtinOverrides = await StorageHelper.get('builtinOverrides') || {};
|
|
||||||
|
|
||||||
// Объект с текстами для каждого элемента
|
// Объект с текстами для каждого элемента
|
||||||
let clipboardTexts = {
|
const clipboardTexts = {
|
||||||
"inWorkRu": "Коллеги, здравствуйте.\nПриняли Ваш запрос в работу. Сообщим по мере поступления информации.",
|
"inWorkRu": "Коллеги, здравствуйте.\nПриняли Ваш запрос в работу. Сообщим по мере поступления информации.",
|
||||||
"inWorkEn": "Dear customer,\nWe are working on your request.",
|
"inWorkEn": "Dear customer,\nWe are working on your request.",
|
||||||
"dialogueRu": "Коллеги, здравствуйте.\nУведомляем вас о том, что мы начали диалог с оператором по данному запросу. Мы ожидаем ответа и будем держать вас в курсе любых изменений.",
|
"dialogueRu": "Коллеги, здравствуйте.\nУведомляем вас о том, что мы начали диалог с оператором по данному запросу. Мы ожидаем ответа и будем держать вас в курсе любых изменений.",
|
||||||
@@ -178,58 +60,10 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|||||||
"managerTemplate": "Страна: \nОператор: \nmccMnc: \nСендер: \nКлиент: \nSource node: \nDestination node: "
|
"managerTemplate": "Страна: \nОператор: \nmccMnc: \nСендер: \nКлиент: \nSource node: \nDestination node: "
|
||||||
};
|
};
|
||||||
|
|
||||||
// Применяем переопределения
|
|
||||||
Object.keys(builtinOverrides).forEach(key => {
|
|
||||||
if (clipboardTexts.hasOwnProperty(key)) {
|
|
||||||
clipboardTexts[key] = builtinOverrides[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Функция для редактирования встроенных шаблонов
|
|
||||||
async function editBuiltinTemplate(templateId) {
|
|
||||||
const newText = prompt('Введите новый текст шаблона:', clipboardTexts[templateId]);
|
|
||||||
if (newText !== null && newText !== clipboardTexts[templateId]) {
|
|
||||||
clipboardTexts[templateId] = newText;
|
|
||||||
builtinOverrides[templateId] = newText;
|
|
||||||
await StorageHelper.set('builtinOverrides', builtinOverrides);
|
|
||||||
showToast('Встроенный шаблон обновлён!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Обработчики для кнопок редактирования встроенных шаблонов
|
|
||||||
document.querySelectorAll('.template-edit-builtin').forEach(btn => {
|
|
||||||
const template = btn.closest('.template');
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
// Находим RU и EN кнопки
|
|
||||||
const ruBtn = template.querySelector('[id$="Ru"]');
|
|
||||||
const enBtn = template.querySelector('[id$="En"]');
|
|
||||||
|
|
||||||
if (ruBtn || enBtn) {
|
|
||||||
const templateName = template.querySelector('.template-title').textContent.trim();
|
|
||||||
const action = confirm(`Редактировать шаблон "${templateName}"?\nОК - Редактировать RU\nОтмена - Редактировать EN`);
|
|
||||||
|
|
||||||
if (action && ruBtn) {
|
|
||||||
editBuiltinTemplate(ruBtn.id);
|
|
||||||
} else if (!action && enBtn) {
|
|
||||||
editBuiltinTemplate(enBtn.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Функция для обработки кликов
|
// Функция для обработки кликов
|
||||||
function handleClipboardClick(buttonId) {
|
function handleClipboardClick(buttonId) {
|
||||||
if (clipboardTexts[buttonId]) {
|
if (clipboardTexts[buttonId]) {
|
||||||
navigator.clipboard.writeText(clipboardTexts[buttonId])
|
navigator.clipboard.writeText(clipboardTexts[buttonId]);
|
||||||
.then(() => {
|
|
||||||
updateUsageStats(buttonId);
|
|
||||||
const count = getUsageCount(buttonId);
|
|
||||||
showToast(`Текст скопирован! (Использований: ${count})`);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error('Не удалось скопировать текст: ', err);
|
|
||||||
showToast('Ошибка копирования');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,138 +74,14 @@ Object.keys(clipboardTexts).forEach(buttonId => {
|
|||||||
button.addEventListener("click", () => handleClipboardClick(buttonId));
|
button.addEventListener("click", () => handleClipboardClick(buttonId));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//---------------------------------------------------Создание новых шаблонов локально---------------------------------------------------------//
|
|
||||||
const templatesEl = document.querySelector('.templates');
|
|
||||||
const addBtn = document.querySelector('.template-add');
|
|
||||||
|
|
||||||
function createTemplate(title, RUText, ENText) {
|
|
||||||
const templateId = Date.now();
|
|
||||||
const templateEl = document.createElement('span');
|
|
||||||
templateEl.setAttribute('data-template-id', templateId);
|
|
||||||
templateEl.classList.add('template');
|
|
||||||
|
|
||||||
templateEl.innerHTML = `
|
|
||||||
<span id="template-title" class="font-monospace">${title}</span>
|
|
||||||
<div class="bigFormochka">
|
|
||||||
<div class="formochka">
|
|
||||||
<input id="template-title-input" class="hidden form-control-sm">
|
|
||||||
<textarea id="template-textareaRU" class="hidden form-control-lg border-2" rows="1" cols="30.5">${RUText}</textarea>
|
|
||||||
<textarea id="template-textareaEN" class="hidden form-control-lg border-2" rows="1" cols="30.5">${ENText}</textarea>
|
|
||||||
</div>
|
|
||||||
<span class="buttonsLong">
|
|
||||||
<span class="btn btn-light" data-ru-text="${RUText}" id="template-ru-text">RU</span>
|
|
||||||
<span class="btn btn-light" data-en-text="${ENText}" id="template-en-text">ENG</span>
|
|
||||||
<button class="template-edit btn"><i class="fa-solid fa-pen-to-square"></i></button>
|
|
||||||
<button class="template-delete btn"><i class="fa-solid fa-trash"></i></button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const editBtn = templateEl.querySelector('.template-edit');
|
|
||||||
const deleteBtn = templateEl.querySelector('.template-delete');
|
|
||||||
const titleEl = templateEl.querySelector('#template-title');
|
|
||||||
const textRUEl = templateEl.querySelector('#template-ru-text');
|
|
||||||
const textENEl = templateEl.querySelector('#template-en-text');
|
|
||||||
const titleInputEl = templateEl.querySelector('#template-title-input');
|
|
||||||
const textRUInputEl = templateEl.querySelector('#template-textareaRU');
|
|
||||||
const textENInputEl = templateEl.querySelector('#template-textareaEN');
|
|
||||||
|
|
||||||
// Функция для переключения видимости элементов
|
|
||||||
function toggleVisibility(...elements) {
|
|
||||||
elements.forEach(el => el.classList.toggle('hidden'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Обработчик для редактирования
|
|
||||||
editBtn.addEventListener('click', () => {
|
|
||||||
toggleVisibility(titleEl, textRUEl, textENEl, titleInputEl, textRUInputEl, textENInputEl);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Обработчик для удаления
|
|
||||||
deleteBtn.addEventListener('click', async () => {
|
|
||||||
const templates = await StorageHelper.get('templates') || [];
|
|
||||||
const templateIndex = templates.findIndex(template => template.title === titleEl.innerText);
|
|
||||||
if (templateIndex !== -1) {
|
|
||||||
templates.splice(templateIndex, 1);
|
|
||||||
await StorageHelper.set('templates', templates);
|
|
||||||
}
|
|
||||||
templateEl.remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Обработчики для изменений в полях
|
|
||||||
const handleChange = (key, valueEl, inputEl) => {
|
|
||||||
valueEl.innerText = inputEl.value;
|
|
||||||
navigator.clipboard.writeText(valueEl);
|
|
||||||
updateTemplateInLocalStorage(title, key, inputEl.value);
|
|
||||||
};
|
|
||||||
|
|
||||||
titleInputEl.addEventListener('change', (e) => handleChange('title', titleEl, e.target));
|
|
||||||
textRUInputEl.addEventListener('change', (e) => handleChange('RUText', textRUEl, e.target));
|
|
||||||
textENInputEl.addEventListener('change', (e) => handleChange('ENText', textENEl, e.target));
|
|
||||||
|
|
||||||
// Обработчик для копирования текста в буфер обмена
|
|
||||||
const handleCopyText = (e, key) => {
|
|
||||||
const templateId = `custom-${title}-${key}`;
|
|
||||||
navigator.clipboard.writeText(e.target.dataset[key])
|
|
||||||
.then(() => {
|
|
||||||
updateUsageStats(templateId);
|
|
||||||
const count = getUsageCount(templateId);
|
|
||||||
showToast(`Текст скопирован! (Использований: ${count})`);
|
|
||||||
console.log(`Текст ${key} скопирован в буфер обмена`);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error(`Не удалось скопировать текст ${key}: `, err);
|
|
||||||
showToast('Ошибка копирования');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
textRUEl.addEventListener('click', (e) => handleCopyText(e, 'ruText'));
|
|
||||||
textENEl.addEventListener('click', (e) => handleCopyText(e, 'enText'));
|
|
||||||
|
|
||||||
return templateEl;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveTemplateToLocalStorage(title, RUText, ENText) {
|
|
||||||
const template = { title, RUText, ENText };
|
|
||||||
const templates = await StorageHelper.get('templates') || [];
|
|
||||||
templates.push(template);
|
|
||||||
await StorageHelper.set('templates', templates);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateTemplateInLocalStorage(title, key, value) {
|
|
||||||
const templates = await StorageHelper.get('templates') || [];
|
|
||||||
const templateIndex = templates.findIndex(template => template.title === title);
|
|
||||||
if (templateIndex !== -1) {
|
|
||||||
templates[templateIndex][key] = value;
|
|
||||||
await StorageHelper.set('templates', templates);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addBtn.addEventListener('click', () => {
|
|
||||||
const title = "Title";
|
|
||||||
const RUText = "RUText";
|
|
||||||
const ENText = "ENText";
|
|
||||||
|
|
||||||
const el = createTemplate(title, RUText, ENText);
|
|
||||||
templatesEl.appendChild(el);
|
|
||||||
saveTemplateToLocalStorage(title, RUText, ENText);
|
|
||||||
});
|
|
||||||
|
|
||||||
window.onload = async function () {
|
|
||||||
const templates = await StorageHelper.get('templates') || [];
|
|
||||||
templates.forEach(template => {
|
|
||||||
const el = createTemplate(template.title, template.RUText, template.ENText);
|
|
||||||
templatesEl.appendChild(el);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
//--------------------------------------------------------------------------------------------------------------------------------//
|
|
||||||
|
|
||||||
// Тема
|
// Тема
|
||||||
const themeToggle = document.getElementById('theme-toggle');
|
const themeToggle = document.getElementById('theme-toggle');
|
||||||
const themeIcon = document.getElementById('theme-icon');
|
const themeIcon = document.getElementById('theme-icon');
|
||||||
|
|
||||||
// Применение сохраненной темы
|
// Применение сохраненной темы
|
||||||
const applySavedTheme = async () => {
|
const applySavedTheme = () => {
|
||||||
const savedTheme = await StorageHelper.get('theme');
|
const savedTheme = localStorage.getItem('theme');
|
||||||
if (savedTheme === 'dark') {
|
if (savedTheme === 'dark') {
|
||||||
document.body.classList.add('dark-theme');
|
document.body.classList.add('dark-theme');
|
||||||
if (themeIcon) {
|
if (themeIcon) {
|
||||||
@@ -387,10 +97,10 @@ const applySavedTheme = async () => {
|
|||||||
|
|
||||||
// Переключение темы
|
// Переключение темы
|
||||||
if (themeToggle && themeIcon) {
|
if (themeToggle && themeIcon) {
|
||||||
themeToggle.addEventListener('click', async () => {
|
themeToggle.addEventListener('click', () => {
|
||||||
document.body.classList.toggle('dark-theme');
|
document.body.classList.toggle('dark-theme');
|
||||||
const theme = document.body.classList.contains('dark-theme') ? 'dark' : 'light';
|
const theme = document.body.classList.contains('dark-theme') ? 'dark' : 'light';
|
||||||
await StorageHelper.set('theme', theme); // Сохраняем выбранную тему
|
localStorage.setItem('theme', theme); // Сохраняем выбранную тему
|
||||||
themeIcon.classList.replace(theme === 'dark' ? 'fa-moon' : 'fa-sun', theme === 'dark' ? 'fa-sun' : 'fa-moon');
|
themeIcon.classList.replace(theme === 'dark' ? 'fa-moon' : 'fa-sun', theme === 'dark' ? 'fa-sun' : 'fa-moon');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -398,203 +108,4 @@ if (themeToggle && themeIcon) {
|
|||||||
// Применить сохраненную тему при загрузке страницы
|
// Применить сохраненную тему при загрузке страницы
|
||||||
applySavedTheme();
|
applySavedTheme();
|
||||||
|
|
||||||
// Управление категориями
|
|
||||||
document.querySelectorAll('.category h3').forEach(async function(header) {
|
|
||||||
const categoryId = header.textContent.trim(); // Уникальный идентификатор категории по названию
|
|
||||||
const content = header.nextElementSibling; // Содержимое категории
|
|
||||||
const icon = header.querySelector('.toggle-icon'); // Иконка стрелки
|
|
||||||
|
|
||||||
// Проверка состояния категории при загрузке страницы
|
|
||||||
const categoryStates = await StorageHelper.get('categoryStates') || {};
|
|
||||||
const isCategoryOpen = categoryStates[categoryId] === 'true';
|
|
||||||
|
|
||||||
if (isCategoryOpen) {
|
|
||||||
content.classList.add('show');
|
|
||||||
if (icon) {
|
|
||||||
icon.classList.replace('fa-chevron-down', 'fa-chevron-up');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
content.classList.remove('show');
|
|
||||||
if (icon) {
|
|
||||||
icon.classList.replace('fa-chevron-up', 'fa-chevron-down');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Обработчик клика по заголовку категории
|
|
||||||
header.addEventListener('click', async function() {
|
|
||||||
content.classList.toggle('show'); // Переключаем видимость категории
|
|
||||||
icon.classList.toggle('fa-chevron-down');
|
|
||||||
icon.classList.toggle('fa-chevron-up');
|
|
||||||
// Сохраняем состояние категории как открытое или закрытое
|
|
||||||
const categoryStates = await StorageHelper.get('categoryStates') || {};
|
|
||||||
categoryStates[categoryId] = content.classList.contains('show') ? 'true' : 'false';
|
|
||||||
await StorageHelper.set('categoryStates', categoryStates);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Импорт/Экспорт шаблонов
|
|
||||||
const exportBtn = document.querySelector('.export-btn');
|
|
||||||
const importBtn = document.querySelector('.import-btn');
|
|
||||||
const importFileInput = document.getElementById('import-file-input');
|
|
||||||
|
|
||||||
if (exportBtn) {
|
|
||||||
exportBtn.addEventListener('click', async () => {
|
|
||||||
const templates = await StorageHelper.get('templates') || [];
|
|
||||||
const dataStr = JSON.stringify(templates, null, 2);
|
|
||||||
const dataBlob = new Blob([dataStr], { type: 'application/json' });
|
|
||||||
|
|
||||||
const url = URL.createObjectURL(dataBlob);
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = url;
|
|
||||||
link.download = `izipus-templates-${new Date().toISOString().split('T')[0]}.json`;
|
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
|
||||||
document.body.removeChild(link);
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
|
|
||||||
showToast(`Экспортировано ${templates.length} шаблонов`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (importBtn && importFileInput) {
|
|
||||||
importBtn.addEventListener('click', () => {
|
|
||||||
importFileInput.click();
|
|
||||||
});
|
|
||||||
|
|
||||||
importFileInput.addEventListener('change', (e) => {
|
|
||||||
const file = e.target.files[0];
|
|
||||||
if (!file) return;
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = async (event) => {
|
|
||||||
try {
|
|
||||||
const importedTemplates = JSON.parse(event.target.result);
|
|
||||||
|
|
||||||
if (!Array.isArray(importedTemplates)) {
|
|
||||||
showToast('Неверный формат файла');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Валидация шаблонов
|
|
||||||
const validTemplates = importedTemplates.filter(t =>
|
|
||||||
t.title && typeof t.title === 'string' &&
|
|
||||||
t.RUText && typeof t.RUText === 'string' &&
|
|
||||||
t.ENText && typeof t.ENText === 'string'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (validTemplates.length === 0) {
|
|
||||||
showToast('Нет валидных шаблонов для импорта');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаем существующие шаблоны
|
|
||||||
const existingTemplates = await StorageHelper.get('templates') || [];
|
|
||||||
|
|
||||||
// Объединяем шаблоны (избегаем дубликатов по названию)
|
|
||||||
const existingTitles = new Set(existingTemplates.map(t => t.title));
|
|
||||||
const newTemplates = validTemplates.filter(t => !existingTitles.has(t.title));
|
|
||||||
|
|
||||||
if (newTemplates.length === 0) {
|
|
||||||
showToast('Все шаблоны уже существуют');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mergedTemplates = [...existingTemplates, ...newTemplates];
|
|
||||||
await StorageHelper.set('templates', mergedTemplates);
|
|
||||||
|
|
||||||
// Добавляем новые шаблоны на страницу
|
|
||||||
newTemplates.forEach(template => {
|
|
||||||
const el = createTemplate(template.title, template.RUText, template.ENText);
|
|
||||||
templatesEl.appendChild(el);
|
|
||||||
});
|
|
||||||
|
|
||||||
showToast(`Импортировано ${newTemplates.length} новых шаблонов`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Ошибка импорта:', error);
|
|
||||||
showToast('Ошибка при импорте файла');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Сбрасываем input
|
|
||||||
importFileInput.value = '';
|
|
||||||
};
|
|
||||||
|
|
||||||
reader.readAsText(file);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Поиск по шаблонам
|
|
||||||
const searchInput = document.getElementById('search-input');
|
|
||||||
if (searchInput) {
|
|
||||||
searchInput.addEventListener('input', function(e) {
|
|
||||||
const searchTerm = e.target.value.toLowerCase().trim();
|
|
||||||
const categories = document.querySelectorAll('.category');
|
|
||||||
|
|
||||||
if (searchTerm === '') {
|
|
||||||
// Если поиск пуст, показываем все шаблоны
|
|
||||||
categories.forEach(category => {
|
|
||||||
category.classList.remove('hidden-by-search');
|
|
||||||
const templates = category.querySelectorAll('.template');
|
|
||||||
templates.forEach(template => {
|
|
||||||
template.classList.remove('hidden-by-search');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ищем по всем категориям
|
|
||||||
categories.forEach(category => {
|
|
||||||
const templates = category.querySelectorAll('.template');
|
|
||||||
let hasVisibleTemplates = false;
|
|
||||||
|
|
||||||
templates.forEach(template => {
|
|
||||||
// Получаем название шаблона
|
|
||||||
const titleEl = template.querySelector('.template-title, #template-title');
|
|
||||||
const title = titleEl ? titleEl.textContent.toLowerCase() : '';
|
|
||||||
|
|
||||||
// Получаем содержимое шаблонов (RU и EN)
|
|
||||||
const ruBtn = template.querySelector('[id$="Ru"], [data-ru-text]');
|
|
||||||
const enBtn = template.querySelector('[id$="En"], [data-en-text]');
|
|
||||||
|
|
||||||
let ruText = '';
|
|
||||||
let enText = '';
|
|
||||||
|
|
||||||
if (ruBtn) {
|
|
||||||
ruText = (ruBtn.dataset.ruText || clipboardTexts[ruBtn.id] || '').toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enBtn) {
|
|
||||||
enText = (enBtn.dataset.enText || clipboardTexts[enBtn.id] || '').toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Проверяем совпадение
|
|
||||||
const matches = title.includes(searchTerm) ||
|
|
||||||
ruText.includes(searchTerm) ||
|
|
||||||
enText.includes(searchTerm);
|
|
||||||
|
|
||||||
if (matches) {
|
|
||||||
template.classList.remove('hidden-by-search');
|
|
||||||
hasVisibleTemplates = true;
|
|
||||||
} else {
|
|
||||||
template.classList.add('hidden-by-search');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Скрываем категорию, если в ней нет видимых шаблонов
|
|
||||||
if (hasVisibleTemplates) {
|
|
||||||
category.classList.remove('hidden-by-search');
|
|
||||||
// Открываем категорию при поиске
|
|
||||||
const content = category.querySelector('.category-content');
|
|
||||||
if (content && !content.classList.contains('show')) {
|
|
||||||
content.classList.add('show');
|
|
||||||
const icon = category.querySelector('.toggle-icon');
|
|
||||||
if (icon) {
|
|
||||||
icon.classList.replace('fa-chevron-down', 'fa-chevron-up');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
category.classList.add('hidden-by-search');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
Reference in New Issue
Block a user