hesabixCore/webUI/src/views/wizard/home.vue

3031 lines
71 KiB
Vue
Raw Normal View History

2025-04-12 18:50:34 +03:30
<template>
2025-07-19 16:34:23 +03:30
<!-- Header Toolbar -->
<v-app-bar
color="primary"
elevation="0"
class="ai-header"
height="70"
>
<div class="header-content">
<!-- Logo and Title -->
<div class="header-left">
<div class="logo-container">
<v-icon color="white" size="28">mdi-robot</v-icon>
2025-07-18 06:29:39 +03:30
</div>
2025-07-19 16:34:23 +03:30
<div class="title-section">
<h1 class="app-title">هوش مصنوعی حسابیکس</h1>
<p class="app-subtitle">دستیار هوشمند شما</p>
2025-07-18 06:29:39 +03:30
</div>
2025-07-19 16:34:23 +03:30
</div>
<!-- Status Indicators -->
<div class="header-center">
<div class="status-indicators">
<!-- AI Service Status -->
<v-tooltip location="bottom">
<template v-slot:activator="{ props }">
<div v-bind="props" class="status-item">
<v-chip
:color="aiSettings.aiEnabled ? 'success' : 'error'"
size="small"
variant="flat"
class="status-chip"
>
<v-icon start size="16">
{{ aiSettings.aiEnabled ? 'mdi-robot' : 'mdi-robot-off' }}
</v-icon>
{{ aiSettings.aiEnabled ? 'فعال' : 'غیرفعال' }}
</v-chip>
</div>
</template>
<span>وضعیت سرویس هوش مصنوعی</span>
</v-tooltip>
<!-- User Balance -->
<v-tooltip location="bottom">
<template v-slot:activator="{ props }">
<div v-bind="props" class="status-item">
<v-chip
:color="userBalance < 1000 ? 'warning' : 'success'"
size="small"
variant="flat"
class="status-chip"
>
<v-icon start size="16">mdi-wallet</v-icon>
{{ formatBalance(userBalance) }}
</v-chip>
</div>
</template>
<span>اعتبار باقیمانده حساب شما</span>
</v-tooltip>
<!-- Connection Status -->
<v-tooltip location="bottom">
<template v-slot:activator="{ props }">
<div v-bind="props" class="status-item">
<v-chip
:color="connectionStatus.color"
size="small"
variant="flat"
class="status-chip"
>
<v-icon start size="16">
{{ connectionStatus.icon }}
</v-icon>
{{ connectionStatus.text }}
</v-chip>
</div>
</template>
<span>وضعیت اتصال به سرور</span>
</v-tooltip>
2025-07-18 06:29:39 +03:30
</div>
2025-07-19 16:34:23 +03:30
</div>
<!-- Action Buttons -->
<div class="header-right">
<v-tooltip location="bottom">
<template v-slot:activator="{ props }">
<v-btn
v-bind="props"
icon
@click="showConversationsDialog = true"
class="action-btn"
color="white"
variant="text"
>
<v-icon>mdi-archive</v-icon>
</v-btn>
</template>
<span>مشاهده آرشیو گفتگوها</span>
</v-tooltip>
</div>
</div>
</v-app-bar>
<!-- Main Content -->
<div class="main-container">
<!-- Info Cards Section -->
<div class="info-section" v-if="aiSettings.aiEnabled">
<div class="info-cards">
<!-- Pricing Card -->
<v-card class="info-card pricing-card" elevation="1">
<div class="card-content">
<div class="pricing-row">
<v-icon color="info" size="16" class="pricing-icon">mdi-currency-usd</v-icon>
<div class="pricing-info">
<span class="pricing-label">قیمت:</span>
<span class="pricing-value">{{ aiSettings.inputTokenPrice.toLocaleString('fa-IR') }} / {{ aiSettings.outputTokenPrice.toLocaleString('fa-IR') }}</span>
<span class="pricing-unit">ریال/1000 توکن</span>
2025-07-18 02:30:04 +03:30
</div>
2025-07-18 06:29:39 +03:30
</div>
2025-07-19 16:34:23 +03:30
</div>
</v-card>
<!-- Usage Stats Card -->
<v-card class="info-card stats-card" elevation="1" v-if="userMessages.length > 0">
<div class="card-content">
<div class="stats-row">
<v-icon color="success" size="16" class="stats-icon">mdi-chart-line</v-icon>
<div class="stats-info">
<span class="stats-label">استفاده:</span>
<span class="stats-value">{{ userMessages.length }} پیام {{ usageStats.totalTokens.toLocaleString('fa-IR') }} توکن {{ usageStats.totalCost.toLocaleString('fa-IR') }} ریال</span>
2025-07-18 02:30:04 +03:30
</div>
</div>
</div>
2025-07-19 16:34:23 +03:30
</v-card>
</div>
2025-07-18 02:30:04 +03:30
</div>
2025-07-19 16:34:23 +03:30
<!-- Chat Container -->
<div class="chat-section">
<v-card class="chat-container" elevation="3">
<!-- Chat Header -->
2025-07-18 06:29:39 +03:30
<div class="chat-header" v-if="currentConversation">
2025-07-19 16:34:23 +03:30
<div class="conversation-info">
<div class="conversation-title">
<v-icon color="primary" size="20">mdi-chat</v-icon>
<span class="title-text">{{ currentConversation.title }}</span>
<v-chip size="small" variant="outlined" class="category-chip">
{{ currentConversation.category }}
</v-chip>
2025-07-18 02:30:04 +03:30
</div>
2025-07-19 16:34:23 +03:30
<v-btn
size="small"
variant="text"
color="primary"
@click="newConversation"
class="new-chat-btn"
>
<v-icon start size="16">mdi-plus</v-icon>
گفتگوی جدید
</v-btn>
2025-07-18 02:30:04 +03:30
</div>
2025-07-18 06:29:39 +03:30
</div>
2025-07-19 16:34:23 +03:30
<!-- Messages Area -->
<div class="messages-area">
2025-05-04 01:07:39 +03:30
<div class="messages-container" ref="messagesContainer">
2025-07-19 16:34:23 +03:30
<!-- Welcome Message -->
2025-05-04 01:07:39 +03:30
<div class="message ai-message" v-if="displayWelcome">
2025-07-19 16:34:23 +03:30
<div class="message-avatar">
<v-avatar color="primary" size="40">
<v-icon color="white" size="20">mdi-robot</v-icon>
</v-avatar>
</div>
2025-05-04 01:07:39 +03:30
<div class="message-content">
2025-07-19 16:34:23 +03:30
<div class="message-text" v-html="renderMarkdown(displayWelcome)"></div>
<div class="message-time">{{ formatTime(new Date()) }}</div>
2025-05-04 01:07:39 +03:30
</div>
</div>
2025-07-19 16:34:23 +03:30
<!-- User and AI Messages -->
2025-05-04 01:07:39 +03:30
<template v-for="(message, index) in userMessages" :key="index">
2025-07-19 16:34:23 +03:30
<!-- User Message -->
2025-05-04 01:07:39 +03:30
<div class="message user-message" v-if="typeof message === 'string'">
<div class="message-content">
<div class="message-text">{{ message }}</div>
2025-07-18 02:30:04 +03:30
<div class="message-time">{{ formatTime(new Date()) }}</div>
2025-05-04 01:07:39 +03:30
</div>
2025-07-19 16:34:23 +03:30
<div class="message-avatar">
<v-avatar color="grey lighten-2" size="40">
<v-icon color="grey darken-1" size="20">mdi-account</v-icon>
</v-avatar>
</div>
2025-05-04 01:07:39 +03:30
</div>
2025-07-19 16:34:23 +03:30
<!-- AI Message -->
2025-05-04 01:07:39 +03:30
<div class="message ai-message" v-else-if="message && message.isAI">
2025-07-19 16:34:23 +03:30
<div class="message-avatar">
<v-avatar
:color="message.text && message.text.startsWith('خطا:') ? 'error' : 'primary'"
size="40"
>
<v-icon color="white" size="20">
{{ message.text && message.text.startsWith('خطا:') ? 'mdi-alert-circle' : 'mdi-robot' }}
</v-icon>
</v-avatar>
</div>
2025-07-18 06:29:39 +03:30
<div class="message-content" :class="{
2025-07-19 16:34:23 +03:30
'error-message': message.text && message.text.startsWith('خطا:'),
'success-message': message.text && !message.text.startsWith('خطا:')
2025-07-18 06:29:39 +03:30
}">
<div class="message-text" v-html="renderMarkdown(message.text)"></div>
2025-07-19 16:34:23 +03:30
<!-- Debug Button -->
<div class="debug-section" v-if="message.debug_info">
<v-btn
size="small"
variant="outlined"
color="info"
@click="openDebugDialog(message.debug_info, index)"
class="debug-btn"
prepend-icon="mdi-bug"
>
<span>دیباگ</span>
<v-chip
size="x-small"
color="info"
variant="tonal"
class="debug-count"
v-if="getDebugInfoCount(message.debug_info) > 0"
>
{{ getDebugInfoCount(message.debug_info) }}
</v-chip>
</v-btn>
2025-07-19 13:49:33 +03:30
</div>
2025-07-19 16:34:23 +03:30
<!-- Charge Button -->
<div class="charge-section" v-if="message.showChargeButton">
2025-07-18 06:29:39 +03:30
<v-btn
color="success"
variant="elevated"
size="small"
@click="goToChargePage"
prepend-icon="mdi-credit-card"
class="charge-btn"
>
شارژ حساب
</v-btn>
</div>
2025-07-18 02:30:04 +03:30
<div class="message-time">{{ formatTime(new Date()) }}</div>
2025-05-04 01:07:39 +03:30
</div>
</div>
</template>
2025-07-19 16:34:23 +03:30
<!-- Typing Indicator -->
2025-05-04 01:07:39 +03:30
<div class="message ai-message" v-if="isTyping">
2025-07-19 16:34:23 +03:30
<div class="message-avatar">
<v-avatar color="primary" size="40">
<v-icon color="white" size="20">mdi-robot</v-icon>
</v-avatar>
</div>
2025-05-04 01:07:39 +03:30
<div class="message-content">
2025-07-19 16:34:23 +03:30
<div class="typing-indicator">
<span></span>
<span></span>
<span></span>
2025-05-04 01:07:39 +03:30
</div>
</div>
</div>
</div>
2025-07-19 16:34:23 +03:30
<!-- Suggestions -->
<div class="suggestions-section" v-if="userMessages.length === 0 && aiSettings.aiEnabled">
<div class="suggestions-title">
<v-icon color="primary" size="20">mdi-lightbulb</v-icon>
<span>پیشنهادات</span>
</div>
<div class="suggestions-grid">
2025-07-18 02:30:04 +03:30
<v-chip
v-for="suggestion in suggestions"
:key="suggestion"
2025-07-19 16:34:23 +03:30
size="small"
variant="outlined"
2025-07-18 02:30:04 +03:30
@click="useSuggestion(suggestion)"
class="suggestion-chip"
2025-07-18 06:29:39 +03:30
color="primary"
2025-07-18 02:30:04 +03:30
>
{{ suggestion }}
</v-chip>
</div>
2025-05-04 01:07:39 +03:30
</div>
2025-07-19 16:34:23 +03:30
</div>
</v-card>
</div>
2025-07-18 06:29:39 +03:30
2025-07-19 16:34:23 +03:30
<!-- Input Section -->
<div class="input-section">
<div class="input-container">
<v-text-field
v-model="userMessage"
class="message-input"
:placeholder="aiSettings.aiEnabled ? 'پیام خود را بنویسید...' : 'سرویس هوش مصنوعی غیرفعال است'"
hide-details="auto"
variant="outlined"
@keyup.enter="sendMessage"
:disabled="isLoading || !aiSettings.aiEnabled"
ref="messageInput"
autofocus
rounded
density="comfortable"
>
<template #append-inner>
2025-07-18 06:29:39 +03:30
<v-btn
color="primary"
2025-07-18 07:15:33 +03:30
:disabled="isLoading || !userMessage.trim() || !aiSettings.aiEnabled"
2025-07-18 06:29:39 +03:30
@click="sendMessage"
icon
2025-07-19 16:34:23 +03:30
class="send-btn"
2025-07-18 06:29:39 +03:30
>
2025-07-19 16:34:23 +03:30
<v-icon class="send-icon">mdi-send</v-icon>
2025-07-18 06:29:39 +03:30
</v-btn>
2025-07-19 16:34:23 +03:30
</template>
</v-text-field>
</div>
2025-05-04 01:07:39 +03:30
</div>
</div>
2025-07-18 06:29:39 +03:30
2025-07-19 16:34:23 +03:30
<!-- Conversations Dialog -->
<v-dialog v-model="showConversationsDialog" max-width="900px" persistent>
<v-card class="conversations-dialog">
<v-card-title class="dialog-header">
<div class="dialog-title">
<v-icon color="primary" size="24">mdi-archive</v-icon>
<span>آرشیو گفتگوها</span>
</div>
<v-btn icon @click="showConversationsDialog = false" variant="text">
2025-07-18 06:29:39 +03:30
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
2025-07-19 16:34:23 +03:30
<v-card-text class="dialog-content">
<!-- Filters -->
<div class="filters-section">
2025-07-18 06:29:39 +03:30
<v-text-field
v-model="searchTerm"
placeholder="جستجو در گفتگوها..."
prepend-inner-icon="mdi-magnify"
variant="outlined"
density="compact"
hide-details
2025-07-19 16:34:23 +03:30
class="search-field"
2025-07-18 06:29:39 +03:30
@input="searchConversations"
></v-text-field>
<v-select
v-model="selectedCategory"
:items="categories"
placeholder="دسته‌بندی"
variant="outlined"
density="compact"
hide-details
2025-07-19 16:34:23 +03:30
class="category-field"
2025-07-18 06:29:39 +03:30
@update:model-value="filterByCategory"
></v-select>
</div>
2025-07-19 16:34:23 +03:30
<!-- Conversations List -->
2025-07-18 06:29:39 +03:30
<div class="conversations-list">
2025-07-19 16:34:23 +03:30
<v-list class="conversations-items">
2025-07-18 06:29:39 +03:30
<v-list-item
v-for="conversation in filteredConversations"
:key="conversation.id"
class="conversation-item"
2025-07-19 16:34:23 +03:30
@click="loadConversation(conversation)"
2025-07-18 06:29:39 +03:30
>
<template #prepend>
2025-07-19 16:34:23 +03:30
<v-avatar color="primary" size="48">
2025-07-18 06:29:39 +03:30
<v-icon color="white">mdi-chat</v-icon>
</v-avatar>
</template>
2025-07-19 16:34:23 +03:30
<v-list-item-title class="conversation-title">
{{ conversation.title }}
</v-list-item-title>
<v-list-item-subtitle class="conversation-details">
<div class="conversation-meta">
<v-chip size="x-small" variant="tonal" color="primary">
{{ conversation.category }}
</v-chip>
<span class="conversation-date">{{ conversation.updatedAt }}</span>
2025-07-18 06:29:39 +03:30
</div>
2025-07-19 16:34:23 +03:30
<div class="conversation-stats">
{{ conversation.messageCount || 0 }} پیام
{{ (conversation.stats?.totalTokens || 0).toLocaleString('fa-IR') }} توکن
{{ (conversation.stats?.totalCost || 0).toLocaleString('fa-IR') }} ریال
2025-07-18 06:29:39 +03:30
</div>
</v-list-item-subtitle>
<template #append>
<v-btn
variant="text"
size="small"
color="error"
@click.stop="deleteConversation(conversation)"
class="delete-btn"
icon
>
<v-icon size="18">mdi-delete</v-icon>
<v-tooltip activator="parent" location="top">حذف گفتگو</v-tooltip>
</v-btn>
</template>
</v-list-item>
</v-list>
</div>
</v-card-text>
2025-07-19 16:34:23 +03:30
<v-card-actions class="dialog-actions">
2025-07-18 06:29:39 +03:30
<v-spacer></v-spacer>
<v-btn
color="primary"
@click="newConversation"
prepend-icon="mdi-plus"
2025-07-19 16:34:23 +03:30
variant="elevated"
2025-07-18 06:29:39 +03:30
>
گفتگوی جدید
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
2025-07-19 16:34:23 +03:30
<!-- Delete Confirmation Dialog -->
<v-dialog v-model="showDeleteDialog" max-width="450px" persistent>
<v-card class="delete-dialog">
<v-card-title class="delete-header">
<v-icon color="error" size="24">mdi-delete</v-icon>
<span>حذف گفتگو</span>
2025-07-18 06:29:39 +03:30
</v-card-title>
2025-07-19 16:34:23 +03:30
<v-card-text class="delete-content">
<p class="delete-message">
2025-07-18 06:29:39 +03:30
آیا مطمئن هستید که میخواهید گفتگوی
<strong>"{{ conversationToDelete?.title }}"</strong>
را حذف کنید؟
</p>
2025-07-19 16:34:23 +03:30
<p class="delete-warning">
2025-07-18 06:29:39 +03:30
این عملیات قابل بازگشت نیست و گفتگو از لیست شما حذف خواهد شد.
</p>
</v-card-text>
2025-07-19 16:34:23 +03:30
<v-card-actions class="delete-actions">
2025-07-18 06:29:39 +03:30
<v-spacer></v-spacer>
<v-btn
variant="text"
@click="showDeleteDialog = false"
color="grey"
>
انصراف
</v-btn>
<v-btn
color="error"
@click="confirmDeleteConversation"
:loading="isLoading"
prepend-icon="mdi-delete"
2025-07-19 16:34:23 +03:30
variant="elevated"
2025-07-18 06:29:39 +03:30
>
حذف
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
2025-07-18 19:59:35 +03:30
2025-07-19 16:34:23 +03:30
<!-- Person Info Dialog -->
<v-dialog v-model="showPersonInfo" max-width="1000px" persistent>
<v-card class="person-dialog">
<v-card-title class="dialog-header">
<div class="dialog-title">
<v-icon color="primary" size="24">mdi-account</v-icon>
<span>اطلاعات شخص</span>
</div>
<v-btn icon @click="showPersonInfo = false" variant="text">
2025-07-18 19:59:35 +03:30
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
2025-07-19 16:34:23 +03:30
<v-card-text class="dialog-content">
2025-07-18 19:59:35 +03:30
<PersonInfo
:person="selectedPerson"
:transactions="personTransactions"
/>
</v-card-text>
</v-card>
</v-dialog>
2025-07-19 16:34:23 +03:30
<!-- Debug Dialog -->
<v-dialog v-model="showDebugDialog" max-width="1400px" fullscreen persistent>
<v-card class="debug-dialog">
<!-- Debug Header -->
<v-card-title class="debug-header">
<div class="debug-title-section">
<div class="debug-icon">
<v-icon color="white" size="28">mdi-bug</v-icon>
</div>
<div class="debug-info">
<h2 class="debug-title">اطلاعات دیباگ</h2>
<p class="debug-subtitle">جزئیات کامل عملیات و نتایج</p>
</div>
<v-chip
size="small"
color="info"
variant="flat"
class="debug-count"
v-if="currentDebugInfo && getDebugInfoCount(currentDebugInfo) > 0"
>
{{ getDebugInfoCount(currentDebugInfo) }} آیتم
</v-chip>
</div>
<v-btn
icon
@click="showDebugDialog = false"
color="white"
variant="text"
class="close-btn"
>
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<!-- Debug Content -->
<v-card-text class="debug-content" v-if="currentDebugInfo">
<!-- Main Cards -->
<div class="debug-main-cards">
<!-- Operation Type Card -->
<v-card
v-if="currentDebugInfo.operation_type"
class="debug-main-card operation-card"
elevation="3"
>
<div class="card-header">
<div class="card-icon">
<v-icon color="white" size="24">mdi-cog</v-icon>
</div>
<div class="card-content">
<h3 class="card-title">نوع عملیات</h3>
<v-chip
:color="getOperationTypeColor(currentDebugInfo.operation_type)"
size="small"
variant="flat"
class="operation-chip"
>
{{ getOperationTypeText(currentDebugInfo.operation_type) }}
</v-chip>
</div>
</div>
</v-card>
<!-- Current Step Card -->
<v-card
v-if="currentDebugInfo.current_step"
class="debug-main-card status-card"
elevation="3"
>
<div class="card-header">
<div class="card-icon">
<v-icon color="white" size="24">mdi-play-circle</v-icon>
</div>
<div class="card-content">
<h3 class="card-title">مرحله فعلی</h3>
<p class="card-text">{{ currentDebugInfo.current_step }}</p>
</div>
</div>
</v-card>
<!-- Attempt Count Card -->
<v-card
v-if="currentDebugInfo.attempt_count"
class="debug-main-card attempt-card"
elevation="3"
>
<div class="card-header">
<div class="card-icon">
<v-icon color="white" size="24">mdi-repeat</v-icon>
</div>
<div class="card-content">
<h3 class="card-title">تعداد تلاش</h3>
<div class="attempt-progress">
<v-progress-linear
:model-value="(currentDebugInfo.attempt_count / currentDebugInfo.max_attempts) * 100"
color="warning"
height="10"
class="progress-bar"
></v-progress-linear>
<span class="attempt-text">
{{ currentDebugInfo.attempt_count }} از {{ currentDebugInfo.max_attempts }}
</span>
</div>
</div>
</div>
</v-card>
<!-- Next Action Card -->
<v-card
v-if="currentDebugInfo.next_action"
class="debug-main-card next-card"
elevation="3"
>
<div class="card-header">
<div class="card-icon">
<v-icon color="white" size="24">mdi-arrow-right</v-icon>
</div>
<div class="card-content">
<h3 class="card-title">مرحله بعدی</h3>
<p class="card-text">{{ currentDebugInfo.next_action }}</p>
</div>
</div>
</v-card>
</div>
<!-- Results Section -->
<div class="debug-results-section">
<!-- Tool Commands -->
<v-card
v-if="currentDebugInfo.tool_commands && currentDebugInfo.tool_commands.length > 0"
class="debug-section-card"
elevation="2"
>
<v-card-title class="section-title">
<v-icon color="primary" size="20" class="mr-2">mdi-tools</v-icon>
دستورات ابزار یافت شده
<v-chip size="small" color="primary" variant="tonal" class="ml-2">
{{ currentDebugInfo.tool_commands.length }}
</v-chip>
</v-card-title>
<v-card-text class="section-content">
<div class="tools-grid">
<div
v-for="(command, cmdIndex) in currentDebugInfo.tool_commands"
:key="cmdIndex"
class="tool-item"
>
<div class="tool-header">
<v-icon color="primary" size="18" class="mr-2">mdi-wrench</v-icon>
<span class="tool-name">{{ command.tool }}</span>
</div>
<div class="tool-actions">
<v-btn
size="small"
variant="outlined"
color="info"
@click="toggleJsonView(`tool-${cmdIndex}`)"
class="view-btn"
>
<v-icon size="16" class="mr-1">mdi-code-json</v-icon>
نمایش پارامترها
</v-btn>
</div>
<v-expand-transition>
<div v-show="expandedJsonViews[`tool-${cmdIndex}`]" class="json-container">
<pre class="json-content">{{ JSON.stringify(command.params, null, 2) }}</pre>
</div>
</v-expand-transition>
</div>
</div>
</v-card-text>
</v-card>
<!-- Execution Results -->
<v-card
v-if="currentDebugInfo.execution_results && currentDebugInfo.execution_results.length > 0"
class="debug-section-card"
elevation="2"
>
<v-card-title class="section-title">
<v-icon color="success" size="20" class="mr-2">mdi-playlist-check</v-icon>
نتایج اجرای چندمرحلهای
<v-chip size="small" color="success" variant="tonal" class="ml-2">
{{ currentDebugInfo.execution_results.length }}
</v-chip>
</v-card-title>
<v-card-text class="section-content">
<div class="execution-timeline">
<div
v-for="(result, resultIndex) in currentDebugInfo.execution_results"
:key="resultIndex"
class="timeline-item"
>
<div class="timeline-marker">
<v-icon color="success" size="16">mdi-check-circle</v-icon>
</div>
<div class="timeline-content">
<div class="timeline-header">
<span class="timeline-title">مرحله {{ resultIndex + 1 }}</span>
</div>
<div class="timeline-actions">
<v-btn
size="small"
variant="outlined"
color="info"
@click="toggleJsonView(`execution-${resultIndex}`)"
class="view-btn"
>
<v-icon size="16" class="mr-1">mdi-code-json</v-icon>
نمایش نتیجه
</v-btn>
</div>
<v-expand-transition>
<div v-show="expandedJsonViews[`execution-${resultIndex}`]" class="json-container">
<pre class="json-content">{{ JSON.stringify(result, null, 2) }}</pre>
</div>
</v-expand-transition>
</div>
</div>
</div>
</v-card-text>
</v-card>
<!-- JSON Response -->
<v-card
v-if="currentDebugInfo.json_response"
class="debug-section-card"
elevation="2"
>
<v-card-title class="section-title">
<v-icon color="info" size="20" class="mr-2">mdi-code-json</v-icon>
پاسخ JSON
</v-card-title>
<v-card-text class="section-content">
<v-btn
size="small"
variant="outlined"
color="info"
@click="toggleJsonView('json-response')"
class="view-btn"
>
<v-icon size="16" class="mr-1">mdi-eye</v-icon>
نمایش JSON
</v-btn>
<v-expand-transition>
<div v-show="expandedJsonViews['json-response']" class="json-container">
<pre class="json-content">{{ JSON.stringify(currentDebugInfo.json_response, null, 2) }}</pre>
</div>
</v-expand-transition>
</v-card-text>
</v-card>
<!-- Final Data -->
<v-card
v-if="currentDebugInfo.final_data"
class="debug-section-card"
elevation="2"
>
<v-card-title class="section-title">
<v-icon color="success" size="20" class="mr-2">mdi-database</v-icon>
دادههای نهایی
</v-card-title>
<v-card-text class="section-content">
<v-btn
size="small"
variant="outlined"
color="info"
@click="toggleJsonView('final-data')"
class="view-btn"
>
<v-icon size="16" class="mr-1">mdi-eye</v-icon>
نمایش دادهها
</v-btn>
<v-expand-transition>
<div v-show="expandedJsonViews['final-data']" class="json-container">
<pre class="json-content">{{ JSON.stringify(currentDebugInfo.final_data, null, 2) }}</pre>
</div>
</v-expand-transition>
</v-card-text>
</v-card>
<!-- AI Response -->
<v-card
v-if="currentDebugInfo.ai_response"
class="debug-section-card"
elevation="2"
>
<v-card-title class="section-title">
<v-icon color="primary" size="20" class="mr-2">mdi-robot</v-icon>
پاسخ هوش مصنوعی
</v-card-title>
<v-card-text class="section-content">
<v-btn
size="small"
variant="outlined"
color="info"
@click="toggleJsonView('ai-response')"
class="view-btn"
>
<v-icon size="16" class="mr-1">mdi-eye</v-icon>
نمایش پاسخ
</v-btn>
<v-expand-transition>
<div v-show="expandedJsonViews['ai-response']" class="ai-response-container">
<div class="ai-response-content">{{ currentDebugInfo.ai_response }}</div>
</div>
</v-expand-transition>
</v-card-text>
</v-card>
<!-- Final Result -->
<v-card
v-if="currentDebugInfo.final_result"
class="debug-section-card success-card"
elevation="2"
>
<v-card-title class="section-title">
<v-icon color="success" size="20" class="mr-2">mdi-check-circle</v-icon>
نتیجه نهایی
</v-card-title>
<v-card-text class="section-content">
<div class="final-result-content">
<p class="result-text">{{ currentDebugInfo.final_result }}</p>
</div>
</v-card-text>
</v-card>
<!-- Error -->
<v-card
v-if="currentDebugInfo.error"
class="debug-section-card error-card"
elevation="2"
>
<v-card-title class="section-title">
<v-icon color="error" size="20" class="mr-2">mdi-alert-circle</v-icon>
خطا
</v-card-title>
<v-card-text class="section-content">
<div class="error-content">
<p class="error-text">{{ currentDebugInfo.error }}</p>
</div>
</v-card-text>
</v-card>
</div>
</v-card-text>
</v-card>
</v-dialog>
2025-04-12 18:50:34 +03:30
</template>
<script>
2025-07-18 06:29:39 +03:30
import { marked } from 'marked';
import DOMPurify from 'dompurify';
import axios from 'axios';
2025-07-18 19:59:35 +03:30
import PersonInfo from '@/components/PersonInfo.vue';
2025-07-18 02:30:04 +03:30
2025-04-12 18:50:34 +03:30
export default {
name: 'WizardHome',
2025-07-18 19:59:35 +03:30
components: {
PersonInfo
},
2025-04-12 18:50:34 +03:30
data() {
return {
2025-07-19 16:34:23 +03:30
// User Input
2025-05-04 01:07:39 +03:30
userMessage: '',
2025-07-18 06:29:39 +03:30
isLoading: false,
isTyping: false,
2025-07-19 16:34:23 +03:30
// Messages
userMessages: [],
displayWelcome: '',
// AI Settings
2025-07-18 02:30:04 +03:30
aiSettings: {
aiEnabled: false,
2025-07-18 06:29:39 +03:30
aiAgentSource: '',
aiModel: '',
2025-07-18 02:30:04 +03:30
inputTokenPrice: 0,
outputTokenPrice: 0,
aiPrompt: ''
},
2025-07-19 16:34:23 +03:30
// Status
2025-07-18 06:29:39 +03:30
connectionStatus: {
color: 'grey',
icon: 'mdi-help-circle',
text: 'در حال بررسی...'
},
2025-07-19 16:34:23 +03:30
// User Data
userBalance: 0,
// Conversations
2025-07-18 06:29:39 +03:30
currentConversation: null,
showConversationsDialog: false,
conversations: [],
filteredConversations: [],
searchTerm: '',
selectedCategory: '',
categories: [],
2025-07-19 16:34:23 +03:30
// Delete Dialog
2025-07-18 06:29:39 +03:30
showDeleteDialog: false,
2025-07-18 19:59:35 +03:30
conversationToDelete: null,
2025-07-19 16:34:23 +03:30
// Person Info
2025-07-18 19:59:35 +03:30
selectedPerson: null,
personTransactions: [],
showPersonInfo: false,
2025-07-19 16:34:23 +03:30
// Debug
expandedJsonViews: {},
showDebugDialog: false,
currentDebugInfo: null,
// Suggestions
suggestions: [
'چگونه سند حسابداری ثبت کنم؟',
'راهنمای انبارداری',
'ثبت خرید و فروش',
'گزارش‌های مالی',
'تنظیمات کاربران',
'اطلاعات اشخاص',
'جستجو در اشخاص',
'موجودی اشخاص',
'شخص علی اضافه کن',
'تلفن 09123456789 را برای محسن اضافه کن',
'شخص احمد را حذف کن'
]
2025-07-18 06:29:39 +03:30
};
2025-07-18 02:30:04 +03:30
},
2025-07-19 16:34:23 +03:30
2025-07-18 02:30:04 +03:30
computed: {
2025-07-18 06:29:39 +03:30
usageStats() {
let totalTokens = 0;
let totalCost = 0;
2025-07-19 16:34:23 +03:30
this.userMessages.forEach((msg) => {
2025-07-18 06:29:39 +03:30
if (msg && msg.isAI && msg.usage) {
2025-07-19 16:34:23 +03:30
const promptTokens = msg.usage.prompt_tokens || 0;
const completionTokens = msg.usage.completion_tokens || 0;
const messageTokens = promptTokens + completionTokens;
const messageCost = msg.cost?.total_cost || 0;
totalTokens += messageTokens;
totalCost += messageCost;
2025-07-18 06:29:39 +03:30
}
});
2025-07-19 16:34:23 +03:30
return { totalTokens, totalCost };
2025-05-04 01:07:39 +03:30
}
},
2025-07-19 16:34:23 +03:30
2025-05-04 01:07:39 +03:30
watch: {
2025-07-18 06:29:39 +03:30
searchTerm() {
this.searchConversations();
2025-05-04 01:07:39 +03:30
},
2025-07-18 06:29:39 +03:30
selectedCategory() {
this.filterByCategory();
2025-04-12 18:50:34 +03:30
}
},
2025-07-19 16:34:23 +03:30
2025-07-18 06:29:39 +03:30
async mounted() {
2025-07-19 16:34:23 +03:30
await this.initializeApp();
2025-07-18 06:29:39 +03:30
},
2025-07-19 16:34:23 +03:30
2025-04-12 18:50:34 +03:30
methods: {
2025-07-19 16:34:23 +03:30
// Initialization
async initializeApp() {
try {
await Promise.all([
this.loadSettings(),
this.checkConnection(),
this.loadConversations(),
this.loadBalance()
]);
this.setWelcomeMessage();
} catch (error) {
console.error('خطا در راه‌اندازی برنامه:', error);
}
},
// Settings & Status
2025-07-18 06:29:39 +03:30
async loadSettings() {
2025-07-18 02:30:04 +03:30
try {
2025-07-18 06:29:39 +03:30
const response = await axios.get('/api/wizard/settings');
2025-07-18 02:30:04 +03:30
if (response.data.success) {
2025-07-18 06:29:39 +03:30
this.aiSettings = response.data.settings;
2025-07-18 02:30:04 +03:30
}
} catch (error) {
2025-07-18 06:29:39 +03:30
console.error('خطا در بارگذاری تنظیمات:', error);
2025-07-18 02:30:04 +03:30
}
},
2025-07-19 16:34:23 +03:30
2025-07-18 06:29:39 +03:30
async checkConnection() {
2025-07-18 02:30:04 +03:30
try {
2025-07-18 06:29:39 +03:30
const response = await axios.get('/api/wizard/status');
2025-07-18 02:30:04 +03:30
if (response.data.success) {
2025-07-18 06:29:39 +03:30
const status = response.data.status;
2025-07-19 16:34:23 +03:30
this.updateConnectionStatus(status);
2025-07-18 02:30:04 +03:30
}
} catch (error) {
this.connectionStatus = {
color: 'error',
icon: 'mdi-alert-circle',
2025-07-19 16:34:23 +03:30
text: 'خطا در بررسی وضعیت'
2025-07-18 06:29:39 +03:30
};
console.error('خطا در بررسی وضعیت:', error);
}
},
2025-07-19 16:34:23 +03:30
updateConnectionStatus(status) {
const statusMap = {
'available': {
color: 'success',
icon: 'mdi-check-circle',
text: 'متصل'
},
'disabled': {
color: 'error',
icon: 'mdi-robot-off',
text: 'غیرفعال'
},
'no_api_key': {
color: 'warning',
icon: 'mdi-key-remove',
text: 'بدون کلید API'
2025-07-18 19:59:35 +03:30
}
2025-07-19 16:34:23 +03:30
};
this.connectionStatus = statusMap[status] || {
color: 'error',
icon: 'mdi-alert-circle',
text: 'خطا در اتصال'
};
2025-07-18 06:29:39 +03:30
},
async loadBalance() {
try {
const response = await axios.get('/api/wizard/balance');
if (response.data.success) {
this.userBalance = response.data.balance;
2025-07-18 02:30:04 +03:30
}
2025-07-18 06:29:39 +03:30
} catch (error) {
console.error('خطا در بارگذاری اعتبار:', error);
2025-07-18 02:30:04 +03:30
}
2025-05-04 01:07:39 +03:30
},
2025-07-19 16:34:23 +03:30
// Conversations
async loadConversations() {
try {
const response = await axios.post('/api/ai/conversations/list');
this.conversations = Array.isArray(response.data) ? response.data : [];
this.filteredConversations = [...this.conversations];
this.extractCategories();
} catch (error) {
console.error('خطا در بارگذاری گفتگوها:', error);
this.conversations = [];
this.filteredConversations = [];
this.categories = [];
2025-07-18 06:29:39 +03:30
}
2025-05-04 01:07:39 +03:30
},
2025-07-18 06:29:39 +03:30
2025-07-19 16:34:23 +03:30
extractCategories() {
const categorySet = new Set();
this.conversations.forEach(conv => {
if (conv.category) {
categorySet.add(conv.category);
}
});
this.categories = Array.from(categorySet);
},
2025-07-18 06:29:39 +03:30
async loadConversation(conversation) {
try {
const response = await axios.post(`/api/ai/conversations/${conversation.id}/messages`);
this.currentConversation = conversation;
this.userMessages = [];
2025-07-18 19:59:35 +03:30
const messages = Array.isArray(response.data) ? response.data : [];
2025-07-19 16:34:23 +03:30
this.convertMessages(messages);
2025-07-18 06:29:39 +03:30
this.showConversationsDialog = false;
this.$nextTick(() => {
this.scrollToBottom();
});
} catch (error) {
console.error('خطا در بارگذاری گفتگو:', error);
2025-05-04 01:07:39 +03:30
}
},
2025-07-18 06:29:39 +03:30
2025-07-19 16:34:23 +03:30
convertMessages(messages) {
messages.forEach(msg => {
if (msg.role === 'user') {
this.userMessages.push(msg.content);
} else if (msg.role === 'assistant') {
this.userMessages.push({
isAI: true,
text: msg.content,
usage: {
prompt_tokens: msg.inputTokens,
completion_tokens: msg.outputTokens
},
cost: {
total_cost: msg.totalCost
}
});
}
});
},
async newConversation() {
this.currentConversation = null;
this.userMessages = [];
this.showConversationsDialog = false;
this.setWelcomeMessage();
},
searchConversations() {
if (!this.searchTerm) {
this.filteredConversations = [...this.conversations];
} else {
this.filteredConversations = this.conversations.filter(conv =>
conv.title && conv.title.toLowerCase().includes(this.searchTerm.toLowerCase())
);
}
},
filterByCategory() {
if (!this.selectedCategory) {
this.filteredConversations = [...this.conversations];
} else {
this.filteredConversations = this.conversations.filter(conv =>
conv.category === this.selectedCategory
);
}
},
2025-07-18 06:29:39 +03:30
deleteConversation(conversation) {
this.conversationToDelete = conversation;
this.showDeleteDialog = true;
2025-05-04 01:07:39 +03:30
},
2025-07-18 06:29:39 +03:30
async confirmDeleteConversation() {
if (!this.conversationToDelete) return;
try {
this.isLoading = true;
const response = await axios.post(`/api/ai/conversations/${this.conversationToDelete.id}/delete`);
if (response.data.success) {
if (this.currentConversation && this.currentConversation.id === this.conversationToDelete.id) {
this.newConversation();
}
await this.loadConversations();
2025-07-19 16:34:23 +03:30
this.showSuccessMessage('گفتگو با موفقیت حذف شد');
2025-07-18 06:29:39 +03:30
} else {
2025-07-19 16:34:23 +03:30
this.showErrorMessage(response.data.error || 'خطا در حذف گفتگو');
2025-07-18 06:29:39 +03:30
}
} catch (error) {
console.error('خطا در حذف گفتگو:', error);
2025-07-19 16:34:23 +03:30
this.showErrorMessage('خطا در حذف گفتگو');
2025-07-18 06:29:39 +03:30
} finally {
this.isLoading = false;
this.showDeleteDialog = false;
this.conversationToDelete = null;
}
},
2025-07-19 16:34:23 +03:30
// Messages
2025-07-18 06:29:39 +03:30
setWelcomeMessage() {
2025-07-18 07:15:33 +03:30
if (!this.aiSettings.aiEnabled) {
this.displayWelcome = 'سرویس هوش مصنوعی در حال حاضر غیرفعال است. لطفاً با مدیر سیستم تماس بگیرید تا این سرویس را فعال کند.';
} else {
this.displayWelcome = 'سلام! من دستیار هوشمند حسابیکس هستم. چطور می‌توانم به شما کمک کنم؟';
}
2025-07-18 06:29:39 +03:30
},
2025-05-04 01:07:39 +03:30
async sendMessage() {
2025-07-18 06:29:39 +03:30
if (!this.userMessage.trim() || this.isLoading) return;
2025-05-04 01:07:39 +03:30
2025-07-18 07:15:33 +03:30
if (!this.aiSettings.aiEnabled) {
2025-07-19 16:34:23 +03:30
this.addErrorMessage('سرویس هوش مصنوعی در حال حاضر غیرفعال است. لطفاً با مدیر سیستم تماس بگیرید تا این سرویس را فعال کند.');
2025-07-18 07:15:33 +03:30
return;
}
2025-07-18 06:29:39 +03:30
if (this.userBalance < 100) {
2025-07-19 16:34:23 +03:30
this.addErrorMessage(
`اعتبار شما کافی نیست (${this.formatBalance(this.userBalance)}). برای شارژ حساب خود روی دکمه زیر کلیک کنید:`,
true
);
2025-07-18 06:29:39 +03:30
return;
2025-07-18 02:30:04 +03:30
}
2025-07-18 06:29:39 +03:30
const message = this.userMessage;
this.userMessage = '';
this.userMessages.push(message);
this.isLoading = true;
this.isTyping = true;
this.$nextTick(() => {
this.scrollToBottom();
});
2025-05-04 01:07:39 +03:30
2025-07-18 02:30:04 +03:30
try {
2025-07-18 19:59:35 +03:30
const personKeywords = ['شخص', 'مشتری', 'تامین‌کننده', 'کارمند', 'موجودی', 'تراکنش'];
const isPersonRequest = personKeywords.some(keyword =>
message.toLowerCase().includes(keyword.toLowerCase())
);
if (isPersonRequest) {
await this.showPersonDetails(message);
}
2025-07-19 16:34:23 +03:30
const response = await axios.post('/api/wizard/talk', {
2025-07-18 02:30:04 +03:30
message: message,
2025-07-18 06:29:39 +03:30
conversationId: this.currentConversation?.id || null
});
2025-05-04 01:07:39 +03:30
2025-07-19 16:34:23 +03:30
this.handleAIResponse(response);
2025-07-18 02:30:04 +03:30
} catch (error) {
2025-07-18 06:29:39 +03:30
console.error('خطا در ارسال پیام:', error);
2025-07-19 16:34:23 +03:30
this.addErrorMessage('خطا در ارتباط با سرور');
2025-07-18 06:29:39 +03:30
} finally {
this.isLoading = false;
this.isTyping = false;
this.$nextTick(() => {
this.scrollToBottom();
2025-07-19 16:34:23 +03:30
this.focusInput();
2025-07-18 06:29:39 +03:30
});
2025-07-18 02:30:04 +03:30
}
2025-07-18 06:29:39 +03:30
},
2025-05-04 01:07:39 +03:30
2025-07-19 16:34:23 +03:30
handleAIResponse(response) {
if (response && response.data && response.data.success) {
this.userMessages.push({
isAI: true,
text: response.data.response,
usage: response.data.usage,
cost: response.data.cost,
debug_info: response.data.debug_info ? {
...response.data.debug_info,
expanded: false
} : null
});
this.loadBalance();
this.loadConversations();
this.showSuccessMessage('پاسخ دریافت شد');
} else if (response && response.data) {
this.userMessages.push({
isAI: true,
text: `خطا: ${response.data.error}`,
isError: true,
debug_info: response.data.debug_info || null
});
} else {
this.userMessages.push({
isAI: true,
text: 'خطا در ارتباط با سرور',
isError: true,
debug_info: null
});
2025-05-04 01:07:39 +03:30
}
2025-07-18 02:30:04 +03:30
},
2025-07-18 06:29:39 +03:30
2025-07-19 16:34:23 +03:30
addErrorMessage(message, showChargeButton = false) {
this.userMessages.push({
isAI: true,
text: message,
isError: true,
showChargeButton
});
this.userMessage = '';
this.$nextTick(() => {
this.scrollToBottom();
2025-07-18 06:29:39 +03:30
});
},
2025-07-19 16:34:23 +03:30
useSuggestion(suggestion) {
this.userMessage = suggestion;
2025-07-18 19:59:35 +03:30
},
2025-07-19 16:34:23 +03:30
// Person Management
2025-07-18 19:59:35 +03:30
async searchPersons(searchTerm) {
try {
const response = await axios.post('/api/wizard/persons/search', {
search: searchTerm
});
if (response.data.success) {
return response.data.persons;
} else {
console.error('خطا در جستجوی اشخاص:', response.data.error);
return [];
}
} catch (error) {
console.error('خطا در جستجوی اشخاص:', error);
return [];
}
},
async getPersonDetails(personId) {
try {
const response = await axios.get(`/api/wizard/persons/${personId}`);
if (response.data.success) {
return response.data.person;
} else {
console.error('خطا در دریافت اطلاعات شخص:', response.data.error);
return null;
}
} catch (error) {
console.error('خطا در دریافت اطلاعات شخص:', error);
return null;
}
},
async getPersonTransactions(personId, limit = 10) {
try {
const response = await axios.get(`/api/wizard/persons/${personId}/transactions?limit=${limit}`);
if (response.data.success) {
return response.data.transactions;
} else {
console.error('خطا در دریافت تراکنش‌های شخص:', response.data.error);
return [];
}
} catch (error) {
console.error('خطا در دریافت تراکنش‌های شخص:', error);
return [];
}
},
async showPersonDetails(searchTerm) {
try {
const persons = await this.searchPersons(searchTerm);
if (persons.length === 0) {
2025-07-19 16:34:23 +03:30
this.showWarningMessage('شخصی با این نام یافت نشد');
2025-07-18 19:59:35 +03:30
return;
}
const person = persons[0];
const personDetails = await this.getPersonDetails(person.id);
2025-07-19 16:34:23 +03:30
2025-07-18 19:59:35 +03:30
if (!personDetails) {
2025-07-19 16:34:23 +03:30
this.showErrorMessage('خطا در دریافت اطلاعات شخص');
2025-07-18 19:59:35 +03:30
return;
}
const transactions = await this.getPersonTransactions(person.id, 10);
this.selectedPerson = personDetails;
this.personTransactions = transactions;
this.showPersonInfo = true;
} catch (error) {
console.error('خطا در نمایش اطلاعات شخص:', error);
2025-07-19 16:34:23 +03:30
this.showErrorMessage('خطا در نمایش اطلاعات شخص');
2025-07-18 19:59:35 +03:30
}
2025-07-19 13:49:33 +03:30
},
2025-07-19 16:34:23 +03:30
// Debug
toggleJsonView(key) {
this.expandedJsonViews[key] = !this.expandedJsonViews[key];
},
openDebugDialog(debugInfo, messageIndex) {
this.currentDebugInfo = debugInfo;
this.showDebugDialog = true;
this.expandedJsonViews = {};
},
getDebugInfoCount(debugInfo) {
let count = 0;
if (debugInfo.operation_type) count++;
if (debugInfo.current_step) count++;
if (debugInfo.attempt_count) count++;
if (debugInfo.next_action) count++;
if (debugInfo.final_result) count++;
if (debugInfo.final_data) count++;
if (debugInfo.json_response) count++;
if (debugInfo.tool_commands && debugInfo.tool_commands.length > 0) count++;
if (debugInfo.execution_result) count++;
if (debugInfo.execution_results && debugInfo.execution_results.length > 0) count++;
if (debugInfo.ai_response) count++;
if (debugInfo.error) count++;
return count;
},
getOperationTypeColor(operationType) {
const colorMap = {
'add_person': 'success',
'edit_person': 'info',
'delete_person': 'error',
'search_person': 'primary',
'show_person': 'primary',
'add_phone': 'success',
'remove_phone': 'error',
'update_balance': 'warning',
'show_transactions': 'info',
'default': 'primary'
};
return colorMap[operationType] || colorMap.default;
},
getOperationTypeText(operationType) {
const textMap = {
'add_person': 'افزودن شخص',
'edit_person': 'ویرایش شخص',
'delete_person': 'حذف شخص',
'search_person': 'جستجوی شخص',
'show_person': 'نمایش شخص',
'add_phone': 'افزودن شماره تلفن',
'remove_phone': 'حذف شماره تلفن',
'update_balance': 'به‌روزرسانی موجودی',
'show_transactions': 'نمایش تراکنش‌ها',
'default': 'عملیات نامشخص'
};
return textMap[operationType] || textMap.default;
},
// Utilities
formatBalance(balance) {
const intBalance = Math.round(balance);
if (intBalance < 1000) {
return `${intBalance.toLocaleString('fa-IR')} ریال`;
} else {
return `${(intBalance / 1000).toFixed(1)} هزار ریال`;
2025-07-19 13:49:33 +03:30
}
2025-07-19 16:34:23 +03:30
},
formatTime(date) {
return date.toLocaleTimeString('fa-IR', {
hour: '2-digit',
minute: '2-digit'
});
},
renderMarkdown(text) {
if (!text) return '';
const html = marked(text);
return DOMPurify.sanitize(html);
},
scrollToBottom() {
this.$nextTick(() => {
if (this.$refs.messagesContainer) {
this.$refs.messagesContainer.scrollTop = this.$refs.messagesContainer.scrollHeight;
}
});
},
focusInput() {
this.$nextTick(() => {
if (this.$refs.messageInput) {
this.$refs.messageInput.focus();
}
});
},
goToChargePage() {
this.$router.push('/acc/sms/panel');
},
// Notifications
showSuccessMessage(text) {
this.$emit('show-snackbar', {
text,
color: 'success'
});
},
showErrorMessage(text) {
this.$emit('show-snackbar', {
text,
color: 'error'
});
},
showWarningMessage(text) {
this.$emit('show-snackbar', {
text,
color: 'warning'
});
2025-05-04 01:07:39 +03:30
}
2025-04-12 18:50:34 +03:30
}
2025-07-18 06:29:39 +03:30
};
2025-04-12 18:50:34 +03:30
</script>
<style scoped>
2025-07-19 16:34:23 +03:30
/* Main Layout */
.main-container {
height: calc(100vh - 70px);
2025-05-04 01:07:39 +03:30
display: flex;
flex-direction: column;
2025-07-19 16:34:23 +03:30
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
overflow: hidden;
2025-05-04 01:07:39 +03:30
}
2025-07-19 16:34:23 +03:30
/* Header Styles */
.ai-header {
background: linear-gradient(135deg, #1a237e 0%, #3949ab 100%) !important;
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
2025-07-18 06:29:39 +03:30
}
2025-07-19 16:34:23 +03:30
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
max-width: 1400px;
margin: 0 auto;
padding: 0 24px;
2025-07-18 06:29:39 +03:30
}
2025-07-19 16:34:23 +03:30
.header-left {
display: flex;
align-items: center;
gap: 16px;
2025-07-18 06:29:39 +03:30
}
2025-07-19 16:34:23 +03:30
.logo-container {
background: rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 8px;
backdrop-filter: blur(10px);
2025-07-18 06:29:39 +03:30
}
2025-07-19 16:34:23 +03:30
.title-section {
2025-07-18 06:29:39 +03:30
display: flex;
flex-direction: column;
2025-05-04 01:07:39 +03:30
}
2025-07-19 16:34:23 +03:30
.app-title {
font-size: 1.25rem;
font-weight: 600;
color: white;
margin: 0;
line-height: 1.2;
2025-07-18 06:29:39 +03:30
}
2025-07-19 16:34:23 +03:30
.app-subtitle {
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.8);
margin: 0;
line-height: 1.2;
2025-05-04 01:07:39 +03:30
}
2025-07-19 16:34:23 +03:30
.header-center {
2025-07-18 06:29:39 +03:30
flex: 1;
2025-05-04 01:07:39 +03:30
display: flex;
2025-07-19 16:34:23 +03:30
justify-content: center;
2025-05-04 01:07:39 +03:30
}
2025-07-19 16:34:23 +03:30
.status-indicators {
2025-05-04 01:07:39 +03:30
display: flex;
gap: 16px;
2025-07-19 16:34:23 +03:30
align-items: center;
2025-05-04 01:07:39 +03:30
}
2025-07-19 16:34:23 +03:30
.status-item {
cursor: pointer;
transition: all 0.3s ease;
2025-04-12 18:50:34 +03:30
}
2025-07-19 16:34:23 +03:30
.status-chip {
transition: all 0.3s ease;
border-radius: 8px;
font-weight: 500;
2025-04-12 18:50:34 +03:30
}
2025-07-19 16:34:23 +03:30
.status-chip:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
2025-05-04 01:07:39 +03:30
}
2025-07-19 16:34:23 +03:30
.header-right {
display: flex;
align-items: center;
2025-05-04 01:07:39 +03:30
}
2025-07-19 16:34:23 +03:30
.action-btn {
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
transition: all 0.3s ease;
2025-04-12 18:50:34 +03:30
}
2025-07-19 16:34:23 +03:30
.action-btn:hover {
background: rgba(255, 255, 255, 0.2);
transform: scale(1.1);
2025-04-12 18:50:34 +03:30
}
2025-07-19 16:34:23 +03:30
/* Info Section */
.info-section {
padding: 12px 24px 0;
flex-shrink: 0;
2025-07-18 02:30:04 +03:30
}
2025-07-19 16:34:23 +03:30
.info-cards {
2025-07-18 06:29:39 +03:30
display: flex;
2025-07-19 16:34:23 +03:30
gap: 12px;
max-width: 1400px;
margin: 0 auto;
flex-wrap: wrap;
}
.info-card {
border-radius: 8px;
overflow: hidden;
2025-07-18 06:29:39 +03:30
background: white;
2025-07-19 16:34:23 +03:30
transition: all 0.3s ease;
border: 1px solid rgba(0, 0, 0, 0.08);
flex: 1;
min-width: 200px;
2025-07-18 02:30:04 +03:30
}
2025-07-19 16:34:23 +03:30
.info-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
2025-07-18 02:30:04 +03:30
}
2025-07-19 16:34:23 +03:30
.card-content {
padding: 12px 16px;
2025-07-18 02:30:04 +03:30
}
2025-07-19 16:34:23 +03:30
.pricing-row,
.stats-row {
display: flex;
align-items: center;
gap: 8px;
2025-04-12 18:50:34 +03:30
}
2025-07-19 16:34:23 +03:30
.pricing-icon,
.stats-icon {
flex-shrink: 0;
}
.pricing-info,
.stats-info {
display: flex;
flex-direction: column;
gap: 2px;
flex: 1;
}
.pricing-label,
.stats-label {
2025-07-18 06:29:39 +03:30
font-size: 0.75rem;
2025-07-19 16:34:23 +03:30
color: #666;
font-weight: 500;
2025-05-04 01:07:39 +03:30
}
2025-07-19 16:34:23 +03:30
.pricing-value,
.stats-value {
font-size: 0.875rem;
color: #333;
font-weight: 600;
line-height: 1.3;
2025-05-04 01:07:39 +03:30
}
2025-07-19 16:34:23 +03:30
.pricing-unit {
font-size: 0.7rem;
color: #999;
2025-05-04 01:07:39 +03:30
}
2025-07-19 16:34:23 +03:30
/* Chat Section */
.chat-section {
flex: 1;
padding: 20px 24px;
overflow: hidden;
2025-04-12 18:50:34 +03:30
}
2025-07-19 16:34:23 +03:30
.chat-container {
height: 100%;
display: flex;
flex-direction: column;
border-radius: 20px;
overflow: hidden;
background: white;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
2025-07-18 06:29:39 +03:30
}
2025-07-19 16:34:23 +03:30
.chat-header {
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
padding: 16px 24px;
flex-shrink: 0;
2025-05-04 01:07:39 +03:30
}
2025-07-19 16:34:23 +03:30
.conversation-info {
display: flex;
align-items: center;
justify-content: space-between;
2025-05-04 01:07:39 +03:30
}
2025-07-19 16:34:23 +03:30
.conversation-title {
display: flex;
align-items: center;
gap: 12px;
}
2025-05-04 01:07:39 +03:30
2025-07-19 16:34:23 +03:30
.title-text {
font-size: 1.125rem;
font-weight: 600;
color: #333;
2025-04-12 18:50:34 +03:30
}
2025-07-19 16:34:23 +03:30
.category-chip {
font-size: 0.75rem;
font-weight: 500;
2025-04-12 18:50:34 +03:30
}
2025-07-18 02:30:04 +03:30
2025-07-19 16:34:23 +03:30
.new-chat-btn {
font-size: 0.875rem;
font-weight: 500;
border-radius: 8px;
transition: all 0.3s ease;
2025-07-18 02:30:04 +03:30
}
2025-07-19 16:34:23 +03:30
.new-chat-btn:hover {
background: rgba(26, 35, 126, 0.1);
transform: translateY(-1px);
}
/* Messages Area */
.messages-area {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.messages-container {
flex: 1;
2025-07-18 06:29:39 +03:30
overflow-y: auto;
2025-07-19 16:34:23 +03:30
padding: 24px;
display: flex;
flex-direction: column;
gap: 20px;
scroll-behavior: smooth;
}
.message {
display: flex;
align-items: flex-start;
gap: 12px;
max-width: 80%;
animation: messageSlideIn 0.3s ease-out;
}
.user-message {
align-self: flex-end;
flex-direction: row-reverse;
}
.ai-message {
align-self: flex-start;
}
.message-avatar {
flex-shrink: 0;
2025-07-18 02:30:04 +03:30
}
2025-07-19 16:34:23 +03:30
.message-content {
background: #f8f9fa;
padding: 16px 20px;
border-radius: 20px;
border-top-left-radius: 4px;
position: relative;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
2025-07-18 02:30:04 +03:30
transition: all 0.3s ease;
}
2025-07-19 16:34:23 +03:30
.user-message .message-content {
background: linear-gradient(135deg, #1a237e 0%, #3949ab 100%);
color: white;
border-top-left-radius: 20px;
border-top-right-radius: 4px;
}
.message-content:hover {
2025-07-18 06:29:39 +03:30
transform: translateY(-1px);
2025-07-19 16:34:23 +03:30
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
2025-07-18 02:30:04 +03:30
}
2025-07-19 16:34:23 +03:30
.message-text {
line-height: 1.6;
white-space: pre-wrap;
font-size: 0.875rem;
}
.message-time {
font-size: 0.75rem;
opacity: 0.7;
margin-top: 8px;
}
.error-message {
background: #ffebee !important;
border: 1px solid #ffcdd2;
}
.success-message {
background: #f1f8e9 !important;
border: 1px solid #c8e6c9;
}
/* Debug Section */
.debug-section {
margin-top: 12px;
display: flex;
justify-content: flex-start;
}
.debug-btn {
font-size: 0.75rem;
font-weight: 500;
2025-07-18 06:29:39 +03:30
border-radius: 8px;
2025-07-19 16:34:23 +03:30
transition: all 0.3s ease;
2025-07-18 02:30:04 +03:30
}
2025-07-19 16:34:23 +03:30
.debug-btn:hover {
2025-07-18 06:29:39 +03:30
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
2025-07-18 02:30:04 +03:30
}
2025-07-19 16:34:23 +03:30
.debug-count {
margin-right: 4px;
2025-07-18 02:30:04 +03:30
}
2025-07-19 16:34:23 +03:30
/* Charge Section */
.charge-section {
margin-top: 12px;
2025-07-18 06:29:39 +03:30
display: flex;
justify-content: center;
2025-07-18 02:30:04 +03:30
}
2025-07-18 06:29:39 +03:30
.charge-btn {
border-radius: 8px;
font-weight: 500;
2025-07-19 16:34:23 +03:30
transition: all 0.3s ease;
2025-07-18 02:30:04 +03:30
}
2025-07-18 06:29:39 +03:30
.charge-btn:hover {
2025-07-18 02:30:04 +03:30
transform: translateY(-1px);
2025-07-18 06:29:39 +03:30
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
2025-07-18 02:30:04 +03:30
}
2025-07-19 16:34:23 +03:30
/* Suggestions */
.suggestions-section {
padding: 24px;
border-top: 1px solid rgba(0, 0, 0, 0.08);
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
flex-shrink: 0;
2025-07-18 02:30:04 +03:30
}
2025-07-19 16:34:23 +03:30
.suggestions-title {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 16px;
font-size: 0.875rem;
font-weight: 600;
color: #333;
2025-07-18 02:30:04 +03:30
}
2025-07-19 16:34:23 +03:30
.suggestions-grid {
display: flex;
flex-wrap: wrap;
gap: 8px;
2025-07-18 02:30:04 +03:30
}
2025-07-19 16:34:23 +03:30
.suggestion-chip {
cursor: pointer;
transition: all 0.3s ease;
font-size: 0.75rem;
border-radius: 16px;
2025-07-18 02:30:04 +03:30
}
2025-07-19 13:49:33 +03:30
2025-07-19 16:34:23 +03:30
.suggestion-chip:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
2025-07-19 13:49:33 +03:30
}
2025-07-19 16:34:23 +03:30
/* Input Section */
.input-section {
padding: 20px 24px;
background: white;
border-top: 1px solid rgba(0, 0, 0, 0.08);
flex-shrink: 0;
2025-07-19 13:49:33 +03:30
}
2025-07-19 16:34:23 +03:30
.input-container {
max-width: 1400px;
margin: 0 auto;
2025-07-19 13:49:33 +03:30
}
2025-07-19 16:34:23 +03:30
.message-input {
border-radius: 24px;
background: #f8f9fa;
transition: all 0.3s ease;
2025-07-19 13:49:33 +03:30
}
2025-07-19 16:34:23 +03:30
.message-input:hover {
background: #f1f3f4;
2025-07-19 13:49:33 +03:30
}
2025-07-19 16:34:23 +03:30
.message-input:focus-within {
background: white;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
2025-07-19 13:49:33 +03:30
}
2025-07-19 16:34:23 +03:30
.send-btn {
transition: all 0.3s ease;
2025-07-19 13:49:33 +03:30
}
2025-07-19 16:34:23 +03:30
.send-btn:hover {
transform: scale(1.1);
2025-07-19 13:49:33 +03:30
}
2025-07-19 16:34:23 +03:30
.send-icon {
transform: rotate(180deg);
2025-07-19 13:49:33 +03:30
}
2025-07-19 16:34:23 +03:30
/* Typing Indicator */
.typing-indicator {
display: inline-flex;
gap: 4px;
padding: 8px 0;
2025-07-19 13:49:33 +03:30
}
2025-07-19 16:34:23 +03:30
.typing-indicator span {
width: 8px;
height: 8px;
border-radius: 50%;
background: #666;
animation: typing 1.4s infinite ease-in-out;
2025-07-19 13:49:33 +03:30
}
2025-07-19 16:34:23 +03:30
.typing-indicator span:nth-child(1) { animation-delay: -0.32s; }
.typing-indicator span:nth-child(2) { animation-delay: -0.16s; }
@keyframes typing {
0%, 80%, 100% {
transform: scale(0.8);
opacity: 0.5;
}
40% {
transform: scale(1);
opacity: 1;
}
}
@keyframes messageSlideIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Dialog Styles */
.conversations-dialog,
.delete-dialog,
.person-dialog {
border-radius: 16px;
overflow: hidden;
}
.dialog-header {
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
padding: 20px 24px;
display: flex;
align-items: center;
justify-content: space-between;
}
.dialog-title {
display: flex;
align-items: center;
gap: 12px;
font-size: 1.125rem;
font-weight: 600;
color: #333;
}
.dialog-content {
padding: 24px;
}
/* Filters */
.filters-section {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-bottom: 24px;
}
.search-field,
.category-field {
border-radius: 8px;
}
/* Conversations List */
.conversations-list {
max-height: 400px;
overflow-y: auto;
}
.conversations-items {
background: transparent;
}
.conversation-item {
border-radius: 12px;
margin-bottom: 8px;
transition: all 0.3s ease;
cursor: pointer;
}
.conversation-item:hover {
background: rgba(26, 35, 126, 0.1);
transform: translateX(4px);
}
.conversation-title {
font-weight: 600;
color: #333;
margin-bottom: 4px;
}
.conversation-details {
color: #666;
}
.conversation-meta {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 4px;
}
.conversation-date {
font-size: 0.75rem;
color: #999;
}
.conversation-stats {
font-size: 0.75rem;
color: #666;
}
.delete-btn {
opacity: 0.7;
transition: all 0.3s ease;
}
.delete-btn:hover {
opacity: 1;
transform: scale(1.1);
}
.conversation-item:hover .delete-btn {
opacity: 1;
}
.dialog-actions {
padding: 16px 24px;
background: #f8f9fa;
border-top: 1px solid rgba(0, 0, 0, 0.08);
}
/* Delete Dialog */
.delete-header {
background: linear-gradient(135deg, #ffebee 0%, #ffffff 100%);
color: #d32f2f;
}
.delete-content {
padding: 24px;
}
.delete-message {
font-size: 1rem;
color: #333;
margin-bottom: 12px;
line-height: 1.6;
}
.delete-warning {
font-size: 0.875rem;
color: #666;
line-height: 1.5;
}
.delete-actions {
padding: 16px 24px;
background: #f8f9fa;
border-top: 1px solid rgba(0, 0, 0, 0.08);
}
/* Debug Dialog */
.debug-dialog {
height: 100vh;
display: flex;
flex-direction: column;
background: #f8f9fa;
}
.debug-header {
background: linear-gradient(135deg, #1a237e 0%, #3949ab 100%);
color: white;
padding: 20px 24px;
position: sticky;
top: 0;
z-index: 10;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: space-between;
}
.debug-title-section {
display: flex;
align-items: center;
gap: 16px;
}
.debug-icon {
background: rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 12px;
backdrop-filter: blur(10px);
}
.debug-info {
display: flex;
flex-direction: column;
}
.debug-title {
font-size: 1.5rem;
font-weight: 600;
margin: 0;
color: white;
}
.debug-subtitle {
font-size: 0.875rem;
margin: 4px 0 0 0;
opacity: 0.8;
color: rgba(255, 255, 255, 0.9);
}
.debug-count {
margin-right: 16px;
font-weight: 500;
}
.close-btn {
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
transition: all 0.3s ease;
}
.close-btn:hover {
background: rgba(255, 255, 255, 0.2);
transform: scale(1.1);
}
.debug-content {
flex: 1;
overflow-y: auto;
padding: 24px;
background: #f8f9fa;
}
/* Debug Main Cards */
.debug-main-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 32px;
}
.debug-main-card {
border-radius: 16px;
overflow: hidden;
transition: all 0.3s ease;
border: none;
background: white;
animation: slideInUp 0.4s ease-out;
}
.debug-main-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.15);
}
.debug-main-card:nth-child(1) { animation-delay: 0.1s; }
.debug-main-card:nth-child(2) { animation-delay: 0.2s; }
.debug-main-card:nth-child(3) { animation-delay: 0.3s; }
.debug-main-card:nth-child(4) { animation-delay: 0.4s; }
.card-header {
display: flex;
align-items: center;
padding: 20px;
gap: 16px;
}
.card-icon {
background: linear-gradient(135deg, #1a237e 0%, #3949ab 100%);
border-radius: 12px;
padding: 12px;
min-width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
}
.card-content {
flex: 1;
}
.card-title {
font-size: 1rem;
font-weight: 600;
margin: 0 0 8px 0;
color: #333;
}
.card-text {
font-size: 0.875rem;
color: #666;
margin: 0;
line-height: 1.5;
}
.operation-chip {
font-weight: 500;
font-size: 0.75rem;
}
.attempt-progress {
display: flex;
flex-direction: column;
gap: 8px;
}
.progress-bar {
border-radius: 4px;
}
.attempt-text {
font-size: 0.875rem;
color: #666;
font-weight: 500;
}
/* Debug Results Section */
.debug-results-section {
display: flex;
flex-direction: column;
gap: 20px;
}
.debug-section-card {
border-radius: 16px;
overflow: hidden;
background: white;
border: 1px solid rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
animation: slideInUp 0.4s ease-out;
}
.debug-section-card:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
border-color: rgba(0, 0, 0, 0.15);
}
.section-title {
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
border-bottom: 1px solid rgba(0, 0, 0, 0.08);
padding: 16px 20px;
font-size: 1rem;
font-weight: 600;
color: #333;
display: flex;
align-items: center;
}
.section-content {
padding: 20px;
}
/* Tools Grid */
.tools-grid {
display: flex;
flex-direction: column;
gap: 16px;
}
.tool-item {
background: #f8f9fa;
border-radius: 12px;
padding: 16px;
border-left: 4px solid #1a237e;
transition: all 0.3s ease;
}
.tool-item:hover {
background: #e3f2fd;
transform: translateX(4px);
}
.tool-header {
display: flex;
align-items: center;
margin-bottom: 12px;
}
.tool-name {
font-weight: 600;
color: #333;
font-size: 0.875rem;
}
.tool-actions {
display: flex;
gap: 8px;
}
.view-btn {
font-size: 0.75rem;
font-weight: 500;
border-radius: 8px;
transition: all 0.3s ease;
}
.view-btn:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
/* Timeline */
.execution-timeline {
position: relative;
padding-left: 24px;
}
.execution-timeline::before {
content: '';
position: absolute;
left: 8px;
top: 0;
bottom: 0;
width: 2px;
background: linear-gradient(to bottom, #4caf50, #81c784);
}
.timeline-item {
position: relative;
margin-bottom: 24px;
}
.timeline-marker {
position: absolute;
left: -16px;
top: 0;
background: white;
border-radius: 50%;
width: 16px;
height: 16px;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.timeline-content {
background: #f1f8e9;
border-radius: 12px;
padding: 16px;
border-left: 4px solid #4caf50;
transition: all 0.3s ease;
}
.timeline-content:hover {
background: #e8f5e8;
transform: translateX(4px);
}
.timeline-header {
margin-bottom: 12px;
}
.timeline-title {
font-weight: 600;
color: #333;
font-size: 0.875rem;
}
.timeline-actions {
display: flex;
gap: 8px;
}
/* JSON Container */
.json-container {
margin-top: 12px;
background: #f5f5f5;
border-radius: 8px;
overflow: hidden;
border: 1px solid #e0e0e0;
}
.json-content {
font-family: 'Fira Code', 'Courier New', monospace;
font-size: 0.75rem;
line-height: 1.4;
color: #333;
background: #ffffff;
padding: 16px;
margin: 0;
overflow-x: auto;
white-space: pre-wrap;
word-break: break-word;
border: 1px solid #e0e0e0;
max-height: 300px;
overflow-y: auto;
}
/* AI Response Container */
.ai-response-container {
margin-top: 12px;
background: #e3f2fd;
border-radius: 8px;
padding: 16px;
border-left: 4px solid #2196f3;
}
.ai-response-content {
font-size: 0.875rem;
line-height: 1.6;
color: #333;
white-space: pre-wrap;
word-break: break-word;
}
/* Success and Error Cards */
.success-card {
border-left: 4px solid #4caf50;
}
.error-card {
border-left: 4px solid #f44336;
}
.final-result-content {
background: #e8f5e8;
border-radius: 8px;
padding: 16px;
border-left: 4px solid #4caf50;
}
.result-text {
font-size: 0.875rem;
line-height: 1.6;
color: #2e7d32;
margin: 0;
font-weight: 500;
}
.error-content {
background: #ffebee;
border-radius: 8px;
padding: 16px;
border-left: 4px solid #f44336;
}
.error-text {
font-size: 0.875rem;
line-height: 1.6;
color: #d32f2f;
margin: 0;
font-weight: 500;
}
/* Animations */
@keyframes slideInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Responsive Design */
@media (max-width: 768px) {
.header-content {
padding: 0 12px;
}
.app-title {
font-size: 0.9rem;
}
.app-subtitle {
font-size: 0.65rem;
}
.status-indicators {
gap: 6px;
}
.status-chip {
font-size: 0.7rem;
}
.header-center {
display: none;
}
.header-right {
margin-right: auto;
}
.info-section {
padding: 6px 12px 0;
}
.info-cards {
flex-direction: column;
gap: 6px;
}
.info-card {
min-width: auto;
border-radius: 6px;
}
.card-content {
padding: 8px 12px;
}
.pricing-row,
.stats-row {
gap: 6px;
}
.pricing-icon,
.stats-icon {
font-size: 14px;
}
.pricing-label,
.stats-label {
font-size: 0.7rem;
}
.pricing-value,
.stats-value {
font-size: 0.75rem;
line-height: 1.2;
}
.pricing-unit {
font-size: 0.65rem;
}
.chat-section {
padding: 12px;
}
.chat-container {
border-radius: 12px;
}
.chat-header {
padding: 12px 16px;
}
.conversation-title {
font-size: 0.9rem;
}
.title-text {
font-size: 1rem;
}
.messages-container {
padding: 12px;
gap: 16px;
}
.message {
max-width: 92%;
gap: 8px;
}
.message-content {
padding: 12px 16px;
}
.message-text {
font-size: 0.8rem;
}
.message-time {
font-size: 0.65rem;
}
.suggestions-section {
padding: 16px;
}
.suggestions-title {
font-size: 0.8rem;
margin-bottom: 12px;
}
.suggestions-grid {
gap: 6px;
}
.suggestion-chip {
font-size: 0.7rem;
height: 28px;
}
.input-section {
padding: 12px;
}
.message-input {
border-radius: 20px;
}
.filters-section {
grid-template-columns: 1fr;
gap: 10px;
}
.search-field,
.category-field {
font-size: 0.8rem;
}
.conversations-list {
max-height: 350px;
}
.conversation-item {
padding: 8px 0;
}
.conversation-title {
font-size: 0.85rem;
}
.conversation-stats {
font-size: 0.7rem;
}
.debug-header {
padding: 12px;
}
.debug-title {
font-size: 1.1rem;
}
.debug-subtitle {
font-size: 0.8rem;
}
.debug-main-cards {
grid-template-columns: 1fr;
gap: 12px;
}
.card-header {
padding: 12px;
gap: 12px;
}
.card-icon {
min-width: 40px;
height: 40px;
}
.card-title {
font-size: 0.9rem;
}
.card-text {
font-size: 0.8rem;
}
.section-content {
padding: 12px;
}
.section-title {
padding: 12px 16px;
font-size: 0.9rem;
}
.tool-item,
.timeline-content {
padding: 12px;
}
.tool-name,
.timeline-title {
font-size: 0.8rem;
}
.view-btn {
font-size: 0.7rem;
}
.json-content {
font-size: 0.65rem;
padding: 10px;
}
.debug-content {
padding: 12px;
}
}
@media (max-width: 480px) {
.header-left {
gap: 8px;
}
.logo-container {
padding: 4px;
}
.app-title {
font-size: 0.8rem;
}
.app-subtitle {
font-size: 0.6rem;
}
.status-indicators {
display: none;
}
.info-section {
padding: 4px 8px 0;
}
.info-cards {
gap: 4px;
}
.card-content {
padding: 6px 10px;
}
.pricing-row,
.stats-row {
gap: 4px;
}
.pricing-icon,
.stats-icon {
font-size: 12px;
}
.pricing-label,
.stats-label {
font-size: 0.65rem;
}
.pricing-value,
.stats-value {
font-size: 0.7rem;
}
.pricing-unit {
font-size: 0.6rem;
}
.chat-section {
padding: 8px;
}
.chat-header {
padding: 10px 12px;
}
.title-text {
font-size: 0.9rem;
}
.messages-container {
padding: 8px;
gap: 12px;
}
.message {
max-width: 95%;
gap: 6px;
}
.message-content {
padding: 10px 14px;
}
.message-text {
font-size: 0.75rem;
}
.message-time {
font-size: 0.6rem;
}
.suggestions-section {
padding: 12px;
}
.suggestions-title {
font-size: 0.75rem;
margin-bottom: 10px;
}
.suggestions-grid {
gap: 4px;
}
.suggestion-chip {
font-size: 0.65rem;
height: 24px;
}
.input-section {
padding: 8px;
}
.message-input {
font-size: 0.8rem;
}
.filters-section {
gap: 8px;
}
.search-field,
.category-field {
font-size: 0.75rem;
}
.conversations-list {
max-height: 300px;
}
.conversation-item {
padding: 6px 0;
}
.conversation-title {
font-size: 0.8rem;
}
.conversation-stats {
font-size: 0.65rem;
}
.debug-header {
padding: 8px;
}
.debug-title {
font-size: 1rem;
}
.debug-subtitle {
font-size: 0.75rem;
}
.debug-main-cards {
gap: 8px;
}
.card-header {
padding: 8px;
gap: 8px;
}
.card-icon {
min-width: 36px;
height: 36px;
}
.card-title {
font-size: 0.8rem;
}
.card-text {
font-size: 0.75rem;
}
.section-content {
padding: 8px;
}
.section-title {
padding: 10px 12px;
font-size: 0.8rem;
}
.tool-item,
.timeline-content {
padding: 8px;
}
.tool-name,
.timeline-title {
font-size: 0.75rem;
}
.view-btn {
font-size: 0.65rem;
}
.json-content {
font-size: 0.6rem;
padding: 8px;
}
.debug-content {
padding: 8px;
}
}
@media (max-width: 480px) {
.header-content {
padding: 0 6px;
}
.header-left {
gap: 8px;
}
.logo-container {
padding: 4px;
}
.app-title {
font-size: 0.7rem;
}
.app-subtitle {
font-size: 0.5rem;
}
.status-indicators {
display: none;
}
.header-center {
display: none;
}
.header-right {
margin-right: auto;
}
.action-btn {
width: 36px;
height: 36px;
}
.message {
max-width: 95%;
}
.message-content {
padding: 12px 16px;
}
.suggestions-grid {
gap: 6px;
}
.suggestion-chip {
font-size: 0.7rem;
}
}
@media (max-width: 360px) {
.header-content {
padding: 0 4px;
}
.header-left {
gap: 6px;
}
.logo-container {
padding: 3px;
}
.app-title {
font-size: 0.65rem;
}
.app-subtitle {
font-size: 0.45rem;
}
.action-btn {
width: 32px;
height: 32px;
}
.action-btn .v-icon {
font-size: 18px;
}
2025-07-19 13:49:33 +03:30
}
2025-04-12 18:50:34 +03:30
</style>