[{"data":1,"prerenderedAt":404},["ShallowReactive",2],{"project-case-hector":3},{"blocks":4,"person":341},[5,15,33,97,113,130,159,272,304,328,334],{"component":6,"data":7},"ProjectCaseHeader",{"index":8,"subtitle":9,"name":10,"headline":11,"description":12,"url":13,"tone":14},"02","МОБИЛЬНОЕ ПРИЛОЖЕНИЕ · ЛИЧНАЯ ОХРАНА","Hector","Приложение для iOS и Android со сборкой iOS в облаке","Приложение для заказа личной охраны и машины с водителем: live-трекинг команды на карте и кнопка SOS. Сервис работает в Европе и ОАЭ.","https:\u002F\u002Fhectorvip.com\u002F","primary",{"component":16,"data":17},"ProjectCaseCover",{"image":18,"imageMobile":19,"imageAlt":31,"frame":32},null,{"src":20,"width":21,"height":22,"variants":23},"\u002F_nuxt\u002Fpreview-mobile.b4yGJ-pG.webp",600,1304,[24,27,30],{"src":25,"width":26},"\u002F_nuxt\u002Fpreview-mobile.CJLUUGVg.webp",160,{"src":28,"width":29},"\u002F_nuxt\u002Fpreview-mobile.BAQF9lQh.webp",300,{"src":20,"width":21},"Экраны приложения Hector: выбор охраны, live-трекинг команды, проверка заказа и экран SOS","devices",{"component":34,"data":35},"ProjectCaseMeta",{"tags":36,"stack":39},[37,38],"Мобильное приложение","Тимлид",[40,43,45,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,77,79,81,83,85,87,89,91,93,95],{"label":41,"own":42},"Vue 3",true,{"label":44,"own":42},"TypeScript",{"label":46,"own":42},"Vite",{"label":48,"own":42},"Capacitor 8",{"label":50,"own":42},"Pinia",{"label":52,"own":42},"Vue Router",{"label":54,"own":42},"vue-i18n",{"label":56,"own":42},"VueUse",{"label":58,"own":42},"Tailwind CSS 4",{"label":60,"own":42},"vee-validate",{"label":62,"own":42},"zod",{"label":64,"own":42},"maska",{"label":66,"own":42},"Vitest",{"label":68,"own":42},"Vue Test Utils",{"label":70,"own":42},"Storybook",{"label":72,"own":42},"oxlint",{"label":74,"own":42},"ESLint",{"label":76,"own":42},"oxfmt",{"label":78,"own":42},"Husky",{"label":80,"own":42},"mise",{"label":82},"Leaflet",{"label":84},"OpenStreetMap",{"label":86},"lottie-web",{"label":88},"View Transitions API",{"label":90},"miragejs",{"label":92},"Codemagic",{"label":94},"TestFlight",{"label":96},"BrowserStack",{"component":98,"data":99},"ProjectCaseFacts",{"facts":100},[101,104,107,110],{"key":102,"value":103},"role","Frontend Team Lead",{"key":105,"value":106},"team","15 человек: 5 frontend, 4 backend, 2 дизайнера, 2 QA, PM и DevOps",{"key":108,"value":109},"duration","С июня 2026",{"key":111,"value":112},"status","В разработке, демо-сборка для заказчика",{"component":114,"data":115},"ProjectCaseRole",{"id":102,"name":116,"items":117},"Что сделал я",[118,121,124,127],{"title":119,"text":120},"Архитектура и стек","Выбрал Vue 3 и Capacitor вместо React Native, Flutter и натива. Написал регламенты: Tailwind без отдельных стилей, весь текст через i18n, без as и комментариев, тесты в формате AAA.",{"title":122,"text":123},"Старт проекта","Заложил проект: Capacitor с live-reload, Tailwind, Storybook, линтеры, pre-push, окружение через mise, README с запуском в эмуляторе и симуляторе.",{"title":125,"text":126},"Код","Колёса выбора времени и даты с бесшовной циклической прокруткой, маска телефона, аккордеон, i18n с локалью устройства, первые версии экранов проверки заказа и активного заказа.",{"title":128,"text":129},"Команда и заказчик","Ставил задачи и приоритеты ещё четырём разработчикам, ревьюил и сливал PR. Анимации, iOS-часть, сигналы и карты они писали по моим задачам. Приложение заказчику презентовал сам.",{"component":131,"data":132},"ProjectCaseChapter",{"id":133,"name":134,"blocks":135},"context","Контекст",[136,148],{"component":131,"data":137},{"id":138,"name":139,"blocks":140},"business","Бизнес",[141,145],{"component":142,"data":143},"ProjectCaseText",{"text":144},"Hector — сервис личной охраны и машин с водителем в Европе и ОАЭ. Клиент заказывает охрану или машину, оплачивает заказ, следит за командой на карте, а в опасной ситуации нажимает SOS.",{"component":142,"data":146},{"text":147},"У агентов охраны свой кабинет: дежурство, назначенные заказы и маршрут, отметка о прибытии, SOS-тревоги клиентов.",{"component":131,"data":149},{"id":150,"name":151,"blocks":152},"situation","Ситуация",[153,156],{"component":142,"data":154},{"text":155},"Приложения не было: это новый продукт по ТЗ заказчика, разработка с нуля с июня 2026.",{"component":142,"data":157},{"text":158},"Заказчику нужно, чтобы всё работало на iOS и Android. Команда работает удалённо из разных городов, каждый на своём рабочем компьютере, а iPhone и Mac не входят в основной набор устройств разработчика. Поэтому сборку и проверку iOS нужно было организовать так, чтобы они не зависели от устройств на руках. Вдобавок приложение работает с API устройства, например реагирует на двойной стук по задней крышке iPhone.",{"component":131,"data":160},{"id":161,"name":162,"blocks":163},"process","Процесс, архитектура и решения",[164,185,193,225,236,244],{"component":131,"data":165},{"id":166,"name":167,"blocks":168},"flow","Путь заказа",[169],{"component":170,"data":171},"ProjectCaseSteps",{"steps":172},[173,176,179,182],{"title":174,"text":175},"Конфигуратор","Охрана: число агентов, с оружием или без, дресс-код, кинолог K9, язык. Или машина с водителем. Маршрут, дата, длительность. В разработке: состояние заказа в Pinia, колёса выбора даты и времени с бесшовной прокруткой, поиск адреса через Photon и Nominatim, чтобы клиент собрал заказ на одном экране за пару минут",{"title":177,"text":178},"Проверка","Клиент проверяет заказ и отправляет его менеджеру. В разработке: экран проверки заказа и маска телефона, чтобы менеджер получал заказ без ошибок в контактах",{"title":180,"text":181},"Оплата","Счёт с разбивкой, таймлайн статусов, заказ можно отредактировать. В разработке: экран активного заказа с таймлайном статусов. При правке заказа клиент возвращается в конфигуратор с сохранёнными данными и не собирает заказ заново",{"title":183,"text":184},"Live-трекинг","Команда на карте с ETA, кнопка SOS под рукой. В разработке: карта на Leaflet и маршрут через OSRM. Кнопка SOS закреплена на экране, чтобы в опасной ситуации её не пришлось искать",{"component":131,"data":186},{"id":187,"name":188,"blocks":189},"stack-choice","Почему Vue и Capacitor",[190],{"component":142,"data":191},{"text":192},"Стек выбирали по сильным сторонам команды. Фронтенд-разработчики уверенно работают с Vue, и я сам хорошо знаю его экосистему. Поэтому команда с первого дня писала продукт, а не осваивала новый инструмент: это сократило путь до демо и снизило риск ошибок. У связки Vue и Capacitor есть и другие преимущества: один код для iOS и Android, интерфейс на обычном CSS, на котором сложный дизайн Hector с градиентами, стеклом и прозрачностями делается точно по макету, и доступ к возможностям телефона через плагины: биометрии, защищённому хранилищу, системным кнопкам. React Native и Flutter тоже рассматривали, но они дали бы меньше свободы в вёрстке и потребовали бы времени на освоение.",{"component":131,"data":194},{"id":195,"name":196,"blocks":197},"architecture","Архитектура",[198,222],{"component":199,"data":200},"ProjectCaseArchitecture",{"items":201},[202,206,210,218],{"label":203,"title":204,"text":205},"APP","Vue 3 SPA","В оболочке Capacitor на Android и iOS. Pinia хранит заказ, конфигуратор, назначенные заказы и сигналы.",{"label":207,"title":208,"text":209},"NATIVE","Нативные плагины","Биометрия, secure storage для PIN, клавиатура, системная кнопка «Назад», safe area под вырезы iPhone.",{"label":211,"title":212,"text":213,"code":214},"API","REST бэкенда","Свой HTTP-клиент с интерцепторами. В демо API подменяет мок-сервер на miragejs, который повторяет контракт.",[215,216,217],"auth","configurator","orders",{"label":219,"title":220,"text":221},"MAPS","Карты","OpenStreetMap: Leaflet для карты и live-трекинга, Photon и Nominatim для поиска адресов, OSRM для маршрута.",{"component":142,"data":223},{"text":224},"Весь текст идёт через vue-i18n, локаль берётся с устройства, поэтому новый язык добавляется файлом. Мок-сервер повторяет контракт API, и фронтенд с демо не ждут бэкенд.",{"component":131,"data":226},{"id":227,"name":228,"blocks":229},"signals","Тихие сигналы",[230,233],{"component":142,"data":231},{"text":232},"Клиент назначает действие на двойной и тройной стук по задней крышке iPhone и на четыре кнопки виджета экрана блокировки. Для каждого сигнала задаётся, кому он уходит, что сделать и за какое время отреагировать: охраннику подойти, водителю подать машину, диспетчерской просто узнать.",{"component":142,"data":234},{"text":235},"Приложение ловит сигнал через URL-схему, в том числе при холодном старте. Пока это прототип, в демо-сборке он скрыт.",{"component":131,"data":237},{"id":238,"name":239,"blocks":240},"security","Безопасность данных",[241],{"component":142,"data":242},{"text":243},"Вход в приложение защищён как в банковских приложениях: PIN-кодом, Face ID или отпечатком пальца. PIN хранится в защищённой части телефона, куда нет доступа другим приложениям. Если сессия истекла, приложение само возвращает на экран входа, и посторонний не сможет продолжить работу в чужом аккаунте. Клиенты и агенты охраны видят только свои разделы.",{"component":131,"data":245},{"id":246,"name":247,"blocks":248},"quality","Качество и релизы",[249,252,263,266,269],{"component":142,"data":250},{"text":251},"Vitest и Vue Test Utils: 177 файлов, около 1700 тест-кейсов рядом с кодом, включая сервисы сессии, PIN, safe area и клавиатуры. Storybook с проверкой доступности. E2E на Playwright пишет другой разработчик по сценариям демо: вход, заказ от конфигуратора до завершения, SOS, профиль, кабинет агента.",{"component":253,"data":254},"ProjectCasePipeline",{"stages":255},[256,259,261],{"name":257,"next":258},"pre-push","тесты и линт изменённых файлов",{"name":92,"next":260},"сборка iOS в облаке",{"name":94,"next":262},"приложение на iPhone",{"component":142,"data":264},{"text":265},"Pre-push с тестами и линтом и сборку iOS в Codemagic настроил я. Android собирается подписанным bundle из Android Studio.",{"component":142,"data":267},{"text":268},"Работаем недельными спринтами с демо в конце каждого. Задачи в Jira, ветка на задачу, PR с ревью.",{"component":142,"data":270},{"text":271},"Для заказчика такой процесс означает предсказуемость. Каждую неделю он видит рабочую версию на демо и может поправить курс раньше, чем ошибка станет дорогой. Облачная сборка iOS и TestFlight позволяют в любой момент показать приложение на настоящем iPhone. Для разработки тесты рядом с кодом и проверка перед пушем ловят ошибки ещё до ревью, а пять разработчиков меняют общий код параллельно и не ломают работу друг друга.",{"component":131,"data":273},{"id":274,"name":275,"blocks":276},"challenges","Сложности",[277],{"component":278,"data":279},"ProjectCaseChallenges",{"items":280},[281,294],{"id":282,"name":283,"rows":284},"ios","iOS-сборка в распределённой команде",[285,288,291],{"key":286,"text":287},"problem","Всё должно работать на iOS. Команда распределена по разным городам и работает на своих устройствах, а iPhone и Mac для сборки и отладки в рабочий набор разработчика не входят. Нужен был способ собирать и проверять iOS без привязки к конкретному устройству.",{"key":289,"text":290},"solution","Сборку iOS вынесли в облако Codemagic: вариант под симулятор и подписанный .ipa с выгрузкой в TestFlight. Нативный проект патчится скриптом на каждой сборке, иначе приложение из Xcode 27+ закрывается сразу после запуска. Тестировали на облачных устройствах BrowserStack и через TestFlight.",{"key":292,"text":293},"result","Приложение дошло до демо под iOS и Android. В демо-сборке закрыты баги, которые видно только на iPhone: двойной тап приближал экран, шапка заезжала под строку состояния, низ шторки уходил под полоску «домой».",{"id":295,"name":296,"rows":297},"visual","Сложный визуал в WebView",[298,300,302],{"key":286,"text":299},"Градиенты, прозрачности, стеклянная шапка, шторка-параллакс, переходы между экранами: на iOS такой визуал постоянно ломается.",{"key":289,"text":301},"Интро сделали на Lottie вместо видео: видео много весило и теряло качество. Переходы построили на View Transitions API. При подготовке демо собрали визуальные баги в один список и закрывали по одному.",{"key":292,"text":303},"Из примерно 20 визуальных багов демо закрыты все, кроме одного. Интерфейс в WebView выглядит как нативное приложение.",{"component":131,"data":305},{"id":306,"name":307,"blocks":308},"results","Метрики и результаты",[309,325],{"component":310,"data":311},"ProjectCaseResults",{"stats":312},[313,316,319,322],{"value":314,"label":315},"26","экранов для клиента и агента",{"value":317,"label":318},"~1700","тест-кейсов в 177 файлах",{"value":320,"label":321},"55","переиспользуемых компонентов",{"value":323,"label":324},"2","платформы из одного кода: iOS и Android",{"component":142,"data":326},{"text":327},"Приложение доведено до демо-сборки для заказчика. Бизнес-метрик пока нет: оно ещё не вышло в релиз.",{"component":329,"data":330},"ProjectCaseCta",{"id":331,"title":332,"text":333,"url":13},"cta","Нужно мобильное приложение для iOS и Android со сложным дизайном?","Подберу стек под команду, заложу проект и доведу приложение до демо на обеих платформах.",{"component":335,"data":336},"ProjectCaseNext",{"slug":337,"name":338,"index":339,"tone":340},"brendmart","Brendmart","03","secondary",{"name":342,"alternateNames":343,"role":346,"skills":347},"Михаил Малахов",[344,345],"Michael Malakhov","Mikhail Malakhov","Fullstack Developer",[348,44,349,350,351,352,353,354,355,52,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,46,375,376,377,378,379,380,66,381,382,383,70,384,385,386,387,388,389,390,391,392,50,393,394,395,396,397,398,399,400,401,402,403],"JavaScript (ES6+)","HTML5","CSS3","Sass","Vue.js (Vue 2\u002FVue 3, Composition API)","Nuxt 3\u002F4","React","Next.js","Tailwind CSS","Bootstrap","SPA","SSR","MPA","SEO","Chrome DevTools","Node.js","NestJS","Express","Laravel","Rust","PostgreSQL","Redis","REST API","Docker","Nginx","CI\u002FCD","GitHub Actions","Webpack","Git","GitHub","GitLab","Figma","Redux","Jest","Tauri","Prometheus","Jira","Claude Code","Codex","Gemini","MCP","Atlassian Rovo","Zustand","SWR","TanStack Query","Vuex","React Hook Form","Zod","Capacitor","GraphQL","WebSocket","Clinic.js","autocannon","Lighthouse","GitHub Copilot","AI agents",1793309466836]