Panel klienta VulCode

Клиентская панель нашего собственного бренда: проекты, расчеты, партнерская программа с автоматической выплатой комиссии и обращения в поддержку. Ролевой контроль доступа, многоязычный интерфейс и реальные данные, а не макет. Тот же код работает как нативное приложение на телефоне.

Panel klienta VulCode
TL;DR

Клиентская панель нашего собственного бренда VulCode: проекты, расчёты, обращения и партнёрская программа в одном окне вместо писем, которые гоняют туда-сюда. Комиссии считаются в целых grosze и автоматически зачисляются в кошелёк, контроль доступа основан на ролях, а тот же код, что работает на компьютере, ставится на телефон и ведёт себя как нативное приложение.

Введение

VulCode - наш собственный бренд, поэтому эту панель мы строили для себя, и это оказалось самым трудным из возможных клиентом. Когда делаете что-то для себя, спрятаться не за кого: каждое упрощение, каждое число, которое не сходится, возвращается прямо к вам. Поэтому с самого начала этот проект должен был стать образцом того, как вообще выглядит у нас клиентская панель, а не времянкой для внутреннего пользования.

Задача звучала просто: дать клиенту одно место, где он видит всё, что его касается, и сам решает вопросы, которые раньше требовали письма. На практике это означало связать сразу несколько миров - состояние проектов, счета, обращения в поддержку и партнёрскую программу, считающую реальные деньги - в один цельный экран, который одинаково работает на столе и в кармане. Это исследование показывает, где были настоящие ловушки и как мы их обошли.

Конец письмам "где мой счёт"

До панели каждый вопрос об этапе проекта или о документе заканчивался письмом, а ответ зависел от того, кто его прочитал и помнил ли он контекст. Это не масштабируется даже на десяток клиентов: одна и та же информация жила в почтовом ящике одного человека, а клиент ждал вместо того, чтобы просто посмотреть.

Панель переворачивает это. Клиент входит и сразу видит перед собой свои проекты со статусом работ, список счетов с тем, что оплачено и что к оплате, и историю всех обращений, которые он когда-либо открывал. Ничего не нужно выискивать в письмах, потому что источник правды - одно представление, а не чья-то память.

Меньше ожидания с обеих сторон

Эффект ощутим с обеих сторон. Клиент перестаёт ждать человека, чтобы узнать то, что система и так знает. Команда перестаёт отвечать на одни и те же вопросы в сотый раз, потому что ответ уже на экране раньше, чем кто-либо его наберёт.

Шаги, чтобы проверить статус проекта (ориентировочно)

Письмом и ожиданием
5
В панели
1

Деньги, посчитанные в grosze, а не приблизительно

Партнёрская программа выглядит безобидно, пока не начнёте считать комиссии на суммах с grosze. Тут начинаются классические драмы: десять процентов от 19.99 zł, посчитанные на числе с плавающей точкой, могут дать три слегка разных результата в списке комиссий, в кошельке и на выплате, и тогда балансы перестают сходиться и панели никто не доверяет.

Поэтому по всей панели мы храним деньги как целые числа в grosze и считаем каждую комиссию на целых. Здесь никогда нет доли злотого, которая могла бы округлиться то в одну, то в другую сторону. Одна операция, одно правило округления вниз, один и тот же результат везде, где эта сумма появляется.

commission.ts · ts
type Grosze = number;

function commission(amountGrosze: Grosze, rateBps: number): Grosze {
  return Math.floor((amountGrosze * rateBps) / 10000);
}
➜
Совет

Ставку комиссии мы держим в базисных пунктах (bps), а не в процентах с запятой. Десять процентов - это 1000 bps, а не 0.1, записанное как float. Благодаря этому вся математика комиссий идёт на целых числах, от ставки до результата, и нет места, где могла бы закрасться ошибка округления.

Кто что видит и почему

Клиентская панель показывает деньги и документы, поэтому вопрос "кто вправе это видеть" не косметический. Клиент должен видеть исключительно свои данные, команда - только то, на что имеет право, и граница между ними не может зависеть от того, спрятал ли фронтенд кнопку.

Мы построили это на ролевом контроле доступа и безопасной аутентификации, но ключевое решение архитектурное: объём того, что можно увидеть и сделать, проверяет бэкенд на входе в API, а не интерфейс. Фронтенд может показывать или прятать что угодно, но если запрос касается чужих данных, API отклоняет его ещё до того, как он доберётся до базы.

Это тот же принцип, который мы применяем везде: границу стерегут там, где входят данные извне, то есть на API, и не рассчитывают на то, что пользователь не подменит идентификатор в адресе.

Один и тот же код на столе и в кармане

Клиент не сидит всё время за компьютером. Счёт он проверяет в трамвае, на уведомление об обращении реагирует с телефона. Строить для этого отдельное мобильное приложение означало бы второй код для поддержки и уверенность, что рано или поздно две версии разойдутся.

Вместо этого панель - веб-приложение, которое ставится на телефон как PWA, с манифестом и service worker. Тот же код, тот же бэкенд, та же логика комиссий. На компьютере это полноценный рабочий стол, на телефоне - иконка на главном экране и окно без панели браузера, ведущее себя как нативное приложение.

2
языка интерфейса из одного источника (PL и EN)
1
общий бэкенд для веба и телефона
0
ошибок округления на суммах комиссий

Настоящие данные с первого дня

Проще всего сделать панель, которая красиво выглядит на заранее подготовленных числах. Она выглядит красиво, пока кто-нибудь не кликнет и не окажется, что под ней ничего нет. Мы пошли обратным путём: панель с первого дня стоит на настоящем бэкенде, и каждый экран показывает то, что действительно есть в базе.

Этот выбор дороже на старте, потому что нужен работающий API прежде, чем что-либо нарисуется. Но он окупается сразу, потому что нет момента "данные подключим позже", в котором обычно всплывают все допущения, не пережившие контакта с реальностью. То, что видно в панели, - это то, что система на самом деле знает.

Стек

СлойТехнология
FrontendReact + TypeScript на Vite
BackendNode + TypeScript, SQLite
Доступроли и аутентификация на стороне API
Деньгицелые числа в grosze, ставки в bps
i18nодин источник переводов PL и EN
На телефонеPWA с манифестом и service worker

Каков итоговый результат

Клиент получает одно место, где он видит своё состояние и сам решает вопросы, которые раньше требовали письма команде. Он входит, проверяет проект, скачивает счёт, отвечает в обращении, смотрит на баланс комиссий из партнёрской программы, а если он в дороге, делает то же самое с телефона, потому что это то же приложение. Ничто из этого не зависит от того, сидит ли кто-то из команды у почтового ящика.

С нашей стороны выгода столь же конкретна. Партнёрская программа работает без того, чтобы кто-то пересчитывал что-либо вручную, потому что её стережёт код на целых числах, а не таблица, которую кто-то должен сводить. Балансы сходятся, потому что нет места, где мог бы родиться сбой округления. Команда возвращает себе время, которое раньше уходило на ответы на одни и те же вопросы.

Самое важное, однако, в том, что эта панель стала нашим образцом. Ядро, на котором она стоит, - роли, кошельки, комиссии, обращения, i18n, режим приложения - достаточно чистое, чтобы его удалось перенести на другие бренды без переписывания с нуля. Мы начали с самого трудного клиента, с самих себя, и получилась архитектура, которую потом мы лишь одевали в новые цвета.

Больше проектов

Другие работы из той же категории - посмотрите, как мы решаем похожие задачи.

Есть похожий проект?

Напишите нам - смета бесплатна и приходит в течение часа.