laravel-12-tech-specs/task05-freelance-portfolio.md

18 KiB
Raw Permalink Blame History

Техническое задание: Портфолио-платформа для фрилансеров

1. Описание проекта

Веб-сервис для публикации портфолио фрилансеров, поиска заказов и обмена отзывами. Проект закрепляет навыки CRUD, работы с рейтингами и отзывами, разграничения прав и совместной работы через Git.

Команда: 23 человека


2. Функциональные требования

2.1. Аутентификация и регистрация

  • Регистрация, вход, выход (Laravel Breeze)
  • При регистрации выбирается роль: фрилансер или клиент

2.2. Профиль фрилансера

  • Публичный профиль: имя, фото, описание, навыки, контакты, рейтинг
  • Редактирование профиля (только владелец)
  • Загрузка аватара (валидация: jpg/png, макс. 2MB)
  • Список навыков (свободный ввод или выбор из тегов)

2.3. Портфолио-проекты

  • CRUD проектов в портфолио: название, описание, изображения (до 5), технологии/навыки, ссылка на проект
  • Привязка проекта к навыкам (многие-ко-многим)
  • Публичный просмотр на странице профиля
  • Сортировка по дате, рейтингу

2.4. Каталог фрилансеров

  • Просмотр списка фрилансеров с пагинацией
  • Фильтрация по навыкам/категориям услуг
  • Поиск по имени и описанию
  • Карточка фрилансера (аватар, имя, рейтинг, основные навыки)

2.5. Заявки на проекты (клиент → фрилансер)

  • Клиент создаёт заявку: описание проекта, бюджет, срок, требуемые навыки
  • Привязка заявки к фрилансеру
  • Фрилансер видит входящие заявки
  • Фрилансер отвечает на заявку (принять / отклонить с комментарием)
  • Статусы заявки: новая, принята, отклонена, завершена

2.6. Отзывы и рейтинги

  • Клиент оставляет отзыв фрилансеру после завершения заявки
  • Поля отзыва: текст, рейтинг (15 звёзд)
  • Средний рейтинг фрилансера (автоматический пересчёт)
  • Отзывы отображаются на странице профиля

2.7. Панель фрилансера

  • Мой профиль (редактирование)
  • Мои проекты портфолио
  • Входящие заявки (статусы, ответы)
  • Мой рейтинг и отзывы

2.8. Панель клиента

  • Мои заявки (статусы)
  • Создание новой заявки (выбор фрилансера)
  • Оставить отзыв (после завершения)

2.9. Панель администратора

  • Управление пользователями
  • Модерация отзывов (скрыть/удалить)
  • Общая статистика

3. Структура базы данных

Таблица users

Поле Тип Описание
id BIGINT UNSIGNED Первичный ключ
name VARCHAR(255) Имя
email VARCHAR(255) Email (unique)
password VARCHAR(255) Хеш пароля
role ENUM('admin', 'freelancer', 'client') Роль
bio TEXT | NULL О себе
avatar VARCHAR(255) | NULL Аватар
rating DECIMAL(3, 2) | NULL Средний рейтинг (0.005.00)
created_at TIMESTAMP
updated_at TIMESTAMP

Таблица skills

Поле Тип Описание
id BIGINT UNSIGNED Первичный ключ
name VARCHAR(255) Название навыка (unique)
slug VARCHAR(255) URL-идентификатор (unique)
created_at TIMESTAMP
updated_at TIMESTAMP

Таблица freelancer_skill (многие-ко-многим)

Поле Тип Описание
freelancer_id BIGINT UNSIGNED FK → users
skill_id BIGINT UNSIGNED FK → skills

Таблица portfolio_projects

Поле Тип Описание
id BIGINT UNSIGNED Первичный ключ
freelancer_id BIGINT UNSIGNED FK → users
title VARCHAR(255) Название проекта
description TEXT Описание
project_url VARCHAR(255) | NULL Ссылка на проект
created_at TIMESTAMP
updated_at TIMESTAMP

Таблица portfolio_skill (многие-ко-многим)

Поле Тип Описание
portfolio_project_id BIGINT UNSIGNED FK → portfolio_projects
skill_id BIGINT UNSIGNED FK → skills

Таблица project_proposals

Поле Тип Описание
id BIGINT UNSIGNED Первичный ключ
client_id BIGINT UNSIGNED Клиент (FK → users)
freelancer_id BIGINT UNSIGNED Фрилансер (FK → users)
title VARCHAR(255) Название заявки
description TEXT Описание проекта
budget DECIMAL(10, 2) | NULL Бюджет
deadline DATE | NULL Срок выполнения
status ENUM('new', 'accepted', 'rejected', 'completed') Статус
response_message TEXT | NULL Ответ фрилансера
created_at TIMESTAMP
updated_at TIMESTAMP

Таблица reviews

Поле Тип Описание
id BIGINT UNSIGNED Первичный ключ
client_id BIGINT UNSIGNED Автор отзыва (FK → users)
freelancer_id BIGINT UNSIGNED Получатель отзыва (FK → users)
proposal_id BIGINT UNSIGNED | NULL Связанная заявка (FK → project_proposals)
rating TINYINT Рейтинг (15)
comment TEXT Текст отзыва
is_published BOOLEAN Опубликован ли
created_at TIMESTAMP
updated_at TIMESTAMP

4. Маршруты и контроллеры

// Публичные маршруты
Route::get('/', [FreelancerController::class, 'index'])->name('freelancers.index');
Route::get('/freelancers/{user}', [FreelancerController::class, 'show'])->name('freelancers.show');

// Аутентифицированные маршруты
Route::middleware('auth')->group(function () {
    // Профиль
    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');

    // Портфолио (фрилансер)
    Route::prefix('my-portfolio')->name('portfolio.')->middleware('can:be-freelancer')->group(function () {
        Route::resource('projects', PortfolioProjectController::class);
        Route::get('dashboard', [FreelancerDashboardController::class, 'index'])->name('dashboard');
    });

    // Заявки
    Route::prefix('proposals')->name('proposals.')->group(function () {
        Route::post('/send/{freelancer}', [ProposalController::class, 'store'])->name('send');
        Route::get('/my-sent', [ProposalController::class, 'mySent'])->name('my-sent'); // клиент
        Route::get('/incoming', [ProposalController::class, 'incoming'])->name('incoming'); // фрилансер
        Route::post('/{proposal}/accept', [ProposalController::class, 'accept'])->name('accept');
        Route::post('/{proposal}/reject', [ProposalController::class, 'reject'])->name('reject');
    });

    // Отзывы
    Route::resource('reviews', ReviewController::class)->only(['store', 'update', 'destroy']);

    // Админка
    Route::prefix('admin')->name('admin.')->middleware('can:access-admin-panel')->group(function () {
        Route::resource('users', AdminUserController::class);
        Route::resource('skills', AdminSkillController::class);
        Route::patch('reviews/{review}/toggle-publish', [AdminReviewController::class, 'togglePublish'])->name('reviews.publish');
        Route::get('dashboard', [AdminDashboardController::class, 'index'])->name('dashboard');
    });
});

Контроллеры

  • FreelancerController — публичный каталог и профиль
  • ProfileController — редактирование профиля
  • PortfolioProjectController — CRUD проектов портфолио
  • ProposalController — создание и управление заявками
  • ReviewController — создание/удаление отзывов
  • FreelancerDashboardController — панель фрилансера
  • AdminUserController, AdminSkillController, AdminReviewController, AdminDashboardController — админка

5. Роли, Gates и Policies

Роли

Роль Описание
admin Полный доступ, модерация отзывов, управление пользователями
freelancer Управление профилем, портфолио, просмотр входящих заявок, ответы
client Создание заявок, просмотр каталога, написание отзывов

Gates

Gate::define('be-freelancer', function (User $user) {
    return in_array($user->role, ['admin', 'freelancer']);
});

Gate::define('be-client', function (User $user) {
    return in_array($user->role, ['admin', 'client']);
});

Gate::define('access-admin-panel', function (User $user) {
    return $user->role === 'admin';
});

Policies

PortfolioProjectPolicy:

  • view — все (публичное портфолио)
  • create, update, delete — только владелец портфолио или admin

ProposalPolicy:

  • create — client
  • view — клиент (отправитель), фрилансер (получатель), admin
  • accept, reject — только фрилансер (получатель)

ReviewPolicy:

  • create — client (после завершения заявки)
  • view — все (если опубликован), автор или admin (если не опубликован)
  • delete — автор отзыва или admin

6. Требования к интерфейсу (Bootstrap 5)

Компоненты

  • Навбар — логотип, каталог фрилансеров, панель (мои заявки / портфолио), профиль
  • Карточки фрилансеров — аватар (круглый), имя, рейтинг (звёзды), навыки (бейджи)
  • Профиль фрилансера — большое фото, описание, навыки (бейджи), проекты портфолио (карточки), отзывы
  • Звёздный рейтинг — интерактивные звёзды (radio buttons + стилизация или простой JS)
  • Карточки проектов портфолио — изображение, название, описание, технологии (бейджи)
  • Форма заявки — описание, бюджет (число), срок (дата), требуемые навыки
  • Таблица заявок — статусы (цветные бейджи), действия (принять/отклонить)
  • Дашборд фрилансера — статистика (карточки), входящие заявки, рейтинг
  • Адаптивная сеткаcol-md-4 для карточек, col-md-3 + col-md-9 для профиля

Цветовая схема статусов заявок

  • Новая — bg-info
  • Принята — bg-success
  • Отклонена — bg-danger
  • Завершена — bg-secondary

7. Git-workflow для команды

Распределение модулей (для 2 человек)

Участник Модуль Ветки
Участник A Профили, навыки, портфолио, каталог feature/profiles, feature-skills, feature-portfolio-crud
Участник B Заявки, отзывы, рейтинги, UI feature/proposals, feature-proposal-responses, feature-proposal-statuses

Распределение модулей (для 3 человек)

Участник Модуль Ветки
Участник A Профили, навыки, портфолио feature/profiles, feature-skills, feature-portfolio-crud
Участник B Заявки, статусы, ответы фрилансера feature/proposals, feature-proposal-responses, feature-proposal-statuses
Участник C Отзывы, рейтинги, каталог фрилансеров, UI feature-reviews, feature-ratings, feature-freelancer-catalog

Правила

  1. Ветка develop — основная ветка разработки
  2. Каждый участник создаёт фич-ветки от develop
  3. Минимум 3 PR на участника
  4. Обязательный код-ревью перед мёржем
  5. Conventional Commits

Визуализация workflow (2 участника)

gitGraph
   commit id: "init" tag: "v1.0"
   branch develop
   checkout develop
   branch feature/profiles
   checkout develop
   branch feature/proposals
   checkout feature/profiles
   commit id: "feat: add profiles and skills"
   commit id: "feat: add portfolio CRUD"
   checkout feature/proposals
   commit id: "feat: add proposals system"
   commit id: "feat: add proposal responses"
   checkout develop
   merge feature/profiles tag: "PR + review"
   merge feature/proposals tag: "PR + review"
   checkout main
   merge develop tag: "release v1.1"

Визуализация workflow (3 участника)

gitGraph
   commit id: "init" tag: "v1.0"
   branch develop
   checkout develop
   branch feature/profiles
   checkout develop
   branch feature/proposals
   checkout develop
   branch feature-reviews
   checkout feature/profiles
   commit id: "feat: add profiles and skills"
   commit id: "feat: add portfolio images"
   checkout feature/proposals
   commit id: "feat: add proposals system"
   commit id: "feat: add proposal responses"
   checkout feature-reviews
   commit id: "feat: add reviews and ratings"
   commit id: "feat: add freelancer catalog"
   checkout develop
   merge feature/profiles tag: "PR + review"
   merge feature/proposals tag: "PR + review"
   merge feature-reviews tag: "PR + review"
   checkout main
   merge develop tag: "release v1.1"

8. Критерии приёмки

Обязательно

  • Регистрация с выбором роли (фрилансер / клиент)
  • Редактирование профиля с загрузкой аватара
  • CRUD проектов портфолио с изображениями
  • Навыки (многие-ко-многим фрилансер ↔ навыки)
  • Каталог фрилансеров с пагинацией и фильтрацией по навыкам
  • Создание заявки клиентом
  • Фрилансер может принять/отклонить заявку
  • Оставление отзыва клиентом (после завершения заявки)
  • Расчёт среднего рейтинга фрилансера
  • Policies: фрилансер не видит чужие входящие заявки, клиент не может управлять чужими заявками
  • Адаптивная Bootstrap-вёрстка
  • Flash-сообщения
  • Git-история: минимум 3 ветки на участника, PR с ревью

Дополнительно (бонусные баллы)

  • Поиск фрилансеров по имени и описанию
  • Несколько изображений в проекте портфолио (галерея)
  • Сортировка фрилансеров по рейтингу, дате регистрации
  • Уведомления (email при новой заявке / ответе)
  • Мягкое удаление отзывов
  • Тесты: PHPUnit Feature-тесты для ProposalController
  • API: RESTful API для каталога фрилансеров

9. Дополнительные задания (для продвинутых)

  1. Мессенджер: встроенный чат между клиентом и фрилансером по заявке
  2. Контракт: генерация PDF-документа с условиями при принятии заявки
  3. Уведомления: email и on-site уведомления при новых заявках/ответах/отзывах
  4. Фильтры: расширенная фильтрация (рейтинг, стоимость, навыки)
  5. Тесты: PHPUnit Feature-тесты для ReviewController и ProposalController
  6. API: RESTful API для каталога фрилансеров и создания заявок
  7. Queue: асинхронная отправка email-уведомлений

10. Рекомендуемые материалы