protected $_clonable = true;
protected $_bulk = true;
protected $_oneInstance= false;
+
+ public function setup() {
+ parent::setup();
+
+ // Override max menu depth when re-ordering. There's a parent level (eg. #main or #footer)
+ // so an extra level is needed to allow 4 menu levels on front-end. A depth setting of 0 = infinite.
+ // Ref: https://backpackforlaravel.com/docs/3.6/crud-operation-reorder
+ $this->crud->enableReorder('name', 5);
+ }
}
parent::setFields();
}
+
+ // Custom accessor: ->title_solutions_variant
+ // Provides more context (mainly used for Applications 'SelectFromModel' field in Solutions template)
+ public function getTitleSolutionsVariantAttribute() {
+ return $this->title_solutions . ' ['. implode(', ', $this->variant) .']';
+ }
}
class News extends BaseNews
{
+ public $thumb_width = 348;
+ public $thumb_height = 196;
+
public function registerMediaConversions(Media $media = null)
{
parent::registerMediaConversions($media);
->width(768);
$this->addMediaConversion('index_thumb')
- ->crop(Manipulations::CROP_CENTER, 348, 196);
+ ->crop(Manipulations::CROP_CENTER, $this->thumb_width, $this->thumb_height);
}
public static function getPosts($per_page = 8, $exclude = null) {
return $fallback;
}
- return '<img class="'. $class .'" src="'. $src .'" alt="'. $alt .'">';
+ // Include thumbnail dimensions to avoid grid jumping as post images are loaded
+ // Ideally this should be based on actual dimensions of each image but for now it'll do
+ // See: https://github.com/spatie/laravel-medialibrary/issues/893
+ $dimensions = ($size == 'index_thumb') ?
+ 'width="'. $this->thumb_width .'" height="'. $this->thumb_height .'"' : '';
+
+ return '<img class="'. $class .'" src="'. $src .'" alt="'. $alt .'" '. $dimensions .'>';
}
public function getURL($id) {
--- /dev/null
+<?php
+
+namespace App\SubForms;
+
+use Cubist\Backpack\app\Magic\SubForm;
+
+class ImageBlock extends SubForm
+{
+
+
+ public function init()
+ {
+ parent::init();
+ $this->addField(['name' => 'title',
+ 'label' => 'Titre',
+ 'type' => 'Text']);
+
+ $this->addField(['name' => 'text',
+ 'label' => 'Texte',
+ 'type' => 'Textarea',
+ 'attributes' => [
+ 'rows' => 4,
+ ],
+ ]);
+
+ $this->addField(['name' => 'image',
+ 'label' => 'Image',
+ 'type' => 'Images']);
+
+ $this->addField(['name' => 'button',
+ 'label' => 'Lien',
+ 'type' => 'Button']);
+ }
+}
'type' => 'Text',
'label' => 'Filtres',
'tab' => 'Catégorie']);
+
+ $this->addField(['name' => 'highlights',
+ 'type' => 'Markdown',
+ 'label' => 'Liste de 3 highlights affichés sur le template « Listing Catégories »',
+ 'tab' => 'Highlights']);
+
}
public function setData(&$data)
--- /dev/null
+<?php
+
+
+namespace App\Templates;
+
+use App\Models\Application;
+use App\Models\Page;
+use Cubist\Backpack\app\Magic\PageData;
+
+class CategoryListing extends Base
+{
+ public function getName()
+ {
+ return 'Catégorie (Listing)';
+ }
+
+ public function init()
+ {
+ parent::init();
+
+ $this->addField([
+ 'name' => 'intro',
+ 'type' => 'BunchOfFields',
+ 'bunch' => 'App\SubForms\Intro',
+ 'label' => 'Introduction',
+ 'tab' => 'Introduction',
+ 'translatable' => true,
+ ]);
+
+ $this->addField(['name' => 'related_applications',
+ 'label' => 'Applications associées',
+ 'type' => 'SelectFromModel',
+ 'optionsmodel' => Application::class,
+ 'multiple' => true,
+ 'attribute' => 'title',
+ 'tab' => 'Applications Associées',
+ ]);
+ }
+
+ public function setData(&$data)
+ {
+ parent::setData($data);
+
+ // Get all the child pages of this page that use the template 'category'.
+ // These will be used to populate the grid of sub-categories
+ $current_page_ID = $data['page']->id;
+ $categories = Page::with('media')
+ ->where('parent_id', $current_page_ID)
+ ->where('template', 'category')
+ ->orderBy('lft')
+ ->get();
+
+ $data['categories'] = [];
+
+ foreach ($categories as $category) {
+ $data['categories'][$category->id] = $category->getPageData();
+ }
+
+ // Get related applications, if any
+ if (isset($data['page']->related_applications) && count($data['page']->related_applications) > 0) {
+ $data['applications'] = PageData::fromEntities(Application::whereVariant()->whereIn('id', $data['page']->related_applications)->get());
+ }
+ }
+}
namespace App\Templates;
+use App\Models\Application;
use App\Models\News;
+use Cubist\Backpack\app\Magic\PageData;
class Home extends Base
{
{
parent::init();
+ $this->removeField('intro'); // Replaced by custom intro fields below
+
+ $this->addField(['name' => 'our_business_title',
+ 'label' => 'Titre',
+ 'type' => 'Text',
+ 'tab' => 'Intro']);
+
+ $this->addField([
+ 'name' => 'our_business_details',
+ 'type' => 'BunchOfFieldsMultiple',
+ 'bunch' => 'App\SubForms\ImageBlock',
+ 'label' => 'Notre métier',
+ 'tab' => 'Intro',
+ ]);
+
+
$this->addField([
'name' => 'slideshow',
'type' => 'BunchOfFieldsMultiple',
// Set extra data for Home blade view
public function setData(&$data)
{
- $page = $data['page'];
+ // Latest News posts
+ $data['news'] = News::getPosts(4);
- // News data
- $data['news'] = $this->_getNews();
- }
-
- protected function _getNews()
- {
- return News::getPosts(4);
+ // Latest Applications
+ $data['applications'] = PageData::fromEntities(Application::with('media')->whereVariant()->latest()->take(4)->get());
}
Debugbar::startMeasure('nav_news', 'Make news nav items');
parent::setMenuChildren($menu);
- $news = NewsModel::whereVariant()->get();
+ $locale = app()->getLocale();
- $i = 0;
+ $news = NewsModel::whereVariant()
+ ->where("status->$locale", 1)
+ ->whereDate('date', '<=', Carbon::now())
+ ->get();
foreach ($news as $newsItem) {
- // Don't include offline items
- if ($newsItem->getPageData()->get('status') != 1) {
- continue;
- }
-
$item = new PageItem();
$item->initFromEntity($newsItem);
$item->setLocale($menu->getLocale());
--- /dev/null
+<?php
+
+namespace App\Templates;
+
+use Cubist\Backpack\app\Template\TemplateAbstract;
+
+/*
+ * The purpose of this template is to create a virtual page entry that can be used in the menu without being clickable.
+ * It is used for nested menus where certain levels are just labels / placeholders that contain other pages.
+ * This is an alternative to the FirstRedirection template.
+ */
+class Placeholder extends TemplateAbstract {
+ protected $_virtual = true;
+ protected $_navigable = false;
+
+ public function getName()
+ {
+ return '* Placeholder (lien non-actif)';
+ }
+
+ public function init()
+ {
+ parent::init();
+ }
+}
class Solution extends Base
{
+ protected static $_applications = [];
+
public function getName()
{
return 'Solution';
'label' => 'Applications',
'type' => 'SelectFromModel',
'optionsmodel' => 'App\Models\Application',
- 'attribute' => 'title_solutions',
+ 'attribute' => 'title_solutions_variant', // Custom accessor provides more detail
'order' => true,
'multiple' => true,
'tab' => 'Applications']);
Debugbar::startMeasure('nav_applications', 'Make applications nav items');
parent::setMenuChildren($menu);
- $applications = Application::whereVariant($menu->getVariant())->get();
-
$solution_apps = $menu->getPageData()->get('applications');
- if (null === $solution_apps || !$solution_apps) {
+
+ if (null === $solution_apps || !is_array($solution_apps)) {
return;
}
+ $applications = self::_getApplications($menu->getVariant(), $menu->getLocale());
+
foreach ($applications as $application) {
- if (!in_array($application->id, $solution_apps)) {
- continue;
- }
+
$item = new PageItem();
$item->initFromEntity($application);
$item->setVariant($menu->getVariant());
$item->setHref($application->getSlugOrTitleAttribute());
$item->setId('application/' . $application->id);
$item->setController(['controller' => 'ApplicationController', 'action' => 'view', 'params' => ['id' => $application->id]]);
- $item->showInAllMenus();
+
+ if (!in_array($application->id, $solution_apps)) {
+ $item->hideInAllMenus();
+ } else {
+ $item->showInAllMenus();
+ }
+
$menu->addChild($item);
}
$data['applications'] = [];
return;
}
- $data['applications'] = PageData::fromEntities(Application::whereIn('id', $data['page']->applications)->get());
+ $data['applications'] = PageData::fromEntities(Application::whereVariant()->whereIn('id', $data['page']->applications)->get());
+ }
+
+ /**
+ * @param $variant
+ * @param $locale
+ * @return Application[]
+ */
+ public static function _getApplications($variant, $locale)
+ {
+ if (!isset(self::$_applications[$variant][$locale])) {
+ self::$_applications[$variant][$locale] = [];
+ }
+ if (empty(self::$_applications[$variant][$locale])) {
+ self::$_applications[$variant][$locale] = Application::whereVariant($variant)->get();
+ }
+ return self::$_applications[$variant][$locale];
}
}
--- /dev/null
+<?php
+
+
+namespace App\Templates;
+
+use Barryvdh\Debugbar\Facade as Debugbar;
+
+/*
+ * The purpose of this template is to create a virtual page entry that can be used in the menu without being clickable.
+ * It inherits from the Solution template and is similar in that it will have any associated Applications added as
+ * child pages in the menu. However, there is no page that can be visited directly - it's parent wrapper only.
+ */
+class SolutionPlaceholder extends Solution
+{
+ protected static $_applications = [];
+
+ public function getName()
+ {
+ return 'Solution (placeholder)';
+ }
+
+ public function init()
+ {
+ parent::init();
+
+ $this->removeField('intro');
+ }
+
+ public function setMenuChildren($menu)
+ {
+ Debugbar::startMeasure('nav_applications_placeholder', 'Make applications nav items (placeholder template)');
+ parent::setMenuChildren($menu);
+ Debugbar::stopMeasure('nav_applications_placeholder');
+ }
+}
.nav-primary
@apply flex mx-auto px-8
- // Top level items
+ // Trigger for submenus, at any depth
+ li:hover > ul
+ display: block // Or set to flex so we can have 2 menus side-by-side in submenu
+ // Top level items
> li
@apply py-4
//position: relative
&:not(:last-child)
@apply mr-10
- &:hover
- > ul // First level submenus
- display: block // Set to flex so we can have 2 menus side-by-side in submenu
- animation: submenu-fade-in 0.3s
-
- // Top level links
-
- > li > a
- @apply text-inherit cursor-pointer
- white-space nowrap
+ // Top level links
+ > a
+ @apply text-inherit cursor-pointer
+ white-space nowrap
- &:hover
- @apply text-primary
+ &:hover
+ @apply text-primary
+ // 1st level Submenus
+ > li > ul
+ margin-top: 1rem // Extra margin to replace top: 100% (see IE11 notes above)
+ animation: submenu-fade-in 0.3s
- // Submenus
+ // Any Submenus below the first level
+ > li > ul li > ul
+ top: -1rem // To offset extra padding on <li>s (sub-submenus are relative to parent <li>)
+ left: 100%
+ // ALL Submenus
> li ul
@apply bg-white text-base shadow-2xl
- //top: 100%
- //left: 0
- position: absolute
+ position: absolute // Any deeper submenus will be relative to the parent <ul>
display: none
- margin-top: 1rem // Extra margin to replace top: 100% (see IE11 notes above)
padding: 1em 0
+ max-width: 450px
li
- &:hover, &.active
- > a
- @apply text-primary
- transform: translateX(0)
-
- &:before
- transform: scaleX(1)
-
-
- > li > ul // Only target 1st level of submenu items
- max-width 450px
-
- > li a span
- max-width 350px
- white-space nowrap
- overflow hidden
- text-overflow ellipsis
-
- > li
- position relative
+ position: relative
+ // Animate / activate submenu links
&:hover, &.active
> a
@apply text-primary
&:before
transform: scaleX(1)
- &:hover
- > ul
- display block
-
- > ul
- position absolute
- left 100%
- top: -32px
- display none
- max-width 450px
-
- > li a span
- display: block;
- max-width 350px
- white-space nowrap
- overflow hidden
- text-overflow ellipsis
-
// Submenu links
-
a
@apply text-navy flex items-center w-full py-2
white-space: nowrap
transform: translateX(-2em)
// Animated dash before link
-
&:before
content: ''
display: block
transform: scaleX(0)
transform-origin: right
- // Set transition for links - same for both elements
-
+ // Set transition for links - same for both elements so animated dash and text shift are synchronised
a, a:before
transition: transform 0.2s ease-out
+ // Submenu link text
+ a span
+ max-width: 350px
+ white-space: nowrap
+ overflow: hidden
+ text-overflow: ellipsis
+
#mobile-nav
+below($breakpoint-menu)
display: block
constrain(right, 1.75vw)
constrain(bottom, 2.5vw)
- +below(900px)
+ +below(1200px)
@apply text-xl
&:after
--- /dev/null
+<article>
+
+ <a href="{{ $item->getURL($item->id) }}" class="block bg-grey-100">
+ {!! $item->get() !!}
+ </a>
+
+ <div class="mt-6">
+ <time datetime="{{ $item->date->toDateTimeLocalString() }}"
+ class="block font-display font-medium text-navy">
+ {{ $item->date->format('d/m/y') }}
+ </time>
+ <h4 class="text-2xl sm:text-xl mt-2">
+ <a class="text-navy" href="{{ $item->getURL($item->id) }}">
+ {{ $item->title }}
+ </a>
+ </h4>
+ <p>{{ $item['chapo'] }}</p>
+
+ <p><a href="{{ $item->getURL($item->id) }}">{{ __("Lire l'article") }}</a></p>
+ </div>
+
+</article>
@php
$text = $slot;
+ $type = $type ?? 'button';
+ $base_class = ($type === 'link') ? 'animated-underline' : 'btn';
$enabled = true;
if (isset($data) && is_array($data)) {
if ($data['type'] == 'none') {
@endphp
@if($enabled && $text)
- <a href="{{ $href ?? '#' }}" class="btn {{ $class ?? '' }}">
+ <a href="{{ $href ?? '#' }}" class="{{ $base_class }} {{ $class ?? '' }}">
<span class="btn-text">{{ $text }}</span>
</a>
@endif
<article>
- <a href="{{ $item->getURL($item->id) }}" class="news-index-article-image">
+ <a href="{{ $item->getURL($item->id) }}" class="news-index-article-image block bg-grey-100">
{!! $item->getThumbnailImage() !!}
</a>
--- /dev/null
+@extends('layouts/app')
+
+@section('content')
+ <script>
+ window.baseFilters = "{{$page->get('filter')}}";
+ </script>
+ @intro(['padding' => 'pb-1v'])
+
+ <full-width class="bg-grey-100" padding="pt-3v pb-3v">
+
+ <content>
+
+ <grid cols="4" gap="lg" class="md:grid-cols-3 sm:grid-cols-2 xs:grid-cols-1">
+ @foreach($categories as $category)
+ @php
+ $URL = $nav->getHrefById($category->id);
+ @endphp
+ <div class="bg-white">
+ {{-- Image Holder --}}
+ <div class="bg-grey-200 bg-cover"
+ style="background-image:url({{ $category->getImageURLByCollection($category->intro['image']) }})">
+ {{-- Image sizer + link --}}
+ <a href="{{ $URL }}" class="block" style="padding-bottom: 56.32%"></a>
+ </div>
+
+ {{-- Details --}}
+ <div class="p-6">
+ <h3>
+ <a href="{{ $URL }}" class="text-navy text-2xl">
+ {{ $category->intro['title'] }}
+ </a>
+ </h3>
+ <div class="my-6">
+ @markdown($category->highlights)
+ </div>
+
+ <a href="{{ $URL }}">{{ __('En savoir plus') }}</a>
+ </div>
+ </div>
+ @endforeach
+ </grid>
+
+ </content>
+
+ </full-width>
+
+ @isset($applications)
+ <full-width padding="pt-3v pb-3v">
+ <content>
+ <text-block :title="__('Quelques applications')"></text-block>
+ <grid cols="4" class="sm:grid-cols-2 xs:grid-cols-1">
+ @foreach ($applications as $application)
+ <a class="solutions-link" href="{{ $nav->getHrefByID('application/'.$application->id) }}">
+ <div class="solutions-link-sizer"></div>
+ <span class="solutions-link-bg"
+ style="background-image:url({{ $application->getImageURLbyCollection($application['image_solutions'], 'thumb') }})"></span>
+ <span class="solutions-link-text">{{ $application->title_solutions }}</span>
+ </a>
+ @endforeach
+ </grid>
+ </content>
+ </full-width>
+ @endisset
+
+@endsection
</text-block>
</column>
<column
- class="overlap-bottom md:-mr-2v sm:-ml-2v sm:mb-0 slide-content slide-img-container relative">
+ class="-mb-1v z-10 md:-mr-2v sm:-ml-2v sm:mb-0 slide-content slide-img-container relative">
<div class="slide-mask">
<div class="bg-image h-full bg-cover bg-no-repeat slide-img">
<img class="img-slider" draggable="false" ondragstart="return false;"
</div>
</section>
- {{-- Intro text --}}
- @intro(['title_tag' => 'h2', 'padding' => 'pt-5v pb-4v'])
+ {{-- Intro Section --}}
+ <full-width padding="pt-4v pb-3v">
+ <content>
+ <text-block :title="$page->get('our_business_title')" title-tag="h2" title-class="h2"></text-block>
+
+ <grid cols="3" gap="lg" class="sm:grid-cols-1">
+ @foreach($page->get('our_business_details', []) as $business_detail)
+ <div>
+ <div class="bg-grey-100 bg-cover" style="padding-bottom: 56.25%; background-image:url({{ $page->getImageUrlByCollection($business_detail['image']) }})"></div>
+ <h3 class="font-semibold text-4xl lg:text-3xl md:text-2xl leading-none my-6 sm:mb-4">{{ $business_detail['title'] }}</h3>
+ <p>{{ $business_detail['text'] }}</p>
+ <link-button :data="$business_detail['button']" type="link" />
+ </div>
+
+ @endforeach
+ </grid>
+ </content>
+ </full-width>
+
{{-- Our products --}}
<full-width class="bg-grey-100">
<content>
<text-block title-class="h1 text-inherit" :title="__('Solutions / Applications')"/>
- <grid cols="3" class="sm:grid-cols-2 xs:grid-cols-1">
+ <grid cols="4" class="sm:grid-cols-2 xs:grid-cols-1">
@foreach ($page->get('solutions', []) as $solution)
<a class="solutions-link" href="{{ $nav->getHrefByID($solution['page']) }}">
<div class="solutions-link-sizer"></div>
{{-- Services & Support --}}
@intro(['name' => 'services_support', 'title_tag' => 'h2', 'class' => 'bg-grey-100'])
+ @if(count($applications) > 0)
+ {{-- News --}}
+ <full-width padding="pt-3v">
+ <content>
+ <text-block :title="__('Notes d’application')"/>
+ <grid cols="4" class="sm:grid-cols-2 xs:grid-cols-1">
+ @foreach ($applications as $application)
+ <a class="solutions-link" href="{{ $nav->getHrefByID('application/'.$application->id) }}">
+ <div class="solutions-link-sizer"></div>
+ <span class="solutions-link-bg"
+ style="background-image:url({{ $application->getImageURLbyCollection($application['image_solutions'], 'thumb') }})"></span>
+ <span class="solutions-link-text">{{ $application->title_solutions }}</span>
+ </a>
+ @endforeach
+ </grid>
+ </content>
+ </full-width>
+ @endif
+
@if(config('features.news') && count($news) > 0)
{{-- News --}}
<full-width>
class="block font-display font-medium text-navy mb-4">
{{ $featured_news->date->format('d/m/y') }}
</time>
- <h4 class="h1 simple leading-none mb-8">{{ $featured_news->title }}</h4>
+ <h4 class="h1 simple leading-none mb-8">
+ <a class="text-navy" href="{{ $featured_news->getURL($featured_news->id) }}">
+ {{ $featured_news->title }}
+ </a>
+ </h4>
<p class="font-display font-medium text-navy">{{ $featured_news->chapo }}</p>
<p>
<grid cols="4" class="sm:grid-cols-2 xs:grid-cols-1">
@foreach ($applications as $application)
+{{-- @dump($application->id, $nav->getHrefByID('application/'.$application->id))--}}
<a class="solutions-link" href="{{ $nav->getHrefByID('application/'.$application->id) }}">
<div class="solutions-link-sizer"></div>
<span class="solutions-link-bg"
- style="background-image:url({{ $application->getImageURLbyCollection($application['image_solutions']) }})"></span>
+ style="background-image:url({{ $application->getImageURLbyCollection($application['image_solutions'], 'thumb') }})"></span>
<span class="solutions-link-text">{{ $application->title_solutions }}</span>
</a>
@endforeach
@php
$technical_sheet = $product->getMediaUrl('technical_sheet', false)
@endphp
-<div data-product-id="{{$id}}" class="product-grid-item bg-grey-200">
+<div data-product-id="{{ $id }}" class="product-grid-item bg-grey-200">
{{-- Image holder --}}
<a href="{{ $product->getEntity()->getURLAttribute() }}" class="">
<div class="product-img-holder">
<div class="product-img">
- <img src="{{$product->getEntity()->image}}"
- alt="{{$product->getEntity()->image_alt}}"/>
+ <img src="{{ $product->getEntity()->image }}"
+ alt="{{ $product->getEntity()->image_alt }}"/>
</div>
</div>
</a><!--
There shoulln't be any space between closing of a and opening of div
{{-- Product details --}}
--><div class="product-content p-4">
- <h3><a class="text-navy" href="{{ $product->getEntity()->getURLAttribute() }}">{{ $product->get('reference') }}</a>
+ <h3><a class="text-navy" href="{{ $product->getEntity()->URL }}">{{ $product->get('reference') }}</a>
</h3>
<div class="product-highlights text-sm">
{{$product->get('name')}}
@endif
<div class="links mt-4">
<div class="link">
- <a href="{{ $product->getEntity()->getURLAttribute() }}">
+ <a href="{{ $product->getEntity()->URL }}">
{{ __('En savoir plus') }}
</a>
</div>