From c5959d5dbb356ecc97a70d969f92d47a3abca8de Mon Sep 17 00:00:00 2001 From: Ruslan Bakiev <572431+veikab@users.noreply.github.com> Date: Tue, 10 Feb 2026 17:40:47 +0700 Subject: [PATCH] Update metrics to on-time NSM and inputs --- app/app.vue | 88 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 28 deletions(-) diff --git a/app/app.vue b/app/app.vue index 7acce0e..bf35ee5 100644 --- a/app/app.vue +++ b/app/app.vue @@ -150,7 +150,7 @@ -
+
05Key user flow

Как это работает: key user flow

@@ -159,16 +159,13 @@ как сервис их снимает — через регламент, доказуемую проверку и эскроу.

-
+

North Star Metric

-

Доля этапов, принятых с первого раза без спора.

+

Доля этапов, выполненных в обещанный срок.

- Input-метрики: доля этапов с зафиксированными критериями, среднее время согласования - этапа, точность автосметы, доля приемок с «зеленым» статусом. + Input-метрики: ошибка прогноза по срокам (факт минус план), отклонение объема работ от плана, + фактическая длительность по типу работ (средние значения).

@@ -434,7 +431,7 @@ const flowNodes = ref([ { id: '2', type: 'step', - position: { x: 480, y: 40 }, + position: { x: 180, y: 40 }, data: { step: '02', title: 'Смета и этапы', @@ -444,7 +441,7 @@ const flowNodes = ref([ { id: '3', type: 'step', - position: { x: 960, y: 40 }, + position: { x: 360, y: 40 }, data: { step: '03', title: 'Создание заявки', @@ -454,7 +451,7 @@ const flowNodes = ref([ { id: '4', type: 'step', - position: { x: 1440, y: 40 }, + position: { x: 540, y: 40 }, data: { step: '04', title: 'Выбор исполнителя', @@ -464,7 +461,7 @@ const flowNodes = ref([ { id: '5', type: 'step', - position: { x: 1920, y: 40 }, + position: { x: 720, y: 40 }, data: { step: '05', title: 'Эскроу и запуск', @@ -474,7 +471,7 @@ const flowNodes = ref([ { id: '6', type: 'step', - position: { x: 2400, y: 40 }, + position: { x: 900, y: 40 }, data: { step: '06', title: 'Фиксация прогресса', @@ -484,7 +481,7 @@ const flowNodes = ref([ { id: '7', type: 'step', - position: { x: 2880, y: 40 }, + position: { x: 1080, y: 40 }, data: { step: '07', title: 'Приемка и расчет', @@ -494,12 +491,12 @@ const flowNodes = ref([ ]) 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-r', targetHandle: 'target-l', type: 'smoothstep', markerEnd: MarkerType.ArrowClosed }, - { id: 'e5-6', source: '5', target: '6', sourceHandle: 'source-r', targetHandle: 'target-l', type: 'smoothstep', markerEnd: MarkerType.ArrowClosed }, - { id: 'e6-7', source: '6', target: '7', sourceHandle: 'source-r', targetHandle: 'target-l', type: 'smoothstep', markerEnd: MarkerType.ArrowClosed } + { id: 'e1-2', source: '1', target: '2', sourceHandle: 'source-r', targetHandle: 'target-l', type: 'straight', markerEnd: MarkerType.ArrowClosed }, + { id: 'e2-3', source: '2', target: '3', sourceHandle: 'source-r', targetHandle: 'target-l', type: 'straight', markerEnd: MarkerType.ArrowClosed }, + { id: 'e3-4', source: '3', target: '4', sourceHandle: 'source-r', targetHandle: 'target-l', type: 'straight', markerEnd: MarkerType.ArrowClosed }, + { id: 'e4-5', source: '4', target: '5', sourceHandle: 'source-r', targetHandle: 'target-l', type: 'straight', markerEnd: MarkerType.ArrowClosed }, + { id: 'e5-6', source: '5', target: '6', sourceHandle: 'source-r', targetHandle: 'target-l', type: 'straight', markerEnd: MarkerType.ArrowClosed }, + { id: 'e6-7', source: '6', target: '7', sourceHandle: 'source-r', targetHandle: 'target-l', type: 'straight', markerEnd: MarkerType.ArrowClosed } ]) onMounted(() => { @@ -557,10 +554,11 @@ html { .flow-wrapper { width: 100%; - height: 280px; + height: 100%; overflow-x: auto; overflow-y: hidden; scrollbar-width: thin; + position: relative; } .flow-wrapper::-webkit-scrollbar { @@ -582,9 +580,27 @@ html { height: 100%; } +.vue-flow { + position: relative; +} + +.vue-flow__nodes { + position: absolute; + top: 0; + left: 0; +} + +.vue-flow__edges { + position: absolute; + inset: 0; +} + .flow-card { - min-width: 200px; - max-width: 220px; + min-width: 160px; + max-width: 160px; + background: rgba(255, 255, 255, 0.78); + border: 1px solid rgba(20, 20, 20, 0.12); + box-shadow: 0 14px 30px rgba(20, 20, 20, 0.14); } .flow-handle { @@ -699,6 +715,16 @@ html { gap: 1.5rem; } +.flow-slide .slide-main { + flex: 1; + min-height: 420px; +} + +.flow-main .flow-wrapper { + height: 100%; + min-height: 420px; +} + .glass-panel { background: rgba(255, 255, 255, 0.55); border: 1px solid rgba(255, 255, 255, 0.5); @@ -732,6 +758,12 @@ html { -webkit-backdrop-filter: blur(16px) saturate(1.05); } +.flow-card.glass-card { + background: rgba(255, 255, 255, 0.78); + border: 1px solid rgba(20, 20, 20, 0.12); + box-shadow: 0 14px 30px rgba(20, 20, 20, 0.14); +} + .iphone-frame { position: relative; width: min(330px, 90vw); @@ -774,12 +806,12 @@ html { .iphone-notch { position: absolute; - top: 10px; + top: 0; left: 50%; transform: translateX(-50%); - width: 46%; - height: 24px; - border-radius: 0 0 18px 18px; + width: 42%; + height: 26px; + border-radius: 0 0 20px 20px; background: #0b0b0e; box-shadow: 0 6px 10px rgba(0, 0, 0, 0.35); z-index: 2;