Документация ЛК
Модули

Новости

Модуль "Новости"

Версия: 1.0
Дата: 2025-11-27
Статус: Реализован


Содержание

  1. Общее описание
  2. Текущая реализация
  3. Требования заказчика
  4. Выявленные проблемы
  5. Рекомендации по улучшению

1. Общее описание

1.1 Предназначение модуля

Модуль "Новости" предназначен для:

  • Публикации информационных сообщений для дилеров
  • Оповещения о новых продуктах, изменениях цен, акциях
  • Информирования о днях рождения сотрудников и внутренних событиях
  • Распространения корпоративных новостей

1.2 Роли и доступ

РольПросмотрСозданиеРедактированиеУдаление
Администратор
Менеджер⚠️ Настраивается⚠️ Настраивается⚠️ Настраивается
Дилер✅ (только опубликованные)
Производство

1.3 Политики доступа

news_can_view   — Может смотреть новости
news_can_add    — Может добавлять новости
news_can_edit   — Может редактировать новости
news_can_delete — Может удалять новости

2. Текущая реализация

2.1 Архитектура

┌─────────────────────────────────────────────────────────────────────────┐
│                           МОДУЛЬ НОВОСТИ                                │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  routes/web.php                                                         │
│  ├── GET /news          → NewsController@index    → Список новостей     │
│  ├── GET /news/add      → NewsController@create   → Форма создания      │
│  └── GET /news/edit/{id}→ NewsController@show     → Просмотр/Редакт.    │
│                                                                         │
│  app/Http/Controllers/NewsController.php                                │
│  └── Роутинг по group_id:                                               │
│      ├── Дилер (2)     → templates/news/list.blade.php (карточки)       │
│      │                 → templates/news/show.blade.php (просмотр)       │
│      └── Остальные     → templates/news/table.blade.php (таблица)       │
│                        → templates/news/edit.blade.php (редактирование) │
│                                                                         │
│  app/Livewire/News/                                                     │
│  ├── Listing.php       — Filament Table для админов                     │
│  ├── Add.php           — Форма создания новости                         │
│  └── Edit.php          — Форма редактирования                           │
│                                                                         │
│  app/View/Components/Dealer/                                            │
│  ├── Widget/NewsWidget.php  — Виджет на Dashboard (6 последних)         │
│  └── NewsOther.php          — Блок "Другие новости" (2 случайных)       │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

2.2 Модель данных

Таблица: news

ПолеТипОписание
idbigintПервичный ключ
namestring(255)Заголовок новости
descriptiontextСодержимое (HTML)
imagesjsonМассив путей к изображениям
publishedbooleanФлаг публикации
publishedondatetimeДата публикации
indexintПорядок сортировки (не используется)
created_attimestampДата создания
updated_attimestampДата обновления

Модель: app/Models/News.php

// Автоматическая публикация по дате
static::retrieved(function ($model) {
    if(!$model->published && $model->publishedon && Carbon::now()->gt($model->publishedon)){
        $model->update(['published' => true]);
    }
});

// Установка даты публикации при включении флага
static::saving(function ($model) {
    if ($model->isDirty('published') && $model->published === true) {
        $model->publishedon = now();
    }
});

2.3 Интерфейс для Администратора/Менеджера

Список новостей (Filament Table)

┌─────────────────────────────────────────────────────────────────────────┐
│ Новости                                              [+ Создать]        │
├─────────────────────────────────────────────────────────────────────────┤
│ [Поиск...]                                           [Столбцы ▾]        │
├──────┬─────────────────────┬─────────────────┬────────────┬─────────────┤
│ ☐    │ Название            │ Описание        │ Дата публ. │ Опублик.    │
├──────┼─────────────────────┼─────────────────┼────────────┼─────────────┤
│ ☐    │ Привет              │ <p>Всем прив... │ 03.10.2025 │ [●]         │
│ ☐    │ Заголовок новости   │ Nulla qui au... │ 15.11.2025 │ [●]         │
├──────┴─────────────────────┴─────────────────┴────────────┴─────────────┤
│ Показано с 1 по 2 из 2                               на страницу [25 ▾] │
└─────────────────────────────────────────────────────────────────────────┘

Функции:
✅ Поиск по названию
✅ Сортировка по дате, описанию
✅ Toggle публикации прямо в таблице
✅ Удаление (одиночное и массовое)
✅ Переход к редактированию по клику
✅ Пагинация (25, 50, все)

Форма редактирования

┌─────────────────────────────────────────────────────────────────────────┐
│ [← Назад]  Редактирование новости               [Удалить] [Сохранить]   │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────┐  ┌───────────────────────────┐ │
│  │ Название*                           │  │ Загрузите изображения     │ │
│  │ [Привет                          ]  │  │                           │ │
│  │                                     │  │  ┌─────────────────────┐  │ │
│  │ Дата публикации    Опубликован*     │  │  │ Перетащите файлы    │  │ │
│  │ [03.10.2025 📅]    [●]              │  │  │ или выберите        │  │ │
│  │                                     │  │  └─────────────────────┘  │ │
│  │ Описание                            │  │                           │ │
│  │ [B][I][S][🔗][H2][H3][❝][</>][≡][#] │  │  [img1.jpg] [img2.jpg]    │ │
│  │ ┌─────────────────────────────────┐ │  │                           │ │
│  │ │ Всем привет! Это просто        │ │  └───────────────────────────┘ │
│  │ │ новость о том, что мы          │ │                                │
│  │ │ запускаемся!                   │ │                                │
│  │ └─────────────────────────────────┘ │                                │
│  └─────────────────────────────────────┘                                │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Поля формы:
- Название* (TextInput, max 255)
- Дата публикации (DateTimePicker)
- Опубликован* (Toggle)
- Описание (RichEditor с toolbar)
- Изображения (FileUpload, multiple, reorderable, imageEditor)

2.4 Интерфейс для Дилера

Список новостей (карточки)

┌─────────────────────────────────────────────────────────────────────────┐
│ Новости                                                                 │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐       │
│  │ ┌──────────────┐ │  │ ┌──────────────┐ │  │ ┌──────────────┐ │       │
│  │ │   [IMAGE]    │ │  │ │   [IMAGE]    │ │  │ │   [IMAGE]    │ │       │
│  │ └──────────────┘ │  │ └──────────────┘ │  │ └──────────────┘ │       │
│  │ 03.10.2025       │  │ 10.11.2025       │  │ 05.11.2025       │       │
│  │ Привет           │  │ Заголовок...     │  │ Новость 3        │       │
│  │ Краткий текст... │  │ Краткий текст... │  │ Краткий текст... │       │
│  └──────────────────┘  └──────────────────┘  └──────────────────┘       │
│                                                                         │
│  [1] [2] [→]                                                            │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Особенности:
- Только опубликованные новости (published = true)
- Пагинация по 8 записей
- Сетка 4 колонки на десктопе
- Карточки с изображением, датой, заголовком, кратким описанием (200 символов)

Просмотр новости

┌─────────────────────────────────────────────────────────────────────────┐
│ [← Назад]                                                               │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌───────────────────────────────────────┐  ┌─────────────────────────┐ │
│  │                                       │  │ Другие новости          │ │
│  │  Привет                               │  │ ┌─────────────────────┐ │ │
│  │  03.10.2025                           │  │ │ [img] Заголовок 2   │ │ │
│  │                                       │  │ │ Краткий текст...    │ │ │
│  │  [ИЗОБРАЖЕНИЕ]                        │  │ └─────────────────────┘ │ │
│  │                                       │  │ ┌─────────────────────┐ │ │
│  │  Всем привет! Это просто новость      │  │ │ [img] Заголовок 3   │ │ │
│  │  о том, что мы запускаемся!           │  │ │ Краткий текст...    │ │ │
│  │                                       │  │ └─────────────────────┘ │ │
│  └───────────────────────────────────────┘  └─────────────────────────┘ │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

Особенности:
- Полный текст новости (HTML)
- Изображение (первое из массива)
- Блок "Другие новости" — 2 случайные новости (кроме текущей)

2.5 Виджет на Dashboard дилера

// NewsWidget.php
$this->news = News::where('published', true)
    ->orderBy('created_at', 'desc')
    ->paginate(6);
┌─ Новости ───────────────────────────────────────────────────────────────┐
│                                                                         │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐                   │
│  │   [IMAGE]    │  │   [IMAGE]    │  │   [IMAGE]    │                   │
│  │ 03.10.2025   │  │ 10.11.2025   │  │              │                   │
│  │ Привет       │  │ Заголовок    │  │              │                   │
│  │ Краткое...   │  │ Краткое...   │  │              │                   │
│  └──────────────┘  └──────────────┘  └──────────────┘                   │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

3. Требования заказчика

3.1 Из ТЗ

Источники: /docs/Пояснительная_записка_к_системе_ЛК.docx, /docs/ТЗ_на_создание_кабинета_Дилера10_2023.docx

ТребованиеСтатусКомментарий
Раздел "Новости" на главной страницеВиджет на Dashboard дилера
Публикация информации для дилеровАдмин/Менеджер могут создавать
Дни рождения сотрудников⚠️Нет отдельного типа новости
Внутренние коммуникацииОбщий функционал новостей
Отдельная страница новостей/news

3.2 Неявные требования

ФункцияОписание
Категории новостейРазделение на типы: Акции, Продукция, События
Целевая аудиторияНовости для конкретных групп пользователей
УведомленияPush-уведомления о новых публикациях
Прикреплённые файлыВозможность прикрепить документы (PDF, DOC)

4. Выявленные проблемы

4.1 Критические

#ПроблемаВлияниеРешение
1Неиспользуемое поле indexНет сортировки по важностиДобавить функционал закрепления новости
2Нет валидации XSS в descriptionБезопасностьСанитизация HTML при сохранении

4.2 Средние

#ПроблемаВлияниеРешение
3Одинаковый URL для просмотра и редактирования/news/edit/{id} некорректно для дилераРазделить на /news/{id} и /news/{id}/edit
4Нет превью при созданииUXДобавить предпросмотр перед публикацией
5Отсутствует SEOДобавить meta-теги (если нужно)
6Нет подтверждения удаления в спискеUXЕсть ✅ (requiresConfirmation)

4.3 Низкие

#ПроблемаВлияниеРешение
7Дата в карточке берётся из created_at, а не publishedonНекорректная датаИсправить в news-item.blade.php
8Нет счётчика просмотровАналитикаДобавить поле views
9Placeholder изображенияUXЗаменить на реальный placeholder

5. Рекомендации по улучшению

5.1 Приоритет: Высокий

5.1.1 Исправить отображение даты в карточках

Файл: resources/views/components/dealer/news-item.blade.php

// Было:
<p class="text-[#838383] text-xs">{{ $item->created_at->format('d.m.Y') }}</p>

// Стало:
<p class="text-[#838383] text-xs">
    {{ $item->publishedon ? Carbon\Carbon::parse($item->publishedon)->format('d.m.Y') : $item->created_at->format('d.m.Y') }}
</p>

5.1.2 Разделить маршруты просмотра и редактирования

Файл: routes/web.php

// Было:
Route::get('/news/edit/{id}', [NewsController::class, 'show'])->name('news-edit');

// Стало:
Route::get('/news/{id}', [NewsController::class, 'show'])->name('news-show');
Route::get('/news/{id}/edit', [NewsController::class, 'edit'])->name('news-edit');

5.2 Приоритет: Средний

5.2.1 Добавить категории новостей

Миграция:

Schema::create('news_categories', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('slug')->unique();
    $table->string('color')->nullable(); // Цвет метки
    $table->timestamps();
});

Schema::table('news', function (Blueprint $table) {
    $table->foreignId('category_id')->nullable()->constrained('news_categories');
});

Предустановленные категории:

  • 📢 Объявления
  • 🎉 События
  • 💰 Акции
  • 📦 Продукция
  • 🎂 Дни рождения

5.2.2 Добавить закрепление новости

Миграция:

Schema::table('news', function (Blueprint $table) {
    $table->boolean('pinned')->default(false);
    $table->integer('sort_order')->default(0);
});

Логика отображения:

$news = News::where('published', true)
    ->orderBy('pinned', 'desc')
    ->orderBy('publishedon', 'desc')
    ->paginate(8);

5.2.3 Добавить счётчик просмотров

Миграция:

Schema::table('news', function (Blueprint $table) {
    $table->unsignedInteger('views')->default(0);
});

Логика в контроллере:

public function show(int $id, Request $request)
{
    $item = News::find($id);
    if (!$item) abort(404);
    
    // Увеличиваем счётчик просмотров
    $item->increment('views');
    
    // ...
}

5.3 Приоритет: Низкий

5.3.1 Уведомления о новых новостях

При публикации новости создавать уведомления для целевой аудитории:

// В модели News
static::updated(function ($model) {
    if ($model->isDirty('published') && $model->published === true) {
        // Отправить уведомления всем дилерам
        $dealers = User::where('group_id', 2)->get();
        foreach ($dealers as $dealer) {
            Notification::create([
                'user_id' => $dealer->id,
                'type' => 'news_published',
                'notifiable_type' => News::class,
                'notifiable_id' => $model->id,
                'message' => 'Новая публикация: ' . $model->name,
            ]);
        }
    }
});

5.3.2 Прикреплённые файлы

Добавить возможность прикреплять документы к новости:

Schema::table('news', function (Blueprint $table) {
    $table->json('attachments')->nullable(); // `[{name, path, size}]`
});

Приложение A: Структура файлов модуля

app/
├── Http/Controllers/
│   └── NewsController.php           # Контроллер
├── Livewire/News/
│   ├── Add.php                      # Форма создания
│   ├── Edit.php                     # Форма редактирования
│   └── Listing.php                  # Таблица списка
├── Models/
│   └── News.php                     # Модель
└── View/Components/Dealer/
    ├── NewsOther.php                # Компонент "Другие новости"
    └── Widget/NewsWidget.php        # Виджет для Dashboard

database/
├── factories/
│   └── NewsFactory.php              # Фабрика для тестов
└── migrations/
    └── 2024_03_04_175928_create_news_table.php

resources/views/
├── components/dealer/
│   ├── news.blade.php               # Список новостей (виджет)
│   ├── news-item.blade.php          # Карточка новости
│   └── news-other.blade.php         # Блок "Другие новости"
├── livewire/news/
│   └── listing.blade.php            # Шаблон таблицы
└── templates/news/
    ├── add.blade.php                # Страница создания
    ├── edit.blade.php               # Страница редактирования
    ├── list.blade.php               # Список для дилера
    ├── show.blade.php               # Просмотр для дилера
    └── table.blade.php              # Таблица для админа

Приложение B: SQL-запросы

Все опубликованные новости

SELECT * FROM news 
WHERE published = 1 
ORDER BY publishedon DESC;

Статистика по новостям

SELECT 
    COUNT(*) as total,
    SUM(CASE WHEN published = 1 THEN 1 ELSE 0 END) as published,
    SUM(CASE WHEN published = 0 THEN 1 ELSE 0 END) as draft,
    AVG(views) as avg_views
FROM news;

Документ подготовлен на основе анализа исходного кода проекта

On this page

Модуль "Новости"Содержание1. Общее описание1.1 Предназначение модуля1.2 Роли и доступ1.3 Политики доступа2. Текущая реализация2.1 Архитектура2.2 Модель данных2.3 Интерфейс для Администратора/МенеджераСписок новостей (Filament Table)Форма редактирования2.4 Интерфейс для ДилераСписок новостей (карточки)Просмотр новости2.5 Виджет на Dashboard дилера3. Требования заказчика3.1 Из ТЗ3.2 Неявные требования4. Выявленные проблемы4.1 Критические4.2 Средние4.3 Низкие5. Рекомендации по улучшению5.1 Приоритет: Высокий5.1.1 Исправить отображение даты в карточках5.1.2 Разделить маршруты просмотра и редактирования5.2 Приоритет: Средний5.2.1 Добавить категории новостей5.2.2 Добавить закрепление новости5.2.3 Добавить счётчик просмотров5.3 Приоритет: Низкий5.3.1 Уведомления о новых новостях5.3.2 Прикреплённые файлыПриложение A: Структура файлов модуляПриложение B: SQL-запросыВсе опубликованные новостиСтатистика по новостям