Найти в Дзене

Alt Shift App генератор сопроводительных писем — от Figma до Vercel

Alt Shift App — веб-приложение для генерации сопроводительных писем под конкретную вакансию. Пользователь заполняет короткую форму, получает сгенерированный текст и может сохранить его в списке и быстро скопировать. Я сделал это как портфолио-проект на базе тестового задания для Variant Group LLC. Цель — показать, как я довожу небольшую задачу до “production-style” качества: от дизайн-системы и UI kit до реализации генерации и деплоя, а не собрать полноценный инструмент для повседневной работы Тестовое задание я выполнял для Variant Group LLC — команды, которая делает consumer SaaS и развивает document-centric продукты (в портфеле — сервисы вокруг резюме, сопроводительных писем, PDF и шаблонов документов). Для себя я зафиксировал три требования к результату: 1. Качество UI — аккуратные состояния (loading/error/empty), консистентные компоненты, базовая доступность, предсказуемое поведение формы и результата. 2. Архитектура — понятные границы ответственности: UI отдельно от логики, миним
Оглавление

Коротко о проекте

Генерация сопроводительного письма из короткого брифа
Генерация сопроводительного письма из короткого брифа

Что это за приложение

Alt Shift App — веб-приложение для генерации сопроводительных писем под конкретную вакансию. Пользователь заполняет короткую форму, получает сгенерированный текст и может сохранить его в списке и быстро скопировать.

Для кого и зачем это сделано

Я сделал это как портфолио-проект на базе тестового задания для Variant Group LLC. Цель — показать, как я довожу небольшую задачу до “production-style” качества: от дизайн-системы и UI kit до реализации генерации и деплоя, а не собрать полноценный инструмент для повседневной работы

Контекст тестового задания

Техническое задание: создание генератора сопроводительных писем
Техническое задание: создание генератора сопроводительных писем

Компания и домен задачи: Variant Group LLC

Тестовое задание я выполнял для Variant Group LLC — команды, которая делает consumer SaaS и развивает document-centric продукты (в портфеле — сервисы вокруг резюме, сопроводительных писем, PDF и шаблонов документов).

Какие ожидания я для себя зафиксировал

Для себя я зафиксировал три требования к результату:

1. Качество UI — аккуратные состояния (loading/error/empty), консистентные компоненты, базовая доступность, предсказуемое поведение формы и результата.

2. Архитектура — понятные границы ответственности: UI отдельно от логики, минимум “магии” в компонентах, чтобы проект легко читать и расширять.

3. Воспроизводимость — стабильная сборка и одинаковый результат у любого, кто запустит проект: фиксированные зависимости/настройки форматирования и единые правила оформления кода.

Что умеет приложение

User Flow: жизненный цикл создания и управления письмом
User Flow: жизненный цикл создания и управления письмом

Основной сценарий: описать запрос → сгенерировать письмо → скопировать результат

Основной сценарий максимально простой: пользователь открывает форму, вводит данные о вакансии и о себе (например, должность, компанию и ключевые навыки), запускает генерацию и получает готовый текст сопроводительного письма. После этого результат можно быстро скопировать и использовать в отклике.

Повторная генерация и удаление

Каждая генерация сохраняется как отдельная запись в списке “applications”. К ней можно вернуться позже, перегенерировать письмо (например, с другим акцентом по навыкам или под другую вакансию) или удалить запись, если она больше не нужна. Таким образом получается простой цикл: описать → сгенерировать → при необходимости перегенерировать → скопировать/удалить.

Что сознательно НЕ делал

Я сознательно не добавлял “редактор письма” внутри приложения. Цель проекта — показать качественный flow генерации, UI-состояния и интеграцию LLM, а финальную правку текста пользователь делает уже в том инструменте, где он будет отправлять отклик (например, в документе или в форме на сайте работодателя).

Экраны и навигация

List
List

/applications — список созданных “applications”

Это главный экран со списком всех сохранённых записей. Здесь можно быстро пробежаться по уже созданным “applications”, скопировать сгенерированный текст или удалить запись, если она больше не нужна. Экран решает задачу “быстро найти и переиспользовать результат”.

New
New

/applications/new — форма и генерация

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

Details
Details

/applications/[id] — просмотр сохранённой записи и повторная генерация

Экран конкретной записи: показывает сохранённые входные данные и текущий результат. Отсюда можно сделать повторную генерацию (если нужно получить другой вариант письма) без создания новой записи “с нуля”.

Ключевые продуктовые и технические решения

Base/semantic tokens: одинаковая семантика в дизайне и коде
Base/semantic tokens: одинаковая семантика в дизайне и коде

Дизайн-система в Figma → tokens (base/semantic) в коде

Перед разработкой я собрал мини дизайн-систему в Figma и перенёс её в код через токены. Токены разделены на base (сырьевые значения: цвета, размеры) и semantic (смысловые роли: surface/text/border/accent и т.п.). Такой подход упрощает поддержку темы и снижает вероятность визуальных расхождений между макетами и реализацией

UI kit в Storybook: состояния компонентов
UI kit в Storybook: состояния компонентов

Storybook-first: сначала UI kit, затем приложение

Я начал с UI kit в Storybook и только потом собирал экраны приложения. Это позволило отдельно “запечатать” базовые компоненты и их состояния (loading/error/disabled/empty), быстро выровнять визуальную консистентность и не размазывать стили по страницам. В итоге экраны собирались из уже проверенных кирпичиков, а не из разрозненных одноразовых решений.

“Глупые” компоненты и границы ответственности

Компоненты UI kit сделаны максимально “глупыми”: они отвечают за разметку, стили и доступность, но не содержат бизнес-логики. Логика генерации, хранения и сценариев пользователя вынесена на уровень features/widgets. Это делает код читаемее, упрощает переиспользование и снижает связность: UI можно менять, не затрагивая бизнес-часть, и наоборот

Границы ответственности: где страницы, где сущности, где общий app-слой
Границы ответственности: где страницы, где сущности, где общий app-слой

Слои проекта: app , app-shared, entities

Чтобы проект было легко читать и расширять, я зафиксировал границы слоёв:

- app — маршруты, страницы и композиция виджитов в конкретные экраны (App Router).

- app-shared — то, что нужно именно приложению на верхнем уровне (например, общая конфигурация, провайдеры, layout-части), но не является частью “универсального” UI kit.

- entities — доменные сущности и их модели/типы (то, вокруг чего строятся сценарии), без привязки к конкретным страницам.

Это даёт понятную “карту” проекта: где искать данные/типы, где UI-склейку, а где инфраструктуру приложения.

Генерация на сервере + предсказуемый формат ответа
Генерация на сервере + предсказуемый формат ответа

LLM-интеграция через server actions + предсказуемый формат ответа

Генерация письма реализована через server actions, чтобы держать интеграцию с моделью на серверной стороне и не тащить лишний API-слой на клиент. Дополнительно я зафиксировал контракт ответа: модель возвращает структурированный результат в предсказуемом формате (а не произвольный “поток текста”), что упрощает обработку, отображение и обработку ошибок в UI.

Реализация

Состояния интерфейса: не только happy-path
Состояния интерфейса: не только happy-path

Модель данных и локальное хранение (Zustand + persist/localStorage)

Данные приложения хранятся локально: я использовал Zustand как простой и прозрачный state manager и подключил persist, чтобы записи не пропадали при обновлении страницы. В хранилище лежит список созданных “applications” (входные данные формы + результат генерации + метаданные вроде id/даты). Такой local-first подход для портфолио-проекта уместен: он показывает законченный пользовательский цикл без необходимости поднимать бэкенд.

UI состояния: loading / error / empty / success

Я явно продумал основные состояния интерфейса, чтобы приложение выглядело “живым”, а не набором статичных экранов:

- loading — генерация идёт, пользователь видит прогресс/блокировку действий, чтобы не отправлять форму повторно.

- error — если генерация не удалась или ответ невалиден, показывается понятная ошибка и возможность повторить.

- empty — когда данных ещё нет (например, список пустой или результат ещё не сгенерирован), интерфейс корректно направляет пользователя к следующему шагу.

- success — результат успешно получен, доступно копирование и сохранённая запись видна в списке.

Copy-to-clipboard и UX мелочи, которые доводят до “портфолио-качества

Чтобы сценарий был завершённым, я добавил copy-to-clipboard прямо в местах, где это ожидается: пользователь получает письмо и может сразу скопировать текст без промежуточных действий. Дополнительно я уделил внимание мелочам, которые обычно отличают “демо” от аккуратного проекта: понятные подписи и подсказки в форме, ограничения по длине полей, предсказуемые состояния кнопок, аккуратные пустые состояния, и консистентный визуальный язык за счёт UI kit.

Деплой и артефакты

-12

Демо на Vercel

Приложение задеплоено на Vercel — можно открыть и пройти весь сценарий: создать запись, сгенерировать письмо, и вернуться к нему из списка. Демо отражает текущее состояние проекта и позволяет оценить UX без локального запуска.

Storybook (UI kit)

Отдельно опубликован Storybook с UI kit. Там собраны базовые компоненты и их состояния — это “витрина” дизайн-системы в коде и удобный способ быстро проверить консистентность интерфейса

Репозиторий

В репозитории лежит весь исходный код, структура слоёв и история изменений. Его можно использовать как артефакт для ревью: видно, как проект организован, как разделены UI/логика, как оформлены зависимости и настройки.

Figma (design system)

В Figma собрана мини дизайн-система проекта: токены (base/semantic), стили для типографики, компоненты и состояния. Это исходная точка, от которой я синхронизировал визуальный язык с реализацией в UI kit.

Итоги

Что получилось сильным в результате

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

- Визуальная часть опирается на дизайн-систему: токены и компоненты консистентны между Figma и кодом.

- Проект хорошо читается за счёт структурирования слоёв и принципа “глупых” UI-компонентов.

- Интеграция LLM сделана с контролируемым контрактом ответа, поэтому результат предсказуемо отображается в UI.

Что бы улучшил в следующей итерации

- Добавить i18n (например, RU/EN режимы генерации и интерфейса) и унифицировать тексты.

- Улучшить качество LLM-output: тонкая настройка промпта/правил, чтобы стабильнее попадать в нужный стиль и структуру.