ваше сообщение коммита

This commit is contained in:
2025-09-25 14:59:05 +03:00
parent 7b2f6937c8
commit ca718e3178
29 changed files with 4086 additions and 6110 deletions

View File

@@ -148,6 +148,40 @@
</div>
</div>
<!-- Операции модулей (динамические) -->
<div v-if="isLoadingModuleOperations" class="loading-modules">
Загрузка операций модулей...
</div>
<div
v-for="moduleOperation in moduleOperations"
:key="moduleOperation.moduleType"
class="operation-category"
>
<h5>{{ getModuleIcon(moduleOperation.moduleType) }} {{ moduleOperation.moduleName }}</h5>
<p class="module-description">{{ moduleOperation.moduleDescription }}</p>
<div class="operation-blocks">
<div
v-for="operation in moduleOperation.operations"
:key="operation.id"
class="operation-block module-operation-block"
>
<div class="operation-icon">{{ operation.icon }}</div>
<h6>{{ operation.name }}</h6>
<p>{{ operation.description }}</p>
<div class="operation-category-tag">{{ operation.category }}</div>
<button
class="create-btn"
@click="openModuleOperationForm(moduleOperation.moduleType, operation)"
:disabled="!props.isAuthenticated || isLoadingModuleOperations"
>
<span v-if="isLoadingModuleOperations">Загрузка...</span>
<span v-else>Создать</span>
</button>
</div>
</div>
</div>
<!-- Оффчейн операции -->
<div class="operation-category">
<h5>📋 Оффчейн операции</h5>
@@ -169,12 +203,13 @@
</template>
<script setup>
import { ref, computed, onMounted, defineProps, defineEmits, inject } from 'vue';
import { ref, computed, onMounted, onUnmounted, defineProps, defineEmits, inject } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { useAuthContext } from '../../composables/useAuth';
import BaseLayout from '../../components/BaseLayout.vue';
import { getDLEInfo, getSupportedChains } from '../../services/dleV2Service.js';
import { createProposal as createProposalAPI } from '../../services/proposalsService.js';
import { getModuleOperations } from '../../services/moduleOperationsService.js';
import api from '../../api/axios';
import wsClient from '../../utils/websocket.js';
import { ethers } from 'ethers';
@@ -221,6 +256,12 @@ const isLoadingDle = ref(false);
// Доступные цепочки (загружаются из конфигурации)
const availableChains = ref([]);
// Состояние модулей и их операций
const moduleOperations = ref([]);
const isLoadingModuleOperations = ref(false);
const modulesWebSocket = ref(null);
const isModulesWSConnected = ref(false);
// Функции для открытия отдельных форм операций
function openTransferForm() {
// TODO: Открыть форму для передачи токенов
@@ -273,6 +314,26 @@ function openOffchainActionForm() {
alert('Форма оффчейн действий будет реализована');
}
// Функция для создания предложения операции модуля
function openModuleOperationForm(moduleType, operation) {
console.log('[CreateProposalView] Открытие формы для операции модуля:', { moduleType, operation });
// TODO: Открыть форму для создания предложения операции модуля
// Пока показываем alert с информацией об операции
alert(`Создание предложения для операции "${operation.name}" модуля ${moduleType}.\n\nОписание: ${operation.description}\ункция: ${operation.functionName}\nКатегория: ${operation.category}`);
}
// Получить иконку для типа модуля
function getModuleIcon(moduleType) {
const icons = {
treasury: '💰',
timelock: '⏰',
reader: '📖',
hierarchicalVoting: '🗳️'
};
return icons[moduleType] || '🔧';
}
// Функции
async function loadDleData() {
console.log('loadDleData вызвана с адресом:', dleAddress.value);
@@ -300,6 +361,9 @@ async function loadDleData() {
const chainsResponse = await getSupportedChains(dleAddress.value);
availableChains.value = chainsResponse.data?.chains || [];
// Загружаем операции модулей
await loadModuleOperations();
} catch (error) {
console.error('Ошибка загрузки данных DLE из блокчейна:', error);
} finally {
@@ -307,6 +371,113 @@ async function loadDleData() {
}
}
// Загрузка операций модулей
async function loadModuleOperations() {
if (!dleAddress.value) {
console.warn('Адрес DLE не указан для загрузки операций модулей');
return;
}
isLoadingModuleOperations.value = true;
try {
console.log('[CreateProposalView] Загрузка операций модулей для DLE:', dleAddress.value);
const response = await getModuleOperations(dleAddress.value);
if (response.success) {
moduleOperations.value = response.data.moduleOperations || [];
console.log('[CreateProposalView] Загружены операции модулей:', moduleOperations.value);
} else {
console.error('[CreateProposalView] Ошибка загрузки операций модулей:', response.error);
moduleOperations.value = [];
}
} catch (error) {
console.error('[CreateProposalView] Ошибка загрузки операций модулей:', error);
moduleOperations.value = [];
} finally {
isLoadingModuleOperations.value = false;
}
}
// WebSocket функции для модулей
function connectModulesWebSocket() {
if (modulesWebSocket.value && modulesWebSocket.value.readyState === WebSocket.OPEN) {
return;
}
const wsUrl = `ws://localhost:8000/ws/deployment`;
modulesWebSocket.value = new WebSocket(wsUrl);
modulesWebSocket.value.onopen = () => {
console.log('[CreateProposalView] WebSocket модулей соединение установлено');
isModulesWSConnected.value = true;
// Подписываемся на обновления модулей для текущего DLE
if (dleAddress.value) {
modulesWebSocket.value.send(JSON.stringify({
type: 'subscribe',
dleAddress: dleAddress.value
}));
}
};
modulesWebSocket.value.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
handleModulesWebSocketMessage(data);
} catch (error) {
console.error('[CreateProposalView] Ошибка парсинга WebSocket сообщения модулей:', error);
}
};
modulesWebSocket.value.onclose = () => {
console.log('[CreateProposalView] WebSocket модулей соединение закрыто');
isModulesWSConnected.value = false;
// Переподключаемся через 5 секунд
setTimeout(() => {
connectModulesWebSocket();
}, 5000);
};
modulesWebSocket.value.onerror = (error) => {
console.error('[CreateProposalView] Ошибка WebSocket модулей:', error);
isModulesWSConnected.value = false;
};
}
function handleModulesWebSocketMessage(data) {
console.log('[CreateProposalView] WebSocket модулей сообщение:', data);
switch (data.type) {
case 'modules_updated':
// Автоматически обновляем список операций модулей
console.log('[CreateProposalView] Получено уведомление об обновлении модулей');
loadModuleOperations();
break;
case 'module_verified':
// Обновляем операции модуля
console.log(`[CreateProposalView] Модуль ${data.moduleType} верифицирован`);
loadModuleOperations();
break;
case 'module_status_changed':
// Обновляем операции модуля
console.log(`[CreateProposalView] Статус модуля ${data.moduleType} изменен`);
loadModuleOperations();
break;
}
}
function disconnectModulesWebSocket() {
if (modulesWebSocket.value) {
modulesWebSocket.value.close();
modulesWebSocket.value = null;
isModulesWSConnected.value = false;
}
}
onMounted(async () => {
// Принудительно загружаем токены, если пользователь аутентифицирован
if (isAuthenticated.value && address.value) {
@@ -318,6 +489,14 @@ onMounted(async () => {
if (dleAddress.value) {
loadDleData();
}
// Подключаемся к WebSocket для получения обновлений модулей
connectModulesWebSocket();
});
// Отключаем WebSocket при размонтировании компонента
onUnmounted(() => {
disconnectModulesWebSocket();
});
</script>
@@ -562,6 +741,91 @@ onMounted(async () => {
display: none;
}
/* Стили для модулей */
.module-description {
color: #666;
font-size: 0.9rem;
margin: 0.5rem 0 1rem 0;
font-style: italic;
}
.module-operation-block {
position: relative;
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
border: 2px solid #e9ecef;
}
.module-operation-block::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, #28a745, #20c997);
transform: scaleX(0);
transition: transform 0.3s ease;
}
.module-operation-block:hover::before {
transform: scaleX(1);
}
.operation-category-tag {
display: inline-block;
background: linear-gradient(135deg, #28a745, #20c997);
color: white;
padding: 0.25rem 0.75rem;
border-radius: 20px;
font-size: 0.75rem;
font-weight: 600;
margin: 0.5rem 0;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* Анимация появления модулей */
.operation-category {
animation: fadeInUp 0.6s ease-out;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Индикатор загрузки модулей */
.loading-modules {
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
color: #666;
font-style: italic;
}
.loading-modules::before {
content: '';
width: 20px;
height: 20px;
border: 2px solid #f3f3f3;
border-top: 2px solid var(--color-primary);
border-radius: 50%;
animation: spin 1s linear infinite;
margin-right: 0.5rem;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Адаптивность */
@media (max-width: 768px) {
.operations-blocks {

File diff suppressed because it is too large Load Diff

View File

@@ -1,329 +0,0 @@
<!--
Copyright (c) 2024-2025 Тарабанов Александр Викторович
All rights reserved.
This software is proprietary and confidential.
Unauthorized copying, modification, or distribution is prohibited.
For licensing inquiries: info@hb3-accelerator.com
Website: https://hb3-accelerator.com
GitHub: https://github.com/HB3-ACCELERATOR
-->
<template>
<BaseLayout
:is-authenticated="isAuthenticated"
:identities="identities"
:token-balances="tokenBalances"
:is-loading-tokens="isLoadingTokens"
@auth-action-completed="$emit('auth-action-completed')"
>
<div class="application-module-deploy">
<!-- Заголовок -->
<div class="page-header">
<div class="header-content">
<h1>Деплой ApplicationModule</h1>
<p>Управление вызовом функций приложения через предложения и голосование</p>
<p v-if="dleAddress" class="dle-address">
<strong>DLE:</strong> {{ dleAddress }}
</p>
</div>
<button class="close-btn" @click="router.push('/management/modules')">×</button>
</div>
<!-- Информация о модуле -->
<div class="module-info">
<div class="info-card">
<h3>🖥 ApplicationModule</h3>
<div class="info-grid">
<div class="info-item">
<strong>Назначение:</strong> Управление функциями приложения через DLE
</div>
<div class="info-item">
<strong>Функции:</strong> Создание предложений для вызова API, голосование за операции
</div>
<div class="info-item">
<strong>Безопасность:</strong> Все операции приложения через кворум токен-холдеров
</div>
<div class="info-item">
<strong>Примеры:</strong> Удаление пользователей, изменение настроек, обновление данных
</div>
</div>
</div>
</div>
<!-- Детальное описание -->
<div class="module-details">
<div class="details-card">
<h3>📋 Как работает ApplicationModule</h3>
<div class="details-content">
<div class="detail-step">
<div class="step-number">1</div>
<div class="step-content">
<h4>Создание предложения</h4>
<p>Токен-холдер создает предложение для выполнения операции в приложении (например, удаление пользователя, изменение настроек)</p>
</div>
</div>
<div class="detail-step">
<div class="step-number">2</div>
<div class="step-content">
<h4>Голосование</h4>
<p>Все токен-холдеры голосуют за или против предложения в течение установленного времени</p>
</div>
</div>
<div class="detail-step">
<div class="step-number">3</div>
<div class="step-content">
<h4>Исполнение</h4>
<p>При достижении кворума предложение исполняется - вызывается соответствующая функция приложения</p>
</div>
</div>
<div class="detail-step">
<div class="step-number">4</div>
<div class="step-content">
<h4>Аудит</h4>
<p>Все операции логируются в блокчейне для полной прозрачности и подотчетности</p>
</div>
</div>
</div>
</div>
</div>
<!-- Форма деплоя будет добавлена позже -->
<div class="deploy-form-placeholder">
<div class="placeholder-content">
<h3>🚧 Форма деплоя в разработке</h3>
<p>Здесь будет форма для деплоя ApplicationModule</p>
</div>
</div>
</div>
</BaseLayout>
</template>
<script setup>
import { defineProps, defineEmits, ref, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import BaseLayout from '../../../components/BaseLayout.vue';
// Определяем props
const props = defineProps({
isAuthenticated: { type: Boolean, default: false },
identities: { type: Array, default: () => [] },
tokenBalances: { type: Object, default: () => ({}) },
isLoadingTokens: { type: Boolean, default: false }
});
// Определяем emits
const emit = defineEmits(['auth-action-completed']);
const router = useRouter();
const route = useRoute();
// Состояние
const isLoading = ref(false);
const dleAddress = ref(route.query.address || null);
// Инициализация
onMounted(() => {
console.log('[ApplicationModuleDeployView] Страница загружена');
});
</script>
<style scoped>
.application-module-deploy {
padding: 20px;
background-color: var(--color-white);
border-radius: var(--radius-lg);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin-top: 20px;
margin-bottom: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #f0f0f0;
}
.page-header h1 {
color: var(--color-primary);
font-size: 2rem;
margin: 0;
}
.page-header p {
margin: 10px 0 0 0;
color: #666;
}
.dle-address {
margin-top: 10px !important;
font-family: monospace;
background: #f8f9fa;
padding: 8px 12px;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.close-btn {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: #666;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: all 0.2s;
}
.close-btn:hover {
background: #f0f0f0;
color: #333;
}
/* Информация о модуле */
.module-info {
margin-bottom: 30px;
}
.info-card {
background: #f8f9fa;
border-radius: var(--radius-md);
padding: 20px;
border: 1px solid #e9ecef;
}
.info-card h3 {
margin: 0 0 15px 0;
color: var(--color-primary);
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
}
.info-item {
padding: 10px;
background: white;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.info-item strong {
color: var(--color-primary);
}
/* Детальное описание */
.module-details {
margin-bottom: 30px;
}
.details-card {
background: #f8f9fa;
border-radius: var(--radius-md);
padding: 20px;
border: 1px solid #e9ecef;
}
.details-card h3 {
margin: 0 0 20px 0;
color: var(--color-primary);
}
.details-content {
display: flex;
flex-direction: column;
gap: 20px;
}
.detail-step {
display: flex;
align-items: flex-start;
gap: 15px;
padding: 15px;
background: white;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.step-number {
background: var(--color-primary);
color: white;
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 14px;
flex-shrink: 0;
}
.step-content h4 {
margin: 0 0 8px 0;
color: var(--color-primary);
font-size: 16px;
}
.step-content p {
margin: 0;
color: #666;
line-height: 1.5;
}
/* Плейсхолдер для формы */
.deploy-form-placeholder {
background: #f8f9fa;
border-radius: var(--radius-md);
padding: 40px;
text-align: center;
border: 2px dashed #dee2e6;
}
.placeholder-content h3 {
color: var(--color-primary);
margin-bottom: 10px;
}
.placeholder-content p {
color: #666;
margin: 0;
}
/* Адаптивность */
@media (max-width: 768px) {
.info-grid {
grid-template-columns: 1fr;
}
.page-header {
flex-direction: column;
align-items: flex-start;
gap: 15px;
}
.detail-step {
flex-direction: column;
text-align: center;
}
.step-number {
align-self: center;
}
}
</style>

View File

@@ -1,515 +0,0 @@
<!--
Copyright (c) 2024-2025 Тарабанов Александр Викторович
All rights reserved.
This software is proprietary and confidential.
Unauthorized copying, modification, or distribution is prohibited.
For licensing inquiries: info@hb3-accelerator.com
Website: https://hb3-accelerator.com
GitHub: https://github.com/HB3-ACCELERATOR
-->
<template>
<BaseLayout
:is-authenticated="isAuthenticated"
:identities="identities"
:token-balances="tokenBalances"
:is-loading-tokens="isLoadingTokens"
@auth-action-completed="$emit('auth-action-completed')"
>
<div class="module-deploy-page">
<!-- Заголовок -->
<div class="page-header">
<div class="header-content">
<h1>🔥 Деплой BurnModule</h1>
<p>Модуль для сжигания токенов DLE через governance</p>
<div v-if="selectedDle" class="dle-info">
<span class="dle-name">{{ selectedDle.name }} ({{ selectedDle.symbol }})</span>
<span class="dle-address">{{ selectedDle.dleAddress }}</span>
</div>
</div>
<button class="close-btn" @click="router.push(`/management/modules?address=${route.query.address}`)">×</button>
</div>
<!-- Описание модуля -->
<div class="module-description">
<div class="description-card">
<h3>📋 Описание BurnModule</h3>
<div class="description-content">
<p><strong>BurnModule</strong> - это модуль для управления сжиганием токенов DLE через систему governance.</p>
<h4>🔧 Функциональность:</h4>
<ul>
<li><strong>Сжигание токенов:</strong> Уменьшение общего предложения токенов DLE</li>
<li><strong>Governance:</strong> Все операции требуют голосования и кворума</li>
<li><strong>Безопасность:</strong> Контролируемое сжигание через коллективные решения</li>
<li><strong>Прозрачность:</strong> Все операции записываются в блокчейн</li>
</ul>
<h4> Важные особенности:</h4>
<ul>
<li>Сжигание токенов возможно только через предложения и голосование</li>
<li>Можно сжигать токены из казны DLE или от имени участников</li>
<li>Все операции требуют достижения кворума</li>
<li>История всех сжиганий сохраняется в блокчейне</li>
<li>Сжигание необратимо - токены уничтожаются навсегда</li>
</ul>
</div>
</div>
</div>
<!-- Форма деплоя -->
<div class="deploy-form-section">
<div class="form-header">
<h3> Настройки деплоя</h3>
<p>Настройте параметры для деплоя BurnModule</p>
</div>
<form @submit.prevent="deployModule" class="deploy-form">
<div class="form-row">
<div class="form-group">
<label for="moduleName">Название модуля:</label>
<input
id="moduleName"
v-model="deployData.moduleName"
type="text"
placeholder="BurnModule"
required
/>
</div>
<div class="form-group">
<label for="moduleVersion">Версия модуля:</label>
<input
id="moduleVersion"
v-model="deployData.moduleVersion"
type="text"
placeholder="1.0.0"
required
/>
</div>
</div>
<div class="form-group">
<label for="moduleDescription">Описание модуля:</label>
<textarea
id="moduleDescription"
v-model="deployData.moduleDescription"
placeholder="Модуль для сжигания токенов DLE через governance..."
rows="3"
required
></textarea>
</div>
<div class="form-group">
<label for="maxBurnPerProposal">Максимальное сжигание за одно предложение:</label>
<input
id="maxBurnPerProposal"
v-model="deployData.maxBurnPerProposal"
type="number"
min="1"
step="1"
placeholder="1000000"
required
/>
<small class="form-help">Максимальное количество токенов, которое можно сжечь за одно предложение</small>
</div>
<div class="form-group">
<label for="burnCooldown">Кулдаун между сжиганиями (часы):</label>
<input
id="burnCooldown"
v-model="deployData.burnCooldown"
type="number"
min="0"
step="1"
placeholder="24"
required
/>
<small class="form-help">Минимальное время между успешными сжиганиями токенов</small>
</div>
<div class="form-group">
<label for="allowTreasuryBurn">Разрешить сжигание из казны:</label>
<select
id="allowTreasuryBurn"
v-model="deployData.allowTreasuryBurn"
required
>
<option value="true">Да</option>
<option value="false">Нет</option>
</select>
<small class="form-help">Разрешить сжигание токенов из казны DLE</small>
</div>
<div class="form-group">
<label for="allowUserBurn">Разрешить сжигание от участников:</label>
<select
id="allowUserBurn"
v-model="deployData.allowUserBurn"
required
>
<option value="true">Да</option>
<option value="false">Нет</option>
</select>
<small class="form-help">Разрешить сжигание токенов от имени участников DLE</small>
</div>
<div class="form-group">
<label for="deployDescription">Описание предложения для деплоя:</label>
<textarea
id="deployDescription"
v-model="deployData.deployDescription"
placeholder="Предложение о деплое BurnModule для управления сжиганием токенов DLE..."
rows="3"
required
></textarea>
</div>
<div class="form-group">
<label for="votingDuration">Длительность голосования (часы):</label>
<input
id="votingDuration"
v-model="deployData.votingDuration"
type="number"
min="1"
max="168"
placeholder="24"
required
/>
<small class="form-help">Время для голосования по предложению деплоя (1-168 часов)</small>
</div>
<button type="submit" class="btn-primary" :disabled="isDeploying">
{{ isDeploying ? 'Деплой...' : 'Деплой BurnModule' }}
</button>
<!-- Статус деплоя -->
<div v-if="deployStatus" class="deploy-status">
<p class="status-message">{{ deployStatus }}</p>
</div>
</form>
</div>
</div>
</BaseLayout>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import BaseLayout from '../../../components/BaseLayout.vue';
import api from '../../../api/axios';
// Props
const props = defineProps({
isAuthenticated: {
type: Boolean,
default: false
},
identities: {
type: Array,
default: () => []
},
tokenBalances: {
type: Object,
default: () => ({})
},
isLoadingTokens: {
type: Boolean,
default: false
}
});
// Emits
const emit = defineEmits(['auth-action-completed']);
// Router
const route = useRoute();
const router = useRouter();
// Состояние
const isDeploying = ref(false);
const deployStatus = ref('');
const selectedDle = ref(null);
const isLoadingDle = ref(false);
// Данные для деплоя
const deployData = ref({
moduleName: 'BurnModule',
moduleVersion: '1.0.0',
moduleDescription: 'Модуль для сжигания токенов DLE через governance',
maxBurnPerProposal: 1000000,
burnCooldown: 24,
allowTreasuryBurn: 'true',
allowUserBurn: 'true',
deployDescription: 'Предложение о деплое BurnModule для управления сжиганием токенов DLE',
votingDuration: 24
});
// Получаем адрес DLE из URL
const dleAddress = computed(() => route.query.address);
// Загрузка данных DLE
const loadDleData = async () => {
if (!dleAddress.value) return;
try {
isLoadingDle.value = true;
const response = await api.post('/blockchain/read-dle-info', {
dleAddress: dleAddress.value
});
if (response.data.success) {
selectedDle.value = response.data.data;
}
} catch (error) {
console.error('Ошибка загрузки данных DLE:', error);
} finally {
isLoadingDle.value = false;
}
};
// Функция деплоя модуля
const deployModule = async () => {
if (isDeploying.value) return;
try {
isDeploying.value = true;
deployStatus.value = 'Подготовка к деплою...';
// Здесь будет логика деплоя модуля
console.log('Деплой BurnModule:', deployData.value);
// Временная заглушка
await new Promise(resolve => setTimeout(resolve, 2000));
deployStatus.value = 'Модуль успешно развернут!';
// Очищаем статус через 3 секунды
setTimeout(() => {
deployStatus.value = '';
}, 3000);
alert('BurnModule успешно развернут!');
} catch (error) {
console.error('Ошибка деплоя модуля:', error);
deployStatus.value = 'Ошибка деплоя модуля';
setTimeout(() => {
deployStatus.value = '';
}, 3000);
alert('Ошибка при деплое модуля');
} finally {
isDeploying.value = false;
}
};
// Загружаем данные при монтировании
onMounted(() => {
loadDleData();
});
</script>
<style scoped>
.module-deploy-page {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #f0f0f0;
}
.header-content h1 {
color: var(--color-primary);
font-size: 2.5rem;
margin: 0 0 10px 0;
}
.header-content p {
color: var(--color-grey-dark);
font-size: 1.1rem;
margin: 0 0 15px 0;
}
.dle-info {
display: flex;
gap: 15px;
align-items: center;
}
.dle-name {
font-weight: 600;
color: var(--color-primary);
}
.dle-address {
font-family: monospace;
color: var(--color-grey-dark);
font-size: 0.9rem;
}
.close-btn {
background: none;
border: none;
font-size: 2rem;
color: var(--color-grey-dark);
cursor: pointer;
padding: 5px;
}
.close-btn:hover {
color: var(--color-primary);
}
.module-description {
margin-bottom: 30px;
}
.description-card {
background: #f8f9fa;
padding: 25px;
border-radius: var(--radius-lg);
border: 1px solid #e9ecef;
}
.description-card h3 {
color: var(--color-primary);
margin: 0 0 20px 0;
}
.description-content h4 {
color: var(--color-grey-dark);
margin: 20px 0 10px 0;
}
.description-content ul {
margin: 10px 0;
padding-left: 20px;
}
.description-content li {
margin: 5px 0;
line-height: 1.5;
}
.deploy-form-section {
background: white;
padding: 30px;
border-radius: var(--radius-lg);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.form-header {
margin-bottom: 25px;
}
.form-header h3 {
color: var(--color-primary);
margin: 0 0 10px 0;
}
.form-header p {
color: var(--color-grey-dark);
margin: 0;
}
.deploy-form {
display: flex;
flex-direction: column;
gap: 20px;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.form-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.form-group label {
font-weight: 600;
color: var(--color-grey-dark);
}
.form-group input,
.form-group select,
.form-group textarea {
padding: 12px;
border: 1px solid #ddd;
border-radius: var(--radius-sm);
font-size: 1rem;
}
.form-group textarea {
resize: vertical;
min-height: 80px;
}
.form-help {
font-size: 0.85rem;
color: var(--color-grey-dark);
font-style: italic;
}
.btn-primary {
background: var(--color-primary);
color: white;
border: none;
padding: 15px 30px;
border-radius: var(--radius-sm);
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: background-color 0.3s;
}
.btn-primary:hover:not(:disabled) {
background: var(--color-primary-dark);
}
.btn-primary:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.deploy-status {
margin-top: 20px;
padding: 15px;
background: #e8f5e8;
border-radius: var(--radius-sm);
border-left: 4px solid #28a745;
}
.status-message {
margin: 0;
font-weight: 600;
color: #28a745;
}
@media (max-width: 768px) {
.form-row {
grid-template-columns: 1fr;
}
.dle-info {
flex-direction: column;
align-items: flex-start;
gap: 5px;
}
}
</style>

View File

@@ -1,218 +0,0 @@
<!--
Copyright (c) 2024-2025 Тарабанов Александр Викторович
All rights reserved.
This software is proprietary and confidential.
Unauthorized copying, modification, or distribution is prohibited.
For licensing inquiries: info@hb3-accelerator.com
Website: https://hb3-accelerator.com
GitHub: https://github.com/HB3-ACCELERATOR
-->
<template>
<BaseLayout
:is-authenticated="isAuthenticated"
:identities="identities"
:token-balances="tokenBalances"
:is-loading-tokens="isLoadingTokens"
@auth-action-completed="$emit('auth-action-completed')"
>
<div class="communication-module-deploy">
<!-- Заголовок -->
<div class="page-header">
<div class="header-content">
<h1>Деплой CommunicationModule</h1>
<p>Коммуникации - сообщения, звонки, история общения между участниками</p>
<p v-if="dleAddress" class="dle-address">
<strong>DLE:</strong> {{ dleAddress }}
</p>
</div>
<button class="close-btn" @click="router.push('/management/modules')">×</button>
</div>
<!-- Информация о модуле -->
<div class="module-info">
<div class="info-card">
<h3>💬 CommunicationModule</h3>
<div class="info-grid">
<div class="info-item">
<strong>Назначение:</strong> Коммуникации между участниками DLE
</div>
<div class="info-item">
<strong>Функции:</strong> Сообщения, аудио/видео звонки, история общения
</div>
<div class="info-item">
<strong>Безопасность:</strong> Кворум для коммуникационных операций
</div>
</div>
</div>
</div>
<!-- Форма деплоя будет добавлена позже -->
<div class="deploy-form-placeholder">
<div class="placeholder-content">
<h3>🚧 Форма деплоя в разработке</h3>
<p>Здесь будет форма для деплоя CommunicationModule</p>
</div>
</div>
</div>
</BaseLayout>
</template>
<script setup>
import { defineProps, defineEmits, ref, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import BaseLayout from '../../../components/BaseLayout.vue';
// Определяем props
const props = defineProps({
isAuthenticated: { type: Boolean, default: false },
identities: { type: Array, default: () => [] },
tokenBalances: { type: Object, default: () => ({}) },
isLoadingTokens: { type: Boolean, default: false }
});
// Определяем emits
const emit = defineEmits(['auth-action-completed']);
const router = useRouter();
const route = useRoute();
// Состояние
const isLoading = ref(false);
const dleAddress = ref(route.query.address || null);
// Инициализация
onMounted(() => {
console.log('[CommunicationModuleDeployView] Страница загружена');
});
</script>
<style scoped>
.communication-module-deploy {
padding: 20px;
background-color: var(--color-white);
border-radius: var(--radius-lg);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin-top: 20px;
margin-bottom: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #f0f0f0;
}
.page-header h1 {
color: var(--color-primary);
font-size: 2rem;
margin: 0;
}
.page-header p {
margin: 10px 0 0 0;
color: #666;
}
.dle-address {
margin-top: 10px !important;
font-family: monospace;
background: #f8f9fa;
padding: 8px 12px;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.close-btn {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: #666;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: all 0.2s;
}
.close-btn:hover {
background: #f0f0f0;
color: #333;
}
/* Информация о модуле */
.module-info {
margin-bottom: 30px;
}
.info-card {
background: #f8f9fa;
border-radius: var(--radius-md);
padding: 20px;
border: 1px solid #e9ecef;
}
.info-card h3 {
margin: 0 0 15px 0;
color: var(--color-primary);
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
}
.info-item {
padding: 10px;
background: white;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.info-item strong {
color: var(--color-primary);
}
/* Плейсхолдер для формы */
.deploy-form-placeholder {
background: #f8f9fa;
border-radius: var(--radius-md);
padding: 40px;
text-align: center;
border: 2px dashed #dee2e6;
}
.placeholder-content h3 {
color: var(--color-primary);
margin-bottom: 10px;
}
.placeholder-content p {
color: #666;
margin: 0;
}
/* Адаптивность */
@media (max-width: 768px) {
.info-grid {
grid-template-columns: 1fr;
}
.page-header {
flex-direction: column;
align-items: flex-start;
gap: 15px;
}
}
</style>

View File

@@ -1,707 +0,0 @@
<!--
Copyright (c) 2024-2025 Тарабанов Александр Викторович
All rights reserved.
This software is proprietary and confidential.
Unauthorized copying, modification, or distribution is prohibited.
For licensing inquiries: info@hb3-accelerator.com
Website: https://hb3-accelerator.com
GitHub: https://github.com/HB3-ACCELERATOR
-->
<template>
<BaseLayout
:is-authenticated="isAuthenticated"
:identities="identities"
:token-balances="tokenBalances"
:is-loading-tokens="isLoadingTokens"
@auth-action-completed="$emit('auth-action-completed')"
>
<div class="reader-module-deploy">
<!-- Заголовок -->
<div class="page-header">
<div class="header-content">
<h1>Деплой DLEReader</h1>
<p>API для чтения данных DLE - получение информации о контракте и предложениях</p>
<p v-if="dleAddress" class="dle-address">
<strong>DLE:</strong> {{ dleAddress }}
</p>
</div>
<button class="close-btn" @click="router.push('/management/modules')">×</button>
</div>
<!-- Информация о модуле -->
<div class="module-info">
<div class="info-card">
<h3>📊 DLEReader</h3>
<div class="info-grid">
<div class="info-item">
<strong>Назначение:</strong> Чтение данных DLE контракта
</div>
<div class="info-item">
<strong>Функции:</strong> API для предложений, голосования, статистики
</div>
<div class="info-item">
<strong>Безопасность:</strong> Только чтение, не изменяет состояние
</div>
</div>
</div>
</div>
<!-- Форма деплоя модуля администратором -->
<div v-if="canManageSettings" class="deploy-form">
<div class="form-header">
<h3>🔧 Деплой DLEReader администратором</h3>
<p>Администратор деплоит модуль, затем создает предложение для добавления в DLE</p>
</div>
<div class="form-content">
<!-- Информация о сетях -->
<div class="networks-info">
<h4>📡 Сети для деплоя:</h4>
<div class="networks-list">
<div class="network-item">
<span class="network-name">Sepolia</span>
<span class="network-chain-id">Chain ID: 11155111</span>
</div>
<div class="network-item">
<span class="network-name">Holesky</span>
<span class="network-chain-id">Chain ID: 17000</span>
</div>
<div class="network-item">
<span class="network-name">Arbitrum Sepolia</span>
<span class="network-chain-id">Chain ID: 421614</span>
</div>
<div class="network-item">
<span class="network-name">Base Sepolia</span>
<span class="network-chain-id">Chain ID: 84532</span>
</div>
</div>
</div>
<!-- Настройки модуля -->
<div class="module-settings">
<h4> Настройки DLEReader:</h4>
<div class="settings-form">
<!-- Поля администратора -->
<div class="admin-section">
<h5>🔐 Настройки администратора:</h5>
<div class="form-row">
<div class="form-group">
<label for="adminPrivateKey">Приватный ключ администратора:</label>
<input
type="password"
id="adminPrivateKey"
v-model="moduleSettings.adminPrivateKey"
class="form-control"
placeholder="0x..."
required
>
<small class="form-help">Приватный ключ для деплоя модуля (администратор платит газ)</small>
</div>
<div class="form-group">
<label for="etherscanApiKey">Etherscan API ключ:</label>
<input
type="text"
id="etherscanApiKey"
v-model="moduleSettings.etherscanApiKey"
class="form-control"
placeholder="YourAPIKey..."
>
<small class="form-help">API ключ для автоматической верификации контрактов</small>
</div>
</div>
</div>
<div class="simple-info">
<h5>📋 Информация о DLEReader:</h5>
<div class="info-text">
<p><strong>DLEReader</strong> - это простой read-only модуль, который:</p>
<ul>
<li> Только читает данные из DLE контракта</li>
<li> Не изменяет состояние блокчейна</li>
<li> Предоставляет API для получения информации</li>
<li> Безопасен для обновления</li>
</ul>
<p><strong>Конструктор принимает только один параметр:</strong> адрес DLE контракта</p>
</div>
</div>
</div>
</div>
<!-- Кнопка деплоя -->
<div class="deploy-actions">
<button
class="btn btn-primary btn-large deploy-module"
@click="deployDLEReader"
:disabled="isDeploying || !dleAddress || !isFormValid"
>
<i class="fas fa-rocket" :class="{ 'fa-spin': isDeploying }"></i>
{{ isDeploying ? 'Деплой модуля...' : 'Деплой DLEReader' }}
</button>
<div v-if="!isFormValid && !isDeploying" class="form-validation-info">
<i class="fas fa-exclamation-triangle"></i>
<span>Заполните приватный ключ и API ключ для деплоя</span>
</div>
<div v-if="deploymentProgress" class="deployment-progress">
<div class="progress-info">
<span>{{ deploymentProgress.message }}</span>
<span class="progress-percentage">{{ deploymentProgress.percentage }}%</span>
</div>
<div class="progress-bar">
<div class="progress-fill" :style="{ width: deploymentProgress.percentage + '%' }"></div>
</div>
</div>
</div>
</div>
</div>
<!-- Сообщение для пользователей без прав доступа -->
<div v-if="!canManageSettings" class="no-access-message">
<div class="message-content">
<h3>🔒 Нет прав доступа</h3>
<p>У вас нет прав для деплоя смарт-контрактов. Только пользователи с ролью Editor могут выполнять деплой.</p>
<button class="btn btn-secondary" @click="router.push('/management/modules')">
Вернуться к модулям
</button>
</div>
</div>
</div>
</BaseLayout>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import BaseLayout from '../../../components/BaseLayout.vue';
import { usePermissions } from '@/composables/usePermissions';
// Props
const props = defineProps({
isAuthenticated: Boolean,
identities: Array,
tokenBalances: Object,
isLoadingTokens: Boolean
});
const emit = defineEmits(['auth-action-completed']);
const router = useRouter();
const route = useRoute();
const { canEdit, canManageSettings } = usePermissions();
// Состояние
const isLoading = ref(false);
const dleAddress = ref(route.query.address || null);
const isDeploying = ref(false);
const deploymentProgress = ref(null);
// Настройки модуля
const moduleSettings = ref({
// Поля администратора
adminPrivateKey: '',
etherscanApiKey: ''
});
// Проверка валидности формы
const isFormValid = computed(() => {
return moduleSettings.value.adminPrivateKey && moduleSettings.value.etherscanApiKey;
});
// Функция деплоя DLEReader
async function deployDLEReader() {
if (!canManageSettings.value) {
alert('У вас нет прав для деплоя смарт-контрактов');
return;
}
try {
isDeploying.value = true;
deploymentProgress.value = {
message: 'Инициализация деплоя...',
percentage: 0
};
console.log('[DLEReaderDeployView] Начинаем деплой DLEReader для DLE:', dleAddress.value);
// Вызываем API для деплоя модуля администратором
const response = await fetch('/api/dle-modules/deploy-reader-admin', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
dleAddress: dleAddress.value,
moduleType: 'reader',
adminPrivateKey: moduleSettings.value.adminPrivateKey,
etherscanApiKey: moduleSettings.value.etherscanApiKey,
settings: {
// Используем настройки по умолчанию
useDefaultSettings: true
}
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result.success) {
console.log('[DLEReaderDeployView] Деплой успешно запущен:', result);
// Обновляем прогресс
deploymentProgress.value = {
message: 'Деплой запущен успешно! Проверьте логи для отслеживания прогресса.',
percentage: 100
};
// Показываем детальную информацию о деплое
const deployInfo = result.data || {};
const deployedAddresses = deployInfo.addresses || [];
let successMessage = '✅ DLEReader успешно задеплоен!\n\n';
successMessage += `📊 Детали деплоя:\n`;
successMessage += `• DLE: ${dleAddress.value}\n`;
successMessage += `• Тип модуля: DLEReader\n`;
successMessage += `• Адрес модуля: ${deployInfo.moduleAddress || 'Не указан'}\n`;
if (deployedAddresses.length > 0) {
successMessage += `\n🌐 Задеплоенные адреса:\n`;
deployedAddresses.forEach((addr, index) => {
successMessage += `${index + 1}. ${addr.network}: ${addr.address}\n`;
});
}
successMessage += `\n📝 Следующий шаг: Создайте предложение для добавления модуля в DLE через governance.`;
alert(successMessage);
// Перенаправляем обратно к модулям
setTimeout(() => {
router.push(`/management/modules?address=${dleAddress.value}`);
}, 3000);
} else {
throw new Error(result.error || 'Неизвестная ошибка');
}
} catch (error) {
console.error('[DLEReaderDeployView] Ошибка деплоя:', error);
alert('❌ Ошибка деплоя: ' + error.message);
deploymentProgress.value = {
message: 'Ошибка деплоя: ' + error.message,
percentage: 0
};
} finally {
isDeploying.value = false;
}
}
// Инициализация
onMounted(() => {
console.log('[DLEReaderDeployView] Страница загружена');
});
</script>
<style scoped>
.reader-module-deploy {
padding: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 30px;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: var(--radius-lg);
color: white;
}
.header-content h1 {
margin: 0 0 10px 0;
font-size: 2rem;
font-weight: 700;
}
.header-content p {
margin: 0 0 5px 0;
opacity: 0.9;
font-size: 1.1rem;
}
.dle-address {
font-family: 'Courier New', monospace;
background: rgba(255, 255, 255, 0.1);
padding: 8px 12px;
border-radius: var(--radius-sm);
margin-top: 10px;
}
.close-btn {
background: rgba(255, 255, 255, 0.2);
border: none;
color: white;
font-size: 24px;
width: 40px;
height: 40px;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.close-btn:hover {
background: rgba(255, 255, 255, 0.3);
color: white;
}
/* Информация о модуле */
.module-info {
margin-bottom: 30px;
}
.info-card {
background: white;
border-radius: var(--radius-md);
padding: 20px;
border: 1px solid #e9ecef;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.info-card h3 {
margin: 0 0 15px 0;
color: var(--color-primary);
font-size: 1.5rem;
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 15px;
}
.info-item {
padding: 10px;
background: #f8f9fa;
border-radius: var(--radius-sm);
border-left: 4px solid var(--color-primary);
}
.info-item strong {
color: var(--color-primary);
}
/* Форма деплоя */
.deploy-form {
background: #f8f9fa;
border-radius: var(--radius-md);
padding: 20px;
margin-bottom: 30px;
border: 1px solid #e9ecef;
}
.form-header h3 {
margin: 0 0 10px 0;
color: var(--color-primary);
}
.form-header p {
margin: 0 0 20px 0;
color: #666;
}
.networks-info,
.module-settings {
margin-bottom: 20px;
padding: 15px;
background: white;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.settings-form {
margin-top: 15px;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-bottom: 15px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #333;
}
.form-control {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: var(--radius-sm);
font-size: 14px;
transition: border-color 0.2s;
}
.form-control:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(var(--color-primary-rgb), 0.1);
}
.form-help {
display: block;
margin-top: 5px;
font-size: 12px;
color: #666;
line-height: 1.4;
}
/* Настройки отображения данных */
.data-display-settings {
margin-top: 20px;
padding: 20px;
background: #f8f9fa;
border-radius: var(--radius-sm);
border: 1px solid #e9ecef;
}
.data-display-settings h5 {
margin: 0 0 15px 0;
color: var(--color-primary);
font-size: 1.1rem;
font-weight: 600;
}
.data-display-settings .form-row {
margin-bottom: 15px;
}
.data-display-settings .form-group {
margin-bottom: 15px;
}
.data-display-settings .form-group:last-child {
margin-bottom: 0;
}
/* Секция администратора */
.admin-section {
margin-bottom: 20px;
padding: 20px;
background: #fff3cd;
border-radius: var(--radius-sm);
border: 1px solid #ffeaa7;
}
.admin-section h5 {
margin: 0 0 15px 0;
color: #856404;
font-size: 1.1rem;
font-weight: 600;
}
/* Простая информация */
.simple-info {
margin-top: 20px;
padding: 20px;
background: #f8f9fa;
border-radius: var(--radius-sm);
border: 1px solid #e9ecef;
}
.simple-info h5 {
margin: 0 0 15px 0;
color: var(--color-primary);
font-size: 1.1rem;
font-weight: 600;
}
.info-text {
color: #666;
line-height: 1.6;
}
.info-text p {
margin: 0 0 10px 0;
}
.info-text ul {
margin: 10px 0;
padding-left: 20px;
}
.info-text li {
margin: 5px 0;
color: #555;
}
.info-text strong {
color: var(--color-primary);
}
.deploy-actions {
text-align: center;
margin-top: 20px;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: var(--radius-md);
cursor: pointer;
font-size: 16px;
font-weight: 500;
transition: all 0.3s;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark));
color: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.btn-primary:hover:not(:disabled) {
background: linear-gradient(135deg, var(--color-primary-dark), var(--color-primary));
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
transform: translateY(-1px);
}
.btn-primary:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.btn-large {
padding: 16px 32px;
font-size: 18px;
}
.deployment-progress {
margin-top: 20px;
padding: 15px;
background: white;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.progress-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.progress-percentage {
font-weight: 600;
color: var(--color-primary);
}
.progress-bar {
width: 100%;
height: 8px;
background: #f0f0f0;
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--color-primary), var(--color-primary-dark));
transition: width 0.3s ease;
}
/* Сети */
.networks-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 10px;
margin-top: 10px;
}
.network-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background: #f8f9fa;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.network-name {
font-weight: 600;
color: var(--color-primary);
}
.network-chain-id {
font-size: 12px;
color: #666;
font-family: 'Courier New', monospace;
}
/* Сообщение об отсутствии прав доступа */
.no-access-message {
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: var(--radius-md);
padding: 30px;
margin: 20px 0;
text-align: center;
}
.message-content h3 {
color: #856404;
margin-bottom: 15px;
font-size: 1.4em;
}
.message-content p {
color: #856404;
margin-bottom: 20px;
font-size: 1.1em;
line-height: 1.5;
}
.message-content .btn {
background: #6c757d;
color: white;
border: none;
border-radius: var(--radius-sm);
padding: 12px 24px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.message-content .btn:hover {
background: #5a6268;
}
</style>

View File

@@ -1,663 +0,0 @@
<!--
Copyright (c) 2024-2025 Тарабанов Александр Викторович
All rights reserved.
This software is proprietary and confidential.
Unauthorized copying, modification, or distribution is prohibited.
For licensing inquiries: info@hb3-accelerator.com
Website: https://hb3-accelerator.com
GitHub: https://github.com/HB3-ACCELERATOR
-->
<template>
<BaseLayout
:is-authenticated="isAuthenticated"
:identities="identities"
:token-balances="tokenBalances"
:is-loading-tokens="isLoadingTokens"
@auth-action-completed="$emit('auth-action-completed')"
>
<div class="module-deploy-page">
<!-- Заголовок -->
<div class="page-header">
<div class="header-content">
<h1>🏛 Деплой InheritanceModule</h1>
<p>Модуль наследования токенов DLE - защита активов и автоматическая передача наследникам</p>
<div v-if="selectedDle" class="dle-info">
<span class="dle-name">{{ selectedDle.name }} ({{ selectedDle.symbol }})</span>
<span class="dle-address">{{ selectedDle.dleAddress }}</span>
</div>
</div>
<button class="close-btn" @click="router.push(`/management/modules?address=${route.query.address}`)">×</button>
</div>
<!-- Описание модуля -->
<div class="module-description">
<div class="description-card">
<h3>📋 Описание InheritanceModule</h3>
<div class="description-content">
<p><strong>InheritanceModule</strong> - это модуль для автоматической передачи токенов DLE наследникам в случае смерти или недееспособности токенхолдера.</p>
<h4>🔧 Основная функциональность:</h4>
<ul>
<li><strong>Назначение наследников:</strong> Токенхолдеры могут указать один или несколько наследников</li>
<li><strong>Распределение долей:</strong> Настройка процентного распределения токенов между наследниками</li>
<li><strong>Условия активации:</strong> Настройка условий для передачи токенов (смерть, недееспособность)</li>
<li><strong>Временные ограничения:</strong> Установка минимального периода владения токенами</li>
<li><strong>Множественные наследники:</strong> Поддержка сложных схем наследования</li>
<li><strong>Отзыв и изменение:</strong> Возможность изменения наследников в любое время</li>
</ul>
<h4>🏛 Юридические аспекты:</h4>
<ul>
<li><strong>Соответствие законам:</strong> Интеграция с юридическими системами наследования</li>
<li><strong>Документооборот:</strong> Автоматическое создание юридических документов</li>
<li><strong>Подтверждение смерти:</strong> Интеграция с государственными реестрами</li>
<li><strong>Споры и оспаривание:</strong> Механизмы разрешения споров о наследстве</li>
<li><strong>Налоговые обязательства:</strong> Автоматический расчет налогов на наследство</li>
</ul>
<h4>🔐 Безопасность и контроль:</h4>
<ul>
<li>Все изменения наследников требуют подтверждения через governance</li>
<li>Криптографическая защита данных о наследниках</li>
<li>Аудит всех операций наследования</li>
<li>Возможность экстренной блокировки в случае споров</li>
<li>Интеграция с системой идентификации для подтверждения личности</li>
</ul>
</div>
</div>
</div>
<!-- Архитектура модуля -->
<div class="module-architecture">
<div class="architecture-card">
<h3>🏗 Архитектура InheritanceModule</h3>
<div class="architecture-content">
<div class="architecture-diagram">
<div class="diagram-row">
<div class="diagram-item tokenholder">
<h5>👤 Токенхолдер</h5>
<ul>
<li>Назначает наследников</li>
<li>Устанавливает доли</li>
<li>Управляет условиями</li>
<li>Может отозвать</li>
</ul>
</div>
<div class="diagram-arrow"></div>
<div class="diagram-item inheritance">
<h5>🏛 InheritanceModule</h5>
<ul>
<li>Хранит данные наследников</li>
<li>Проверяет условия</li>
<li>Выполняет передачу</li>
<li>Ведет аудит</li>
</ul>
</div>
<div class="diagram-arrow"></div>
<div class="diagram-item heirs">
<h5>👥 Наследники</h5>
<ul>
<li>Получают токены</li>
<li>Подтверждают получение</li>
<li>Управляют наследством</li>
<li>Планируют налоги</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Типы наследования -->
<div class="inheritance-types">
<div class="types-card">
<h3>📊 Типы наследования</h3>
<div class="types-grid">
<div class="type-item">
<h4>👨👩👧👦 Семейное наследование</h4>
<p>Передача токенов членам семьи согласно традиционным схемам</p>
<ul>
<li>Супруг/супруга (50%)</li>
<li>Дети (равные доли)</li>
<li>Родители (при отсутствии детей)</li>
<li>Братья/сестры (при отсутствии родителей)</li>
</ul>
</div>
<div class="type-item">
<h4>🏢 Корпоративное наследование</h4>
<p>Передача токенов в рамках бизнес-структур и организаций</p>
<ul>
<li>Партнеры по бизнесу</li>
<li>Ключевые сотрудники</li>
<li>Дочерние компании</li>
<li>Благотворительные фонды</li>
</ul>
</div>
<div class="type-item">
<h4>🎯 Целевое наследование</h4>
<p>Передача токенов для достижения конкретных целей</p>
<ul>
<li>Образовательные учреждения</li>
<li>Исследовательские проекты</li>
<li>Экологические инициативы</li>
<li>Социальные программы</li>
</ul>
</div>
<div class="type-item">
<h4> Условное наследование</h4>
<p>Передача токенов при выполнении определенных условий</p>
<ul>
<li>Достижение определенного возраста</li>
<li>Завершение образования</li>
<li>Создание семьи</li>
<li>Достижение карьерных целей</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Примеры использования -->
<div class="usage-examples">
<div class="examples-card">
<h3>💡 Примеры использования</h3>
<div class="examples-content">
<div class="example-item">
<h4>👨👩👧👦 Семейное планирование</h4>
<div class="example-code">
<pre><code>// Назначение наследников для семьи
function setFamilyInheritance() {
setHeir(spouse, 50); // Супруг 50%
setHeir(son, 25); // Сын 25%
setHeir(daughter, 25); // Дочь 25%
setActivationCondition("death");
}</code></pre>
</div>
</div>
<div class="example-item">
<h4>🏢 Бизнес-преемственность</h4>
<div class="example-code">
<pre><code>// Передача бизнеса партнеру
function setBusinessInheritance() {
setHeir(businessPartner, 100); // Партнер 100%
setActivationCondition("death");
setTimeLock(365 days); // Минимум 1 год владения
}</code></pre>
</div>
</div>
<div class="example-item">
<h4>🎯 Благотворительное наследование</h4>
<div class="example-code">
<pre><code>// Передача в благотворительный фонд
function setCharityInheritance() {
setHeir(environmentalFund, 70); // Экологический фонд 70%
setHeir(educationFund, 30); // Образовательный фонд 30%
setActivationCondition("death");
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<!-- Юридические аспекты -->
<div class="legal-aspects">
<div class="legal-card">
<h3> Юридические аспекты</h3>
<div class="legal-content">
<div class="legal-section">
<h4>📜 Соответствие законодательству</h4>
<ul>
<li><strong>Гражданский кодекс:</strong> Соответствие нормам наследования</li>
<li><strong>Налоговый кодекс:</strong> Правильный расчет налогов на наследство</li>
<li><strong>Семейный кодекс:</strong> Учет семейных обязательств</li>
<li><strong>Международное право:</strong> Наследование в разных юрисдикциях</li>
</ul>
</div>
<div class="legal-section">
<h4>🔍 Процедура подтверждения</h4>
<ul>
<li><strong>Свидетельство о смерти:</strong> Официальное подтверждение</li>
<li><strong>Медицинское заключение:</strong> При недееспособности</li>
<li><strong>Судебное решение:</strong> При спорах о наследстве</li>
<li><strong>Нотариальное заверение:</strong> Документов о наследниках</li>
</ul>
</div>
<div class="legal-section">
<h4>💰 Налоговые обязательства</h4>
<ul>
<li><strong>Налог на наследство:</strong> Автоматический расчет</li>
<li><strong>НДФЛ:</strong> При получении токенов</li>
<li><strong>Отчетность:</strong> Автоматическая подача деклараций</li>
<li><strong>Льготы:</strong> Учет налоговых льгот для наследников</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Статус разработки -->
<div class="development-status">
<div class="status-card">
<h3>🚧 Статус разработки</h3>
<div class="status-content">
<p><strong>InheritanceModule находится в стадии планирования.</strong></p>
<p>Модуль будет включать:</p>
<ul>
<li> Систему назначения наследников</li>
<li> Управление долями и условиями</li>
<li> Интеграцию с юридическими системами</li>
<li> Автоматическую передачу токенов</li>
<li> Налоговые расчеты</li>
<li> Аудит и мониторинг</li>
<li> Разрешение споров</li>
</ul>
<p><em>Модуль будет доступен в следующих обновлениях DLE.</em></p>
</div>
</div>
</div>
</div>
</BaseLayout>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import BaseLayout from '../../../components/BaseLayout.vue';
import api from '../../../api/axios';
// Props
const props = defineProps({
isAuthenticated: {
type: Boolean,
default: false
},
identities: {
type: Array,
default: () => []
},
tokenBalances: {
type: Object,
default: () => ({})
},
isLoadingTokens: {
type: Boolean,
default: false
}
});
// Emits
const emit = defineEmits(['auth-action-completed']);
// Router
const route = useRoute();
const router = useRouter();
// Состояние
const selectedDle = ref(null);
const isLoadingDle = ref(false);
// Получаем адрес DLE из URL
const dleAddress = computed(() => route.query.address);
// Загрузка данных DLE
const loadDleData = async () => {
if (!dleAddress.value) return;
try {
isLoadingDle.value = true;
const response = await api.post('/blockchain/read-dle-info', {
dleAddress: dleAddress.value
});
if (response.data.success) {
selectedDle.value = response.data.data;
}
} catch (error) {
console.error('Ошибка загрузки данных DLE:', error);
} finally {
isLoadingDle.value = false;
}
};
// Загружаем данные при монтировании
onMounted(() => {
loadDleData();
});
</script>
<style scoped>
.module-deploy-page {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #f0f0f0;
}
.header-content h1 {
color: var(--color-primary);
font-size: 2.5rem;
margin: 0 0 10px 0;
}
.header-content p {
color: var(--color-grey-dark);
font-size: 1.1rem;
margin: 0 0 15px 0;
}
.dle-info {
display: flex;
gap: 15px;
align-items: center;
}
.dle-name {
font-weight: 600;
color: var(--color-primary);
}
.dle-address {
font-family: monospace;
color: var(--color-grey-dark);
font-size: 0.9rem;
}
.close-btn {
background: none;
border: none;
font-size: 2rem;
color: var(--color-grey-dark);
cursor: pointer;
padding: 5px;
}
.close-btn:hover {
color: var(--color-primary);
}
.module-description,
.module-architecture,
.inheritance-types,
.usage-examples,
.legal-aspects,
.development-status {
margin-bottom: 30px;
}
.description-card,
.architecture-card,
.types-card,
.examples-card,
.legal-card,
.status-card {
background: #f8f9fa;
padding: 25px;
border-radius: var(--radius-lg);
border: 1px solid #e9ecef;
}
.description-card h3,
.architecture-card h3,
.types-card h3,
.examples-card h3,
.legal-card h3,
.status-card h3 {
color: var(--color-primary);
margin: 0 0 20px 0;
}
.description-content h4 {
color: var(--color-grey-dark);
margin: 20px 0 10px 0;
}
.description-content ul {
margin: 10px 0;
padding-left: 20px;
}
.description-content li {
margin: 5px 0;
line-height: 1.5;
}
/* Архитектурная диаграмма */
.architecture-diagram {
margin: 20px 0;
}
.diagram-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
margin-bottom: 20px;
}
.diagram-item {
flex: 1;
padding: 20px;
border-radius: var(--radius-sm);
text-align: center;
min-height: 150px;
display: flex;
flex-direction: column;
justify-content: center;
}
.diagram-item.tokenholder {
background: #e8f5e8;
border: 2px solid #4caf50;
}
.diagram-item.inheritance {
background: #fff3e0;
border: 2px solid #ff9800;
}
.diagram-item.heirs {
background: #f3e5f5;
border: 2px solid #9c27b0;
}
.diagram-item h5 {
margin: 0 0 15px 0;
font-weight: 600;
}
.diagram-item ul {
margin: 0;
padding: 0;
list-style: none;
font-size: 0.9rem;
}
.diagram-item li {
margin: 5px 0;
}
.diagram-arrow {
font-size: 2rem;
color: var(--color-primary);
font-weight: bold;
}
/* Типы наследования */
.types-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
margin-top: 20px;
}
.type-item {
background: white;
padding: 20px;
border-radius: var(--radius-sm);
border: 1px solid #e9ecef;
}
.type-item h4 {
color: var(--color-primary);
margin: 0 0 10px 0;
}
.type-item p {
color: var(--color-grey-dark);
margin: 0 0 15px 0;
font-size: 0.9rem;
}
.type-item ul {
margin: 0;
padding-left: 20px;
font-size: 0.9rem;
}
.type-item li {
margin: 5px 0;
color: var(--color-grey-dark);
}
/* Примеры использования */
.examples-content {
display: flex;
flex-direction: column;
gap: 20px;
}
.example-item {
background: white;
padding: 20px;
border-radius: var(--radius-sm);
border: 1px solid #e9ecef;
}
.example-item h4 {
color: var(--color-primary);
margin: 0 0 15px 0;
}
.example-code {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: var(--radius-sm);
padding: 15px;
overflow-x: auto;
}
.example-code pre {
margin: 0;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
color: #333;
}
.example-code code {
background: none;
padding: 0;
}
/* Юридические аспекты */
.legal-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.legal-section {
background: white;
padding: 20px;
border-radius: var(--radius-sm);
border: 1px solid #e9ecef;
}
.legal-section h4 {
color: var(--color-primary);
margin: 0 0 15px 0;
}
.legal-section ul {
margin: 0;
padding-left: 20px;
}
.legal-section li {
margin: 8px 0;
line-height: 1.5;
color: var(--color-grey-dark);
}
/* Статус разработки */
.status-content {
background: white;
padding: 20px;
border-radius: var(--radius-sm);
border: 1px solid #e9ecef;
}
.status-content p {
margin: 0 0 15px 0;
line-height: 1.6;
}
.status-content ul {
margin: 15px 0;
padding-left: 20px;
}
.status-content li {
margin: 5px 0;
color: var(--color-grey-dark);
}
.status-content em {
color: var(--color-primary);
font-style: italic;
}
@media (max-width: 768px) {
.diagram-row {
flex-direction: column;
gap: 15px;
}
.diagram-arrow {
transform: rotate(90deg);
}
.types-grid {
grid-template-columns: 1fr;
}
.legal-content {
grid-template-columns: 1fr;
}
.dle-info {
flex-direction: column;
align-items: flex-start;
gap: 5px;
}
}
</style>

View File

@@ -1,485 +0,0 @@
<!--
Copyright (c) 2024-2025 Тарабанов Александр Викторович
All rights reserved.
This software is proprietary and confidential.
Unauthorized copying, modification, or distribution is prohibited.
For licensing inquiries: info@hb3-accelerator.com
Website: https://hb3-accelerator.com
GitHub: https://github.com/HB3-ACCELERATOR
-->
<template>
<BaseLayout
:is-authenticated="isAuthenticated"
:identities="identities"
:token-balances="tokenBalances"
:is-loading-tokens="isLoadingTokens"
@auth-action-completed="$emit('auth-action-completed')"
>
<div class="module-deploy-page">
<!-- Заголовок -->
<div class="page-header">
<div class="header-content">
<h1>🚀 Деплой MintModule</h1>
<p>Модуль для выпуска новых токенов DLE через governance</p>
<div v-if="selectedDle" class="dle-info">
<span class="dle-name">{{ selectedDle.name }} ({{ selectedDle.symbol }})</span>
<span class="dle-address">{{ selectedDle.dleAddress }}</span>
</div>
</div>
<button class="close-btn" @click="router.push(`/management/modules?address=${route.query.address}`)">×</button>
</div>
<!-- Описание модуля -->
<div class="module-description">
<div class="description-card">
<h3>📋 Описание MintModule</h3>
<div class="description-content">
<p><strong>MintModule</strong> - это модуль для управления выпуском новых токенов DLE через систему governance.</p>
<h4>🔧 Функциональность:</h4>
<ul>
<li><strong>Выпуск токенов:</strong> Создание дополнительных токенов DLE</li>
<li><strong>Governance:</strong> Все операции требуют голосования и кворума</li>
<li><strong>Безопасность:</strong> Контролируемый выпуск через коллективные решения</li>
<li><strong>Прозрачность:</strong> Все операции записываются в блокчейн</li>
</ul>
<h4> Важные особенности:</h4>
<ul>
<li>Выпуск токенов возможен только через предложения и голосование</li>
<li>Новые токены могут быть распределены между участниками или добавлены в казну DLE</li>
<li>Все операции требуют достижения кворума</li>
<li>История всех выпусков сохраняется в блокчейне</li>
</ul>
</div>
</div>
</div>
<!-- Форма деплоя -->
<div class="deploy-form-section">
<div class="form-header">
<h3> Настройки деплоя</h3>
<p>Настройте параметры для деплоя MintModule</p>
</div>
<form @submit.prevent="deployModule" class="deploy-form">
<div class="form-row">
<div class="form-group">
<label for="moduleName">Название модуля:</label>
<input
id="moduleName"
v-model="deployData.moduleName"
type="text"
placeholder="MintModule"
required
/>
</div>
<div class="form-group">
<label for="moduleVersion">Версия модуля:</label>
<input
id="moduleVersion"
v-model="deployData.moduleVersion"
type="text"
placeholder="1.0.0"
required
/>
</div>
</div>
<div class="form-group">
<label for="moduleDescription">Описание модуля:</label>
<textarea
id="moduleDescription"
v-model="deployData.moduleDescription"
placeholder="Модуль для выпуска новых токенов DLE через governance..."
rows="3"
required
></textarea>
</div>
<div class="form-group">
<label for="maxMintPerProposal">Максимальный выпуск за одно предложение:</label>
<input
id="maxMintPerProposal"
v-model="deployData.maxMintPerProposal"
type="number"
min="1"
step="1"
placeholder="1000000"
required
/>
<small class="form-help">Максимальное количество токенов, которое можно выпустить за одно предложение</small>
</div>
<div class="form-group">
<label for="mintCooldown">Кулдаун между выпусками (часы):</label>
<input
id="mintCooldown"
v-model="deployData.mintCooldown"
type="number"
min="0"
step="1"
placeholder="24"
required
/>
<small class="form-help">Минимальное время между успешными выпусками токенов</small>
</div>
<div class="form-group">
<label for="deployDescription">Описание предложения для деплоя:</label>
<textarea
id="deployDescription"
v-model="deployData.deployDescription"
placeholder="Предложение о деплое MintModule для управления выпуском токенов DLE..."
rows="3"
required
></textarea>
</div>
<div class="form-group">
<label for="votingDuration">Длительность голосования (часы):</label>
<input
id="votingDuration"
v-model="deployData.votingDuration"
type="number"
min="1"
max="168"
placeholder="24"
required
/>
<small class="form-help">Время для голосования по предложению деплоя (1-168 часов)</small>
</div>
<button type="submit" class="btn-primary" :disabled="isDeploying">
{{ isDeploying ? 'Деплой...' : 'Деплой MintModule' }}
</button>
<!-- Статус деплоя -->
<div v-if="deployStatus" class="deploy-status">
<p class="status-message">{{ deployStatus }}</p>
</div>
</form>
</div>
</div>
</BaseLayout>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import BaseLayout from '../../../components/BaseLayout.vue';
import api from '../../../api/axios';
// Props
const props = defineProps({
isAuthenticated: {
type: Boolean,
default: false
},
identities: {
type: Array,
default: () => []
},
tokenBalances: {
type: Object,
default: () => ({})
},
isLoadingTokens: {
type: Boolean,
default: false
}
});
// Emits
const emit = defineEmits(['auth-action-completed']);
// Router
const route = useRoute();
const router = useRouter();
// Состояние
const isDeploying = ref(false);
const deployStatus = ref('');
const selectedDle = ref(null);
const isLoadingDle = ref(false);
// Данные для деплоя
const deployData = ref({
moduleName: 'MintModule',
moduleVersion: '1.0.0',
moduleDescription: 'Модуль для выпуска новых токенов DLE через governance',
maxMintPerProposal: 1000000,
mintCooldown: 24,
deployDescription: 'Предложение о деплое MintModule для управления выпуском токенов DLE',
votingDuration: 24
});
// Получаем адрес DLE из URL
const dleAddress = computed(() => route.query.address);
// Загрузка данных DLE
const loadDleData = async () => {
if (!dleAddress.value) return;
try {
isLoadingDle.value = true;
const response = await api.post('/blockchain/read-dle-info', {
dleAddress: dleAddress.value
});
if (response.data.success) {
selectedDle.value = response.data.data;
}
} catch (error) {
console.error('Ошибка загрузки данных DLE:', error);
} finally {
isLoadingDle.value = false;
}
};
// Функция деплоя модуля
const deployModule = async () => {
if (isDeploying.value) return;
try {
isDeploying.value = true;
deployStatus.value = 'Подготовка к деплою...';
// Здесь будет логика деплоя модуля
console.log('Деплой MintModule:', deployData.value);
// Временная заглушка
await new Promise(resolve => setTimeout(resolve, 2000));
deployStatus.value = 'Модуль успешно развернут!';
// Очищаем статус через 3 секунды
setTimeout(() => {
deployStatus.value = '';
}, 3000);
alert('MintModule успешно развернут!');
} catch (error) {
console.error('Ошибка деплоя модуля:', error);
deployStatus.value = 'Ошибка деплоя модуля';
setTimeout(() => {
deployStatus.value = '';
}, 3000);
alert('Ошибка при деплое модуля');
} finally {
isDeploying.value = false;
}
};
// Загружаем данные при монтировании
onMounted(() => {
loadDleData();
});
</script>
<style scoped>
.module-deploy-page {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #f0f0f0;
}
.header-content h1 {
color: var(--color-primary);
font-size: 2.5rem;
margin: 0 0 10px 0;
}
.header-content p {
color: var(--color-grey-dark);
font-size: 1.1rem;
margin: 0 0 15px 0;
}
.dle-info {
display: flex;
gap: 15px;
align-items: center;
}
.dle-name {
font-weight: 600;
color: var(--color-primary);
}
.dle-address {
font-family: monospace;
color: var(--color-grey-dark);
font-size: 0.9rem;
}
.close-btn {
background: none;
border: none;
font-size: 2rem;
color: var(--color-grey-dark);
cursor: pointer;
padding: 5px;
}
.close-btn:hover {
color: var(--color-primary);
}
.module-description {
margin-bottom: 30px;
}
.description-card {
background: #f8f9fa;
padding: 25px;
border-radius: var(--radius-lg);
border: 1px solid #e9ecef;
}
.description-card h3 {
color: var(--color-primary);
margin: 0 0 20px 0;
}
.description-content h4 {
color: var(--color-grey-dark);
margin: 20px 0 10px 0;
}
.description-content ul {
margin: 10px 0;
padding-left: 20px;
}
.description-content li {
margin: 5px 0;
line-height: 1.5;
}
.deploy-form-section {
background: white;
padding: 30px;
border-radius: var(--radius-lg);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.form-header {
margin-bottom: 25px;
}
.form-header h3 {
color: var(--color-primary);
margin: 0 0 10px 0;
}
.form-header p {
color: var(--color-grey-dark);
margin: 0;
}
.deploy-form {
display: flex;
flex-direction: column;
gap: 20px;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.form-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.form-group label {
font-weight: 600;
color: var(--color-grey-dark);
}
.form-group input,
.form-group textarea {
padding: 12px;
border: 1px solid #ddd;
border-radius: var(--radius-sm);
font-size: 1rem;
}
.form-group textarea {
resize: vertical;
min-height: 80px;
}
.form-help {
font-size: 0.85rem;
color: var(--color-grey-dark);
font-style: italic;
}
.btn-primary {
background: var(--color-primary);
color: white;
border: none;
padding: 15px 30px;
border-radius: var(--radius-sm);
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: background-color 0.3s;
}
.btn-primary:hover:not(:disabled) {
background: var(--color-primary-dark);
}
.btn-primary:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.deploy-status {
margin-top: 20px;
padding: 15px;
background: #e8f5e8;
border-radius: var(--radius-sm);
border-left: 4px solid #28a745;
}
.status-message {
margin: 0;
font-weight: 600;
color: #28a745;
}
@media (max-width: 768px) {
.form-row {
grid-template-columns: 1fr;
}
.dle-info {
flex-direction: column;
align-items: flex-start;
gap: 5px;
}
}
</style>

View File

@@ -1,205 +0,0 @@
<!--
Copyright (c) 2024-2025 Тарабанов Александр Викторович
All rights reserved.
This software is proprietary and confidential.
Unauthorized copying, modification, or distribution is prohibited.
For licensing inquiries: info@hb3-accelerator.com
Website: https://hb3-accelerator.com
GitHub: https://github.com/HB3-ACCELERATOR
-->
<template>
<BaseLayout
:is-authenticated="isAuthenticated"
:identities="identities"
:token-balances="tokenBalances"
:is-loading-tokens="isLoadingTokens"
@auth-action-completed="$emit('auth-action-completed')"
>
<div class="module-deploy-form">
<!-- Заголовок -->
<div class="page-header">
<div class="header-content">
<h1>Деплой пользовательского модуля</h1>
<p>Создание и деплой кастомного модуля для DLE</p>
</div>
<button class="close-btn" @click="router.push('/management/modules')">×</button>
</div>
<!-- Информация о пользовательских модулях -->
<div class="module-info">
<div class="info-card">
<h3>🔧 Пользовательские модули</h3>
<div class="info-grid">
<div class="info-item">
<strong>Назначение:</strong> Расширение функциональности DLE
</div>
<div class="info-item">
<strong>Возможности:</strong> Любая кастомная логика, интеграции, API
</div>
<div class="info-item">
<strong>Безопасность:</strong> Проверка через голосование токен-холдеров
</div>
</div>
</div>
</div>
<!-- Форма деплоя будет добавлена позже -->
<div class="deploy-form-placeholder">
<div class="placeholder-content">
<h3>🚧 Форма деплоя в разработке</h3>
<p>Здесь будет универсальная форма для деплоя пользовательских модулей</p>
</div>
</div>
</div>
</BaseLayout>
</template>
<script setup>
import { defineProps, defineEmits, ref, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import BaseLayout from '../../../components/BaseLayout.vue';
// Определяем props
const props = defineProps({
isAuthenticated: { type: Boolean, default: false },
identities: { type: Array, default: () => [] },
tokenBalances: { type: Object, default: () => ({}) },
isLoadingTokens: { type: Boolean, default: false }
});
// Определяем emits
const emit = defineEmits(['auth-action-completed']);
const router = useRouter();
const route = useRoute();
// Состояние
const isLoading = ref(false);
// Инициализация
onMounted(() => {
console.log('[ModuleDeployFormView] Страница загружена');
});
</script>
<style scoped>
.module-deploy-form {
padding: 20px;
background-color: var(--color-white);
border-radius: var(--radius-lg);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin-top: 20px;
margin-bottom: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #f0f0f0;
}
.page-header h1 {
color: var(--color-primary);
font-size: 2rem;
margin: 0;
}
.page-header p {
margin: 10px 0 0 0;
color: #666;
}
.close-btn {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: #666;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: all 0.2s;
}
.close-btn:hover {
background: #f0f0f0;
color: #333;
}
/* Информация о модуле */
.module-info {
margin-bottom: 30px;
}
.info-card {
background: #f8f9fa;
border-radius: var(--radius-md);
padding: 20px;
border: 1px solid #e9ecef;
}
.info-card h3 {
margin: 0 0 15px 0;
color: var(--color-primary);
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
}
.info-item {
padding: 10px;
background: white;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.info-item strong {
color: var(--color-primary);
}
/* Плейсхолдер для формы */
.deploy-form-placeholder {
background: #f8f9fa;
border-radius: var(--radius-md);
padding: 40px;
text-align: center;
border: 2px dashed #dee2e6;
}
.placeholder-content h3 {
color: var(--color-primary);
margin-bottom: 10px;
}
.placeholder-content p {
color: #666;
margin: 0;
}
/* Адаптивность */
@media (max-width: 768px) {
.info-grid {
grid-template-columns: 1fr;
}
.page-header {
flex-direction: column;
align-items: flex-start;
gap: 15px;
}
}
</style>

View File

@@ -1,584 +0,0 @@
<!--
Copyright (c) 2024-2025 Тарабанов Александр Викторович
All rights reserved.
This software is proprietary and confidential.
Unauthorized copying, modification, or distribution is prohibited.
For licensing inquiries: info@hb3-accelerator.com
Website: https://hb3-accelerator.com
GitHub: https://github.com/HB3-ACCELERATOR
-->
<template>
<BaseLayout
:is-authenticated="isAuthenticated"
:identities="identities"
:token-balances="tokenBalances"
:is-loading-tokens="isLoadingTokens"
@auth-action-completed="$emit('auth-action-completed')"
>
<div class="module-deploy-page">
<!-- Заголовок -->
<div class="page-header">
<div class="header-content">
<h1>🔗 Деплой OracleModule</h1>
<p>Модуль для интеграции с внешними данными и автоматизации DLE</p>
<div v-if="selectedDle" class="dle-info">
<span class="dle-name">{{ selectedDle.name }} ({{ selectedDle.symbol }})</span>
<span class="dle-address">{{ selectedDle.dleAddress }}</span>
</div>
</div>
<button class="close-btn" @click="router.push(`/management/modules?address=${route.query.address}`)">×</button>
</div>
<!-- Описание модуля -->
<div class="module-description">
<div class="description-card">
<h3>📋 Описание OracleModule</h3>
<div class="description-content">
<p><strong>OracleModule</strong> - это модуль для интеграции DLE с внешними источниками данных и автоматизации процессов на основе реальных событий.</p>
<h4>🔧 Основная функциональность:</h4>
<ul>
<li><strong>Интеграция с IoT:</strong> Получение данных от датчиков, производственных линий, оборудования</li>
<li><strong>API интеграция:</strong> Подключение к внешним системам, ERP, CRM, аналитическим платформам</li>
<li><strong>Автоматические триггеры:</strong> Создание предложений на основе внешних событий</li>
<li><strong>Валидация данных:</strong> Проверка достоверности полученной информации</li>
<li><strong>Множественные оракулы:</strong> Подтверждение данных от нескольких источников</li>
</ul>
<h4>🏭 Примеры применения:</h4>
<ul>
<li><strong>Производственные токены:</strong> Автоматический выпуск токенов за завершение партий продукции</li>
<li><strong>Качественные бонусы:</strong> Токены за высокое качество продукции на основе данных датчиков</li>
<li><strong>Экологические токены:</strong> Вознаграждения за снижение энергопотребления и выбросов</li>
<li><strong>Инновационные токены:</strong> Токены за внедрение новых технологий и процессов</li>
<li><strong>Рыночные корректировки:</strong> Автоматическая адаптация стратегии на основе рыночных данных</li>
</ul>
<h4>🔐 Безопасность и контроль:</h4>
<ul>
<li>Все оракулы должны быть авторизованы через governance</li>
<li>Данные валидируются перед обработкой</li>
<li>Критические решения требуют множественного подтверждения</li>
<li>История всех оракульных данных сохраняется в блокчейне</li>
<li>Возможность отключения оракулов в экстренных случаях</li>
</ul>
</div>
</div>
</div>
<!-- Архитектура модуля -->
<div class="module-architecture">
<div class="architecture-card">
<h3>🏗 Архитектура OracleModule</h3>
<div class="architecture-content">
<div class="architecture-diagram">
<div class="diagram-row">
<div class="diagram-item external">
<h5>🌐 Внешние источники</h5>
<ul>
<li>IoT датчики</li>
<li>Производственные линии</li>
<li>ERP/CRM системы</li>
<li>Аналитические платформы</li>
<li>Рыночные данные</li>
</ul>
</div>
<div class="diagram-arrow"></div>
<div class="diagram-item oracle">
<h5>🔗 OracleModule</h5>
<ul>
<li>Получение данных</li>
<li>Валидация</li>
<li>Обработка</li>
<li>Создание предложений</li>
</ul>
</div>
<div class="diagram-arrow"></div>
<div class="diagram-item dle">
<h5>🏛 DLE Governance</h5>
<ul>
<li>Предложения</li>
<li>Голосование</li>
<li>Исполнение</li>
<li>Выпуск токенов</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Типы оракулов -->
<div class="oracle-types">
<div class="types-card">
<h3>📊 Типы оракулов</h3>
<div class="types-grid">
<div class="type-item">
<h4>🏭 Производственные оракулы</h4>
<p>Данные от производственных линий, оборудования, датчиков качества</p>
<ul>
<li>Завершение партий продукции</li>
<li>Показатели качества</li>
<li>Энергопотребление</li>
<li>Время простоя</li>
</ul>
</div>
<div class="type-item">
<h4>📈 Рыночные оракулы</h4>
<p>Рыночные данные, цены, спрос, конкуренция</p>
<ul>
<li>Цены на сырье</li>
<li>Спрос на продукцию</li>
<li>Конкурентные данные</li>
<li>Экономические индикаторы</li>
</ul>
</div>
<div class="type-item">
<h4>🌱 Экологические оракулы</h4>
<p>Данные об экологическом воздействии и устойчивости</p>
<ul>
<li>Выбросы CO2</li>
<li>Потребление энергии</li>
<li>Переработка отходов</li>
<li>Использование возобновляемых ресурсов</li>
</ul>
</div>
<div class="type-item">
<h4>💼 Бизнес-оракулы</h4>
<p>Бизнес-метрики, продажи, удовлетворенность клиентов</p>
<ul>
<li>Объемы продаж</li>
<li>Удовлетворенность клиентов</li>
<li>Эффективность процессов</li>
<li>Финансовые показатели</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Примеры использования -->
<div class="usage-examples">
<div class="examples-card">
<h3>💡 Примеры использования</h3>
<div class="examples-content">
<div class="example-item">
<h4>🏭 Автоматические производственные токены</h4>
<div class="example-code">
<pre><code>// При завершении партии продукции
function onBatchCompleted(uint256 quantity, uint256 quality) {
uint256 tokens = quantity * quality * 10;
createMintProposal(tokens, "Production reward");
}</code></pre>
</div>
</div>
<div class="example-item">
<h4>🌱 Экологические бонусы</h4>
<div class="example-code">
<pre><code>// При снижении энергопотребления
function onEnergyReduction(uint256 savedEnergy) {
uint256 tokens = savedEnergy * 5;
createMintProposal(tokens, "Energy efficiency bonus");
}</code></pre>
</div>
</div>
<div class="example-item">
<h4>📈 Рыночная адаптация</h4>
<div class="example-code">
<pre><code>// При изменении рыночных условий
function onMarketChange(uint256 priceChange) {
if (priceChange > 10) {
createStrategyProposal("Increase production");
}
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<!-- Статус разработки -->
<div class="development-status">
<div class="status-card">
<h3>🚧 Статус разработки</h3>
<div class="status-content">
<p><strong>OracleModule находится в стадии планирования.</strong></p>
<p>Модуль будет включать:</p>
<ul>
<li> Систему авторизации оракулов</li>
<li> Валидацию и обработку данных</li>
<li> Интеграцию с IoT и API</li>
<li> Автоматические триггеры</li>
<li> Множественное подтверждение данных</li>
<li> Аудит и мониторинг</li>
</ul>
<p><em>Модуль будет доступен в следующих обновлениях DLE.</em></p>
</div>
</div>
</div>
</div>
</BaseLayout>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import BaseLayout from '../../../components/BaseLayout.vue';
import api from '../../../api/axios';
// Props
const props = defineProps({
isAuthenticated: {
type: Boolean,
default: false
},
identities: {
type: Array,
default: () => []
},
tokenBalances: {
type: Object,
default: () => ({})
},
isLoadingTokens: {
type: Boolean,
default: false
}
});
// Emits
const emit = defineEmits(['auth-action-completed']);
// Router
const route = useRoute();
const router = useRouter();
// Состояние
const selectedDle = ref(null);
const isLoadingDle = ref(false);
// Получаем адрес DLE из URL
const dleAddress = computed(() => route.query.address);
// Загрузка данных DLE
const loadDleData = async () => {
if (!dleAddress.value) return;
try {
isLoadingDle.value = true;
const response = await api.post('/blockchain/read-dle-info', {
dleAddress: dleAddress.value
});
if (response.data.success) {
selectedDle.value = response.data.data;
}
} catch (error) {
console.error('Ошибка загрузки данных DLE:', error);
} finally {
isLoadingDle.value = false;
}
};
// Загружаем данные при монтировании
onMounted(() => {
loadDleData();
});
</script>
<style scoped>
.module-deploy-page {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #f0f0f0;
}
.header-content h1 {
color: var(--color-primary);
font-size: 2.5rem;
margin: 0 0 10px 0;
}
.header-content p {
color: var(--color-grey-dark);
font-size: 1.1rem;
margin: 0 0 15px 0;
}
.dle-info {
display: flex;
gap: 15px;
align-items: center;
}
.dle-name {
font-weight: 600;
color: var(--color-primary);
}
.dle-address {
font-family: monospace;
color: var(--color-grey-dark);
font-size: 0.9rem;
}
.close-btn {
background: none;
border: none;
font-size: 2rem;
color: var(--color-grey-dark);
cursor: pointer;
padding: 5px;
}
.close-btn:hover {
color: var(--color-primary);
}
.module-description,
.module-architecture,
.oracle-types,
.usage-examples,
.development-status {
margin-bottom: 30px;
}
.description-card,
.architecture-card,
.types-card,
.examples-card,
.status-card {
background: #f8f9fa;
padding: 25px;
border-radius: var(--radius-lg);
border: 1px solid #e9ecef;
}
.description-card h3,
.architecture-card h3,
.types-card h3,
.examples-card h3,
.status-card h3 {
color: var(--color-primary);
margin: 0 0 20px 0;
}
.description-content h4 {
color: var(--color-grey-dark);
margin: 20px 0 10px 0;
}
.description-content ul {
margin: 10px 0;
padding-left: 20px;
}
.description-content li {
margin: 5px 0;
line-height: 1.5;
}
/* Архитектурная диаграмма */
.architecture-diagram {
margin: 20px 0;
}
.diagram-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
margin-bottom: 20px;
}
.diagram-item {
flex: 1;
padding: 20px;
border-radius: var(--radius-sm);
text-align: center;
min-height: 150px;
display: flex;
flex-direction: column;
justify-content: center;
}
.diagram-item.external {
background: #e3f2fd;
border: 2px solid #2196f3;
}
.diagram-item.oracle {
background: #f3e5f5;
border: 2px solid #9c27b0;
}
.diagram-item.dle {
background: #e8f5e8;
border: 2px solid #4caf50;
}
.diagram-item h5 {
margin: 0 0 15px 0;
font-weight: 600;
}
.diagram-item ul {
margin: 0;
padding: 0;
list-style: none;
font-size: 0.9rem;
}
.diagram-item li {
margin: 5px 0;
}
.diagram-arrow {
font-size: 2rem;
color: var(--color-primary);
font-weight: bold;
}
/* Типы оракулов */
.types-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
margin-top: 20px;
}
.type-item {
background: white;
padding: 20px;
border-radius: var(--radius-sm);
border: 1px solid #e9ecef;
}
.type-item h4 {
color: var(--color-primary);
margin: 0 0 10px 0;
}
.type-item p {
color: var(--color-grey-dark);
margin: 0 0 15px 0;
font-size: 0.9rem;
}
.type-item ul {
margin: 0;
padding-left: 20px;
font-size: 0.9rem;
}
.type-item li {
margin: 5px 0;
color: var(--color-grey-dark);
}
/* Примеры использования */
.examples-content {
display: flex;
flex-direction: column;
gap: 20px;
}
.example-item {
background: white;
padding: 20px;
border-radius: var(--radius-sm);
border: 1px solid #e9ecef;
}
.example-item h4 {
color: var(--color-primary);
margin: 0 0 15px 0;
}
.example-code {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: var(--radius-sm);
padding: 15px;
overflow-x: auto;
}
.example-code pre {
margin: 0;
font-family: 'Courier New', monospace;
font-size: 0.9rem;
color: #333;
}
.example-code code {
background: none;
padding: 0;
}
/* Статус разработки */
.status-content {
background: white;
padding: 20px;
border-radius: var(--radius-sm);
border: 1px solid #e9ecef;
}
.status-content p {
margin: 0 0 15px 0;
line-height: 1.6;
}
.status-content ul {
margin: 15px 0;
padding-left: 20px;
}
.status-content li {
margin: 5px 0;
color: var(--color-grey-dark);
}
.status-content em {
color: var(--color-primary);
font-style: italic;
}
@media (max-width: 768px) {
.diagram-row {
flex-direction: column;
gap: 15px;
}
.diagram-arrow {
transform: rotate(90deg);
}
.types-grid {
grid-template-columns: 1fr;
}
.dle-info {
flex-direction: column;
align-items: flex-start;
gap: 5px;
}
}
</style>

View File

@@ -1,673 +0,0 @@
<!--
Copyright (c) 2024-2025 Тарабанов Александр Викторович
All rights reserved.
This software is proprietary and confidential.
Unauthorized copying, modification, or distribution is prohibited.
For licensing inquiries: info@hb3-accelerator.com
Website: https://hb3-accelerator.com
GitHub: https://github.com/HB3-ACCELERATOR
-->
<template>
<BaseLayout
:is-authenticated="isAuthenticated"
:identities="identities"
:token-balances="tokenBalances"
:is-loading-tokens="isLoadingTokens"
@auth-action-completed="$emit('auth-action-completed')"
>
<div class="timelock-module-deploy">
<!-- Заголовок -->
<div class="page-header">
<div class="header-content">
<h1>Деплой TimelockModule</h1>
<p>Задержки исполнения - безопасность критических операций через таймлоки</p>
<p v-if="dleAddress" class="dle-address">
<strong>DLE:</strong> {{ dleAddress }}
</p>
</div>
<button class="close-btn" @click="router.push('/management/modules')">×</button>
</div>
<!-- Информация о модуле -->
<div class="module-info">
<div class="info-card">
<h3> TimelockModule</h3>
<div class="info-grid">
<div class="info-item">
<strong>Назначение:</strong> Безопасность критических операций
</div>
<div class="info-item">
<strong>Функции:</strong> Настраиваемые таймлоки, отмена предложений, аудит
</div>
<div class="info-item">
<strong>Безопасность:</strong> Задержки исполнения для защиты от атак
</div>
</div>
</div>
</div>
<!-- Форма деплоя модуля администратором -->
<div v-if="canManageSettings" class="deploy-form">
<div class="form-header">
<h3>🔧 Деплой TimelockModule администратором</h3>
<p>Администратор деплоит модуль, затем создает предложение для добавления в DLE</p>
</div>
<div class="form-content">
<!-- Информация о сетях -->
<div class="networks-info">
<h4>📡 Сети для деплоя:</h4>
<div class="networks-list">
<div class="network-item">
<span class="network-name">Sepolia</span>
<span class="network-chain-id">Chain ID: 11155111</span>
</div>
<div class="network-item">
<span class="network-name">Holesky</span>
<span class="network-chain-id">Chain ID: 17000</span>
</div>
<div class="network-item">
<span class="network-name">Arbitrum Sepolia</span>
<span class="network-chain-id">Chain ID: 421614</span>
</div>
<div class="network-item">
<span class="network-name">Base Sepolia</span>
<span class="network-chain-id">Chain ID: 84532</span>
</div>
</div>
</div>
<!-- Настройки модуля -->
<div class="module-settings">
<h4> Настройки TimelockModule:</h4>
<div class="settings-form">
<!-- Поля администратора -->
<div class="admin-section">
<h5>🔐 Настройки администратора:</h5>
<div class="form-row">
<div class="form-group">
<label for="adminPrivateKey">Приватный ключ администратора:</label>
<input
type="password"
id="adminPrivateKey"
v-model="moduleSettings.adminPrivateKey"
class="form-control"
placeholder="0x..."
required
>
<small class="form-help">Приватный ключ для деплоя модуля (администратор платит газ)</small>
</div>
<div class="form-group">
<label for="etherscanApiKey">Etherscan API ключ:</label>
<input
type="text"
id="etherscanApiKey"
v-model="moduleSettings.etherscanApiKey"
class="form-control"
placeholder="YourAPIKey..."
>
<small class="form-help">API ключ для автоматической верификации контрактов</small>
</div>
</div>
</div>
<div class="simple-info">
<h5>📋 Информация о TimelockModule:</h5>
<div class="info-text">
<p><strong>TimelockModule</strong> будет задеплоен с настройками по умолчанию:</p>
<ul>
<li> Стандартная задержка: 2 дня</li>
<li> Экстренная задержка: 30 минут</li>
<li> Автоматическое исполнение операций</li>
<li> Готовые настройки безопасности</li>
</ul>
<p><em>После деплоя настройки можно будет изменить через governance.</em></p>
</div>
</div>
</div>
</div>
<!-- Кнопка деплоя -->
<div class="deploy-actions">
<button
class="btn btn-primary btn-large deploy-module"
@click="deployTimelockModule"
:disabled="isDeploying || !dleAddress || !isFormValid"
>
<i class="fas fa-rocket" :class="{ 'fa-spin': isDeploying }"></i>
{{ isDeploying ? 'Деплой модуля...' : 'Деплой TimelockModule' }}
</button>
<div v-if="!isFormValid && !isDeploying" class="form-validation-info">
<i class="fas fa-exclamation-triangle"></i>
<span>Заполните приватный ключ и API ключ для деплоя</span>
</div>
<div v-if="deploymentProgress" class="deployment-progress">
<div class="progress-info">
<span>{{ deploymentProgress.message }}</span>
<span class="progress-percentage">{{ deploymentProgress.percentage }}%</span>
</div>
<div class="progress-bar">
<div class="progress-fill" :style="{ width: deploymentProgress.percentage + '%' }"></div>
</div>
</div>
</div>
</div>
</div>
<!-- Сообщение для пользователей без прав доступа -->
<div v-if="!canManageSettings" class="no-access-message">
<div class="message-content">
<h3>🔒 Нет прав доступа</h3>
<p>У вас нет прав для деплоя смарт-контрактов. Только пользователи с ролью Editor могут выполнять деплой.</p>
<button class="btn btn-secondary" @click="router.push('/management/modules')">
Вернуться к модулям
</button>
</div>
</div>
</div>
</BaseLayout>
</template>
<script setup>
import { defineProps, defineEmits, ref, computed, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import BaseLayout from '../../../components/BaseLayout.vue';
import { usePermissions } from '@/composables/usePermissions';
// Определяем props
const props = defineProps({
isAuthenticated: { type: Boolean, default: false },
identities: { type: Array, default: () => [] },
tokenBalances: { type: Object, default: () => ({}) },
isLoadingTokens: { type: Boolean, default: false }
});
// Определяем emits
const emit = defineEmits(['auth-action-completed']);
const router = useRouter();
const route = useRoute();
const { canEdit, canManageSettings } = usePermissions();
// Состояние
const isLoading = ref(false);
const dleAddress = ref(route.query.address || null);
const isDeploying = ref(false);
const deploymentProgress = ref(null);
// Настройки модуля
const moduleSettings = ref({
// Поля администратора
adminPrivateKey: '',
etherscanApiKey: ''
});
// Проверка валидности формы
const isFormValid = computed(() => {
return moduleSettings.value.adminPrivateKey && moduleSettings.value.etherscanApiKey;
});
// Функция деплоя TimelockModule
async function deployTimelockModule() {
if (!canManageSettings.value) {
alert('У вас нет прав для деплоя смарт-контрактов');
return;
}
try {
isDeploying.value = true;
deploymentProgress.value = {
message: 'Инициализация деплоя...',
percentage: 0
};
console.log('[TimelockModuleDeployView] Начинаем деплой TimelockModule для DLE:', dleAddress.value);
// Вызываем API для деплоя модуля администратором
const response = await fetch('/api/dle-modules/deploy-timelock-admin', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
dleAddress: dleAddress.value,
moduleType: 'timelock',
adminPrivateKey: moduleSettings.value.adminPrivateKey,
etherscanApiKey: moduleSettings.value.etherscanApiKey,
settings: {
// Используем настройки по умолчанию
useDefaultSettings: true
}
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result.success) {
console.log('[TimelockModuleDeployView] Деплой успешно запущен:', result);
// Обновляем прогресс
deploymentProgress.value = {
message: 'Деплой запущен успешно! Проверьте логи для отслеживания прогресса.',
percentage: 100
};
// Показываем детальную информацию о деплое
const deployInfo = result.data || {};
const deployedAddresses = deployInfo.addresses || [];
let successMessage = '✅ TimelockModule успешно задеплоен!\n\n';
successMessage += `📊 Детали деплоя:\n`;
successMessage += `• DLE: ${dleAddress.value}\n`;
successMessage += `• Тип модуля: TimelockModule\n`;
successMessage += `• Адрес модуля: ${deployInfo.moduleAddress || 'Не указан'}\n`;
if (deployedAddresses.length > 0) {
successMessage += `\n🌐 Задеплоенные адреса:\n`;
deployedAddresses.forEach((addr, index) => {
successMessage += `${index + 1}. ${addr.network}: ${addr.address}\n`;
});
}
successMessage += `\n📝 Следующий шаг: Создайте предложение для добавления модуля в DLE через governance.`;
alert(successMessage);
// Перенаправляем обратно к модулям
setTimeout(() => {
router.push(`/management/modules?address=${dleAddress.value}`);
}, 3000);
} else {
throw new Error(result.error || 'Неизвестная ошибка');
}
} catch (error) {
console.error('[TimelockModuleDeployView] Ошибка деплоя:', error);
alert('❌ Ошибка деплоя: ' + error.message);
deploymentProgress.value = {
message: 'Ошибка деплоя: ' + error.message,
percentage: 0
};
} finally {
isDeploying.value = false;
}
}
// Инициализация
onMounted(() => {
console.log('[TimelockModuleDeployView] Страница загружена');
});
</script>
<style scoped>
.timelock-module-deploy {
padding: 20px;
background-color: var(--color-white);
border-radius: var(--radius-lg);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin-top: 20px;
margin-bottom: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #f0f0f0;
}
.page-header h1 {
color: var(--color-primary);
font-size: 2rem;
margin: 0;
}
.page-header p {
margin: 10px 0 0 0;
color: #666;
}
.dle-address {
margin-top: 10px !important;
font-family: monospace;
background: #f8f9fa;
padding: 8px 12px;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.close-btn {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: #666;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: all 0.2s;
}
.close-btn:hover {
background: #f0f0f0;
color: #333;
}
/* Форма деплоя */
.deploy-form {
background: #f8f9fa;
border-radius: var(--radius-md);
padding: 20px;
margin-bottom: 30px;
border: 1px solid #e9ecef;
}
.form-header h3 {
margin: 0 0 10px 0;
color: var(--color-primary);
}
.form-header p {
margin: 0 0 20px 0;
color: #666;
}
.networks-info,
.module-settings {
margin-bottom: 20px;
padding: 15px;
background: white;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.settings-form {
margin-top: 15px;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-bottom: 15px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #333;
}
.form-control {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: var(--radius-sm);
font-size: 14px;
transition: border-color 0.2s;
}
.form-control:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(var(--color-primary-rgb), 0.1);
}
.form-help {
display: block;
margin-top: 5px;
font-size: 12px;
color: #666;
line-height: 1.4;
}
/* Секция администратора */
.admin-section {
margin-bottom: 20px;
padding: 20px;
background: #fff3cd;
border-radius: var(--radius-sm);
border: 1px solid #ffeaa7;
}
.admin-section h5 {
margin: 0 0 15px 0;
color: #856404;
font-size: 1.1rem;
font-weight: 600;
}
/* Дополнительные настройки */
.advanced-settings {
margin-top: 20px;
padding: 20px;
background: #f8f9fa;
border-radius: var(--radius-sm);
border: 1px solid #e9ecef;
}
.advanced-settings h5 {
margin: 0 0 15px 0;
color: var(--color-primary);
font-size: 1.1rem;
font-weight: 600;
}
.advanced-settings .form-row {
margin-bottom: 15px;
}
.advanced-settings .form-group {
margin-bottom: 15px;
}
.advanced-settings .form-group:last-child {
margin-bottom: 0;
}
.deploy-actions {
text-align: center;
margin-top: 20px;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: var(--radius-md);
cursor: pointer;
font-size: 16px;
font-weight: 500;
transition: all 0.3s;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark));
color: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.btn-primary:hover:not(:disabled) {
background: linear-gradient(135deg, var(--color-primary-dark), var(--color-primary));
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
transform: translateY(-1px);
}
.btn-primary:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.btn-large {
padding: 16px 32px;
font-size: 18px;
}
.deployment-progress {
margin-top: 20px;
padding: 15px;
background: white;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.progress-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.progress-percentage {
font-weight: 600;
color: var(--color-primary);
}
.progress-bar {
width: 100%;
height: 8px;
background: #f0f0f0;
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--color-primary), var(--color-primary-dark));
transition: width 0.3s ease;
}
/* Информация о модуле */
.module-info {
margin-bottom: 30px;
}
.info-card {
background: #f8f9fa;
border-radius: var(--radius-md);
padding: 20px;
border: 1px solid #e9ecef;
}
.info-card h3 {
margin: 0 0 15px 0;
color: var(--color-primary);
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
}
.info-item {
padding: 10px;
background: white;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.info-item strong {
color: var(--color-primary);
}
/* Плейсхолдер для формы */
.deploy-form-placeholder {
background: #f8f9fa;
border-radius: var(--radius-md);
padding: 40px;
text-align: center;
border: 2px dashed #dee2e6;
}
.placeholder-content h3 {
color: var(--color-primary);
margin-bottom: 10px;
}
.placeholder-content p {
color: #666;
margin: 0;
}
/* Сообщение об отсутствии прав доступа */
.no-access-message {
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: var(--radius-md);
padding: 30px;
margin: 20px 0;
text-align: center;
}
.message-content h3 {
color: #856404;
margin-bottom: 15px;
font-size: 1.4em;
}
.message-content p {
color: #856404;
margin-bottom: 20px;
font-size: 1.1em;
line-height: 1.5;
}
.message-content .btn {
background: #6c757d;
color: white;
border: none;
border-radius: var(--radius-sm);
padding: 12px 24px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.message-content .btn:hover {
background: #5a6268;
}
/* Адаптивность */
@media (max-width: 768px) {
.info-grid {
grid-template-columns: 1fr;
}
.page-header {
flex-direction: column;
align-items: flex-start;
gap: 15px;
}
}
</style>

View File

@@ -1,886 +0,0 @@
<!--
Copyright (c) 2024-2025 Тарабанов Александр Викторович
All rights reserved.
This software is proprietary and confidential.
Unauthorized copying, modification, or distribution is prohibited.
For licensing inquiries: info@hb3-accelerator.com
Website: https://hb3-accelerator.com
GitHub: https://github.com/HB3-ACCELERATOR
-->
<template>
<BaseLayout
:is-authenticated="isAuthenticated"
:identities="identities"
:token-balances="tokenBalances"
:is-loading-tokens="isLoadingTokens"
@auth-action-completed="$emit('auth-action-completed')"
>
<div class="treasury-module-deploy">
<!-- Заголовок -->
<div class="page-header">
<div class="header-content">
<h1>Деплой TreasuryModule</h1>
<p>Казначейство DLE - управление финансами, депозиты, выводы, дивиденды</p>
<p v-if="dleAddress" class="dle-address">
<strong>DLE:</strong> {{ dleAddress }}
</p>
</div>
<button class="close-btn" @click="router.push('/management/modules')">×</button>
</div>
<!-- Информация о модуле -->
<div class="module-info">
<div class="info-card">
<h3>💰 TreasuryModule</h3>
<div class="info-grid">
<div class="info-item">
<strong>Назначение:</strong> Управление финансами DLE
</div>
<div class="info-item">
<strong>Функции:</strong> Депозиты, выводы, дивиденды, бюджетирование
</div>
<div class="info-item">
<strong>Безопасность:</strong> Все операции через голосование
</div>
</div>
</div>
</div>
<!-- Форма деплоя модуля администратором -->
<div v-if="canManageSettings" class="deploy-form">
<div class="form-header">
<h3>🌐 Деплой TreasuryModule во всех сетях</h3>
<p>Администратор деплоит модуль во всех 4 сетях одновременно, затем создает предложение для добавления в DLE</p>
</div>
<div class="form-content">
<!-- Информация о сетях -->
<div class="networks-info">
<h4>📡 Сети для деплоя:</h4>
<div class="networks-list">
<div class="network-item">
<span class="network-name">Sepolia</span>
<span class="network-chain-id">Chain ID: 11155111</span>
</div>
<div class="network-item">
<span class="network-name">Holesky</span>
<span class="network-chain-id">Chain ID: 17000</span>
</div>
<div class="network-item">
<span class="network-name">Arbitrum Sepolia</span>
<span class="network-chain-id">Chain ID: 421614</span>
</div>
<div class="network-item">
<span class="network-name">Base Sepolia</span>
<span class="network-chain-id">Chain ID: 84532</span>
</div>
</div>
</div>
<!-- Настройки модуля -->
<div class="module-settings">
<h4> Настройки TreasuryModule:</h4>
<div class="settings-form">
<!-- Поля администратора -->
<div class="admin-section">
<h5>🔐 Настройки администратора:</h5>
<div class="form-row">
<div class="form-group">
<label for="adminPrivateKey">Приватный ключ администратора:</label>
<input
type="password"
id="adminPrivateKey"
v-model="moduleSettings.adminPrivateKey"
class="form-control"
placeholder="0x..."
required
>
<small class="form-help">Приватный ключ для деплоя модуля (администратор платит газ)</small>
</div>
<div class="form-group">
<label for="etherscanApiKey">Etherscan API ключ:</label>
<input
type="text"
id="etherscanApiKey"
v-model="moduleSettings.etherscanApiKey"
class="form-control"
placeholder="YourAPIKey..."
>
<small class="form-help">API ключ для автоматической верификации контрактов</small>
</div>
</div>
</div>
<div class="simple-info">
<h5>📋 Информация о TreasuryModule:</h5>
<div class="info-text">
<p><strong>TreasuryModule</strong> будет задеплоен с настройками по умолчанию:</p>
<ul>
<li> Поддержка ETH и основных ERC20 токенов</li>
<li> Стандартные задержки для безопасности</li>
<li> Автоматическая настройка для всех сетей</li>
<li> Готовые настройки безопасности</li>
</ul>
<p><em>После деплоя настройки можно будет изменить через governance.</em></p>
</div>
</div>
</div>
</div>
<!-- Кнопка деплоя -->
<div class="deploy-actions">
<button
class="btn btn-primary btn-large deploy-module"
@click="deployTreasuryModule"
:disabled="isDeploying || !dleAddress || !isFormValid"
>
<i class="fas fa-rocket" :class="{ 'fa-spin': isDeploying }"></i>
{{ isDeploying ? 'Деплой во всех сетях...' : 'Деплой TreasuryModule во всех сетях' }}
</button>
<div v-if="!isFormValid && !isDeploying" class="form-validation-info">
<i class="fas fa-exclamation-triangle"></i>
<span>Заполните приватный ключ и API ключ для деплоя</span>
</div>
<div v-if="deploymentProgress" class="deployment-progress">
<div class="progress-info">
<span>{{ deploymentProgress.message }}</span>
<span class="progress-percentage">{{ deploymentProgress.percentage }}%</span>
</div>
<div class="progress-bar">
<div class="progress-fill" :style="{ width: deploymentProgress.percentage + '%' }"></div>
</div>
</div>
</div>
</div>
</div>
<!-- Сообщение для пользователей без прав доступа -->
<div v-if="!canManageSettings" class="no-access-message">
<div class="message-content">
<h3>🔒 Нет прав доступа</h3>
<p>У вас нет прав для деплоя смарт-контрактов. Только пользователи с ролью Editor могут выполнять деплой.</p>
<button class="btn btn-secondary" @click="router.push('/management/modules')">
Вернуться к модулям
</button>
</div>
</div>
</div>
</BaseLayout>
</template>
<script setup>
import { defineProps, defineEmits, ref, computed, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import BaseLayout from '../../../components/BaseLayout.vue';
import { usePermissions } from '@/composables/usePermissions';
// Определяем props
const props = defineProps({
isAuthenticated: { type: Boolean, default: false },
identities: { type: Array, default: () => [] },
tokenBalances: { type: Object, default: () => ({}) },
isLoadingTokens: { type: Boolean, default: false }
});
// Определяем emits
const emit = defineEmits(['auth-action-completed']);
const router = useRouter();
const route = useRoute();
const { canEdit, canManageSettings } = usePermissions();
// Состояние
const isLoading = ref(false);
const dleAddress = ref(route.query.address || null);
const isDeploying = ref(false);
const deploymentProgress = ref(null);
// Настройки модуля
const moduleSettings = ref({
// Поля администратора
adminPrivateKey: '',
etherscanApiKey: ''
});
// Проверка валидности формы
const isFormValid = computed(() => {
return moduleSettings.value.adminPrivateKey && moduleSettings.value.etherscanApiKey;
});
// Функция деплоя TreasuryModule
async function deployTreasuryModule() {
if (!canManageSettings.value) {
alert('У вас нет прав для деплоя смарт-контрактов');
return;
}
try {
isDeploying.value = true;
deploymentProgress.value = {
message: 'Инициализация деплоя...',
percentage: 0
};
console.log('[TreasuryModuleDeployView] Начинаем деплой TreasuryModule для DLE:', dleAddress.value);
// Вызываем API для деплоя модуля администратором
const response = await fetch('/api/dle-modules/deploy-treasury-admin', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
dleAddress: dleAddress.value,
moduleType: 'treasury',
adminPrivateKey: moduleSettings.value.adminPrivateKey,
etherscanApiKey: moduleSettings.value.etherscanApiKey,
settings: {
// Используем настройки по умолчанию
useDefaultSettings: true
}
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
if (result.success) {
console.log('[TreasuryModuleDeployView] Деплой успешно запущен:', result);
// Обновляем прогресс
deploymentProgress.value = {
message: 'Деплой запущен успешно! Проверьте логи для отслеживания прогресса.',
percentage: 100
};
// Показываем детальную информацию о деплое
const deployInfo = result.data || {};
const deployedAddresses = deployInfo.addresses || [];
let successMessage = '✅ TreasuryModule успешно задеплоен во всех сетях!\n\n';
successMessage += `📊 Детали деплоя:\n`;
successMessage += `• DLE: ${dleAddress.value}\n`;
successMessage += `• Тип модуля: TreasuryModule\n`;
successMessage += `• Сети: Sepolia, Holesky, Arbitrum Sepolia, Base Sepolia\n`;
if (deployedAddresses.length > 0) {
successMessage += `\n🌐 Задеплоенные адреса:\n`;
deployedAddresses.forEach((addr, index) => {
successMessage += `${index + 1}. ${addr.network}: ${addr.address}\n`;
});
}
successMessage += `\n📝 Следующий шаг: Создайте предложение для добавления модуля в DLE через governance.`;
alert(successMessage);
// Перенаправляем обратно к модулям
setTimeout(() => {
router.push(`/management/modules?address=${dleAddress.value}`);
}, 3000);
} else {
throw new Error(result.error || 'Неизвестная ошибка');
}
} catch (error) {
console.error('[TreasuryModuleDeployView] Ошибка деплоя:', error);
alert('❌ Ошибка деплоя: ' + error.message);
deploymentProgress.value = {
message: 'Ошибка деплоя: ' + error.message,
percentage: 0
};
} finally {
isDeploying.value = false;
}
}
// Инициализация
onMounted(() => {
console.log('[TreasuryModuleDeployView] Страница загружена');
});
</script>
<style scoped>
.treasury-module-deploy {
padding: 20px;
background-color: var(--color-white);
border-radius: var(--radius-lg);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin-top: 20px;
margin-bottom: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #f0f0f0;
}
.page-header h1 {
color: var(--color-primary);
font-size: 2rem;
margin: 0;
}
.page-header p {
margin: 10px 0 0 0;
color: #666;
}
.dle-address {
margin-top: 10px !important;
font-family: monospace;
background: #f8f9fa;
padding: 8px 12px;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.close-btn {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: #666;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: all 0.2s;
}
.close-btn:hover {
background: #f0f0f0;
color: #333;
}
/* Форма деплоя */
.deploy-form {
background: #f8f9fa;
border-radius: var(--radius-md);
padding: 20px;
margin-bottom: 30px;
border: 1px solid #e9ecef;
}
.form-header h3 {
margin: 0 0 10px 0;
color: var(--color-primary);
}
.form-header p {
margin: 0 0 20px 0;
color: #666;
}
.networks-info,
.module-settings {
margin-bottom: 20px;
padding: 15px;
background: white;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.settings-form {
margin-top: 15px;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-bottom: 15px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #333;
}
.form-control {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: var(--radius-sm);
font-size: 14px;
transition: border-color 0.2s;
}
.form-control:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(var(--color-primary-rgb), 0.1);
}
.form-help {
display: block;
margin-top: 5px;
font-size: 12px;
color: #666;
line-height: 1.4;
}
/* Секция администратора */
.admin-section {
margin-bottom: 20px;
padding: 20px;
background: #fff3cd;
border-radius: var(--radius-sm);
border: 1px solid #ffeaa7;
}
.admin-section h5 {
margin: 0 0 15px 0;
color: #856404;
font-size: 1.1rem;
font-weight: 600;
}
/* Простая информация */
.simple-info {
margin-top: 20px;
padding: 20px;
background: #f8f9fa;
border-radius: var(--radius-sm);
border: 1px solid #e9ecef;
}
.simple-info h5 {
margin: 0 0 15px 0;
color: var(--color-primary);
font-size: 1.1rem;
font-weight: 600;
}
.info-text {
color: #666;
line-height: 1.6;
}
.info-text ul {
margin: 10px 0;
padding-left: 20px;
}
.info-text li {
margin-bottom: 5px;
}
.token-item {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 10px;
}
.token-input-wrapper {
position: relative;
flex: 1;
}
.token-input {
width: 100%;
padding-right: 40px;
}
.token-input.is-valid {
border-color: #28a745;
box-shadow: 0 0 0 2px rgba(40, 167, 69, 0.1);
}
.token-input.is-invalid {
border-color: #dc3545;
box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.1);
}
.validation-icon {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
font-size: 10px;
}
.validation-icon.valid {
background: #28a745;
color: white;
}
.validation-icon.invalid {
background: #dc3545;
color: white;
}
.validation-icon.loading {
background: #6c757d;
color: white;
}
.remove-token {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: #dc3545;
border: none;
color: white;
font-size: 12px;
transition: all 0.2s ease;
flex-shrink: 0;
}
.remove-token:hover {
background: #c82333;
transform: scale(1.05);
}
.remove-token:disabled {
background: #6c757d;
cursor: not-allowed;
transform: none;
}
.add-token {
margin-top: 15px;
width: 100%;
padding: 12px 20px;
border: 2px dashed #28a745;
background: #f8f9fa;
color: #28a745;
border-radius: var(--radius-sm);
font-weight: 500;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.add-token:hover {
background: #28a745;
color: white;
border-color: #28a745;
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(40, 167, 69, 0.2);
}
/* Сообщение валидации */
.form-validation-info {
display: flex;
align-items: center;
gap: 8px;
margin-top: 15px;
padding: 10px 15px;
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: var(--radius-sm);
color: #856404;
font-size: 14px;
}
.form-validation-info i {
color: #f39c12;
}
/* Дополнительные настройки */
.advanced-settings {
margin-top: 20px;
padding: 20px;
background: #f8f9fa;
border-radius: var(--radius-sm);
border: 1px solid #e9ecef;
}
.advanced-settings h5 {
margin: 0 0 15px 0;
color: var(--color-primary);
font-size: 1.1rem;
font-weight: 600;
}
.advanced-settings .form-row {
margin-bottom: 15px;
}
.advanced-settings .form-group {
margin-bottom: 15px;
}
.advanced-settings .form-group:last-child {
margin-bottom: 0;
}
.networks-info h4,
.deploy-parameters h4 {
margin: 0 0 15px 0;
color: var(--color-primary);
}
.networks-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 10px;
}
.network-item {
display: flex;
flex-direction: column;
padding: 10px;
background: #f8f9fa;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.network-name {
font-weight: 600;
color: var(--color-primary);
}
.network-chain-id {
font-size: 12px;
color: #666;
font-family: monospace;
}
.parameter-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
}
.parameter-item:last-child {
border-bottom: none;
}
.parameter-item label {
font-weight: 500;
color: #333;
}
.parameter-value {
font-family: monospace;
color: var(--color-primary);
background: #f8f9fa;
padding: 4px 8px;
border-radius: var(--radius-sm);
font-size: 14px;
}
.deploy-actions {
text-align: center;
margin-top: 20px;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: var(--radius-md);
cursor: pointer;
font-size: 16px;
font-weight: 500;
transition: all 0.3s;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark));
color: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.btn-primary:hover:not(:disabled) {
background: linear-gradient(135deg, var(--color-primary-dark), var(--color-primary));
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
transform: translateY(-1px);
}
.btn-primary:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.btn-large {
padding: 16px 32px;
font-size: 18px;
}
.deployment-progress {
margin-top: 20px;
padding: 15px;
background: white;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.progress-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.progress-percentage {
font-weight: 600;
color: var(--color-primary);
}
.progress-bar {
width: 100%;
height: 8px;
background: #f0f0f0;
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--color-primary), var(--color-primary-dark));
transition: width 0.3s ease;
}
/* Информация о модуле */
.module-info {
margin-bottom: 30px;
}
.info-card {
background: #f8f9fa;
border-radius: var(--radius-md);
padding: 20px;
border: 1px solid #e9ecef;
}
.info-card h3 {
margin: 0 0 15px 0;
color: var(--color-primary);
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
}
.info-item {
padding: 10px;
background: white;
border-radius: var(--radius-sm);
border: 1px solid #dee2e6;
}
.info-item strong {
color: var(--color-primary);
}
/* Плейсхолдер для формы */
.deploy-form-placeholder {
background: #f8f9fa;
border-radius: var(--radius-md);
padding: 40px;
text-align: center;
border: 2px dashed #dee2e6;
}
.placeholder-content h3 {
color: var(--color-primary);
margin-bottom: 10px;
}
.placeholder-content p {
color: #666;
margin: 0;
}
/* Сообщение об отсутствии прав доступа */
.no-access-message {
background: #fff3cd;
border: 1px solid #ffeaa7;
border-radius: var(--radius-md);
padding: 30px;
margin: 20px 0;
text-align: center;
}
.message-content h3 {
color: #856404;
margin-bottom: 15px;
font-size: 1.4em;
}
.message-content p {
color: #856404;
margin-bottom: 20px;
font-size: 1.1em;
line-height: 1.5;
}
.message-content .btn {
background: #6c757d;
color: white;
border: none;
border-radius: var(--radius-sm);
padding: 12px 24px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.message-content .btn:hover {
background: #5a6268;
}
/* Адаптивность */
@media (max-width: 768px) {
.info-grid {
grid-template-columns: 1fr;
}
.page-header {
flex-direction: column;
align-items: flex-start;
gap: 15px;
}
}
</style>