Версия 0.1.0 #1
@ -22,6 +22,8 @@ APP_MAINTENANCE_STORE=database
|
|||||||
#LOGIN_MAX_REQUEST=50
|
#LOGIN_MAX_REQUEST=50
|
||||||
#LOGIN_MAX_EMAIL_REQUEST=10
|
#LOGIN_MAX_EMAIL_REQUEST=10
|
||||||
|
|
||||||
|
TINYMCE_LICENSE_KEY="gpl"
|
||||||
|
|
||||||
BCRYPT_ROUNDS=12
|
BCRYPT_ROUNDS=12
|
||||||
|
|
||||||
LOG_CHANNEL=stack
|
LOG_CHANNEL=stack
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Dto\Service\Admin\Project\About;
|
||||||
|
|
||||||
|
use App\Dto\Service\Dto;
|
||||||
|
|
||||||
|
final readonly class StoreUpdate extends Dto
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private string $title,
|
||||||
|
private string $description,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function getTitle(): string
|
||||||
|
{
|
||||||
|
return $this->title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,7 @@ enum Permission: string
|
|||||||
case Role = 'role';
|
case Role = 'role';
|
||||||
case User = 'user';
|
case User = 'user';
|
||||||
case Project = 'project';
|
case Project = 'project';
|
||||||
|
case ProjectContent = 'project-content';
|
||||||
|
|
||||||
public function getPermissions(): array
|
public function getPermissions(): array
|
||||||
{
|
{
|
||||||
@ -15,6 +16,11 @@ public function getPermissions(): array
|
|||||||
self::AdminPanel => [
|
self::AdminPanel => [
|
||||||
'view' => __('permissions.Administrative panel allowed'),
|
'view' => __('permissions.Administrative panel allowed'),
|
||||||
],
|
],
|
||||||
|
self::ProjectContent => [
|
||||||
|
'view' => __('permissions.Allowed to watch'),
|
||||||
|
'create' => __('permissions.Allowed to create'),
|
||||||
|
'update' => __('permissions.Allowed to edit'),
|
||||||
|
],
|
||||||
default => $this->getBasePermissions()
|
default => $this->getBasePermissions()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin\Projects;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Admin\Controller;
|
||||||
|
use App\Http\Requests\Admin\Projects\About\StoreUpdateRequest;
|
||||||
|
use App\Services\Admin\Project\AboutService;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
final class AboutController extends Controller
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly AboutService $aboutService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function languages(int $project, Request $request): View
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->aboutService->languages($project, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin/projects/about/languages', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(int $project, int $language, Request $request): View
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->aboutService->edit($project, $language, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin/projects/about/edit', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(int $project, int $language, StoreUpdateRequest $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$data = $request->getDto();
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->aboutService->storeOrUpdate($project, $language, $data, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
return redirect()->back()->withInput()->withErrors($result->getErrorsOrMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->route('admin.projects.about.edit', [
|
||||||
|
'project' => $project,
|
||||||
|
'language' => $language,
|
||||||
|
])->withSuccess($result->getMessage());
|
||||||
|
}
|
||||||
|
}
|
@ -34,6 +34,17 @@ public function index(IndexRequest $request): View
|
|||||||
return view('admin/projects/index', $result->getData());
|
return view('admin/projects/index', $result->getData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function show(int $id, Request $request): View
|
||||||
|
{
|
||||||
|
$user = $request->user();
|
||||||
|
$result = $this->projectService->show($id, $user);
|
||||||
|
if ($result->isError()) {
|
||||||
|
$this->errors($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return view('admin/projects/show', $result->getData());
|
||||||
|
}
|
||||||
|
|
||||||
public function create(Request $request): View
|
public function create(Request $request): View
|
||||||
{
|
{
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Admin\Projects\About;
|
||||||
|
|
||||||
|
use App\Contracts\FormRequestDto;
|
||||||
|
use App\Dto\Service\Admin\Project\About\StoreUpdate;
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
final class StoreUpdateRequest extends FormRequest implements FormRequestDto
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'title' => ['required', 'string', 'max:255',],
|
||||||
|
'description' => ['nullable', 'string',],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDto(): StoreUpdate
|
||||||
|
{
|
||||||
|
return new StoreUpdate(
|
||||||
|
title: $this->input('title'),
|
||||||
|
description: $this->input('description'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -47,4 +47,9 @@ public function languages(): HasMany
|
|||||||
{
|
{
|
||||||
return $this->hasMany(ProjectLanguage::class);
|
return $this->hasMany(ProjectLanguage::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function contents(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(ProjectContent::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
19
app/application/app/Models/ProjectContent.php
Normal file
19
app/application/app/Models/ProjectContent.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
final class ProjectContent extends Model
|
||||||
|
{
|
||||||
|
use HasFactory, SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'project_content';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'title',
|
||||||
|
'description',
|
||||||
|
];
|
||||||
|
}
|
29
app/application/app/Policies/ProjectContentPolicy.php
Normal file
29
app/application/app/Policies/ProjectContentPolicy.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
use App\Models\ProjectContent;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
|
final readonly class ProjectContentPolicy extends Policy
|
||||||
|
{
|
||||||
|
public function viewAny(User $user): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermission('project-content.view');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function view(User $user, ProjectContent $projectContent): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermission('project-content.view');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(User $user): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermission('project-content.create');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(User $user, ProjectContent $projectContent): bool
|
||||||
|
{
|
||||||
|
return $user->hasPermission('project-content.update');
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Repositories;
|
||||||
|
|
||||||
|
use App\Models\ProjectContent;
|
||||||
|
|
||||||
|
final class ProjectContentRepository
|
||||||
|
{
|
||||||
|
public function getContentByLanguageId(int $projectId, int $languageId): ?ProjectContent
|
||||||
|
{
|
||||||
|
return ProjectContent::query()
|
||||||
|
->where(['project_id' => $projectId, 'language_id' => $languageId])
|
||||||
|
->first();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Repositories;
|
||||||
|
|
||||||
|
use App\Models\ProjectLanguage;
|
||||||
|
|
||||||
|
final class ProjectLanguageRepository
|
||||||
|
{
|
||||||
|
public function isExistsLanguageById(int $projectId, int $languageId): bool
|
||||||
|
{
|
||||||
|
return ProjectLanguage::query()
|
||||||
|
->where(['id' => $languageId, 'project_id' => $projectId])
|
||||||
|
->exists();
|
||||||
|
}
|
||||||
|
}
|
132
app/application/app/Services/Admin/Project/AboutService.php
Normal file
132
app/application/app/Services/Admin/Project/AboutService.php
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\Admin\Project;
|
||||||
|
|
||||||
|
use App\Contracts\ServiceResultError;
|
||||||
|
use App\Dto\Service\Admin\Project\About\StoreUpdate;
|
||||||
|
use App\Models\ProjectContent;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Repositories\ProjectContentRepository;
|
||||||
|
use App\Repositories\ProjectLanguageRepository;
|
||||||
|
use App\Repositories\ProjectRepository;
|
||||||
|
use App\ServiceResults\ServiceResultArray;
|
||||||
|
use App\ServiceResults\StoreUpdateResult;
|
||||||
|
use App\Services\ProjectContent\ProjectContentCommandHandler;
|
||||||
|
use App\Services\Service;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
final class AboutService extends Service
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly ProjectRepository $projectRepository,
|
||||||
|
private readonly ProjectLanguageRepository $projectLanguageRepository,
|
||||||
|
private readonly ProjectContentRepository $projectContentRepository,
|
||||||
|
private readonly ProjectContentCommandHandler $projectContentCommandHandler,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public function languages(int $projectId, User $user): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$project = $this->projectRepository->getProjectById($projectId);
|
||||||
|
|
||||||
|
if (is_null($project)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('viewAny', ProjectContent::class)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->result([
|
||||||
|
'project' => $project,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(int $projectId, int $languageId, User $user): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$project = $this->projectRepository->getProjectById($projectId);
|
||||||
|
$language = $project?->languages()->firstWhere('id', $languageId);
|
||||||
|
|
||||||
|
if (\is_null($project) || \is_null($language)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = $project->contents()->firstWhere('language_id', $languageId);
|
||||||
|
if (\is_null($content)) {
|
||||||
|
if ($user->cannot('create', ProjectContent::class)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
$content = new ProjectContent();
|
||||||
|
$content->project_id = $project->id;
|
||||||
|
$content->language_id = $language->id;
|
||||||
|
} else if($user->cannot('update', $content)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->result([
|
||||||
|
'project' => $project,
|
||||||
|
'language' => $language,
|
||||||
|
'content' => $content,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function storeOrUpdate(int $projectId, int $languageId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||||
|
{
|
||||||
|
if (! $this->projectLanguageRepository->isExistsLanguageById($projectId, $languageId)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = $this->projectContentRepository->getContentByLanguageId($projectId, $languageId);
|
||||||
|
if (is_null($content)) {
|
||||||
|
return $this->store($projectId, $languageId, $data, $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->update($content, $data, $user);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function store(int $projectId, int $languageId, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||||
|
{
|
||||||
|
if ($user->cannot('create', ProjectContent::class)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$aboutProject = DB::transaction(function () use ($data, $projectId, $languageId) {
|
||||||
|
$dataAboutProject = $this->getDataAboutProject($data);
|
||||||
|
|
||||||
|
return $this->projectContentCommandHandler->handleStore($projectId, $languageId, $dataAboutProject);
|
||||||
|
});
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
return $this->errService(__('Server Error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->resultStoreUpdateModel($aboutProject, __('Project information has been successfully updated'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function update(ProjectContent $content, StoreUpdate $data, User $user): ServiceResultError | StoreUpdateResult
|
||||||
|
{
|
||||||
|
if ($user->cannot('update', $content)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$aboutProject = DB::transaction(function () use ($data, $content) {
|
||||||
|
$dataAboutProject = $this->getDataAboutProject($data);
|
||||||
|
|
||||||
|
return $this->projectContentCommandHandler->handleUpdate($content, $dataAboutProject);
|
||||||
|
});
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
return $this->errService(__('Server Error'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->resultStoreUpdateModel($aboutProject, __('Project information has been successfully updated'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDataAboutProject(StoreUpdate $data): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'title' => $data->getTitle(),
|
||||||
|
'description' => $data->getDescription(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -50,6 +50,23 @@ public function index(ProjectBuilderDto $projectBuilderDto, QuerySettingsDto $qu
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function show(int $id, User $user): ServiceResultError | ServiceResultArray
|
||||||
|
{
|
||||||
|
$project = $this->projectRepository->getProjectById($id);
|
||||||
|
|
||||||
|
if (is_null($project)) {
|
||||||
|
return $this->errNotFound(__('Not Found'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user->cannot('view', $project)) {
|
||||||
|
return $this->errFobidden(__('Access is denied'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->result([
|
||||||
|
'project' => $project,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
public function create(User $user): ServiceResultError | ServiceResultArray
|
public function create(User $user): ServiceResultError | ServiceResultArray
|
||||||
{
|
{
|
||||||
if ($user->cannot('create', Project::class)) {
|
if ($user->cannot('create', Project::class)) {
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Services\ProjectContent;
|
||||||
|
|
||||||
|
use App\Models\ProjectContent;
|
||||||
|
|
||||||
|
final readonly class ProjectContentCommandHandler
|
||||||
|
{
|
||||||
|
public function handleStore(int $projectId, int $languageId, array $data): ProjectContent
|
||||||
|
{
|
||||||
|
$content = new ProjectContent();
|
||||||
|
$content->project_id = $projectId;
|
||||||
|
$content->language_id = $languageId;
|
||||||
|
|
||||||
|
$content->fill($data);
|
||||||
|
$content->save();
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleUpdate(ProjectContent $content, array $data): ProjectContent
|
||||||
|
{
|
||||||
|
$content->update($data);
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
}
|
43
app/application/app/View/Components/Volt/Forms/Textarea.php
Normal file
43
app/application/app/View/Components/Volt/Forms/Textarea.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\View\Components\Volt\Forms;
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
final class Textarea extends Form
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly string $title,
|
||||||
|
private readonly string $name,
|
||||||
|
private readonly ?string $value = '',
|
||||||
|
) { }
|
||||||
|
|
||||||
|
protected function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTitle(): string
|
||||||
|
{
|
||||||
|
return Str::ucfirst($this->title);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getValue(): string
|
||||||
|
{
|
||||||
|
return (string) old($this->getRequestName(), $this->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
return view('components.volt.forms.textarea', [
|
||||||
|
'title' => $this->getTitle(),
|
||||||
|
'name' => $this->getName(),
|
||||||
|
'requestName' => $this->getRequestName(),
|
||||||
|
'value' => $this->getValue(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\View\Components\Volt\Forms;
|
||||||
|
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use Illuminate\View\View;
|
||||||
|
|
||||||
|
final class TextareaWysiwyg extends Form
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly string $title,
|
||||||
|
private readonly string $name,
|
||||||
|
private readonly ?string $value = '',
|
||||||
|
) { }
|
||||||
|
|
||||||
|
protected function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTitle(): string
|
||||||
|
{
|
||||||
|
return Str::ucfirst($this->title);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getValue(): string
|
||||||
|
{
|
||||||
|
return (string) old($this->getRequestName(), $this->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function render(): View
|
||||||
|
{
|
||||||
|
$tinymceLicenseKey = config('tinymce.license_key');
|
||||||
|
|
||||||
|
return view('components.volt.forms.textarea-wysiwyg', [
|
||||||
|
'tinymceLicenseKey' => $tinymceLicenseKey,
|
||||||
|
'title' => $this->getTitle(),
|
||||||
|
'name' => $this->getName(),
|
||||||
|
'requestName' => $this->getRequestName(),
|
||||||
|
'value' => $this->getValue(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
4
app/application/config/tinymce.php
Normal file
4
app/application/config/tinymce.php
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?php
|
||||||
|
return [
|
||||||
|
'license_key' => (string) env('TINYMCE_LICENSE_KEY', 'gpl'),
|
||||||
|
];
|
@ -248,5 +248,6 @@
|
|||||||
"Add language": "Add language",
|
"Add language": "Add language",
|
||||||
"There was an error adding a language": "There was an error adding a language",
|
"There was an error adding a language": "There was an error adding a language",
|
||||||
"Select images": "Select images",
|
"Select images": "Select images",
|
||||||
"loading": "loading"
|
"loading": "loading",
|
||||||
|
"Project information has been successfully updated": "Project information has been successfully updated"
|
||||||
}
|
}
|
||||||
|
@ -5,4 +5,9 @@
|
|||||||
'Users' => 'Users',
|
'Users' => 'Users',
|
||||||
'AdminPanel' => 'Admin Panel',
|
'AdminPanel' => 'Admin Panel',
|
||||||
'Projects' => 'Projects',
|
'Projects' => 'Projects',
|
||||||
|
'Project' => 'Project',
|
||||||
|
'Sections' => 'Sections',
|
||||||
|
'About project' => 'About the project',
|
||||||
|
'Languages' => 'Languages',
|
||||||
|
'Last update' => 'Last update',
|
||||||
];
|
];
|
||||||
|
@ -10,4 +10,5 @@
|
|||||||
'Administrative panel allowed' => 'Administrative panel allowed',
|
'Administrative panel allowed' => 'Administrative panel allowed',
|
||||||
'AdminPanel' => 'Administrative panel allowed',
|
'AdminPanel' => 'Administrative panel allowed',
|
||||||
'Project' => 'Projects',
|
'Project' => 'Projects',
|
||||||
|
'ProjectContent' => 'About the project',
|
||||||
];
|
];
|
||||||
|
@ -248,5 +248,6 @@
|
|||||||
"Add language": "Добавить язык",
|
"Add language": "Добавить язык",
|
||||||
"There was an error adding a language": "Произошла ошибка при добавлении языка",
|
"There was an error adding a language": "Произошла ошибка при добавлении языка",
|
||||||
"Select images": "Выберите изображения",
|
"Select images": "Выберите изображения",
|
||||||
"loading": "загрузка"
|
"loading": "загрузка",
|
||||||
|
"Project information has been successfully updated": "Информация о проекте успешно обновлена"
|
||||||
}
|
}
|
||||||
|
@ -5,4 +5,9 @@
|
|||||||
'Users' => 'Пользователи',
|
'Users' => 'Пользователи',
|
||||||
'AdminPanel' => 'Админка',
|
'AdminPanel' => 'Админка',
|
||||||
'Projects' => 'Проекты',
|
'Projects' => 'Проекты',
|
||||||
|
'Project' => 'Проект',
|
||||||
|
'Sections' => 'Разделы',
|
||||||
|
'About project' => 'О проекте',
|
||||||
|
'Languages' => 'Языки',
|
||||||
|
'Last update' => 'Последнее обновление',
|
||||||
];
|
];
|
||||||
|
@ -11,4 +11,5 @@
|
|||||||
'Administrative panel allowed' => 'Административная панель разрешена',
|
'Administrative panel allowed' => 'Административная панель разрешена',
|
||||||
'AdminPanel' => 'Административная панель разрешена',
|
'AdminPanel' => 'Административная панель разрешена',
|
||||||
'Project' => 'Проекты',
|
'Project' => 'Проекты',
|
||||||
|
'ProjectContent' => 'О проекте',
|
||||||
];
|
];
|
||||||
|
200
app/application/package-lock.json
generated
200
app/application/package-lock.json
generated
@ -17,6 +17,7 @@
|
|||||||
"simplebar": "^5.3.4",
|
"simplebar": "^5.3.4",
|
||||||
"smooth-scroll": "^16.1.3",
|
"smooth-scroll": "^16.1.3",
|
||||||
"sweetalert2": "^11.0.18",
|
"sweetalert2": "^11.0.18",
|
||||||
|
"tinymce": "^7.0.1",
|
||||||
"vanillajs-datepicker": "^1.2.0",
|
"vanillajs-datepicker": "^1.2.0",
|
||||||
"waypoints": "^4.0.1"
|
"waypoints": "^4.0.1"
|
||||||
},
|
},
|
||||||
@ -24,7 +25,8 @@
|
|||||||
"axios": "^1.6.4",
|
"axios": "^1.6.4",
|
||||||
"laravel-vite-plugin": "^1.0",
|
"laravel-vite-plugin": "^1.0",
|
||||||
"sass-loader": "^13.3.2",
|
"sass-loader": "^13.3.2",
|
||||||
"vite": "^5.0"
|
"vite": "^5.0",
|
||||||
|
"vite-plugin-static-copy": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
@ -464,6 +466,41 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz",
|
||||||
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
|
"integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
|
"version": "2.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.stat": "2.0.5",
|
||||||
|
"run-parallel": "^1.1.9"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nodelib/fs.stat": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@nodelib/fs.walk": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.scandir": "2.1.5",
|
||||||
|
"fastq": "^1.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@popperjs/core": {
|
"node_modules/@popperjs/core": {
|
||||||
"version": "2.11.8",
|
"version": "2.11.8",
|
||||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||||
@ -1308,6 +1345,22 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-glob": {
|
||||||
|
"version": "3.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
|
||||||
|
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@nodelib/fs.stat": "^2.0.2",
|
||||||
|
"@nodelib/fs.walk": "^1.2.3",
|
||||||
|
"glob-parent": "^5.1.2",
|
||||||
|
"merge2": "^1.3.0",
|
||||||
|
"micromatch": "^4.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fast-json-stable-stringify": {
|
"node_modules/fast-json-stable-stringify": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||||
@ -1315,6 +1368,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/fastq": {
|
||||||
|
"version": "1.17.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
|
||||||
|
"integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"reusify": "^1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
"version": "7.0.1",
|
"version": "7.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||||
@ -1360,6 +1422,20 @@
|
|||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fs-extra": {
|
||||||
|
"version": "11.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
|
||||||
|
"integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^6.0.1",
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
@ -1395,8 +1471,7 @@
|
|||||||
"version": "4.2.11",
|
"version": "4.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
|
||||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
"dev": true,
|
"dev": true
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/has-flag": {
|
"node_modules/has-flag": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
@ -1480,6 +1555,18 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/jsonfile": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/just-extend": {
|
"node_modules/just-extend": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-5.1.1.tgz",
|
||||||
@ -1536,6 +1623,28 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/merge2": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/micromatch": {
|
||||||
|
"version": "4.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
|
||||||
|
"integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"braces": "^3.0.2",
|
||||||
|
"picomatch": "^2.3.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mime-db": {
|
"node_modules/mime-db": {
|
||||||
"version": "1.52.0",
|
"version": "1.52.0",
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
@ -1673,6 +1782,26 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/queue-microtask": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/randombytes": {
|
"node_modules/randombytes": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||||
@ -1694,6 +1823,16 @@
|
|||||||
"node": ">=8.10.0"
|
"node": ">=8.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/reusify": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"iojs": ">=1.0.0",
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.13.2",
|
"version": "4.13.2",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.2.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.2.tgz",
|
||||||
@ -1728,6 +1867,29 @@
|
|||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/run-parallel": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"queue-microtask": "^1.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/safe-buffer": {
|
"node_modules/safe-buffer": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
@ -1967,6 +2129,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tinymce": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tinymce/-/tinymce-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-0a7DJnhniBx2psRuKcVQ9g4hujN6PAR4fPS0NSF1T1luH1RBDZVVEn2pGND6Ly+AW1lUm/cHOHjsasqBelMhbw=="
|
||||||
|
},
|
||||||
"node_modules/to-regex-range": {
|
"node_modules/to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||||
@ -1985,6 +2152,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/universalify": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.0.13",
|
"version": "1.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
|
||||||
@ -2096,6 +2272,24 @@
|
|||||||
"picomatch": "^2.3.1"
|
"picomatch": "^2.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vite-plugin-static-copy": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-AfmEF+a/mfjsUsrgjbCkhzUCeIUF4EKQXXt3Ie1cour9MBpy6f6GphbdW2td28oYfOrwCyRzFCksgLkpk58q6Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"chokidar": "^3.5.3",
|
||||||
|
"fast-glob": "^3.2.11",
|
||||||
|
"fs-extra": "^11.1.0",
|
||||||
|
"picocolors": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.0.0 || >=20.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vite": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/watchpack": {
|
"node_modules/watchpack": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz",
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"simplebar": "^5.3.4",
|
"simplebar": "^5.3.4",
|
||||||
"smooth-scroll": "^16.1.3",
|
"smooth-scroll": "^16.1.3",
|
||||||
"sweetalert2": "^11.0.18",
|
"sweetalert2": "^11.0.18",
|
||||||
|
"tinymce": "^7.0.1",
|
||||||
"vanillajs-datepicker": "^1.2.0",
|
"vanillajs-datepicker": "^1.2.0",
|
||||||
"waypoints": "^4.0.1"
|
"waypoints": "^4.0.1"
|
||||||
},
|
},
|
||||||
@ -25,6 +26,7 @@
|
|||||||
"axios": "^1.6.4",
|
"axios": "^1.6.4",
|
||||||
"laravel-vite-plugin": "^1.0",
|
"laravel-vite-plugin": "^1.0",
|
||||||
"sass-loader": "^13.3.2",
|
"sass-loader": "^13.3.2",
|
||||||
"vite": "^5.0"
|
"vite": "^5.0",
|
||||||
|
"vite-plugin-static-copy": "^1.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1
app/application/resources/tinymce/langs/ru.js
Normal file
1
app/application/resources/tinymce/langs/ru.js
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,7 @@
|
|||||||
|
@csrf
|
||||||
|
<x-volt.forms.input :title="__('validation.attributes.title')" name="title" type="text" :value="$content->title" required autofocus />
|
||||||
|
<x-volt.forms.textarea-wysiwyg :title="__('validation.attributes.description')" name="description" :value="$content->description" />
|
||||||
|
|
||||||
|
@canany(['create', 'update'], $content)
|
||||||
|
<button class="btn btn-primary" type="submit">{{ __('Save') }}</button>
|
||||||
|
@endcanany
|
@ -0,0 +1,17 @@
|
|||||||
|
@section('meta_title', __('admin-sections.Project') . ': ' . $project->name)
|
||||||
|
@section('h1', __('admin-sections.Project') . ': ' . $project->name)
|
||||||
|
<x-admin.layout>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 mb-4">
|
||||||
|
<div class="card border-0 shadow components-section">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 id="category" class="mb-4">{{ __('admin-sections.About project') }} ({{ $language->title }})</h3>
|
||||||
|
<form method="post" action="{{ route('admin.projects.about.update', ['project' => $project->id, 'language' => $language->id]) }}">
|
||||||
|
@method('PUT')
|
||||||
|
@include('admin.projects.about._from')
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</x-admin.layout>
|
@ -0,0 +1,42 @@
|
|||||||
|
@section('meta_title', __('admin-sections.Project') . ': ' . $project->name)
|
||||||
|
@section('h1', __('admin-sections.Project') . ': ' . $project->name)
|
||||||
|
@php
|
||||||
|
$contents = $project->contents;
|
||||||
|
@endphp
|
||||||
|
<x-admin.layout>
|
||||||
|
<div class="card border-0 shadow mb-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3 id="category" class="mb-4">{{ __('admin-sections.About project') }}</h3>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-centered table-nowrap mb-0 rounded">
|
||||||
|
<thead class="thead-light">
|
||||||
|
<tr>
|
||||||
|
<th class="border-0">{{ __('admin-sections.Languages') }}</th>
|
||||||
|
<th class="border-0">{{ __('admin-sections.Last update') }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($project->languages as $language)
|
||||||
|
@php
|
||||||
|
$content = $contents->firstWhere('language_id', $language->id);
|
||||||
|
@endphp
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="{{ route('admin.projects.about.edit', ['project' => $project->id, 'language' => $language->id]) }}" class="fw-bold">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="align-text-top" viewBox="0 0 16 16">
|
||||||
|
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"/>
|
||||||
|
</svg>
|
||||||
|
{{ $language->title }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ $content?->updated_at }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</x-admin.layout>
|
@ -11,6 +11,7 @@
|
|||||||
<th class="border-0">{{ __('validation.attributes.name') }}</th>
|
<th class="border-0">{{ __('validation.attributes.name') }}</th>
|
||||||
<th class="border-0">{{ __('validation.attributes.code') }}</th>
|
<th class="border-0">{{ __('validation.attributes.code') }}</th>
|
||||||
<th class="border-0">{{ __('validation.attributes.http_host') }}</th>
|
<th class="border-0">{{ __('validation.attributes.http_host') }}</th>
|
||||||
|
<th class="border-0">{{ __('validation.attributes.is_public') }}</th>
|
||||||
<th class="border-0 rounded-end" style="width: 150px"></th>
|
<th class="border-0 rounded-end" style="width: 150px"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -18,9 +19,9 @@
|
|||||||
@foreach($projects as $project)
|
@foreach($projects as $project)
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ route('admin.projects.edit', $project) }}" class="fw-bold">
|
<a href="{{ route('admin.projects.show', $project) }}" class="fw-bold">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="align-text-top" viewBox="0 0 16 16">
|
<svg data-slot="icon" width="16" height="16" fill="currentColor" class="align-text-top" stroke-width="1.5" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||||
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"/>
|
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12.75V12A2.25 2.25 0 0 1 4.5 9.75h15A2.25 2.25 0 0 1 21.75 12v.75m-8.69-6.44-2.12-2.12a1.5 1.5 0 0 0-1.061-.44H4.5A2.25 2.25 0 0 0 2.25 6v12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9a2.25 2.25 0 0 0-2.25-2.25h-5.379a1.5 1.5 0 0 1-1.06-.44Z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
{{ $project->name }}
|
{{ $project->name }}
|
||||||
</a>
|
</a>
|
||||||
@ -32,12 +33,26 @@
|
|||||||
{{ $project->http_host }}
|
{{ $project->http_host }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
@if($project->is_public)
|
||||||
|
{{ __('Yes') }}
|
||||||
|
@else
|
||||||
|
{{ __('No') }}
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ route('admin.projects.edit', $project) }}" class="btn btn-primary" data-bs-toggle="tooltip" data-bs-placement="top" title="{{ __('Edit') }}">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="align-text-top" viewBox="0 0 16 16">
|
||||||
|
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"/>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
@can('delete', $project)
|
@can('delete', $project)
|
||||||
<form method="post" action="{{ route('admin.projects.destroy', $project) }}">
|
<form method="post" class="d-inline-block" action="{{ route('admin.projects.destroy', $project) }}">
|
||||||
@csrf
|
@csrf
|
||||||
@method('DELETE')
|
@method('DELETE')
|
||||||
<button type="submit" class="btn btn-danger click-confirm">
|
<button type="submit" class="btn btn-danger click-confirm" data-bs-toggle="tooltip" data-bs-placement="top" title="{{ __('Delete') }}">
|
||||||
{{ __('Delete') }}
|
<svg data-slot="icon" fill="currentColor" width="20" height="20" class="align-text-center" stroke-width="1.5" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12"></path>
|
||||||
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@endcan
|
@endcan
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
@section('meta_title', __('admin-sections.Project') . ': ' . $project->name)
|
||||||
|
@section('h1', __('admin-sections.Project') . ': ' . $project->name)
|
||||||
|
<x-admin.layout>
|
||||||
|
@include('admin.projects._top')
|
||||||
|
<div class="card border-0 shadow mb-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-centered table-nowrap mb-0 rounded">
|
||||||
|
<thead class="thead-light">
|
||||||
|
<tr>
|
||||||
|
<th class="border-0">{{ __('admin-sections.Sections') }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@can('viewAny', \App\Models\ProjectContent::class)
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="{{ route('admin.projects.about.languages', ['project' => $project->id]) }}" class="fw-bold">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="align-text-top" viewBox="0 0 16 16">
|
||||||
|
<path d="M12.854.146a.5.5 0 0 0-.707 0L10.5 1.793 14.207 5.5l1.647-1.646a.5.5 0 0 0 0-.708l-3-3zm.646 6.061L9.793 2.5 3.293 9H3.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.207l6.5-6.5zm-7.468 7.468A.5.5 0 0 1 6 13.5V13h-.5a.5.5 0 0 1-.5-.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.5-.5V10h-.5a.499.499 0 0 1-.175-.032l-.179.178a.5.5 0 0 0-.11.168l-2 5a.5.5 0 0 0 .65.65l5-2a.5.5 0 0 0 .168-.11l.178-.178z"/>
|
||||||
|
</svg>
|
||||||
|
{{ __('admin-sections.About project') }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endcan
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</x-admin.layout>
|
@ -0,0 +1,24 @@
|
|||||||
|
<div class="mb-3">
|
||||||
|
<label for="form-textarea-wysiwyg-{{ $requestName }}">{{ $title }}</label>
|
||||||
|
<textarea class="form-control textarea-tinymce @error($requestName) is-invalid @enderror" name="{{ $name }}" id="form-textarea-wysiwyg-{{ $requestName }}" rows="3">{{ $value }}</textarea>
|
||||||
|
@error($requestName)
|
||||||
|
<span class="invalid-feedback">{{ $message }}</span>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
@pushOnce('scripts')
|
||||||
|
<script src="{{ asset('/build/tinymce/tinymce.min.js') }}" referrerpolicy="origin"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
tinymce.init({
|
||||||
|
selector: '.textarea-tinymce',
|
||||||
|
@if(in_array(app()->getLocale(), ['ru'], true))
|
||||||
|
language: '{{ app()->getLocale() }}',
|
||||||
|
@endif
|
||||||
|
license_key: '{{ $tinymceLicenseKey }}',
|
||||||
|
plugins: 'advlist code emoticons link lists table codesample',
|
||||||
|
toolbar: 'bold italic | bullist numlist | link emoticons codesample',
|
||||||
|
referrer_policy: 'origin',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endpushonce
|
@ -0,0 +1,7 @@
|
|||||||
|
<div class="mb-3">
|
||||||
|
<label for="form-textarea-{{ $requestName }}">{{ $title }}</label>
|
||||||
|
<textarea class="form-control @error($requestName) is-invalid @enderror" name="{{ $name }}" id="form-textarea-{{ $requestName }}" rows="3">{{ $value }}</textarea>
|
||||||
|
@error($requestName)
|
||||||
|
<span class="invalid-feedback">{{ $message }}</span>
|
||||||
|
@enderror
|
||||||
|
</div>
|
@ -18,7 +18,14 @@
|
|||||||
|
|
||||||
Route::resource('roles', \App\Http\Controllers\Admin\RolesController::class)->only(['index', 'create', 'store', 'edit', 'update', 'destroy'])->where(['role' => '[0-9]+']);
|
Route::resource('roles', \App\Http\Controllers\Admin\RolesController::class)->only(['index', 'create', 'store', 'edit', 'update', 'destroy'])->where(['role' => '[0-9]+']);
|
||||||
|
|
||||||
Route::resource('projects', \App\Http\Controllers\Admin\ProjectsController::class)->only(['index', 'create', 'store', 'edit', 'update', 'destroy'])->where(['project' => '[0-9]+']);
|
Route::resource('projects', \App\Http\Controllers\Admin\ProjectsController::class)->where(['project' => '[0-9]+']);
|
||||||
|
Route::prefix('projects/{project}')->as('projects.')->group(function () {
|
||||||
|
|
||||||
|
Route::get('about', [\App\Http\Controllers\Admin\Projects\AboutController::class, 'languages'])->name('about.languages');
|
||||||
|
Route::get('about/{language}', [\App\Http\Controllers\Admin\Projects\AboutController::class, 'edit'])->name('about.edit')->where(['language' => '[0-9]+']);
|
||||||
|
Route::put('about/{language}', [\App\Http\Controllers\Admin\Projects\AboutController::class, 'update'])->name('about.update')->where(['language' => '[0-9]+']);
|
||||||
|
|
||||||
|
})->where(['project' => '[0-9]+']);
|
||||||
|
|
||||||
Route::post('languages/new-language', [\App\Http\Controllers\Admin\LanguagesController::class, 'newLanguage'])->name('new-language');
|
Route::post('languages/new-language', [\App\Http\Controllers\Admin\LanguagesController::class, 'newLanguage'])->name('new-language');
|
||||||
});
|
});
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import laravel from 'laravel-vite-plugin';
|
import laravel from 'laravel-vite-plugin';
|
||||||
|
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
laravel({
|
laravel({
|
||||||
input: [
|
input: [
|
||||||
'resources/volt/scss/app.scss',
|
'resources/volt/scss/app.scss',
|
||||||
'resources/volt/js/app.js'
|
'resources/volt/js/app.js',
|
||||||
],
|
],
|
||||||
refresh: true,
|
refresh: true,
|
||||||
}),
|
}),
|
||||||
|
viteStaticCopy({
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
src: ['node_modules/tinymce/*', 'resources/tinymce/*'],
|
||||||
|
dest: 'tinymce'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user