Загрузить файлы в «/»

main
MadHowl 2026-04-08 04:59:53 +00:00
parent d86abfe469
commit b7c9d8491f
5 changed files with 2010 additions and 0 deletions

347
task06-event-manager.md Normal file
View File

@ -0,0 +1,347 @@
# Техническое задание: Система управления мероприятиями (Event Manager)
## 1. Описание проекта
Веб-платформа для создания и продвижения мероприятий с регистрацией участников, расписанием и обсуждениями. Проект закрепляет навыки CRUD, валидации дат и вместимости, разграничения прав и совместной работы через Git.
**Команда:** 23 человека
---
## 2. Функциональные требования
### 2.1. Аутентификация и регистрация
- Регистрация, вход, выход (Laravel Breeze)
### 2.2. Каталог мероприятий
- Просмотр списка мероприятий с пагинацией
- Фильтрация по категориям (конференции, воркшопы, вебинары, митапы)
- Фильтрация по дате (сегодня, неделя, месяц, будущие)
- Поиск по названию и описанию
- Детальная страница мероприятия (обложка, описание, место, дата, организатор, список участников, комментарии)
### 2.3. Управление мероприятиями (организатор/админ)
- CRUD мероприятий: название, описание, обложка, категория, дата начала/окончания, место (адрес или онлайн-ссылка), вместимость, стоимость (бесплатно/платно)
- Загрузка обложки мероприятия (валидация: jpg/png, макс. 2MB)
- Управление участниками (просмотр списка, отмена регистрации участника)
### 2.4. Регистрация на мероприятие
- Кнопка «Зарегистрироваться» на странице мероприятия
- **Валидация:** проверка на наличие свободных мест (если вместимость ограничена)
- **Валидация:** пользователь не может зарегистрироваться дважды
- Отмена регистрации (пользователь — до начала мероприятия)
- Список участников мероприятия (публичный)
### 2.5. Комментарии и обсуждения
- Добавление комментариев к мероприятию
- Удаление своего комментария
- Удаление любого комментария (админ/организатор)
- Пагинация комментариев
### 2.6. Панель организатора
- Мои мероприятия (созданные мной)
- Статистика по каждому: количество зарегистрированных, свободные места
- Список участников с возможностью экспорта
- Управление комментариями
### 2.7. Панель участника
- Мои мероприятия (на которые зарегистрирован)
- Прошедшие мероприятия (история)
- Рекомендации (похожие мероприятия)
### 2.8. Панель администратора
- Управление пользователями и ролями
- Модерация мероприятий (скрыть/показать)
- Общая статистика платформы
---
## 3. Структура базы данных
### Таблица `users`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| name | VARCHAR(255) | Имя |
| email | VARCHAR(255) | Email (unique) |
| password | VARCHAR(255) | Хеш пароля |
| role | ENUM('admin', 'organizer', 'participant') | Роль |
| avatar | VARCHAR(255) \| NULL | Аватар |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `categories`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| name | VARCHAR(255) | Название категории |
| slug | VARCHAR(255) | URL-идентификатор (unique) |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `events`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| title | VARCHAR(255) | Название мероприятия |
| slug | VARCHAR(255) | URL-идентификатор (unique) |
| description | TEXT | Описание |
| cover_image | VARCHAR(255) \| NULL | Обложка |
| category_id | BIGINT UNSIGNED | Категория (FK → categories) |
| organizer_id | BIGINT UNSIGNED | Организатор (FK → users) |
| start_datetime | DATETIME | Дата и время начала |
| end_datetime | DATETIME | Дата и время окончания |
| location | VARCHAR(255) | Место проведения (адрес или «Онлайн») |
| location_url | VARCHAR(255) \| NULL | Ссылка для онлайн-мероприятий |
| capacity | INT \| NULL | Вместимость (NULL = без ограничений) |
| price | DECIMAL(10, 2) \| NULL | Стоимость (NULL = бесплатно) |
| is_published | BOOLEAN | Опубликован ли |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `event_user` (регистрация на мероприятие)
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| event_id | BIGINT UNSIGNED | FK → events |
| user_id | BIGINT UNSIGNED | FK → users |
| registered_at | TIMESTAMP | Дата регистрации |
| attended | BOOLEAN | Присутствовал ли |
### Таблица `comments`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| body | TEXT | Текст комментария |
| user_id | BIGINT UNSIGNED | Автор (FK → users) |
| event_id | BIGINT UNSIGNED | Мероприятие (FK → events) |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
---
## 4. Маршруты и контроллеры
```php
// Публичные маршруты
Route::get('/', [EventController::class, 'index'])->name('events.index');
Route::get('/events/{event:slug}', [EventController::class, 'show'])->name('events.show');
// Аутентифицированные маршруты
Route::middleware('auth')->group(function () {
// Регистрация на мероприятие
Route::post('/events/{event}/register', [EventController::class, 'register'])->name('events.register');
Route::post('/events/{event}/unregister', [EventController::class, 'unregister'])->name('events.unregister');
// Комментарии
Route::resource('events.comments', CommentController::class)->scoped(['events' => 'event'])->only(['store', 'destroy']);
// Мои мероприятия (участник)
Route::get('/my-events', [ParticipantController::class, 'index'])->name('participant.events');
// Панель организатора
Route::prefix('organizer')->name('organizer.')->middleware('can:be-organizer')->group(function () {
Route::resource('events', OrganizerEventController::class);
Route::get('events/{event}/attendees', [OrganizerEventController::class, 'attendees'])->name('events.attendees');
Route::post('events/{event}/attendees/{user}/remove', [OrganizerEventController::class, 'removeAttendee'])->name('events.attendees.remove');
Route::get('dashboard', [OrganizerDashboardController::class, 'index'])->name('dashboard');
});
// Админка
Route::prefix('admin')->name('admin.')->middleware('can:access-admin-panel')->group(function () {
Route::resource('users', AdminUserController::class);
Route::patch('events/{event}/toggle-publish', [AdminEventController::class, 'togglePublish'])->name('events.publish');
Route::get('dashboard', [AdminDashboardController::class, 'index'])->name('dashboard');
});
});
```
### Контроллеры
- `EventController` — публичный каталог, регистрация/отмена
- `CommentController` — создание/удаление комментариев
- `ParticipantController` — мои мероприятия (участник)
- `OrganizerEventController` — CRUD мероприятий организатором
- `OrganizerDashboardController` — панель организатора
- `AdminUserController`, `AdminEventController`, `AdminDashboardController` — админка
---
## 5. Роли, Gates и Policies
### Роли
| Роль | Описание |
|---|---|
| `admin` | Полный доступ, модерация мероприятий, управление пользователями |
| `organizer` | Создание/редактирование своих мероприятий, просмотр участников |
| `participant` | Регистрация на мероприятия, комментарии, просмотр расписания |
### Gates
```php
Gate::define('be-organizer', function (User $user) {
return in_array($user->role, ['admin', 'organizer']);
});
Gate::define('access-admin-panel', function (User $user) {
return $user->role === 'admin';
});
```
### Policies
**EventPolicy:**
- `view` — все (если опубликован), организатор/admin (если не опубликован)
- `create` — organizer или admin
- `update`, `delete` — организатор мероприятия или admin
- `register` — participant (не зарегистрирован на это мероприятие, есть свободные места)
**CommentPolicy:**
- `create` — все аутентифицированные
- `delete` — автор комментария, организатор мероприятия или admin
---
## 6. Требования к интерфейсу (Bootstrap 5)
### Компоненты
- **Навбар** — логотип, каталог, «Мои мероприятия», профиль
- **Карточки мероприятий** — обложка, название, дата (бейдж), категория (бейдж), вместимость
- **Страница мероприятия** — большая обложка, описание, место, дата, кнопка регистрации, список участников, комментарии
- **Модальное окно** — быстрая регистрация (подтверждение)
- **Таблица участников** — имя, email, дата регистрации, кнопка удаления (организатор)
- **Форма мероприятия** — Bootstrap-форма с загрузкой обложки, datetime-local для дат
- **Дашборд организатора** — статистика (карточки), таблица мероприятий
- **Адаптивная сетка** — `col-md-4` для карточек, `col-md-8` + `col-md-4` для основного контента и сайдбара
### Цветовая схема категорий
- Конференция — `bg-primary`
- Воркшоп — `bg-success`
- Вебинар — `bg-info`
- Митап — `bg-warning`
---
## 7. Git-workflow для команды
### Распределение модулей (для 2 человек)
| Участник | Модуль | Ветки |
|---|---|---|
| **Участник A** | Мероприятия, категории, обложки, регистрация | `feature/events-crud`, `feature/categories`, `feature-event-covers` |
| **Участник B** | Участники, комментарии, панель организатора, UI | `feature/event-registration`, `feature-attendees`, `feature-capacity-validation` |
### Распределение модулей (для 3 человек)
| Участник | Модуль | Ветки |
|---|---|---|
| **Участник A** | Мероприятия, категории, загрузка обложек | `feature/events-crud`, `feature/categories`, `feature-event-covers` |
| **Участник B** | Регистрация, участники, валидация вместимости | `feature/event-registration`, `feature-attendees`, `feature-capacity-validation` |
| **Участник C** | Комментарии, панель организатора, каталог, UI | `feature/comments`, `feature-organizer-panel`, `feature-event-catalog-ui` |
### Правила
1. Ветка `develop` — основная ветка разработки
2. Каждый участник создаёт фич-ветки от `develop`
3. Минимум 3 PR на участника
4. Обязательный код-ревью перед мёржем
5. Conventional Commits
### Визуализация workflow (2 участника)
```mermaid
gitGraph
commit id: "init" tag: "v1.0"
branch develop
checkout develop
branch feature/events-crud
checkout develop
branch feature/event-registration
checkout feature/events-crud
commit id: "feat: add events CRUD"
commit id: "feat: add event categories"
checkout feature/event-registration
commit id: "feat: add event registration"
commit id: "feat: add attendees management"
checkout develop
merge feature/events-crud tag: "PR + review"
merge feature/event-registration tag: "PR + review"
checkout main
merge develop tag: "release v1.1"
```
### Визуализация workflow (3 участника)
```mermaid
gitGraph
commit id: "init" tag: "v1.0"
branch develop
checkout develop
branch feature/events-crud
checkout develop
branch feature/event-registration
checkout develop
branch feature/comments
checkout feature/events-crud
commit id: "feat: add events CRUD"
commit id: "feat: add event covers"
checkout feature/event-registration
commit id: "feat: add event registration"
commit id: "feat: add attendees management"
checkout feature/comments
commit id: "feat: add event comments"
commit id: "feat: add organizer panel"
checkout develop
merge feature/events-crud tag: "PR + review"
merge feature/event-registration tag: "PR + review"
merge feature/comments tag: "PR + review"
checkout main
merge develop tag: "release v1.1"
```
---
## 8. Критерии приёмки
### Обязательно
- [ ] CRUD мероприятий с загрузкой обложки
- [ ] CRUD категорий мероприятий
- [ ] Регистрация на мероприятие с валидацией вместимости
- [ ] Отмена регистрации (до начала мероприятия)
- [ ] Список участников мероприятия
- [ ] Комментарии к мероприятиям (создание + удаление)
- [ ] Панель организатора (мои мероприятия, статистика, участники)
- [ ] Policies: участник не может редактировать мероприятия, организатор не может редактировать чужие мероприятия
- [ ] Адаптивная Bootstrap-вёрстка
- [ ] Flash-сообщения
- [ ] Пагинация каталога и комментариев
- [ ] Git-история: минимум 3 ветки на участника, PR с ревью
### Дополнительно (бонусные баллы)
- [ ] Поиск мероприятий по названию/описанию
- [ ] Фильтрация по дате (сегодня, неделя, месяц)
- [ ] Экспорт списка участников в CSV
- [ ] Рейтинг мероприятия (после завершения)
- [ ] Похожие мероприятия (рекомендации)
- [ ] Уведомления (email при регистрации / напоминание)
- [ ] Soft Deletes для мероприятий
---
## 9. Дополнительные задания (для продвинутых)
1. **Билеты:** генерация PDF-билета с QR-кодом при регистрации
2. **Уведомления:** email при регистрации и напоминание за день до мероприятия
3. **Онлайн-мероприятия:** интеграция с Zoom/Jitsi (сохранение ссылки, автоматическая отправка)
4. **Тесты:** PHPUnit Feature-тесты для EventController (регистрация, валидация вместимости)
5. **API:** RESTful API для каталога мероприятий
6. **Queue:** асинхронная отправка email-уведомлений
7. **Events & Listeners:** событие «регистрация на мероприятие» → отправка email
---
## 10. Рекомендуемые материалы
- Laravel Docs: https://laravel.com/docs/12.x
- Laravel File Uploads: https://laravel.com/docs/12.x/filesystem#file-uploads
- Bootstrap 5 Cards: https://getbootstrap.com/docs/5.3/components/card/
- Bootstrap 5 Modal: https://getbootstrap.com/docs/5.3/components/modal/
- Laravel Policies: https://laravel.com/docs/12.x/authorization#creating-policies

400
task07-recipe-catalog.md Normal file
View File

@ -0,0 +1,400 @@
# Техническое задание: Каталог рецептов с рейтингом
## 1. Описание проекта
Кулинарная платформа для публикации рецептов, комментирования и оценки блюд. Проект закрепляет навыки CRUD, отношений многие-ко-многим (рецепт ↔ ингредиенты), расчёта среднего рейтинга, разграничения прав и совместной работы через Git.
**Команда:** 23 человека
---
## 2. Функциональные требования
### 2.1. Аутентификация и регистрация
- Регистрация, вход, выход (Laravel Breeze)
### 2.2. Каталог рецептов
- Просмотр списка рецептов с пагинацией
- Фильтрация по категориям (завтрак, обед, ужин, выпечка, десерты, напитки)
- Поиск по названию и ингредиентам
- Сортировка по дате добавления, рейтингу, времени приготовления
- Детальная страница рецепта
### 2.3. Управление рецептами (автор/админ)
- CRUD рецептов: название, описание, категория, время приготовления, количество порций, изображение, сложность
- Пошаговое приготовление (несколько шагов, каждое — отдельная запись)
- Привязка ингредиентов с количеством и единицей измерения
- Загрузка изображения рецепта (валидация: jpg/png, макс. 2MB)
### 2.4. Ингредиенты
- Справочник ингредиентов (название, единица измерения по умолчанию)
- CRUD ингредиентов (админ)
- Привязка к рецептам (многие-ко-многим) с указанием количества и единицы
### 2.5. Комментарии
- Добавление комментариев к рецепту
- Удаление своего комментария
- Удаление любого комментария (админ/автор рецепта)
- Пагинация комментариев
### 2.6. Оценки и рейтинг
- Оценка рецепта (15 звёзд) — один голос на пользователя
- Отображение среднего рейтинга на карточке и странице рецепта
- Автоматический пересчёт среднего рейтинга
### 2.7. Избранное
- Добавление рецепта в избранное
- Список избранных рецептов (личный кабинет)
### 2.8. Личный кабинет автора
- Мои рецепты
- Статистика: количество рецептов, средний рейтинг, количество комментариев
- Избранные рецепты
### 2.9. Панель администратора
- Управление пользователями и ролями
- CRUD ингредиентов (справочник)
- Модерация рецептов и комментариев
- Общая статистика
---
## 3. Структура базы данных
### Таблица `users`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| name | VARCHAR(255) | Имя |
| email | VARCHAR(255) | Email (unique) |
| password | VARCHAR(255) | Хеш пароля |
| role | ENUM('admin', 'author', 'user') | Роль |
| avatar | VARCHAR(255) \| NULL | Аватар |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `categories`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| name | VARCHAR(255) | Название категории |
| slug | VARCHAR(255) | URL-идентификатор (unique) |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `recipes`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| title | VARCHAR(255) | Название рецепта |
| slug | VARCHAR(255) | URL-идентификатор (unique) |
| description | TEXT | Краткое описание |
| image | VARCHAR(255) \| NULL | Изображение |
| category_id | BIGINT UNSIGNED | Категория (FK → categories) |
| author_id | BIGINT UNSIGNED | Автор (FK → users) |
| cooking_time | INT | Время приготовления (минуты) |
| servings | INT | Количество порций |
| difficulty | ENUM('easy', 'medium', 'hard') | Сложность |
| rating | DECIMAL(3, 2) \| NULL | Средний рейтинг (0.005.00) |
| rating_count | INT | Количество оценок |
| is_published | BOOLEAN | Опубликован ли |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `recipe_steps`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| recipe_id | BIGINT UNSIGNED | Рецепт (FK → recipes) |
| step_number | INT | Порядковый номер шага |
| description | TEXT | Описание шага |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `ingredients`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| name | VARCHAR(255) | Название ингредиента (unique) |
| default_unit | VARCHAR(50) | Единица измерения по умолчанию (г, мл, шт, ст.л.) |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `ingredient_recipe` (многие-ко-многим)
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| recipe_id | BIGINT UNSIGNED | FK → recipes |
| ingredient_id | BIGINT UNSIGNED | FK → ingredients |
| quantity | DECIMAL(10, 2) | Количество |
| unit | VARCHAR(50) | Единица измерения |
### Таблица `comments`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| body | TEXT | Текст комментария |
| user_id | BIGINT UNSIGNED | Автор (FK → users) |
| recipe_id | BIGINT UNSIGNED | Рецепт (FK → recipes) |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `ratings`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| user_id | BIGINT UNSIGNED | FK → users |
| recipe_id | BIGINT UNSIGNED | FK → recipes |
| rating | TINYINT | Оценка (15) |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP |
### Таблица `recipe_user` (избранное)
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| user_id | BIGINT UNSIGNED | FK → users |
| recipe_id | BIGINT UNSIGNED | FK → recipes |
| added_at | TIMESTAMP | |
---
## 4. Маршруты и контроллеры
```php
// Публичные маршруты
Route::get('/', [RecipeController::class, 'index'])->name('recipes.index');
Route::get('/recipes/{recipe:slug}', [RecipeController::class, 'show'])->name('recipes.show');
// Аутентифицированные маршруты
Route::middleware('auth')->group(function () {
// Оценки
Route::post('/recipes/{recipe}/rate', [RatingController::class, 'store'])->name('recipes.rate');
// Избранное
Route::get('/favorites', [FavoriteController::class, 'index'])->name('favorites.index');
Route::post('/recipes/{recipe}/favorite', [FavoriteController::class, 'toggle'])->name('recipes.favorite');
// Комментарии
Route::resource('recipes.comments', CommentController::class)->scoped(['recipes' => 'recipe'])->only(['store', 'destroy']);
// Мои рецепты (автор)
Route::prefix('my-recipes')->name('my-recipes.')->middleware('can:be-author')->group(function () {
Route::resource('recipes', AuthorRecipeController::class);
Route::get('dashboard', [AuthorDashboardController::class, 'index'])->name('dashboard');
});
// Админка
Route::prefix('admin')->name('admin.')->middleware('can:access-admin-panel')->group(function () {
Route::resource('users', AdminUserController::class);
Route::resource('ingredients', AdminIngredientController::class);
Route::patch('recipes/{recipe}/toggle-publish', [AdminRecipeController::class, 'togglePublish'])->name('recipes.publish');
Route::get('dashboard', [AdminDashboardController::class, 'index'])->name('dashboard');
});
});
```
### Контроллеры
- `RecipeController` — публичный каталог и детальный просмотр
- `AuthorRecipeController` — CRUD рецептов автором
- `RatingController` — добавление/обновление оценки
- `FavoriteController` — управление избранным
- `CommentController` — создание/удаление комментариев
- `AuthorDashboardController` — панель автора
- `AdminUserController`, `AdminIngredientController`, `AdminRecipeController`, `AdminDashboardController` — админка
---
## 5. Роли, Gates и Policies
### Роли
| Роль | Описание |
|---|---|
| `admin` | Полный доступ, модерация, CRUD ингредиентов, управление пользователями |
| `author` | Создание/редактирование своих рецептов, управление комментариями к своим рецептам |
| `user` | Просмотр каталога, комментирование, оценка, добавление в избранное |
### Gates
```php
Gate::define('be-author', function (User $user) {
return in_array($user->role, ['admin', 'author']);
});
Gate::define('access-admin-panel', function (User $user) {
return $user->role === 'admin';
});
```
### Policies
**RecipePolicy:**
- `view` — все (если опубликован), автор/admin (если не опубликован)
- `create` — author или admin
- `update`, `delete` — автор рецепта или admin
- `rate` — user (ещё не оценивал этот рецепт)
**CommentPolicy:**
- `create` — все аутентифицированные
- `delete` — автор комментария, автор рецепта или admin
**IngredientPolicy:**
- `view` — все
- `create`, `update`, `delete` — только admin
---
## 6. Требования к интерфейсу (Bootstrap 5)
### Компоненты
- **Навбар** — логотип, каталог, «Избранное», «Мои рецепты», профиль
- **Карточки рецептов** — изображение, название, категория (бейдж), время (иконка + текст), рейтинг (звёзды)
- **Страница рецепта** — большое изображение, описание, ингредиенты (таблица), шаги приготовления (нумерованный список), комментарии, форма оценки
- **Звёздный рейтинг** — интерактивные звёзды (radio buttons + стилизация)
- **Форма рецепта** — Bootstrap-форма с загрузкой изображения, динамическое добавление шагов и ингредиентов
- **Таблица ингредиентов** — название, количество, единица измерения
- **Дашборд автора** — статистика (карточки), список моих рецептов
- **Адаптивная сетка** — `col-md-4` для карточек, `col-md-8` + `col-md-4` для основного контента и сайдбара
### Цветовая схема сложности
- Лёгкий — `bg-success`
- Средний — `bg-warning`
- Сложный — `bg-danger`
### Цветовая схема категорий
- Завтрак — `bg-info`
- Обед — `bg-primary`
- Ужин — `bg-dark`
- Выпечка — `bg-warning`
- Десерты — `bg-pink`
- Напитки — `bg-light text-dark`
---
## 7. Git-workflow для команды
### Распределение модулей (для 2 человек)
| Участник | Модуль | Ветки |
|---|---|---|
| **Участник A** | Рецепты, категории, шаги, ингредиенты, каталог | `feature/recipes-crud`, `feature/categories`, `feature-recipe-steps` |
| **Участник B** | Оценки, комментарии, избранное, дашборд, UI | `feature-ingredients`, `feature-recipe-ingredients-m2m`, `feature-ratings` |
### Распределение модулей (для 3 человек)
| Участник | Модуль | Ветки |
|---|---|---|
| **Участник A** | Рецепты, категории, шаги приготовления, загрузка изображений | `feature/recipes-crud`, `feature/categories`, `feature-recipe-steps` |
| **Участник B** | Ингредиенты, многие-ко-многим, оценки, расчёт рейтинга | `feature-ingredients`, `feature-recipe-ingredients-m2m`, `feature-ratings` |
| **Участник C** | Комментарии, избранное, каталог, UI, дашборд | `feature-comments`, `feature-favorites`, `feature-recipe-catalog-ui` |
### Правила
1. Ветка `develop` — основная ветка разработки
2. Каждый участник создаёт фич-ветки от `develop`
3. Минимум 3 PR на участника
4. Обязательный код-ревью перед мёржем
5. Conventional Commits
### Визуализация workflow (2 участника)
```mermaid
gitGraph
commit id: "init" tag: "v1.0"
branch develop
checkout develop
branch feature/recipes-crud
checkout develop
branch feature-ingredients
checkout feature/recipes-crud
commit id: "feat: add recipes CRUD"
commit id: "feat: add recipe steps"
checkout feature-ingredients
commit id: "feat: add ingredients m2m"
commit id: "feat: add ratings system"
checkout develop
merge feature/recipes-crud tag: "PR + review"
merge feature-ingredients tag: "PR + review"
checkout main
merge develop tag: "release v1.1"
```
### Визуализация workflow (3 участника)
```mermaid
gitGraph
commit id: "init" tag: "v1.0"
branch develop
checkout develop
branch feature/recipes-crud
checkout develop
branch feature-ingredients
checkout develop
branch feature-comments
checkout feature/recipes-crud
commit id: "feat: add recipes CRUD"
commit id: "feat: add recipe categories"
checkout feature-ingredients
commit id: "feat: add ingredients m2m"
commit id: "feat: add ratings system"
checkout feature-comments
commit id: "feat: add recipe comments"
commit id: "feat: add favorites system"
checkout develop
merge feature/recipes-crud tag: "PR + review"
merge feature-ingredients tag: "PR + review"
merge feature-comments tag: "PR + review"
checkout main
merge develop tag: "release v1.1"
```
---
## 8. Критерии приёмки
### Обязательно
- [ ] CRUD рецептов с загрузкой изображений
- [ ] CRUD категорий рецептов
- [ ] Шаги приготовления (CRUD внутри рецепта)
- [ ] Справочник ингредиентов (CRUD, только админ)
- [ ] Привязка ингредиентов к рецептам (многие-ко-многим с количеством и единицей)
- [ ] Оценка рецепта (15 звёзд, один голос на пользователя)
- [ ] Автоматический расчёт среднего рейтинга
- [ ] Комментарии к рецептам (создание + удаление)
- [ ] Избранное (добавление/удаление)
- [ ] Панель автора (мои рецепты, статистика)
- [ ] Policies: пользователь не может редактировать чужие рецепты
- [ ] Адаптивная Bootstrap-вёрстка
- [ ] Flash-сообщения
- [ ] Пагинация каталога и комментариев
- [ ] Git-история: минимум 3 ветки на участника, PR с ревью
### Дополнительно (бонусные баллы)
- [ ] Поиск рецептов по названию и ингредиентам
- [ ] Фильтрация по времени приготовления и сложности
- [ ] Сортировка по рейтингу, дате, времени приготовления
- [ ] Печать рецепта (кнопка «Распечатать» → print-friendly страница)
- [ ] Мягкое удаление рецептов и комментариев
- [ ] Тесты: PHPUnit Feature-тесты для RatingController
- [ ] API: RESTful API для каталога рецептов
---
## 9. Дополнительные задания (для продвинутых)
1. **Масштабирование рецепта:** калькулятор для пересчёта ингредиентов на другое количество порций
2. **Планировщик питания:** календарь с рецептами на неделю
3. **Коллекции:** пользовательские подборки рецептов (как плейлисты)
4. **Тесты:** PHPUnit Feature-тесты для RecipeController и RatingController
5. **API:** RESTful API для каталога и создания рецептов
6. **Queue:** асинхронный пересчёт рейтинга при большом количестве оценок
7. **Events & Listeners:** событие «новая оценка» → пересчёт рейтинга
---
## 10. Рекомендуемые материалы
- Laravel Docs: https://laravel.com/docs/12.x
- Laravel Many-to-Many with Pivot: https://laravel.com/docs/12.x/eloquent-relationships#many-to-many
- Bootstrap 5 Cards: https://getbootstrap.com/docs/5.3/components/card/
- Laravel Aggregates: https://laravel.com/docs/12.x/queries#aggregates
- Laravel Policies: https://laravel.com/docs/12.x/authorization#creating-policies

View File

@ -0,0 +1,443 @@
# Техническое задание: Система управления складом (Inventory Management)
## 1. Описание проекта
Веб-приложение для учёта товаров на складе с контролем прихода и расхода, управлением поставщиками и формированием отчётов. Проект закрепляет навыки CRUD, вложенных моделей (накладная → позиции), транзакций БД, разграничения прав и совместной работы через Git.
**Команда:** 23 человека
---
## 2. Функциональные требования
### 2.1. Аутентификация и регистрация
- Регистрация, вход, выход (Laravel Breeze)
- Регистрация только через администратора (без публичной регистрации)
### 2.2. Справочники
- **Товары:** артикул, название, описание, категория, единица измерения, текущий остаток
- **Категории товаров:** название, описание
- **Поставщики:** название, контакт, телефон, email, адрес
- **Единицы измерения:** название (шт, кг, л, м, уп.)
### 2.3. Управление товарами
- CRUD товаров: артикул (уникальный), название, описание, категория, единица измерения
- Привязка к категории
- Просмотр текущего остатка по каждому товару
- Поиск по артикулу и названию
### 2.4. Приходные накладные
- Создание приходной накладной: дата, поставщик, комментарий
- Позиции приходной накладной: товар, количество, цена за единицу
- **Автоматическое увеличение остатков** товаров при проведении накладной
- Статусы накладной: черновик, проведена, отменена
- Отмена приходной накладной (уменьшение остатков)
### 2.5. Расходные накладные
- Создание расходной накладной: дата, получатель (отдел/сотрудник), комментарий
- Позиции расходной накладной: товар, количество
- **Валидация:** количество не превышает текущий остаток на складе
- **Автоматическое уменьшение остатков** при проведении накладной
- Статусы накладной: черновик, проведена, отменена
- Отмена расходной накладной (возврат остатков)
### 2.6. Остатки на складе
- Таблица остатков: товар, категория, текущее количество, единица измерения
- Фильтрация по категории
- Индикатор критического остатка (ниже минимального порога)
- Сортировка по названию, количеству
### 2.7. История операций
- Журнал всех приходных и расходных накладных
- Фильтрация по типу (приход/расход), дате, поставщику/получателю
- Детали накладной (список позиций)
### 2.8. Отчёты
- Отчёт по остаткам (сводная таблица)
- Отчёт по приходам за период
- Отчёт по расходам за период
- Отчёт по поставщикам (сумма закупок)
### 2.9. Панель кладовщика
- Быстрое создание приходной/расходной накладной
- Текущие остатки с индикаторами
- Последние операции
### 2.10. Панель менеджера
- Просмотр каталога товаров и остатков
- Просмотр истории операций
- Формирование отчётов (без права создания накладных)
### 2.11. Панель администратора
- Управление пользователями и ролями
- Управление справочниками (категории, поставщики, единицы измерения)
- Полный доступ ко всем операциям
---
## 3. Структура базы данных
### Таблица `users`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| name | VARCHAR(255) | Имя |
| email | VARCHAR(255) | Email (unique) |
| password | VARCHAR(255) | Хеш пароля |
| role | ENUM('admin', 'storekeeper', 'manager') | Роль |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `categories`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| name | VARCHAR(255) | Название категории |
| description | TEXT \| NULL | Описание |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `units`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| name | VARCHAR(50) | Название единицы (шт, кг, л, м, уп.) |
| abbreviation | VARCHAR(10) | Сокращение (unique) |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `products`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| sku | VARCHAR(50) | Артикул (unique) |
| name | VARCHAR(255) | Название |
| description | TEXT \| NULL | Описание |
| category_id | BIGINT UNSIGNED | Категория (FK → categories) |
| unit_id | BIGINT UNSIGNED | Единица измерения (FK → units) |
| min_stock | INT \| NULL | Минимальный порог остатка |
| stock_quantity | INT | Текущий остаток (по умолчанию 0) |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `suppliers`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| name | VARCHAR(255) | Название поставщика |
| contact_person | VARCHAR(255) \| NULL | Контактное лицо |
| phone | VARCHAR(20) \| NULL | Телефон |
| email | VARCHAR(255) \| NULL | Email |
| address | TEXT \| NULL | Адрес |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `incoming_invoices`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| invoice_number | VARCHAR(50) | Номер накладной (unique) |
| invoice_date | DATE | Дата накладной |
| supplier_id | BIGINT UNSIGNED | Поставщик (FK → suppliers) |
| user_id | BIGINT UNSIGNED | Создатель (FK → users) |
| comment | TEXT \| NULL | Комментарий |
| status | ENUM('draft', 'posted', 'cancelled') | Статус |
| total_amount | DECIMAL(12, 2) | Общая сумма |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `incoming_invoice_items`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| incoming_invoice_id | BIGINT UNSIGNED | FK → incoming_invoices |
| product_id | BIGINT UNSIGNED | FK → products |
| quantity | DECIMAL(10, 2) | Количество |
| unit_price | DECIMAL(10, 2) | Цена за единицу |
| total | DECIMAL(10, 2) | Сумма позиции |
### Таблица `outgoing_invoices`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| invoice_number | VARCHAR(50) | Номер накладной (unique) |
| invoice_date | DATE | Дата накладной |
| recipient | VARCHAR(255) | Получатель (отдел/сотрудник) |
| user_id | BIGINT UNSIGNED | Создатель (FK → users) |
| comment | TEXT \| NULL | Комментарий |
| status | ENUM('draft', 'posted', 'cancelled') | Статус |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `outgoing_invoice_items`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| outgoing_invoice_id | BIGINT UNSIGNED | FK → outgoing_invoices |
| product_id | BIGINT UNSIGNED | FK → products |
| quantity | DECIMAL(10, 2) | Количество |
---
## 4. Маршруты и контроллеры
```php
// Аутентифицированные маршруты
Route::middleware('auth')->group(function () {
// Дашборд (по умолчанию)
Route::get('/', [DashboardController::class, 'index'])->name('dashboard');
// Остатки (все авторизованные)
Route::get('/stock', [StockController::class, 'index'])->name('stock.index');
// Справочники (все авторизованные)
Route::get('/products', [ProductController::class, 'index'])->name('products.index');
Route::get('/products/{product}', [ProductController::class, 'show'])->name('products.show');
// Приходные накладные (кладовщик + админ)
Route::prefix('incoming')->name('incoming.')->middleware('can:manage-inventory')->group(function () {
Route::resource('invoices', IncomingInvoiceController::class);
Route::post('invoices/{invoice}/post', [IncomingInvoiceController::class, 'post'])->name('invoices.post');
Route::post('invoices/{invoice}/cancel', [IncomingInvoiceController::class, 'cancel'])->name('invoices.cancel');
});
// Расходные накладные (кладовщик + админ)
Route::prefix('outgoing')->name('outgoing.')->middleware('can:manage-inventory')->group(function () {
Route::resource('invoices', OutgoingInvoiceController::class);
Route::post('invoices/{invoice}/post', [OutgoingInvoiceController::class, 'post'])->name('invoices.post');
Route::post('invoices/{invoice}/cancel', [OutgoingInvoiceController::class, 'cancel'])->name('invoices.cancel');
});
// История операций (все авторизованные)
Route::get('/history', [HistoryController::class, 'index'])->name('history.index');
Route::get('/history/incoming/{invoice}', [HistoryController::class, 'incomingDetail'])->name('history.incoming');
Route::get('/history/outgoing/{invoice}', [HistoryController::class, 'outgoingDetail'])->name('history.outgoing');
// Отчёты (менеджер + админ)
Route::prefix('reports')->name('reports.')->middleware('can:view-reports')->group(function () {
Route::get('/stock', [ReportController::class, 'stock'])->name('stock');
Route::get('/incoming', [ReportController::class, 'incoming'])->name('incoming');
Route::get('/outgoing', [ReportController::class, 'outgoing'])->name('outgoing');
Route::get('/suppliers', [ReportController::class, 'suppliers'])->name('suppliers');
});
// Админка
Route::prefix('admin')->name('admin.')->middleware('can:access-admin-panel')->group(function () {
Route::resource('users', AdminUserController::class);
Route::resource('categories', AdminCategoryController::class);
Route::resource('suppliers', AdminSupplierController::class);
Route::resource('units', AdminUnitController::class);
Route::resource('products', AdminProductController::class);
Route::get('dashboard', [AdminDashboardController::class, 'index'])->name('dashboard');
});
});
```
### Контроллеры
- `DashboardController` — общий дашборд
- `StockController` — таблица остатков
- `ProductController` — просмотр каталога товаров
- `IncomingInvoiceController` — CRUD приходных накладных + проведение/отмена
- `OutgoingInvoiceController` — CRUD расходных накладных + проведение/отмена
- `HistoryController` — журнал операций
- `ReportController` — отчёты
- `AdminUserController`, `AdminCategoryController`, `AdminSupplierController`, `AdminUnitController`, `AdminProductController`, `AdminDashboardController` — админка
---
## 5. Роли, Gates и Policies
### Роли
| Роль | Описание |
|---|---|
| `admin` | Полный доступ, управление пользователями и справочниками |
| `storekeeper` | Создание/проведение приходных и расходных накладных, управление остатками |
| `manager` | Просмотр каталога, остатков, истории, формирование отчётов (без права создания накладных) |
### Gates
```php
Gate::define('manage-inventory', function (User $user) {
return in_array($user->role, ['admin', 'storekeeper']);
});
Gate::define('view-reports', function (User $user) {
return in_array($user->role, ['admin', 'manager', 'storekeeper']);
});
Gate::define('access-admin-panel', function (User $user) {
return $user->role === 'admin';
});
```
### Policies
**IncomingInvoicePolicy:**
- `view` — все авторизованные
- `create`, `update` — storekeeper или admin
- `post`, `cancel` — storekeeper или admin
- `delete` — только admin
**OutgoingInvoicePolicy:**
- `view` — все авторизованные
- `create`, `update` — storekeeper или admin
- `post`, `cancel` — storekeeper или admin
- `delete` — только admin
**ProductPolicy:**
- `view` — все авторизованные
- `create`, `update`, `delete` — только admin
**SupplierPolicy, CategoryPolicy, UnitPolicy:**
- `view` — все авторизованные
- `create`, `update`, `delete` — только admin
---
## 6. Требования к интерфейсу (Bootstrap 5)
### Компоненты
- **Навбар** — логотип, остатки, приход, расход, история, отчёты, профиль
- **Таблица остатков** — товар, категория, количество (цветовой индикатор: зелёный/жёлтый/красный), единица
- **Форма накладной** — дата, поставщик/получатель, динамическое добавление позиций (JS), итоговая сумма
- **Таблица позиций** — товар, количество, цена, сумма (для прихода)
- **Карточки дашборда** — последние операции, критические остатки, быстрые действия
- **Бейджи статусов** — черновик (`bg-secondary`), проведена (`bg-success`), отменена (`bg-danger`)
- **Дашборд** — `row` + `col-md-*` карточки статистики
- **Адаптивная сетка** — таблицы с `table-responsive`
### Цветовая схема остатков
- Норма — `table-success` (зелёный фон)
- Ниже минимума — `table-warning` (жёлтый фон)
- Критический (0) — `table danger` (красный фон)
---
## 7. Git-workflow для команды
### Распределение модулей (для 2 человек)
| Участник | Модуль | Ветки |
|---|---|---|
| **Участник A** | Справочники, товары, поставщики, остатки | `feature-products`, `feature-categories`, `feature-suppliers` |
| **Участник B** | Приходные/расходные накладные, история, отчёты | `feature-incoming-invoices`, `feature-stock-update`, `feature-db-transactions` |
### Распределение модулей (для 3 человек)
| Участник | Модуль | Ветки |
|---|---|---|
| **Участник A** | Справочники (товары, категории, поставщики, единицы) | `feature-products`, `feature-categories`, `feature-suppliers` |
| **Участник B** | Приходные накладные, обновление остатков, транзакции | `feature-incoming-invoices`, `feature-stock-update`, `feature-db-transactions` |
| **Участник C** | Расходные накладные, история, отчёты, UI | `feature-outgoing-invoices`, `feature-history`, `feature-reports` |
### Правила
1. Ветка `develop` — основная ветка разработки
2. Каждый участник создаёт фич-ветки от `develop`
3. Минимум 3 PR на участника
4. Обязательный код-ревью перед мёржем
5. Conventional Commits
### Визуализация workflow (2 участника)
```mermaid
gitGraph
commit id: "init" tag: "v1.0"
branch develop
checkout develop
branch feature-products
checkout develop
branch feature-incoming-invoices
checkout feature-products
commit id: "feat: add products CRUD"
commit id: "feat: add suppliers CRUD"
checkout feature-incoming-invoices
commit id: "feat: add incoming invoices"
commit id: "feat: add stock transactions"
checkout develop
merge feature-products tag: "PR + review"
merge feature-incoming-invoices tag: "PR + review"
checkout main
merge develop tag: "release v1.1"
```
### Визуализация workflow (3 участника)
```mermaid
gitGraph
commit id: "init" tag: "v1.0"
branch develop
checkout develop
branch feature-products
checkout develop
branch feature-incoming-invoices
checkout develop
branch feature-outgoing-invoices
checkout feature-products
commit id: "feat: add products CRUD"
commit id: "feat: add categories CRUD"
checkout feature-incoming-invoices
commit id: "feat: add incoming invoices"
commit id: "feat: add stock transactions"
checkout feature-outgoing-invoices
commit id: "feat: add outgoing invoices"
commit id: "feat: add reports"
checkout develop
merge feature-products tag: "PR + review"
merge feature-incoming-invoices tag: "PR + review"
merge feature-outgoing-invoices tag: "PR + review"
checkout main
merge develop tag: "release v1.1"
```
---
## 8. Критерии приёмки
### Обязательно
- [ ] CRUD товаров с уникальным артикулом
- [ ] CRUD категорий, поставщиков, единиц измерения
- [ ] Создание приходной накладной с позициями (товар + количество + цена)
- [ ] Создание расходной накладной с позициями (товар + количество)
- [ ] **Валидация расхода:** количество ≤ текущий остаток
- [ ] **Автоматическое обновление остатков** при проведении приходной/расходной накладной
- [ ] **Транзакции БД:** обновление остатков и создание позиций — атомарная операция
- [ ] Отмена накладной с обратным пересчётом остатков
- [ ] Таблица остатков с фильтрацией по категории
- [ ] История всех операций
- [ ] Отчёты (остатки, приходы, расходы, поставщики)
- [ ] Policies: менеджер не может создавать накладные
- [ ] Адаптивная Bootstrap-вёрстка
- [ ] Flash-сообщения
- [ ] Пагинация таблиц
- [ ] Git-история: минимум 3 ветки на участника, PR с ревью
### Дополнительно (бонусные баллы)
- [ ] Печать накладной (print-friendly страница)
- [ ] Экспорт отчётов в CSV
- [ ] Индикатор критических остатков на дашборде
- [ ] Поиск товаров по артикулу/названию
- [ ] Мягкое удаление товаров и накладных
- [ ] Тесты: PHPUnit Feature-тесты для IncomingInvoiceController
- [ ] API: RESTful API для остатков и товаров
---
## 9. Дополнительные задания (для продвинутых)
1. **Автоматические оповещения:** email при достижении минимального порога остатка
2. **Инвентаризация:** сверка фактических остатков с системными, корректировка
3. **Перемещения:** накладные перемещения между складами (несколько складов)
4. **Тесты:** PHPUnit Feature-тесты для транзакций БД (построение/отмена накладной)
5. **API:** RESTful API для остатков и создания накладных
6. **Queue:** асинхронная генерация отчётов в CSV
7. **Events & Listeners:** событие «проведение накладной» → обновление остатков + логирование
---
## 10. Рекомендуемые материалы
- Laravel Docs: https://laravel.com/docs/12.x
- Laravel Database Transactions: https://laravel.com/docs/12.x/database#database-transactions
- Laravel Form Collections (динамические поля): https://laravel.com/docs/12.x/requests
- Bootstrap 5 Tables: https://getbootstrap.com/docs/5.3/content/tables/
- Laravel Policies: https://laravel.com/docs/12.x/authorization#creating-policies

388
task09-book-exchange.md Normal file
View File

@ -0,0 +1,388 @@
# Техническое задание: Платформа для обмена книгами (Book Exchange)
## 1. Описание проекта
Веб-сервис для обмена книгами между пользователями с системой Wishlist, обменными предложениями и отзывами. Проект закрепляет навыки CRUD, статусных переходов, работы с отношениями многие-ко-многим, разграничения прав и совместной работы через Git.
**Команда:** 23 человека
---
## 2. Функциональные требования
### 2.1. Аутентификация и регистрация
- Регистрация, вход, выход (Laravel Breeze)
### 2.2. Каталог книг
- Просмотр списка доступных для обмена книг с пагинацией
- Фильтрация по жанрам/категориям
- Поиск по названию, автору, ISBN
- Детальная страница книги (обложка, описание, владелец, состояние, кнопка «Предложить обмен»)
### 2.3. Управление книгами (владелец)
- CRUD книг: название, автор, ISBN, описание, жанр, обложка, состояние (новая, хорошая, удовлетворительная)
- Загрузка обложки книги (валидация: jpg/png, макс. 2MB)
- Статус книги: доступна для обмена / зарезервирована / обмен состоялась
- Мои книги (личный кабинет владельца)
### 2.4. Wishlist (список желаемого)
- Добавление книг в wishlist: название, автор (свободный ввод)
- Уведомление владельцу книги из wishlist, если кто-то её добавил
- Страница «Мой Wishlist»
### 2.5. Обменные предложения
- Пользователь создаёт предложение обмена: книга владельца → книга взамен (из книг отправителя) + комментарий
- Статусы обмена: предложено, принято, отклонено, завершено, отменено
- Владелец книги принимает или отклоняет предложение
- При принятии — обе книги получают статус «обмен состоялась»
- При отмене — возврат статусов «доступна для обмена»
- Отзывы после завершения обмена
### 2.6. Отзывы об обмене
- Оба участника оставляют отзыв друг другу после завершения обмена
- Поля отзыва: текст, рейтинг (15 звёзд)
- Средний рейтинг пользователя
- Отзывы на странице профиля
### 2.7. Панель пользователя
- Мои книги (созданные мной)
- Мои обменные предложения (отправленные и полученные)
- Мой Wishlist
- Мои отзывы (полученные и оставленные)
- Профиль и рейтинг
### 2.8. Панель администратора
- Управление пользователями и ролями
- Модерация книг и отзывов
- Общая статистика платформы
---
## 3. Структура базы данных
### Таблица `users`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| name | VARCHAR(255) | Имя |
| email | VARCHAR(255) | Email (unique) |
| password | VARCHAR(255) | Хеш пароля |
| role | ENUM('admin', 'owner', 'requester') | Роль |
| avatar | VARCHAR(255) \| NULL | Аватар |
| rating | DECIMAL(3, 2) \| NULL | Средний рейтинг (0.005.00) |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `genres`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| name | VARCHAR(255) | Название жанра |
| slug | VARCHAR(255) | URL-идентификатор (unique) |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `books`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| title | VARCHAR(255) | Название книги |
| author | VARCHAR(255) | Автор |
| isbn | VARCHAR(20) \| NULL | ISBN |
| description | TEXT \| NULL | Описание |
| cover_image | VARCHAR(255) \| NULL | Обложка |
| genre_id | BIGINT UNSIGNED | Жанр (FK → genres) |
| owner_id | BIGINT UNSIGNED | Владелец (FK → users) |
| condition | ENUM('new', 'good', 'fair') | Состояние |
| status | ENUM('available', 'reserved', 'exchanged') | Статус |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `wishlists`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| user_id | BIGINT UNSIGNED | FK → users |
| title | VARCHAR(255) | Желаемая книга (название) |
| author | VARCHAR(255) \| NULL | Желаемый автор |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `exchange_offers`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| requester_id | BIGINT UNSIGNED | Инициатор обмена (FK → users) |
| owner_book_id | BIGINT UNSIGNED | Книга владельца (FK → books) |
| offered_book_id | BIGINT UNSIGNED | Предлагаемая книга взамен (FK → books) |
| message | TEXT \| NULL | Комментарий инициатора |
| status | ENUM('proposed', 'accepted', 'rejected', 'completed', 'cancelled') | Статус |
| response_message | TEXT \| NULL | Ответ владельца |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `reviews`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| exchange_offer_id | BIGINT UNSIGNED | Связанный обмен (FK → exchange_offers) |
| author_id | BIGINT UNSIGNED | Автор отзыва (FK → users) |
| target_user_id | BIGINT UNSIGNED | Получатель отзыва (FK → users) |
| rating | TINYINT | Рейтинг (15) |
| comment | TEXT | Текст отзыва |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
---
## 4. Маршруты и контроллеры
```php
// Публичные маршруты
Route::get('/', [BookController::class, 'index'])->name('books.index');
Route::get('/books/{book}', [BookController::class, 'show'])->name('books.show');
// Аутентифицированные маршруты
Route::middleware('auth')->group(function () {
// Wishlist
Route::resource('wishlist', WishlistController::class);
// Обменные предложения
Route::prefix('exchanges')->name('exchanges.')->group(function () {
Route::post('/offer/{book}', [ExchangeController::class, 'store'])->name('offer');
Route::get('/sent', [ExchangeController::class, 'sent'])->name('sent');
Route::get('/received', [ExchangeController::class, 'received'])->name('received');
Route::get('/{exchange}', [ExchangeController::class, 'show'])->name('show');
Route::post('/{exchange}/accept', [ExchangeController::class, 'accept'])->name('accept');
Route::post('/{exchange}/reject', [ExchangeController::class, 'reject'])->name('reject');
Route::post('/{exchange}/complete', [ExchangeController::class, 'complete'])->name('complete');
Route::post('/{exchange}/cancel', [ExchangeController::class, 'cancel'])->name('cancel');
});
// Отзывы
Route::resource('reviews', ReviewController::class)->only(['store', 'destroy']);
// Мои книги (владелец)
Route::prefix('my-books')->name('my-books.')->group(function () {
Route::resource('books', BookController::class);
Route::get('dashboard', [DashboardController::class, 'index'])->name('dashboard');
});
// Админка
Route::prefix('admin')->name('admin.')->middleware('can:access-admin-panel')->group(function () {
Route::resource('users', AdminUserController::class);
Route::resource('genres', AdminGenreController::class);
Route::patch('books/{book}/toggle-status', [AdminBookController::class, 'toggleStatus'])->name('books.status');
Route::patch('reviews/{review}/toggle-publish', [AdminReviewController::class, 'togglePublish'])->name('reviews.publish');
Route::get('dashboard', [AdminDashboardController::class, 'index'])->name('dashboard');
});
});
```
### Контроллеры
- `BookController` — публичный каталог + CRUD книг для владельца
- `WishlistController` — CRUD желаемых книг
- `ExchangeController` — создание и управление обменными предложениями
- `ReviewController` — создание/удаление отзывов
- `DashboardController` — панель пользователя
- `AdminUserController`, `AdminGenreController`, `AdminBookController`, `AdminReviewController`, `AdminDashboardController` — админка
---
## 5. Роли, Gates и Policies
### Роли
| Роль | Описание |
|---|---|
| `admin` | Полный доступ, модерация книг и отзывов, управление пользователями |
| `owner` | Добавление книг, управление обменными предложениями, подтверждение обмена |
| `requester` | Просмотр каталога, создание запросов на обмен, Wishlist |
> **Примечание:** при регистрации пользователь получает обе роли `owner` и `requester` (фактически — роль `user`). Разделение логическое, а не через middleware.
### Gates
```php
Gate::define('access-admin-panel', function (User $user) {
return $user->role === 'admin';
});
```
### Policies
**BookPolicy:**
- `view` — все (публичный каталог)
- `create` — все аутентифицированные
- `update`, `delete` — владелец книги или admin
- `offer` — не владелец этой книги (и не предлагает свою же)
**ExchangeOfferPolicy:**
- `create` — все аутентифицированные (не владелец целевой книги)
- `view` — инициатор, владелец книги или admin
- `accept`, `reject` — владелец целевой книги
- `complete`, `cancel` — инициатор или владелец (до завершения)
**WishlistPolicy:**
- `create`, `update`, `delete` — владелец wishlist или admin
- `view` — только владелец wishlist
**ReviewPolicy:**
- `create` — участник завершённого обмена (ещё не оставил отзыв)
- `view` — все (если опубликован), автор или admin (если не опубликован)
- `delete` — автор отзыва или admin
---
## 6. Требования к интерфейсу (Bootstrap 5)
### Компоненты
- **Навбар** — логотип, каталог, «Мои книги», «Обмены», «Wishlist», профиль
- **Карточки книг** — обложка, название, автор, жанр (бейдж), состояние (бейдж), владелец
- **Страница книги** — большая обложка, описание, автор, ISBN, состояние, кнопка «Предложить обмен»
- **Форма обменного предложения** — выбор своей книги взамен (dropdown), комментарий
- **Таблица обменных предложений** — книги, статус (цветные бейджи), действия
- **Звёздный рейтинг** — для отзывов
- **Wishlist** — список желаемых книг (таблица/карточки)
- **Дашборд** — статистика (карточки), активные обмены, wishlist-совпадения
- **Адаптивная сетка** — `col-md-4` для карточек, `col-md-8` + `col-md-4` для основного контента
### Цветовая схема статусов обмена
- Предложено — `bg-info`
- Принято — `bg-success`
- Отклонено — `bg-danger`
- Завершено — `bg-primary`
- Отменено — `bg-secondary`
### Цветовая схема состояния книги
- Новая — `bg-success`
- Хорошая — `bg-primary`
- Удовлетворительная — `bg-warning`
---
## 7. Git-workflow для команды
### Распределение модулей (для 2 человек)
| Участник | Модуль | Ветки |
|---|---|---|
| **Участник A** | Книги, жанры, обложки, обменные предложения | `feature/books-crud`, `feature-genres`, `feature-book-covers` |
| **Участник B** | Wishlist, отзывы, рейтинги, дашборд, UI | `feature-exchange-offers`, `feature-offer-statuses`, `feature-offer-validation` |
### Распределение модулей (для 3 человек)
| Участник | Модуль | Ветки |
|---|---|---|
| **Участник A** | Книги, жанры, загрузка обложек, каталог | `feature/books-crud`, `feature-genres`, `feature-book-covers` |
| **Участник B** | Обменные предложения, статусы, валидация | `feature-exchange-offers`, `feature-offer-statuses`, `feature-offer-validation` |
| **Участник C** | Wishlist, отзывы, рейтинги, UI, дашборд | `feature-wishlist`, `feature-reviews`, `feature-dashboard-ui` |
### Правила
1. Ветка `develop` — основная ветка разработки
2. Каждый участник создаёт фич-ветки от `develop`
3. Минимум 3 PR на участника
4. Обязательный код-ревью перед мёржем
5. Conventional Commits
### Визуализация workflow (2 участника)
```mermaid
gitGraph
commit id: "init" tag: "v1.0"
branch develop
checkout develop
branch feature/books-crud
checkout develop
branch feature-exchange-offers
checkout feature/books-crud
commit id: "feat: add books CRUD"
commit id: "feat: add book covers"
checkout feature-exchange-offers
commit id: "feat: add exchange offers"
commit id: "feat: add offer statuses"
checkout develop
merge feature/books-crud tag: "PR + review"
merge feature-exchange-offers tag: "PR + review"
checkout main
merge develop tag: "release v1.1"
```
### Визуализация workflow (3 участника)
```mermaid
gitGraph
commit id: "init" tag: "v1.0"
branch develop
checkout develop
branch feature/books-crud
checkout develop
branch feature-exchange-offers
checkout develop
branch feature-wishlist
checkout feature/books-crud
commit id: "feat: add books CRUD"
commit id: "feat: add genres CRUD"
checkout feature-exchange-offers
commit id: "feat: add exchange offers"
commit id: "feat: add offer statuses"
checkout feature-wishlist
commit id: "feat: add wishlist CRUD"
commit id: "feat: add reviews system"
checkout develop
merge feature/books-crud tag: "PR + review"
merge feature-exchange-offers tag: "PR + review"
merge feature-wishlist tag: "PR + review"
checkout main
merge develop tag: "release v1.1"
```
---
## 8. Критерии приёмки
### Обязательно
- [ ] CRUD книг с загрузкой обложек
- [ ] CRUD жанров
- [ ] Каталог книг с пагинацией и фильтрацией по жанрам
- [ ] Wishlist (добавление, просмотр, удаление)
- [ ] Создание обменного предложения (книга владельца → книга взамен)
- [ ] Принятие/отклонение предложения владельцем
- [ ] При принятии — обновление статусов обеих книг на «exchanged»
- [ ] При отмене — возврат статусов «available»
- [ ] Отзывы после завершения обмена (оба участника)
- [ ] Расчёт среднего рейтинга пользователя
- [ ] Policies: пользователь не может управлять чужими книгами и предложениями
- [ ] Адаптивная Bootstrap-вёрстка
- [ ] Flash-сообщения
- [ ] Git-история: минимум 3 ветки на участника, PR с ревью
### Дополнительно (бонусные баллы)
- [ ] Поиск книг по названию, автору, ISBN
- [ ] Уведомления владельцу книги из wishlist
- [ ] Уведомления (email при новом предложении / ответе)
- [ ] Рекомендации книг (по жанру, wishlist)
- [ ] Мягкое удаление книг и отзывов
- [ ] Тесты: PHPUnit Feature-тесты для ExchangeController
- [ ] API: RESTful API для каталога книг
---
## 9. Дополнительные задания (для продвинутых)
1. **Уведомления:** email и on-site уведомления при новых предложениях, ответах, wishlist-совпадениях
2. **Совпадения Wishlist:** автоматическое определение, когда книга одного пользователя есть в wishlist другого
3. **Множественный обмен:** предложение нескольких книг взамен
4. **Тесты:** PHPUnit Feature-тесты для ExchangeController (статусные переходы)
5. **API:** RESTful API для каталога и создания предложений
6. **Queue:** асинхронная отправка email-уведомлений
7. **Events & Listeners:** событие «обмен завершён» → напоминание оставить отзыв
---
## 10. Рекомендуемые материалы
- Laravel Docs: https://laravel.com/docs/12.x
- Laravel Many-to-Many: https://laravel.com/docs/12.x/eloquent-relationships#many-to-many
- Bootstrap 5 Badges: https://getbootstrap.com/docs/5.3/components/badge/
- Laravel Policies: https://laravel.com/docs/12.x/authorization#creating-policies
- State Machines (статусные переходы): https://github.com/brentroose/laravel-state-machine

432
task10-pet-shelter.md Normal file
View File

@ -0,0 +1,432 @@
# Техническое задание: Система помощи питомцам (Pet Shelter Management)
## 1. Описание проекта
Веб-платформа для приюта животных: каталог питомцев, заявки на усыновление, управление волонтёрами и историями лечения. Проект закрепляет навыки CRUD, загрузки файлов, статусных переходов, разграничения прав и совместной работы через Git.
**Команда:** 23 человека
---
## 2. Функциональные требования
### 2.1. Аутентификация и регистрация
- Регистрация, вход, выход (Laravel Breeze)
### 2.2. Каталог питомцев
- Просмотр списка питомцев с пагинацией
- Фильтрация по видам (кошки, собаки, грызуны, птицы, рептилии)
- Поиск по кличке, породе
- Детальная страница питомца (фото, описание, вид, порода, возраст, пол, статус, истории лечения)
### 2.3. Управление питомцами (волонтёр/админ)
- CRUD питомцев: кличка, вид, порода, возраст (примерный), пол, описание, фото (до 3), статус (доступен для усыновления, на лечении, усыновлён)
- Загрузка фотографий питомца (валидация: jpg/png, макс. 2MB на каждое фото)
- Особенности характера (свободный текст или теги)
### 2.4. Категории питомцев
- CRUD категорий (видов): кошки, собаки, грызуны, птицы, рептилии
- Описание вида, особенности содержания
### 2.5. Заявки на усыновление
- Форма заявки: данные заявителя (ФИО, телефон, email, адрес), опыт содержания, цель усыновления, условия содержания
- Привязка заявки к питомцу
- Статусы заявки: новая, на рассмотрении, одобрена, отклонена, завершена
- Волонтёр меняет статус заявки (новая → на рассмотрении → одобрена/отклонена)
- При одобрении — питомец получает статус «усыновлён»
- При отклонении — комментарий с причиной
### 2.6. Волонтёры
- Профиль волонтёра: имя, телефон, email, дата начала волонтёрства, статус (активен/неактивен)
- Расписание волонтёра: дата, время, тип деятельности (выгул, кормление, уборка)
- CRUD расписания (волонтёр создаёт свои смены)
### 2.7. Истории питомцев
- Записи о здоровье питомца: дата, описание (лечение, вакцинация, осмотр, операция)
- Привязка к питомцу
- Хронология на странице питомца
### 2.8. Панель волонтёра
- Мои питомцы (за которыми закреплён)
- Входящие заявки на усыновление (управление статусами)
- Моё расписание
- Добавление историй питомцев
### 2.9. Панель потенциального хозяина
- Каталог питомцев
- Мои заявки на усыновление (статусы)
- Создание новой заявки
### 2.10. Панель администратора
- Управление пользователями и ролями
- Управление категориями питомцев
- Модерация заявок
- Общая статистика приюта
---
## 3. Структура базы данных
### Таблица `users`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| name | VARCHAR(255) | Имя |
| email | VARCHAR(255) | Email (unique) |
| password | VARCHAR(255) | Хеш пароля |
| role | ENUM('admin', 'volunteer', 'adopter') | Роль |
| phone | VARCHAR(20) \| NULL | Телефон |
| avatar | VARCHAR(255) \| NULL | Аватар |
| volunteer_start_date | DATE \| NULL | Дата начала волонтёрства (для волонтёров) |
| is_active | BOOLEAN | Активен ли (для волонтёров) |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `pet_categories`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| name | VARCHAR(255) | Название вида (кошки, собаки, ...) |
| slug | VARCHAR(255) | URL-идентификатор (unique) |
| description | TEXT \| NULL | Описание вида |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `pets`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| name | VARCHAR(255) | Кличка питомца |
| slug | VARCHAR(255) | URL-идентификатор (unique) |
| species_id | BIGINT UNSIGNED | Вид (FK → pet_categories) |
| breed | VARCHAR(255) \| NULL | Порода |
| age_estimate | VARCHAR(50) | Примерный возраст (например, «2 года», «6 месяцев») |
| gender | ENUM('male', 'female') | Пол |
| description | TEXT | Описание, особенности характера |
| status | ENUM('available', 'treatment', 'adopted', 'reserved') | Статус |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `pet_photos`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| pet_id | BIGINT UNSIGNED | FK → pets |
| photo_path | VARCHAR(255) | Путь к изображению |
| is_primary | BOOLEAN | Главное фото (true/false) |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `adoption_applications`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| pet_id | BIGINT UNSIGNED | Питомец (FK → pets) |
| applicant_id | BIGINT UNSIGNED | Заявитель (FK → users) |
| full_name | VARCHAR(255) | ФИО заявителя |
| phone | VARCHAR(20) | Телефон |
| email | VARCHAR(255) | Email |
| address | TEXT | Адрес проживания |
| experience | TEXT | Опыт содержания животных |
| purpose | TEXT | Цель усыновления |
| living_conditions | TEXT | Условия содержания |
| status | ENUM('new', 'under_review', 'approved', 'rejected', 'completed') | Статус |
| rejection_reason | TEXT \| NULL | Причина отклонения |
| volunteer_notes | TEXT \| NULL | Заметки волонтёра |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `volunteer_schedules`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| volunteer_id | BIGINT UNSIGNED | Волонтёр (FK → users) |
| pet_id | BIGINT UNSIGNED \| NULL | Питомец (FK → pets), NULL если общая смена |
| schedule_date | DATE | Дата смены |
| start_time | TIME | Время начала |
| end_time | TIME | Время окончания |
| activity_type | ENUM('walking', 'feeding', 'cleaning', 'grooming', 'other') | Тип деятельности |
| notes | TEXT \| NULL | Заметки |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
### Таблица `pet_histories`
| Поле | Тип | Описание |
|---|---|---|
| id | BIGINT UNSIGNED | Первичный ключ |
| pet_id | BIGINT UNSIGNED | FK → pets |
| history_date | DATE | Дата записи |
| type | ENUM('vaccination', 'treatment', 'examination', 'surgery', 'other') | Тип записи |
| description | TEXT | Описание |
| veterinarian | VARCHAR(255) \| NULL | Ветеринар |
| created_by | BIGINT UNSIGNED | Создатель записи (FK → users) |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |
---
## 4. Маршруты и контроллеры
```php
// Публичные маршруты
Route::get('/', [PetController::class, 'index'])->name('pets.index');
Route::get('/pets/{pet:slug}', [PetController::class, 'show'])->name('pets.show');
// Аутентифицированные маршруты
Route::middleware('auth')->group(function () {
// Заявки на усыновление
Route::prefix('applications')->name('applications.')->group(function () {
Route::post('/apply/{pet}', [ApplicationController::class, 'store'])->name('apply');
Route::get('/my-applications', [ApplicationController::class, 'myApplications'])->name('my');
Route::get('/{application}', [ApplicationController::class, 'show'])->name('show');
});
// Расписание волонтёра
Route::prefix('volunteer')->name('volunteer.')->middleware('can:be-volunteer')->group(function () {
Route::resource('schedules', VolunteerScheduleController::class);
Route::get('dashboard', [VolunteerDashboardController::class, 'index'])->name('dashboard');
});
// Управление питомцами (волонтёр/админ)
Route::prefix('shelter')->name('shelter.')->middleware('can:manage-pets')->group(function () {
Route::resource('pets', ShelterPetController::class);
Route::resource('pets.photos', PetPhotoController::class)->scoped(['pets' => 'pet']);
Route::resource('pets.histories', PetHistoryController::class)->scoped(['pets' => 'pet']);
// Управление заявками
Route::get('applications', [ShelterApplicationController::class, 'index'])->name('applications.index');
Route::get('applications/{application}', [ShelterApplicationController::class, 'show'])->name('applications.show');
Route::post('applications/{application}/review', [ShelterApplicationController::class, 'startReview'])->name('applications.review');
Route::post('applications/{application}/approve', [ShelterApplicationController::class, 'approve'])->name('applications.approve');
Route::post('applications/{application}/reject', [ShelterApplicationController::class, 'reject'])->name('applications.reject');
Route::post('applications/{application}/complete', [ShelterApplicationController::class, 'complete'])->name('applications.complete');
});
// Админка
Route::prefix('admin')->name('admin.')->middleware('can:access-admin-panel')->group(function () {
Route::resource('users', AdminUserController::class);
Route::resource('categories', AdminCategoryController::class);
Route::get('dashboard', [AdminDashboardController::class, 'index'])->name('dashboard');
});
});
```
### Контроллеры
- `PetController` — публичный каталог и детальный просмотр
- `ApplicationController` — подача и просмотр своих заявок
- `VolunteerScheduleController` — CRUD расписания волонтёра
- `ShelterPetController` — CRUD питомцев (волонтёр/админ)
- `PetPhotoController` — загрузка/удаление фото питомца
- `PetHistoryController` — CRUD историй питомца
- `ShelterApplicationController` — управление заявками (смена статусов)
- `VolunteerDashboardController`, `AdminUserController`, `AdminCategoryController`, `AdminDashboardController` — панели
---
## 5. Роли, Gates и Policies
### Роли
| Роль | Описание |
|---|---|
| `admin` | Полный доступ, управление пользователями, категориями, модерация |
| `volunteer` | Управление питомцами, заявками, расписанием, историями питомцев |
| `adopter` | Просмотр каталога, подача заявок на усыновление, просмотр своих заявок |
### Gates
```php
Gate::define('be-volunteer', function (User $user) {
return in_array($user->role, ['admin', 'volunteer']);
});
Gate::define('manage-pets', function (User $user) {
return in_array($user->role, ['admin', 'volunteer']);
});
Gate::define('access-admin-panel', function (User $user) {
return $user->role === 'admin';
});
```
### Policies
**PetPolicy:**
- `view` — все (публичный каталог)
- `create`, `update`, `delete` — volunteer или admin
**PetPhotoPolicy:**
- `create`, `delete` — volunteer или admin
**PetHistoryPolicy:**
- `create`, `update`, `delete` — volunteer или admin
- `view` — все
**AdoptionApplicationPolicy:**
- `create` — adopter (не подавал одобренную заявку на этого питомца)
- `view` — заявитель, волонтёр/admin
- `review`, `approve`, `reject`, `complete` — volunteer или admin
**VolunteerSchedulePolicy:**
- `create`, `update`, `delete` — владелец расписания (волонтёр) или admin
- `view` — volunteer или admin
---
## 6. Требования к интерфейсу (Bootstrap 5)
### Компоненты
- **Навбар** — логотип, каталог питомцев, «Мои заявки», панель волонтёра, профиль
- **Карточки питомцев** — главное фото, кличка, вид (бейдж), порода, возраст, статус (бейдж)
- **Страница питомца** — галерея фото (карусель или грид), описание, характеристики, кнопка «Усыновить», истории питомца (хронология)
- **Карусель фото** — Bootstrap Carousel для галереи питомца
- **Форма заявки** — Bootstrap-форма с валидацией, textarea для опыта и условий
- **Таблица заявок** — питомец, заявитель, статус (цветные бейджи), действия
- **Календарь/таблица расписания** — дата, время, тип деятельности
- **Хронология историй** — вертикальный timeline (Bootstrap + кастомные стили)
- **Дашборд волонтёра** — статистика (карточки), входящие заявки, ближайшее расписание
- **Адаптивная сетка** — `col-md-4` для карточек, `col-md-8` + `col-md-4` для основного контента
### Цветовая схема статусов питомца
- Доступен — `bg-success`
- На лечении — `bg-warning`
- Усыновлён — `bg-primary`
- Зарезервирован — `bg-info`
### Цветовая схема статусов заявки
- Новая — `bg-info`
- На рассмотрении — `bg-warning`
- Одобрена — `bg-success`
- Отклонена — `bg-danger`
- Завершена — `bg-secondary`
---
## 7. Git-workflow для команды
### Распределение модулей (для 2 человек)
| Участник | Модуль | Ветки |
|---|---|---|
| **Участник A** | Питомцы, категории, фото, заявки на усыновление | `feature/pets-crud`, `feature-categories`, `feature-pet-photos` |
| **Участник B** | Расписание волонтёра, истории, дашборд, UI | `feature/applications`, `feature-application-statuses`, `feature-approval-flow` |
### Распределение модулей (для 3 человек)
| Участник | Модуль | Ветки |
|---|---|---|
| **Участник A** | Питомцы, категории, загрузка фото, каталог | `feature/pets-crud`, `feature-categories`, `feature-pet-photos` |
| **Участник B** | Заявки на усыновление, статусные переходы | `feature/applications`, `feature-application-statuses`, `feature-approval-flow` |
| **Участник C** | Расписание волонтёра, истории питомцев, UI, дашборд | `feature-volunteer-schedules`, `feature-pet-histories`, `feature-dashboard-ui` |
### Правила
1. Ветка `develop` — основная ветка разработки
2. Каждый участник создаёт фич-ветки от `develop`
3. Минимум 3 PR на участника
4. Обязательный код-ревью перед мёржем
5. Conventional Commits
### Визуализация workflow (2 участника)
```mermaid
gitGraph
commit id: "init" tag: "v1.0"
branch develop
checkout develop
branch feature/pets-crud
checkout develop
branch feature/applications
checkout feature/pets-crud
commit id: "feat: add pets CRUD"
commit id: "feat: add pet photos"
checkout feature/applications
commit id: "feat: add adoption applications"
commit id: "feat: add application statuses"
checkout develop
merge feature/pets-crud tag: "PR + review"
merge feature/applications tag: "PR + review"
checkout main
merge develop tag: "release v1.1"
```
### Визуализация workflow (3 участника)
```mermaid
gitGraph
commit id: "init" tag: "v1.0"
branch develop
checkout develop
branch feature/pets-crud
checkout develop
branch feature/applications
checkout develop
branch feature-volunteer-schedules
checkout feature/pets-crud
commit id: "feat: add pets CRUD"
commit id: "feat: add pet categories"
checkout feature/applications
commit id: "feat: add adoption applications"
commit id: "feat: add application statuses"
checkout feature-volunteer-schedules
commit id: "feat: add volunteer schedules"
commit id: "feat: add pet histories"
checkout develop
merge feature/pets-crud tag: "PR + review"
merge feature/applications tag: "PR + review"
merge feature-volunteer-schedules tag: "PR + review"
checkout main
merge develop tag: "release v1.1"
```
---
## 8. Критерии приёмки
### Обязательно
- [ ] CRUD питомцев с загрузкой фото (до 3 фото на питомца)
- [ ] CRUD категорий питомцев
- [ ] Каталог питомцев с пагинацией и фильтрацией по видам
- [ ] Подача заявки на усыновление (форма с валидацией всех полей)
- [ ] Просмотр своих заявок (заявитель)
- [ ] Управление статусами заявок волонтёром (новая → на рассмотрении → одобрена/отклонена)
- [ ] При одобрении заявки — обновление статуса питомца на «adopted»
- [ ] При отклонении — обязательная причина
- [ ] CRUD расписания волонтёра
- [ ] CRUD историй питомцев (лечение, вакцинация, осмотр)
- [ ] Панель волонтёра (мои питомцы, заявки, расписание)
- [ ] Policies: adoptter не может управлять питомцами или заявками
- [ ] Адаптивная Bootstrap-вёрстка
- [ ] Flash-сообщения
- [ ] Git-история: минимум 3 ветки на участника, PR с ревью
### Дополнительно (бонусные баллы)
- [ ] Поиск питомцев по кличке и породе
- [ ] Фильтрация по возрасту и полу
- [ ] Галерея фото (Bootstrap Carousel)
- [ ] Экспорт расписания волонтёра в PDF
- [ ] Уведомления (email при изменении статуса заявки)
- [ ] Мягкое удаление питомцев и историй
- [ ] Тесты: PHPUnit Feature-тесты для ApplicationController
- [ ] API: RESTful API для каталога питомцев
---
## 9. Дополнительные задания (для продвинутых)
1. **Уведомления:** email заявителю при изменении статуса заявки
2. **Совместимость:** рекомендации питомцев заявителю на основе его опыта и условий
3. **Статистика приюта:** дашборд с графиками (Chart.js) — усыновления по месяцам, популярные виды
4. **Тесты:** PHPUnit Feature-тесты для статусных переходов заявок
5. **API:** RESTful API для каталога питомцев и подачи заявок
6. **Queue:** асинхронная отправка email-уведомлений
7. **Events & Listeners:** событие «заявка одобрена» → обновление статуса питомца + email
---
## 10. Рекомендуемые материалы
- Laravel Docs: https://laravel.com/docs/12.x
- Laravel File Uploads (multiple): https://laravel.com/docs/12.x/filesystem#file-uploads
- Bootstrap 5 Carousel: https://getbootstrap.com/docs/5.3/components/carousel/
- Bootstrap 5 Badges: https://getbootstrap.com/docs/5.3/components/badge/
- Laravel Policies: https://laravel.com/docs/12.x/authorization#creating-policies
- State Machines (статусные переходы): https://github.com/brentroose/laravel-state-machine