This commit is contained in:
F4ilji
2026-04-09 09:09:17 +05:00
parent 169a2f5654
commit e986940680
14 changed files with 312 additions and 112 deletions
@@ -13,6 +13,7 @@ use App\Containers\Dashboard\Actions\Posts\GetPostFormDataAction;
use App\Containers\Dashboard\Actions\Posts\ListAiPreparedPostsAction;
use App\Containers\Dashboard\Actions\Posts\ListPostsAction;
use App\Containers\Dashboard\Actions\Posts\UpdatePostAction;
use App\Containers\Dashboard\Actions\Posts\UploadPostImagesAction;
use App\Containers\Dashboard\UI\WEB\Requests\StorePostRequest;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
@@ -31,6 +32,7 @@ class PostController extends Controller
private readonly BulkDeletePostsAction $bulkDeletePostsAction,
private readonly BulkPublishPostsAction $bulkPublishPostsAction,
private readonly BulkVerificationPostsAction $bulkVerificationPostsAction,
private readonly UploadPostImagesAction $uploadPostImagesAction,
) {}
/**
@@ -49,7 +51,7 @@ class PostController extends Controller
public function store(StorePostRequest $request): RedirectResponse
{
try {
$this->createPostAction->run($request->validated());
$post = $this->createPostAction->run($request->validated());
return redirect()->route('dashboard.posts.index')
->with('success', 'Новость успешно создана!');
@@ -71,6 +73,7 @@ class PostController extends Controller
return Inertia::render('Dashboard/Posts/Edit', [
'post' => [
...$post->toArray(),
'status' => $post->status?->value ?? $post->status,
'publish_setting' => [
'publish_after' => $post->publish_at !== null,
'publish_at' => $post->publish_at,
@@ -215,4 +218,29 @@ class PostController extends Controller
->with('error', 'Ошибка: ' . $e->getMessage());
}
}
/**
* Загружает изображения для поста и возвращает пути
*/
public function uploadImages(Request $request): \Illuminate\Http\JsonResponse
{
$request->validate([
'images' => 'required|array',
'images.*' => 'required|image|mimes:jpeg,png,jpg,webp|max:20480',
]);
try {
$paths = $this->uploadPostImagesAction->run($request->file('images'));
return response()->json([
'success' => true,
'paths' => $paths,
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'error' => 'Ошибка при загрузке изображений: ' . $e->getMessage(),
], 500);
}
}
}
@@ -12,12 +12,67 @@ class StorePostRequest extends FormRequest
return true;
}
/**
* Prepare the data for validation.
*
* Decodes JSON-encoded fields from FormData back to arrays.
*/
protected function prepareForValidation(): void
{
$fieldsToDecode = [
'content',
'tags',
'authors',
'images',
'publish_setting',
'publication',
'slide',
];
foreach ($fieldsToDecode as $field) {
$value = $this->input($field);
if (is_string($value)) {
$decoded = json_decode($value, true);
if (json_last_error() === JSON_ERROR_NONE) {
$this->merge([$field => $decoded]);
}
}
}
// Handle nested slide properties
if (is_array($this->input('slide'))) {
$slideData = $this->input('slide');
if (isset($slideData['image']) && is_string($slideData['image'])) {
$decoded = json_decode($slideData['image'], true);
if (json_last_error() === JSON_ERROR_NONE) {
$slideData['image'] = $decoded;
}
}
if (isset($slideData['settings']) && is_string($slideData['settings'])) {
$decoded = json_decode($slideData['settings'], true);
if (json_last_error() === JSON_ERROR_NONE) {
$slideData['settings'] = $decoded;
}
}
$this->merge(['slide' => $slideData]);
}
// Удаляем preview если это пустой объект
$preview = $this->input('preview');
if ($preview === '{}' || $preview === 'null' || $preview === null || $preview === '') {
$this->merge(['preview' => null]);
}
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'slug' => ['required', 'string', 'max:255', Rule::unique('posts', 'slug')->ignore($this->route('post'))],
'status' => ['required', 'integer', Rule::in([0, 1, 2, 3])], // PostStatus enum values
'status' => ['required', 'string', Rule::in(['draft', 'published', 'verification', 'rejected'])],
'category_id' => ['nullable', 'integer', 'exists:categories,id'],
'tags' => ['nullable', 'array'],
'authors' => ['nullable', 'array'],
@@ -26,7 +81,7 @@ class StorePostRequest extends FormRequest
'images' => ['nullable', 'array'],
'publish_setting' => ['nullable', 'array'],
'publish_setting.publish_after' => ['nullable', 'boolean'],
'publish_setting.publish_at' => ['nullable', 'date', 'after:now'],
'publish_setting.publish_at' => ['nullable', 'date'],
'publication' => ['nullable', 'array'],
'publication.vk' => ['nullable', 'boolean'],
'publication.telegram' => ['nullable', 'boolean'],
@@ -54,7 +109,7 @@ class StorePostRequest extends FormRequest
'slug.required' => 'URL-адрес обязателен',
'slug.unique' => 'Такой URL-адрес уже используется',
'status.required' => 'Статус публикации обязателен',
'status.in' => 'Некорректный статус публикации',
'status.in' => 'Некорректный статус публикации. Допустимые значения: draft, published, verification, rejected',
'category_id.exists' => 'Выбранная категория не существует',
'content.required' => 'Содержание новости обязательно',
'publish_setting.publish_at.after' => 'Дата публикации должна быть в будущем',
@@ -68,6 +68,7 @@ Route::middleware(['access-check', 'dashboard.auth'])->group(function () {
Route::post('/ai-prepared/parse-email', [ParseEmailNewsController::class, '__invoke'])->name('ai-prepared.parse-email');
Route::get('/create', [PostController::class, 'create'])->name('create');
Route::post('/', [PostController::class, 'store'])->name('store');
Route::post('/upload-images', [PostController::class, 'uploadImages'])->name('upload-images');
Route::delete('/bulk-destroy', [PostController::class, 'bulkDestroy'])->name('bulk-destroy');
Route::post('/bulk-publish', [PostController::class, 'bulkPublish'])->name('bulk-publish');
Route::post('/bulk-verification', [PostController::class, 'bulkVerification'])->name('bulk-verification');