fix
This commit is contained in:
+41
-37
@@ -1,16 +1,16 @@
|
||||
/* =========================================================
|
||||
KAI v4.0 Dashboard — Dark Theme Globals
|
||||
KAI v4.0 Dashboard - Light Theme Globals
|
||||
Loaded by MarketingAnalysisV3.vue via <style>
|
||||
========================================================= */
|
||||
@import url('https://fonts.googleapis.com/css2?family=Unbounded:wght@400;700;900&family=Onest:wght@400;500;600;700&display=swap');
|
||||
|
||||
:root {
|
||||
--kai-bg: #07090F;
|
||||
--kai-bg2: #0A0D14;
|
||||
--kai-card: rgba(255,255,255,0.03);
|
||||
--kai-card-hover: rgba(255,255,255,0.06);
|
||||
--kai-border: rgba(45,123,255,0.12);
|
||||
--kai-border-hover: rgba(45,123,255,0.3);
|
||||
--kai-bg: #F5F8FF;
|
||||
--kai-bg2: #EEF4FF;
|
||||
--kai-card: #FFFFFF;
|
||||
--kai-card-hover: #F8FBFF;
|
||||
--kai-border: rgba(45,123,255,0.18);
|
||||
--kai-border-hover: rgba(45,123,255,0.35);
|
||||
--kai-blue: #2D7BFF;
|
||||
--kai-blue-rgb: 45, 123, 255;
|
||||
--kai-green: #10B981;
|
||||
@@ -19,10 +19,10 @@
|
||||
--kai-purple: #8B5CF6;
|
||||
--kai-yellow: #F59E0B;
|
||||
--kai-red: #EF4444;
|
||||
--kai-txt: rgba(255,255,255,0.95);
|
||||
--kai-txt2: rgba(255,255,255,0.65);
|
||||
--kai-txt3: rgba(255,255,255,0.40);
|
||||
|
||||
--kai-txt: #0F172A;
|
||||
--kai-txt2: #334155;
|
||||
--kai-txt3: #64748B;
|
||||
|
||||
/* Font size unification - smaller & professional */
|
||||
--kai-fs-base: 12.5px;
|
||||
--kai-fs-sm: 11px;
|
||||
@@ -35,9 +35,9 @@
|
||||
/* Layout */
|
||||
.kai-page {
|
||||
background: var(--kai-bg);
|
||||
background-image:
|
||||
radial-gradient(at 0% 0%, rgba(var(--kai-blue-rgb), 0.05) 0px, transparent 50%),
|
||||
radial-gradient(at 100% 100%, rgba(var(--kai-green-rgb), 0.03) 0px, transparent 50%);
|
||||
background-image:
|
||||
radial-gradient(at 0% 0%, rgba(var(--kai-blue-rgb), 0.09) 0px, transparent 50%),
|
||||
radial-gradient(at 100% 100%, rgba(var(--kai-green-rgb), 0.06) 0px, transparent 50%);
|
||||
min-height: 100vh;
|
||||
font-family: 'Onest', sans-serif;
|
||||
color: var(--kai-txt);
|
||||
@@ -51,9 +51,9 @@
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
height: 64px;
|
||||
background: rgba(10,14,26,0.96);
|
||||
background: rgba(255,255,255,0.92);
|
||||
backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid rgba(45,123,255,0.2);
|
||||
border-bottom: 1px solid rgba(45,123,255,0.16);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 32px;
|
||||
@@ -140,7 +140,7 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
.kai-pdf-btn:hover {
|
||||
background: var(--kai-card);
|
||||
background: #F0F7FF;
|
||||
border-color: var(--kai-border-hover);
|
||||
color: var(--kai-txt);
|
||||
}
|
||||
@@ -153,6 +153,7 @@
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
transition: border-color .3s;
|
||||
box-shadow: 0 10px 24px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
.kai-section:hover {
|
||||
border-color: rgba(45,123,255,0.25);
|
||||
@@ -160,7 +161,7 @@
|
||||
.kai-section-header {
|
||||
padding: 16px 24px 14px;
|
||||
border-bottom: 1px solid var(--kai-border);
|
||||
background: linear-gradient(to right, rgba(var(--kai-blue-rgb), 0.05), transparent);
|
||||
background: linear-gradient(to right, rgba(var(--kai-blue-rgb), 0.08), transparent);
|
||||
}
|
||||
.kai-section-title {
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
@@ -200,7 +201,7 @@
|
||||
@media (max-width: 640px) { .kai-kpi-grid { grid-template-columns: 1fr; } }
|
||||
|
||||
.kai-kpi {
|
||||
background: rgba(255,255,255,0.03);
|
||||
background: #FFFFFF;
|
||||
border: 1px solid var(--kai-border);
|
||||
border-radius: 14px;
|
||||
padding: 20px;
|
||||
@@ -212,13 +213,14 @@
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(circle at top right, rgba(45,123,255,0.07), transparent 70%);
|
||||
background: radial-gradient(circle at top right, rgba(45,123,255,0.09), transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.kai-kpi:hover {
|
||||
border-color: var(--kai-border-hover);
|
||||
background: var(--kai-card-hover);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 12px 26px rgba(45,123,255,0.12);
|
||||
}
|
||||
.kai-kpi-icon {
|
||||
width: 36px;
|
||||
@@ -276,7 +278,7 @@
|
||||
.kai-stars { color: var(--kai-yellow); font-size: 16px; letter-spacing: 2px; }
|
||||
|
||||
/* Heatmap bars */
|
||||
.kai-progress-track { background: rgba(255,255,255,0.06); border-radius: 999px; height: 6px; overflow: hidden; }
|
||||
.kai-progress-track { background: rgba(45,123,255,0.10); border-radius: 999px; height: 6px; overflow: hidden; }
|
||||
.kai-progress-fill { height: 100%; border-radius: 999px; transition: width 1s ease-out; background: var(--kai-blue); }
|
||||
|
||||
/* Info badge strip */
|
||||
@@ -317,7 +319,7 @@
|
||||
.kai-table th:hover { color: var(--kai-txt2); }
|
||||
.kai-table td {
|
||||
padding: 12px 14px;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.04);
|
||||
border-bottom: 1px solid var(--kai-border);
|
||||
color: var(--kai-txt);
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -338,7 +340,7 @@
|
||||
/* Competitor expander */
|
||||
.kai-expand-btn { background: none; border: none; cursor: pointer; color: var(--kai-txt2); padding: 4px; border-radius: 6px; transition: all .2s; display: flex; align-items: center; }
|
||||
.kai-expand-btn:hover { background: rgba(45,123,255,0.1); color: var(--kai-blue); }
|
||||
.kai-expand-row { background: rgba(10,14,26,0.5); border-top: 1px solid var(--kai-border); }
|
||||
.kai-expand-row { background: rgba(45,123,255,0.04); border-top: 1px solid var(--kai-border); }
|
||||
.kai-expand-inner { padding: 16px 20px; display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
||||
@media (max-width: 640px) { .kai-expand-inner { grid-template-columns: 1fr; } }
|
||||
|
||||
@@ -379,25 +381,26 @@
|
||||
.kai-conc-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; }
|
||||
@media (max-width: 1024px) { .kai-conc-grid { grid-template-columns: 1fr; } }
|
||||
.kai-conc-card {
|
||||
background: rgba(255,255,255,0.03);
|
||||
border: 1px solid var(--kai-border);
|
||||
background: rgba(45,123,255,0.03);
|
||||
border: 1px solid rgba(45,123,255,0.14);
|
||||
border-radius: 14px;
|
||||
padding: 18px 20px;
|
||||
cursor: default;
|
||||
transition: all .3s;
|
||||
}
|
||||
.kai-conc-card:hover { border-color: var(--kai-border-hover); transform: translateY(-4px); box-shadow: 0 0 24px rgba(45,123,255,0.12); }
|
||||
.kai-conc-card:hover { border-color: var(--kai-border-hover); transform: translateY(-4px); box-shadow: 0 8px 24px rgba(45,123,255,0.1); }
|
||||
.kai-conc-icon { font-size: 20px; margin-bottom: 10px; }
|
||||
.kai-conc-text { font-size: 13px; color: var(--kai-txt); line-height: 1.6; }
|
||||
|
||||
/* SMM Rationale */
|
||||
.kai-rationale {
|
||||
background: linear-gradient(135deg, #0D1F4A 0%, var(--kai-bg) 100%);
|
||||
background: linear-gradient(135deg, rgba(45,123,255,0.06) 0%, rgba(16,185,129,0.03) 100%);
|
||||
border-left: 4px solid var(--kai-blue);
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(45,123,255,0.18);
|
||||
border-radius: 16px;
|
||||
padding: 32px;
|
||||
}
|
||||
.kai-rationale-text { font-size: 15px; color: rgba(255,255,255,0.87); line-height: 1.8; white-space: pre-line; }
|
||||
.kai-rationale-text { font-size: 14px; color: var(--kai-txt); line-height: 1.9; white-space: pre-line; font-weight: 500; }
|
||||
.kai-cta-box {
|
||||
margin-top: 28px;
|
||||
border: 1px solid rgba(45,123,255,0.3);
|
||||
@@ -435,11 +438,12 @@
|
||||
z-index: 90;
|
||||
}
|
||||
@media (max-width: 1280px) { .kai-sidenav { display: none; } }
|
||||
/* FIX: was rgba(255,255,255,0.15) — invisible on light background */
|
||||
.kai-sidenav-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,0.15);
|
||||
background: rgba(45,123,255,0.20);
|
||||
cursor: pointer;
|
||||
transition: all .2s;
|
||||
border: none;
|
||||
@@ -449,11 +453,11 @@
|
||||
.kai-sidenav-dot.active { background: var(--kai-blue); box-shadow: 0 0 8px rgba(45,123,255,0.6); width: 10px; height: 10px; }
|
||||
|
||||
/* Area chart gradient */
|
||||
.kai-area-chart-wrap { background: rgba(255,255,255,0.02); border-radius: 12px; padding: 16px; }
|
||||
.kai-area-chart-wrap { background: rgba(45,123,255,0.03); border-radius: 12px; padding: 16px; border: 1px solid rgba(45,123,255,0.07); }
|
||||
|
||||
/* Loading skeleton */
|
||||
.kai-skeleton {
|
||||
background: rgba(255,255,255,0.05);
|
||||
background: rgba(45,123,255,0.05);
|
||||
border-radius: 12px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@@ -462,15 +466,15 @@
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.04), transparent);
|
||||
background: linear-gradient(90deg, transparent, rgba(45,123,255,0.06), transparent);
|
||||
animation: kai-shimmer 1.5s infinite;
|
||||
}
|
||||
@keyframes kai-shimmer { 0% { transform: translateX(-100%) } 100% { transform: translateX(100%) } }
|
||||
|
||||
/* Factor card */
|
||||
.kai-factor {
|
||||
background: rgba(255,255,255,0.03);
|
||||
border: 1px solid var(--kai-border);
|
||||
background: rgba(45,123,255,0.04);
|
||||
border: 1px solid rgba(45,123,255,0.14);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
}
|
||||
@@ -504,7 +508,7 @@
|
||||
.kai-focus-text { font-size: 13px; font-weight: 600; color: var(--kai-txt); line-height: 1.5; padding-top: 6px; }
|
||||
|
||||
/* Search demand badges */
|
||||
.kai-query-card { padding: 12px 16px; background: rgba(255,255,255,0.03); border: 1px solid var(--kai-border); border-radius: 10px; }
|
||||
.kai-query-card { padding: 12px 16px; background: rgba(45,123,255,0.04); border: 1px solid rgba(45,123,255,0.14); border-radius: 10px; }
|
||||
.kai-query-label { font-size: 13px; color: var(--kai-txt); font-weight: 500; margin-bottom: 8px; }
|
||||
|
||||
/* CTA tags (behavioral) */
|
||||
@@ -530,5 +534,5 @@
|
||||
}
|
||||
.kai-peak-title { font-weight: 700; color: var(--kai-blue); font-size: 13px; margin-bottom: 4px; }
|
||||
.kai-peak-sub { font-size: 11px; color: var(--kai-txt2); }
|
||||
.kai-term-icon { color: rgba(255,255,255,0.4); font-size: 13px; margin-left: 4px; cursor: help; transition: color 0.2s; }
|
||||
.kai-term-icon { color: rgba(45,123,255,0.4); font-size: 13px; margin-left: 4px; cursor: help; transition: color 0.2s; }
|
||||
.kai-term-icon:hover { color: #2D7BFF; }
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
<script setup>
|
||||
import AppConfigurator from '@/layout/AppConfigurator.vue';
|
||||
import { useLayout } from '@/layout/composables/layout';
|
||||
|
||||
const { toggleDarkMode, isDarkTheme } = useLayout();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="fixed flex gap-4 top-8 right-8">
|
||||
<Button type="button" @click="toggleDarkMode" rounded :icon="isDarkTheme ? 'pi pi-moon' : 'pi pi-sun'" severity="secondary" />
|
||||
<div class="relative">
|
||||
<Button
|
||||
icon="pi pi-palette"
|
||||
|
||||
@@ -299,6 +299,19 @@ const goBack = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const extractAnalysisId = (result) => {
|
||||
if (!result) return null;
|
||||
return (
|
||||
result.analysisId ||
|
||||
result.id ||
|
||||
result.analysis?.id ||
|
||||
result.analysis?.analysisId ||
|
||||
result.data?.analysisId ||
|
||||
result.data?.id ||
|
||||
null
|
||||
);
|
||||
};
|
||||
|
||||
// Submit
|
||||
const handleSubmit = async () => {
|
||||
// Финальная проверка всех шагов перед отправкой на всякий случай
|
||||
@@ -327,7 +340,7 @@ const handleSubmit = async () => {
|
||||
};
|
||||
|
||||
const result = await MarketingV3Service.startAnalysisV3(payload);
|
||||
const analysisId = result.analysisId || result.id;
|
||||
const analysisId = extractAnalysisId(result);
|
||||
|
||||
if (analysisId) {
|
||||
toast.add({
|
||||
@@ -337,7 +350,14 @@ const handleSubmit = async () => {
|
||||
life: 3000
|
||||
});
|
||||
|
||||
router.push(`/marketing-analysis/v3/${analysisId}`);
|
||||
await router.push({
|
||||
name: 'marketing-v3-analysis',
|
||||
params: { id: String(analysisId) }
|
||||
});
|
||||
|
||||
if (router.currentRoute.value.params.id !== String(analysisId)) {
|
||||
window.location.assign(`/marketing-analysis/v3/${analysisId}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error('Не получен ID анализа');
|
||||
}
|
||||
@@ -422,7 +442,7 @@ export default {
|
||||
<div v-for="(label, index) in STEPS" :key="index"
|
||||
class="h-1 rounded-full flex-1 transition-all duration-500"
|
||||
:class="[
|
||||
index <= currentStep ? 'bg-blue-500 shadow-[0_0_8px_rgba(45,123,255,0.4)]' : 'bg-surface-800'
|
||||
index <= currentStep ? 'bg-blue-500 shadow-[0_0_8px_rgba(45,123,255,0.4)]' : 'bg-slate-200'
|
||||
]">
|
||||
</div>
|
||||
</div>
|
||||
@@ -589,7 +609,7 @@ export default {
|
||||
</div>
|
||||
|
||||
<!-- 3. FOOTER SECTION -->
|
||||
<div class="p-6 border-t border-surface-800 bg-black/20 flex flex-col sm:flex-row items-center justify-between gap-4">
|
||||
<div class="p-6 border-t border-surface-100 bg-white/80 backdrop-blur-sm flex flex-col sm:flex-row items-center justify-between gap-4">
|
||||
<Button label="Назад" icon="pi pi-arrow-left" severity="secondary" text class="font-bold text-[11px] uppercase tracking-widest" :disabled="currentStep === 0 || submitting" @click="goBack" />
|
||||
|
||||
<div class="flex items-center gap-3 w-full sm:w-auto">
|
||||
@@ -602,13 +622,6 @@ export default {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI Status Bar -->
|
||||
<div class="mt-4 flex items-center justify-center gap-4 text-[9px] font-black uppercase tracking-[0.2em] text-surface-500">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-emerald-500 shadow-[0_0_8px_var(--kai-green)]"></span>
|
||||
KAI Engine: Ready
|
||||
<span class="w-px h-3 bg-surface-800"></span>
|
||||
Secure Input Enabled
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -283,7 +283,7 @@ const maxY = computed(() => Math.max(Math.max(...scatter.value.map(d => d.y)) *
|
||||
|
||||
.expanded-row td {
|
||||
padding: 0 !important;
|
||||
background: rgba(0,0,0,0.2);
|
||||
background: rgba(45,123,255,0.04);
|
||||
border-bottom: 1px solid var(--kai-border);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,80 +2,212 @@
|
||||
import { computed } from 'vue';
|
||||
const props = defineProps({ status: String, exec: Object });
|
||||
const emit = defineEmits(['strategy', 'back']);
|
||||
const niche = computed(() => props.exec?.geography || '');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="kai-header">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="kai-logo">KAI</div>
|
||||
<div class="kai-title-area">
|
||||
<div class="kai-title-niche">Market Intelligence · {{ exec?.geography || '' }}</div>
|
||||
<div class="kai-title-sub">Professional Ed. · KAI Prime (2026)</div>
|
||||
<header class="v4-header">
|
||||
<!-- Left: brand + niche info -->
|
||||
<div class="v4-header__left">
|
||||
<div class="v4-header__logo">KAI</div>
|
||||
<div class="v4-header__info">
|
||||
<div class="v4-header__niche">Маркет Интеллект · {{ niche }}</div>
|
||||
<div class="v4-header__edition">KAI Prime 2026 · Professional Ed.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 ml-auto">
|
||||
<div v-if="status === 'COMPLETED'" class="kai-badge-status green">
|
||||
<span class="w-1 h-1 rounded-full bg-emerald-500 animate-pulse"></span>
|
||||
Ready
|
||||
|
||||
<!-- Right: status + actions -->
|
||||
<div class="v4-header__right">
|
||||
<!-- Status badge -->
|
||||
<div v-if="status === 'COMPLETED'" class="v4-status v4-status--ready">
|
||||
<span class="v4-status__dot v4-status__dot--green"></span>
|
||||
Готово
|
||||
</div>
|
||||
<div v-else-if="status === 'PROCESSING' || status === 'QUEUED'" class="kai-badge-status yellow">
|
||||
<span class="w-1 h-1 rounded-full bg-yellow-500 animate-pulse"></span>
|
||||
Ingesting...
|
||||
<div v-else-if="status === 'PROCESSING' || status === 'QUEUED'" class="v4-status v4-status--processing">
|
||||
<span class="v4-status__dot v4-status__dot--yellow"></span>
|
||||
Обработка...
|
||||
</div>
|
||||
|
||||
<button class="kai-btn-minor" @click="$emit('back')">
|
||||
<i class="pi pi-list text-[10px]" />
|
||||
|
||||
<!-- Back to list -->
|
||||
<button class="v4-btn-icon" @click="$emit('back')" title="Вернуться к списку анализов">
|
||||
<i class="pi pi-list"></i>
|
||||
</button>
|
||||
|
||||
<button v-if="status === 'COMPLETED'" class="kai-cta-btn-header" @click="$emit('strategy')">
|
||||
Generate Strategy
|
||||
<i class="pi pi-arrow-right ml-1 text-[9px] opacity-60" />
|
||||
|
||||
<!-- Generate Strategy -->
|
||||
<button v-if="status === 'COMPLETED'" class="v4-btn-strategy" @click="$emit('strategy')">
|
||||
<i class="pi pi-sparkles"></i>
|
||||
Создать стратегию
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.kai-btn-minor {
|
||||
background: var(--kai-card);
|
||||
border: 1px solid var(--kai-border);
|
||||
color: var(--kai-txt3);
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 8px;
|
||||
.v4-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
height: 60px;
|
||||
background: rgba(255, 255, 255, 0.94);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid rgba(45, 123, 255, 0.14);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 28px;
|
||||
gap: 16px;
|
||||
box-shadow: 0 2px 16px rgba(45,123,255,0.06);
|
||||
}
|
||||
|
||||
.v4-header__left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.v4-header__logo {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 9px;
|
||||
background: linear-gradient(135deg, #2D7BFF, #1a55cc);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
font-weight: 900;
|
||||
font-size: 10px;
|
||||
color: #fff;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0 4px 12px rgba(45,123,255,0.35);
|
||||
}
|
||||
|
||||
.v4-header__info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.v4-header__niche {
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 11px;
|
||||
color: #0F172A;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.v4-header__edition {
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
color: #94A3B8;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
.v4-header__right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* Status badge */
|
||||
.v4-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 12px;
|
||||
border-radius: 999px;
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
font-size: 9px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.v4-status--ready {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border: 1px solid rgba(16, 185, 129, 0.25);
|
||||
color: #10B981;
|
||||
}
|
||||
|
||||
.v4-status--processing {
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
border: 1px solid rgba(245, 158, 11, 0.25);
|
||||
color: #F59E0B;
|
||||
}
|
||||
|
||||
.v4-status__dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.v4-status__dot--green {
|
||||
background: #10B981;
|
||||
box-shadow: 0 0 6px rgba(16, 185, 129, 0.5);
|
||||
}
|
||||
|
||||
.v4-status__dot--yellow {
|
||||
background: #F59E0B;
|
||||
box-shadow: 0 0 6px rgba(245, 158, 11, 0.5);
|
||||
animation: pulse-yellow 1.2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse-yellow {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.4; }
|
||||
}
|
||||
|
||||
/* Icon button */
|
||||
.v4-btn-icon {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(45, 123, 255, 0.18);
|
||||
background: rgba(45, 123, 255, 0.04);
|
||||
color: #64748B;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
font-size: 13px;
|
||||
}
|
||||
.v4-btn-icon:hover {
|
||||
background: rgba(45, 123, 255, 0.09);
|
||||
color: #2D7BFF;
|
||||
border-color: rgba(45, 123, 255, 0.32);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.kai-btn-minor:hover {
|
||||
background: var(--kai-card-hover);
|
||||
border-color: var(--kai-border-hover);
|
||||
color: var(--kai-txt);
|
||||
}
|
||||
|
||||
.kai-cta-btn-header {
|
||||
background: var(--kai-blue);
|
||||
/* Strategy button */
|
||||
.v4-btn-strategy {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
padding: 8px 18px;
|
||||
border-radius: 10px;
|
||||
background: #2D7BFF;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
transition: all 0.3s;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 12px rgba(var(--kai-blue-rgb), 0.2);
|
||||
transition: all 0.25s;
|
||||
box-shadow: 0 4px 16px rgba(45, 123, 255, 0.3);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.kai-cta-btn-header:hover {
|
||||
transform: translateY(-1px);
|
||||
filter: brightness(1.1);
|
||||
box-shadow: 0 6px 16px rgba(var(--kai-blue-rgb), 0.3);
|
||||
.v4-btn-strategy:hover {
|
||||
background: #1a6bff;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 24px rgba(45, 123, 255, 0.45);
|
||||
}
|
||||
.v4-btn-strategy i {
|
||||
font-size: 11px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,58 +2,64 @@
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
|
||||
const sections = [
|
||||
{ id: 's0', label: 'Overview' },
|
||||
{ id: 's1', label: 'Landscape' },
|
||||
{ id: 's2', label: 'Geometry' },
|
||||
{ id: 's3', label: 'Competitors' },
|
||||
{ id: 's4', label: 'Content' },
|
||||
{ id: 's5', label: 'Intensity' },
|
||||
{ id: 's6', label: 'Reputation' },
|
||||
{ id: 's7', label: 'Behavior' },
|
||||
{ id: 's8', label: 'Demand' },
|
||||
{ id: 's9', label: 'Positioning' },
|
||||
{ id: 's10', label: 'Summary' },
|
||||
{ id: 's11', label: 'Rationale' },
|
||||
{ id: 's0', label: 'Обзор', icon: 'pi pi-home' },
|
||||
{ id: 's1', label: 'Ландшафт', icon: 'pi pi-map' },
|
||||
{ id: 's2', label: 'География', icon: 'pi pi-map-marker' },
|
||||
{ id: 's3', label: 'Конкуренты', icon: 'pi pi-users' },
|
||||
{ id: 's4', label: 'Контент', icon: 'pi pi-file' },
|
||||
{ id: 's5', label: 'Интенсивность', icon: 'pi pi-bolt' },
|
||||
{ id: 's6', label: 'Репутация', icon: 'pi pi-star' },
|
||||
{ id: 's7', label: 'Поведение', icon: 'pi pi-eye' },
|
||||
{ id: 's8', label: 'Спрос', icon: 'pi pi-chart-line' },
|
||||
{ id: 's9', label: 'Позиционирование',icon: 'pi pi-crosshairs' },
|
||||
{ id: 's10', label: 'Выводы', icon: 'pi pi-check-circle' },
|
||||
{ id: 's11', label: 'Стратегия', icon: 'pi pi-sparkles' },
|
||||
];
|
||||
|
||||
const active = ref('s0');
|
||||
|
||||
const go = (id) => {
|
||||
const el = document.getElementById(id);
|
||||
if (el) {
|
||||
window.scrollTo({
|
||||
top: el.offsetTop - 100,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
window.scrollTo({ top: el.offsetTop - 80, behavior: 'smooth' });
|
||||
}
|
||||
};
|
||||
|
||||
let obs;
|
||||
onMounted(() => {
|
||||
obs = new IntersectionObserver(entries => {
|
||||
entries.forEach(e => {
|
||||
if (e.isIntersecting) active.value = e.target.id;
|
||||
});
|
||||
}, { threshold: 0.3, rootMargin: '-10% 0px -70% 0px' });
|
||||
|
||||
sections.forEach(s => {
|
||||
const el = document.getElementById(s.id);
|
||||
if (el) obs.observe(el);
|
||||
});
|
||||
entries.forEach(e => { if (e.isIntersecting) active.value = e.target.id; });
|
||||
}, { threshold: 0.25, rootMargin: '-5% 0px -65% 0px' });
|
||||
sections.forEach(s => { const el = document.getElementById(s.id); if (el) obs.observe(el); });
|
||||
});
|
||||
|
||||
onUnmounted(() => obs?.disconnect());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nav class="kai-sidenav">
|
||||
<div class="sidenav-inner">
|
||||
<button v-for="(s, i) in sections" :key="s.id"
|
||||
class="sidenav-item"
|
||||
:class="{ active: active === s.id }"
|
||||
@click="go(s.id)">
|
||||
<span class="sidenav-num">{{ i }}</span>
|
||||
<span class="sidenav-label">{{ s.label }}</span>
|
||||
</button>
|
||||
<div class="sidenav-track">
|
||||
<button
|
||||
v-for="(s, i) in sections"
|
||||
:key="s.id"
|
||||
class="sidenav-item"
|
||||
:class="{ active: active === s.id }"
|
||||
@click="go(s.id)"
|
||||
:title="s.label"
|
||||
>
|
||||
<!-- Active line indicator -->
|
||||
<span class="sidenav-line" :class="{ 'sidenav-line--active': active === s.id }"></span>
|
||||
|
||||
<!-- Number dot -->
|
||||
<span class="sidenav-dot">
|
||||
<i v-if="active === s.id" :class="s.icon" class="sidenav-icon-active"></i>
|
||||
<span v-else class="sidenav-num">{{ i + 1 }}</span>
|
||||
</span>
|
||||
|
||||
<!-- Tooltip label -->
|
||||
<span class="sidenav-label">{{ s.label }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
@@ -61,91 +67,144 @@ onUnmounted(() => obs?.disconnect());
|
||||
<style scoped>
|
||||
.kai-sidenav {
|
||||
position: fixed;
|
||||
left: 24px;
|
||||
left: 16px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
z-index: 200;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.sidenav-inner {
|
||||
pointer-events: auto;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
backdrop-filter: blur(16px);
|
||||
-webkit-backdrop-filter: blur(16px);
|
||||
border: 1px solid rgba(45, 123, 255, 0.18);
|
||||
border-radius: 20px;
|
||||
padding: 10px 6px;
|
||||
box-shadow: 0 8px 32px rgba(45, 123, 255, 0.1), 0 2px 8px rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
.sidenav-track {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
pointer-events: auto;
|
||||
padding: 12px 8px;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--kai-border);
|
||||
border-radius: 16px;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.sidenav-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0;
|
||||
padding: 5px 4px;
|
||||
border-radius: 10px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.sidenav-item:hover {
|
||||
background: rgba(45, 123, 255, 0.06);
|
||||
}
|
||||
|
||||
.sidenav-item.active {
|
||||
background: rgba(45, 123, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Left accent line */
|
||||
.sidenav-line {
|
||||
position: absolute;
|
||||
left: -6px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 3px;
|
||||
height: 0;
|
||||
background: var(--kai-blue, #2D7BFF);
|
||||
border-radius: 4px;
|
||||
transition: height 0.25s ease;
|
||||
}
|
||||
.sidenav-line--active {
|
||||
height: 60%;
|
||||
}
|
||||
|
||||
/* Dot */
|
||||
.sidenav-dot {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 8px;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
overflow: visible;
|
||||
flex-shrink: 0;
|
||||
transition: all 0.25s;
|
||||
}
|
||||
|
||||
.sidenav-num {
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
font-size: 8px;
|
||||
font-size: 8.5px;
|
||||
font-weight: 800;
|
||||
color: var(--kai-txt3);
|
||||
transition: all 0.3s;
|
||||
color: #94A3B8;
|
||||
transition: color 0.25s;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.sidenav-item:hover .sidenav-num {
|
||||
color: #2D7BFF;
|
||||
}
|
||||
|
||||
.sidenav-item.active .sidenav-num {
|
||||
color: #2D7BFF;
|
||||
}
|
||||
|
||||
.sidenav-icon-active {
|
||||
font-size: 11px;
|
||||
color: #2D7BFF;
|
||||
}
|
||||
|
||||
/* Tooltip */
|
||||
.sidenav-label {
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
background: var(--kai-bg);
|
||||
border: 1px solid var(--kai-border);
|
||||
color: var(--kai-txt);
|
||||
padding: 4px 10px;
|
||||
border-radius: 6px;
|
||||
left: calc(100% + 12px);
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: #0F172A;
|
||||
color: #fff;
|
||||
padding: 5px 10px;
|
||||
border-radius: 8px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
font-family: 'Onest', sans-serif;
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
transform: translateX(-10px);
|
||||
pointer-events: none;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transform: translateY(-50%) translateX(-6px);
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.2);
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
.sidenav-label::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 100%;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
border: 5px solid transparent;
|
||||
border-right-color: #0F172A;
|
||||
}
|
||||
|
||||
.sidenav-item:hover .sidenav-label {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
transform: translateY(-50%) translateX(0);
|
||||
}
|
||||
|
||||
.sidenav-item:hover {
|
||||
background: var(--kai-card-hover);
|
||||
border-color: var(--kai-border);
|
||||
}
|
||||
|
||||
.sidenav-item.active {
|
||||
background: var(--kai-blue);
|
||||
border-color: var(--kai-blue);
|
||||
box-shadow: 0 0 15px rgba(var(--kai-blue-rgb), 0.4);
|
||||
}
|
||||
|
||||
.sidenav-item.active .sidenav-num {
|
||||
color: #fff;
|
||||
.sidenav-item.active .sidenav-label {
|
||||
opacity: 1;
|
||||
transform: translateY(-50%) translateX(0);
|
||||
}
|
||||
|
||||
@media (max-width: 1400px) {
|
||||
.kai-sidenav {
|
||||
display: none;
|
||||
}
|
||||
.kai-sidenav { display: none; }
|
||||
}
|
||||
</style>
|
||||
|
||||
+13
-2
@@ -37,9 +37,20 @@ const model = ref([
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Admin',
|
||||
label: 'Сервисы',
|
||||
items: [
|
||||
{ label: 'Users', icon: 'pi pi-fw pi-users', to: '/admin/users' },
|
||||
{
|
||||
label: 'CRM колл-центр',
|
||||
icon: 'pi pi-fw pi-external-link',
|
||||
url: 'https://call-center.konturai.kz/operator#workspace',
|
||||
target: '_blank'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Администрирование',
|
||||
items: [
|
||||
{ label: 'Пользователи', icon: 'pi pi-fw pi-users', to: '/admin/users' },
|
||||
{ label: 'Управление парсером', icon: 'pi pi-fw pi-cog', to: '/admin/news' }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -71,7 +71,15 @@ function checkActiveRoute(item) {
|
||||
<template>
|
||||
<li :class="{ 'layout-root-menuitem': root, 'active-menuitem': isActiveMenu }">
|
||||
<div v-if="root && item.visible !== false" class="layout-menuitem-root-text">{{ item.label }}</div>
|
||||
<a v-if="(!item.to || item.items) && item.visible !== false" :href="item.url" @click="itemClick($event, item, index)" :class="item.class" :target="item.target" tabindex="0">
|
||||
<a
|
||||
v-if="(!item.to || item.items) && item.visible !== false"
|
||||
:href="item.url"
|
||||
@click="itemClick($event, item, index)"
|
||||
:class="item.class"
|
||||
:target="item.target"
|
||||
:rel="item.target === '_blank' ? 'noopener noreferrer' : null"
|
||||
tabindex="0"
|
||||
>
|
||||
<i :class="item.icon" class="layout-menuitem-icon"></i>
|
||||
<span class="layout-menuitem-text">{{ item.label }}</span>
|
||||
<i class="pi pi-fw pi-angle-down layout-submenu-toggler" v-if="item.items"></i>
|
||||
|
||||
@@ -4,7 +4,7 @@ import AuthService from '@/service/AuthService';
|
||||
import { useRouter } from 'vue-router';
|
||||
import AppConfigurator from './AppConfigurator.vue';
|
||||
|
||||
const { toggleMenu, toggleDarkMode, isDarkTheme } = useLayout();
|
||||
const { toggleMenu } = useLayout();
|
||||
const router = useRouter();
|
||||
|
||||
async function onLogout() {
|
||||
@@ -44,9 +44,6 @@ async function onLogout() {
|
||||
|
||||
<div class="layout-topbar-actions">
|
||||
<div class="layout-config-menu">
|
||||
<button type="button" class="layout-topbar-action" @click="toggleDarkMode">
|
||||
<i :class="['pi', { 'pi-moon': isDarkTheme, 'pi-sun': !isDarkTheme }]"></i>
|
||||
</button>
|
||||
<div class="relative">
|
||||
<button
|
||||
v-styleclass="{ selector: '@next', enterFromClass: 'hidden', enterActiveClass: 'animate-scalein', leaveToClass: 'hidden', leaveActiveClass: 'animate-fadeout', hideOnOutsideClick: true }"
|
||||
@@ -78,7 +75,7 @@ async function onLogout() {
|
||||
</button> -->
|
||||
<button type="button" class="layout-topbar-action" @click="onLogout">
|
||||
<i class="pi pi-sign-out"></i>
|
||||
<span>Logout</span>
|
||||
<span>Выйти</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -24,18 +24,13 @@ export function useLayout() {
|
||||
};
|
||||
|
||||
const toggleDarkMode = () => {
|
||||
if (!document.startViewTransition) {
|
||||
executeDarkModeToggle();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
document.startViewTransition(() => executeDarkModeToggle(event));
|
||||
layoutConfig.darkTheme = false;
|
||||
document.documentElement.classList.remove('app-dark');
|
||||
};
|
||||
|
||||
const executeDarkModeToggle = () => {
|
||||
layoutConfig.darkTheme = !layoutConfig.darkTheme;
|
||||
document.documentElement.classList.toggle('app-dark');
|
||||
layoutConfig.darkTheme = false;
|
||||
document.documentElement.classList.remove('app-dark');
|
||||
};
|
||||
|
||||
const toggleMenu = () => {
|
||||
@@ -58,6 +53,8 @@ export function useLayout() {
|
||||
|
||||
const getSurface = computed(() => layoutConfig.surface);
|
||||
|
||||
executeDarkModeToggle();
|
||||
|
||||
return {
|
||||
layoutConfig,
|
||||
layoutState,
|
||||
|
||||
@@ -12,6 +12,8 @@ import '@/assets/styles.scss';
|
||||
import '@/assets/kai-v4.css';
|
||||
import 'katex/dist/katex.min.css';
|
||||
|
||||
document.documentElement.classList.remove('app-dark');
|
||||
|
||||
const app = createApp(App);
|
||||
const pinia = createPinia();
|
||||
|
||||
|
||||
@@ -1,13 +1,26 @@
|
||||
<script>
|
||||
export default {
|
||||
name: 'DashboardNew'
|
||||
name: 'DashboardNew',
|
||||
methods: {
|
||||
openCrm() {
|
||||
const url = 'https://call-center.konturai.kz/operator#workspace';
|
||||
const newWindow = window.open(url, '_blank', 'noopener,noreferrer');
|
||||
if (newWindow) newWindow.opener = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card">
|
||||
<h1 class="text-2xl font-semibold">New Dashboard</h1>
|
||||
<p class="text-color-secondary">Страница пока пустая.</p>
|
||||
<div class="flex items-center justify-between gap-4 flex-wrap">
|
||||
<div>
|
||||
<h1 class="text-2xl font-semibold">Главный экран</h1>
|
||||
<p class="text-color-secondary">Быстрые ссылки и основные разделы.</p>
|
||||
</div>
|
||||
|
||||
<Button label="CRM Call-Center" icon="pi pi-external-link" severity="secondary" @click="openCrm" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -67,9 +67,9 @@ async function onSubmit() {
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<Checkbox v-model="checked" id="rememberme1" binary inputId="rememberme1" />
|
||||
<label for="rememberme1" class="ml-2 text-sm text-surface-600 dark:text-surface-400">Remember me</label>
|
||||
<label for="rememberme1" class="ml-2 text-sm text-surface-600 dark:text-surface-400">Запомнить меня</label>
|
||||
</div>
|
||||
<span class="cursor-pointer text-sm font-medium text-primary no-underline hover:text-primary-600 dark:text-primary-400"> Forgot password? </span>
|
||||
<span class="cursor-pointer text-sm font-medium text-primary no-underline hover:text-primary-600 dark:text-primary-400"> Забыли пароль? </span>
|
||||
</div>
|
||||
|
||||
<Button :loading="loading" label="Войти" icon="pi pi-sign-in" icon-pos="right" class="w-full" @click="onSubmit" />
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
<script setup>
|
||||
import '@/assets/kai-v4.css';
|
||||
import MarketingV3Service from '@/service/MarketingV3Service';
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import '@/assets/kai-v4.css';
|
||||
|
||||
// ─── Sections ────────────────────────────────────────────────────────────────
|
||||
import V4Header from '@/components/marketing/dashboard-v3/v4/V4Header.vue';
|
||||
import V4Executive from '@/components/marketing/dashboard-v3/v4/V4Executive.vue';
|
||||
import V4Landscape from '@/components/marketing/dashboard-v3/v4/V4Landscape.vue';
|
||||
import V4Geo from '@/components/marketing/dashboard-v3/v4/V4Geo.vue';
|
||||
import V4Competitors from '@/components/marketing/dashboard-v3/v4/V4Competitors.vue';
|
||||
import V4Content from '@/components/marketing/dashboard-v3/v4/V4Content.vue';
|
||||
import V4Cii from '@/components/marketing/dashboard-v3/v4/V4Cii.vue';
|
||||
import V4Reputation from '@/components/marketing/dashboard-v3/v4/V4Reputation.vue';
|
||||
import V4Behavioral from '@/components/marketing/dashboard-v3/v4/V4Behavioral.vue';
|
||||
import V4Demand from '@/components/marketing/dashboard-v3/v4/V4Demand.vue';
|
||||
import V4Positioning from '@/components/marketing/dashboard-v3/v4/V4Positioning.vue';
|
||||
import V4Conclusions from '@/components/marketing/dashboard-v3/v4/V4Conclusions.vue';
|
||||
import V4Rationale from '@/components/marketing/dashboard-v3/v4/V4Rationale.vue';
|
||||
import V4SideNav from '@/components/marketing/dashboard-v3/v4/V4SideNav.vue';
|
||||
import V4Behavioral from '@/components/marketing/dashboard-v3/v4/V4Behavioral.vue';
|
||||
import V4Cii from '@/components/marketing/dashboard-v3/v4/V4Cii.vue';
|
||||
import V4Competitors from '@/components/marketing/dashboard-v3/v4/V4Competitors.vue';
|
||||
import V4Conclusions from '@/components/marketing/dashboard-v3/v4/V4Conclusions.vue';
|
||||
import V4Content from '@/components/marketing/dashboard-v3/v4/V4Content.vue';
|
||||
import V4Demand from '@/components/marketing/dashboard-v3/v4/V4Demand.vue';
|
||||
import V4Executive from '@/components/marketing/dashboard-v3/v4/V4Executive.vue';
|
||||
import V4Geo from '@/components/marketing/dashboard-v3/v4/V4Geo.vue';
|
||||
import V4Header from '@/components/marketing/dashboard-v3/v4/V4Header.vue';
|
||||
import V4Landscape from '@/components/marketing/dashboard-v3/v4/V4Landscape.vue';
|
||||
import V4Positioning from '@/components/marketing/dashboard-v3/v4/V4Positioning.vue';
|
||||
import V4Rationale from '@/components/marketing/dashboard-v3/v4/V4Rationale.vue';
|
||||
import V4Reputation from '@/components/marketing/dashboard-v3/v4/V4Reputation.vue';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
@@ -29,7 +28,7 @@ const status = ref('IDLE');
|
||||
const result = ref(null);
|
||||
const errorMsg = ref('');
|
||||
|
||||
const MSGS = ['Data Ingestion: Active','Contextual Mapping...','Competitor Core Analysis...','Sentiment Extraction...','Synthesis & Prioritization...'];
|
||||
const MSGS = ['Сбор данных активен', 'Контекстная маршрутизация...', 'Анализ ядра конкурентов...', 'Извлечение тональности...', 'Синтез и приоритизация...'];
|
||||
const loaderMsg = ref(MSGS[0]);
|
||||
const loaderDot = ref(1);
|
||||
let pollInterval = null;
|
||||
@@ -76,7 +75,8 @@ const fetchAnalysis = async () => {
|
||||
loaderMsg.value = MSGS[++tick % MSGS.length];
|
||||
loaderDot.value = (loaderDot.value % 5) + 1;
|
||||
} catch (e) {
|
||||
status.value = 'FAILED'; errorMsg.value = e.message || 'Ошибка загрузки.';
|
||||
status.value = 'FAILED';
|
||||
errorMsg.value = e.message || 'Ошибка загрузки анализа.';
|
||||
} finally { loading.value = false; }
|
||||
};
|
||||
|
||||
@@ -97,8 +97,8 @@ onUnmounted(() => stopPolling());
|
||||
<!-- Sticky Header -->
|
||||
<V4Header :status="status" :exec="exec" @strategy="goStrategy" @back="$router.push('/marketing-analysis/v3')" />
|
||||
|
||||
<!-- Side Nav -->
|
||||
<V4SideNav v-if="status === 'COMPLETED'" />
|
||||
<!-- Side Nav - DISABLED -->
|
||||
<!-- <V4SideNav v-if="status === 'COMPLETED'" /> -->
|
||||
|
||||
<div class="analysis-container">
|
||||
|
||||
@@ -111,7 +111,7 @@ onUnmounted(() => stopPolling());
|
||||
</div>
|
||||
<div class="loading-info">
|
||||
<div class="loading-title">{{ loaderMsg }}</div>
|
||||
<p class="loading-sub">KAI Prime Engine is synthesising market intelligence...</p>
|
||||
<p class="loading-sub">KAI Prime обрабатывает данные и формирует маркетинговую аналитику...</p>
|
||||
<div class="loading-dots">
|
||||
<span v-for="i in 5" :key="i" class="dot" :class="{ active: loaderDot === i }"></span>
|
||||
</div>
|
||||
@@ -123,10 +123,10 @@ onUnmounted(() => stopPolling());
|
||||
<div class="error-icon">
|
||||
<i class="pi pi-exclamation-triangle" />
|
||||
</div>
|
||||
<h2 class="error-title">Analysis Interrupted</h2>
|
||||
<p class="error-desc">{{ errorMsg || 'An unexpected error occurred during intelligence synthesis.' }}</p>
|
||||
<h2 class="error-title">Анализ прерван</h2>
|
||||
<p class="error-desc">{{ errorMsg || 'Во время анализа произошла непредвиденная ошибка.' }}</p>
|
||||
<button class="kai-btn-secondary" @click="$router.push('/marketing-analysis/v3')">
|
||||
<i class="pi pi-arrow-left mr-2" /> Return to Command Center
|
||||
<i class="pi pi-arrow-left mr-2" /> Вернуться к списку анализов
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@
|
||||
import { useRouter } from 'vue-router';
|
||||
const router = useRouter();
|
||||
const nav = (path, query) => query ? router.push({ path, query }) : router.push(path);
|
||||
const navExternal = (url) => window.open(url, '_blank');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -17,7 +18,7 @@ const nav = (path, query) => query ? router.push({ path, query }) : router.push(
|
||||
<div class="mkt-hub__header">
|
||||
<div class="mkt-hub__badge">
|
||||
<span class="mkt-hub__badge-dot"></span>
|
||||
KAI Intelligence · Prime 2026
|
||||
KAI Intelligence · Платформа 2026
|
||||
</div>
|
||||
<h1 class="mkt-hub__title">Маркетинг-Хаб</h1>
|
||||
<p class="mkt-hub__sub">Автономное управление маркетингом: от глубоких исследований рынка до генерации контента и запуска рекламных кампаний.</p>
|
||||
@@ -67,6 +68,26 @@ const nav = (path, query) => query ? router.push({ path, query }) : router.push(
|
||||
<div class="mkt-card__arrow"><i class="pi pi-arrow-right"></i></div>
|
||||
</div>
|
||||
|
||||
<!-- ИИ-Таргетолог -->
|
||||
<div class="mkt-card mkt-card--blue" @click="navExternal('https://call-center.konturai.kz')">
|
||||
<div class="mkt-card__icon" style="background:rgba(45,123,255,0.15);color:#2D7BFF;">
|
||||
<i class="pi pi-bullseye"></i>
|
||||
</div>
|
||||
<h3 class="mkt-card__title">ИИ-Таргетолог</h3>
|
||||
<p class="mkt-card__desc">Развёртывание рекламных кампаний — ИИ создаст аудитории, оптимизирует бюджет и запустит рекламу в Facebook, Instagram и TikTok</p>
|
||||
<div class="mkt-card__tag">Бета-режим API</div>
|
||||
</div>
|
||||
|
||||
<!-- CRM -->
|
||||
<div class="mkt-card mkt-card--teal" @click="nav('/crm')">
|
||||
<div class="mkt-card__icon" style="background:rgba(20,184,166,0.15);color:#14B8A6;">
|
||||
<i class="pi pi-users"></i>
|
||||
</div>
|
||||
<h3 class="mkt-card__title">CRM-система</h3>
|
||||
<p class="mkt-card__desc">Управление клиентской базой, сделками и воронкой продаж в едином пространстве</p>
|
||||
<div class="mkt-card__arrow" style="background:rgba(20,184,166,0.1);color:#14B8A6;"><i class="pi pi-arrow-right"></i></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- AI TARGETOLOGIST — full-width banner -->
|
||||
@@ -79,7 +100,7 @@ const nav = (path, query) => query ? router.push({ path, query }) : router.push(
|
||||
<div>
|
||||
<div class="mkt-banner__label">
|
||||
<span class="mkt-banner__dot"></span>
|
||||
Beta API
|
||||
Бета-режим API
|
||||
</div>
|
||||
<h2 class="mkt-banner__title">ИИ-Таргетолог</h2>
|
||||
<p class="mkt-banner__desc">Развёртывание рекламных кампаний — ИИ создаст аудитории, оптимизирует бюджет и запустит рекламу в Facebook, Instagram и TikTok</p>
|
||||
@@ -98,12 +119,13 @@ const nav = (path, query) => query ? router.push({ path, query }) : router.push(
|
||||
<style scoped>
|
||||
/* ───── Page layout ───── */
|
||||
.mkt-hub {
|
||||
background: var(--kai-bg);
|
||||
background: #F5F8FF;
|
||||
min-height: 100vh;
|
||||
font-family: 'Onest', sans-serif;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 48px 24px 80px;
|
||||
color: #0F172A;
|
||||
}
|
||||
|
||||
.mkt-hub__glow1 {
|
||||
@@ -144,22 +166,22 @@ const nav = (path, query) => query ? router.push({ path, query }) : router.push(
|
||||
gap: 6px;
|
||||
padding: 4px 12px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--kai-border);
|
||||
background: rgba(var(--kai-blue-rgb), 0.08);
|
||||
border: 1px solid rgba(45,123,255,0.18);
|
||||
background: rgba(45,123,255,0.08);
|
||||
font-size: 9px;
|
||||
font-weight: 800;
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--kai-blue);
|
||||
color: #2D7BFF;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.mkt-hub__badge-dot {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
background: var(--kai-blue);
|
||||
box-shadow: 0 0 8px var(--kai-blue);
|
||||
background: #2D7BFF;
|
||||
box-shadow: 0 0 8px #2D7BFF;
|
||||
animation: pulse-dot 2s infinite;
|
||||
}
|
||||
@keyframes pulse-dot {
|
||||
@@ -171,7 +193,7 @@ const nav = (path, query) => query ? router.push({ path, query }) : router.push(
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
font-size: 32px;
|
||||
font-weight: 900;
|
||||
color: var(--kai-txt);
|
||||
color: #0F172A;
|
||||
letter-spacing: -0.03em;
|
||||
line-height: 1.1;
|
||||
margin-bottom: 12px;
|
||||
@@ -179,7 +201,7 @@ const nav = (path, query) => query ? router.push({ path, query }) : router.push(
|
||||
|
||||
.mkt-hub__sub {
|
||||
font-size: 13px;
|
||||
color: var(--kai-txt2);
|
||||
color: #334155;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
line-height: 1.6;
|
||||
@@ -189,24 +211,28 @@ const nav = (path, query) => query ? router.push({ path, query }) : router.push(
|
||||
/* ───── Grid ───── */
|
||||
.mkt-hub__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.mkt-hub__grid { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.mkt-hub__grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
/* ───── Cards ───── */
|
||||
.mkt-card {
|
||||
background: var(--kai-card);
|
||||
border: 1px solid var(--kai-border);
|
||||
background: #FFFFFF;
|
||||
border: 1px solid rgba(45,123,255,0.18);
|
||||
border-radius: 20px;
|
||||
padding: 28px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s cubic-bezier(0.4,0,0.2,1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(45,123,255,0.05);
|
||||
}
|
||||
.mkt-card::before {
|
||||
content: '';
|
||||
@@ -219,16 +245,17 @@ const nav = (path, query) => query ? router.push({ path, query }) : router.push(
|
||||
}
|
||||
.mkt-card:hover {
|
||||
transform: translateY(-4px);
|
||||
border-color: var(--kai-border-hover);
|
||||
background: var(--kai-card-hover);
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.4), 0 0 40px rgba(45,123,255,0.08);
|
||||
border-color: rgba(45,123,255,0.35);
|
||||
background: #F8FBFF;
|
||||
box-shadow: 0 16px 36px rgba(15,23,42,0.1), 0 0 28px rgba(45,123,255,0.12);
|
||||
}
|
||||
.mkt-card:hover::before { opacity: 1; }
|
||||
|
||||
.mkt-card--blue::before { background: radial-gradient(circle at top right, rgba(45,123,255,0.1), transparent 60%); }
|
||||
.mkt-card--blue::before { background: radial-gradient(circle at top right, rgba(45,123,255,0.1), transparent 60%); }
|
||||
.mkt-card--purple::before { background: radial-gradient(circle at top right, rgba(168,85,247,0.1), transparent 60%); }
|
||||
.mkt-card--orange::before { background: radial-gradient(circle at top right, rgba(255,122,45,0.1), transparent 60%); }
|
||||
.mkt-card--accent::before { background: radial-gradient(circle at top right, rgba(0,212,139,0.1), transparent 60%); }
|
||||
.mkt-card--teal::before { background: radial-gradient(circle at top right, rgba(20,184,166,0.12), transparent 60%); }
|
||||
|
||||
.mkt-card__icon {
|
||||
width: 52px;
|
||||
@@ -247,14 +274,14 @@ const nav = (path, query) => query ? router.push({ path, query }) : router.push(
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: var(--kai-txt);
|
||||
color: #0F172A;
|
||||
margin-bottom: 8px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.mkt-card__desc {
|
||||
font-size: 11.5px;
|
||||
color: var(--kai-txt2);
|
||||
color: #334155;
|
||||
line-height: 1.55;
|
||||
font-weight: 400;
|
||||
}
|
||||
@@ -296,9 +323,9 @@ const nav = (path, query) => query ? router.push({ path, query }) : router.push(
|
||||
margin-top: 12px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(var(--kai-green-rgb), 0.25);
|
||||
background: rgba(var(--kai-green-rgb), 0.07);
|
||||
color: var(--kai-green);
|
||||
border: 1px solid rgba(16,185,129,0.25);
|
||||
background: rgba(16,185,129,0.07);
|
||||
color: #10B981;
|
||||
font-size: 8.5px;
|
||||
font-weight: 800;
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
@@ -308,8 +335,8 @@ const nav = (path, query) => query ? router.push({ path, query }) : router.push(
|
||||
|
||||
/* ───── Targetologist Banner ───── */
|
||||
.mkt-banner {
|
||||
background: linear-gradient(135deg, rgba(var(--kai-blue-rgb), 0.08) 0%, rgba(139,92,246,0.05) 100%);
|
||||
border: 1px solid var(--kai-border);
|
||||
background: linear-gradient(135deg, rgba(45,123,255,0.08) 0%, rgba(139,92,246,0.05) 100%);
|
||||
border: 1px solid rgba(45,123,255,0.18);
|
||||
border-radius: 20px;
|
||||
padding: 24px 32px;
|
||||
display: flex;
|
||||
@@ -320,6 +347,7 @@ const nav = (path, query) => query ? router.push({ path, query }) : router.push(
|
||||
transition: all 0.3s;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 16px rgba(45,123,255,0.06);
|
||||
}
|
||||
.mkt-banner:hover {
|
||||
border-color: rgba(45,123,255,0.6);
|
||||
@@ -385,14 +413,14 @@ const nav = (path, query) => query ? router.push({ path, query }) : router.push(
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
font-size: 20px;
|
||||
font-weight: 900;
|
||||
color: var(--kai-txt);
|
||||
color: #0F172A;
|
||||
margin-bottom: 6px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.mkt-banner__desc {
|
||||
font-size: 12px;
|
||||
color: var(--kai-txt2);
|
||||
color: #334155;
|
||||
line-height: 1.5;
|
||||
max-width: 520px;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<script setup>
|
||||
import { DEFAULT_STRATEGY_MODEL, STRATEGY_MODELS } from '@/config/marketingModels';
|
||||
import MarketingV3Service from '@/service/MarketingV3Service';
|
||||
import { STRATEGY_MODELS, DEFAULT_STRATEGY_MODEL } from '@/config/marketingModels';
|
||||
import { useTargetingStore } from '@/stores/targeting';
|
||||
import Button from 'primevue/button';
|
||||
import Dialog from 'primevue/dialog';
|
||||
import Tag from 'primevue/tag';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import { useTargetingStore } from '@/stores/targeting';
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
@@ -2059,7 +2058,21 @@ onUnmounted(() => {
|
||||
.footer-success-status { flex: 1; background: rgba(var(--kai-green-rgb), 0.1); border: 1px solid var(--kai-green); border-radius: 18px; display: flex; align-items: center; justify-content: center; gap: 12px; color: var(--kai-green); font-weight: 800; text-transform: uppercase; font-size: 11px; }
|
||||
|
||||
/* ───── MODAL ───── */
|
||||
.premium-modal { :deep(.p-dialog-content) { background: transparent !important; } }
|
||||
.premium-modal {
|
||||
:deep(.p-dialog-content) { background: transparent !important; }
|
||||
:deep(.p-dialog) {
|
||||
background: rgba(0, 0, 0, 0.7) !important;
|
||||
backdrop-filter: blur(10px);
|
||||
border: none !important;
|
||||
}
|
||||
:deep(.p-dialog-header) {
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
}
|
||||
:deep(.p-dialog-mask) {
|
||||
background: transparent !important;
|
||||
}
|
||||
}
|
||||
.modal-content-wrapper { position: relative; max-width: 900px; margin: 0 auto; }
|
||||
.modal-close-trigger { position: absolute; top: -40px; right: 0; color: #fff !important; background: rgba(255,255,255,0.1) !important; }
|
||||
.modal-media-container { background: #000; border-radius: 24px; overflow: hidden; border: 1px solid rgba(255,255,255,0.1); }
|
||||
|
||||
@@ -58,11 +58,11 @@ const formatDate = dt => dt ? new Date(dt).toLocaleString('ru-RU', { day: '2-dig
|
||||
const statusLabel = s => ({ COMPLETED: 'Завершена', PROCESSING: 'В работе', QUEUED: 'В очереди', FAILED: 'Ошибка' })[(s||'').toUpperCase()] || s;
|
||||
|
||||
const statusStyle = s => ({
|
||||
COMPLETED: { color: 'var(--kai-green)', bg: 'rgba(0,212,139,0.1)', border: 'rgba(0,212,139,0.25)' },
|
||||
PROCESSING: { color: 'var(--kai-blue)', bg: 'rgba(45,123,255,0.1)', border: 'rgba(45,123,255,0.25)' },
|
||||
QUEUED: { color: 'var(--kai-yellow)', bg: 'rgba(245,158,11,0.1)', border: 'rgba(245,158,11,0.25)' },
|
||||
FAILED: { color: 'var(--kai-red)', bg: 'rgba(239,68,68,0.1)', border: 'rgba(239,68,68,0.25)' }
|
||||
})[(s||'').toUpperCase()] || { color: 'var(--kai-txt3)', bg: 'rgba(255,255,255,0.05)', border: 'rgba(255,255,255,0.1)' };
|
||||
COMPLETED: { color: '#10B981', bg: 'rgba(16,185,129,0.09)', border: 'rgba(16,185,129,0.22)', accent: '#10B981' },
|
||||
PROCESSING: { color: '#2D7BFF', bg: 'rgba(45,123,255,0.09)', border: 'rgba(45,123,255,0.22)', accent: '#2D7BFF' },
|
||||
QUEUED: { color: '#F59E0B', bg: 'rgba(245,158,11,0.09)', border: 'rgba(245,158,11,0.22)', accent: '#F59E0B' },
|
||||
FAILED: { color: '#EF4444', bg: 'rgba(239,68,68,0.09)', border: 'rgba(239,68,68,0.22)', accent: '#EF4444' }
|
||||
})[(s||'').toUpperCase()] || { color: '#64748B', bg: 'rgba(100,116,139,0.08)', border: 'rgba(100,116,139,0.18)', accent: '#64748B' };
|
||||
|
||||
const statusIcon = s => ({
|
||||
COMPLETED: 'pi pi-check-circle',
|
||||
@@ -71,13 +71,19 @@ const statusIcon = s => ({
|
||||
FAILED: 'pi pi-times-circle'
|
||||
})[(s||'').toUpperCase()] || 'pi pi-circle';
|
||||
|
||||
const getPlatformIcon = p => {
|
||||
const pl = (p||'').toLowerCase();
|
||||
if (pl.includes('insta')) return 'pi pi-instagram';
|
||||
if (pl.includes('tik')) return 'pi pi-tiktok';
|
||||
if (pl.includes('you')) return 'pi pi-youtube';
|
||||
if (pl.includes('tg') || pl.includes('tele')) return 'pi pi-telegram';
|
||||
return 'pi pi-share-alt';
|
||||
const PLAT_META = {
|
||||
instagram: { icon: 'pi pi-instagram', color: '#E1306C', bg: 'rgba(225,48,108,0.1)' },
|
||||
facebook: { icon: 'pi pi-facebook', color: '#1877F2', bg: 'rgba(24,119,242,0.1)' },
|
||||
tiktok: { icon: 'pi pi-tiktok', color: '#69C9D0', bg: 'rgba(105,201,208,0.1)' },
|
||||
youtube: { icon: 'pi pi-youtube', color: '#FF0000', bg: 'rgba(255,0,0,0.1)' },
|
||||
telegram: { icon: 'pi pi-telegram', color: '#2AABEE', bg: 'rgba(42,171,238,0.1)' },
|
||||
};
|
||||
const getPlatMeta = p => {
|
||||
const key = (p || '').toLowerCase();
|
||||
for (const [k, v] of Object.entries(PLAT_META)) {
|
||||
if (key.includes(k)) return v;
|
||||
}
|
||||
return { icon: 'pi pi-share-alt', color: '#64748B', bg: 'rgba(100,116,139,0.1)' };
|
||||
};
|
||||
|
||||
const getModelInfo = modelName => {
|
||||
@@ -93,7 +99,7 @@ const getStrategyName = item =>
|
||||
|
||||
const getLatestMsg = item => {
|
||||
if (item.statusHistory?.length > 0) return item.statusHistory[item.statusHistory.length - 1].message;
|
||||
return item.status === 'QUEUED' ? 'В очереди...' : 'Фабрика генерирует контент...';
|
||||
return item.status === 'QUEUED' ? 'Ожидание в очереди...' : 'Контент-фабрика генерирует материалы...';
|
||||
};
|
||||
|
||||
const openStrategy = item => {
|
||||
@@ -111,74 +117,111 @@ onMounted(load);
|
||||
<div class="sl-page">
|
||||
<Toast />
|
||||
|
||||
<div class="sl-glow sl-glow--green"></div>
|
||||
<div class="sl-glow sl-glow--blue"></div>
|
||||
|
||||
<div class="sl-inner">
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="sl-header">
|
||||
<div class="sl-header__left">
|
||||
<button class="sl-back-btn" @click="$router.push('/marketing-analysis')">
|
||||
<button class="sl-back-btn" @click="$router.push('/marketing-analysis')" title="Назад">
|
||||
<i class="pi pi-arrow-left"></i>
|
||||
</button>
|
||||
<div>
|
||||
<div class="sl-eyebrow">Content Factory · v3</div>
|
||||
<div class="sl-eyebrow">
|
||||
<span class="sl-eyebrow__dot"></span>
|
||||
Контент-Фабрика · v3
|
||||
</div>
|
||||
<h1 class="sl-title">Мои стратегии</h1>
|
||||
</div>
|
||||
</div>
|
||||
<button class="sl-btn sl-btn--ghost" @click="$router.push('/marketing-analysis/v3')">
|
||||
<i class="pi pi-chart-bar"></i> Мои анализы
|
||||
<i class="pi pi-chart-bar"></i>
|
||||
<span>Мои анализы</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- STATS -->
|
||||
<div class="sl-stats">
|
||||
<div class="sl-stat" v-for="f in STATUS_FILTERS.filter(f=>f.value)" :key="f.value">
|
||||
<span class="sl-stat__num" :style="{ color: statusStyle(f.value).color }">{{ countByStatus(f.value) }}</span>
|
||||
<span class="sl-stat__label">{{ f.label }}</span>
|
||||
<div class="sl-stats-row">
|
||||
<div class="sl-stat-card" v-for="f in STATUS_FILTERS.filter(f => f.value)" :key="f.value"
|
||||
:style="{ borderColor: statusStyle(f.value).border }">
|
||||
<div class="sl-stat-card__icon" :style="{ background: statusStyle(f.value).bg }">
|
||||
<i :class="statusIcon(f.value)" :style="{ color: statusStyle(f.value).color }"></i>
|
||||
</div>
|
||||
<div class="sl-stat-card__body">
|
||||
<div class="sl-stat-card__num" :style="{ color: statusStyle(f.value).color }">
|
||||
{{ countByStatus(f.value) }}
|
||||
</div>
|
||||
<div class="sl-stat-card__label">{{ f.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sl-stat">
|
||||
<span class="sl-stat__num" style="color:var(--kai-txt);">{{ strategies.length }}</span>
|
||||
<span class="sl-stat__label">Всего</span>
|
||||
<div class="sl-stat-card sl-stat-card--total">
|
||||
<div class="sl-stat-card__icon" style="background: rgba(16,185,129,0.09);">
|
||||
<i class="pi pi-sparkles" style="color: #10B981;"></i>
|
||||
</div>
|
||||
<div class="sl-stat-card__body">
|
||||
<div class="sl-stat-card__num" style="color: #0F172A;">{{ strategies.length }}</div>
|
||||
<div class="sl-stat-card__label">Всего стратегий</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- FILTERS -->
|
||||
<div class="sl-filters">
|
||||
<!-- TOOLBAR -->
|
||||
<div class="sl-toolbar">
|
||||
<div class="sl-search">
|
||||
<i class="pi pi-search sl-search__icon"></i>
|
||||
<input v-model="searchQuery" type="text" placeholder="Поиск (ниша, ID)..." class="sl-search__input" />
|
||||
<input v-model="searchQuery" type="text" placeholder="Поиск по нише или ID..." class="sl-search__input" />
|
||||
<button v-if="searchQuery" class="sl-search__clear" @click="searchQuery = ''" title="Очистить">
|
||||
<i class="pi pi-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="sl-pills">
|
||||
<button v-for="f in STATUS_FILTERS" :key="f.value"
|
||||
@click="filterStatus = f.value"
|
||||
@click="filterStatus = f.value; currentPage = 1"
|
||||
class="sl-pill" :class="{ 'sl-pill--active': filterStatus === f.value }">
|
||||
{{ f.label }}
|
||||
<span v-if="f.value" class="sl-pill__count">{{ countByStatus(f.value) }}</span>
|
||||
</button>
|
||||
<button class="sl-icon-btn" @click="load" title="Обновить">
|
||||
<i class="pi pi-refresh" :class="{ 'pi-spin': loading }"></i>
|
||||
<span v-if="f.value && countByStatus(f.value) > 0" class="sl-pill__badge">
|
||||
{{ countByStatus(f.value) }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<button class="sl-refresh-btn" @click="load" :title="loading ? 'Загрузка...' : 'Обновить'">
|
||||
<i class="pi pi-refresh" :class="{ 'pi-spin': loading }"></i>
|
||||
<span>Обновить</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- LOADING -->
|
||||
<div v-if="loading" class="sl-skeleton-list">
|
||||
<div v-for="i in 4" :key="i" class="sl-skeleton-item">
|
||||
<div class="sl-skeleton-icon"></div>
|
||||
<div class="sl-skeleton-icon shimmer"></div>
|
||||
<div class="sl-skeleton-lines">
|
||||
<div class="sl-skeleton-line sl-skeleton-line--w40"></div>
|
||||
<div class="sl-skeleton-line sl-skeleton-line--w60"></div>
|
||||
<div class="sl-skeleton-line shimmer" style="width: 40%;"></div>
|
||||
<div class="sl-skeleton-line shimmer" style="width: 65%;"></div>
|
||||
</div>
|
||||
<div class="sl-skeleton-badge shimmer"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- EMPTY -->
|
||||
<div v-else-if="!filtered.length" class="sl-empty">
|
||||
<div class="sl-empty__icon"><i class="pi pi-sparkles"></i></div>
|
||||
<div class="sl-empty__visual">
|
||||
<div class="sl-empty__ring"></div>
|
||||
<div class="sl-empty__icon">
|
||||
<i class="pi pi-sparkles"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="sl-empty__title">{{ filterStatus ? 'Нет стратегий с таким статусом' : 'Стратегий пока нет' }}</h2>
|
||||
<p class="sl-empty__desc">Сгенерируйте первую стратегию ИИ-маркетологом на основе готового анализа.</p>
|
||||
<button class="sl-btn sl-btn--primary" @click="$router.push('/marketing-analysis/v3')">
|
||||
<i class="pi pi-arrow-right"></i> К анализам
|
||||
</button>
|
||||
<div class="sl-empty__actions">
|
||||
<button v-if="filterStatus || searchQuery" class="sl-btn sl-btn--ghost" @click="filterStatus = ''; searchQuery = ''">
|
||||
<i class="pi pi-filter-slash"></i> Сбросить
|
||||
</button>
|
||||
<button class="sl-btn sl-btn--primary" @click="$router.push('/marketing-analysis/v3')">
|
||||
<i class="pi pi-arrow-right"></i> К анализам
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- LIST -->
|
||||
@@ -188,7 +231,10 @@ onMounted(load);
|
||||
:class="{ 'sl-item--clickable': ['COMPLETED','PROCESSING','QUEUED'].includes((item.status||'').toUpperCase()) }"
|
||||
@click="openStrategy(item)">
|
||||
|
||||
<!-- Status -->
|
||||
<!-- Accent stripe -->
|
||||
<div class="sl-item__stripe" :style="{ background: statusStyle(item.status).accent }"></div>
|
||||
|
||||
<!-- Status icon -->
|
||||
<div class="sl-item__status"
|
||||
:style="{ background: statusStyle(item.status).bg, border: '1px solid ' + statusStyle(item.status).border }">
|
||||
<i :class="statusIcon(item.status)" :style="{ color: statusStyle(item.status).color }"></i>
|
||||
@@ -196,11 +242,13 @@ onMounted(load);
|
||||
|
||||
<!-- Content -->
|
||||
<div class="sl-item__content">
|
||||
<div class="sl-item__row1">
|
||||
<div class="sl-item__top">
|
||||
<h3 class="sl-item__name">{{ getStrategyName(item) }}</h3>
|
||||
|
||||
<div class="sl-item__badges">
|
||||
<span class="sl-item__badge"
|
||||
:style="{ color: statusStyle(item.status).color, background: statusStyle(item.status).bg, borderColor: statusStyle(item.status).border }">
|
||||
<i :class="statusIcon(item.status)"></i>
|
||||
{{ statusLabel(item.status) }}
|
||||
</span>
|
||||
<span class="sl-item__model"
|
||||
@@ -210,42 +258,64 @@ onMounted(load);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Meta chips -->
|
||||
<div class="sl-item__meta">
|
||||
<span v-if="item.durationWeeks || item.strategyData?.durationWeeks">
|
||||
<i class="pi pi-calendar"></i> {{ item.durationWeeks || item.strategyData?.durationWeeks }} нед.
|
||||
<span v-if="item.durationWeeks || item.strategyData?.durationWeeks" class="sl-meta-chip">
|
||||
<i class="pi pi-calendar"></i>
|
||||
{{ item.durationWeeks || item.strategyData?.durationWeeks }} нед.
|
||||
</span>
|
||||
<span v-if="(item.priorityPlatforms || item.strategyData?.priorityPlatforms)?.length" class="sl-platforms">
|
||||
<i class="pi pi-sitemap"></i>
|
||||
<span v-for="p in (item.priorityPlatforms || item.strategyData?.priorityPlatforms)" :key="p" class="sl-plat-chip">
|
||||
<i :class="getPlatformIcon(p)"></i> {{ p }}
|
||||
<span class="sl-meta-chip">
|
||||
<i class="pi pi-clock"></i>
|
||||
{{ formatDate(item.createdAt) }}
|
||||
</span>
|
||||
|
||||
<!-- Platforms -->
|
||||
<template v-if="(item.priorityPlatforms || item.strategyData?.priorityPlatforms)?.length">
|
||||
<span
|
||||
v-for="p in (item.priorityPlatforms || item.strategyData?.priorityPlatforms)"
|
||||
:key="p"
|
||||
class="sl-plat-chip"
|
||||
:style="{ background: getPlatMeta(p).bg, color: getPlatMeta(p).color }"
|
||||
>
|
||||
<i :class="getPlatMeta(p).icon"></i>
|
||||
{{ p }}
|
||||
</span>
|
||||
</span>
|
||||
<span><i class="pi pi-clock"></i> {{ formatDate(item.createdAt) }}</span>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Progress -->
|
||||
<div v-if="['PROCESSING','QUEUED'].includes((item.status||'').toUpperCase())" class="sl-item__progress">
|
||||
<span class="sl-item__progress-msg">{{ getLatestMsg(item) }}</span>
|
||||
<div class="sl-item__progress-track">
|
||||
<div class="sl-item__progress-bar" :style="{ width: item.status === 'QUEUED' ? '8%' : '60%' }"></div>
|
||||
<div class="sl-item__progress-fill"
|
||||
:style="{ width: item.status === 'QUEUED' ? '8%' : '60%', background: statusStyle(item.status).accent }">
|
||||
</div>
|
||||
</div>
|
||||
<span class="sl-item__progress-msg">{{ getLatestMsg(item) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action -->
|
||||
<div class="sl-item__action" @click.stop>
|
||||
<div class="sl-item__actions" @click.stop>
|
||||
<button v-if="['COMPLETED','PROCESSING'].includes((item.status||'').toUpperCase())"
|
||||
class="sl-icon-btn sl-icon-btn--view" @click="openStrategy(item)" title="Открыть">
|
||||
class="sl-action-btn sl-action-btn--view" @click="openStrategy(item)" title="Открыть стратегию">
|
||||
<i class="pi pi-eye"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Arrow -->
|
||||
<div v-if="['COMPLETED','PROCESSING','QUEUED'].includes((item.status||'').toUpperCase())" class="sl-item__arrow">
|
||||
<i class="pi pi-angle-right"></i>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
|
||||
<!-- PAGINATION -->
|
||||
<div v-if="filtered.length > pageSize" class="sl-pagination">
|
||||
<span class="sl-pagination__info">Показано {{ (currentPage-1)*pageSize+1 }}–{{ Math.min(currentPage*pageSize, filtered.length) }} из {{ filtered.length }}</span>
|
||||
<Paginator :rows="pageSize" :totalRecords="filtered.length" :first="(currentPage-1)*pageSize" @page="currentPage=$event.page+1" template="PrevPageLink PageLinks NextPageLink" />
|
||||
<span class="sl-pagination__info">
|
||||
Показано {{ (currentPage-1)*pageSize+1 }}–{{ Math.min(currentPage*pageSize, filtered.length) }} из {{ filtered.length }} стратегий
|
||||
</span>
|
||||
<Paginator :rows="pageSize" :totalRecords="filtered.length" :first="(currentPage-1)*pageSize"
|
||||
@page="currentPage=$event.page+1" template="PrevPageLink PageLinks NextPageLink" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -253,78 +323,272 @@ onMounted(load);
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.sl-page { background: var(--kai-bg); min-height: 100vh; font-family: 'Onest', sans-serif; padding: 32px 24px 80px; }
|
||||
.sl-inner { max-width: 1100px; margin: 0 auto; }
|
||||
/* ──────────────────── PAGE ──────────────────── */
|
||||
.sl-page {
|
||||
background: #F5F8FF;
|
||||
background-image:
|
||||
radial-gradient(at 0% 0%, rgba(16,185,129,0.06) 0px, transparent 50%),
|
||||
radial-gradient(at 100% 100%, rgba(45,123,255,0.06) 0px, transparent 50%);
|
||||
min-height: 100vh;
|
||||
font-family: 'Onest', sans-serif;
|
||||
padding: 36px 24px 100px;
|
||||
position: relative;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.sl-glow {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
.sl-glow--green { top: -150px; left: -100px; width: 500px; height: 500px; background: radial-gradient(circle, rgba(16,185,129,0.07) 0%, transparent 70%); }
|
||||
.sl-glow--blue { bottom: -80px; right: -80px; width: 400px; height: 400px; background: radial-gradient(circle, rgba(45,123,255,0.06) 0%, transparent 70%); }
|
||||
.sl-inner { max-width: 1100px; margin: 0 auto; position: relative; z-index: 1; }
|
||||
|
||||
.sl-header { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 28px; flex-wrap: wrap; }
|
||||
/* ──────────────────── HEADER ──────────────────── */
|
||||
.sl-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 32px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.sl-header__left { display: flex; align-items: center; gap: 16px; }
|
||||
.sl-back-btn { width: 36px; height: 36px; border-radius: 10px; border: 1px solid var(--kai-border); background: var(--kai-card); color: var(--kai-txt2); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.2s; }
|
||||
.sl-back-btn:hover { background: var(--kai-card-hover); color: var(--kai-txt); border-color: var(--kai-border-hover); }
|
||||
.sl-eyebrow { font-size: 10px; font-weight: 700; font-family: 'Unbounded', sans-serif; text-transform: uppercase; letter-spacing: 0.08em; color: var(--kai-green); margin-bottom: 4px; }
|
||||
.sl-title { font-family: 'Unbounded', sans-serif; font-size: 24px; font-weight: 900; color: var(--kai-txt); }
|
||||
.sl-btn { display: inline-flex; align-items: center; gap: 7px; padding: 9px 18px; border-radius: 10px; font-size: 13px; font-weight: 700; cursor: pointer; transition: all 0.2s; border: 1px solid; font-family: 'Onest', sans-serif; }
|
||||
.sl-btn--primary { background: var(--kai-green); color: #0A0E1A; border-color: var(--kai-green); }
|
||||
.sl-btn--primary:hover { opacity: 0.9; transform: translateY(-1px); }
|
||||
.sl-btn--ghost { background: var(--kai-card); color: var(--kai-txt2); border-color: var(--kai-border); }
|
||||
.sl-btn--ghost:hover { background: var(--kai-card-hover); color: var(--kai-txt); border-color: var(--kai-border-hover); }
|
||||
.sl-back-btn {
|
||||
width: 40px; height: 40px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(16,185,129,0.22);
|
||||
background: #fff;
|
||||
color: #64748B;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 2px 8px rgba(16,185,129,0.06);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sl-back-btn:hover { background: rgba(16,185,129,0.06); color: #10B981; border-color: rgba(16,185,129,0.4); transform: translateX(-2px); }
|
||||
|
||||
.sl-stats { display: flex; gap: 24px; margin-bottom: 24px; flex-wrap: wrap; }
|
||||
.sl-stat { display: flex; flex-direction: column; gap: 2px; }
|
||||
.sl-stat__num { font-family: 'Unbounded', sans-serif; font-size: 22px; font-weight: 900; line-height: 1; }
|
||||
.sl-stat__label { font-size: 11px; color: var(--kai-txt3); font-weight: 600; text-transform: uppercase; letter-spacing: 0.06em; }
|
||||
.sl-eyebrow {
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
font-size: 10px; font-weight: 800; font-family: 'Unbounded', sans-serif;
|
||||
text-transform: uppercase; letter-spacing: 0.1em; color: #10B981; margin-bottom: 5px;
|
||||
}
|
||||
.sl-eyebrow__dot {
|
||||
width: 6px; height: 6px; border-radius: 50%; background: #10B981;
|
||||
box-shadow: 0 0 8px rgba(16,185,129,0.5); animation: blink 2s infinite;
|
||||
}
|
||||
@keyframes blink { 0%,100%{opacity:1}50%{opacity:0.4} }
|
||||
|
||||
.sl-filters { display: flex; gap: 12px; margin-bottom: 20px; flex-wrap: wrap; align-items: center; }
|
||||
.sl-search { position: relative; flex: 1; min-width: 200px; max-width: 300px; }
|
||||
.sl-search__icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--kai-txt3); font-size: 13px; }
|
||||
.sl-search__input { width: 100%; padding: 9px 12px 9px 36px; border-radius: 10px; background: var(--kai-card); border: 1px solid var(--kai-border); color: var(--kai-txt); font-size: 13px; font-family: 'Onest', sans-serif; outline: none; transition: border-color 0.2s; }
|
||||
.sl-search__input:focus { border-color: var(--kai-border-hover); }
|
||||
.sl-search__input::placeholder { color: var(--kai-txt3); }
|
||||
.sl-pills { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
|
||||
.sl-pill { padding: 6px 14px; border-radius: 999px; font-size: 12px; font-weight: 600; border: 1px solid var(--kai-border); background: var(--kai-card); color: var(--kai-txt2); cursor: pointer; transition: all 0.2s; display: flex; align-items: center; gap: 5px; }
|
||||
.sl-pill:hover { border-color: var(--kai-border-hover); color: var(--kai-txt); }
|
||||
.sl-pill--active { background: rgba(0,212,139,0.1); border-color: rgba(0,212,139,0.4); color: var(--kai-green); }
|
||||
.sl-pill__count { opacity: 0.7; font-size: 10px; }
|
||||
.sl-icon-btn { width: 32px; height: 32px; border-radius: 8px; border: 1px solid var(--kai-border); background: var(--kai-card); color: var(--kai-txt2); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.2s; font-size: 13px; }
|
||||
.sl-icon-btn:hover { background: var(--kai-card-hover); color: var(--kai-txt); border-color: var(--kai-border-hover); }
|
||||
.sl-icon-btn--view:hover { background: rgba(45,123,255,0.1); color: var(--kai-blue); border-color: rgba(45,123,255,0.3); }
|
||||
.sl-title { font-family: 'Unbounded', sans-serif; font-size: 26px; font-weight: 900; color: #0F172A; letter-spacing: -0.02em; }
|
||||
|
||||
.sl-skeleton-list { display: flex; flex-direction: column; gap: 8px; }
|
||||
.sl-skeleton-item { display: flex; gap: 14px; align-items: center; padding: 18px 20px; background: var(--kai-card); border: 1px solid var(--kai-border); border-radius: 14px; }
|
||||
.sl-skeleton-icon { width: 44px; height: 44px; border-radius: 12px; background: rgba(255,255,255,0.06); flex-shrink: 0; }
|
||||
.sl-skeleton-lines { flex: 1; display: flex; flex-direction: column; gap: 8px; }
|
||||
.sl-skeleton-line { height: 12px; border-radius: 6px; background: rgba(255,255,255,0.06); }
|
||||
.sl-skeleton-line--w40 { width: 40%; }
|
||||
.sl-skeleton-line--w60 { width: 65%; }
|
||||
/* ──────────────────── BUTTONS ──────────────────── */
|
||||
.sl-btn {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
padding: 10px 18px; border-radius: 12px;
|
||||
font-size: 13px; font-weight: 700; cursor: pointer;
|
||||
transition: all 0.22s; border: 1px solid; font-family: 'Onest', sans-serif; white-space: nowrap;
|
||||
}
|
||||
.sl-btn--primary { background: #10B981; color: #fff; border-color: #10B981; box-shadow: 0 4px 16px rgba(16,185,129,0.3); }
|
||||
.sl-btn--primary:hover { background: #0EA572; box-shadow: 0 8px 28px rgba(16,185,129,0.4); transform: translateY(-2px); }
|
||||
.sl-btn--ghost { background: #fff; color: #334155; border-color: rgba(16,185,129,0.2); box-shadow: 0 2px 8px rgba(16,185,129,0.05); }
|
||||
.sl-btn--ghost:hover { background: rgba(16,185,129,0.05); color: #0F172A; border-color: rgba(16,185,129,0.35); }
|
||||
|
||||
.sl-empty { text-align: center; padding: 80px 24px; background: var(--kai-card); border: 1px solid var(--kai-border); border-radius: 20px; }
|
||||
.sl-empty__icon { width: 72px; height: 72px; border-radius: 20px; background: rgba(0,212,139,0.1); border: 1px solid rgba(0,212,139,0.2); display: flex; align-items: center; justify-content: center; font-size: 30px; color: var(--kai-green); margin: 0 auto 20px; }
|
||||
.sl-empty__title { font-family: 'Unbounded', sans-serif; font-size: 20px; font-weight: 700; color: var(--kai-txt); margin-bottom: 10px; }
|
||||
.sl-empty__desc { font-size: 14px; color: var(--kai-txt2); max-width: 380px; margin: 0 auto 24px; line-height: 1.6; }
|
||||
/* ──────────────────── STATS ──────────────────── */
|
||||
.sl-stats-row { display: flex; gap: 12px; margin-bottom: 28px; flex-wrap: wrap; }
|
||||
.sl-stat-card {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 14px 18px; background: #fff;
|
||||
border: 1px solid rgba(16,185,129,0.14); border-radius: 14px;
|
||||
flex: 1; min-width: 130px; transition: all 0.22s;
|
||||
box-shadow: 0 2px 8px rgba(16,185,129,0.05);
|
||||
}
|
||||
.sl-stat-card:hover { transform: translateY(-3px); box-shadow: 0 8px 24px rgba(16,185,129,0.1); }
|
||||
.sl-stat-card__icon {
|
||||
width: 36px; height: 36px; border-radius: 10px;
|
||||
display: flex; align-items: center; justify-content: center; font-size: 15px; flex-shrink: 0;
|
||||
}
|
||||
.sl-stat-card__body { display: flex; flex-direction: column; gap: 2px; }
|
||||
.sl-stat-card__num { font-family: 'Unbounded', sans-serif; font-size: 20px; font-weight: 900; line-height: 1; }
|
||||
.sl-stat-card__label { font-size: 10px; color: #94A3B8; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; white-space: nowrap; }
|
||||
|
||||
.sl-list { display: flex; flex-direction: column; gap: 8px; }
|
||||
.sl-item { display: flex; align-items: flex-start; gap: 16px; padding: 18px 20px; border-radius: 14px; background: var(--kai-card); border: 1px solid var(--kai-border); transition: all 0.25s; }
|
||||
/* ──────────────────── TOOLBAR ──────────────────── */
|
||||
.sl-toolbar { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; flex-wrap: wrap; }
|
||||
.sl-search { position: relative; flex: 1; min-width: 200px; max-width: 320px; }
|
||||
.sl-search__icon { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: #94A3B8; font-size: 13px; pointer-events: none; }
|
||||
.sl-search__input {
|
||||
width: 100%; padding: 10px 36px 10px 40px; border-radius: 12px;
|
||||
background: #fff; border: 1px solid rgba(16,185,129,0.18); color: #0F172A;
|
||||
font-size: 13px; font-family: 'Onest', sans-serif; outline: none; transition: all 0.2s;
|
||||
box-shadow: 0 2px 8px rgba(16,185,129,0.04);
|
||||
}
|
||||
.sl-search__input:focus { border-color: rgba(16,185,129,0.45); box-shadow: 0 0 0 3px rgba(16,185,129,0.08); }
|
||||
.sl-search__input::placeholder { color: #94A3B8; }
|
||||
.sl-search__clear {
|
||||
position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
|
||||
width: 22px; height: 22px; border-radius: 50%; border: none;
|
||||
background: rgba(100,116,139,0.12); color: #64748B;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
cursor: pointer; font-size: 10px; transition: all 0.2s;
|
||||
}
|
||||
.sl-search__clear:hover { background: rgba(239,68,68,0.12); color: #EF4444; }
|
||||
|
||||
.sl-pills { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
|
||||
.sl-pill {
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
padding: 7px 14px; border-radius: 999px; font-size: 12px; font-weight: 600;
|
||||
border: 1px solid rgba(16,185,129,0.16); background: #fff; color: #64748B;
|
||||
cursor: pointer; transition: all 0.2s; font-family: 'Onest', sans-serif;
|
||||
}
|
||||
.sl-pill:hover { border-color: rgba(16,185,129,0.35); color: #334155; background: rgba(16,185,129,0.04); }
|
||||
.sl-pill--active { background: #10B981; border-color: #10B981; color: #fff; box-shadow: 0 4px 14px rgba(16,185,129,0.3); }
|
||||
.sl-pill--active:hover { background: #0EA572; }
|
||||
.sl-pill__badge {
|
||||
background: rgba(255,255,255,0.25); border-radius: 999px; padding: 1px 6px;
|
||||
font-size: 9px; font-weight: 800; font-family: 'Unbounded', sans-serif;
|
||||
}
|
||||
|
||||
.sl-refresh-btn {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 9px 14px; border-radius: 10px;
|
||||
border: 1px solid rgba(16,185,129,0.18); background: #fff; color: #64748B;
|
||||
font-size: 12px; font-weight: 600; cursor: pointer; transition: all 0.2s;
|
||||
font-family: 'Onest', sans-serif; white-space: nowrap;
|
||||
}
|
||||
.sl-refresh-btn:hover { background: rgba(16,185,129,0.05); color: #10B981; border-color: rgba(16,185,129,0.3); }
|
||||
|
||||
/* ──────────────────── SKELETON ──────────────────── */
|
||||
.sl-skeleton-list { display: flex; flex-direction: column; gap: 10px; }
|
||||
.sl-skeleton-item {
|
||||
display: flex; gap: 16px; align-items: center;
|
||||
padding: 20px 22px; background: #fff;
|
||||
border: 1px solid rgba(16,185,129,0.1); border-radius: 16px;
|
||||
}
|
||||
.sl-skeleton-icon { width: 46px; height: 46px; border-radius: 13px; flex-shrink: 0; background: rgba(16,185,129,0.07); }
|
||||
.sl-skeleton-lines { flex: 1; display: flex; flex-direction: column; gap: 9px; }
|
||||
.sl-skeleton-line { height: 13px; border-radius: 7px; background: rgba(16,185,129,0.07); }
|
||||
.sl-skeleton-badge { width: 80px; height: 24px; border-radius: 999px; background: rgba(16,185,129,0.07); }
|
||||
.shimmer { position: relative; overflow: hidden; }
|
||||
.shimmer::after {
|
||||
content: ''; position: absolute; inset: 0;
|
||||
background: linear-gradient(90deg, transparent, rgba(16,185,129,0.08), transparent);
|
||||
animation: shimmer 1.6s infinite;
|
||||
}
|
||||
@keyframes shimmer { 0%{transform:translateX(-100%)} 100%{transform:translateX(100%)} }
|
||||
|
||||
/* ──────────────────── EMPTY ──────────────────── */
|
||||
.sl-empty {
|
||||
text-align: center; padding: 80px 24px; background: #fff;
|
||||
border: 1px solid rgba(16,185,129,0.14); border-radius: 24px;
|
||||
box-shadow: 0 4px 24px rgba(16,185,129,0.06);
|
||||
}
|
||||
.sl-empty__visual { position: relative; width: 80px; height: 80px; margin: 0 auto 24px; }
|
||||
.sl-empty__ring {
|
||||
position: absolute; inset: -6px; border-radius: 50%;
|
||||
border: 2px solid rgba(16,185,129,0.15); animation: spin-slow 8s linear infinite;
|
||||
}
|
||||
@keyframes spin-slow { from{transform:rotate(0deg)} to{transform:rotate(360deg)} }
|
||||
.sl-empty__icon {
|
||||
width: 80px; height: 80px; border-radius: 22px;
|
||||
background: rgba(16,185,129,0.09); border: 1px solid rgba(16,185,129,0.2);
|
||||
display: flex; align-items: center; justify-content: center; font-size: 32px; color: #10B981;
|
||||
}
|
||||
.sl-empty__title { font-family: 'Unbounded', sans-serif; font-size: 20px; font-weight: 800; color: #0F172A; margin-bottom: 10px; }
|
||||
.sl-empty__desc { font-size: 13.5px; color: #64748B; max-width: 400px; margin: 0 auto 28px; line-height: 1.65; }
|
||||
.sl-empty__actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
|
||||
|
||||
/* ──────────────────── LIST ──────────────────── */
|
||||
.sl-list { display: flex; flex-direction: column; gap: 10px; }
|
||||
.sl-item {
|
||||
display: flex; align-items: flex-start; gap: 16px;
|
||||
padding: 18px 20px 18px 24px; border-radius: 16px; background: #fff;
|
||||
border: 1px solid rgba(16,185,129,0.12); transition: all 0.25s cubic-bezier(0.4,0,0.2,1);
|
||||
position: relative; overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(16,185,129,0.04);
|
||||
}
|
||||
.sl-item--clickable { cursor: pointer; }
|
||||
.sl-item--clickable:hover { background: var(--kai-card-hover); border-color: var(--kai-border-hover); transform: translateY(-2px); box-shadow: 0 8px 32px rgba(0,0,0,0.3); }
|
||||
.sl-item__status { width: 44px; height: 44px; border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 18px; flex-shrink: 0; }
|
||||
.sl-item__content { flex: 1; min-width: 0; }
|
||||
.sl-item__row1 { display: flex; align-items: flex-start; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
|
||||
.sl-item__name { font-weight: 700; font-size: 14px; color: var(--kai-txt); line-height: 1.3; flex: 1; min-width: 0; }
|
||||
.sl-item__badges { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; flex-shrink: 0; }
|
||||
.sl-item__badge { padding: 2px 10px; border-radius: 999px; font-size: 10px; font-weight: 700; font-family: 'Unbounded', sans-serif; text-transform: uppercase; letter-spacing: 0.05em; border: 1px solid; }
|
||||
.sl-item__model { padding: 2px 10px; border-radius: 999px; font-size: 10px; font-weight: 700; font-family: 'Unbounded', sans-serif; text-transform: uppercase; letter-spacing: 0.05em; border: 1px solid; }
|
||||
.sl-item__meta { display: flex; gap: 14px; flex-wrap: wrap; font-size: 12px; color: var(--kai-txt3); }
|
||||
.sl-item__meta i { margin-right: 3px; font-size: 11px; }
|
||||
.sl-platforms { display: flex; align-items: center; gap: 6px; }
|
||||
.sl-plat-chip { background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.1); padding: 1px 7px; border-radius: 6px; font-size: 10px; color: var(--kai-txt2); }
|
||||
.sl-item__progress { margin-top: 10px; }
|
||||
.sl-item__progress-msg { font-size: 11px; color: var(--kai-txt3); margin-bottom: 6px; display: block; }
|
||||
.sl-item__progress-track { height: 3px; background: rgba(255,255,255,0.06); border-radius: 999px; overflow: hidden; }
|
||||
.sl-item__progress-bar { height: 100%; background: linear-gradient(90deg, var(--kai-green), rgba(0,212,139,0.4)); border-radius: 999px; animation: sl-pulse 2s ease-in-out infinite; }
|
||||
@keyframes sl-pulse { 0%,100%{opacity:1}50%{opacity:0.6} }
|
||||
.sl-item__action { flex-shrink: 0; }
|
||||
.sl-item--clickable:hover {
|
||||
background: #FAFFFD; border-color: rgba(16,185,129,0.3);
|
||||
transform: translateY(-3px); box-shadow: 0 12px 32px rgba(16,185,129,0.1);
|
||||
}
|
||||
|
||||
.sl-pagination { display: flex; align-items: center; justify-content: space-between; padding-top: 16px; margin-top: 8px; flex-wrap: wrap; gap: 12px; }
|
||||
.sl-pagination__info { font-size: 12px; color: var(--kai-txt3); }
|
||||
.sl-item__stripe {
|
||||
position: absolute; left: 0; top: 0; bottom: 0;
|
||||
width: 4px; border-radius: 4px 0 0 4px; opacity: 0.7; transition: opacity 0.2s;
|
||||
}
|
||||
.sl-item--clickable:hover .sl-item__stripe { opacity: 1; }
|
||||
|
||||
.sl-item__status {
|
||||
width: 46px; height: 46px; border-radius: 13px;
|
||||
display: flex; align-items: center; justify-content: center; font-size: 18px; flex-shrink: 0;
|
||||
transition: transform 0.2s; margin-top: 2px;
|
||||
}
|
||||
.sl-item--clickable:hover .sl-item__status { transform: scale(1.08); }
|
||||
|
||||
.sl-item__content { flex: 1; min-width: 0; }
|
||||
.sl-item__top { display: flex; align-items: flex-start; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
|
||||
.sl-item__name { font-weight: 700; font-size: 14.5px; color: #0F172A; line-height: 1.3; flex: 1; min-width: 0; }
|
||||
.sl-item__badges { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; flex-shrink: 0; }
|
||||
.sl-item__badge {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
padding: 3px 10px; border-radius: 999px; font-size: 9.5px; font-weight: 800;
|
||||
font-family: 'Unbounded', sans-serif; text-transform: uppercase; letter-spacing: 0.06em; border: 1px solid;
|
||||
}
|
||||
.sl-item__badge i { font-size: 8px; }
|
||||
.sl-item__model {
|
||||
padding: 3px 10px; border-radius: 999px; font-size: 9.5px; font-weight: 800;
|
||||
font-family: 'Unbounded', sans-serif; text-transform: uppercase; letter-spacing: 0.05em; border: 1px solid;
|
||||
}
|
||||
|
||||
.sl-item__meta { display: flex; gap: 7px; flex-wrap: wrap; align-items: center; }
|
||||
.sl-meta-chip {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
font-size: 11.5px; color: #64748B; font-weight: 500;
|
||||
background: rgba(100,116,139,0.07); border: 1px solid rgba(100,116,139,0.12);
|
||||
padding: 3px 9px; border-radius: 8px;
|
||||
}
|
||||
.sl-meta-chip i { font-size: 10px; color: #94A3B8; }
|
||||
|
||||
.sl-plat-chip {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
padding: 3px 9px; border-radius: 8px;
|
||||
font-size: 11px; font-weight: 600;
|
||||
}
|
||||
.sl-plat-chip i { font-size: 10px; }
|
||||
|
||||
.sl-item__progress { margin-top: 10px; display: flex; align-items: center; gap: 10px; }
|
||||
.sl-item__progress-track {
|
||||
flex: 1; height: 4px; background: rgba(16,185,129,0.1); border-radius: 999px; overflow: hidden;
|
||||
}
|
||||
.sl-item__progress-fill { height: 100%; border-radius: 999px; animation: sl-pulse 2.5s ease-in-out infinite; }
|
||||
@keyframes sl-pulse { 0%,100%{opacity:1}50%{opacity:0.55} }
|
||||
.sl-item__progress-msg { font-size: 10.5px; color: #94A3B8; font-weight: 600; white-space: nowrap; flex-shrink: 0; }
|
||||
|
||||
.sl-item__actions { flex-shrink: 0; display: flex; gap: 6px; align-items: center; }
|
||||
.sl-action-btn {
|
||||
width: 34px; height: 34px; border-radius: 10px;
|
||||
border: 1px solid rgba(16,185,129,0.16); background: rgba(16,185,129,0.05);
|
||||
color: #64748B; display: flex; align-items: center; justify-content: center;
|
||||
cursor: pointer; transition: all 0.2s; font-size: 13px;
|
||||
}
|
||||
.sl-action-btn--view:hover { background: rgba(45,123,255,0.1); color: #2D7BFF; border-color: rgba(45,123,255,0.3); transform: scale(1.08); }
|
||||
|
||||
.sl-item__arrow {
|
||||
width: 28px; height: 28px; border-radius: 8px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: #CBD5E1; font-size: 14px; transition: all 0.2s; flex-shrink: 0;
|
||||
}
|
||||
.sl-item--clickable:hover .sl-item__arrow { color: #10B981; transform: translateX(3px); }
|
||||
|
||||
/* ──────────────────── PAGINATION ──────────────────── */
|
||||
.sl-pagination {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding-top: 20px; margin-top: 8px; flex-wrap: wrap; gap: 12px;
|
||||
border-top: 1px solid rgba(16,185,129,0.1);
|
||||
}
|
||||
.sl-pagination__info { font-size: 12px; color: #94A3B8; font-weight: 500; }
|
||||
|
||||
/* ──────────────────── TRANSITIONS ──────────────────── */
|
||||
.sl-list-enter-active, .sl-list-leave-active { transition: all 0.25s ease; }
|
||||
.sl-list-enter-from, .sl-list-leave-to { opacity: 0; transform: translateY(-6px); }
|
||||
.sl-list-enter-from, .sl-list-leave-to { opacity: 0; transform: translateY(-8px); }
|
||||
</style>
|
||||
|
||||
@@ -327,7 +327,7 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mkt-hub app-dark">
|
||||
<div class="td-campaign-page">
|
||||
<Toast />
|
||||
|
||||
<!-- AMBIENT GLOW -->
|
||||
@@ -465,24 +465,24 @@ onMounted(() => {
|
||||
</TabPanel>
|
||||
<TabPanel header="Аудитория" value="3">
|
||||
<div class="grid gap-6 md:grid-cols-2 p-4">
|
||||
<div class="mkt-card" style="background: rgba(255,255,255,0.02);">
|
||||
<h3 class="wiz-label" style="opacity:0.7"><i class="pi pi-users"></i> Профиль аудитории</h3>
|
||||
<div class="mkt-card" style="background: rgba(45,123,255,0.02);">
|
||||
<h3 class="wiz-label" style="opacity:0.8"><i class="pi pi-users"></i> Профиль аудитории</h3>
|
||||
<div class="space-y-3 pt-4 text-sm" style="color: var(--kai-txt2);">
|
||||
<div><span class="text-white opacity-40 uppercase text-[10px] block mb-1">Сегмент</span> {{ audienceProfile?.primarySegment || '—' }}</div>
|
||||
<div><span class="info-label">Сегмент</span> {{ audienceProfile?.primarySegment || '—' }}</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div><span class="text-white opacity-40 uppercase text-[10px] block mb-1">Возраст</span> {{ audienceProfile?.ageRange || '—' }}</div>
|
||||
<div><span class="text-white opacity-40 uppercase text-[10px] block mb-1">Гендер</span> {{ audienceProfile?.gender || '—' }}</div>
|
||||
<div><span class="info-label">Возраст</span> {{ audienceProfile?.ageRange || '—' }}</div>
|
||||
<div><span class="info-label">Гендер</span> {{ audienceProfile?.gender || '—' }}</div>
|
||||
</div>
|
||||
<div><span class="text-white opacity-40 uppercase text-[10px] block mb-1">География</span> {{ audienceProfile?.geography ? formatGeo(audienceProfile.geography) : '—' }}</div>
|
||||
<div><span class="text-white opacity-40 uppercase text-[10px] block mb-1">Интересы</span> {{ formatArrayField(audienceProfile?.interests ?? null) }}</div>
|
||||
<div><span class="info-label">География</span> {{ audienceProfile?.geography ? formatGeo(audienceProfile.geography) : '—' }}</div>
|
||||
<div><span class="info-label">Интересы</span> {{ formatArrayField(audienceProfile?.interests ?? null) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mkt-card" style="background: rgba(255,255,255,0.02);">
|
||||
<h3 class="wiz-label" style="opacity:0.7"><i class="pi pi-th-large"></i> Плейсменты и форматы</h3>
|
||||
<div class="mkt-card" style="background: rgba(45,123,255,0.02);">
|
||||
<h3 class="wiz-label" style="opacity:0.8"><i class="pi pi-th-large"></i> Плейсменты и форматы</h3>
|
||||
<div class="space-y-3 pt-4 text-sm" style="color: var(--kai-txt2);">
|
||||
<div><span class="text-white opacity-40 uppercase text-[10px] block mb-1">Рекумендованные площадки</span> {{ formatArrayField(recommendation?.recommendedPlacements ?? null) }}</div>
|
||||
<div><span class="text-white opacity-40 uppercase text-[10px] block mb-1">Форматы</span> {{ formatArrayField(recommendation?.recommendedAdFormats ?? null) }}</div>
|
||||
<div><span class="text-white opacity-40 uppercase text-[10px] block mb-1">Тип таргетинга</span> {{ targetingTypeLabel(recommendation?.recommendedType ?? null) }}</div>
|
||||
<div><span class="info-label">Рекомендованные площадки</span> {{ formatArrayField(recommendation?.recommendedPlacements ?? null) }}</div>
|
||||
<div><span class="info-label">Форматы</span> {{ formatArrayField(recommendation?.recommendedAdFormats ?? null) }}</div>
|
||||
<div><span class="info-label">Тип таргетинга</span> {{ targetingTypeLabel(recommendation?.recommendedType ?? null) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -499,71 +499,70 @@ onMounted(() => {
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:root {
|
||||
--kai-bg: #0A0E1A;
|
||||
--kai-card: rgba(255,255,255,0.04);
|
||||
--kai-card-hover: rgba(255,255,255,0.07);
|
||||
--kai-border: rgba(45,123,255,0.15);
|
||||
--kai-border-hover: rgba(45,123,255,0.4);
|
||||
--kai-blue: #2D7BFF;
|
||||
--kai-green: #00D48B;
|
||||
--kai-txt: rgba(255,255,255,0.92);
|
||||
--kai-txt2: rgba(255,255,255,0.60);
|
||||
--kai-txt3: rgba(255,255,255,0.35);
|
||||
}
|
||||
|
||||
.mkt-hub {
|
||||
background: #0A0E1A;
|
||||
.td-campaign-page {
|
||||
background: #F5F8FF;
|
||||
min-height: 100vh;
|
||||
font-family: 'Onest', sans-serif;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 48px 24px 80px;
|
||||
color: var(--kai-txt);
|
||||
color: #0F172A;
|
||||
}
|
||||
|
||||
.mkt-hub__glow1 { position: absolute; top: -120px; right: -100px; width: 600px; height: 600px; background: radial-gradient(circle, rgba(45,123,255,0.08) 0%, transparent 70%); pointer-events: none; }
|
||||
.mkt-hub__glow2 { position: absolute; bottom: -80px; left: -60px; width: 400px; height: 400px; background: radial-gradient(circle, rgba(0,212,139,0.06) 0%, transparent 70%); pointer-events: none; }
|
||||
.info-label {
|
||||
font-size: 9.5px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: #64748B;
|
||||
display: block;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.mkt-hub__glow1 { position: absolute; top: -120px; right: -100px; width: 600px; height: 600px; background: radial-gradient(circle, rgba(45,123,255,0.06) 0%, transparent 70%); pointer-events: none; }
|
||||
.mkt-hub__glow2 { position: absolute; bottom: -80px; left: -60px; width: 400px; height: 400px; background: radial-gradient(circle, rgba(0,212,139,0.04) 0%, transparent 70%); pointer-events: none; }
|
||||
|
||||
|
||||
.mkt-hub__inner { max-width: 1200px; margin: 0 auto; position: relative; z-index: 1; }
|
||||
|
||||
.mkt-hub__badge { display: inline-flex; align-items: center; gap: 8px; padding: 5px 14px; border-radius: 999px; border: 1px solid rgba(45,123,255,0.3); background: rgba(45,123,255,0.08); font-size: 11px; font-weight: 700; font-family: 'Unbounded', sans-serif; text-transform: uppercase; letter-spacing: 0.07em; color: var(--kai-blue); margin-bottom: 20px; }
|
||||
.mkt-hub__badge-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--kai-blue); box-shadow: 0 0 8px var(--kai-blue); animation: pulse-dot 2s infinite; }
|
||||
.mkt-hub__badge { display: inline-flex; align-items: center; gap: 8px; padding: 5px 14px; border-radius: 999px; border: 1px solid rgba(45,123,255,0.2); background: rgba(45,123,255,0.08); font-size: 11px; font-weight: 700; font-family: 'Unbounded', sans-serif; text-transform: uppercase; letter-spacing: 0.07em; color: #2D7BFF; margin-bottom: 20px; }
|
||||
.mkt-hub__badge-dot { width: 6px; height: 6px; border-radius: 50%; background: #2D7BFF; box-shadow: 0 0 8px #2D7BFF; animation: pulse-dot 2s infinite; }
|
||||
@keyframes pulse-dot { 0%,100% { opacity:1; transform: scale(1); } 50% { opacity:0.5; transform: scale(0.8); } }
|
||||
|
||||
.mkt-hub__title { font-family: 'Unbounded', sans-serif; font-size: clamp(24px, 4vw, 36px); font-weight: 900; color: var(--kai-txt); letter-spacing: -0.02em; line-height: 1.1; }
|
||||
.mkt-hub__title { font-family: 'Unbounded', sans-serif; font-size: clamp(24px, 4vw, 36px); font-weight: 900; color: #0F172A; letter-spacing: -0.02em; line-height: 1.1; }
|
||||
|
||||
.mkt-card { background: var(--kai-card); border: 1px solid var(--kai-border); border-radius: 20px; padding: 24px; transition: all 0.3s; position: relative; overflow: hidden; }
|
||||
.mkt-card::before { content: ''; position: absolute; inset: 0; opacity: 0; transition: opacity 0.3s; background: radial-gradient(circle at top right, rgba(45,123,255,0.1), transparent 60%); pointer-events: none; }
|
||||
.mkt-card:hover { transform: translateY(-2px); border-color: var(--kai-border-hover); background: var(--kai-card-hover); }
|
||||
.mkt-card { background: #FFFFFF; border: 1px solid rgba(45,123,255,0.18); border-radius: 20px; padding: 24px; transition: all 0.3s; position: relative; overflow: hidden; box-shadow: 0 2px 12px rgba(45,123,255,0.06); }
|
||||
.mkt-card::before { content: ''; position: absolute; inset: 0; opacity: 0; transition: opacity 0.3s; background: radial-gradient(circle at top right, rgba(45,123,255,0.06), transparent 60%); pointer-events: none; }
|
||||
.mkt-card:hover { transform: translateY(-2px); border-color: rgba(45,123,255,0.35); background: #F8FBFF; box-shadow: 0 8px 24px rgba(45,123,255,0.1); }
|
||||
.mkt-card:hover::before { opacity: 1; }
|
||||
|
||||
.td-back-mini { width: 36px; height: 36px; border-radius: 10px; border: 1px solid var(--kai-border); background: rgba(255,255,255,0.05); color: var(--kai-txt2); cursor: pointer; transition: all 0.2s; }
|
||||
.td-back-mini:hover { color: #fff; background: var(--kai-blue); border-color: var(--kai-blue); }
|
||||
.td-back-mini { width: 36px; height: 36px; border-radius: 10px; border: 1px solid rgba(45,123,255,0.18); background: rgba(45,123,255,0.05); color: #334155; cursor: pointer; transition: all 0.2s; display:flex; align-items:center; justify-content:center; }
|
||||
.td-back-mini:hover { color: #fff; background: #2D7BFF; border-color: #2D7BFF; }
|
||||
|
||||
.td-campaign__status { display: inline-flex; align-items: center; gap: 8px; padding: 6px 16px; border-radius: 999px; border: 1px solid; font-size: 11px; font-weight: 700; font-family: 'Unbounded', sans-serif; text-transform: uppercase; letter-spacing: 0.05em; }
|
||||
|
||||
.td-kpi-card { padding: 20px; text-align: center; }
|
||||
.td-kpi-label { font-size: 10px; font-weight: 700; color: var(--kai-txt3); text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 8px; }
|
||||
.td-kpi-val { font-family: 'Unbounded', sans-serif; font-size: 20px; font-weight: 900; color: #fff; }
|
||||
.td-kpi-label { font-size: 10px; font-weight: 700; color: #64748B; text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 8px; }
|
||||
.td-kpi-val { font-family: 'Unbounded', sans-serif; font-size: 20px; font-weight: 900; color: #0F172A; }
|
||||
|
||||
.wiz-pipe-node { flex: 1; padding: 12px; border-radius: 14px; border: 1px solid var(--kai-border); background: rgba(255,255,255,0.02); text-align: center; filter: grayscale(1); opacity: 0.4; transition: all 0.5s; }
|
||||
.wiz-pipe-node.wiz-pipe--done { filter: grayscale(0); opacity: 1; border-color: var(--kai-green); background: rgba(0,212,139,0.05); color: var(--kai-green); }
|
||||
.wiz-pipe-node.wiz-pipe--active { filter: grayscale(0); opacity: 1; border-color: var(--kai-blue); background: rgba(45,123,255,0.1); color: var(--kai-blue); box-shadow: 0 0 15px rgba(45,123,255,0.2); }
|
||||
.wiz-pipe-node { flex: 1; padding: 12px; border-radius: 14px; border: 1px solid rgba(45,123,255,0.18); background: rgba(45,123,255,0.03); text-align: center; filter: grayscale(1); opacity: 0.5; transition: all 0.5s; color: #64748B; }
|
||||
.wiz-pipe-node.wiz-pipe--done { filter: grayscale(0); opacity: 1; border-color: #10B981; background: rgba(16,185,129,0.08); color: #10B981; }
|
||||
.wiz-pipe-node.wiz-pipe--active { filter: grayscale(0); opacity: 1; border-color: #2D7BFF; background: rgba(45,123,255,0.08); color: #2D7BFF; box-shadow: 0 0 15px rgba(45,123,255,0.15); }
|
||||
.wiz-pipe-icon { font-size: 18px; margin-bottom: 4px; }
|
||||
.wiz-pipe-label { font-size: 10px; font-weight: 700; text-transform: uppercase; }
|
||||
|
||||
.wiz-label { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 700; font-family: 'Unbounded', sans-serif; text-transform: uppercase; letter-spacing: 0.1em; color: var(--kai-txt3); }
|
||||
.wiz-label { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 700; font-family: 'Unbounded', sans-serif; text-transform: uppercase; letter-spacing: 0.1em; color: #334155; }
|
||||
|
||||
.wiz-btn-sec { background: transparent; border: 1px solid var(--kai-border); color: var(--kai-txt2); padding: 12px 24px; border-radius: 12px; font-weight: 600; cursor: pointer; transition: all 0.2s; }
|
||||
.wiz-btn-sec:hover { background: var(--kai-card-hover); color: #fff; }
|
||||
.wiz-btn-sec { background: transparent; border: 1px solid rgba(45,123,255,0.18); color: #334155; padding: 12px 24px; border-radius: 12px; font-weight: 600; cursor: pointer; transition: all 0.2s; }
|
||||
.wiz-btn-sec:hover { background: rgba(45,123,255,0.06); color: #0F172A; }
|
||||
|
||||
.mkt-banner__cta { display: inline-flex; align-items: center; gap: 8px; padding: 12px 28px; border-radius: 12px; background: var(--kai-blue); color: #fff; font-size: 13px; font-weight: 700; font-family: 'Unbounded', sans-serif; transition: all 0.3s; cursor: pointer; }
|
||||
.mkt-banner__cta { display: inline-flex; align-items: center; gap: 8px; padding: 12px 28px; border-radius: 12px; background: #2D7BFF; color: #fff; font-size: 13px; font-weight: 700; font-family: 'Unbounded', sans-serif; transition: all 0.3s; cursor: pointer; }
|
||||
.mkt-banner__cta:hover { background: #1a6bff; box-shadow: 0 0 24px rgba(45,123,255,0.4); transform: translateY(-2px); }
|
||||
|
||||
:deep(.p-tabview-nav) { background: transparent !important; border: none !important; margin-bottom: 20px; }
|
||||
:deep(.p-tabview-nav li .p-tabview-nav-link) { background: transparent !important; color: var(--kai-txt3) !important; border-bottom: 2px solid transparent; font-family: 'Unbounded', sans-serif; font-size: 12px; font-weight: 700; box-shadow: none !important; }
|
||||
:deep(.p-tabview-nav li.p-highlight .p-tabview-nav-link) { color: var(--kai-blue) !important; border-color: var(--kai-blue) !important; }
|
||||
:deep(.p-tabview-nav li .p-tabview-nav-link) { background: transparent !important; color: #64748B !important; border-bottom: 2px solid transparent; font-family: 'Unbounded', sans-serif; font-size: 12px; font-weight: 700; box-shadow: none !important; }
|
||||
:deep(.p-tabview-nav li.p-highlight .p-tabview-nav-link) { color: #2D7BFF !important; border-color: #2D7BFF !important; }
|
||||
:deep(.p-tabview-panels) { background: transparent !important; padding: 0 !important; }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { useTargeting } from './composables/useTargeting';
|
||||
import TargetingService from '@/service/TargetingService';
|
||||
import MarketingV3Service from '@/service/MarketingV3Service';
|
||||
import TargetingService from '@/service/TargetingService';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useTargeting } from './composables/useTargeting';
|
||||
|
||||
import Button from 'primevue/button';
|
||||
import InputNumber from 'primevue/inputnumber';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import Checkbox from 'primevue/checkbox';
|
||||
import InputSwitch from 'primevue/inputswitch';
|
||||
import Dropdown from 'primevue/dropdown';
|
||||
import InputNumber from 'primevue/inputnumber';
|
||||
import InputSwitch from 'primevue/inputswitch';
|
||||
import Toast from 'primevue/toast';
|
||||
|
||||
const router = useRouter();
|
||||
@@ -262,10 +259,10 @@ const togglePlat = (val) => {
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
<div class="mkt-card" style="padding: 24px; background: rgba(45,123,255,0.05); border-color: rgba(45,123,255,0.2);">
|
||||
<div class="mkt-card" style="padding: 24px; background: rgba(45,123,255,0.08); border-color: rgba(45,123,255,0.18);">
|
||||
<div class="flex gap-4">
|
||||
<i class="pi pi-info-circle" style="color: var(--kai-blue); font-size: 20px;"></i>
|
||||
<p class="mkt-card__desc" style="color: var(--kai-txt);">ИИ автоматически проанализирует рынок и сегменты из стратегии для настройки точного таргетинга.</p>
|
||||
<p class="mkt-card__desc" style="color: #334155;">ИИ автоматически проанализирует рынок и сегменты из стратегии для настройки точного таргетинга.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -385,27 +382,27 @@ const togglePlat = (val) => {
|
||||
|
||||
<style scoped>
|
||||
:root {
|
||||
--kai-bg: #0A0E1A;
|
||||
--kai-card: rgba(255,255,255,0.04);
|
||||
--kai-card-hover: rgba(255,255,255,0.07);
|
||||
--kai-border: rgba(45,123,255,0.15);
|
||||
--kai-border-hover: rgba(45,123,255,0.4);
|
||||
--kai-bg: #F5F8FF;
|
||||
--kai-card: #FFFFFF;
|
||||
--kai-card-hover: #F8FBFF;
|
||||
--kai-border: rgba(45,123,255,0.18);
|
||||
--kai-border-hover: rgba(45,123,255,0.35);
|
||||
--kai-blue: #2D7BFF;
|
||||
--kai-green: #00D48B;
|
||||
--kai-purple: #A855F7;
|
||||
--kai-txt: rgba(255,255,255,0.92);
|
||||
--kai-txt2: rgba(255,255,255,0.60);
|
||||
--kai-txt3: rgba(255,255,255,0.35);
|
||||
--kai-txt: #0F172A;
|
||||
--kai-txt2: #334155;
|
||||
--kai-txt3: #64748B;
|
||||
}
|
||||
|
||||
.mkt-hub {
|
||||
background: #0A0E1A;
|
||||
background: #F5F8FF;
|
||||
min-height: 100vh;
|
||||
font-family: 'Onest', sans-serif;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 48px 24px 80px;
|
||||
color: var(--kai-txt);
|
||||
color: #0F172A;
|
||||
}
|
||||
|
||||
.mkt-hub__glow1 {
|
||||
@@ -440,7 +437,7 @@ const togglePlat = (val) => {
|
||||
gap: 8px;
|
||||
padding: 5px 14px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(45,123,255,0.3);
|
||||
border: 1px solid rgba(45,123,255,0.18);
|
||||
background: rgba(45,123,255,0.08);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
@@ -460,73 +457,74 @@ const togglePlat = (val) => {
|
||||
}
|
||||
@keyframes pulse-dot { 0%,100% { opacity:1; transform: scale(1); } 50% { opacity:0.5; transform: scale(0.8); } }
|
||||
|
||||
.mkt-hub__title { font-family: 'Unbounded', sans-serif; font-size: clamp(24px, 4vw, 42px); font-weight: 900; color: var(--kai-txt); letter-spacing: -0.02em; line-height: 1.1; }
|
||||
.mkt-hub__sub { font-size: 15px; color: var(--kai-txt2); line-height: 1.6; font-weight: 400; }
|
||||
.mkt-hub__title { font-family: 'Unbounded', sans-serif; font-size: clamp(24px, 4vw, 42px); font-weight: 900; color: #0F172A; letter-spacing: -0.02em; line-height: 1.1; }
|
||||
.mkt-hub__sub { font-size: 15px; color: #334155; line-height: 1.6; font-weight: 400; }
|
||||
|
||||
.mkt-card {
|
||||
background: var(--kai-card);
|
||||
border: 1px solid var(--kai-border);
|
||||
background: #FFFFFF;
|
||||
border: 1px solid rgba(45,123,255,0.18);
|
||||
border-radius: 20px;
|
||||
padding: 24px;
|
||||
transition: all 0.3s cubic-bezier(0.4,0,0.2,1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(45,123,255,0.05);
|
||||
}
|
||||
.mkt-card::before { content: ''; position: absolute; inset: 0; opacity: 0; transition: opacity 0.3s; background: radial-gradient(circle at top right, rgba(45,123,255,0.1), transparent 60%); pointer-events: none; }
|
||||
.mkt-card:hover { transform: translateY(-4px); border-color: var(--kai-border-hover); background: var(--kai-card-hover); }
|
||||
.mkt-card:hover { transform: translateY(-4px); border-color: rgba(45,123,255,0.35); background: #F8FBFF; box-shadow: 0 16px 36px rgba(15,23,42,0.1), 0 0 28px rgba(45,123,255,0.12); }
|
||||
.mkt-card:hover::before { opacity: 1; }
|
||||
|
||||
.wiz-step-pill { width: 40px; height: 4px; border-radius: 2px; background: var(--kai-border); transition: all 0.4s; }
|
||||
.wiz-step-pill { width: 40px; height: 4px; border-radius: 2px; background: rgba(45,123,255,0.18); transition: all 0.4s; }
|
||||
.wiz-step-pill--active { background: var(--kai-blue); box-shadow: 0 0 10px var(--kai-blue); width: 60px; }
|
||||
|
||||
.wiz-container { width: 100%; animation: wiz-fade-in 0.6s ease-out; }
|
||||
@keyframes wiz-fade-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
|
||||
|
||||
.wiz-card { padding: 32px; }
|
||||
.wiz-label { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 700; font-family: 'Unbounded', sans-serif; text-transform: uppercase; letter-spacing: 0.1em; color: var(--kai-txt3); margin-bottom: 12px; }
|
||||
.wiz-label { display: flex; align-items: center; gap: 8px; font-size: 11px; font-weight: 700; font-family: 'Unbounded', sans-serif; text-transform: uppercase; letter-spacing: 0.1em; color: #64748B; margin-bottom: 12px; }
|
||||
|
||||
.wiz-dropdown :deep(.p-dropdown) { background: rgba(255,255,255,0.03); border: 1px solid var(--kai-border); border-radius: 12px; padding: 6px 12px; width: 100%; }
|
||||
.wiz-dropdown :deep(.p-dropdown-label) { font-family: 'Onest', sans-serif; font-weight: 600; color: var(--kai-txt); }
|
||||
.wiz-dropdown :deep(.p-dropdown) { background: #FFFFFF; border: 1px solid rgba(45,123,255,0.18); border-radius: 12px; padding: 6px 12px; width: 100%; }
|
||||
.wiz-dropdown :deep(.p-dropdown-label) { font-family: 'Onest', sans-serif; font-weight: 600; color: #0F172A; }
|
||||
|
||||
.wiz-icon-mini { width: 32px; height: 32px; border-radius: 8px; background: rgba(45,123,255,0.15); color: var(--kai-blue); display: flex; align-items: center; justify-content: center; }
|
||||
|
||||
.wiz-input { width: 100%; background: rgba(255,255,255,0.03); border: 1px solid var(--kai-border); border-radius: 12px; padding: 14px 20px; color: #fff; font-family: 'Onest', sans-serif; font-size: 18px; font-weight: 600; outline: none; transition: all 0.3s; }
|
||||
.wiz-input:focus { border-color: var(--kai-blue); background: rgba(255,255,255,0.06); box-shadow: 0 0 20px rgba(45,123,255,0.1); }
|
||||
.wiz-input { width: 100%; background: #FFFFFF; border: 1px solid rgba(45,123,255,0.18); border-radius: 12px; padding: 14px 20px; color: #0F172A; font-family: 'Onest', sans-serif; font-size: 18px; font-weight: 600; outline: none; transition: all 0.3s; }
|
||||
.wiz-input:focus { border-color: var(--kai-blue); background: #F8FBFF; box-shadow: 0 0 20px rgba(45,123,255,0.1); }
|
||||
|
||||
.wiz-input-num :deep(.p-inputtext) { background: transparent; border: none; font-family: 'Unbounded', sans-serif; font-size: 22px; font-weight: 900; color: #fff; padding: 0; }
|
||||
.wiz-input-num :deep(.p-inputtext) { background: transparent; border: none; font-family: 'Unbounded', sans-serif; font-size: 22px; font-weight: 900; color: #0F172A; padding: 0; }
|
||||
|
||||
.wiz-platform { flex: 1; padding: 24px; background: rgba(255,255,255,0.03); border: 1px solid var(--kai-border); border-radius: 20px; display: flex; flex-direction: column; align-items: center; gap: 12px; cursor: pointer; transition: all 0.3s; position: relative; }
|
||||
.wiz-platform:hover { border-color: var(--kai-border-hover); transform: translateY(-2px); }
|
||||
.wiz-platform--active { background: rgba(45,123,255,0.1); border-color: var(--kai-blue); box-shadow: 0 0 20px rgba(45,123,255,0.15); }
|
||||
.wiz-platform { flex: 1; padding: 24px; background: #FFFFFF; border: 1px solid rgba(45,123,255,0.18); border-radius: 20px; display: flex; flex-direction: column; align-items: center; gap: 12px; cursor: pointer; transition: all 0.3s; position: relative; box-shadow: 0 2px 8px rgba(45,123,255,0.05); }
|
||||
.wiz-platform:hover { border-color: rgba(45,123,255,0.35); transform: translateY(-2px); box-shadow: 0 8px 20px rgba(45,123,255,0.1); }
|
||||
.wiz-platform--active { background: rgba(45,123,255,0.08); border-color: var(--kai-blue); box-shadow: 0 0 20px rgba(45,123,255,0.15); }
|
||||
.wiz-platform i { font-size: 32px; }
|
||||
.wiz-check { position: absolute; top: 16px; right: 16px; width: 24px; height: 24px; border-radius: 50%; border: 2px solid var(--kai-border); display: flex; align-items: center; justify-content: center; }
|
||||
.wiz-check { position: absolute; top: 16px; right: 16px; width: 24px; height: 24px; border-radius: 50%; border: 2px solid rgba(45,123,255,0.18); display: flex; align-items: center; justify-content: center; }
|
||||
.wiz-platform--active .wiz-check { background: var(--kai-blue); border-color: var(--kai-blue); color: #fff; }
|
||||
|
||||
.wiz-toggle-card { display: flex; align-items: center; gap: 20px; padding: 24px; cursor: pointer; }
|
||||
.wiz-toggle-card--active { border-color: var(--kai-blue); background: rgba(45,123,255,0.06); }
|
||||
.wiz-toggle-card { display: flex; align-items: center; gap: 20px; padding: 24px; cursor: pointer; background: #FFFFFF; border: 1px solid rgba(45,123,255,0.18); box-shadow: 0 2px 8px rgba(45,123,255,0.05); }
|
||||
.wiz-toggle-card--active { border-color: var(--kai-blue); background: rgba(45,123,255,0.06); box-shadow: 0 8px 20px rgba(45,123,255,0.1); }
|
||||
.wiz-icon-main { width: 56px; height: 56px; border-radius: 16px; display: flex; align-items: center; justify-content: center; font-size: 24px; }
|
||||
.wiz-toggle-title { font-family: 'Unbounded', sans-serif; font-size: 16px; font-weight: 700; color: var(--kai-txt); margin-bottom: 4px; }
|
||||
.wiz-toggle-sub { font-size: 13px; color: var(--kai-txt3); }
|
||||
.wiz-toggle-title { font-family: 'Unbounded', sans-serif; font-size: 16px; font-weight: 700; color: #0F172A; margin-bottom: 4px; }
|
||||
.wiz-toggle-sub { font-size: 13px; color: #334155; }
|
||||
|
||||
.wiz-footer { margin-top: 60px; padding-top: 40px; border-top: 1px solid var(--kai-border); display: flex; justify-content: space-between; align-items: center; }
|
||||
.wiz-footer { margin-top: 60px; padding-top: 40px; border-top: 1px solid rgba(45,123,255,0.18); display: flex; justify-content: space-between; align-items: center; }
|
||||
|
||||
.mkt-banner__cta { display: flex; align-items: center; gap: 8px; padding: 14px 32px; border-radius: 14px; background: var(--kai-blue); color: #fff; font-size: 14px; font-weight: 700; font-family: 'Unbounded', sans-serif; transition: all 0.3s; }
|
||||
.mkt-banner__cta:hover { background: #1a6bff; box-shadow: 0 0 24px rgba(45,123,255,0.4); transform: translateY(-2px); }
|
||||
|
||||
.wiz-btn-sec { background: transparent; border: 1px solid var(--kai-border); color: var(--kai-txt2); padding: 12px 28px; border-radius: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; }
|
||||
.wiz-btn-sec:hover:not(:disabled) { background: var(--kai-card-hover); color: #fff; }
|
||||
.wiz-btn-sec { background: transparent; border: 1px solid rgba(45,123,255,0.18); color: #334155; padding: 12px 28px; border-radius: 14px; font-weight: 600; cursor: pointer; transition: all 0.2s; }
|
||||
.wiz-btn-sec:hover:not(:disabled) { background: #F8FBFF; color: #0F172A; }
|
||||
|
||||
.wiz-loading-view { flex: 1; display: flex; align-items: center; justify-content: center; padding: 40px 0; }
|
||||
.wiz-loading-card { background: var(--kai-card); border: 1px solid var(--kai-border); border-radius: 40px; padding: 60px; width: 100%; max-width: 540px; position: relative; overflow: hidden; }
|
||||
.wiz-loading-card { background: #FFFFFF; border: 1px solid rgba(45,123,255,0.18); border-radius: 40px; padding: 60px; width: 100%; max-width: 540px; position: relative; overflow: hidden; box-shadow: 0 8px 32px rgba(45,123,255,0.1); }
|
||||
.wiz-loading-orb { position: absolute; top: -50px; right: -50px; width: 250px; height: 250px; background: radial-gradient(circle, rgba(45,123,255,0.15) 0%, transparent 70%); filter: blur(40px); }
|
||||
.wiz-loading-icon { font-size: 48px; color: var(--kai-blue); margin-bottom: 24px; filter: drop-shadow(0 0 15px var(--kai-blue)); }
|
||||
.wiz-loading-title { font-family: 'Unbounded', sans-serif; font-size: 28px; font-weight: 900; text-transform: uppercase; letter-spacing: 0.1em; color: #fff; }
|
||||
.wiz-loading-title { font-family: 'Unbounded', sans-serif; font-size: 28px; font-weight: 900; text-transform: uppercase; letter-spacing: 0.1em; color: #0F172A; }
|
||||
|
||||
.wiz-pipeline { position: relative; }
|
||||
.wiz-pipeline-line { position: absolute; left: 17px; top: 10px; bottom: 10px; width: 2px; background: var(--kai-border); }
|
||||
.wiz-pipeline-line { position: absolute; left: 17px; top: 10px; bottom: 10px; width: 2px; background: rgba(45,123,255,0.18); }
|
||||
.wiz-pipeline-item { display: flex; gap: 20px; margin-bottom: 28px; opacity: 0.3; transition: all 0.5s; }
|
||||
.wiz-pipeline-item--active { opacity: 1; }
|
||||
.wiz-pipeline-dot { width: 36px; height: 36px; border-radius: 50%; background: var(--kai-bg); border: 2px solid var(--kai-border); display: flex; align-items: center; justify-content: center; z-index: 1; transition: all 0.5s; color: var(--kai-txt3); }
|
||||
.wiz-pipeline-dot { width: 36px; height: 36px; border-radius: 50%; background: #FFFFFF; border: 2px solid rgba(45,123,255,0.18); display: flex; align-items: center; justify-content: center; z-index: 1; transition: all 0.5s; color: #64748B; }
|
||||
.wiz-pipeline-item--active .wiz-pipeline-dot { border-color: var(--kai-blue); color: var(--kai-blue); box-shadow: 0 0 20px rgba(45,123,255,0.3); }
|
||||
|
||||
.animate-spin-slow { animation: spin 3s linear infinite; }
|
||||
@@ -534,7 +532,7 @@ const togglePlat = (val) => {
|
||||
|
||||
.wiz-error { text-align: center; padding: 32px; background: rgba(239,68,68,0.1); border: 1px solid rgba(239,68,68,0.2); border-radius: 24px; color: #ef4444; }
|
||||
|
||||
.wiz-skeleton { background: rgba(255,255,255,0.03); border-radius: 20px; animation: pulse 2s infinite; }
|
||||
.wiz-skeleton { background: rgba(45,123,255,0.06); border-radius: 20px; animation: pulse 2s infinite; }
|
||||
@keyframes pulse { 0%, 100% { opacity: 0.5; } 50% { opacity: 0.3; } }
|
||||
|
||||
.fade-slide-enter-active, .fade-slide-leave-active { transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); }
|
||||
|
||||
@@ -17,12 +17,12 @@ onMounted(async () => {
|
||||
});
|
||||
|
||||
const STATUS_META = {
|
||||
ACTIVE: { color: 'var(--kai-green)', bg: 'rgba(0,212,139,0.12)', border: 'rgba(0,212,139,0.3)', icon: 'pi pi-bolt', label: 'ACTIVE' },
|
||||
PAUSED: { color: 'var(--kai-yellow)', bg: 'rgba(245,158,11,0.12)', border: 'rgba(245,158,11,0.3)', icon: 'pi pi-pause', label: 'PAUSED' },
|
||||
FAILED: { color: 'var(--kai-red)', bg: 'rgba(239,68,68,0.12)', border: 'rgba(239,68,68,0.3)', icon: 'pi pi-exclamation-triangle', label: 'FAILED' },
|
||||
GENERATING: { color: 'var(--kai-blue)', bg: 'rgba(45,123,255,0.12)', border: 'rgba(45,123,255,0.3)', icon: 'pi pi-spin pi-cog', label: 'GENERATING' },
|
||||
ACTIVE: { color: '#10B981', bg: 'rgba(16,185,129,0.1)', border: 'rgba(16,185,129,0.28)', icon: 'pi pi-bolt', label: 'АКТИВНА', accent: '#10B981' },
|
||||
PAUSED: { color: '#F59E0B', bg: 'rgba(245,158,11,0.1)', border: 'rgba(245,158,11,0.28)', icon: 'pi pi-pause', label: 'НА ПАУЗЕ ', accent: '#F59E0B' },
|
||||
FAILED: { color: '#EF4444', bg: 'rgba(239,68,68,0.1)', border: 'rgba(239,68,68,0.28)', icon: 'pi pi-exclamation-triangle', label: 'ОШИБКА', accent: '#EF4444' },
|
||||
GENERATING: { color: '#2D7BFF', bg: 'rgba(45,123,255,0.1)', border: 'rgba(45,123,255,0.28)', icon: 'pi pi-spin pi-cog', label: 'ЗАПУСК', accent: '#2D7BFF' },
|
||||
};
|
||||
const sm = s => STATUS_META[s] || { color: 'var(--kai-txt3)', bg: 'rgba(255,255,255,0.05)', border: 'rgba(255,255,255,0.1)', icon: 'pi pi-circle', label: s };
|
||||
const sm = s => STATUS_META[(s||'').toUpperCase()] || { color: '#64748B', bg: 'rgba(100,116,139,0.08)', border: 'rgba(100,116,139,0.2)', icon: 'pi pi-circle', label: s || '—', accent: '#64748B' };
|
||||
|
||||
const totalBudget = () => campaigns.value.reduce((sum, c) => sum + (c.budgetKzt || 0), 0);
|
||||
const totalSpent = () => campaigns.value.reduce((sum, c) => sum + (c.spentKzt || 0), 0);
|
||||
@@ -51,82 +51,96 @@ const filteredCampaigns = () => campaigns.value.filter(c =>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mkt-hub">
|
||||
<div class="td-page">
|
||||
<Toast />
|
||||
<!-- AMBIENT GLOW -->
|
||||
<div class="mkt-hub__glow1"></div>
|
||||
<div class="mkt-hub__glow2"></div>
|
||||
<div class="td-glow td-glow--blue"></div>
|
||||
<div class="td-glow td-glow--purple"></div>
|
||||
|
||||
<div class="mkt-hub__inner">
|
||||
<div class="td-inner">
|
||||
|
||||
<!-- HEADER -->
|
||||
<div class="mkt-hub__header" style="text-align: left; margin-bottom: 40px;">
|
||||
<div class="flex items-center gap-4 mb-6">
|
||||
<button class="td-back" @click="$router.push('/marketing-analysis')">
|
||||
<div class="td-header">
|
||||
<div class="td-header__top">
|
||||
<button class="td-back-btn" @click="$router.push('/marketing-analysis')" title="Назад">
|
||||
<i class="pi pi-arrow-left"></i>
|
||||
</button>
|
||||
<div>
|
||||
<div class="mkt-hub__badge" style="margin-bottom: 0;">
|
||||
<span class="mkt-hub__badge-dot"></span>
|
||||
AI Targetologist · Beta
|
||||
</div>
|
||||
<div class="td-badge">
|
||||
<span class="td-badge__dot"></span>
|
||||
ИИ-Таргетолог · Бета
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-6 flex-wrap">
|
||||
<div>
|
||||
<h1 class="mkt-hub__title" style="margin-bottom: 8px;">Кабинет Таргетолога</h1>
|
||||
<p class="mkt-hub__sub" style="margin: 0; text-align: left;">Управление рекламными кампаниями и AI-оптимизация бюджета</p>
|
||||
|
||||
<div class="td-header__main">
|
||||
<div class="td-header__text">
|
||||
<h1 class="td-title">Кабинет Таргетолога</h1>
|
||||
<p class="td-subtitle">Управление рекламными кампаниями и ИИ-оптимизация бюджета</p>
|
||||
</div>
|
||||
<button class="td-launch-btn" @click="$router.push('/marketing-analysis/v3/targetologist/wizard')">
|
||||
<i class="pi pi-sparkles"></i>
|
||||
Создать AI кампанию
|
||||
Создать ИИ-кампанию
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- KPI ROW -->
|
||||
<div class="mkt-hub__grid" style="grid-template-columns: repeat(4, 1fr); margin-bottom: 40px;">
|
||||
<div class="mkt-card">
|
||||
<div class="mkt-card__icon" style="background:rgba(45,123,255,0.15);color:var(--kai-blue);">
|
||||
<!-- KPI CARDS -->
|
||||
<div class="td-kpi-grid">
|
||||
<div class="td-kpi-card">
|
||||
<div class="td-kpi-card__icon" style="background: rgba(45,123,255,0.12); color: #2D7BFF;">
|
||||
<i class="pi pi-list"></i>
|
||||
</div>
|
||||
<div class="td-kpi__num">{{ campaigns.length }}</div>
|
||||
<div class="td-kpi__label">Кампаний всего</div>
|
||||
<div class="td-kpi-card__body">
|
||||
<div class="td-kpi-card__num">{{ campaigns.length }}</div>
|
||||
<div class="td-kpi-card__label">Всего кампаний</div>
|
||||
</div>
|
||||
<div class="td-kpi-card__accent" style="background: rgba(45,123,255,0.06);"></div>
|
||||
</div>
|
||||
<div class="mkt-card">
|
||||
<div class="mkt-card__icon" style="background:rgba(0,212,139,0.15);color:var(--kai-green);">
|
||||
|
||||
<div class="td-kpi-card">
|
||||
<div class="td-kpi-card__icon" style="background: rgba(16,185,129,0.12); color: #10B981;">
|
||||
<i class="pi pi-bolt"></i>
|
||||
</div>
|
||||
<div class="td-kpi__num" style="color:var(--kai-green);">{{ activeCnt() }}</div>
|
||||
<div class="td-kpi__label">Активно сейчас</div>
|
||||
<div class="td-kpi-card__body">
|
||||
<div class="td-kpi-card__num" style="color: #10B981;">{{ activeCnt() }}</div>
|
||||
<div class="td-kpi-card__label">Активно сейчас</div>
|
||||
</div>
|
||||
<div class="td-kpi-card__accent" style="background: rgba(16,185,129,0.06);"></div>
|
||||
</div>
|
||||
<div class="mkt-card">
|
||||
<div class="mkt-card__icon" style="background:rgba(255,122,45,0.15);color:var(--kai-orange);">
|
||||
|
||||
<div class="td-kpi-card">
|
||||
<div class="td-kpi-card__icon" style="background: rgba(249,115,22,0.12); color: #F97316;">
|
||||
<i class="pi pi-dollar"></i>
|
||||
</div>
|
||||
<div class="td-kpi__num">{{ formatCurrency(totalSpent()) }}</div>
|
||||
<div class="td-kpi__label">Всего потрачено</div>
|
||||
<div class="td-kpi-card__body">
|
||||
<div class="td-kpi-card__num">{{ formatCurrency(totalSpent()) }}</div>
|
||||
<div class="td-kpi-card__label">Потрачено</div>
|
||||
</div>
|
||||
<div class="td-kpi-card__accent" style="background: rgba(249,115,22,0.06);"></div>
|
||||
</div>
|
||||
<div class="mkt-card">
|
||||
<div class="mkt-card__icon" style="background:rgba(168,85,247,0.15);color:var(--kai-purple);">
|
||||
|
||||
<div class="td-kpi-card">
|
||||
<div class="td-kpi-card__icon" style="background: rgba(139,92,246,0.12); color: #8B5CF6;">
|
||||
<i class="pi pi-wallet"></i>
|
||||
</div>
|
||||
<div class="td-kpi__num">{{ formatCurrency(totalBudget()) }}</div>
|
||||
<div class="td-kpi__label">Общий лимит</div>
|
||||
<div class="td-kpi-card__body">
|
||||
<div class="td-kpi-card__num">{{ formatCurrency(totalBudget()) }}</div>
|
||||
<div class="td-kpi-card__label">Общий лимит</div>
|
||||
</div>
|
||||
<div class="td-kpi-card__accent" style="background: rgba(139,92,246,0.06);"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- INTEGRATIONS -->
|
||||
<div class="td-section-label">
|
||||
<i class="pi pi-link" style="color:var(--kai-txt3)"></i>
|
||||
Рекламные кабинеты
|
||||
<div class="td-section-header">
|
||||
<div class="td-section-label">
|
||||
<i class="pi pi-link"></i>
|
||||
Рекламные кабинеты
|
||||
</div>
|
||||
</div>
|
||||
<div class="mkt-hub__grid" style="grid-template-columns: repeat(2, 1fr); margin-bottom: 40px;">
|
||||
<div class="td-integrations-grid">
|
||||
<OAuthConnectionWidget
|
||||
platform="facebook"
|
||||
title="Facebook & Instagram"
|
||||
subtitle="Ads Manager API"
|
||||
subtitle="API рекламного кабинета"
|
||||
icon="pi pi-facebook"
|
||||
themeClasses="bg-blue-500/10 blue"
|
||||
:isConnected="state.accounts.facebook"
|
||||
@@ -136,7 +150,7 @@ const filteredCampaigns = () => campaigns.value.filter(c =>
|
||||
<OAuthConnectionWidget
|
||||
platform="tiktok"
|
||||
title="TikTok for Business"
|
||||
subtitle="Marketing API"
|
||||
subtitle="API маркетинга"
|
||||
icon="pi pi-tiktok"
|
||||
themeClasses="bg-rose-500/10 rose"
|
||||
:isConnected="state.accounts.tiktok"
|
||||
@@ -145,87 +159,132 @@ const filteredCampaigns = () => campaigns.value.filter(c =>
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- CAMPAIGNS -->
|
||||
<!-- CAMPAIGNS SECTION -->
|
||||
<div class="td-campaigns-header">
|
||||
<div class="td-section-label" style="margin-bottom:0;">
|
||||
<i class="pi pi-chart-bar" style="color:var(--kai-txt3)"></i>
|
||||
<div class="td-section-label">
|
||||
<i class="pi pi-chart-bar"></i>
|
||||
Список кампаний
|
||||
</div>
|
||||
<div class="td-search">
|
||||
<i class="pi pi-search td-search__icon"></i>
|
||||
<input v-model="searchQ" type="text" placeholder="Найти кампанию..." class="td-search__input" />
|
||||
<button v-if="searchQ" class="td-search__clear" @click="searchQ = ''">
|
||||
<i class="pi pi-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SKELETON / EMPTY / LIST -->
|
||||
<!-- SKELETON -->
|
||||
<div v-if="loading" class="td-skeleton-list">
|
||||
<div v-for="i in 3" :key="i" class="td-skeleton-item">
|
||||
<div class="td-skeleton-bar"></div>
|
||||
<div class="td-skeleton-stripe shimmer"></div>
|
||||
<div class="td-skeleton-body">
|
||||
<div class="td-skeleton-line shimmer" style="width: 35%; height: 14px; margin-bottom: 10px;"></div>
|
||||
<div class="td-skeleton-line shimmer" style="width: 55%;"></div>
|
||||
<div class="td-skeleton-metrics">
|
||||
<div class="td-skeleton-metric shimmer"></div>
|
||||
<div class="td-skeleton-metric shimmer"></div>
|
||||
<div class="td-skeleton-metric shimmer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="filteredCampaigns().length === 0" class="mkt-card" style="text-align: center; padding: 60px 24px;">
|
||||
<div class="td-empty__icon"><i class="pi pi-chart-line"></i></div>
|
||||
<h2 class="mkt-card__title" style="font-size: 20px;">Нет запущенных кампаний</h2>
|
||||
<p class="mkt-card__desc" style="max-width: 420px; margin: 0 auto 24px;">ИИ-Таргетолог готов автоматизировать ваши расходы на рекламу, протестировать креативы и найти идеальную аудиторию.</p>
|
||||
<button class="td-launch-btn" style="margin: 0 auto;" @click="$router.push('/marketing-analysis/v3/targetologist/wizard')">
|
||||
<i class="pi pi-bolt"></i> Запустить первую кампанию
|
||||
<!-- EMPTY -->
|
||||
<div v-else-if="filteredCampaigns().length === 0" class="td-empty">
|
||||
<div class="td-empty__visual">
|
||||
<div class="td-empty__ring"></div>
|
||||
<div class="td-empty__icon">
|
||||
<i class="pi pi-chart-line"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="td-empty__title">Нет запущенных кампаний</h2>
|
||||
<p class="td-empty__desc">ИИ-Таргетолог готов автоматизировать ваши расходы на рекламу, протестировать креативы и найти идеальную аудиторию.</p>
|
||||
<button class="td-launch-btn" style="margin: 0 auto;"
|
||||
@click="$router.push('/marketing-analysis/v3/targetologist/wizard')">
|
||||
<i class="pi pi-bolt"></i>
|
||||
Запустить первую кампанию
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- LIST -->
|
||||
<div v-else class="td-campaign-list">
|
||||
<div v-for="cmp in filteredCampaigns()" :key="cmp.id"
|
||||
class="mkt-card td-campaign"
|
||||
class="td-campaign"
|
||||
@click="$router.push(`/marketing-analysis/v3/targetologist/campaign/${cmp.id}`)">
|
||||
|
||||
<div class="td-campaign__bar" :style="{ background: sm(cmp.status).color }"></div>
|
||||
<!-- Left accent -->
|
||||
<div class="td-campaign__stripe" :style="{ background: sm(cmp.status).accent }"></div>
|
||||
|
||||
<div class="td-campaign__body">
|
||||
<!-- Top row -->
|
||||
<div class="td-campaign__top">
|
||||
<div class="td-campaign__info">
|
||||
<h4 class="mkt-card__title" style="margin-bottom: 4px;">{{ cmp.name }}</h4>
|
||||
<div class="td-campaign__name-row">
|
||||
<h4 class="td-campaign__name">{{ cmp.name }}</h4>
|
||||
<div class="td-campaign__status"
|
||||
:style="{ color: sm(cmp.status).color, background: sm(cmp.status).bg, borderColor: sm(cmp.status).border }">
|
||||
<i :class="sm(cmp.status).icon"></i>
|
||||
{{ sm(cmp.status).label }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="td-campaign__meta">
|
||||
<span><i class="pi pi-flag"></i> {{ cmp.goal }}</span>
|
||||
<span>
|
||||
<i v-if="cmp.platforms?.some(p=>p.includes('Insta'))" class="pi pi-instagram"></i>
|
||||
<i v-if="cmp.platforms?.some(p=>p.includes('Face'))" class="pi pi-facebook"></i>
|
||||
<i v-if="cmp.platforms?.some(p=>p.includes('Tik'))" class="pi pi-tiktok"></i>
|
||||
<span v-if="cmp.goal">
|
||||
<i class="pi pi-flag"></i>
|
||||
{{ cmp.goal }}
|
||||
</span>
|
||||
<span v-if="cmp.platforms?.length" class="td-plat-chips">
|
||||
<span v-if="cmp.platforms?.some(p => p.includes('Insta'))" class="td-plat-chip" style="color:#E1306C; background: rgba(225,48,108,0.08);">
|
||||
<i class="pi pi-instagram"></i> Instagram
|
||||
</span>
|
||||
<span v-if="cmp.platforms?.some(p => p.includes('Face'))" class="td-plat-chip" style="color:#1877F2; background: rgba(24,119,242,0.08);">
|
||||
<i class="pi pi-facebook"></i> Facebook
|
||||
</span>
|
||||
<span v-if="cmp.platforms?.some(p => p.includes('Tik'))" class="td-plat-chip" style="color:#69C9D0; background: rgba(105,201,208,0.08);">
|
||||
<i class="pi pi-tiktok"></i> TikTok
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="td-campaign__status"
|
||||
:style="{ color: sm(cmp.status).color, background: sm(cmp.status).bg, borderColor: sm(cmp.status).border }">
|
||||
<i :class="sm(cmp.status).icon"></i>
|
||||
{{ sm(cmp.status).label }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="td-campaign__bottom">
|
||||
<!-- Metrics row -->
|
||||
<div class="td-campaign__metrics">
|
||||
<div class="td-metric">
|
||||
<div class="td-metric__label">Потрачено</div>
|
||||
<div class="td-metric__val">{{ formatCurrency(cmp.spentKzt || 0) }}</div>
|
||||
<div class="td-metric__bar">
|
||||
<div class="td-metric__bar-fill" :style="{ width: `${Math.min(((cmp.spentKzt||0)/(cmp.budgetKzt||1))*100,100)}%` }"></div>
|
||||
<div class="td-metric__bar-fill"
|
||||
:style="{ width: `${Math.min(((cmp.spentKzt||0)/(cmp.budgetKzt||1))*100,100)}%`, background: sm(cmp.status).accent }">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="td-metric">
|
||||
<div class="td-metric__label">CTR</div>
|
||||
<div class="td-metric__val" style="color:var(--kai-green);">{{ cmp.ctr || '0.00' }}%</div>
|
||||
<div class="td-metric__val" style="color: #10B981;">{{ cmp.ctr || '0.00' }}%</div>
|
||||
</div>
|
||||
<div class="td-metric">
|
||||
<div class="td-metric__label">Охват</div>
|
||||
<div class="td-metric__val">{{ new Intl.NumberFormat('ru-RU').format(cmp.reach || 0) }}</div>
|
||||
</div>
|
||||
<div class="td-metric">
|
||||
<div class="td-metric__label">Бюджет</div>
|
||||
<div class="td-metric__val" style="color: #8B5CF6;">{{ formatCurrency(cmp.budgetKzt || 0) }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="td-campaign__actions" @click.stop>
|
||||
<button v-if="cmp.status === 'ACTIVE'" class="td-act-btn td-act-btn--pause" @click="toggleStatus(cmp)" title="Пауза">
|
||||
<button v-if="cmp.status === 'ACTIVE'" class="td-act-btn td-act-btn--pause"
|
||||
@click="toggleStatus(cmp)" title="Поставить на паузу">
|
||||
<i class="pi pi-pause"></i>
|
||||
</button>
|
||||
<button v-if="cmp.status === 'PAUSED'" class="td-act-btn td-act-btn--play" @click="toggleStatus(cmp)" title="Возобновить">
|
||||
<button v-if="cmp.status === 'PAUSED'" class="td-act-btn td-act-btn--play"
|
||||
@click="toggleStatus(cmp)" title="Возобновить кампанию">
|
||||
<i class="pi pi-play"></i>
|
||||
</button>
|
||||
<button class="td-act-btn" @click="$router.push(`/marketing-analysis/v3/targetologist/campaign/${cmp.id}`)" title="Аналитика">
|
||||
<button class="td-act-btn td-act-btn--open"
|
||||
@click="$router.push(`/marketing-analysis/v3/targetologist/campaign/${cmp.id}`)"
|
||||
title="Открыть аналитику">
|
||||
<i class="pi pi-external-link"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -239,377 +298,294 @@ const filteredCampaigns = () => campaigns.value.filter(c =>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* ───── Page layout (from MarketingMain) ───── */
|
||||
.mkt-hub {
|
||||
background: var(--kai-bg);
|
||||
/* ──────────────────── PAGE ──────────────────── */
|
||||
.td-page {
|
||||
background: #F5F8FF;
|
||||
background-image:
|
||||
radial-gradient(at 10% 0%, rgba(45,123,255,0.07) 0px, transparent 50%),
|
||||
radial-gradient(at 90% 100%, rgba(139,92,246,0.05) 0px, transparent 50%);
|
||||
min-height: 100vh;
|
||||
font-family: 'Onest', sans-serif;
|
||||
padding: 40px 24px 100px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 48px 24px 80px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.td-glow { position: absolute; border-radius: 50%; pointer-events: none; z-index: 0; }
|
||||
.td-glow--blue { top: -180px; right: -120px; width: 600px; height: 600px; background: radial-gradient(circle, rgba(45,123,255,0.08) 0%, transparent 70%); }
|
||||
.td-glow--purple{ bottom: -100px; left: -80px; width: 400px; height: 400px; background: radial-gradient(circle, rgba(139,92,246,0.06) 0%, transparent 70%); }
|
||||
.td-inner { max-width: 1100px; margin: 0 auto; position: relative; z-index: 1; }
|
||||
|
||||
.mkt-hub__glow1 {
|
||||
position: absolute;
|
||||
top: -120px;
|
||||
right: -100px;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
background: radial-gradient(circle, rgba(45,123,255,0.08) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.mkt-hub__glow2 {
|
||||
position: absolute;
|
||||
bottom: -80px;
|
||||
left: -60px;
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
background: radial-gradient(circle, rgba(0,212,139,0.06) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mkt-hub__inner {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* ───── Header (V4 Style) ───── */
|
||||
.mkt-hub__badge {
|
||||
display: inline-flex;
|
||||
/* ──────────────────── HEADER ──────────────────── */
|
||||
.td-header { margin-bottom: 40px; }
|
||||
.td-header__top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 5px 14px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(45,123,255,0.3);
|
||||
background: rgba(45,123,255,0.08);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.07em;
|
||||
color: var(--kai-blue);
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.mkt-hub__badge-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--kai-blue);
|
||||
box-shadow: 0 0 8px var(--kai-blue);
|
||||
animation: pulse-dot 2s infinite;
|
||||
}
|
||||
@keyframes pulse-dot {
|
||||
0%,100% { opacity:1; transform: scale(1); }
|
||||
50% { opacity:0.5; transform: scale(0.8); }
|
||||
.td-back-btn {
|
||||
width: 40px; height: 40px; border-radius: 12px;
|
||||
border: 1px solid rgba(45,123,255,0.2); background: #fff; color: #64748B;
|
||||
display: flex; align-items: center; justify-content: center; cursor: pointer;
|
||||
transition: all 0.2s; box-shadow: 0 2px 8px rgba(45,123,255,0.06); flex-shrink: 0;
|
||||
}
|
||||
.td-back-btn:hover { background: rgba(45,123,255,0.06); color: #2D7BFF; border-color: rgba(45,123,255,0.35); transform: translateX(-2px); }
|
||||
|
||||
.mkt-hub__title {
|
||||
.td-badge {
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
padding: 5px 14px; border-radius: 999px;
|
||||
border: 1px solid rgba(45,123,255,0.25); background: rgba(45,123,255,0.07);
|
||||
font-size: 10px; font-weight: 800; font-family: 'Unbounded', sans-serif;
|
||||
text-transform: uppercase; letter-spacing: 0.08em; color: #2D7BFF;
|
||||
}
|
||||
.td-badge__dot {
|
||||
width: 6px; height: 6px; border-radius: 50%; background: #2D7BFF;
|
||||
box-shadow: 0 0 8px rgba(45,123,255,0.5); animation: td-blink 2s infinite;
|
||||
}
|
||||
@keyframes td-blink { 0%,100%{opacity:1}50%{opacity:0.4} }
|
||||
|
||||
.td-header__main {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.td-title {
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
font-size: clamp(24px, 4vw, 36px);
|
||||
font-size: clamp(22px, 3.5vw, 32px);
|
||||
font-weight: 900;
|
||||
color: var(--kai-txt);
|
||||
color: #0F172A;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1.1;
|
||||
line-height: 1.15;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mkt-hub__sub {
|
||||
font-size: 15px;
|
||||
color: var(--kai-txt2);
|
||||
line-height: 1.6;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* ───── Grid ───── */
|
||||
.mkt-hub__grid {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.mkt-hub__grid { grid-template-columns: 1fr !important; }
|
||||
}
|
||||
|
||||
/* ───── Cards (V4 Style) ───── */
|
||||
.mkt-card {
|
||||
background: var(--kai-card);
|
||||
border: 1px solid var(--kai-border);
|
||||
border-radius: 20px;
|
||||
padding: 24px;
|
||||
transition: all 0.3s cubic-bezier(0.4,0,0.2,1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.mkt-card:hover {
|
||||
transform: translateY(-4px);
|
||||
border-color: var(--kai-border-hover);
|
||||
background: var(--kai-card-hover);
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.4), 0 0 40px rgba(45,123,255,0.08);
|
||||
}
|
||||
|
||||
.mkt-card__icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
margin-bottom: 16px;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
.mkt-card:hover .mkt-card__icon { transform: scale(1.1) rotate(3deg); }
|
||||
|
||||
.mkt-card__title {
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: var(--kai-txt);
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.mkt-card__desc {
|
||||
font-size: 13px;
|
||||
color: var(--kai-txt2);
|
||||
line-height: 1.6;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* ───── Targetologist Specifics ───── */
|
||||
.td-back {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--kai-border);
|
||||
background: var(--kai-card);
|
||||
color: var(--kai-txt2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.td-back:hover {
|
||||
background: var(--kai-card-hover);
|
||||
color: var(--kai-txt);
|
||||
border-color: var(--kai-border-hover);
|
||||
transform: translateX(-2px);
|
||||
.td-subtitle {
|
||||
font-size: 14px;
|
||||
color: #64748B;
|
||||
line-height: 1.55;
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
.td-launch-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 24px;
|
||||
border-radius: 14px;
|
||||
background: var(--kai-blue);
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
display: inline-flex; align-items: center; gap: 9px;
|
||||
padding: 13px 24px; border-radius: 14px;
|
||||
background: #2D7BFF; color: #fff;
|
||||
font-size: 13px; font-weight: 700; font-family: 'Unbounded', sans-serif;
|
||||
border: none; cursor: pointer; transition: all 0.3s;
|
||||
box-shadow: 0 6px 24px rgba(45,123,255,0.35); white-space: nowrap; flex-shrink: 0;
|
||||
}
|
||||
.td-launch-btn:hover {
|
||||
background: #1a6bff;
|
||||
box-shadow: 0 0 28px rgba(45,123,255,0.45);
|
||||
transform: translateY(-2px);
|
||||
.td-launch-btn:hover { background: #1a6bff; box-shadow: 0 10px 36px rgba(45,123,255,0.5); transform: translateY(-3px); }
|
||||
|
||||
/* ──────────────────── KPI GRID ──────────────────── */
|
||||
.td-kpi-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 14px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
@media (max-width: 900px) { .td-kpi-grid { grid-template-columns: repeat(2, 1fr); } }
|
||||
@media (max-width: 480px) { .td-kpi-grid { grid-template-columns: 1fr; } }
|
||||
|
||||
.td-kpi__num {
|
||||
.td-kpi-card {
|
||||
background: #fff;
|
||||
border: 1px solid rgba(45,123,255,0.12);
|
||||
border-radius: 18px;
|
||||
padding: 22px 20px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: all 0.25s cubic-bezier(0.4,0,0.2,1);
|
||||
box-shadow: 0 2px 10px rgba(45,123,255,0.05);
|
||||
}
|
||||
.td-kpi-card:hover { transform: translateY(-4px); box-shadow: 0 16px 40px rgba(45,123,255,0.12); }
|
||||
.td-kpi-card__accent {
|
||||
position: absolute;
|
||||
top: 0; right: 0;
|
||||
width: 80px; height: 80px;
|
||||
border-radius: 0 18px 0 80px;
|
||||
}
|
||||
.td-kpi-card__icon {
|
||||
width: 44px; height: 44px; border-radius: 12px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 18px; margin-bottom: 16px; position: relative; z-index: 1;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
.td-kpi-card:hover .td-kpi-card__icon { transform: scale(1.12) rotate(5deg); }
|
||||
.td-kpi-card__body { position: relative; z-index: 1; }
|
||||
.td-kpi-card__num {
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
font-size: 22px;
|
||||
font-weight: 900;
|
||||
color: var(--kai-txt);
|
||||
line-height: 1;
|
||||
margin-bottom: 6px;
|
||||
font-size: 22px; font-weight: 900; color: #0F172A; line-height: 1; margin-bottom: 6px;
|
||||
}
|
||||
.td-kpi__label {
|
||||
font-size: 11px;
|
||||
color: var(--kai-txt3);
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
.td-kpi-card__label {
|
||||
font-size: 10px; color: #94A3B8; font-weight: 700;
|
||||
text-transform: uppercase; letter-spacing: 0.07em;
|
||||
}
|
||||
|
||||
/* ──────────────────── SECTION HEADER ──────────────────── */
|
||||
.td-section-header { margin-bottom: 16px; }
|
||||
.td-section-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--kai-txt2);
|
||||
margin-bottom: 20px;
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
font-size: 11px; font-weight: 800; font-family: 'Unbounded', sans-serif;
|
||||
text-transform: uppercase; letter-spacing: 0.1em; color: #94A3B8;
|
||||
}
|
||||
.td-section-label i { font-size: 12px; }
|
||||
|
||||
/* ──────────────────── INTEGRATIONS ──────────────────── */
|
||||
.td-integrations-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 14px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
@media (max-width: 640px) { .td-integrations-grid { grid-template-columns: 1fr; } }
|
||||
|
||||
/* ──────────────────── CAMPAIGNS HEADER ──────────────────── */
|
||||
.td-campaigns-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.td-search {
|
||||
position: relative;
|
||||
gap: 16px;
|
||||
margin-bottom: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.td-search { position: relative; }
|
||||
.td-search__icon {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--kai-txt3);
|
||||
font-size: 14px;
|
||||
position: absolute; left: 14px; top: 50%; transform: translateY(-50%);
|
||||
color: #94A3B8; font-size: 13px; pointer-events: none;
|
||||
}
|
||||
.td-search__input {
|
||||
padding: 10px 16px 10px 44px;
|
||||
border-radius: 12px;
|
||||
background: var(--kai-card);
|
||||
border: 1px solid var(--kai-border);
|
||||
color: var(--kai-txt);
|
||||
font-size: 14px;
|
||||
font-family: 'Onest', sans-serif;
|
||||
outline: none;
|
||||
width: 280px;
|
||||
transition: all 0.2s;
|
||||
padding: 10px 38px 10px 42px; border-radius: 12px;
|
||||
background: #fff; border: 1px solid rgba(45,123,255,0.16); color: #0F172A;
|
||||
font-size: 13.5px; font-family: 'Onest', sans-serif; outline: none;
|
||||
width: 280px; transition: all 0.2s; box-shadow: 0 2px 8px rgba(45,123,255,0.05);
|
||||
}
|
||||
.td-search__input:focus {
|
||||
border-color: var(--kai-blue);
|
||||
background: var(--kai-card-hover);
|
||||
width: 320px;
|
||||
.td-search__input:focus { border-color: rgba(45,123,255,0.4); width: 320px; box-shadow: 0 0 0 3px rgba(45,123,255,0.08); }
|
||||
.td-search__input::placeholder { color: #94A3B8; }
|
||||
.td-search__clear {
|
||||
position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
|
||||
width: 22px; height: 22px; border-radius: 50%; border: none;
|
||||
background: rgba(100,116,139,0.12); color: #64748B;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
cursor: pointer; font-size: 10px; transition: all 0.2s;
|
||||
}
|
||||
.td-search__clear:hover { background: rgba(239,68,68,0.12); color: #EF4444; }
|
||||
|
||||
.td-campaign-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
/* ──────────────────── SKELETON ──────────────────── */
|
||||
.td-skeleton-list { display: flex; flex-direction: column; gap: 12px; }
|
||||
.td-skeleton-item {
|
||||
display: flex; background: #fff;
|
||||
border: 1px solid rgba(45,123,255,0.1); border-radius: 18px; overflow: hidden; height: 130px;
|
||||
}
|
||||
.td-skeleton-stripe { width: 5px; flex-shrink: 0; background: rgba(45,123,255,0.1); }
|
||||
.td-skeleton-body { flex: 1; padding: 22px 24px; }
|
||||
.td-skeleton-line { height: 12px; border-radius: 6px; background: rgba(45,123,255,0.07); }
|
||||
.td-skeleton-metrics { display: flex; gap: 24px; margin-top: 16px; }
|
||||
.td-skeleton-metric { width: 80px; height: 40px; border-radius: 10px; background: rgba(45,123,255,0.07); }
|
||||
.shimmer { position: relative; overflow: hidden; }
|
||||
.shimmer::after {
|
||||
content: ''; position: absolute; inset: 0;
|
||||
background: linear-gradient(90deg, transparent, rgba(45,123,255,0.07), transparent);
|
||||
animation: shimmer 1.6s infinite;
|
||||
}
|
||||
@keyframes shimmer { 0%{transform:translateX(-100%)} 100%{transform:translateX(100%)} }
|
||||
|
||||
/* ──────────────────── EMPTY ──────────────────── */
|
||||
.td-empty {
|
||||
text-align: center; padding: 80px 24px; background: #fff;
|
||||
border: 1px solid rgba(45,123,255,0.14); border-radius: 24px;
|
||||
box-shadow: 0 4px 24px rgba(45,123,255,0.06);
|
||||
display: flex; flex-direction: column; align-items: center; gap: 16px;
|
||||
}
|
||||
.td-empty__visual { position: relative; width: 84px; height: 84px; }
|
||||
.td-empty__ring {
|
||||
position: absolute; inset: -7px; border-radius: 50%;
|
||||
border: 2px solid rgba(45,123,255,0.12); animation: spin-slow 8s linear infinite;
|
||||
}
|
||||
@keyframes spin-slow { from{transform:rotate(0deg)} to{transform:rotate(360deg)} }
|
||||
.td-empty__icon {
|
||||
width: 84px; height: 84px; border-radius: 22px;
|
||||
background: rgba(45,123,255,0.08); border: 1px solid rgba(45,123,255,0.18);
|
||||
display: flex; align-items: center; justify-content: center; font-size: 34px; color: #2D7BFF;
|
||||
}
|
||||
.td-empty__title { font-family: 'Unbounded', sans-serif; font-size: 20px; font-weight: 800; color: #0F172A; }
|
||||
.td-empty__desc { font-size: 13.5px; color: #64748B; max-width: 420px; line-height: 1.65; }
|
||||
|
||||
/* ──────────────────── CAMPAIGN LIST ──────────────────── */
|
||||
.td-campaign-list { display: flex; flex-direction: column; gap: 12px; }
|
||||
|
||||
.td-campaign {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(45,123,255,0.12);
|
||||
border-radius: 18px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
transition: all 0.25s cubic-bezier(0.4,0,0.2,1);
|
||||
box-shadow: 0 2px 10px rgba(45,123,255,0.05);
|
||||
}
|
||||
.td-campaign__bar {
|
||||
width: 4px;
|
||||
flex-shrink: 0;
|
||||
.td-campaign:hover { transform: translateY(-3px); box-shadow: 0 14px 40px rgba(45,123,255,0.12); border-color: rgba(45,123,255,0.28); }
|
||||
|
||||
.td-campaign__stripe { width: 5px; flex-shrink: 0; transition: width 0.2s; }
|
||||
.td-campaign:hover .td-campaign__stripe { width: 6px; }
|
||||
|
||||
.td-campaign__body { flex: 1; padding: 22px 24px; }
|
||||
|
||||
.td-campaign__top { margin-bottom: 18px; }
|
||||
.td-campaign__name-row {
|
||||
display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 8px;
|
||||
}
|
||||
.td-campaign__body {
|
||||
flex: 1;
|
||||
padding: 24px;
|
||||
}
|
||||
.td-campaign__top {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.td-campaign__meta {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
font-size: 12px;
|
||||
color: var(--kai-txt3);
|
||||
align-items: center;
|
||||
.td-campaign__name {
|
||||
font-family: 'Unbounded', sans-serif; font-size: 15px; font-weight: 700; color: #0F172A; line-height: 1.3; flex: 1;
|
||||
}
|
||||
.td-campaign__status {
|
||||
display: inline-flex;
|
||||
display: inline-flex; align-items: center; gap: 5px;
|
||||
padding: 4px 12px; border-radius: 999px; border: 1px solid;
|
||||
font-size: 9.5px; font-weight: 800; font-family: 'Unbounded', sans-serif;
|
||||
text-transform: uppercase; letter-spacing: 0.07em; flex-shrink: 0;
|
||||
}
|
||||
.td-campaign__status i { font-size: 9px; }
|
||||
|
||||
.td-campaign__meta {
|
||||
display: flex; align-items: center; gap: 10px; font-size: 12px; color: #94A3B8; flex-wrap: wrap;
|
||||
}
|
||||
.td-campaign__meta i { font-size: 11px; margin-right: 3px; }
|
||||
.td-plat-chips { display: flex; gap: 6px; flex-wrap: wrap; }
|
||||
.td-plat-chip {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
padding: 3px 9px; border-radius: 8px; font-size: 11px; font-weight: 600;
|
||||
}
|
||||
.td-plat-chip i { font-size: 10px; }
|
||||
|
||||
.td-campaign__metrics {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 14px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
gap: 28px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.td-campaign__bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
}
|
||||
.td-metric {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
min-width: 120px;
|
||||
}
|
||||
.td-metric { display: flex; flex-direction: column; gap: 4px; min-width: 100px; }
|
||||
.td-metric__label {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
color: var(--kai-txt3);
|
||||
font-size: 9.5px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.08em; color: #94A3B8;
|
||||
}
|
||||
.td-metric__val {
|
||||
font-family: 'Unbounded', sans-serif;
|
||||
font-size: 18px;
|
||||
font-weight: 900;
|
||||
color: var(--kai-txt);
|
||||
line-height: 1;
|
||||
font-family: 'Unbounded', sans-serif; font-size: 17px; font-weight: 900; color: #0F172A; line-height: 1;
|
||||
}
|
||||
.td-metric__bar {
|
||||
height: 4px;
|
||||
background: rgba(255,255,255,0.06);
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
margin-top: 6px;
|
||||
width: 100%;
|
||||
height: 4px; background: rgba(45,123,255,0.1); border-radius: 999px; overflow: hidden; margin-top: 4px;
|
||||
}
|
||||
.td-metric__bar-fill {
|
||||
height: 100%;
|
||||
background: var(--kai-blue);
|
||||
border-radius: 999px;
|
||||
transition: width 0.8s cubic-bezier(0.4,0,0.2,1);
|
||||
height: 100%; border-radius: 999px; transition: width 0.9s cubic-bezier(0.4,0,0.2,1);
|
||||
}
|
||||
|
||||
.td-campaign__actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
}
|
||||
.td-campaign__actions { display: flex; gap: 8px; align-items: center; margin-left: auto; }
|
||||
.td-act-btn {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--kai-border);
|
||||
background: rgba(255,255,255,0.04);
|
||||
color: var(--kai-txt2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
font-size: 14px;
|
||||
width: 36px; height: 36px; border-radius: 10px;
|
||||
border: 1px solid rgba(45,123,255,0.14); background: rgba(45,123,255,0.04);
|
||||
color: #64748B; display: flex; align-items: center; justify-content: center;
|
||||
cursor: pointer; transition: all 0.2s; font-size: 13px;
|
||||
}
|
||||
.td-act-btn:hover {
|
||||
background: var(--kai-card-hover);
|
||||
color: var(--kai-txt);
|
||||
border-color: var(--kai-border-hover);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.td-empty__icon {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: 20px;
|
||||
background: rgba(45,123,255,0.1);
|
||||
border: 1px solid rgba(45,123,255,0.2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 30px;
|
||||
color: var(--kai-blue);
|
||||
margin: 0 auto 20px;
|
||||
}
|
||||
|
||||
.td-skeleton-list { display: flex; flex-direction: column; gap: 12px; }
|
||||
.td-skeleton-item { height: 140px; background: var(--kai-card); border: 1px solid var(--kai-border); border-radius: 20px; padding: 24px; }
|
||||
.td-skeleton-bar { height: 20px; background: rgba(255,255,255,0.04); border-radius: 10px; width: 60%; }
|
||||
.td-act-btn:hover { transform: scale(1.08); }
|
||||
.td-act-btn--pause:hover { background: rgba(245,158,11,0.1); color: #F59E0B; border-color: rgba(245,158,11,0.3); }
|
||||
.td-act-btn--play:hover { background: rgba(16,185,129,0.1); color: #10B981; border-color: rgba(16,185,129,0.3); }
|
||||
.td-act-btn--open:hover { background: rgba(45,123,255,0.1); color: #2D7BFF; border-color: rgba(45,123,255,0.3); }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user