Update roadmap to 3-phase 6-month plan
This commit is contained in:
212
app/app.vue
212
app/app.vue
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user