feat: migrate alpine ui into app shell

This commit is contained in:
a.tolmachev
2026-03-30 00:15:02 +03:00
parent f33aa29c88
commit 7070231c3e
84 changed files with 12530 additions and 92 deletions
+289
View File
@@ -0,0 +1,289 @@
/* ═══════════════════════════════════════════════
Crank — i18n
Reads `crank_lang` from localStorage ('en'|'ru')
═══════════════════════════════════════════════ */
var TRANSLATIONS = {
en: {
// Nav
'nav.operations': 'Operations',
'nav.agents': 'Agents',
'nav.apikeys': 'API Keys',
'nav.logs': 'Logs',
'nav.usage': 'Usage',
'nav.settings': 'Settings',
'nav.logout': 'Log out',
// Operations page
'ops.title': 'Operations',
'ops.subtitle': 'Tool contracts that expose your upstream APIs as MCP tools',
'ops.new': 'New operation',
'ops.loading': 'Loading operations…',
'ops.empty.title': 'No operations found',
'ops.empty.sub': 'Try adjusting your search or filters.',
// Stats
'stats.total': 'Total operations',
'stats.active': 'Active',
'stats.requests': 'Requests today',
'stats.latency': 'Avg. latency',
// Tabs
'tab.all': 'All',
'tab.active': 'Active',
'tab.draft': 'Draft',
'tab.error': 'Error',
'tab.inactive': 'Inactive',
// Filters & sort
'filter.search': 'Search operations...',
'filter.protocol': 'Protocol',
'filter.status': 'Status',
'filter.category': 'Category',
'filter.showing': 'Showing',
'filter.of': 'of',
'filter.clear': 'Clear filter',
'sort.created_desc':'Sort: Last created',
'sort.created_asc': 'Sort: Oldest first',
'sort.name_asc': 'Sort: Name AZ',
'sort.name_desc': 'Sort: Name ZA',
// Table headers
'th.name': 'NAME',
'th.protocol': 'PROTOCOL',
'th.status': 'STATUS',
'th.created': 'CREATED',
'th.url': 'TARGET URL',
// Pagination
'page.showing': 'Showing',
'page.of': 'of',
'page.ops': 'operations',
// API Keys page
'apikeys.title': 'API Keys',
'apikeys.subtitle': 'Manage access tokens for external integrations',
'apikeys.new': 'Create key',
'apikeys.th.name': 'NAME',
'apikeys.th.scope': 'SCOPE',
'apikeys.th.created':'CREATED',
'apikeys.th.last': 'LAST USED',
// Logs page
'logs.title': 'Logs',
'logs.subtitle': 'Real-time request and event log',
'logs.search': 'Search logs...',
'logs.level.all': 'All',
'logs.level.info': 'Info',
'logs.level.warn': 'Warn',
'logs.level.error': 'Error',
'logs.level.debug': 'Debug',
// Usage page
'usage.title': 'Usage',
'usage.subtitle': 'Request volume and performance metrics',
// Settings sidebar
'settings.title': 'Settings',
'settings.nav.workspace': 'Workspace',
'settings.nav.profile': 'Profile',
'settings.nav.members': 'Members',
'settings.nav.security': 'Security',
'settings.nav.notif': 'Notifications',
'settings.nav.danger': 'Danger zone',
// Settings — workspace section
'settings.ws.title': 'Workspace settings',
'settings.ws.subtitle': 'General information about your workspace',
'settings.ws.name': 'Workspace name',
'settings.ws.display': 'Display name',
'settings.ws.desc': 'Description',
'settings.ws.tz': 'Timezone',
'settings.ws.protocol': 'Default protocol',
// Settings — language
'settings.lang.title': 'Language',
'settings.lang.subtitle': 'Interface display language',
'settings.lang.en': 'English',
'settings.lang.ru': 'Русский',
// Settings — profile section
'settings.profile.title': 'Profile',
'settings.profile.name': 'Full name',
'settings.profile.email': 'Email',
'settings.profile.role': 'Role',
// Common buttons
'btn.save': 'Save changes',
'btn.cancel': 'Cancel',
'btn.create': 'Create',
'btn.delete': 'Delete',
'btn.revoke': 'Revoke',
'btn.copy': 'Copy',
// Login
'login.title': 'Sign in',
'login.subtitle': 'Welcome back to your workspace',
'login.email': 'Email',
'login.password': 'Password',
'login.submit': 'Sign in',
'login.sso': 'Continue with Google',
},
ru: {
// Nav
'nav.operations': 'Операции',
'nav.agents': 'Агенты',
'nav.apikeys': 'API Ключи',
'nav.logs': 'Логи',
'nav.usage': 'Использование',
'nav.settings': 'Настройки',
'nav.logout': 'Выйти',
// Operations page
'ops.title': 'Операции',
'ops.subtitle': 'Контракты инструментов, открывающие ваши API как MCP-инструменты',
'ops.new': 'Новая операция',
'ops.loading': 'Загрузка операций…',
'ops.empty.title': 'Операции не найдены',
'ops.empty.sub': 'Попробуйте изменить поисковый запрос или фильтры.',
// Stats
'stats.total': 'Всего операций',
'stats.active': 'Активные',
'stats.requests': 'Запросов сегодня',
'stats.latency': 'Ср. задержка',
// Tabs
'tab.all': 'Все',
'tab.active': 'Активные',
'tab.draft': 'Черновик',
'tab.error': 'Ошибка',
'tab.inactive': 'Неактивные',
// Filters & sort
'filter.search': 'Поиск операций...',
'filter.protocol': 'Протокол',
'filter.status': 'Статус',
'filter.category': 'Категория',
'filter.showing': 'Показано',
'filter.of': 'из',
'filter.clear': 'Сбросить',
'sort.created_desc':'Сначала новые',
'sort.created_asc': 'Сначала старые',
'sort.name_asc': 'Название А–Я',
'sort.name_desc': 'Название Я–А',
// Table headers
'th.name': 'НАЗВАНИЕ',
'th.protocol': 'ПРОТОКОЛ',
'th.status': 'СТАТУС',
'th.created': 'СОЗДАН',
'th.url': 'ЦЕЛЕВОЙ URL',
// Pagination
'page.showing': 'Показано',
'page.of': 'из',
'page.ops': 'операций',
// API Keys page
'apikeys.title': 'API Ключи',
'apikeys.subtitle': 'Управление токенами доступа для внешних интеграций',
'apikeys.new': 'Создать ключ',
'apikeys.th.name': 'НАЗВАНИЕ',
'apikeys.th.scope': 'ОБЛАСТЬ',
'apikeys.th.created':'СОЗДАН',
'apikeys.th.last': 'ПОСЛЕДНЕЕ ИСПОЛЬЗОВАНИЕ',
// Logs page
'logs.title': 'Логи',
'logs.subtitle': 'Журнал запросов и событий в реальном времени',
'logs.search': 'Поиск в логах...',
'logs.level.all': 'Все',
'logs.level.info': 'Info',
'logs.level.warn': 'Warn',
'logs.level.error': 'Error',
'logs.level.debug': 'Debug',
// Usage page
'usage.title': 'Использование',
'usage.subtitle': 'Объём запросов и метрики производительности',
// Settings sidebar
'settings.title': 'Настройки',
'settings.nav.workspace': 'Воркспейс',
'settings.nav.profile': 'Профиль',
'settings.nav.members': 'Участники',
'settings.nav.security': 'Безопасность',
'settings.nav.notif': 'Уведомления',
'settings.nav.danger': 'Опасная зона',
// Settings — workspace section
'settings.ws.title': 'Настройки воркспейса',
'settings.ws.subtitle': 'Общая информация о вашем воркспейсе',
'settings.ws.name': 'Имя воркспейса',
'settings.ws.display': 'Отображаемое имя',
'settings.ws.desc': 'Описание',
'settings.ws.tz': 'Часовой пояс',
'settings.ws.protocol': 'Протокол по умолчанию',
// Settings — language
'settings.lang.title': 'Язык',
'settings.lang.subtitle': 'Язык отображения интерфейса',
'settings.lang.en': 'English',
'settings.lang.ru': 'Русский',
// Settings — profile section
'settings.profile.title': 'Профиль',
'settings.profile.name': 'Полное имя',
'settings.profile.email': 'Email',
'settings.profile.role': 'Роль',
// Common buttons
'btn.save': 'Сохранить',
'btn.cancel': 'Отмена',
'btn.create': 'Создать',
'btn.delete': 'Удалить',
'btn.revoke': 'Отозвать',
'btn.copy': 'Копировать',
// Login
'login.title': 'Войти',
'login.subtitle': 'Добро пожаловать обратно',
'login.email': 'Email',
'login.password': 'Пароль',
'login.submit': 'Войти',
'login.sso': 'Войти через Google',
}
};
function t(key) {
var lang = localStorage.getItem('crank_lang') || 'en';
var tr = TRANSLATIONS[lang] || TRANSLATIONS.en;
return tr[key] !== undefined ? tr[key] : (TRANSLATIONS.en[key] !== undefined ? TRANSLATIONS.en[key] : key);
}
function applyLang() {
// text content
document.querySelectorAll('[data-i18n]').forEach(function(el) {
el.textContent = t(el.getAttribute('data-i18n'));
});
// placeholder
document.querySelectorAll('[data-i18n-ph]').forEach(function(el) {
el.placeholder = t(el.getAttribute('data-i18n-ph'));
});
// highlight active language button in settings
var lang = localStorage.getItem('crank_lang') || 'en';
document.querySelectorAll('.lang-btn').forEach(function(btn) {
btn.classList.toggle('active', btn.dataset.lang === lang);
});
}
function setLang(lang) {
localStorage.setItem('crank_lang', lang);
applyLang();
// Notify Alpine components to re-render reactive getters
window.dispatchEvent(new CustomEvent('crank:langchange', { detail: { lang: lang } }));
}
document.addEventListener('DOMContentLoaded', applyLang);