feat: ✨ add google map in 'nous-connaitre' page

This commit is contained in:
Alexis Gousseau 2025-12-01 14:42:50 +01:00
parent 1ec5733419
commit 1e5f37b20a
72 changed files with 4732 additions and 292 deletions

View file

@ -67,3 +67,6 @@ SAML2_SP_CERT_x509=
SAML2_SP_CERT_PRIVATEKEY=
SAML2_CONTACT_TECHNICAL_EMAIL=
SAML2_CONTACT_SUPPORT_EMAIL=
# GOOGLE
GOOGLE_MAPS_KEY=

View file

@ -63,7 +63,7 @@ enum DynamicFieldTypes: int
self::vimeoId->name => 'required|string|min:8|max:10',
self::youtubeId->name => 'required|string|size:11',
self::date->name => 'required|date',
self::color->name => 'required|hex_color',
self::color->name => 'required|string|hex_color',
];
/**

View file

@ -0,0 +1,239 @@
<?php
namespace App\Http\Controllers\Back;
use App\Http\Requests\Back\CompanyActivities\StoreCompanyActivityRequest;
use App\Http\Requests\Back\CompanyActivities\UpdateCompanyActivityRequest;
use App\Models\CompanyActivity;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Inertia\Inertia;
use Uwil\LaravelBackend\Resources\CommonResource;
use Uwil\LaravelCommon\Controllers\Controller;
class CompanyActivityController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(CompanyActivity::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Inertia\Response
*/
public function index(Request $request): \Inertia\Response
{
/** @var \Illuminate\Database\Eloquent\Builder<\App\Models\CompanyActivity> $query */
$query = CompanyActivity::query();
if ($request->routeIs("back.company-activities.trash")) {
$query = $query->onlyTrashed();
}
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$query,
$search,
null,
'name',
);
}
$searchFields = \implode(', ', [
trans('validation.attributes.name'),
]);
/** Sort columns with a query */
$this->sortQuery($query);
/** Custom pagination */
$companyActivityModels = $this->paginate($query);
return Inertia::render('CompanyActivities/Index', [
'companyActivityModels' => CommonResource::collection($companyActivityModels),
'search' => $search,
'searchFields' => $searchFields,
]);
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\CompanyActivity $company_activity
* @return \Inertia\Response
*/
public function create(CompanyActivity $company_activity): \Inertia\Response
{
return Inertia::render('CompanyActivities/Create', [
'data' => CommonResource::make($company_activity),
]);
}
/**
* Show a specific resource.
*
* @param \App\Models\CompanyActivity $company_activity
* @return \Inertia\Response
*/
public function show(CompanyActivity $company_activity): \Inertia\Response
{
return Inertia::render('CompanyActivities/Show', [
'data' => CommonResource::make($company_activity),
]);
}
/**
* Store a newly created resource.
*
* @param \App\Http\Requests\Back\CompanyActivities\StoreCompanyActivityRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreCompanyActivityRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$pageConfigModel = new CompanyActivity();
$pageConfigModel->fill($request->validated());
if ($pageConfigModel->saveOrFail()) {
return redirect()->route('back.company-activities.edit', $pageConfigModel)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst()
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst()
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\CompanyActivity $company_activity
* @return \Inertia\Response
*/
public function edit(CompanyActivity $company_activity): \Inertia\Response
{
return Inertia::render('CompanyActivities/Edit', [
'data' => CommonResource::make($company_activity),
]);
}
/**
* Update the specified resource.
*
* @param \App\Http\Requests\Back\CompanyActivities\UpdateCompanyActivityRequest $request
* @param \App\Models\CompanyActivity $company_activity
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateCompanyActivityRequest $request, CompanyActivity $company_activity): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $company_activity) {
$company_activity->fill($request->validated());
if ($company_activity->saveOrFail()) {
return redirect()->route('back.company-activities.edit', $company_activity)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::of(trans('models.classes.company'))->ucfirst()
]));
}
return redirect()->route('back.company-activities.edit', $company_activity)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::of(trans('models.classes.company'))->ucfirst()
]));
});
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\CompanyActivity $company_activity
* @return \Inertia\Response
*/
public function duplicate(CompanyActivity $company_activity): \Inertia\Response
{
return $this->create($company_activity->replicate());
}
/**
* Restore the specified resource.
*
* @param integer $company_activity
* @return \Illuminate\Http\RedirectResponse
*/
public function restore(int $company_activity): \Illuminate\Http\RedirectResponse
{
$company_activity = CompanyActivity::withTrashed()->findOrFail($company_activity);
/** @var array<string,string> */
$previousQueries = [];
\parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
if ($company_activity->restore()) {
return redirect()->route('back.company-activities.trash', $previousQueries)
->with('success', __('crud.messages.has_been_restored', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_restored', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
/**
* Destroy the specified resource.
*
* @param \App\Models\CompanyActivity $company_activity
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(CompanyActivity $company_activity): \Illuminate\Http\RedirectResponse
{
/** @var array<string,string> */
$previousQueries = [];
\parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
if ($company_activity->delete()) {
return redirect()->route('back.company-activities.index', $previousQueries)
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
/**
* Destroy the specified resource.
*
* @param \App\Models\CompanyActivity|integer $company_activity
* @return \Illuminate\Http\RedirectResponse
*/
public function forceDestroy(CompanyActivity|int $company_activity): \Illuminate\Http\RedirectResponse
{
if (is_int($company_activity)) {
$company_activity = CompanyActivity::withTrashed()->findOrFail($company_activity);
}
/** @var array<string,string> */
$previousQueries = [];
\parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
if ($company_activity->forceDelete()) {
return redirect()->route('back.company-activities.trash', $previousQueries)
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
}

View file

@ -0,0 +1,253 @@
<?php
namespace App\Http\Controllers\Back;
use App\Http\Requests\Back\Companies\StoreCompanyRequest;
use App\Http\Requests\Back\Companies\UpdateCompanyRequest;
use App\Models\Company;
use App\Models\CompanyActivity;
use App\Models\CompanySector;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Inertia\Inertia;
use Uwil\LaravelBackend\Resources\CommonResource;
use Uwil\LaravelCommon\Controllers\Controller;
class CompanyController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Company::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Inertia\Response
*/
public function index(Request $request): \Inertia\Response
{
/** @var \Illuminate\Database\Eloquent\Builder<\App\Models\Company> $query */
$query = Company::query();
if ($request->routeIs("back.companies.trash")) {
$query = $query->onlyTrashed();
}
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$query,
$search,
null,
'name',
'latitude',
'longitude',
);
}
$searchFields = \implode(', ', [
trans('validation.attributes.name'),
trans('validation.custom.latitude'),
trans('validation.custom.longitude'),
]);
/** Sort columns with a query */
$this->sortQuery($query);
/** Custom pagination */
$companyModels = $this->paginate($query);
return Inertia::render('Companies/Index', [
'companyModels' => CommonResource::collection($companyModels),
'search' => $search,
'searchFields' => $searchFields,
]);
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Company $company
* @return \Inertia\Response
*/
public function create(Company $company): \Inertia\Response
{
$companyActivityModels = CompanyActivity::getAllCompanyActivities();
$companySectorModels = CompanySector::getAllCompanySectors();
return Inertia::render('Companies/Create', [
'data' => CommonResource::make($company),
'companyActivityModels' => $companyActivityModels,
'companySectorModels' => $companySectorModels,
]);
}
/**
* Show a specific resource.
*
* @param \App\Models\Company $company
* @return \Inertia\Response
*/
public function show(Company $company): \Inertia\Response
{
return Inertia::render('Companies/Show', [
'data' => CommonResource::make($company),
]);
}
/**
* Store a newly created resource.
*
* @param \App\Http\Requests\Back\Companies\StoreCompanyRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreCompanyRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$pageConfigModel = new Company();
$pageConfigModel->inertiaFill($request->validated());
if ($pageConfigModel->saveOrFail()) {
return redirect()->route('back.companies.edit', $pageConfigModel)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst()
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst()
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Company $company
* @return \Inertia\Response
*/
public function edit(Company $company): \Inertia\Response
{
$companyActivityModels = CompanyActivity::getAllCompanyActivities();
$companySectorModels = CompanySector::getAllCompanySectors();
return Inertia::render('Companies/Edit', [
'data' => CommonResource::make($company),
'companyActivityModels' => $companyActivityModels,
'companySectorModels' => $companySectorModels,
]);
}
/**
* Update the specified resource.
*
* @param \App\Http\Requests\Back\Companies\UpdateCompanyRequest $request
* @param \App\Models\Company $company
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateCompanyRequest $request, Company $company): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $company) {
$company->inertiaFill($request->validated());
if ($company->saveOrFail()) {
return redirect()->route('back.companies.edit', $company)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::of(trans('models.classes.company'))->ucfirst()
]));
}
return redirect()->route('back.companies.edit', $company)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::of(trans('models.classes.company'))->ucfirst()
]));
});
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\Company $company
* @return \Inertia\Response
*/
public function duplicate(Company $company): \Inertia\Response
{
return $this->create($company->replicate());
}
/**
* Restore the specified resource.
*
* @param integer $company
* @return \Illuminate\Http\RedirectResponse
*/
public function restore(int $company): \Illuminate\Http\RedirectResponse
{
$company = Company::withTrashed()->findOrFail($company);
/** @var array<string,string> */
$previousQueries = [];
\parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
if ($company->restore()) {
return redirect()->route('back.companies.trash', $previousQueries)
->with('success', __('crud.messages.has_been_restored', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_restored', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
/**
* Destroy the specified resource.
*
* @param \App\Models\Company $company
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Company $company): \Illuminate\Http\RedirectResponse
{
/** @var array<string,string> */
$previousQueries = [];
\parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
if ($company->delete()) {
return redirect()->route('back.companies.index', $previousQueries)
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
/**
* Destroy the specified resource.
*
* @param \App\Models\Company|integer $company
* @return \Illuminate\Http\RedirectResponse
*/
public function forceDestroy(Company|int $company): \Illuminate\Http\RedirectResponse
{
if (is_int($company)) {
$company = Company::withTrashed()->findOrFail($company);
}
/** @var array<string,string> */
$previousQueries = [];
\parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
if ($company->forceDelete()) {
return redirect()->route('back.companies.trash', $previousQueries)
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
}

View file

@ -0,0 +1,239 @@
<?php
namespace App\Http\Controllers\Back;
use App\Http\Requests\Back\CompanySectors\StoreCompanySectorRequest;
use App\Http\Requests\Back\CompanySectors\UpdateCompanySectorRequest;
use App\Models\CompanySector;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Inertia\Inertia;
use Uwil\LaravelBackend\Resources\CommonResource;
use Uwil\LaravelCommon\Controllers\Controller;
class CompanySectorController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(CompanySector::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Inertia\Response
*/
public function index(Request $request): \Inertia\Response
{
/** @var \Illuminate\Database\Eloquent\Builder<\App\Models\CompanySector> $query */
$query = CompanySector::query();
if ($request->routeIs("back.company-sectors.trash")) {
$query = $query->onlyTrashed();
}
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$query,
$search,
null,
'name',
);
}
$searchFields = \implode(', ', [
trans('validation.attributes.name'),
]);
/** Sort columns with a query */
$this->sortQuery($query);
/** Custom pagination */
$companySectorModels = $this->paginate($query);
return Inertia::render('CompanySectors/Index', [
'companySectorModels' => CommonResource::collection($companySectorModels),
'search' => $search,
'searchFields' => $searchFields,
]);
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\CompanySector $company_sector
* @return \Inertia\Response
*/
public function create(CompanySector $company_sector): \Inertia\Response
{
return Inertia::render('CompanySectors/Create', [
'data' => CommonResource::make($company_sector),
]);
}
/**
* Show a specific resource.
*
* @param \App\Models\CompanySector $company_sector
* @return \Inertia\Response
*/
public function show(CompanySector $company_sector): \Inertia\Response
{
return Inertia::render('CompanySectors/Show', [
'data' => CommonResource::make($company_sector),
]);
}
/**
* Store a newly created resource.
*
* @param \App\Http\Requests\Back\CompanySectors\StoreCompanySectorRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreCompanySectorRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$pageConfigModel = new CompanySector();
$pageConfigModel->fill($request->validated());
if ($pageConfigModel->saveOrFail()) {
return redirect()->route('back.company-sectors.edit', $pageConfigModel)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst()
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst()
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\CompanySector $company_sector
* @return \Inertia\Response
*/
public function edit(CompanySector $company_sector): \Inertia\Response
{
return Inertia::render('CompanySectors/Edit', [
'data' => CommonResource::make($company_sector),
]);
}
/**
* Update the specified resource.
*
* @param \App\Http\Requests\Back\CompanySectors\UpdateCompanySectorRequest $request
* @param \App\Models\CompanySector $company_sector
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateCompanySectorRequest $request, CompanySector $company_sector): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $company_sector) {
$company_sector->fill($request->validated());
if ($company_sector->saveOrFail()) {
return redirect()->route('back.company-sectors.edit', $company_sector)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::of(trans('models.classes.company'))->ucfirst()
]));
}
return redirect()->route('back.company-sectors.edit', $company_sector)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::of(trans('models.classes.company'))->ucfirst()
]));
});
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\CompanySector $company_sector
* @return \Inertia\Response
*/
public function duplicate(CompanySector $company_sector): \Inertia\Response
{
return $this->create($company_sector->replicate());
}
/**
* Restore the specified resource.
*
* @param integer $company_sector
* @return \Illuminate\Http\RedirectResponse
*/
public function restore(int $company_sector): \Illuminate\Http\RedirectResponse
{
$company_sector = CompanySector::withTrashed()->findOrFail($company_sector);
/** @var array<string,string> */
$previousQueries = [];
\parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
if ($company_sector->restore()) {
return redirect()->route('back.company-sectors.trash', $previousQueries)
->with('success', __('crud.messages.has_been_restored', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_restored', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
/**
* Destroy the specified resource.
*
* @param \App\Models\CompanySector $company_sector
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(CompanySector $company_sector): \Illuminate\Http\RedirectResponse
{
/** @var array<string,string> */
$previousQueries = [];
\parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
if ($company_sector->delete()) {
return redirect()->route('back.company-sectors.index', $previousQueries)
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
/**
* Destroy the specified resource.
*
* @param \App\Models\CompanySector|integer $company_sector
* @return \Illuminate\Http\RedirectResponse
*/
public function forceDestroy(CompanySector|int $company_sector): \Illuminate\Http\RedirectResponse
{
if (is_int($company_sector)) {
$company_sector = CompanySector::withTrashed()->findOrFail($company_sector);
}
/** @var array<string,string> */
$previousQueries = [];
\parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries);
if ($company_sector->forceDelete()) {
return redirect()->route('back.company-sectors.trash', $previousQueries)
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::of(trans('models.classes.company'))->plural()->ucfirst(),
]));
}
}

View file

@ -7,10 +7,13 @@ use App\Enums\DynamicPages\PageStatusEnum;
use App\Enums\News\NewsCategoryEnum;
use App\Enums\News\NewsStatusEnum;
use App\Helpers\TranslationHelper;
use App\Models\DynamicPage;
use App\Models\PageConfig;
use App\Models\Company;
use App\Models\CompanyActivity;
use App\Models\CompanySector;
use App\Models\ContactCount;
use App\Models\DynamicPage;
use App\Models\News;
use App\Models\PageConfig;
use Diglactic\Breadcrumbs\Breadcrumbs;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
@ -49,7 +52,7 @@ class HandleInertiaRequests extends Middleware
$props = [
...parent::share($request),
'appEnv' => [
'appEnv' => [
'mode' => config('app.env'),
'name' => config('app.name'),
'url' => config('app.url'),
@ -57,34 +60,49 @@ class HandleInertiaRequests extends Middleware
'locales' => config('app.locales'),
'fallbackLocale' => app()->getFallbackLocale(),
],
'auth' => [
'auth' => [
'user' => $request->user(),
'policies' => [
'activityLogs' => [
'activityLogs' => [
'viewAny' => Gate::allows('viewAny', User::class),
],
'users' => [
'users' => [
'viewAny' => Gate::allows('viewAny', User::class),
'create' => Gate::allows('create', User::class),
],
'dynamicPages' => [
'dynamicPages' => [
'viewAny' => Gate::allows('viewAny', DynamicPage::class),
'viewTrash' => Gate::allows('viewTrash', DynamicPage::class),
'create' => Gate::allows('create', DynamicPage::class),
],
'pageConfigs' => [
'pageConfigs' => [
'viewAny' => Gate::allows('viewAny', PageConfig::class),
'viewTrash' => Gate::allows('viewTrash', PageConfig::class),
'create' => Gate::allows('create', PageConfig::class),
],
'contact_counts' => [
'contact_counts' => [
"viewAny" => Gate::allows('viewAny', ContactCount::class)
],
'news' => [
'news' => [
'viewAny' => Gate::allows('viewAny', News::class),
'viewTrash' => Gate::allows('viewTrash', News::class),
'create' => Gate::allows('create', News::class),
]
],
'companies' => [
'viewAny' => Gate::allows('viewAny', Company::class),
'viewTrash' => Gate::allows('viewTrash', Company::class),
'create' => Gate::allows('create', Company::class),
],
'companyActivities' => [
'viewAny' => Gate::allows('viewAny', CompanyActivity::class),
'viewTrash' => Gate::allows('viewTrash', CompanyActivity::class),
'create' => Gate::allows('create', CompanyActivity::class),
],
'companySectors' => [
'viewAny' => Gate::allows('viewAny', CompanySector::class),
'viewTrash' => Gate::allows('viewTrash', CompanySector::class),
'create' => Gate::allows('create', CompanySector::class),
],
]
],
'enumItemsPerPagination' => ItemsPerPaginationEnum::toArray(),

View file

@ -0,0 +1,80 @@
<?php
namespace App\Http\Requests\Back\Companies;
use App\Enums\DynamicFieldTypes;
use App\Models\Company;
use Illuminate\Foundation\Http\FormRequest;
use App\Traits\Requests\HasInertiaPicture;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rules\Enum;
class StoreCompanyRequest extends FormRequest
{
use HasInertiaPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('create', Company::class);
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation(): void
{
$this->merge([
'published' => $this->boolean('published'),
]);
$this->mergeInertiaPicture();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'name' => 'required|string|between:3,255',
'logo' => $this->pictureRules('required|nullable|file|mimes:jpg,jpeg,png,webp|max:5120|dimensions:width=500,height=500'),
'responsable' => 'required|nullable|string|between:3,255',
'url' => 'required|nullable|string|between:3,255',
'informations' => 'required|string|between:3,255',
'employees' => 'required|nullable|numeric|between:0,100000',
'latitude' => 'required|numeric|between:-90,90',
'longitude' => 'required|numeric|between:-180,180',
'activity_id' => 'required|numeric|exists:company_activities,id',
'sector_id' => 'required|nullable|numeric|exists:company_sectors,id',
'published' => 'required|boolean',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'name' => trans('validation.attributes.name'),
'logo' => trans('validation.custom.logo'),
'informations' => trans('validation.custom.informations'),
'responsable' => trans('validation.custom.responsable'),
'latitude' => trans('validation.custom.latitude'),
'longitude' => trans('validation.custom.longitude'),
'activity_id' => trans('models.classes.activity'),
'sector_id' => trans('models.classes.sector'),
'published' => trans('validation.custom.published'),
];
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace App\Http\Requests\Back\Companies;
use Illuminate\Support\Facades\Gate;
class UpdateCompanyRequest extends StoreCompanyRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('update', $this->route('company'));
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace App\Http\Requests\Back\CompanyActivities;
use App\Enums\DynamicFieldTypes;
use App\Models\CompanyActivity;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rules\Enum;
class StoreCompanyActivityRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('create', CompanyActivity::class);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'name' => 'required|string|between:3,255',
'color' => 'required|string|hex_color',
'published' => 'required|boolean',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'name' => trans('validation.custom.name'),
'color' => trans('validation.custom.color'),
'published' => trans('validation.custom.published'),
];
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace App\Http\Requests\Back\CompanyActivities;
use Illuminate\Support\Facades\Gate;
class UpdateCompanyActivityRequest extends StoreCompanyActivityRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('update', $this->route('company_activity'));
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace App\Http\Requests\Back\CompanySectors;
use App\Enums\DynamicFieldTypes;
use App\Models\CompanySector;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rules\Enum;
class StoreCompanySectorRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('create', CompanySector::class);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'name' => 'required|string|between:3,255',
'published' => 'required|boolean',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'name' => trans('validation.custom.name'),
'published' => trans('validation.custom.published'),
];
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace App\Http\Requests\Back\CompanySectors;
use Illuminate\Support\Facades\Gate;
class UpdateCompanySectorRequest extends StoreCompanySectorRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('update', $this->route('company_sector'));
}
}

149
app/Models/Company.php Normal file
View file

@ -0,0 +1,149 @@
<?php
namespace App\Models;
use App\Models\CompanyActivity;
use App\Models\CompanySector;
use App\Traits\Models\HasUwilImages;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Cache;
use Uwil\LaravelActivityLogs\Traits\ActivityLog;
/**
* @property integer $id Id.
* @property string $name Name of the company.
* @property string $logo Logo of the company.
* @property string $responsable Responsable of the company.
* @property string $url Link the website of the company.
* @property string $informations Informations of the company.
* @property string $employees Effectif number of of the company.
* @property string $latitude Latitude coordination of the company.
* @property string $longitude Longitude of the company.
* @property integer $activity_id Activity associated to the company.
* @property integer|null $sector_id Sector associated to the company.
* @property string $published Publish status of the company.
* @property-read \Carbon\Carbon $created_at Created date.
* @property-read \Carbon\Carbon $updated_at Updated date.
* @property-read \Illuminate\Support\Carbon $deleted_at Deleted date.
*/
class Company extends Model
{
use ActivityLog;
use HasFactory;
use SoftDeletes;
use HasUwilImages;
/**
* The attributes that are mass assignable.
*
* @var array<int, string, array[]>
*/
protected $fillable = [
'name',
'logo',
'responsable',
'url',
'informations',
'employees',
'latitude',
'longitude',
'activity_id',
'sector_id',
'published',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'published' => 'boolean',
];
/**
* Perform any actions required after the model boots.
*
* @return void
*/
protected static function booted(): void
{
static::created(function () {
self::resetCache();
});
static::updated(function () {
self::resetCache();
});
static::deleted(function () {
self::resetCache();
});
static::restored(function () {
self::resetCache();
});
static::forceDeleted(function () {
self::resetCache();
});
}
/**
* Reset all cache elements related to this model.
*
* @return void
*/
public static function resetCache(): void
{
Cache::forget('allCompanies');
self::getAllCompanies();
Cache::forget('publishedCompanies');
self::getPublishedCompanies();
}
/**
* Get all companies.
*
* @return \Illuminate\Database\Eloquent\Collection<int,self>
*/
public static function getAllCompanies(): \Illuminate\Database\Eloquent\Collection
{
return Cache::remember('allCompanies', 86400, function () {
return self::query()->with(['companyActivity', 'companySector'])->get();
});
}
/**
* Get all published companies.
*
* @return \Illuminate\Database\Eloquent\Collection<int,self>
*/
public static function getPublishedCompanies(): \Illuminate\Database\Eloquent\Collection
{
return Cache::remember('publishedCompanies', 86400, function () {
return self::getAllCompanies()->where('published', true);
});
}
// * RELATIONSHIPS
/**
* Get the activity for this company.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\App\Models\CompanyActivity,$this>
*/
public function companyActivity(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(CompanyActivity::class, 'activity_id');
}
/**
* Get the sector for this company.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\App\Models\CompanySector,$this>
*/
public function companySector(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(CompanySector::class, 'sector_id');
}
}

View file

@ -0,0 +1,155 @@
<?php
namespace App\Models;
use App\Contracts\TranslatableModel;
use App\Helpers\TranslationHelper;
use App\Traits\Models\HasTranslations;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Cache;
use Uwil\LaravelActivityLogs\Traits\ActivityLog;
/**
* @property integer $id Id.
* @property string $name Name of the company activity.
* @property string $color Color of the company activity.
* @property string $published Publish status of the company activity.
* @property-read \Carbon\Carbon $created_at Created date.
* @property-read \Carbon\Carbon $updated_at Updated date.
* @property-read \Illuminate\Support\Carbon $deleted_at Deleted date.
*/
class CompanyActivity extends Model implements TranslatableModel
{
use ActivityLog;
use HasFactory;
use HasTranslations;
use SoftDeletes;
/**
* The attributes that are mass assignable.
*
* @var array<int, string, array[]>
*/
protected $fillable = [
'name',
'color',
'published',
];
/**
* The translatable columns.
*
* @var list<string>
*/
public $translatable = [
'name',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'published' => 'boolean',
];
/**
* Perform any actions required after the model boots.
*
* @return void
*/
protected static function booted(): void
{
static::creating(function (self $model) {
self::manageFields($model);
});
static::created(function (self $model) {
self::resetCache();
});
static::updating(function (self $model) {
self::manageFields($model);
});
static::updated(function () {
self::resetCache();
});
static::deleted(function () {
self::resetCache();
});
static::restored(function () {
self::resetCache();
});
static::forceDeleted(function () {
self::resetCache();
});
}
// * METHODS
/**
* Manage model fields.
*
* @param self $model
* @return void
*/
private static function manageFields(self $model): void
{
TranslationHelper::assertTranslationFieldIsUnique($model, $model->name, 'name');
self::validateFallbackTranslations($model);
}
/**
* Assert fallback locale translation is present.
*
* @param self $model
* @return void
* @throws \Illuminate\Validation\ValidationException Translations missing.
*/
public static function validateFallbackTranslations(self $model): void
{
if (!$model->published) {
return;
}
TranslationHelper::validateFallbackTranslations($model);
}
/**
* Reset all cache elements related to this model.
*
* @return void
*/
public static function resetCache(): void
{
Cache::forget('allCompanyActivities');
self::getAllCompanyActivities();
Cache::forget('publishedCompanyActivities');
self::getPublishedCompanyActivities();
}
/**
* Get all company activities.
*
* @return \Illuminate\Database\Eloquent\Collection<int,self>
*/
public static function getAllCompanyActivities(): \Illuminate\Database\Eloquent\Collection
{
return Cache::remember('allCompanyActivities', 86400, function () {
return self::query()->get();
});
}
/**
* Get all published company activities.
*
* @return \Illuminate\Database\Eloquent\Collection<int,self>
*/
public static function getPublishedCompanyActivities(): \Illuminate\Database\Eloquent\Collection
{
return Cache::remember('publishedCompanyActivities', 86400, function () {
return self::getAllCompanyActivities()->where('published', true);
});
}
}

View file

@ -0,0 +1,153 @@
<?php
namespace App\Models;
use App\Contracts\TranslatableModel;
use App\Helpers\TranslationHelper;
use App\Traits\Models\HasTranslations;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Cache;
use Uwil\LaravelActivityLogs\Traits\ActivityLog;
/**
* @property integer $id Id.
* @property string $name Name of the company sector.
* @property string $published Publish status of the company sector.
* @property-read \Carbon\Carbon $created_at Created date.
* @property-read \Carbon\Carbon $updated_at Updated date.
* @property-read \Illuminate\Support\Carbon $deleted_at Deleted date.
*/
class CompanySector extends Model implements TranslatableModel
{
use ActivityLog;
use HasFactory;
use HasTranslations;
use SoftDeletes;
/**
* The attributes that are mass assignable.
*
* @var array<int, string, array[]>
*/
protected $fillable = [
'name',
'published',
];
/**
* The translatable columns.
*
* @var list<string>
*/
public $translatable = [
'name',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'published' => 'boolean',
];
/**
* Perform any actions required after the model boots.
*
* @return void
*/
protected static function booted(): void
{
static::creating(function (self $model) {
self::manageFields($model);
});
static::created(function (self $model) {
self::resetCache();
});
static::updating(function (self $model) {
self::manageFields($model);
});
static::updated(function () {
self::resetCache();
});
static::deleted(function () {
self::resetCache();
});
static::restored(function () {
self::resetCache();
});
static::forceDeleted(function () {
self::resetCache();
});
}
// * METHODS
/**
* Manage model fields.
*
* @param self $model
* @return void
*/
private static function manageFields(self $model): void
{
TranslationHelper::assertTranslationFieldIsUnique($model, $model->name, 'name');
self::validateFallbackTranslations($model);
}
/**
* Assert fallback locale translation is present.
*
* @param self $model
* @return void
* @throws \Illuminate\Validation\ValidationException Translations missing.
*/
public static function validateFallbackTranslations(self $model): void
{
if (!$model->published) {
return;
}
TranslationHelper::validateFallbackTranslations($model);
}
/**
* Reset all cache elements related to this model.
*
* @return void
*/
public static function resetCache(): void
{
Cache::forget('allCompanySectors');
self::getAllCompanySectors();
Cache::forget('publishedCompanySectors');
self::getPublishedCompanySectors();
}
/**
* Get all company sectors.
*
* @return \Illuminate\Database\Eloquent\Collection<int,self>
*/
public static function getAllCompanySectors(): \Illuminate\Database\Eloquent\Collection
{
return Cache::remember('allCompanySectors', 86400, function () {
return self::query()->get();
});
}
/**
* Get all published company sectors.
*
* @return \Illuminate\Database\Eloquent\Collection<int,self>
*/
public static function getPublishedCompanySectors(): \Illuminate\Database\Eloquent\Collection
{
return Cache::remember('publishedCompanySectors', 86400, function () {
return self::getAllCompanySectors()->where('published', true);
});
}
}

View file

@ -235,7 +235,7 @@ class DynamicPage extends Model implements TranslatableModel
*/
public static function validateFallbackTranslations(self $model): void
{
if (! $model->status->equals(PageStatusEnum::published)) {
if (!$model->status->equals(PageStatusEnum::published)) {
return;
}
TranslationHelper::validateFallbackTranslations($model);

View file

@ -320,7 +320,7 @@ class News extends Model implements TranslatableModel
*/
public static function validateFallbackTranslations(self $model): void
{
if (! $model->status->equals(NewsStatusEnum::published)) {
if (!$model->status->equals(NewsStatusEnum::published)) {
return;
}
TranslationHelper::validateFallbackTranslations($model);

View file

@ -0,0 +1,109 @@
<?php
namespace App\Policies;
use Illuminate\Database\Eloquent\Model;
use Uwil\LaravelBackend\Enums\RoleEnum;
use Uwil\LaravelBackend\Policies\Rules\UserStaticRules;
class CompanyActivityPolicy
{
/**
* Determine whether the user can view any models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function viewAny(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can view trash models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function viewTrash(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can view the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function view(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can create models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function create(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can update the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function update(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can duplicate the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function duplicate(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can restore the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function restore(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can delete the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function delete(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can delete the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function forceDelete(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace App\Policies;
use Illuminate\Database\Eloquent\Model;
use Uwil\LaravelBackend\Enums\RoleEnum;
use Uwil\LaravelBackend\Policies\Rules\UserStaticRules;
class CompanyPolicy
{
/**
* Determine whether the user can view any models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function viewAny(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can view trash models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function viewTrash(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can view the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function view(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can create models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function create(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can update the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function update(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can duplicate the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function duplicate(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can restore the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function restore(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can delete the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function delete(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can delete the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function forceDelete(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace App\Policies;
use Illuminate\Database\Eloquent\Model;
use Uwil\LaravelBackend\Enums\RoleEnum;
use Uwil\LaravelBackend\Policies\Rules\UserStaticRules;
class CompanySectorPolicy
{
/**
* Determine whether the user can view any models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function viewAny(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can view trash models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function viewTrash(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can view the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function view(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can create models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function create(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can update the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function update(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can duplicate the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function duplicate(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can restore the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function restore(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can delete the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function delete(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can delete the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function forceDelete(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace App\View\Components\Front\Pages\KnowUs;
use App\Enums\News\NewsCategoryEnum;
use App\Models\Company;
use App\Models\CompanyActivity;
use Illuminate\View\Component;
class OurImplantations extends Component
{
public string $id;
public $implantations;
public $companyModels;
public $companyActivityModels;
/**
* Create a new component instance.
*/
public function __construct($id, $implantations)
{
$this->id = $id;
$this->implantations = $implantations;
$this->companyModels = Company::getPublishedCompanies()->whereNotNull('sector_id')->values();
$this->companyActivityModels = CompanyActivity::getPublishedCompanyActivities()->values();
}
/**
* Get the view / contents that represent the component.
*
* @return \Illuminate\Contracts\View\View|\Closure|string
*/
public function render(): \Illuminate\Contracts\View\View|\Closure|string
{
return view('components.front.pages.know-us.our-implantations', [
'companyModels' => $this->companyModels,
'companyActivityModels' => $this->companyActivityModels,
]);
}
}

View file

@ -35,4 +35,7 @@ return [
],
],
'google' => [
'maps' => env('GOOGLE_MAPS_KEY'),
],
];

View file

@ -0,0 +1,34 @@
<?php
namespace Database\Factories;
use App\Enums\DynamicFieldTypes;
use App\Models\CompanyActivity;
use App\Models\CompanySector;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class CompanyFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
$companyActivityModel = fake()->randomElement(CompanyActivity::all());
$companySectorModel = fake()->randomElement(CompanySector::all());
return [
'name' => fake()->company(),
'responsable' => fake()->name(),
'informations' => fake()->text(200),
'latitude' => fake()->latitude(),
'longitude' => fake()->longitude(),
'activity_id' => $companyActivityModel->getKey(),
'sector_id' => $companySectorModel->getKey(),
'published' => fake()->boolean(50),
];
}
}

View file

@ -0,0 +1,134 @@
<?php
use App\Models\Company;
use App\Models\CompanyActivity;
use App\Models\CompanySector;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Uwil\LaravelCommon\Helpers\FileHelper;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::create('company_activities', function (Blueprint $table) {
$table->id();
$table->string('name')->unique()->comment('Name of the company activity.');
$table->string('color')->comment('Color of the company activity.');
$table->boolean('published')->comment('Publish status of the company activity.');
$table->timestamps();
$table->softDeletes();
});
Schema::create('company_sectors', function (Blueprint $table) {
$table->id();
$table->string('name')->unique()->comment('Name of the company sector.');
$table->boolean('published')->comment('Publish status of the company sector.');
$table->timestamps();
$table->softDeletes();
});
Schema::create('companies', function (Blueprint $table) {
$table->id();
$table->string('name')->unique()->comment('Name of the company.');
$table->string('logo')->nullable()->comment('Logo of the company.');
$table->string('responsable')->nullable()->comment('Responsable of the company.');
$table->string('url')->nullable()->comment('Link the website of the company.');
$table->text('informations')->comment('Informations of the company.');
$table->mediumInteger('employees')->nullable()->comment('Effectif number of of the company.');
$table->float('latitude')->comment('Latitude coordination of the company.');
$table->float('longitude')->comment('Longitude of the company.');
$table->foreignId('activity_id')->comment('Activity of the company.')
->constrained('company_activities')->onUpdate('restrict')->onDelete('restrict');
$table->foreignId('sector_id')->nullable()->comment('Sector of the company.')
->constrained('company_sectors')->onUpdate('restrict')->onDelete('restrict');
$table->boolean('published')->comment('Publish status of the company.');
$table->timestamps();
$table->softDeletes();
});
// Export from wordpress...
$jsonPath = storage_path("uploads/export/societes.json");
if (file_exists($jsonPath)) {
$jsonContent = file_get_contents($jsonPath);
$data = json_decode($jsonContent, true);
// Seed sectors.
$sectors = array_unique(array_map(fn($array) => $array['filiere'] ?? null, $data));
$sectors = array_filter($sectors);
$sectors = array_values($sectors);
sort($sectors);
foreach ($sectors as $key => $sector) {
$sectorModel = new CompanySector();
$sectorModel->name = $sector;
$sectorModel->published = true;
$sectorModel->save();
}
// Seed activities.
$activities = array_unique(array_map(fn($array) => $array['type_activite'], $data));
$activities = array_values($activities);
sort($activities);
foreach ($activities as $key => $activity) {
$activityModel = new CompanyActivity();
$activityModel->name = Str::ucFirst($activity);
$activityModel->color = "#b43534";
$activityModel->published = true;
$activityModel->save();
}
// Seed companies.
foreach ($data as $value) {
if ($value['logo']) {
$logoPath = explode("https://www.ldc.fr/app/", $value['logo']);
$logoPath = explode("https://www.ldc.fr/content/", $logoPath[count($logoPath) - 1]);
$logoPath = \storage_path($logoPath[count($logoPath) - 1]);
if (!file_exists($logoPath)) {
$logoPath = null;
}
} else {
$logoPath = null;
}
$companyModel = new Company();
$companyModel->name = $value['titre'];
$companyModel->logo = !is_null($logoPath) ? FileHelper::storeFile($companyModel, new \SplFileInfo($logoPath)) : $logoPath;
$companyModel->responsable = isset($value['responsable']) && !empty($value['responsable']) ? $value['responsable'] : null;
$companyModel->url = isset($value['url']) && !empty($value['url']) ? $value['url'] : null;
$companyModel->informations = $value['adresse'];
$companyModel->latitude = $value['latitude'];
$companyModel->longitude = $value['longitude'];
$companyModel->employees = isset($value['effectif']) && !empty($value['effectif']) ? (int) $value['effectif'] : null;
$companyModel->activity_id = CompanyActivity::getAllCompanyActivities()
->where('name', Str::ucFirst($value['type_activite']))
->first()
->getKey();
$companyModel->sector_id = isset($value['filiere']) ? CompanySector::getAllCompanySectors()
->where('name', Str::ucFirst($value['filiere']))
->first()
->getKey() : null;
$companyModel->published = true;
$companyModel->saveQuietly();
}
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::dropIfExists('companies');
Schema::dropIfExists('company_activities');
Schema::dropIfExists('company_sectors');
}
};

View file

@ -0,0 +1,19 @@
<?php
namespace Database\Seeders;
use App\Models\Company;
use Illuminate\Database\Seeder;
class CompanySeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run(): void
{
Company::factory(10)->create();
}
}

View file

@ -16,7 +16,8 @@ class DatabaseSeeder extends Seeder
$this->call([
PageConfigSeeder::class,
DynamicPageSeeder::class,
NewsSeeder::class
NewsSeeder::class,
CompanySeeder::class,
]);
}
}

View file

@ -1,143 +1,147 @@
<?php
return [
"access_to_front_page" => "link to your page",
"access_to_front_page" => "link to your page",
"access_to_front_page_when_published" => "link to your page when published",
"login_title" => "Authentication",
"login_desc" => "Log in to access the administrative part of your website
"login_title" => "Authentication",
"login_desc" => "Log in to access the administrative part of your website
<span class='font-bold'>:appName</span> !",
"login_btn" => "Connection to UWIL Support",
"login_unpublished_user" => "Your account has been deactivated, please contact a site administrator.",
"dashboard" => "Dashboard",
"your_website" => "Your website",
"pages" => "Pages",
"modules" => "Modules",
"telescope" => "Telescope",
"yes_delete" => "Yes, delete",
"yes_update" => "Yes, update",
"yes_restore" => "Yes, restore",
"yes_archive" => "Yes, archive",
"no_cancel" => "No, cancel",
"yes" => "Yes",
"no" => "No",
"are_you_sure" => "Are you sure ?",
"trash_data" => "All data will be archived !",
"restore_data" => "All data will be restored !",
"lost_data" => "All data will be lost forever !",
"model_action_updated_at" => "Last modified on :",
"profile" => "Profile",
"disconnect" => "Disconnect",
"sidebar_trigger_tooltip" => "Collapse/expand navigation bar",
"warning" => "Warning",
"warning_production" => "You are currently logged into <strong>PRODUCTION</strong>.
"login_btn" => "Connection to UWIL Support",
"login_unpublished_user" => "Your account has been deactivated, please contact a site administrator.",
"dashboard" => "Dashboard",
"your_website" => "Your website",
"pages" => "Pages",
"modules" => "Modules",
"telescope" => "Telescope",
"yes_delete" => "Yes, delete",
"yes_update" => "Yes, update",
"yes_restore" => "Yes, restore",
"yes_archive" => "Yes, archive",
"no_cancel" => "No, cancel",
"yes" => "Yes",
"no" => "No",
"are_you_sure" => "Are you sure ?",
"trash_data" => "All data will be archived !",
"restore_data" => "All data will be restored !",
"lost_data" => "All data will be lost forever !",
"model_action_updated_at" => "Last modified on :",
"profile" => "Profile",
"disconnect" => "Disconnect",
"sidebar_trigger_tooltip" => "Collapse/expand navigation bar",
"warning" => "Warning",
"warning_production" => "You are currently logged into <strong>PRODUCTION</strong>.
To test, please connect to the recipe !",
"warning_trash" => "Deletion of entries after 30 days spent as an archive !",
"dashboard_paragraph_1" => "Welcome to your website's <span class='font-bold'>back-office</span>
"warning_trash" => "Deletion of entries after 30 days spent as an archive !",
"dashboard_paragraph_1" => "Welcome to your website's <span class='font-bold'>back-office</span>
<span class='font-bold'>:appName</span>.",
"dashboard_paragraph_2" => "Here you can manage content that appears on your website, such as images,
"dashboard_paragraph_2" => "Here you can manage content that appears on your website, such as images,
text and much more.",
"dashboard_paragraph_3" => "<span class='font-bold'>UWIL</span> supports you in the
"dashboard_paragraph_3" => "<span class='font-bold'>UWIL</span> supports you in the
<span class='font-bold'>conception</span> and the <span class='font-bold'>design</span> of your current
and future projects.",
"dashboard_paragraph_4" => "Don't hesitate to use our tools <span class='font-bold'>UWIL by LDC</span> :",
"uwil_report_desc" => "Access your site statistics via our tool",
"uwil_support_desc" => "If you have any problems or would like to make any changes, please contact us via
"dashboard_paragraph_4" => "Don't hesitate to use our tools <span class='font-bold'>UWIL by LDC</span> :",
"uwil_report_desc" => "Access your site statistics via our tool",
"uwil_support_desc" => "If you have any problems or would like to make any changes, please contact us via
our tool",
"uwil_expo_desc" => "...",
"uwil_util_tooltip" => "Access our tool in a new tab",
"words" => "Words : ",
"characters" => "Characters : ",
"table_no_results" => "No results.",
"table_field" => "Field",
"table_value" => "Value",
"table_not_known" => "Not known",
"enter_text_here" => "Insert text here",
"enter_number_here" => "Insert number here",
"errors" => "Errors",
"light" => "Light",
"dark" => "Dark",
"system" => "System",
"mods" => "Mods",
"languages" => "Languages",
"settings" => "Settings",
"crud_back_to_list" => "Back to the list of entries",
"crud_add_entry" => "Add a new entry",
"crud_access_trash" => "Access archived entries",
"table_search" => "Search by :searchFields",
"table_search_reset" => "Reset the search",
"table_filter_button" => "Filter results by :field",
"table_filter_reset" => "Remove all selected filters",
"form_title_additional_files" => "Additionnal files",
"form_subtitle_additional_files" => "Add additionnal files",
"form_title_global" => "General information",
"form_subtitle_global" => "Modify the main information related to this data model.",
"form_title_visibility" => "Visibility",
"form_subtitle_visibility" => "Manage account display.",
"form_title_isLocked" => "Restriction",
"form_subtitle_isLocked" => "Manage page locking.",
"form_associate_to" => ":attribute associated to this model|:attribute associated to this model",
"form_associate_to_plural" => ":attribute associated to this model|:attribute associated to this model",
"form_title_config" => "Setup",
"form_subtitle_config" => "Manage configuration settings.",
"form_title_page" => "Configuration",
"form_subtitle_page" => "Manage the content for a dynamic page.",
"form_title_files" => "Files",
"form_subtitle_files" => "Manage files of the model.",
"form_title_seo" => "SEO fields",
"form_subtitle_seo" => "Manage SEO fields of the model",
"form_title_content" => "Content",
"form_subtitle_content" => "Manage content of the model",
"form_title_configuration" => "Configuration",
"form_subtitle_configuration" => "Manage model configuration.",
"form_title_display" => "Display",
"form_subtitle_display" => "Manage the final display of the model",
"access_model" => "Access to the :model",
"close" => "Close",
"clear" => "clear",
"config_modal_title" => "Preview",
"config_modal_descripion" => "Contents of the selected configuration",
"config_none_selected" => "No configuration selected",
"config_select_fields" => "Overwrite fields with those of an existing language",
"config_save_warning" => "The configuration of related pages in :locale must be updated accordingly.",
"config_save_warning_list" => "Here is the list of :models concerned:|Here is the list of :models concerned:",
"config_archived" => "This configuration is currently archived",
"config_complete_label" => "Copy the complete label of the input",
"entries_count" => ":length entry filled in|:length entries filled in",
"new_field" => "Add a new field",
"delete_field" => "Delete this field",
"dnd_field" => "Move this field",
"list_children_needed" => "Children are required for this type :enumLabel",
"empty_warning" => "No data recorded",
"page_locked" => "This page is currently locked",
"unsupported_type" => "Unsupported type:",
"missing_translations" => "The translation of the :field for the language :locales is missing.|The translation
"uwil_expo_desc" => "...",
"uwil_util_tooltip" => "Access our tool in a new tab",
"words" => "Words : ",
"characters" => "Characters : ",
"table_no_results" => "No results.",
"table_field" => "Field",
"table_value" => "Value",
"table_not_known" => "Not known",
"enter_text_here" => "Insert text here",
"enter_number_here" => "Insert number here",
"errors" => "Errors",
"light" => "Light",
"dark" => "Dark",
"system" => "System",
"mods" => "Mods",
"languages" => "Languages",
"settings" => "Settings",
"crud_back_to_list" => "Back to the list of entries",
"crud_add_entry" => "Add a new entry",
"crud_access_trash" => "Access archived entries",
"table_search" => "Search by :searchFields",
"table_search_reset" => "Reset the search",
"table_filter_button" => "Filter results by :field",
"table_filter_reset" => "Remove all selected filters",
"form_title_additional_files" => "Additionnal files",
"form_subtitle_additional_files" => "Add additionnal files",
"form_title_global" => "General information",
"form_subtitle_global" => "Modify the main information related to this data model.",
"form_title_visibility" => "Visibility",
"form_subtitle_visibility" => "Manage account display.",
"form_title_isLocked" => "Restriction",
"form_subtitle_isLocked" => "Manage page locking.",
"form_associate_to" => ":attribute associated to this model|:attribute associated to this model",
"form_associate_to_plural" => ":attribute associated to this model|:attribute associated to this model",
"form_title_config" => "Setup",
"form_subtitle_config" => "Manage configuration settings.",
"form_title_page" => "Configuration",
"form_subtitle_page" => "Manage the content for a dynamic page.",
"form_title_files" => "Files",
"form_subtitle_files" => "Manage files of the model.",
"form_title_seo" => "SEO fields",
"form_subtitle_seo" => "Manage SEO fields of the model",
"form_title_content" => "Content",
"form_subtitle_content" => "Manage content of the model",
"form_title_configuration" => "Configuration",
"form_subtitle_configuration" => "Manage model configuration.",
"form_title_display" => "Display",
"form_subtitle_display" => "Manage the final display of the model",
"form_title_associations" => "Associations",
"form_subtitle_associations" => "Manage model relationships.",
"form_title_implantation" => "Geographic coordinates",
"form_subtitle_implantation" => "Change the location information for this data model.",
"access_model" => "Access to the :model",
"close" => "Close",
"clear" => "clear",
"config_modal_title" => "Preview",
"config_modal_descripion" => "Contents of the selected configuration",
"config_none_selected" => "No configuration selected",
"config_select_fields" => "Overwrite fields with those of an existing language",
"config_save_warning" => "The configuration of related pages in :locale must be updated accordingly.",
"config_save_warning_list" => "Here is the list of :models concerned:|Here is the list of :models concerned:",
"config_archived" => "This configuration is currently archived",
"config_complete_label" => "Copy the complete label of the input",
"entries_count" => ":length entry filled in|:length entries filled in",
"new_field" => "Add a new field",
"delete_field" => "Delete this field",
"dnd_field" => "Move this field",
"list_children_needed" => "Children are required for this type :enumLabel",
"empty_warning" => "No data recorded",
"page_locked" => "This page is currently locked",
"unsupported_type" => "Unsupported type:",
"missing_translations" => "The translation of the :field for the language :locales is missing.|The translation
of the :field for :locales languages are missing.",
"no_more_children" => "Limit reached, you cannot add new items",
"archives" => "archives",
"validate" => "confirm",
"cancel" => "cancel",
"crop" => "crop",
"slugify" => "slugify",
"filter" => "filter|filters",
"image_cropper" => [
"no_more_children" => "Limit reached, you cannot add new items",
"archives" => "archives",
"validate" => "confirm",
"cancel" => "cancel",
"crop" => "crop",
"slugify" => "slugify",
"filter" => "filter|filters",
"image_cropper" => [
"state_file_uploaded" => "a file is uploaded with success",
"modale_title" => "Image modification",
"modale_description" => "Customize the rendering according to your needs",
"action_the_crop" => ":action the crop",
"select_img_zone" => "Drag and drop an image or click to select one",
"toast" => [
"modale_title" => "Image modification",
"modale_description" => "Customize the rendering according to your needs",
"action_the_crop" => ":action the crop",
"select_img_zone" => "Drag and drop an image or click to select one",
"toast" => [
"too_big" => "Max size : :maxSize. This file is too big",
"no_file" => "no file selected !",
"forbidden_file" => "file not allowed.",
"too_many" => "the max file allowed exceeded (1)",
"defaultError" => "an error occured !"
],
"validation" => [
"validation" => [
"rules_between" => "media must do :width px width and :height px height.",
"rules_weight" => "max size: :size",
"allowed_formats" => "allowed formats: :formats"
],
"filters" => [
"filters" => [
'zoomIn' => "zoom in",
"zoomOut" => "zoom out",
"zoom" => "zoom",
@ -154,22 +158,23 @@ return [
"horizontal_flip" => "horizontal flip",
]
],
"multi_select" => [
"multi_select" => [
"search_options" => "search options...",
"select_all" => "select all",
"more" => "more",
],
"simple_date_picker" => [
"simple_date_picker" => [
"reset" => "reset the date"
],
"simple_color_picker" => [
"simple_color_picker" => [
"select" => "select a color",
"reset" => "reset the color",
"delete" => "delete the color",
],
"news" => [
"news" => [
"categories" => [
"placeholder" => "select your article's categories"
]
]
],
"implantations" => "implantations",
];

View file

@ -100,5 +100,9 @@ return [
'newsletter' => [
'label' => 'I would like to subscribe to the newsletter'
]
]
],
'filter_plants' => 'Filter the sites',
'pole' => 'Pole',
'responsible' => 'Responsible',
'address' => 'Address',
];

View file

@ -8,5 +8,8 @@ return [
"activity-log" => "activity log",
"contact-count" => "contact form",
"news" => "news|news",
"company" => "company|companies",
"company-activity" => "activity de société|activities de société",
"company-sector" => "company sector|company sectors",
],
];

View file

@ -247,18 +247,26 @@ return [
"title" => "seo title",
"description" => "seo description",
],
"category" => "category|categories",
"picture" => [
"category" => "category|categories",
"picture" => [
"picture" => "image",
"picture_alt" => "alternative text of the image",
"picture_title" => "title image"
],
"content" => "content",
"content" => "content",
"contactCount" => [
"type" => "form type",
"submissions" => "count of soumissions"
],
"vimeo_id" => "VIMEO ID",
"youtube_id" => "YOUTUBE ID"
"vimeo_id" => "VIMEO ID",
"youtube_id" => "YOUTUBE ID",
"logo" => "logo",
"responsable" => "responsable",
"url" => "url",
"informations" => "informations",
"employees" => "employees",
"latitude" => "latitude",
"longitude" => "longitude",
"color" => "color",
],
];

View file

@ -1,144 +1,148 @@
<?php
return [
"access_to_front_page" => "lien de votre page",
"access_to_front_page" => "lien de votre page",
"access_to_front_page_when_published" => "lien de votre page une fois publiée",
"login_title" => "Authentification",
"login_desc" => "Connectez-vous afin d'accéder à la partie administrative de votre site
"login_title" => "Authentification",
"login_desc" => "Connectez-vous afin d'accéder à la partie administrative de votre site
internet <span class='font-bold'>:appName</span> !",
"login_btn" => "Connexion avec UWIL Support",
"login_unpublished_user" => "Votre compte à été désactivé, veuillez contactez un administrateur du site.",
"dashboard" => "Tableau de bord",
"your_website" => "Votre site",
"pages" => "Pages",
"modules" => "Modules",
"telescope" => "Telescope",
"yes_delete" => "Oui, supprimer",
"yes_update" => "Oui, mettre à jour",
"yes_restore" => "Oui, restorer",
"yes_archive" => "Oui, archiver",
"no_cancel" => "Non, annuler",
"yes" => "Oui",
"no" => "Non",
"are_you_sure" => "Êtes-vous sûr ?",
"trash_data" => "Toutes les données seront archivées !",
"restore_data" => "Toutes les données seront restorées !",
"lost_data" => "Toutes les données seront définitivement perdues !",
"model_action_updated_at" => "Dernières modifications le :",
"profile" => "Profil",
"disconnect" => "Se déconnecter",
"sidebar_trigger_tooltip" => "Réduire/aggrandir la barre de navigation",
"warning" => "Attention",
"warning_production" => "Vous êtes actuellement connecté en <span class='font-bold'>PRODUCTION</span>.
"login_btn" => "Connexion avec UWIL Support",
"login_unpublished_user" => "Votre compte à été désactivé, veuillez contactez un administrateur du site.",
"dashboard" => "Tableau de bord",
"your_website" => "Votre site",
"pages" => "Pages",
"modules" => "Modules",
"telescope" => "Telescope",
"yes_delete" => "Oui, supprimer",
"yes_update" => "Oui, mettre à jour",
"yes_restore" => "Oui, restorer",
"yes_archive" => "Oui, archiver",
"no_cancel" => "Non, annuler",
"yes" => "Oui",
"no" => "Non",
"are_you_sure" => "Êtes-vous sûr ?",
"trash_data" => "Toutes les données seront archivées !",
"restore_data" => "Toutes les données seront restorées !",
"lost_data" => "Toutes les données seront définitivement perdues !",
"model_action_updated_at" => "Dernières modifications le :",
"profile" => "Profil",
"disconnect" => "Se déconnecter",
"sidebar_trigger_tooltip" => "Réduire/aggrandir la barre de navigation",
"warning" => "Attention",
"warning_production" => "Vous êtes actuellement connecté en <span class='font-bold'>PRODUCTION</span>.
Pour tester, veuillez vous connecter à la recette !",
"warning_trash" => "Suppression des entrées après 30 jours passé en temps qu'archive !",
"dashboard_paragraph_1" => "Bienvenue dans le <span class='font-bold'>back-office</span> de votre site internet
"warning_trash" => "Suppression des entrées après 30 jours passé en temps qu'archive !",
"dashboard_paragraph_1" => "Bienvenue dans le <span class='font-bold'>back-office</span> de votre site internet
<span class='font-bold'>:appName</span>.",
"dashboard_paragraph_2" => "Ici, vous pouvez gérer des contenus apparaissant dans votre site internet, tel que
"dashboard_paragraph_2" => "Ici, vous pouvez gérer des contenus apparaissant dans votre site internet, tel que
des images, des textes et bien d'autres éléments.",
"dashboard_paragraph_3" => "<span class='font-bold'>UWIL</span> vous accompagne dans votre
"dashboard_paragraph_3" => "<span class='font-bold'>UWIL</span> vous accompagne dans votre
<span class='font-bold'>conception</span> ainsi que la <span class='font-bold'>réalisation</span> de vos projets
actuels et futurs.",
"dashboard_paragraph_4" => "N'hésitez pas à utiliser nos outils <span class='font-bold'>UWIL by LDC</span> :",
"uwil_report_desc" => "Accéder aux statistiques de votre site via notre outil",
"uwil_support_desc" => "Pour tout problème ou évolution souhaitée, veuillez nous contacter via notre outil",
"uwil_expo_desc" => "...",
"uwil_util_tooltip" => "Accéder à notre outil dans un nouvel onglet",
"words" => "Mots : ",
"characters" => "Caractères : ",
"table_no_results" => "Aucun résultat.",
"table_field" => "Champ",
"table_value" => "Valeur",
"table_not_known" => "Non renseigné",
"enter_text_here" => "Renseigner du texte ici",
"enter_number_here" => "Renseigner un chiffre/nombre ici",
"errors" => "Erreurs",
"light" => "Clair",
"dark" => "Sombre",
"system" => "Système",
"mods" => "Modes",
"languages" => "Langues",
"settings" => "Paramètres",
"crud_back_to_list" => "Revenir à la liste des entrées",
"crud_add_entry" => "Ajouter une nouvelle entrée",
"crud_access_trash" => "Accéder aux entrées archivées",
"table_search" => "Rechercher sur :searchFields",
"table_search_reset" => "Effacer la recherche",
"table_filter_button" => "Filtrer les résultats par le champs :field",
"table_filter_reset" => "Retirer tous les filtres sélectionnés",
"form_title_additional_files" => "Fichier supplémentaire",
"form_subtitle_additional_files" => "Ajouter des fichiers supplémentaires",
"form_title_global" => "Informations générales",
"form_subtitle_global" => "Modifier les informations principales liées à ce modèle de données.",
"form_title_visibility" => "Visibilité",
"form_subtitle_visibility" => "Gérer l'affichage du compte.",
"form_title_isLocked" => "Restriction",
"form_subtitle_isLocked" => "Gérer le vérouillage de la page.",
"form_associate_to" => ":attribute associé au modèle|:attribute associée au modèle",
"form_associate_to_plural" => ":attribute associés au modèle|:attribute associées au modèle",
"form_title_config" => "Réglage",
"form_subtitle_config" => "Gérer le paramétrage d'une configuration.",
"form_title_page" => "Configuration",
"form_subtitle_page" => "Gérer le contenu d'une page dynamique.",
"form_title_files" => "Fichiers",
"form_subtitle_files" => "Gérer les fichiers du modèle.",
"form_title_seo" => "Champs SEO",
"form_subtitle_seo" => "Gérer les champs SEO du modèle.",
"form_title_content" => "Contenu",
"form_subtitle_content" => "Gérer le contenu du modèle.",
"form_title_configuration" => "Configuration",
"form_subtitle_configuration" => "Gérer la configuration du modèle.",
"form_title_display" => "Affichage",
"form_subtitle_display" => "Gérer l'ordre d'affichage sur le rendu final.",
"access_model" => "Accéder à la :model",
"clear" => "vider",
"close" => "Fermer",
"config_modal_title" => "Prévisualisation",
"config_modal_descripion" => "Contenu de la configuration sélectionnée",
"config_none_selected" => "Aucune configuration sélectionnée",
"config_select_fields" => "Écraser les champs avec ceux d'une langue déjà existante",
"config_save_warning" => "La configuration des pages associées dans la langue :locale devra être mise à jour
"dashboard_paragraph_4" => "N'hésitez pas à utiliser nos outils <span class='font-bold'>UWIL by LDC</span> :",
"uwil_report_desc" => "Accéder aux statistiques de votre site via notre outil",
"uwil_support_desc" => "Pour tout problème ou évolution souhaitée, veuillez nous contacter via notre outil",
"uwil_expo_desc" => "...",
"uwil_util_tooltip" => "Accéder à notre outil dans un nouvel onglet",
"words" => "Mots : ",
"characters" => "Caractères : ",
"table_no_results" => "Aucun résultat.",
"table_field" => "Champ",
"table_value" => "Valeur",
"table_not_known" => "Non renseigné",
"enter_text_here" => "Renseigner du texte ici",
"enter_number_here" => "Renseigner un chiffre/nombre ici",
"errors" => "Erreurs",
"light" => "Clair",
"dark" => "Sombre",
"system" => "Système",
"mods" => "Modes",
"languages" => "Langues",
"settings" => "Paramètres",
"crud_back_to_list" => "Revenir à la liste des entrées",
"crud_add_entry" => "Ajouter une nouvelle entrée",
"crud_access_trash" => "Accéder aux entrées archivées",
"table_search" => "Rechercher sur :searchFields",
"table_search_reset" => "Effacer la recherche",
"table_filter_button" => "Filtrer les résultats par le champs :field",
"table_filter_reset" => "Retirer tous les filtres sélectionnés",
"form_title_additional_files" => "Fichier supplémentaire",
"form_subtitle_additional_files" => "Ajouter des fichiers supplémentaires",
"form_title_global" => "Informations générales",
"form_subtitle_global" => "Modifier les informations principales liées à ce modèle de données.",
"form_title_visibility" => "Visibilité",
"form_subtitle_visibility" => "Gérer l'affichage du compte.",
"form_title_isLocked" => "Restriction",
"form_subtitle_isLocked" => "Gérer le vérouillage de la page.",
"form_associate_to" => ":attribute associé au modèle|:attribute associée au modèle",
"form_associate_to_plural" => ":attribute associés au modèle|:attribute associées au modèle",
"form_title_config" => "Réglage",
"form_subtitle_config" => "Gérer le paramétrage d'une configuration.",
"form_title_page" => "Configuration",
"form_subtitle_page" => "Gérer le contenu d'une page dynamique.",
"form_title_files" => "Fichiers",
"form_subtitle_files" => "Gérer les fichiers du modèle.",
"form_title_seo" => "Champs SEO",
"form_subtitle_seo" => "Gérer les champs SEO du modèle.",
"form_title_content" => "Contenu",
"form_subtitle_content" => "Gérer le contenu du modèle.",
"form_title_configuration" => "Configuration",
"form_subtitle_configuration" => "Gérer la configuration du modèle.",
"form_title_display" => "Affichage",
"form_subtitle_display" => "Gérer l'ordre d'affichage sur le rendu final.",
"form_title_associations" => "Associations",
"form_subtitle_associations" => "Gérer les relations du modèle.",
"form_title_implantation" => "Coordonnées géographiques",
"form_subtitle_implantation" => "Modifiez les informations de localisation de ce modèle de données.",
"access_model" => "Accéder à la :model",
"clear" => "vider",
"close" => "Fermer",
"config_modal_title" => "Prévisualisation",
"config_modal_descripion" => "Contenu de la configuration sélectionnée",
"config_none_selected" => "Aucune configuration sélectionnée",
"config_select_fields" => "Écraser les champs avec ceux d'une langue déjà existante",
"config_save_warning" => "La configuration des pages associées dans la langue :locale devra être mise à jour
en conséquence.",
"config_save_warning_list" => "Voici la liste des :models concernés:|Voici la liste des :models concernées:",
"config_archived" => "Cette configuration est actuellement archivée",
"config_complete_label" => "Copier le libellé complet de l'input",
"entries_count" => ":length entrée renseignée|:length entrées renseignées",
"new_field" => "Ajouter un nouveau champ",
"delete_field" => "Supprimer ce champ",
"dnd_field" => "Déplacer ce champ",
"list_children_needed" => "Des enfants sont nécessaires pour ce type :enumLabel",
"empty_warning" => "Aucune donnée enregistrée",
"page_locked" => "Cette page est actuellement vérouillée",
"unsupported_type" => "Type non supporté:",
"missing_translations" => "La traduction du champs :field pour la langue :locales est manquante.|La traduction
du champs :field pour l es langues :locales sont manquantes.",
"no_more_children" => "Limite atteinte, vous ne pouvez pas ajouter de nouveaux éléments",
"archives" => "archives",
"validate" => "valider",
"cancel" => "annuler",
"crop" => "recadrer",
"slugify" => "slugifier",
"filter" => "filtre|filtres",
"image_cropper" => [
"config_save_warning_list" => "Voici la liste des :models concernés:|Voici la liste des :models concernées:",
"config_archived" => "Cette configuration est actuellement archivée",
"config_complete_label" => "Copier le libellé complet de l'input",
"entries_count" => ":length entrée renseignée|:length entrées renseignées",
"new_field" => "Ajouter un nouveau champ",
"delete_field" => "Supprimer ce champ",
"dnd_field" => "Déplacer ce champ",
"list_children_needed" => "Des enfants sont nécessaires pour ce type :enumLabel",
"empty_warning" => "Aucune donnée enregistrée",
"page_locked" => "Cette page est actuellement vérouillée",
"unsupported_type" => "Type non supporté:",
"missing_translations" => "La traduction du champs :field pour la langue :locales est manquante.|La traduction
du champs :field pour l es langues :locales sont manquantes.",
"no_more_children" => "Limite atteinte, vous ne pouvez pas ajouter de nouveaux éléments",
"archives" => "archives",
"validate" => "valider",
"cancel" => "annuler",
"crop" => "recadrer",
"slugify" => "slugifier",
"filter" => "filtre|filtres",
"image_cropper" => [
"state_file_uploaded" => "un fichier à bien été chargé",
"modale_title" => "Modification de l'image",
"modale_description" => "Personnalisez le rendu selon vos besoins",
"action_the_crop" => ":action le cadrage",
"select_img_zone" => "Glisser-déposer une image ou cliquez pour en sélectionner une",
"toast" => [
"modale_title" => "Modification de l'image",
"modale_description" => "Personnalisez le rendu selon vos besoins",
"action_the_crop" => ":action le cadrage",
"select_img_zone" => "Glisser-déposer une image ou cliquez pour en sélectionner une",
"toast" => [
"too_big" => "taille maximale autorisée : :maxSize. Le fichier est trop volumineux",
"no_file" => "aucun fichier sélectionné !",
"forbidden_file" => "fichier non autorisé.",
"too_many" => "le nombre de fichier autorisé est dépassé (1)",
"defaultError" => "une erreur est survenue !"
],
"validation" => [
"validation" => [
"rules_between" => "le média doit faire :width px de large et :height px de haut.",
"rules_weight" => "taille max: :size",
"allowed_formats" => "formats autorisés: :formats",
"all_formats" => "tous"
],
"filters" => [
"filters" => [
'zoomIn' => "zoom avant",
"zoomOut" => "zoom arrière",
"zoom" => "zoom",
@ -155,22 +159,23 @@ return [
"horizontal_flip" => "inverser horizontalement",
]
],
"multi_select" => [
"multi_select" => [
"search_options" => "cherchez parmi les options...",
"select_all" => "tout sélectionner",
"more" => "supplémentaire(s)",
],
"simple_date_picker" => [
"simple_date_picker" => [
"reset" => "vider la date",
],
"simple_color_picker" => [
"simple_color_picker" => [
"select" => "sélectionner une couleur",
"reset" => "réinitialiser la couleur",
"delete" => "supprimer la couleur",
],
"news" => [
"news" => [
"categories" => [
"placeholder" => "sélectionnez les catégories de votre article",
]
]
],
"implantations" => "implantations",
];

View file

@ -100,5 +100,9 @@ return [
'newsletter' => [
'label' => 'Je souhaite m’inscrire à la newsletter'
]
]
],
'filter_plants' => 'Filtrer les implantations',
'pole' => 'Pôle',
'responsible' => 'Responsable',
'address' => 'Adresse',
];

View file

@ -8,5 +8,8 @@ return [
"activity-log" => "journal d'activité",
"contact-count" => "formulaire de contact",
"news" => "actualité|actualités",
"company" => "société|sociétés",
"company-activity" => "activité de société|activités de société",
"company-sector" => "secteur de société|secteurs de société",
],
];

View file

@ -256,7 +256,15 @@ return [
"type" => "type de formulaire",
"submissions" => "nombre de soumissions"
],
"vimeo_id" => "ID de la vidéo VIMEO",
"youtube_id" => "ID de la vidéo YOUTUBE"
"vimeo_id" => "ID de la vidéo VIMEO",
"youtube_id" => "ID de la vidéo YOUTUBE",
"logo" => "logo",
"responsable" => "responsable",
"url" => "lien",
"informations" => "informations",
"employees" => "employés",
"latitude" => "latitude",
"longitude" => "longitude",
"color" => "couleur",
],
];

View file

@ -32,6 +32,7 @@ data:
GOOGLE_RECAPTCHA_PRIVATE: "${APP_GOOGLE_RECAPTCHA_PRIVATE}"
GOOGLE_RECAPTCHA_PUBLIC: "${APP_GOOGLE_RECAPTCHA_PUBLIC}"
GOOGLE_RECAPTCHA_VERSION: "${APP_GOOGLE_RECAPTCHA_VERSION}"
GOOGLE_MAPS_KEY: "${APP_GOOGLE_MAPS_KEY}"
SAML2_SP_CERT_x509: "${APP_SAML2_SP_CERT_x509}"
SAML2_SP_CERT_PRIVATEKEY: "${APP_SAML2_SP_CERT_PRIVATEKEY}"

View file

@ -13,6 +13,9 @@ import {
HistoryIcon,
ContactIcon,
NewspaperIcon,
Building2Icon,
RadarIcon,
RollerCoasterIcon,
} from "lucide-react";
import { route } from "ziggy-js";
// * Components shadcn
@ -128,7 +131,7 @@ export default function AppSidebar() {
},
{
title: useTrans("back.modules"),
show: authPolicies.dynamicPages.viewAny || authPolicies.pageConfigs.viewAny,
show: authPolicies.news.viewAny || auth.policies.contact_counts.viewAny,
links: [
{
title: str(useTrans("models.classes.news", { choice: 2 }))
@ -166,6 +169,91 @@ export default function AppSidebar() {
},
],
},
{
title: str(useTrans("back.implantations")).ucFirst().value(),
show: auth.policies.companies.viewAny || auth.policies.companyActivities.viewAny || auth.policies.companySectors.viewAny,
links: [
{
title: str(useTrans("models.classes.company", { choice: 2 }))
.ucFirst()
.plural()
.value(),
model: "companies",
icon: Building2Icon,
show: auth.policies.companies.viewAny,
actions: (model: string) => filterActions({
items: [
authPolicies.companies.create && (
<CustomDropdownMenuItem
key="create"
route={route(`back.${model}.create`)}
/>
),
authPolicies.companies.viewTrash && (
<CustomDropdownMenuItem
key="trash"
route={route(`back.${model}.trash`)}
icon={Views.ARCHIVATION.icon}
trans={str(useTrans("back.archives")).ucFirst().value()}
/>
),
]
})
},
{
title: str(useTrans("models.classes.company-activity", { choice: 2 }))
.ucFirst()
.value(),
model: "company-activities",
icon: RollerCoasterIcon,
show: auth.policies.companyActivities.viewAny,
actions: (model: string) => filterActions({
items: [
authPolicies.companyActivities.create && (
<CustomDropdownMenuItem
key="create"
route={route(`back.${model}.create`)}
/>
),
authPolicies.companyActivities.viewTrash && (
<CustomDropdownMenuItem
key="trash"
route={route(`back.${model}.trash`)}
icon={Views.ARCHIVATION.icon}
trans={str(useTrans("back.archives")).ucFirst().value()}
/>
),
]
})
},
{
title: str(useTrans("models.classes.company-sector", { choice: 2 }))
.ucFirst()
.value(),
model: "company-sectors",
icon: RadarIcon,
show: auth.policies.companySectors.viewAny,
actions: (model: string) => filterActions({
items: [
authPolicies.companySectors.create && (
<CustomDropdownMenuItem
key="create"
route={route(`back.${model}.create`)}
/>
),
authPolicies.companySectors.viewTrash && (
<CustomDropdownMenuItem
key="trash"
route={route(`back.${model}.trash`)}
icon={Views.ARCHIVATION.icon}
trans={str(useTrans("back.archives")).ucFirst().value()}
/>
),
]
})
},
],
},
{
title: str(useTrans("enums.user-role.admin")).plural().value(),
show: auth.user.role === enumUserRole.conceptor.value,

View file

@ -0,0 +1,57 @@
// * Packages
import { usePage } from "@inertiajs/react";
import { route } from "ziggy-js";
import { ArrowLeftIcon } from "lucide-react";
// * Other
import AuthenticatedLayout from "@/back/Layouts/AuthenticatedLayout";
import Form from "@/back/Pages/Companies/Form";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
import useTrans from "@/hooks/use-translations";
import { Actions, Methods } from "@/lib/enums";
import {
AuthProp,
CompanyModel,
SingleResource,
} from "@/back/types/global";
export default function Create({ data }: { data: SingleResource }) {
const { viewAny } = (usePage().props.auth as AuthProp).policies.companies;
const companyModel = data.data as CompanyModel;
const transTitle = useTrans(
route().current(`back.companies.duplicate`)
? "crud.helpers.duplication_model"
: "crud.helpers.creation_model",
{
params: {
model: useTrans("models.classes.company", { choice: 1 }),
},
},
);
const beforeNode = () => {
return viewAny && (
<div className="flex justify-end items-center gap-x-1.5">
<BtnOptionnalLinkWithTooltip
link={route("back.companies.index")}
content={<ArrowLeftIcon />}
tooltipContent={<p>{useTrans("back.crud_back_to_list")}</p>}
/>
</div>
);
};
return (
<AuthenticatedLayout
title={transTitle}
beforeNode={beforeNode()}
>
<Form
model={companyModel}
modelName="company"
modelRoute="companies"
action={Actions.STORE}
method={Methods.POST}
/>
</AuthenticatedLayout>
);
}

View file

@ -0,0 +1,58 @@
// * Packages
import { usePage } from "@inertiajs/react";
import { route } from "ziggy-js";
import { ArrowLeftIcon } from "lucide-react";
// * Other
import AuthenticatedLayout from "@/back/Layouts/AuthenticatedLayout";
import ModelActions from "@/back/Components/ModelActions";
import Form from "@/back/Pages/Companies/Form";
import useTrans from "@/hooks/use-translations";
import { Actions, Methods } from "@/lib/enums";
import { Views } from "@/lib/utils";
import { AuthProp, CompanyModel, SingleResource } from "@/back/types/global";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
export default function Edit({ data }: { data: SingleResource }) {
const { viewAny } = (usePage().props.auth as AuthProp).policies.companies;
const companyModel = data.data as CompanyModel;
const actionModels = [
Views.VISUALIZATION,
Views.DUPLICATION,
Views.ARCHIVATION,
];
const beforeNode = () => {
return viewAny && (
<div className="flex justify-end items-center gap-x-1.5">
<BtnOptionnalLinkWithTooltip
link={route("back.companies.index")}
content={<ArrowLeftIcon />}
tooltipContent={<p>{useTrans("back.crud_back_to_list")}</p>}
/>
</div>
);
};
return (
<AuthenticatedLayout
title={useTrans("crud.helpers.edition_model", {
params: { model: useTrans("models.classes.company", { choice: 1 }) },
})}
beforeNode={beforeNode()}
>
<ModelActions
actions={actionModels}
modelRoute="companies"
model={companyModel}
modelUpdatedAt={companyModel.updated_at.toString()}
/>
<Form
model={companyModel}
modelName="company"
modelRoute="companies"
action={Actions.UPDATE}
method={Methods.PATCH}
/>
</AuthenticatedLayout>
);
}

View file

@ -0,0 +1,317 @@
// * Packages
import { useForm, usePage } from "@inertiajs/react";
import { BookTextIcon, EyeIcon, MapPinIcon, RouteIcon } from "lucide-react";
import { route } from "ziggy-js";
import { FormEvent } from "react";
// * Other
import SimpleString from "@/back/Components/Inputs/SimpleString";
import SimpleNumber from "@/back/Components/Inputs/SimpleNumber";
import FieldsetWrapper from "@/back/Components/FieldsetWrapper";
import SimpleSwitch from "@/back/Components/Inputs/SimpleSwitch";
import CompleteInputFile from "@/back/Components/Inputs/CompleteInputFile";
import SimpleWysiwyg from "@/back/Components/Inputs/SimpleWysiwyg";
import ModelSelect from "@/back/Components/Inputs/ModelSelect";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
import useTrans from "@/hooks/use-translations";
import str from "@/hooks/use-string";
import InputError from "@/back/Components/InputError";
import { useToast } from "@/hooks/use-toast";
import { Methods } from "@/lib/enums";
import { CompanyActivityModel, CompanyModel, CompanySectorModel, DynamicFieldTypes } from "@/back/types/global";
export default function Form({
model,
modelName,
modelRoute,
action,
method,
}: {
model?: CompanyModel;
modelName: string;
modelRoute: string;
action: string;
method: number;
}) {
const enumDynamicFieldTypes = usePage().props.enumDynamicFieldTypes as DynamicFieldTypes;
const companyActivityModels = usePage().props.companyActivityModels as CompanyActivityModel[];
const companySectorModels = usePage().props.companySectorModels as CompanySectorModel[];
const { toast } = useToast();
/** Set messages success/errors */
const messages = {} as Record<string, string>;
messages.titleSuccess = useTrans(
method === Methods.POST ? "Création réussie !" : "Mise à jour réussie !",
);
messages.descriptionSuccess = str(
useTrans(
method === Methods.POST ? "crud.messages.has_been_created" : "crud.messages.has_been_updated",
{ params: { model: useTrans(`models.classes.${modelName}`) } },
),
).ucFirst().value();
messages.titleError = useTrans(
method === Methods.POST ? "Création annulée !" : "Mise à jour annulée !",
);
messages.descriptionError = str(
useTrans(
method === Methods.POST ? "crud.messages.cannot_be_created" : "crud.messages.cannot_be_updated",
{ params: { model: useTrans(`models.classes.${modelName}`) } },
),
).ucFirst().value();
/** Form section */
const { post, setData, data, reset } = useForm({
_method: method === Methods.POST ? 'post' : 'put',
name: model?.name,
logo: model?.logo ?? "",
responsable: model?.responsable ?? "",
url: model?.url ?? "",
informations: model?.informations,
employees: model?.employees ?? 0,
latitude: model?.latitude,
longitude: model?.longitude,
activity_id: model?.activity_id,
sector_id: model?.sector_id,
published: model?.published,
});
const submit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const routeUrl = route(`back.${modelRoute}.${action}`, model?.id);
if (method !== Methods.PATCH && method !== Methods.POST) throw new Error("The method must be post or patch");
post(routeUrl, {
preserveScroll: true,
onSuccess: () => {
toast({
title: messages.titleSuccess,
description: messages.descriptionSuccess,
});
},
onError: () => {
toast({
variant: "destructive",
title: messages.titleError,
description: messages.descriptionError,
});
},
});
};
return (
<form onSubmit={submit}>
<InputError all />
<FieldsetWrapper
title={useTrans("back.form_title_global")}
icon={<BookTextIcon className="mr-1" />}
description={useTrans("back.form_subtitle_global")}
className="sm:grid-cols-2 gap-4"
>
<SimpleString
name="name"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.attributes.name"),
},
choice: 1,
}),
).ucFirst().value()}
placeholder="ex: John Doe"
value={data.name}
setData={setData}
className="justify-start"
/>
<CompleteInputFile
fileName="logo"
modelRoute="companies"
name="logo"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.custom.logo"),
},
choice: 1,
}),
).ucFirst().value()}
setData={setData}
reset={reset}
value={data.logo}
required={false}
maxFileSize="5120"
allowedFormats="jpg,jpeg,png,webp"
validationRules={{
width: "500",
height: "500"
}}
inputType={enumDynamicFieldTypes.image.value}
/>
<SimpleString
name="responsable"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.custom.responsable"),
},
choice: 1,
}),
).ucFirst().value()}
placeholder="ex: John Doe"
required={false}
value={data.responsable}
setData={setData}
/>
<SimpleString
name="url"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.custom.url"),
},
choice: 1,
}),
).ucFirst().value()}
type="url"
placeholder="ex: https://..."
required={false}
value={data.url}
setData={setData}
/>
<SimpleWysiwyg
name="informations"
model="Companies"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.custom.informations"),
},
choice: 2,
}),
).ucFirst().value()}
value={data.informations}
setData={setData}
/>
<SimpleNumber
name="employees"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.custom.employees"),
},
choice: 2,
}),
).ucFirst().value()}
placeholder="ex: 258"
limits={{ min: 0, max: 100000 }}
required={false}
value={data.employees}
setData={setData}
/>
</FieldsetWrapper>
<FieldsetWrapper
title={useTrans("back.form_title_associations")}
icon={<RouteIcon className="mr-1" />}
description={useTrans("back.form_subtitle_associations")}
className="sm:grid-cols-2 gap-4"
>
<ModelSelect
name="activity_id"
options={companyActivityModels}
field="name"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("models.classes.company-activity", { choice: 1 }),
},
choice: 1,
}),
)
.ucFirst()
.value()}
placeholder="ex: John Doe"
selected={data.activity_id}
setData={setData}
/>
<ModelSelect
name="sector_id"
options={companySectorModels}
field="name"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("models.classes.company-sector", { choice: 1 }),
},
choice: 1,
}),
)
.ucFirst()
.value()}
placeholder="ex: John Doe"
selected={data.sector_id}
setData={setData}
/>
</FieldsetWrapper>
<FieldsetWrapper
title={useTrans("back.form_title_implantation")}
icon={<MapPinIcon className="mr-1" />}
description={useTrans("back.form_subtitle_implantation")}
className="sm:grid-cols-2 gap-4"
>
<SimpleNumber
name="latitude"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.custom.latitude"),
},
choice: 2,
}),
).ucFirst().value()}
placeholder="ex: 47.827475"
step={0.00000000000000000001}
limits={{ min: -90, max: 90 }}
value={data.latitude}
setData={setData}
/>
<SimpleNumber
name="longitude"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.custom.longitude"),
},
choice: 2,
}),
).ucFirst().value()}
limits={{ min: -180, max: 180 }}
placeholder="ex: -0.309621"
step={0.00000000000000000001}
value={data.longitude}
setData={setData}
/>
</FieldsetWrapper>
<FieldsetWrapper
className="gap-4"
title={useTrans("back.form_title_visibility")}
icon={<EyeIcon className="mr-1" />}
description={useTrans("back.form_subtitle_visibility")}
inline
>
<SimpleSwitch
name="published"
checked={data.published}
setData={setData}
/>
</FieldsetWrapper>
<div className="text-center">
<BtnOptionnalLinkWithTooltip
content={str(useTrans("crud.actions.save")).ucFirst().value()}
tooltipContent={useTrans("crud.helpers.save_model", {
params: {
model: useTrans(`models.classes.${modelName}`, { choice: 1 }),
},
})}
type="submit"
size="default"
/>
</div>
</form>
);
}

View file

@ -0,0 +1,172 @@
// * Packages
import { route } from "ziggy-js";
import { usePage } from "@inertiajs/react";
import { Row } from "@tanstack/react-table";
import { AlertCircleIcon, ArchiveIcon, ArrowLeftIcon, CheckIcon, PlusIcon, XIcon } from "lucide-react";
// * Components shadcn
import { Badge } from "@/components/ui/badge";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
// * Other
import AuthenticatedLayout from "@/back/Layouts/AuthenticatedLayout";
import DataTable from "@/back/Components/Table/DataTable";
import DataTableSearch from "@/back/Components/Table/DataTableSearch";
import DataTableFilterButton from "@/back/Components/Table/DataTableFilterButton";
import InputError from "@/back/Components/InputError";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
import { Views } from "@/lib/utils";
import {
AuthProp,
CompanyModel,
Resource,
} from "@/back/types/global";
import str from "@/hooks/use-string";
import useTrans from "@/hooks/use-translations";
export default function Index({
companyModels,
search,
searchFields,
}: {
companyModels: Resource;
search: string;
searchFields: string;
}) {
const { auth } = usePage().props as unknown as {
auth: AuthProp;
};
const { viewAny, viewTrash, create } = auth.policies.companies;
const isArchiveRoute = route().current("back.companies.trash");
const actionModels = isArchiveRoute ? [
Views.RESTORATION,
Views.FORCEDELETION,
] : [
Views.VISUALIZATION,
Views.EDITION,
Views.DUPLICATION,
Views.ARCHIVATION,
];
const columns = [
{
accessorKey: "name",
header: ({ column }: { column: { id: string } }) => (
<DataTableFilterButton
label={str(useTrans(`validation.attributes.${column.id}`)).ucFirst().value()}
field={column.id}
/>
),
cell: ({ row }: { row: Row<CompanyModel> }) => row.getValue("name"),
},
{
accessorKey: "latitude",
header: ({ column }: { column: { id: string } }) => (
<DataTableFilterButton
label={str(useTrans(`validation.custom.${column.id}`)).ucFirst().value()}
field={column.id}
/>
),
cell: ({ row }: { row: Row<CompanyModel> }) => row.getValue("latitude"),
},
{
accessorKey: "longitude",
header: ({ column }: { column: { id: string } }) => (
<DataTableFilterButton
label={str(useTrans(`validation.custom.${column.id}`)).ucFirst().value()}
field={column.id}
/>
),
cell: ({ row }: { row: Row<CompanyModel> }) => row.getValue("longitude"),
},
{
accessorKey: "published",
header: ({ column }: { column: { id: string } }) => (
<DataTableFilterButton
label={str(useTrans(`validation.custom.${column.id}`)).ucFirst().value()}
field={column.id}
/>
),
cell: ({ row }: { row: Row<CompanyModel> }) => (
<Badge
variant="outline"
className={`border-0 ${
row.getValue("published") ? "bg-green-200 dark:bg-green-900" : "bg-red-200 dark:bg-red-900"
}`}
>
{row.getValue("published") ? (
<CheckIcon size={15} className="mr-1" />
) : (
<XIcon size={15} className="mr-1" />
)}
{str(useTrans(row.getValue("published") ? "back.yes" : "back.no")).ucFirst().value()}
</Badge>
),
},
];
const transModel = useTrans("models.classes.company", { choice: 2 });
const transArchiveTitle = useTrans("crud.helpers.archives_model", { params: { model: transModel }})
const transTitle = str(isArchiveRoute ? transArchiveTitle : transModel).ucFirst().value();
const beforeNode = () => {
return isArchiveRoute && viewAny && (
<div className="flex justify-end items-center gap-x-1.5">
<BtnOptionnalLinkWithTooltip
link={route("back.companies.index")}
content={<ArrowLeftIcon />}
tooltipContent={<p>{useTrans("back.crud_back_to_list")}</p>}
/>
</div>
);
};
const afterNode = () => {
return !isArchiveRoute && (
<div className="flex justify-end items-center gap-x-1.5">
{viewTrash && <BtnOptionnalLinkWithTooltip
link={route("back.companies.trash")}
content={<ArchiveIcon />}
tooltipContent={<p>{useTrans("back.crud_access_trash")}</p>}
variant={"outline"}
/>}
{create && <BtnOptionnalLinkWithTooltip
link={route("back.companies.create")}
content={<PlusIcon />}
tooltipContent={<p>{useTrans("back.crud_add_entry")}</p>}
/>}
</div>
);
};
return (
<AuthenticatedLayout
title={transTitle}
beforeNode={beforeNode()}
afterNode={afterNode()}
>
{isArchiveRoute && (
<>
<Alert
variant="destructive"
className="text-left mb-4"
>
<AlertCircleIcon className="h-4 w-4" />
<AlertTitle>{useTrans('back.warning')}</AlertTitle>
<AlertDescription>
<p>{useTrans('back.warning_trash')}</p>
</AlertDescription>
</Alert>
<InputError all={true} />
</>
)}
<DataTableSearch
modelRoute="companies"
searchText={search}
searchFields={searchFields}
/>
<DataTable
columns={columns}
modelRoute="companies"
modelsPaginate={companyModels}
actions={actionModels}
/>
</AuthenticatedLayout>
);
}

View file

@ -0,0 +1,128 @@
// * Packages
import { usePage } from "@inertiajs/react";
import { route } from "ziggy-js";
// * Components shadcn
import { Badge } from "@/components/ui/badge";
// * Other
import AuthenticatedLayout from "@/back/Layouts/AuthenticatedLayout";
import DetailsTable from "@/back/Components/Table/DetailsTable";
import ModelActions from "@/back/Components/ModelActions";
import { Views } from "@/lib/utils";
import { useFormatedDate } from "@/hooks/use-formated-value";
import {
AppEnv,
AuthProp,
CompanyModel,
SingleResource,
} from "@/back/types/global";
import str from "@/hooks/use-string";
import useTrans from "@/hooks/use-translations";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
import { ArrowLeftIcon } from "lucide-react";
export default function Show({ data }: { data: SingleResource }) {
const { auth, appEnv } = usePage().props as unknown as {
auth: AuthProp;
appEnv: AppEnv;
};
const { viewAny } = auth.policies.companies;
const companyModel = data.data as CompanyModel;
const actionModels = [
Views.EDITION,
Views.DUPLICATION,
Views.ARCHIVATION,
];
const columns = [
{
accessorKey: "name",
header: str(useTrans("validation.attributes.name")).ucFirst().value(),
cell: ({ value }: { value: string }) => value,
},
{
accessorKey: "logo",
header: str(useTrans("validation.custom.logo")).ucFirst().value(),
cell: ({ value }: { value: string }) => value,
},
{
accessorKey: "responsable",
header: str(useTrans("validation.custom.responsable")).ucFirst().value(),
cell: ({ value }: { value: string }) => value,
},
{
accessorKey: "url",
header: str(useTrans("validation.custom.url")).ucFirst().value(),
cell: ({ value }: { value: string }) => value,
},
{
accessorKey: "informations",
header: str(useTrans("validation.custom.informations")).ucFirst().value(),
cell: ({ value }: { value: string }) => value,
},
{
accessorKey: "employees",
header: str(useTrans("validation.custom.employees")).ucFirst().value(),
cell: ({ value }: { value: number }) => value,
},
{
accessorKey: "latitude",
header: str(useTrans("validation.custom.latitude")).ucFirst().value(),
cell: ({ value }: { value: number }) => value,
},
{
accessorKey: "longitude",
header: str(useTrans("validation.custom.longitude")).ucFirst().value(),
cell: ({ value }: { value: number }) => value,
},
{
accessorKey: "created_at",
header: str(useTrans("validation.attributes.created_at")).ucFirst().value(),
cell: ({ value }: { value: string }) => (
<Badge variant="secondary">{useFormatedDate(value)}</Badge>
),
},
{
accessorKey: "updated_at",
header: str(useTrans("validation.attributes.updated_at")).ucFirst().value(),
cell: ({ value }: { value: string }) => (
<Badge variant="secondary">{useFormatedDate(value)}</Badge>
),
},
{
accessorKey: "deleted_at",
header: str(useTrans("validation.attributes.deleted_at")).ucFirst().value(),
cell: ({ value }: { value: string }) => (
<Badge variant="secondary">{useFormatedDate(value)}</Badge>
),
},
];
const beforeNode = () => {
return viewAny && (
<div className="flex justify-end items-center gap-x-1.5">
<BtnOptionnalLinkWithTooltip
link={route("back.companies.index")}
content={<ArrowLeftIcon />}
tooltipContent={<p>{useTrans("back.crud_back_to_list")}</p>}
/>
</div>
);
};
return (
<AuthenticatedLayout
title={useTrans("crud.helpers.visualization_model", {
params: { model: useTrans("models.classes.company", { choice: 1 })},
})}
beforeNode={beforeNode()}
>
<ModelActions
actions={actionModels}
modelRoute="companies"
model={companyModel}
modelUpdatedAt={companyModel.updated_at.toString()}
/>
{/* @ts-expect-error Weird error type on cell. */}
<DetailsTable columns={columns} model={companyModel} />
</AuthenticatedLayout>
);
}

View file

@ -0,0 +1,57 @@
// * Packages
import { usePage } from "@inertiajs/react";
import { route } from "ziggy-js";
import { ArrowLeftIcon } from "lucide-react";
// * Other
import AuthenticatedLayout from "@/back/Layouts/AuthenticatedLayout";
import Form from "@/back/Pages/CompanyActivities/Form";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
import useTrans from "@/hooks/use-translations";
import { Actions, Methods } from "@/lib/enums";
import {
AuthProp,
CompanyActivityModel,
SingleResource,
} from "@/back/types/global";
export default function Create({ data }: { data: SingleResource }) {
const { viewAny } = (usePage().props.auth as AuthProp).policies.companyActivities;
const companyActivityModel = data.data as CompanyActivityModel;
const transTitle = useTrans(
route().current(`back.company-activities.duplicate`)
? "crud.helpers.duplication_model"
: "crud.helpers.creation_model",
{
params: {
model: useTrans("models.classes.company-activity", { choice: 1 }),
},
},
);
const beforeNode = () => {
return viewAny && (
<div className="flex justify-end items-center gap-x-1.5">
<BtnOptionnalLinkWithTooltip
link={route("back.company-activities.index")}
content={<ArrowLeftIcon />}
tooltipContent={<p>{useTrans("back.crud_back_to_list")}</p>}
/>
</div>
);
};
return (
<AuthenticatedLayout
title={transTitle}
beforeNode={beforeNode()}
>
<Form
model={companyActivityModel}
modelName="company-activity"
modelRoute="company-activities"
action={Actions.STORE}
method={Methods.POST}
/>
</AuthenticatedLayout>
);
}

View file

@ -0,0 +1,58 @@
// * Packages
import { usePage } from "@inertiajs/react";
import { route } from "ziggy-js";
import { ArrowLeftIcon } from "lucide-react";
// * Other
import AuthenticatedLayout from "@/back/Layouts/AuthenticatedLayout";
import ModelActions from "@/back/Components/ModelActions";
import Form from "@/back/Pages/CompanyActivities/Form";
import useTrans from "@/hooks/use-translations";
import { Actions, Methods } from "@/lib/enums";
import { Views } from "@/lib/utils";
import { AuthProp, CompanyActivityModel, SingleResource } from "@/back/types/global";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
export default function Edit({ data }: { data: SingleResource }) {
const { viewAny } = (usePage().props.auth as AuthProp).policies.companyActivities;
const companyActivityModel = data.data as CompanyActivityModel;
const actionModels = [
Views.VISUALIZATION,
Views.DUPLICATION,
Views.ARCHIVATION,
];
const beforeNode = () => {
return viewAny && (
<div className="flex justify-end items-center gap-x-1.5">
<BtnOptionnalLinkWithTooltip
link={route("back.company-activities.index")}
content={<ArrowLeftIcon />}
tooltipContent={<p>{useTrans("back.crud_back_to_list")}</p>}
/>
</div>
);
};
return (
<AuthenticatedLayout
title={useTrans("crud.helpers.edition_model", {
params: { model: useTrans("models.classes.company-activity", { choice: 1 }) },
})}
beforeNode={beforeNode()}
>
<ModelActions
actions={actionModels}
modelRoute="company-activities"
model={companyActivityModel}
modelUpdatedAt={companyActivityModel.updated_at.toString()}
/>
<Form
model={companyActivityModel}
modelName="company-activity"
modelRoute="company-activities"
action={Actions.UPDATE}
method={Methods.PATCH}
/>
</AuthenticatedLayout>
);
}

View file

@ -0,0 +1,160 @@
// * Packages
import { useForm, usePage } from "@inertiajs/react";
import { BookTextIcon, EyeIcon } from "lucide-react";
import { route } from "ziggy-js";
import { FormEvent } from "react";
// * Other
import SimpleString from "@/back/Components/Inputs/SimpleString";
import FieldsetWrapper from "@/back/Components/FieldsetWrapper";
import SimpleSwitch from "@/back/Components/Inputs/SimpleSwitch";
import SimpleColorPicker from "@/back/Components/Inputs/SimpleColorPicker";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
import useTrans from "@/hooks/use-translations";
import str from "@/hooks/use-string";
import InputError from "@/back/Components/InputError";
import { useToast } from "@/hooks/use-toast";
import { Methods } from "@/lib/enums";
import { AppEnv, CompanyActivityModel } from "@/back/types/global";
export default function Form({
model,
modelName,
modelRoute,
action,
method,
}: {
model?: CompanyActivityModel;
modelName: string;
modelRoute: string;
action: string;
method: number;
}) {
const { locale } = usePage().props.appEnv as AppEnv;
const { toast } = useToast();
/** Set messages success/errors */
const messages = {} as Record<string, string>;
messages.titleSuccess = useTrans(
method === Methods.POST ? "Création réussie !" : "Mise à jour réussie !",
);
messages.descriptionSuccess = str(
useTrans(
method === Methods.POST ? "crud.messages.has_been_created" : "crud.messages.has_been_updated",
{ params: { model: useTrans(`models.classes.${modelName}`, { choice: 1 }) } },
),
).ucFirst().value();
messages.titleError = useTrans(
method === Methods.POST ? "Création annulée !" : "Mise à jour annulée !",
);
messages.descriptionError = str(
useTrans(
method === Methods.POST ? "crud.messages.cannot_be_created" : "crud.messages.cannot_be_updated",
{ params: { model: useTrans(`models.classes.${modelName}`, { choice: 1 }) } },
),
).ucFirst().value();
/** Form section */
const { post, patch, setData, data } = useForm({
name: model?.name ? model?.name[locale] : "",
color: model?.color ?? null,
published: model?.published,
});
const routeOptions = () => {
return {
preserveScroll: true,
onSuccess: () => {
toast({
title: messages.titleSuccess,
description: messages.descriptionSuccess,
});
},
onError: () => {
toast({
variant: "destructive",
title: messages.titleError,
description: messages.descriptionError,
});
},
};
};
const submit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const routeUrl = route(`back.${modelRoute}.${action}`, model?.id);
switch (method) {
case Methods.POST:
post(routeUrl, routeOptions());
break;
case Methods.PATCH:
patch(routeUrl, routeOptions());
break;
default:
throw new Error("The method must be post or patch");
}
};
return (
<form onSubmit={submit}>
<InputError all />
<FieldsetWrapper
title={useTrans("back.form_title_global")}
icon={<BookTextIcon className="mr-1" />}
description={useTrans("back.form_subtitle_global")}
className="sm:grid-cols-2 gap-4"
>
<SimpleString
name="name"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.attributes.name"),
},
choice: 1,
}),
).ucFirst().value()}
placeholder="ex: John Doe"
value={data.name}
setData={setData}
/>
<SimpleColorPicker
name="color"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.custom.color"),
},
choice: 1,
}),
).ucFirst().value()}
setData={setData}
value={data.color}
required={true}
/>
</FieldsetWrapper>
<FieldsetWrapper
className="gap-4"
title={useTrans("back.form_title_visibility")}
icon={<EyeIcon className="mr-1" />}
description={useTrans("back.form_subtitle_visibility")}
inline
>
<SimpleSwitch
name="published"
checked={data.published}
setData={setData}
/>
</FieldsetWrapper>
<div className="text-center">
<BtnOptionnalLinkWithTooltip
content={str(useTrans("crud.actions.save")).ucFirst().value()}
tooltipContent={useTrans("crud.helpers.save_model", {
params: {
model: useTrans(`models.classes.${modelName}`, { choice: 1 }),
},
})}
type="submit"
size="default"
/>
</div>
</form>
);
}

View file

@ -0,0 +1,181 @@
// * Packages
import { route } from "ziggy-js";
import { usePage } from "@inertiajs/react";
import { Row } from "@tanstack/react-table";
import { AlertCircleIcon, ArchiveIcon, ArrowLeftIcon, CheckIcon, PlusIcon, XIcon } from "lucide-react";
// * Components shadcn
import { Badge } from "@/components/ui/badge";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
// * Other
import AuthenticatedLayout from "@/back/Layouts/AuthenticatedLayout";
import DataTable from "@/back/Components/Table/DataTable";
import DataTableSearch from "@/back/Components/Table/DataTableSearch";
import DataTableFilterButton from "@/back/Components/Table/DataTableFilterButton";
import InputError from "@/back/Components/InputError";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
import { Views } from "@/lib/utils";
import {
AppEnv,
AuthProp,
CompanyActivityModel,
Resource,
TranslationField,
} from "@/back/types/global";
import str from "@/hooks/use-string";
import useTrans from "@/hooks/use-translations";
export default function Index({
companyActivityModels,
search,
searchFields,
}: {
companyActivityModels: Resource;
search: string;
searchFields: string;
}) {
const { auth, appEnv } = usePage().props as unknown as {
auth: AuthProp;
appEnv: AppEnv;
};
const { viewAny, viewTrash, create } = auth.policies.companyActivities;
const isArchiveRoute = route().current("back.company-activities.trash");
const actionModels = isArchiveRoute ? [
Views.RESTORATION,
Views.FORCEDELETION,
] : [
Views.VISUALIZATION,
Views.EDITION,
Views.DUPLICATION,
Views.ARCHIVATION,
];
const columns = [
{
accessorKey: "name",
header: ({ column }: { column: { id: string } }) => (
<DataTableFilterButton
label={str(useTrans(`validation.attributes.${column.id}`)).ucFirst().value()}
field={column.id}
/>
),
cell: ({ row }: { row: Row<CompanyActivityModel> }) => {
return (row.getValue("name") as TranslationField)[appEnv.locale] ? (
(row.getValue("name") as TranslationField)[appEnv.locale]
) : (<p className="text-muted-foreground italic m-0">{useTrans('back.table_not_known')}</p>);
},
},
{
accessorKey: "color",
header: ({ column }: { column: { id: string } }) => (
<DataTableFilterButton
label={str(useTrans(`validation.custom.${column.id}`)).ucFirst().value()}
field={column.id}
/>
),
cell: ({ row }: { row: Row<CompanyActivityModel> }) => {
return (
<Tooltip>
<TooltipTrigger asChild>
<div className="w-8 h-8 rounded-md mx-auto border" style={{
backgroundColor: row.getValue("color"),
}}></div>
</TooltipTrigger>
<TooltipContent>{row.getValue("color")}</TooltipContent>
</Tooltip>
)
},
},
{
accessorKey: "published",
header: ({ column }: { column: { id: string } }) => (
<DataTableFilterButton
label={str(useTrans(`validation.custom.${column.id}`)).ucFirst().value()}
field={column.id}
/>
),
cell: ({ row }: { row: Row<CompanyActivityModel> }) => (
<Badge
variant="outline"
className={`border-0 ${
row.getValue("published") ? "bg-green-200 dark:bg-green-900" : "bg-red-200 dark:bg-red-900"
}`}
>
{row.getValue("published") ? (
<CheckIcon size={15} className="mr-1" />
) : (
<XIcon size={15} className="mr-1" />
)}
{str(useTrans(row.getValue("published") ? "back.yes" : "back.no")).ucFirst().value()}
</Badge>
),
},
];
const transModel = useTrans("models.classes.company-activity", { choice: 2 });
const transArchiveTitle = useTrans("crud.helpers.archives_model", { params: { model: transModel }})
const transTitle = str(isArchiveRoute ? transArchiveTitle : transModel).ucFirst().value();
const beforeNode = () => {
return isArchiveRoute && viewAny && (
<div className="flex justify-end items-center gap-x-1.5">
<BtnOptionnalLinkWithTooltip
link={route("back.company-activities.index")}
content={<ArrowLeftIcon />}
tooltipContent={<p>{useTrans("back.crud_back_to_list")}</p>}
/>
</div>
);
};
const afterNode = () => {
return !isArchiveRoute && (
<div className="flex justify-end items-center gap-x-1.5">
{viewTrash && <BtnOptionnalLinkWithTooltip
link={route("back.company-activities.trash")}
content={<ArchiveIcon />}
tooltipContent={<p>{useTrans("back.crud_access_trash")}</p>}
variant={"outline"}
/>}
{create && <BtnOptionnalLinkWithTooltip
link={route("back.company-activities.create")}
content={<PlusIcon />}
tooltipContent={<p>{useTrans("back.crud_add_entry")}</p>}
/>}
</div>
);
};
return (
<AuthenticatedLayout
title={transTitle}
beforeNode={beforeNode()}
afterNode={afterNode()}
>
{isArchiveRoute && (
<>
<Alert
variant="destructive"
className="text-left mb-4"
>
<AlertCircleIcon className="h-4 w-4" />
<AlertTitle>{useTrans('back.warning')}</AlertTitle>
<AlertDescription>
<p>{useTrans('back.warning_trash')}</p>
</AlertDescription>
</Alert>
<InputError all={true} />
</>
)}
<DataTableSearch
modelRoute="company-activities"
searchText={search}
searchFields={searchFields}
/>
<DataTable
columns={columns}
modelRoute="company-activities"
modelsPaginate={companyActivityModels}
actions={actionModels}
/>
</AuthenticatedLayout>
);
}

View file

@ -0,0 +1,120 @@
// * Packages
import { usePage } from "@inertiajs/react";
import { route } from "ziggy-js";
// * Components shadcn
import { Badge } from "@/components/ui/badge";
// * Other
import AuthenticatedLayout from "@/back/Layouts/AuthenticatedLayout";
import DetailsTable from "@/back/Components/Table/DetailsTable";
import ModelActions from "@/back/Components/ModelActions";
import { Views } from "@/lib/utils";
import { useFormatedDate } from "@/hooks/use-formated-value";
import {
AppEnv,
AuthProp,
CompanyActivityModel,
SingleResource,
TranslationField,
} from "@/back/types/global";
import str from "@/hooks/use-string";
import useTrans from "@/hooks/use-translations";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
import { ArrowLeftIcon, CheckIcon, XIcon } from "lucide-react";
export default function Show({ data }: { data: SingleResource }) {
const { auth, appEnv } = usePage().props as unknown as {
auth: AuthProp;
appEnv: AppEnv;
};
const { viewAny } = auth.policies.companyActivities;
const companyActivityModel = data.data as CompanyActivityModel;
const actionModels = [
Views.EDITION,
Views.DUPLICATION,
Views.ARCHIVATION,
];
const columns = [
{
accessorKey: "name",
header: str(useTrans("validation.attributes.name")).ucFirst().value(),
cell: ({ value }: { value: TranslationField }) => value[appEnv.locale]
? value[appEnv.locale]
: (<p className="text-muted-foreground italic m-0">{useTrans('back.table_not_known')}</p>),
},
{
accessorKey: "color",
header: str(useTrans("validation.custom.color")).ucFirst().value(),
cell: ({ value }: { value: string }) => value,
},
{
accessorKey: "created_at",
header: str(useTrans("validation.attributes.created_at")).ucFirst().value(),
cell: ({ value }: { value: string }) => (
<Badge variant="secondary">{useFormatedDate(value)}</Badge>
),
},
{
accessorKey: "published",
header: str(useTrans("validation.custom.published")).ucFirst().value(),
cell: ({ value }: { value: boolean }) => (
<Badge
variant="outline"
className={`border-0 ${
value ? "bg-green-200 dark:bg-green-900" : "bg-red-200 dark:bg-red-900"
}`}
>
{value ? (
<CheckIcon size={15} className="mr-1" />
) : (
<XIcon size={15} className="mr-1" />
)}
{str(useTrans(value ? "back.yes" : "back.no")).ucFirst().value()}
</Badge>
),
},
{
accessorKey: "updated_at",
header: str(useTrans("validation.attributes.updated_at")).ucFirst().value(),
cell: ({ value }: { value: string }) => (
<Badge variant="secondary">{useFormatedDate(value)}</Badge>
),
},
{
accessorKey: "deleted_at",
header: str(useTrans("validation.attributes.deleted_at")).ucFirst().value(),
cell: ({ value }: { value: string }) => (
<Badge variant="secondary">{useFormatedDate(value)}</Badge>
),
},
];
const beforeNode = () => {
return viewAny && (
<div className="flex justify-end items-center gap-x-1.5">
<BtnOptionnalLinkWithTooltip
link={route("back.company-activities.index")}
content={<ArrowLeftIcon />}
tooltipContent={<p>{useTrans("back.crud_back_to_list")}</p>}
/>
</div>
);
};
return (
<AuthenticatedLayout
title={useTrans("crud.helpers.visualization_model", {
params: { model: useTrans("models.classes.company-activity", { choice: 1 })},
})}
beforeNode={beforeNode()}
>
<ModelActions
actions={actionModels}
modelRoute="company-activities"
model={companyActivityModel}
modelUpdatedAt={companyActivityModel.updated_at.toString()}
/>
{/* @ts-expect-error Weird error type on cell. */}
<DetailsTable columns={columns} model={companyActivityModel} />
</AuthenticatedLayout>
);
}

View file

@ -0,0 +1,57 @@
// * Packages
import { usePage } from "@inertiajs/react";
import { route } from "ziggy-js";
import { ArrowLeftIcon } from "lucide-react";
// * Other
import AuthenticatedLayout from "@/back/Layouts/AuthenticatedLayout";
import Form from "@/back/Pages/CompanySectors/Form";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
import useTrans from "@/hooks/use-translations";
import { Actions, Methods } from "@/lib/enums";
import {
AuthProp,
CompanySectorModel,
SingleResource,
} from "@/back/types/global";
export default function Create({ data }: { data: SingleResource }) {
const { viewAny } = (usePage().props.auth as AuthProp).policies.companySectors;
const companySectorModel = data.data as CompanySectorModel;
const transTitle = useTrans(
route().current(`back.company-sectors.duplicate`)
? "crud.helpers.duplication_model"
: "crud.helpers.creation_model",
{
params: {
model: useTrans("models.classes.company-sector", { choice: 1 }),
},
},
);
const beforeNode = () => {
return viewAny && (
<div className="flex justify-end items-center gap-x-1.5">
<BtnOptionnalLinkWithTooltip
link={route("back.company-sectors.index")}
content={<ArrowLeftIcon />}
tooltipContent={<p>{useTrans("back.crud_back_to_list")}</p>}
/>
</div>
);
};
return (
<AuthenticatedLayout
title={transTitle}
beforeNode={beforeNode()}
>
<Form
model={companySectorModel}
modelName="company-sector"
modelRoute="company-sectors"
action={Actions.STORE}
method={Methods.POST}
/>
</AuthenticatedLayout>
);
}

View file

@ -0,0 +1,58 @@
// * Packages
import { usePage } from "@inertiajs/react";
import { route } from "ziggy-js";
import { ArrowLeftIcon } from "lucide-react";
// * Other
import AuthenticatedLayout from "@/back/Layouts/AuthenticatedLayout";
import ModelActions from "@/back/Components/ModelActions";
import Form from "@/back/Pages/CompanySectors/Form";
import useTrans from "@/hooks/use-translations";
import { Actions, Methods } from "@/lib/enums";
import { Views } from "@/lib/utils";
import { AuthProp, CompanySectorModel, SingleResource } from "@/back/types/global";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
export default function Edit({ data }: { data: SingleResource }) {
const { viewAny } = (usePage().props.auth as AuthProp).policies.companySectors;
const companySectorModel = data.data as CompanySectorModel;
const actionModels = [
Views.VISUALIZATION,
Views.DUPLICATION,
Views.ARCHIVATION,
];
const beforeNode = () => {
return viewAny && (
<div className="flex justify-end items-center gap-x-1.5">
<BtnOptionnalLinkWithTooltip
link={route("back.company-sectors.index")}
content={<ArrowLeftIcon />}
tooltipContent={<p>{useTrans("back.crud_back_to_list")}</p>}
/>
</div>
);
};
return (
<AuthenticatedLayout
title={useTrans("crud.helpers.edition_model", {
params: { model: useTrans("models.classes.company-sector", { choice: 1 }) },
})}
beforeNode={beforeNode()}
>
<ModelActions
actions={actionModels}
modelRoute="company-sectors"
model={companySectorModel}
modelUpdatedAt={companySectorModel.updated_at.toString()}
/>
<Form
model={companySectorModel}
modelName="company-sector"
modelRoute="company-sectors"
action={Actions.UPDATE}
method={Methods.PATCH}
/>
</AuthenticatedLayout>
);
}

View file

@ -0,0 +1,144 @@
// * Packages
import { useForm, usePage } from "@inertiajs/react";
import { BookTextIcon, EyeIcon } from "lucide-react";
import { route } from "ziggy-js";
import { FormEvent } from "react";
// * Other
import SimpleString from "@/back/Components/Inputs/SimpleString";
import FieldsetWrapper from "@/back/Components/FieldsetWrapper";
import SimpleSwitch from "@/back/Components/Inputs/SimpleSwitch";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
import useTrans from "@/hooks/use-translations";
import str from "@/hooks/use-string";
import InputError from "@/back/Components/InputError";
import { useToast } from "@/hooks/use-toast";
import { Methods } from "@/lib/enums";
import { AppEnv, CompanySectorModel } from "@/back/types/global";
export default function Form({
model,
modelName,
modelRoute,
action,
method,
}: {
model?: CompanySectorModel;
modelName: string;
modelRoute: string;
action: string;
method: number;
}) {
const { locale } = usePage().props.appEnv as AppEnv;
const { toast } = useToast();
/** Set messages success/errors */
const messages = {} as Record<string, string>;
messages.titleSuccess = useTrans(
method === Methods.POST ? "Création réussie !" : "Mise à jour réussie !",
);
messages.descriptionSuccess = str(
useTrans(
method === Methods.POST ? "crud.messages.has_been_created" : "crud.messages.has_been_updated",
{ params: { model: useTrans(`models.classes.${modelName}`, { choice: 1 }) } },
),
).ucFirst().value();
messages.titleError = useTrans(
method === Methods.POST ? "Création annulée !" : "Mise à jour annulée !",
);
messages.descriptionError = str(
useTrans(
method === Methods.POST ? "crud.messages.cannot_be_created" : "crud.messages.cannot_be_updated",
{ params: { model: useTrans(`models.classes.${modelName}`, { choice: 1 }) } },
),
).ucFirst().value();
/** Form section */
const { post, patch, setData, data } = useForm({
name: model?.name ? model?.name[locale] : "",
published: model?.published,
});
const routeOptions = () => {
return {
preserveScroll: true,
onSuccess: () => {
toast({
title: messages.titleSuccess,
description: messages.descriptionSuccess,
});
},
onError: () => {
toast({
variant: "destructive",
title: messages.titleError,
description: messages.descriptionError,
});
},
};
};
const submit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
const routeUrl = route(`back.${modelRoute}.${action}`, model?.id);
switch (method) {
case Methods.POST:
post(routeUrl, routeOptions());
break;
case Methods.PATCH:
patch(routeUrl, routeOptions());
break;
default:
throw new Error("The method must be post or patch");
}
};
return (
<form onSubmit={submit}>
<InputError all />
<FieldsetWrapper
title={useTrans("back.form_title_global")}
icon={<BookTextIcon className="mr-1" />}
description={useTrans("back.form_subtitle_global")}
className="sm:grid-cols-2 gap-4"
>
<SimpleString
name="name"
label={str(
useTrans("back.form_associate_to", {
params: {
attribute: useTrans("validation.attributes.name"),
},
choice: 1,
}),
).ucFirst().value()}
placeholder="ex: John Doe"
value={data.name}
setData={setData}
/>
</FieldsetWrapper>
<FieldsetWrapper
className="gap-4"
title={useTrans("back.form_title_visibility")}
icon={<EyeIcon className="mr-1" />}
description={useTrans("back.form_subtitle_visibility")}
inline
>
<SimpleSwitch
name="published"
checked={data.published}
setData={setData}
/>
</FieldsetWrapper>
<div className="text-center">
<BtnOptionnalLinkWithTooltip
content={str(useTrans("crud.actions.save")).ucFirst().value()}
tooltipContent={useTrans("crud.helpers.save_model", {
params: {
model: useTrans(`models.classes.${modelName}`, { choice: 1 }),
},
})}
type="submit"
size="default"
/>
</div>
</form>
);
}

View file

@ -0,0 +1,159 @@
// * Packages
import { route } from "ziggy-js";
import { usePage } from "@inertiajs/react";
import { Row } from "@tanstack/react-table";
import { AlertCircleIcon, ArchiveIcon, ArrowLeftIcon, CheckIcon, PlusIcon, XIcon } from "lucide-react";
// * Components shadcn
import { Badge } from "@/components/ui/badge";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
// * Other
import AuthenticatedLayout from "@/back/Layouts/AuthenticatedLayout";
import DataTable from "@/back/Components/Table/DataTable";
import DataTableSearch from "@/back/Components/Table/DataTableSearch";
import DataTableFilterButton from "@/back/Components/Table/DataTableFilterButton";
import InputError from "@/back/Components/InputError";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
import { Views } from "@/lib/utils";
import {
AppEnv,
AuthProp,
CompanySectorModel,
Resource,
TranslationField,
} from "@/back/types/global";
import str from "@/hooks/use-string";
import useTrans from "@/hooks/use-translations";
export default function Index({
companySectorModels,
search,
searchFields,
}: {
companySectorModels: Resource;
search: string;
searchFields: string;
}) {
const { auth, appEnv } = usePage().props as unknown as {
auth: AuthProp;
appEnv: AppEnv;
};
const { viewAny, viewTrash, create } = auth.policies.companySectors;
const isArchiveRoute = route().current("back.company-sectors.trash");
const actionModels = isArchiveRoute ? [
Views.RESTORATION,
Views.FORCEDELETION,
] : [
Views.VISUALIZATION,
Views.EDITION,
Views.DUPLICATION,
Views.ARCHIVATION,
];
const columns = [
{
accessorKey: "name",
header: ({ column }: { column: { id: string } }) => (
<DataTableFilterButton
label={str(useTrans(`validation.attributes.${column.id}`)).ucFirst().value()}
field={column.id}
/>
),
cell: ({ row }: { row: Row<CompanySectorModel> }) => {
return (row.getValue("name") as TranslationField)[appEnv.locale] ? (
(row.getValue("name") as TranslationField)[appEnv.locale]
) : (<p className="text-muted-foreground italic m-0">{useTrans('back.table_not_known')}</p>);
},
},
{
accessorKey: "published",
header: ({ column }: { column: { id: string } }) => (
<DataTableFilterButton
label={str(useTrans(`validation.custom.${column.id}`)).ucFirst().value()}
field={column.id}
/>
),
cell: ({ row }: { row: Row<CompanySectorModel> }) => (
<Badge
variant="outline"
className={`border-0 ${
row.getValue("published") ? "bg-green-200 dark:bg-green-900" : "bg-red-200 dark:bg-red-900"
}`}
>
{row.getValue("published") ? (
<CheckIcon size={15} className="mr-1" />
) : (
<XIcon size={15} className="mr-1" />
)}
{str(useTrans(row.getValue("published") ? "back.yes" : "back.no")).ucFirst().value()}
</Badge>
),
},
];
const transModel = useTrans("models.classes.company-sector", { choice: 2 });
const transArchiveTitle = useTrans("crud.helpers.archives_model", { params: { model: transModel }})
const transTitle = str(isArchiveRoute ? transArchiveTitle : transModel).ucFirst().value();
const beforeNode = () => {
return isArchiveRoute && viewAny && (
<div className="flex justify-end items-center gap-x-1.5">
<BtnOptionnalLinkWithTooltip
link={route("back.company-sectors.index")}
content={<ArrowLeftIcon />}
tooltipContent={<p>{useTrans("back.crud_back_to_list")}</p>}
/>
</div>
);
};
const afterNode = () => {
return !isArchiveRoute && (
<div className="flex justify-end items-center gap-x-1.5">
{viewTrash && <BtnOptionnalLinkWithTooltip
link={route("back.company-sectors.trash")}
content={<ArchiveIcon />}
tooltipContent={<p>{useTrans("back.crud_access_trash")}</p>}
variant={"outline"}
/>}
{create && <BtnOptionnalLinkWithTooltip
link={route("back.company-sectors.create")}
content={<PlusIcon />}
tooltipContent={<p>{useTrans("back.crud_add_entry")}</p>}
/>}
</div>
);
};
return (
<AuthenticatedLayout
title={transTitle}
beforeNode={beforeNode()}
afterNode={afterNode()}
>
{isArchiveRoute && (
<>
<Alert
variant="destructive"
className="text-left mb-4"
>
<AlertCircleIcon className="h-4 w-4" />
<AlertTitle>{useTrans('back.warning')}</AlertTitle>
<AlertDescription>
<p>{useTrans('back.warning_trash')}</p>
</AlertDescription>
</Alert>
<InputError all={true} />
</>
)}
<DataTableSearch
modelRoute="company-sectors"
searchText={search}
searchFields={searchFields}
/>
<DataTable
columns={columns}
modelRoute="company-sectors"
modelsPaginate={companySectorModels}
actions={actionModels}
/>
</AuthenticatedLayout>
);
}

View file

@ -0,0 +1,115 @@
// * Packages
import { usePage } from "@inertiajs/react";
import { route } from "ziggy-js";
// * Components shadcn
import { Badge } from "@/components/ui/badge";
// * Other
import AuthenticatedLayout from "@/back/Layouts/AuthenticatedLayout";
import DetailsTable from "@/back/Components/Table/DetailsTable";
import ModelActions from "@/back/Components/ModelActions";
import { Views } from "@/lib/utils";
import { useFormatedDate } from "@/hooks/use-formated-value";
import {
AppEnv,
AuthProp,
CompanySectorModel,
SingleResource,
TranslationField,
} from "@/back/types/global";
import str from "@/hooks/use-string";
import useTrans from "@/hooks/use-translations";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
import { ArrowLeftIcon, CheckIcon, XIcon } from "lucide-react";
export default function Show({ data }: { data: SingleResource }) {
const { auth, appEnv } = usePage().props as unknown as {
auth: AuthProp;
appEnv: AppEnv;
};
const { viewAny } = auth.policies.companySectors;
const companySectorModel = data.data as CompanySectorModel;
const actionModels = [
Views.EDITION,
Views.DUPLICATION,
Views.ARCHIVATION,
];
const columns = [
{
accessorKey: "name",
header: str(useTrans("validation.attributes.name")).ucFirst().value(),
cell: ({ value }: { value: TranslationField }) => value[appEnv.locale]
? value[appEnv.locale]
: (<p className="text-muted-foreground italic m-0">{useTrans('back.table_not_known')}</p>),
},
{
accessorKey: "published",
header: str(useTrans("validation.custom.published")).ucFirst().value(),
cell: ({ value }: { value: boolean }) => (
<Badge
variant="outline"
className={`border-0 ${
value ? "bg-green-200 dark:bg-green-900" : "bg-red-200 dark:bg-red-900"
}`}
>
{value ? (
<CheckIcon size={15} className="mr-1" />
) : (
<XIcon size={15} className="mr-1" />
)}
{str(useTrans(value ? "back.yes" : "back.no")).ucFirst().value()}
</Badge>
),
},
{
accessorKey: "created_at",
header: str(useTrans("validation.attributes.created_at")).ucFirst().value(),
cell: ({ value }: { value: string }) => (
<Badge variant="secondary">{useFormatedDate(value)}</Badge>
),
},
{
accessorKey: "updated_at",
header: str(useTrans("validation.attributes.updated_at")).ucFirst().value(),
cell: ({ value }: { value: string }) => (
<Badge variant="secondary">{useFormatedDate(value)}</Badge>
),
},
{
accessorKey: "deleted_at",
header: str(useTrans("validation.attributes.deleted_at")).ucFirst().value(),
cell: ({ value }: { value: string }) => (
<Badge variant="secondary">{useFormatedDate(value)}</Badge>
),
},
];
const beforeNode = () => {
return viewAny && (
<div className="flex justify-end items-center gap-x-1.5">
<BtnOptionnalLinkWithTooltip
link={route("back.companies.index")}
content={<ArrowLeftIcon />}
tooltipContent={<p>{useTrans("back.crud_back_to_list")}</p>}
/>
</div>
);
};
return (
<AuthenticatedLayout
title={useTrans("crud.helpers.visualization_model", {
params: { model: useTrans("models.classes.company-sector", { choice: 1 })},
})}
beforeNode={beforeNode()}
>
<ModelActions
actions={actionModels}
modelRoute="company-sectors"
model={companySectorModel}
modelUpdatedAt={companySectorModel.updated_at.toString()}
/>
{/* @ts-expect-error Weird error type on cell. */}
<DetailsTable columns={columns} model={companySectorModel} />
</AuthenticatedLayout>
);
}

View file

@ -327,7 +327,6 @@ export default function Form({
type="button"
variant="secondary"
onClick={() => {
console.log("test");
setData("slug", str(data.title).slug().value());
}}
/>

View file

@ -132,6 +132,43 @@ type NewsCategoryModel = {
category: number;
}
type CompanyModel = {
id: number;
name: string;
logo: string;
responsable: string;
url: string;
informations: string;
employees: number;
latitude: number;
longitude: number;
activity_id: number;
sector_id: number|null;
published: boolean;
created_at: Date;
updated_at: Date;
deleted_at: Date;
}
type CompanyActivityModel = {
id: number;
name: TranslationField;
color: string;
published: boolean;
created_at: Date;
updated_at: Date;
deleted_at: Date;
}
type CompanySectorModel = {
id: number;
name: TranslationField;
published: boolean;
created_at: Date;
updated_at: Date;
deleted_at: Date;
}
// Default model, with policies.
type DefaultModel = {
policies: ModelPolicy;
@ -147,7 +184,10 @@ type Model =
| PageConfigModel
| DynamicPageModel
| NewsModel
| ActivityLogModel;
| ActivityLogModel
| CompanyModel
| CompanyActivityModel
| CompanySectorModel;
// Common policy type.
type CommonPolicy = {

View file

@ -53,7 +53,8 @@
<div data-anchor="{{ $joinUs['data-anchor'] }}" id="{{ $joinUs['id'] }}">
<div class="breeders fw fw-container">
<div class="breeders__wrapper wrapper">
<x-front.sections.title title="{{ $joinUs['title'] }}" subtitle="{{ $joinUs['subtitle'] }}" class="breeders__title" secondaryClass="breeders__subtitle" />
<x-front.sections.title title="{{ $joinUs['title'] }}" subtitle="{{ $joinUs['subtitle'] }}"
class="breeders__title" secondaryClass="breeders__subtitle" />
<div class="textblock__container">
<div class="textblock__block">
<div class="breeders__text description textblock__info-description">

View file

@ -52,7 +52,7 @@
<x-front.borders :borders="!empty($politics['image_borders']) ? $politics['image_borders'] : []" />
<img src="/assets/uploads/2020/07/politique-RH-2eme-photo.png" alt="NOTRE POLITIQUE">
</div>
<x-front.cta title="Nos valeurs" url="{{ route('front.dynamic-pages.nous-connaitre') }}#nos-valeurs" class="textblock__info-btn" buttonIcon="btn__icon_arrow-right" />
<x-front.cta title="Nos valeurs" url="{{ \App\Helpers\RouteHelper::routeNameChecker('front.dynamic-pages.nous-connaitre') }}#nos-valeurs" class="textblock__info-btn" buttonIcon="btn__icon_arrow-right" />
</div>
<div class="textblock__info textblock__block">
<div class="textblock__info-description description ">

View file

@ -261,7 +261,8 @@
<div data-anchor="{{ $professions['title'] . " " . $professions['subtitle'] }}" id="rejoin_0">
<div class="professions fw fw-container">
<div class="professions__wrapper wrapper">
<x-front.sections.title title="{{ $professions['title'] }}" subtitle="{{ $professions['subtitle'] }}" class="professions__title" secondaryClass="professions__subtitle" />
<x-front.sections.title title="{{ $professions['title'] }}" subtitle="{{ $professions['subtitle'] }}"
class="professions__title" secondaryClass="professions__subtitle" />
<div class="professions__text description">
<h3>{{ $professions['text'] }}</h3>
<p>&nbsp;</p>

View file

@ -99,7 +99,8 @@
<div data-anchor="{{ $career['data-anchor'] }}" id="{{ $career['id'] }}">
<div class="experience fw fw-container gray-bg">
<div class="experience__wrapper wrapper">
<x-front.sections.title title="{{ $career['title'] }}" subtitle="{{ $career['subtitle'] }}" class="experience__title" secondaryClass="experience__subtitle" />
<x-front.sections.title title="{{ $career['title'] }}" subtitle="{{ $career['subtitle'] }}"
class="experience__title" secondaryClass="experience__subtitle" />
<div class="experience__image">
<img src="/assets/uploads/2020/06/LDC_Infographie_Nous_Rejoindre.png" alt="VOTRE PARCOURS">
</div>

View file

@ -3,10 +3,10 @@
<h2 class="history__title title">
<span class="title-text">{{ $ourHistory['string_title'] }}</span>
</h2>
<x-front.pages.known-us.historic-slider id="1" :historicalSlider="$ourHistory['list_history']" />
<x-front.pages.know-us.historic-slider id="1" :historicalSlider="$ourHistory['list_history']" />
<div class="history__text-block description">
{!! $ourHistory['wysiwyg_description'] !!}
</div>
<x-front.pages.known-us.historic-slider id="2" :historicalSlider="$ourHistory['list_key_steps']" />
<x-front.pages.know-us.historic-slider id="2" :historicalSlider="$ourHistory['list_key_steps']" />
</div>
</div>

View file

@ -0,0 +1,280 @@
<div class="location fw fw-container textblock_bg nos-implantations" id="{{ $id ?? 'nos-implantations' }}">
<div class="location__wrapper wrapper">
{{-- Title --}}
<x-front.sections.title :title="$implantations['string_title']" :subtitle="$implantations['string_subtitle'] ?? null"
class="location__title" />
{{-- Content --}}
<div class="location__text description">
{!! $implantations['wysiwyg_content'] !!}
</div>
{{-- Interactive Map --}}
<div id="interactive-map" class="location__map">
{{-- Legend --}}
<div id="map-legend">
<p>{{ trans('front.filter_plants') }}</p>
<hr />
<p class="map-legend-subtitle">{{ trans('front.pole') }}</p>
@foreach ($companyActivityModels as $activity)
<label class="activity-label">
<input type="checkbox" class="activityFilter" value="{{ $activity->getKey() }}"
data-color="{{ $activity->color }}" checked>
<div class="color-box-wrapper">
<span class="color-box"></span>
</div>
{{ $activity->name }}
</label>
@endforeach
</div>
{{-- Company Info --}}
<div id="company-info">
<button id="company-info-close">X</button>
<div id="company-presentation">
<img id="company-logo" src="" alt="">
<div>
<p id="company-name" class="titre"></p>
<p id="company-sector"></p>
<p>{{ trans('front.responsible') }} : <span id="company-responsable"></span></p>
</div>
</div>
<hr />
<p>{{ trans('front.address') }} :</p>
<p id="company-details"></p>
</div>
{{-- Map --}}
<div id="map"></div>
</div>
<div class="location__btn-wrap">
<x-front.cta :title="$implantations['string_label']"
:url="\App\Helpers\RouteHelper::routeNameChecker(sprintf('front.dynamic-pages.nous-rejoindre'))"
buttonIcon="btn__icon_arrow-right" class="location__btn btn" />
</div>
</div>
</div>
<style>
#map-legend .activity-label {
display: flex;
align-items: center;
cursor: pointer;
margin-bottom: 5px;
padding-left: 15px;
}
#map-legend .activity-label input {
display: none;
}
#map-legend .color-box-wrapper {
width: 18px;
height: 18px;
box-shadow: 0px 0px 9px -2px rgba(0, 0, 0, 0.75);
margin-right: 10px;
padding: 6px;
}
#map-legend .color-box {
display: block;
width: 100%;
height: 100%;
}
#map-legend .map-legend-subtitle {
font-weight: bold;
margin-bottom: 10px;
}
/* General Map Container */
#interactive-map { position: relative; }
/* Company Info */
#company-info {
display: none;
position: absolute;
left: 10px;
bottom: 2rem;
padding: 1rem;
z-index: 1;
background: #fff;
border-bottom: 5px solid #DC2034;
width: 450px;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
}
#company-info-close {
position: absolute;
top: 0.5rem;
right: 0.5rem;
cursor: pointer;
}
#company-presentation {
display: flex;
gap: 1rem;
align-items: center;
}
#company-logo {
max-height: 82px;
width: auto;
}
#company-name {
color: #DC2034;
}
#company-sector,
#company-responsable,
#company-info-close {
color: #949494;
}
/* Legend */
#map-legend {
position: absolute;
top: 10px;
left: 10px;
padding: 1rem;
background: #fff;
z-index: 1;
display: flex;
flex-direction: column;
}
#map-legend hr,
#company-info hr {
margin: 5px 0;
}
/* Map */
#map {
width: 100%;
height: 700px;
}
</style>
<script>
let map;
let center;
let zoom;
let oms;
let markersByActivity = {};
let ASSET_BASE = @json(asset(''));
let locale = "{{ app()->getLocale() }}";
let localeFallback = "{{ app()->getFallbackLocale() }}";
var styles = [{"featureType":"administrative","elementType":"labels.text.fill","stylers":[{"color":"#444444"}]},{"featureType":"administrative.country","elementType":"all","stylers":[{"visibility":"off"}]},{"featureType":"administrative.province","elementType":"all","stylers":[{"visibility":"off"}]},{"featureType":"administrative.neighborhood","elementType":"all","stylers":[{"visibility":"off"}]},{"featureType":"landscape","elementType":"all","stylers":[{"color":"#f2f2f2"}]},{"featureType":"landscape.man_made","elementType":"labels.text.fill","stylers":[{"lightness":"0"}]},{"featureType":"poi","elementType":"all","stylers":[{"visibility":"off"}]},{"featureType":"road","elementType":"all","stylers":[{"saturation":-100},{"lightness":45}]},{"featureType":"road.highway","elementType":"all","stylers":[{"visibility":"simplified"}]},{"featureType":"road.highway","elementType":"labels","stylers":[{"visibility":"off"}]},{"featureType":"road.highway.controlled_access","elementType":"labels","stylers":[{"visibility":"off"}]},{"featureType":"road.arterial","elementType":"geometry.stroke","stylers":[{"visibility":"off"}]},{"featureType":"road.arterial","elementType":"labels","stylers":[{"visibility":"off"}]},{"featureType":"road.arterial","elementType":"labels.text.fill","stylers":[{"lightness":"-63"}]},{"featureType":"road.arterial","elementType":"labels.text.stroke","stylers":[{"visibility":"off"}]},{"featureType":"road.arterial","elementType":"labels.icon","stylers":[{"visibility":"off"}]},{"featureType":"road.local","elementType":"all","stylers":[{"visibility":"off"}]},{"featureType":"transit","elementType":"all","stylers":[{"visibility":"off"}]},{"featureType":"water","elementType":"all","stylers":[{"visibility":"on"},{"color":"#92c0d3"}]}];
document.getElementById("company-info-close").addEventListener("click", () => {
document.getElementById("company-info").style.display = "none";
});
function initMap() {
const center = window.matchMedia("(min-width: 769px)").matches
? { lat: 47.888, lng: 2.644 }
: { lat: 49.132846, lng: 1.625977 };
const zoom = window.matchMedia("(min-width: 769px)").matches ? 5 : 4;
map = new google.maps.Map(document.getElementById("map"), {
zoom,
zoomControl: true,
zoomControlOptions: {
position: google.maps.ControlPosition.RIGHT_CENTER
},
center,
mapTypeControl: false,
streetViewControl: false,
fullscreenControl: false,
scrollwheel: false,
draggable: true,
styles,
});
oms = new OverlappingMarkerSpiderfier(map, {
markersWontMove: true,
markersWontHide: true,
basicFormatEvents: true,
nearbyDistance: 50,
circleSpiralSwitchover: Infinity,
circleFootSeparation : 50,
circleStartAngle : 3.14 / 6,
});
const companies = @json($companyModels);
// Création des marqueurs
companies.forEach(c => {
const marker = new google.maps.Marker({
position: { lat: parseFloat(c.latitude), lng: parseFloat(c.longitude) },
map: map,
title: c.name,
icon: getPinColor(c.company_activity.color),
});
if (!markersByActivity[c.activity_id]) {
markersByActivity[c.activity_id] = [];
}
// Ajouter la data au marker
marker.companyData = c;
// Ajouter le marker à OMS
oms.addMarker(marker);
// Clic normal (markers déjà séparés)
oms.addListener('click', (marker) => {
updateCompanyInfo(marker.companyData);
});
// Clic lors du spiderfy → on ignore !
oms.addListener('spider_click', () => {
// On bloque le comportement
});
markersByActivity[c.activity_id].push(marker);
});
// Gestion des filtres
document.querySelectorAll(".activityFilter").forEach(cb => {
const box = cb.nextElementSibling.querySelector(".color-box");
// Initialiser la couleur au chargement
box.style.backgroundColor = cb.checked ? cb.dataset.color : "transparent";
// Mettre à jour au changement
cb.addEventListener("change", () => {
box.style.backgroundColor = cb.checked ? cb.dataset.color : "transparent";
updateMarkers();
});
});
}
function getPinColor(color) {
return {
path: "M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z M12 6.5a2.5 2.5 0 1 1 0 5a2.5 2.5 0 0 1 0-5z",
fillColor: color,
fillOpacity: 1,
strokeWeight: 0,
scale: 1.7,
anchor: new google.maps.Point(12, 24)
};
}
function updateCompanyInfo(company) {
const companyLogo = document.getElementById("company-logo");
if (company.logo) {
companyLogo.style.display = "block";
companyLogo.src = ASSET_BASE + company.logo ?? "";
companyLogo.alt = `Logo de ${company.name}`;
} else {
companyLogo.style.display = "none";
}
document.getElementById("company-name").textContent = company.name;
document.getElementById("company-details").innerHTML = company.informations ?? "";
document.getElementById("company-responsable").textContent = company.responsable ?? "";
document.getElementById("company-sector").textContent = company.company_sector?.name[locale] ?? company.company_sector?.name[localeFallback] ?? "";
document.getElementById("company-info").style.display = "block";
}
// Mise à jour de l'affichage des marqueurs
function updateMarkers() {
const active = Array.from(document.querySelectorAll(".activityFilter:checked")).map(cb => cb.value);
Object.keys(markersByActivity).forEach(activity => {
markersByActivity[activity].forEach(m => m.setMap(active.includes(activity) ? map : null));
});
}
</script>

View file

@ -87,7 +87,8 @@
@if(!empty($professions))
<div class="professions fw fw-container">
<div class="professions__wrapper wrapper">
<x-front.sections.title title="{{ $professions['title'] }}" subtitle="{{ $professions['subtitle'] }}" class="professions__title" secondaryClass="professions__subtitle" />
<x-front.sections.title title="{{ $professions['title'] }}" subtitle="{{ $professions['subtitle'] }}"
class="professions__title" secondaryClass="professions__subtitle" />
<div class="professions__list professions-list">
@foreach ($professions['list'] as $profession)
<x-front.sections.profession :profession='$profession' />

View file

@ -1,4 +1,4 @@
<h2 class="{{ isset($class) ? $class : ''}} textblock__title title">
<h2 class="{{ isset($class) ? $class : 'textblock__title title'}}">
<span class="title-text">{!! $title !!}</span>
@if (isset($subtitle))
<span class="{{ isset($secondaryClass) ? $secondaryClass : ''}} subtitle-text textblock__subtitle subtitle subtitle_paint">{{ $subtitle }}</span>

View file

@ -21,7 +21,7 @@
<x-uwil-common::head.favicon mask-icon-color="#c20e1a" tile-color="#2b5797" theme-color="#ffffff" />
<script type="application/ld+json">
<script nonce="{{ $nonce ?? '' }}" type="application/ld+json">
{{-- <?= esc_js(bcn_display_json_ld()); ?> --}}
</script>
@ -59,7 +59,7 @@
@endphp
@foreach ($scriptFiles as $scriptFile)
<script src='/assets/{{ $scriptFile }}' nonce="{{ $nonce ?? '' }}"></script>
<script nonce="{{ $nonce ?? '' }}" src='/assets/{{ $scriptFile }}'></script>
@endforeach

View file

@ -235,7 +235,7 @@
</div>
</li>
</ul>
<script>
<script nonce="{{ $nonce ?? '' }}">
rejoin_path = "/nous-rejoindre/";
if (window.location.href.indexOf(rejoin_path) == -1) {

View file

@ -62,7 +62,8 @@
<div id="page-press-form">
<div class="page-press-form fw fw-container gray-bg">
<div class="page-press-form__wrapper wrapper">
<x-front.sections.title secondaryClass="page-press-form__subtitle " class="page-press-form__title" title="Contactez notre" subtitle="service presse" />
<x-front.sections.title title="Contactez notre" subtitle="service presse"
class="page-press-form__title" secondaryClass="page-press-form__subtitle " />
<div class="fw-container no-bottom-padding">

View file

@ -25,7 +25,7 @@
}
}
@endphp
<x-front.pages.known-us.pole-page-template
<x-front.pages.know-us.pole-page-template
:img="$dynamicPageModel->content['image_visual']"
:title="$dynamicPageModel->title"
:text="$dynamicPageModel->content['wysiwyg_content']"

View file

@ -23,13 +23,14 @@
{{-- HISTORICAL --}}
@php $section = $getSection('list_section_our_history'); @endphp
@if($section)
<x-front.pages.known-us.historical
<x-front.pages.know-us.historical
:id="$getAnchor($section)"
:ourHistory="$section"
/>
@endif
{{-- PROJECT --}}
{{-- TODO: adapter la liste des pages engagements avec id_key quand elles seront prêtes --}}
@php
$section = $getSection('list_section_our_project');
$ourProject = $section ? [
@ -39,7 +40,6 @@
] : null;
@endphp
@if($section)
{{-- TODO: adapter la liste des pages engagements avec id_key quand elles seront prêtes --}}
<x-front.commitments.wrapper
:id="$getAnchor($section)"
:engagements="$ourProject"
@ -59,7 +59,7 @@
{{-- GOVERNANCE --}}
@php $section = $getSection('list_section_our_governance'); @endphp
@if($section)
<x-front.pages.known-us.our-governance
<x-front.pages.know-us.our-governance
:id="$getAnchor($section)"
:governance="$section"
/>
@ -68,12 +68,22 @@
{{-- ORGANIZATION --}}
@php $section = $getSection('list_section_our_organization'); @endphp
@if($section)
<x-front.pages.known-us.our-organisation
<x-front.pages.know-us.our-organisation
:id="$getAnchor($section)"
:organization="$section"
:childPages="$dynamicPageModel->childPages"
/>
@endif
{{-- TODO: il manque la map : https://lab.s-web.fr/ldc-web/projets/ldc-theme/-/blob/master/societe.class.php?ref_type=heads et dans partials/map (du wordpress) --}}
{{-- IMPLANTATIONS --}}
@php $section = $getSection('list_section_our_implantations'); @endphp
@if($section)
<!-- Charger l'API Google Maps -->
<script async src="https://maps.googleapis.com/maps/api/js?key={{ config('services.google.maps') }}&callback=initMap&language={{ app()->getLocale() }}"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/OverlappingMarkerSpiderfier/1.0.3/oms.min.js"></script>
<x-front.pages.know-us.our-implantations
:id="$getAnchor($section)"
:implantations="$section"
/>
@endif
@endsection

View file

@ -157,3 +157,102 @@ Breadcrumbs::for('back.contact-counts.index', function (Generator $trail) {
route('back.contact-counts.index')
);
});
// * COMPANIES.
Breadcrumbs::for('back.companies.index', function (Generator $trail) {
$trail->parent('back.dashboard.index');
$trail->push(
Str::of(trans_choice('models.classes.company', \INF))->ucfirst()->plural(),
route('back.companies.index'),
);
});
Breadcrumbs::for('back.companies.create', function (Generator $trail) {
$trail->parent('back.companies.index');
$trail->push(Str::ucfirst(trans('crud.actions.create')), route('back.companies.create'));
});
Breadcrumbs::for('back.companies.edit', function (Generator $trail) {
$trail->parent('back.companies.index');
$trail->push(Str::ucfirst(trans('crud.actions.edit')), route('back.companies.edit', [
'company' => request()->company
]));
});
Breadcrumbs::for('back.companies.duplicate', function (Generator $trail) {
$trail->parent('back.companies.index');
$trail->push(Str::ucfirst(trans('crud.actions.duplicate')));
});
Breadcrumbs::for('back.companies.show', function (Generator $trail) {
$trail->parent('back.companies.index');
$trail->push(Str::ucfirst(trans('crud.actions.visualize')), route('back.companies.show', [
'company' => request()->company
]));
});
Breadcrumbs::for('back.companies.trash', function (Generator $trail) {
$trail->parent('back.companies.index');
$trail->push(Str::ucfirst(trans('crud.actions.archive')));
});
// * COMPANY SECTORS.
Breadcrumbs::for('back.company-sectors.index', function (Generator $trail) {
$trail->parent('back.dashboard.index');
$trail->push(
Str::of(trans('models.classes.company'))->ucfirst()->plural(),
route('back.company-sectors.index'),
);
});
Breadcrumbs::for('back.company-sectors.create', function (Generator $trail) {
$trail->parent('back.company-sectors.index');
$trail->push(Str::ucfirst(trans('crud.actions.create')), route('back.company-sectors.create'));
});
Breadcrumbs::for('back.company-sectors.edit', function (Generator $trail) {
$trail->parent('back.company-sectors.index');
$trail->push(Str::ucfirst(trans('crud.actions.edit')), route('back.company-sectors.edit', [
'company_sector' => request()->company_sector,
]));
});
Breadcrumbs::for('back.company-sectors.duplicate', function (Generator $trail) {
$trail->parent('back.company-sectors.index');
$trail->push(Str::ucfirst(trans('crud.actions.duplicate')));
});
Breadcrumbs::for('back.company-sectors.show', function (Generator $trail) {
$trail->parent('back.company-sectors.index');
$trail->push(Str::ucfirst(trans('crud.actions.visualize')), route('back.company-sectors.show', [
'company_sector' => request()->company_sector,
]));
});
Breadcrumbs::for('back.company-sectors.trash', function (Generator $trail) {
$trail->parent('back.company-sectors.index');
$trail->push(Str::ucfirst(trans('crud.actions.archive')));
});
// * COMPANY ACTIVITIES.
Breadcrumbs::for('back.company-activities.index', function (Generator $trail) {
$trail->parent('back.dashboard.index');
$trail->push(
Str::of(trans('models.classes.company'))->ucfirst()->plural(),
route('back.company-activities.index'),
);
});
Breadcrumbs::for('back.company-activities.create', function (Generator $trail) {
$trail->parent('back.company-activities.index');
$trail->push(Str::ucfirst(trans('crud.actions.create')), route('back.company-activities.create'));
});
Breadcrumbs::for('back.company-activities.edit', function (Generator $trail) {
$trail->parent('back.company-activities.index');
$trail->push(Str::ucfirst(trans('crud.actions.edit')), route('back.company-activities.edit', [
'company_activity' => request()->company_activity,
]));
});
Breadcrumbs::for('back.company-activities.duplicate', function (Generator $trail) {
$trail->parent('back.company-activities.index');
$trail->push(Str::ucfirst(trans('crud.actions.duplicate')));
});
Breadcrumbs::for('back.company-activities.show', function (Generator $trail) {
$trail->parent('back.company-activities.index');
$trail->push(Str::ucfirst(trans('crud.actions.visualize')), route('back.company-activities.show', [
'company_activity' => request()->company_activity,
]));
});
Breadcrumbs::for('back.company-activities.trash', function (Generator $trail) {
$trail->parent('back.company-activities.index');
$trail->push(Str::ucfirst(trans('crud.actions.archive')));
});

View file

@ -1,12 +1,15 @@
<?php
use App\Helpers\InertiaErrorHelper;
use App\Http\Controllers\Back\CompanyController;
use App\Http\Controllers\Back\CompanyActivityController;
use App\Http\Controllers\Back\CompanySectorController;
use App\Http\Controllers\Back\ContactController;
use App\Http\Controllers\Back\DynamicPageController;
use App\Http\Controllers\Back\NewsController;
use App\Http\Controllers\Back\PageConfigController;
use App\Http\Controllers\Back\UploadController;
use Illuminate\Http\Request;
use App\Http\Controllers\Back\ContactController;
use App\Http\Controllers\Back\NewsController;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
use Uwil\LaravelBackend\Controllers\AuthenticatedSessionController;
@ -73,6 +76,39 @@ Route::prefix('admin')
Route::resource('news', NewsController::class);
Route::get('/news/{news}/duplicate', [NewsController::class, 'duplicate'])
->name('news.duplicate');
// * COMPANIES.
Route::get('/companies/trash', [CompanyController::class, 'index'])
->name('companies.trash');
Route::patch('/companies/{company}/restore', [CompanyController::class, 'restore'])
->name('companies.restore');
Route::delete('/companies/{company}/force', [CompanyController::class, 'forceDestroy'])
->name('companies.forceDestroy');
Route::resource('companies', CompanyController::class);
Route::get('/companies/{company}/duplicate', [CompanyController::class, 'duplicate'])
->name('companies.duplicate');
// * COMPANY SECTORS.
Route::get('/company-sectors/trash', [CompanySectorController::class, 'index'])
->name('company-sectors.trash');
Route::patch('/company-sectors/{company_sector}/restore', [CompanySectorController::class, 'restore'])
->name('company-sectors.restore');
Route::delete('/company-sectors/{company_sector}/force', [CompanySectorController::class, 'forceDestroy'])
->name('company-sectors.forceDestroy');
Route::resource('company-sectors', CompanySectorController::class);
Route::get('/company-sectors/{company_sector}/duplicate', [CompanySectorController::class, 'duplicate'])
->name('company-sectors.duplicate');
// * COMPANY ACTIVITIES.
Route::get('/company-activities/trash', [CompanyActivityController::class, 'index'])
->name('company-activities.trash');
Route::patch('/company-activities/{company_activity}/restore', [CompanyActivityController::class, 'restore'])
->name('company-activities.restore');
Route::delete('/company-activities/{company_activity}/force', [CompanyActivityController::class, 'forceDestroy'])
->name('company-activities.forceDestroy');
Route::resource('company-activities', CompanyActivityController::class);
Route::get('/company-activities/{company_activity}/duplicate', [CompanyActivityController::class, 'duplicate'])
->name('company-activities.duplicate');
});
// * UPDATE LANGUAGES.