Update roadmap to 3-phase 6-month plan

This commit is contained in:
Ruslan Bakiev
2026-02-10 17:00:56 +07:00
parent 27dc375f33
commit ac6e06f4c5
4 changed files with 410 additions and 28 deletions

View File

@@ -1,5 +1,6 @@
<template>
<div data-theme="silk" class="min-h-screen bg-mesh text-neutral">
<canvas id="canvas-basic" class="gradient-canvas" aria-hidden="true"></canvas>
<div class="relative z-10">
<div
class="pointer-events-none absolute -top-24 left-1/2 h-72 w-[1100px] -translate-x-1/2 rounded-full bg-[radial-gradient(circle_at_center,rgba(67,56,118,0.25),transparent_70%)] blur-3xl"
@@ -159,28 +160,35 @@
</p>
</div>
<div class="slide-main">
<div class="grid gap-6 md:grid-cols-3">
<div class="glass-card rounded-3xl p-6 shadow-soft">
<h3 class="font-display text-2xl">Старт проекта</h3>
<p class="mt-3 text-sm text-neutral/70">
Заказчик добавляет объект (фото/видео/обмеры), получает смету, этапы и критерии. Точка
трения снимается заранее: все ожидают одинаковый результат.
</p>
</div>
<div class="glass-card rounded-3xl p-6 shadow-soft">
<h3 class="font-display text-2xl">Ведение этапа</h3>
<p class="mt-3 text-sm text-neutral/70">
Исполнитель работает по регламенту и чек-листам, прогресс фиксируется. Заказчик видит
прозрачный статус без переписки.
</p>
</div>
<div class="glass-card rounded-3xl p-6 shadow-soft">
<h3 class="font-display text-2xl">Приемка и расчет</h3>
<p class="mt-3 text-sm text-neutral/70">
Независимый инспектор сверяет критерии, сервис фиксирует решение и переводит эскроу.
Спор заменяется фактами.
</p>
</div>
<div class="flow-wrapper glass-panel rounded-3xl border border-base-300 p-4">
<ClientOnly>
<VueFlow
:nodes="flowNodes"
:edges="flowEdges"
:fit-view-on-init="true"
:nodes-draggable="false"
:nodes-connectable="false"
:elements-selectable="false"
:zoom-on-scroll="false"
:zoom-on-pinch="false"
:pan-on-drag="false"
:pan-on-scroll="false"
>
<template #node-step="{ data }">
<div class="flow-card glass-card rounded-3xl p-5 shadow-soft">
<div class="text-xs font-semibold text-neutral/60">{{ data.step }}</div>
<div class="mt-2 font-display text-lg">{{ data.title }}</div>
<div class="mt-3 text-sm text-neutral/70">{{ data.text }}</div>
<Handle id="target-l" type="target" :position="Position.Left" class="flow-handle" />
<Handle id="target-r" type="target" :position="Position.Right" class="flow-handle" />
<Handle id="target-t" type="target" :position="Position.Top" class="flow-handle" />
<Handle id="source-r" type="source" :position="Position.Right" class="flow-handle" />
<Handle id="source-l" type="source" :position="Position.Left" class="flow-handle" />
<Handle id="source-b" type="source" :position="Position.Bottom" class="flow-handle" />
</div>
</template>
</VueFlow>
</ClientOnly>
</div>
</div>
</section>
@@ -326,10 +334,10 @@
<section id="roadmap" class="slide">
<div class="slide-top"><span class="slide-num">10</span><span class="slide-label">Roadmap</span></div>
<div class="slide-head">
<h2 class="slide-title">От пилота к системе приемки</h2>
<h2 class="slide-title">Пилот за 2 месяца, затем смета и приемка</h2>
<p class="slide-preamble">
Сначала проверяем гипотезы на реальных ремонтах, затем усиливаем расчет ресурсов и выносим
независимую приемку как масштабируемый сервис.
Первые два месяца уходит на запуск пилота: этапы работ, декомпозиция объема и подбор исполнителей.
Затем доводим ресурсную ведомость и смету, а в конце выстраиваем приемку с инспекторами.
</p>
</div>
<div class="slide-main">
@@ -346,7 +354,7 @@
</div>
<div class="grid grid-cols-[160px_repeat(6,minmax(0,1fr))] items-center gap-2">
<div class="text-neutral/70">Пилот на ремонтах</div>
<div class="text-neutral/70">Пилот: этапы, объем, исполнители</div>
<div class="rounded-full bg-primary py-2 text-center text-xs text-primary-content">М1</div>
<div class="rounded-full bg-primary py-2 text-center text-xs text-primary-content">М2</div>
<div class="rounded-full bg-base-300/70 py-2"></div>
@@ -356,7 +364,7 @@
</div>
<div class="grid grid-cols-[160px_repeat(6,minmax(0,1fr))] items-center gap-2">
<div class="text-neutral/70">Расчет ресурсной сметы</div>
<div class="text-neutral/70">Ресурсная ведомость и смета</div>
<div class="rounded-full bg-base-300/70 py-2"></div>
<div class="rounded-full bg-base-300/70 py-2"></div>
<div class="rounded-full bg-primary py-2 text-center text-xs text-primary-content">М3</div>
@@ -366,7 +374,7 @@
</div>
<div class="grid grid-cols-[160px_repeat(6,minmax(0,1fr))] items-center gap-2">
<div class="text-neutral/70">Инспектор и приемка</div>
<div class="text-neutral/70">Приемка и инспекторы</div>
<div class="rounded-full bg-base-300/70 py-2"></div>
<div class="rounded-full bg-base-300/70 py-2"></div>
<div class="rounded-full bg-base-300/70 py-2"></div>
@@ -405,6 +413,9 @@
</template>
<script setup>
import { onMounted, onUnmounted, ref } from 'vue'
import { VueFlow, Handle, Position, MarkerType } from '@vue-flow/core'
import Granim from 'granim'
useHead({
title: 'Контроль ремонта — презентация',
@@ -412,9 +423,115 @@ useHead({
{ name: 'description', content: 'Презентация продукта: смета, приемка, эскроу и методология ремонта.' }
]
})
let granimInstance
const flowNodes = ref([
{
id: '1',
type: 'step',
position: { x: 0, y: 40 },
data: {
step: '01',
title: 'Добавление объекта',
text: 'Заказчик загружает фото/видео/обмеры или проект — система получает исходные данные.'
}
},
{
id: '2',
type: 'step',
position: { x: 240, y: 40 },
data: {
step: '02',
title: 'Смета и этапы',
text: 'Алгоритм формирует объем работ, смету, этапы и критерии приемки по каждому шагу.'
}
},
{
id: '3',
type: 'step',
position: { x: 480, y: 40 },
data: {
step: '03',
title: 'Создание заявки',
text: 'Заказчик подтверждает параметры и публикует заявку на выполнение работ.'
}
},
{
id: '4',
type: 'step',
position: { x: 720, y: 40 },
data: {
step: '04',
title: 'Выбор исполнителя',
text: 'Выбирается бригада или мастер, согласуются сроки и старт этапа.'
}
},
{
id: '5',
type: 'step',
position: { x: 720, y: 220 },
data: {
step: '05',
title: 'Эскроу и запуск',
text: 'Заказчик вносит оплату в эскроу, исполнитель запускает работы по регламенту.'
}
},
{
id: '6',
type: 'step',
position: { x: 480, y: 220 },
data: {
step: '06',
title: 'Фиксация прогресса',
text: 'Исполнитель ведет этапы, отмечает завершение, заказчик видит статус и результаты.'
}
},
{
id: '7',
type: 'step',
position: { x: 240, y: 220 },
data: {
step: '07',
title: 'Приемка и расчет',
text: 'Инспектор сверяет критерии: если все ок — эскроу разблокируется, если нет — доработка или удержание.'
}
}
])
const flowEdges = ref([
{ id: 'e1-2', source: '1', target: '2', sourceHandle: 'source-r', targetHandle: 'target-l', type: 'smoothstep', markerEnd: MarkerType.ArrowClosed },
{ id: 'e2-3', source: '2', target: '3', sourceHandle: 'source-r', targetHandle: 'target-l', type: 'smoothstep', markerEnd: MarkerType.ArrowClosed },
{ id: 'e3-4', source: '3', target: '4', sourceHandle: 'source-r', targetHandle: 'target-l', type: 'smoothstep', markerEnd: MarkerType.ArrowClosed },
{ id: 'e4-5', source: '4', target: '5', sourceHandle: 'source-b', targetHandle: 'target-t', type: 'smoothstep', markerEnd: MarkerType.ArrowClosed },
{ id: 'e5-6', source: '5', target: '6', sourceHandle: 'source-l', targetHandle: 'target-r', type: 'smoothstep', markerEnd: MarkerType.ArrowClosed },
{ id: 'e6-7', source: '6', target: '7', sourceHandle: 'source-l', targetHandle: 'target-r', type: 'smoothstep', markerEnd: MarkerType.ArrowClosed }
])
onMounted(() => {
granimInstance = new Granim({
element: '#canvas-basic',
direction: 'radial',
isPausedWhenNotInView: true,
states: {
'default-state': {
gradients: [
['#ff9966', '#ff5e62'],
['#00F260', '#0575E6'],
['#e1eec3', '#f05053']
]
}
}
})
onUnmounted(() => {
if (granimInstance && granimInstance.pause) granimInstance.pause()
})
})
</script>
<style>
@import '@vue-flow/core/dist/style.css';
@import '@vue-flow/core/dist/theme-default.css';
@import url('https://fonts.googleapis.com/css2?family=Manrope:wght@300;400;600;700&family=Space+Grotesk:wght@400;600;700&display=swap');
:root {
@@ -433,6 +550,45 @@ html {
font-family: 'Space Grotesk', system-ui, sans-serif;
}
.gradient-canvas {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
z-index: 0;
pointer-events: none;
filter: saturate(1.1);
}
.flow-wrapper {
width: 100%;
height: 420px;
overflow: hidden;
}
.flow-wrapper .vue-flow {
width: 100%;
height: 100%;
}
.flow-card {
min-width: 200px;
max-width: 220px;
}
.flow-handle {
opacity: 0;
}
.vue-flow__edge-path {
stroke: rgba(20, 20, 20, 0.45);
stroke-width: 1.4;
}
.vue-flow__edge-marker path {
fill: rgba(20, 20, 20, 0.6);
}
.nav-link {
transition: all 0.2s ease;
display: flex;