SPA-приложение для управления заметками и Todo, реализованное на Nuxt 3 и TypeScript.
Ключевые особенности:
- ручная история undo/redo без хранения полных снимков заметки;
- восстановление несохранённых черновиков;
- синхронизация заметок между вкладками;
- версионированное и валидируемое хранилище
localStorage; - unit-тесты стора и истории изменений;
- клавиатурное управление и собственные модальные окна;
- адаптивный интерфейс и запуск в Docker.
После локального запуска приложение доступно по адресу http://localhost:3000.
- Создание, редактирование и удаление заметок.
- Добавление, редактирование, выполнение и удаление Todo внутри заметки.
- Отмена и повтор последних 50 операций с заголовком и Todo.
- Автосохранение черновика через 300 мс после изменения.
- Предложение восстановить найденный черновик при повторном открытии редактора.
- Фильтрация пустых Todo и обязательный непустой заголовок при сохранении.
- Обновление списка заметок по событию
storageиз другой вкладки. - Подтверждение отмены редактирования и удаления заметки.
Проект разделён по ответственности:
pages/— список заметок и редактор заметки;components/notes/— карточка, форма редактора и строка Todo;components/ui/— переиспользуемые кнопки, поля, checkbox и модальное окно;store/notes.ts— состояние заметок и операции над ними в Pinia;composables/— история изменений и жизненный цикл черновика;services/— сериализация и runtime-валидация данных изlocalStorage;tests/unit/— unit-тесты ключевой логики.
Приложение работает как client-side SPA (ssr: false). Постоянное состояние хранится локально, без серверного API.
Undo/redo хранит до 50 типизированных операций, а не полные копии заметки. Каждая операция содержит только изменённое значение, его предыдущее состояние и, где нужно, позицию Todo. Новая операция очищает стек redo.
Заметки и черновики имеют собственные ключи и версию схемы. Данные после JSON.parse проходят runtime-валидацию, потому что статические типы TypeScript не защищают от повреждённого или вручную изменённого localStorage. Изменения заметок в другой вкладке подхватываются через событие storage.
Во время загрузки заметки отслеживание приостанавливается и включается только после завершения реактивной инициализации. Поэтому простое открытие редактора не создаёт ложный черновик, а реальные последующие изменения сохраняются с debounce.
Модальное окно использует нативный <dialog>, получает доступное имя, закрывается по Escape, возвращает фокус вызвавшему элементу и циклически удерживает Tab-фокус среди всех доступных интерактивных элементов.
Запуск unit-тестов:
npm testДополнительные проверки:
npm run typecheck
npm run lint
npm run buildТесты покрывают CRUD-операции и валидацию хранилища в store, а также ограничение и направления истории и сохранение позиций Todo.
Требуется Node.js и npm.
npm ci
npm run devДля production-сборки и локального просмотра:
npm run build
npm run previewdocker compose up --buildПриложение будет доступно по адресу http://localhost:3000.
Основной код приложения и архитектурные решения реализованы мной.
ChatGPT использовался как собеседник и дополнительный инструмент ревью: для преодоления «чистого листа», обсуждения архитектуры, проверки edge cases и формирования тестовых сценариев.
Codex использовался для подготовки первоначальных вариантов unit-тестов и Docker-конфигурации, а позднее — для воспроизведения замечаний ревью, подготовки точечных исправлений, дополнительных тестов и документации. Полученные изменения были проверены, адаптированы и запущены мной.
Проектирование, интеграция компонентов, реализация основной логики и принятие итоговых технических решений выполнялись мной.
Проект был выполнен в рамках технического отбора на позицию Middle Frontend Developer. После проверки я не прошёл на следующий этап, но получил обратную связь и использовал её для дополнительного аудита и улучшения проекта.