| @@ -0,0 +1,5 @@ | |||
| **Possible issues** | |||
| - The plugin only reacts to the state 'shipped', partially shipped and returned items are not influencing the customer tag | |||
| - Plugin only works with a boolean custom field, because it is looking for a truthy value | |||
| - Delete limit set to 999 | |||
| @@ -0,0 +1,34 @@ | |||
| { | |||
| "name": "atl/discount-extension", | |||
| "description": "Discount Extension", | |||
| "version": "1.0.0", | |||
| "type": "shopware-platform-plugin", | |||
| "license": "proprietary", | |||
| "authors": [ | |||
| { | |||
| "name": "Atloss GmbH", | |||
| "role": "Agency" | |||
| } | |||
| ], | |||
| "require": { | |||
| "shopware/core": "~6.4.0" | |||
| }, | |||
| "autoload": { | |||
| "psr-4": { | |||
| "Atl\\DiscountExtension\\": "src/" | |||
| } | |||
| }, | |||
| "extra": { | |||
| "shopware-plugin-class": "Atl\\DiscountExtension\\AtlDiscountExtension", | |||
| "plugin-icon": "src/Resources/config/plugin.png", | |||
| "copyright": "(c) by Atloss", | |||
| "label": { | |||
| "de-DE": "Rabatt Erweiterung", | |||
| "en-GB": "Discount Extension" | |||
| }, | |||
| "description": { | |||
| "de-DE": "Versieht ausgewählte Kunden bei Bestellungen mit Tags für den Rabatt von Folgebestellungen.", | |||
| "en-GB": "Tags selected customers on orders for discount on subsequent orders." | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,22 @@ | |||
| <?php declare(strict_types=1); | |||
| namespace Atl\DiscountExtension; | |||
| use Doctrine\DBAL\Connection; | |||
| use Shopware\Core\Framework\Plugin; | |||
| use Shopware\Core\Framework\Plugin\Context\UninstallContext; | |||
| class AtlDiscountExtension extends Plugin | |||
| { | |||
| public function uninstall(UninstallContext $uninstallContext): void | |||
| { | |||
| parent::uninstall($uninstallContext); | |||
| if ($uninstallContext->keepUserData()) { | |||
| return; | |||
| } | |||
| $connection = $this->container->get(Connection::class); | |||
| $connection->executeStatement("DROP TABLE IF EXISTS `spwn_repeat_discount`"); | |||
| } | |||
| } | |||
| @@ -0,0 +1,136 @@ | |||
| <?php declare(strict_types=1); | |||
| namespace Atl\DiscountExtension\Command; | |||
| use Shopware\Core\Framework\Adapter\Console\ShopwareStyle; | |||
| use Shopware\Core\Framework\Context; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\RangeFilter; | |||
| use Shopware\Core\System\SystemConfig\SystemConfigService; | |||
| use Symfony\Component\Console\Command\Command; | |||
| use Symfony\Component\Console\Input\InputInterface; | |||
| use Symfony\Component\Console\Output\OutputInterface; | |||
| class CheckDiscountEligibility extends Command | |||
| { | |||
| protected static $defaultName = 'atl:check-discount-eligibility'; | |||
| /** | |||
| * @var SystemConfigService | |||
| */ | |||
| private $systemConfigService; | |||
| /** | |||
| * @var EntityRepositoryInterface | |||
| */ | |||
| private $repeatDiscountRepository; | |||
| /** | |||
| * @var EntityRepositoryInterface | |||
| */ | |||
| private $customerTagRepository; | |||
| public function __construct | |||
| ( | |||
| SystemConfigService $systemConfigService, | |||
| EntityRepositoryInterface $repeatDiscountRepository, | |||
| EntityRepositoryInterface $customerTagRepository | |||
| ) | |||
| { | |||
| parent::__construct(); | |||
| $this->systemConfigService = $systemConfigService; | |||
| $this->repeatDiscountRepository = $repeatDiscountRepository; | |||
| $this->customerTagRepository = $customerTagRepository; | |||
| } | |||
| protected function configure(): void | |||
| { | |||
| $this->setDescription('Checks the discount eligibility and removes the discount customer tag if it expired.'); | |||
| } | |||
| /** | |||
| * @param InputInterface $input | |||
| * @param OutputInterface $output | |||
| * @return int | |||
| */ | |||
| protected function execute(InputInterface $input, OutputInterface $output): int | |||
| { | |||
| $io = new ShopwareStyle($input, $output); | |||
| $tagId = $this->systemConfigService->get('AtlDiscountExtension.config.tag'); | |||
| if (empty($tagId)) { | |||
| $io->warning('Customer tag assignment in plugin configuration is missing.'); | |||
| return self::FAILURE; | |||
| } | |||
| $expiredDiscounts = $this->getExpiredDiscounts(); | |||
| if (empty($expiredDiscounts)) { | |||
| $io->success('No customer tags were removed.'); | |||
| return self::SUCCESS; | |||
| } | |||
| $tags = []; | |||
| $discounts = []; | |||
| foreach ($expiredDiscounts as $expiredDiscount) { | |||
| $tags[] = [ | |||
| 'customerId' => $expiredDiscount->getCustomerId(), | |||
| 'tagId' => $tagId | |||
| ]; | |||
| $discounts[] = [ | |||
| 'id' => $expiredDiscount->getId() | |||
| ]; | |||
| } | |||
| $this->removeCustomerTags($tags); | |||
| $this->removeDiscounts($discounts); | |||
| $io->success(sprintf('Successfully deleted %d expired discounts.', count($expiredDiscounts))); | |||
| return self::SUCCESS; | |||
| } | |||
| /** | |||
| * @return array | |||
| */ | |||
| private function getExpiredDiscounts(): array | |||
| { | |||
| $expirationDays = $this->systemConfigService->get('AtlDiscountExtension.config.expirationDays'); | |||
| // Go $expirationDays back in time | |||
| $expirationDate = (new \DateTime())->add(\DateInterval::createFromDateString('-' . $expirationDays . ' days')); | |||
| $criteria = new Criteria(); | |||
| $criteria->addFilter( | |||
| new RangeFilter('lastOrderDate', | |||
| [ | |||
| // last order date is earlier than expiration date | |||
| RangeFilter::LTE => $expirationDate->format(\DATE_ATOM), | |||
| ] | |||
| )); | |||
| $criteria->setLimit(999); | |||
| return $this->repeatDiscountRepository->search($criteria, Context::createDefaultContext())->getElements(); | |||
| } | |||
| /** | |||
| * @param array $tags | |||
| * @return void | |||
| */ | |||
| private function removeCustomerTags(array $tags): void | |||
| { | |||
| $this->customerTagRepository->delete($tags, Context::createDefaultContext()); | |||
| } | |||
| /** | |||
| * @param array $discounts | |||
| * @return void | |||
| */ | |||
| private function removeDiscounts(array $discounts): void | |||
| { | |||
| $this->repeatDiscountRepository->delete($discounts, Context::createDefaultContext()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| <?php declare(strict_types=1); | |||
| namespace Atl\DiscountExtension\Core\System\RepeatDiscount; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection; | |||
| /** | |||
| * @method void add(RepeatDiscountEntity $entity) | |||
| * @method void set(string $key, RepeatDiscountEntity $entity) | |||
| * @method RepeatDiscountEntity[] getIterator() | |||
| * @method RepeatDiscountEntity[] getElements() | |||
| * @method RepeatDiscountEntity[]|null get(string $key) | |||
| * @method RepeatDiscountEntity[]|null first() | |||
| * @method RepeatDiscountEntity[]|null last() | |||
| */ | |||
| class RepeatDiscountCollection extends EntityCollection | |||
| { | |||
| } | |||
| @@ -0,0 +1,44 @@ | |||
| <?php declare(strict_types=1); | |||
| namespace Atl\DiscountExtension\Core\System\RepeatDiscount; | |||
| use Shopware\Core\Checkout\Customer\CustomerDefinition; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\Field\DateTimeField; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\Field\FkField; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\ApiAware; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\PrimaryKey; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\Required; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\Field\IdField; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\Field\OneToOneAssociationField; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\FieldCollection; | |||
| class RepeatDiscountDefinition extends EntityDefinition | |||
| { | |||
| public const ENTITY_NAME = 'spwn_repeat_discount'; | |||
| public function getEntityName(): string | |||
| { | |||
| return self::ENTITY_NAME; | |||
| } | |||
| public function getEntityClass(): string | |||
| { | |||
| return RepeatDiscountEntity::class; | |||
| } | |||
| public function getCollectionClass(): string | |||
| { | |||
| return RepeatDiscountCollection::class; | |||
| } | |||
| protected function defineFields(): FieldCollection | |||
| { | |||
| return new FieldCollection([ | |||
| (new IdField('id', 'id'))->addFlags(new ApiAware(), new PrimaryKey(), new Required()), | |||
| (new FkField('customer_id', 'customerId', CustomerDefinition::class))->addFlags(new ApiAware(), new Required()), | |||
| (new DateTimeField('last_order_date', 'lastOrderDate'))->addFlags(new ApiAware(), new Required()), | |||
| new OneToOneAssociationField('customer', 'customer_id', 'id', CustomerDefinition::class, false) | |||
| ]); | |||
| } | |||
| } | |||
| @@ -0,0 +1,75 @@ | |||
| <?php declare(strict_types=1); | |||
| namespace Atl\DiscountExtension\Core\System\RepeatDiscount; | |||
| use Shopware\Core\Checkout\Customer\CustomerEntity; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\Entity; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\EntityIdTrait; | |||
| class RepeatDiscountEntity extends Entity | |||
| { | |||
| use EntityIdTrait; | |||
| /** | |||
| * @var string | |||
| */ | |||
| protected $customerId; | |||
| /** | |||
| * @var \DateTimeInterface | |||
| */ | |||
| protected $lastOrderDate; | |||
| /** | |||
| * @var CustomerEntity | |||
| */ | |||
| protected $customer; | |||
| /** | |||
| * @return string | |||
| */ | |||
| public function getCustomerId(): string | |||
| { | |||
| return $this->customerId; | |||
| } | |||
| /** | |||
| * @param string $customerId | |||
| */ | |||
| public function setCustomerId(string $customerId): void | |||
| { | |||
| $this->customerId = $customerId; | |||
| } | |||
| /** | |||
| * @return \DateTimeInterface | |||
| */ | |||
| public function getLastOrderDate(): \DateTimeInterface | |||
| { | |||
| return $this->lastOrderDate; | |||
| } | |||
| /** | |||
| * @param \DateTimeInterface $lastOrderDate | |||
| */ | |||
| public function setLastOrderDate(\DateTimeInterface $lastOrderDate): void | |||
| { | |||
| $this->lastOrderDate = $lastOrderDate; | |||
| } | |||
| /** | |||
| * @return CustomerEntity | |||
| */ | |||
| public function getCustomer(): CustomerEntity | |||
| { | |||
| return $this->customer; | |||
| } | |||
| /** | |||
| * @param CustomerEntity $customer | |||
| */ | |||
| public function setCustomer(CustomerEntity $customer): void | |||
| { | |||
| $this->customer = $customer; | |||
| } | |||
| } | |||
| @@ -0,0 +1,24 @@ | |||
| <?php declare(strict_types=1); | |||
| namespace Atl\DiscountExtension\Extension\Checkout\Customer; | |||
| use Atl\DiscountExtension\Core\System\RepeatDiscount\RepeatDiscountDefinition; | |||
| use Shopware\Core\Checkout\Customer\CustomerDefinition; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\EntityExtension; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\Field\OneToOneAssociationField; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\FieldCollection; | |||
| class CustomerExtension extends EntityExtension | |||
| { | |||
| public function extendFields(FieldCollection $collection): void | |||
| { | |||
| $collection->add( | |||
| (new OneToOneAssociationField('repeatDiscount', 'id', 'customer_id', RepeatDiscountDefinition::class)) | |||
| ); | |||
| } | |||
| public function getDefinitionClass(): string | |||
| { | |||
| return CustomerDefinition::class; | |||
| } | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| <?php declare(strict_types=1); | |||
| namespace Atl\DiscountExtension\Migration; | |||
| use Doctrine\DBAL\Connection; | |||
| use Shopware\Core\Framework\Migration\MigrationStep; | |||
| class Migration1645524148SpwnRepeatDiscount extends MigrationStep | |||
| { | |||
| public function getCreationTimestamp(): int | |||
| { | |||
| return 1645524148; | |||
| } | |||
| public function update(Connection $connection): void | |||
| { | |||
| $connection->executeStatement(' | |||
| CREATE TABLE IF NOT EXISTS `spwn_repeat_discount` ( | |||
| `id` BINARY(16) NOT NULL, | |||
| `customer_id` BINARY(16) NOT NULL, | |||
| `last_order_date` DATETIME(3) NOT NULL, | |||
| `created_at` DATETIME(3) NOT NULL, | |||
| `updated_at` DATETIME(3) NULL, | |||
| PRIMARY KEY (`id`), | |||
| CONSTRAINT `uniq.spwn_repeat_discount.customer_id` UNIQUE (`customer_id`), | |||
| CONSTRAINT `fk.spwn_repeat_discount.customer_id` FOREIGN KEY (`customer_id`) | |||
| REFERENCES `customer` (`id`) ON DELETE CASCADE ON UPDATE CASCADE | |||
| ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; | |||
| '); | |||
| } | |||
| public function updateDestructive(Connection $connection): void | |||
| { | |||
| // implement update destructive | |||
| } | |||
| } | |||
| @@ -0,0 +1,37 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/shopware/platform/master/src/Core/System/SystemConfig/Schema/config.xsd"> | |||
| <card> | |||
| <title>Settings</title> | |||
| <title lang="de-DE">Einstellungen</title> | |||
| <component name="sw-entity-single-select"> | |||
| <name>customField</name> | |||
| <entity>custom_field</entity> | |||
| <label lang="de-DE">Zusatzfeld</label> | |||
| <label>Custom field</label> | |||
| </component> | |||
| <input-field type="int"> | |||
| <name>minAmountDiscountableLineItems</name> | |||
| <label lang="de-DE">Mindestanzahl an Bestellpositionen für den Rabatt</label> | |||
| <label>Minimum amount of order items for the discount</label> | |||
| <defaultValue>3</defaultValue> | |||
| </input-field> | |||
| <component name="sw-entity-single-select"> | |||
| <name>tag</name> | |||
| <entity>tag</entity> | |||
| <label lang="de-DE">Kunden Tag</label> | |||
| <label>Customer tag</label> | |||
| </component> | |||
| <input-field type="int"> | |||
| <name>expirationDays</name> | |||
| <label lang="de-DE">Anzahl Tage nachdem der Rabatt ausläuft</label> | |||
| <label>Number of days after the discount expires</label> | |||
| <defaultValue>56</defaultValue> | |||
| </input-field> | |||
| </card> | |||
| </config> | |||
| @@ -0,0 +1,31 @@ | |||
| <?xml version="1.0" ?> | |||
| <container xmlns="http://symfony.com/schema/dic/services" | |||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
| xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> | |||
| <services> | |||
| <service id="Atl\DiscountExtension\Extension\Checkout\Customer\CustomerExtension"> | |||
| <tag name="shopware.entity.extension" /> | |||
| </service> | |||
| <service id="Atl\DiscountExtension\Core\System\RepeatDiscount\RepeatDiscountDefinition"> | |||
| <tag name="shopware.entity.definition" entity="spwn_repeat_discount" /> | |||
| </service> | |||
| <service id="Atl\DiscountExtension\Subscriber\StateMachineTransitionSubscriber"> | |||
| <argument type="service" id="Shopware\Core\System\SystemConfig\SystemConfigService" /> | |||
| <argument type="service" id="order_delivery.repository"/> | |||
| <argument type="service" id="custom_field.repository" /> | |||
| <argument type="service" id="customer.repository" /> | |||
| <tag name="kernel.event_subscriber"/> | |||
| </service> | |||
| <service id="Atl\DiscountExtension\Command\CheckDiscountEligibility"> | |||
| <argument type="service" id="Shopware\Core\System\SystemConfig\SystemConfigService" /> | |||
| <argument type="service" id="spwn_repeat_discount.repository" /> | |||
| <argument type="service" id="customer_tag.repository" /> | |||
| <tag name="console.command"/> | |||
| </service> | |||
| </services> | |||
| </container> | |||
| @@ -0,0 +1,198 @@ | |||
| <?php declare(strict_types=1); | |||
| namespace Atl\DiscountExtension\Subscriber; | |||
| use Atl\DiscountExtension\Core\System\RepeatDiscount\RepeatDiscountEntity; | |||
| use Shopware\Core\Checkout\Customer\CustomerEntity; | |||
| use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryEntity; | |||
| use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryStates; | |||
| use Shopware\Core\Checkout\Order\Aggregate\OrderDeliveryPosition\OrderDeliveryPositionCollection; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; | |||
| use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; | |||
| use Shopware\Core\System\CustomField\CustomFieldEntity; | |||
| use Shopware\Core\System\StateMachine\Event\StateMachineTransitionEvent; | |||
| use Shopware\Core\System\SystemConfig\SystemConfigService; | |||
| use Symfony\Component\EventDispatcher\EventSubscriberInterface; | |||
| class StateMachineTransitionSubscriber implements EventSubscriberInterface | |||
| { | |||
| /** | |||
| * @var SystemConfigService | |||
| */ | |||
| private $systemConfigService; | |||
| /** | |||
| * @var EntityRepositoryInterface | |||
| */ | |||
| private $orderDeliveryRepository; | |||
| /** | |||
| * @var EntityRepositoryInterface | |||
| */ | |||
| private $customFieldRepository; | |||
| /** | |||
| * @var EntityRepositoryInterface | |||
| */ | |||
| private $customerRepository; | |||
| public function __construct | |||
| ( | |||
| SystemConfigService $systemConfigService, | |||
| EntityRepositoryInterface $orderDeliveryRepository, | |||
| EntityRepositoryInterface $customFieldRepository, | |||
| EntityRepositoryInterface $customerRepository | |||
| ) | |||
| { | |||
| $this->systemConfigService = $systemConfigService; | |||
| $this->orderDeliveryRepository = $orderDeliveryRepository; | |||
| $this->customFieldRepository = $customFieldRepository; | |||
| $this->customerRepository = $customerRepository; | |||
| } | |||
| /** | |||
| * @return string[] | |||
| */ | |||
| public static function getSubscribedEvents(): array | |||
| { | |||
| return [ | |||
| StateMachineTransitionEvent::class => 'onStateTransition' | |||
| ]; | |||
| } | |||
| /** | |||
| * @param StateMachineTransitionEvent $event | |||
| * @return void | |||
| */ | |||
| public function onStateTransition(StateMachineTransitionEvent $event): void | |||
| { | |||
| // Early return if transition state is not 'shipped' | |||
| if ($event->getToPlace()->getTechnicalName() !== OrderDeliveryStates::STATE_SHIPPED) { | |||
| return; | |||
| } | |||
| $orderDeliveryId = $event->getEntityId(); | |||
| $orderDelivery = $this->getOrderDelivery($orderDeliveryId, $event); | |||
| // In case no order delivery could be found - return | |||
| if (empty($orderDelivery)) { | |||
| return; | |||
| } | |||
| $amountDiscountableLineItems = $this->getAmountDiscountableLineItems($orderDelivery->getPositions(), $event); | |||
| $minAmountDiscountableLineItems = $this->systemConfigService->get('AtlDiscountExtension.config.minAmountDiscountableLineItems'); | |||
| if ($amountDiscountableLineItems < $minAmountDiscountableLineItems) { | |||
| return; | |||
| } | |||
| $customer = $orderDelivery->getOrder()->getOrderCustomer()->getCustomer(); | |||
| // Tag the customer and trace timestamp in 'spwn_repeat_discount' table | |||
| $this->tagCustomer($customer, $event); | |||
| } | |||
| /** | |||
| * @param string $orderDeliveryId | |||
| * @param StateMachineTransitionEvent $event | |||
| * @return OrderDeliveryEntity | |||
| */ | |||
| private function getOrderDelivery(string $orderDeliveryId, StateMachineTransitionEvent $event): OrderDeliveryEntity | |||
| { | |||
| $criteria = new Criteria(); | |||
| $criteria->addFilter(new EqualsFilter('id', $orderDeliveryId)); | |||
| $criteria->addAssociation('positions.orderLineItem'); | |||
| $criteria->addAssociation('order.orderCustomer.customer'); | |||
| return $this->orderDeliveryRepository->search($criteria, $event->getContext())->first(); | |||
| } | |||
| /** | |||
| * @param OrderDeliveryPositionCollection $positions | |||
| * @return int | |||
| */ | |||
| private function getAmountDiscountableLineItems(OrderDeliveryPositionCollection $positions, StateMachineTransitionEvent $event): int | |||
| { | |||
| $customFieldName = $this->getCustomFieldName($event); | |||
| // In case no custom field was assigned in the plugin config - return | |||
| if ($customFieldName === '') { | |||
| return 0; | |||
| } | |||
| $amountDiscountableLineItems = 0; | |||
| foreach($positions as $position) { | |||
| $customFields = $position->getOrderLineItem()->getPayload()['customFields']; | |||
| // Checks if the custom field is set to true | |||
| if ($customFields[$customFieldName] === true) { | |||
| $amountDiscountableLineItems++; | |||
| } | |||
| } | |||
| return $amountDiscountableLineItems; | |||
| } | |||
| /** | |||
| * @param StateMachineTransitionEvent $event | |||
| * @return string | |||
| */ | |||
| private function getCustomFieldName(StateMachineTransitionEvent $event): string | |||
| { | |||
| $customFieldId = $this->systemConfigService->get('AtlDiscountExtension.config.customField'); | |||
| $criteria = new Criteria(); | |||
| $criteria->addFilter(new EqualsFilter('id', $customFieldId)); | |||
| /** @var CustomFieldEntity $customField */ | |||
| $customField = $this->customFieldRepository->search($criteria, $event->getContext())->first(); | |||
| // In case no custom field was assigned in the plugin config - return | |||
| if (empty($customField)) { | |||
| return ''; | |||
| } | |||
| return $customField->getName(); | |||
| } | |||
| /** | |||
| * @param CustomerEntity $customer | |||
| * @param StateMachineTransitionEvent $event | |||
| * @return void | |||
| */ | |||
| private function tagCustomer(CustomerEntity $customer, StateMachineTransitionEvent $event): void | |||
| { | |||
| $tagId = $this->systemConfigService->get('AtlDiscountExtension.config.tag'); | |||
| if (empty($tagId)) { | |||
| return; | |||
| } | |||
| $update = [ | |||
| 'id' => $customer->getId(), | |||
| 'tags' => [ | |||
| [ | |||
| 'id' => $tagId | |||
| ] | |||
| ], | |||
| 'repeatDiscount' => [ | |||
| 'customerId' => $customer->getId(), | |||
| 'lastOrderDate' => new \DateTime() | |||
| ] | |||
| ]; | |||
| /** @var RepeatDiscountEntity $repeatDiscount */ | |||
| $repeatDiscount = $customer->getExtensions()['repeatDiscount']; | |||
| // If a discount is already applied, update its duration, else create one | |||
| if (!empty($repeatDiscount)) { | |||
| $repeatDiscountId = $repeatDiscount->getId(); | |||
| $update['repeatDiscount']['id'] = $repeatDiscountId; | |||
| } | |||
| $this->customerRepository->update([$update], $event->getContext()); | |||
| } | |||
| } | |||
| @@ -1,7 +1,10 @@ | |||
| @import 'layout/header'; | |||
| @import 'layout/footer'; | |||
| @import 'component/card'; | |||
| @import 'component/cms-block'; | |||
| @import 'component/cms-element'; | |||
| @import 'component/filter-multi-select'; | |||
| @import 'component/product-box'; | |||
| @import 'page/content/breadcrumb'; | |||
| @import 'page/checkout/cart'; | |||
| @import 'page/product-detail/product-detail'; | |||
| @@ -0,0 +1,24 @@ | |||
| .filter-panel-items-container { | |||
| display: block; | |||
| } | |||
| .filter-panel-item { | |||
| margin-right: 0; | |||
| } | |||
| .filter-multi-select-list { | |||
| display: flex; | |||
| justify-content: center; | |||
| } | |||
| .filter-multi-select-list-item { | |||
| background-color: transparent; | |||
| border-bottom: none; | |||
| &:hover { | |||
| background-color: transparent; | |||
| } | |||
| } | |||
| .filter-multi-select-item-label { | |||
| color: #222; | |||
| } | |||
| @@ -2,6 +2,10 @@ | |||
| &.highlight { | |||
| background-color: #E9F4FC; | |||
| } | |||
| .brandLogo { | |||
| max-width: 60px; | |||
| margin: 8px 0 0 0; | |||
| } | |||
| .product-name { | |||
| font-size: 18px; | |||
| line-height: 20px; | |||
| @@ -31,4 +35,12 @@ | |||
| .product-cheapest-price { | |||
| display: none; | |||
| } | |||
| } | |||
| .cms-block.easyfit { | |||
| .product-box { | |||
| .product-info { | |||
| min-height: 135px; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,99 @@ | |||
| #collapseFooterCustom { | |||
| p { | |||
| margin-bottom: 5px; | |||
| img { | |||
| margin-bottom: 8px; | |||
| } | |||
| } | |||
| } | |||
| .footer-newsletter { | |||
| .newsletter-inner-text { | |||
| font-size: 18px; | |||
| line-height: 24px; | |||
| } | |||
| .footer-newsletter-column-input-email { | |||
| border-bottom: 2px solid $sw-color-brand-primary; | |||
| margin: 0 15px; | |||
| padding: 0; | |||
| } | |||
| .form-control, | |||
| .input-group-append .btn { | |||
| border: none; | |||
| } | |||
| .form-control { | |||
| padding-top: 0; | |||
| padding-left: 0; | |||
| padding-bottom: 0; | |||
| font-size: 18px; | |||
| line-height: 24px; | |||
| } | |||
| } | |||
| .footer-hotline-column { | |||
| .footer-contact-hotline { | |||
| margin-bottom: 8px; | |||
| a:not(.btn) { | |||
| margin-top: 16px; | |||
| margin-bottom: 0; | |||
| line-height: 15px; | |||
| } | |||
| } | |||
| .footer-contact-form { | |||
| margin-top: 0; | |||
| a { | |||
| color: #222; | |||
| text-decoration: underline; | |||
| &:hover { | |||
| color: #222; | |||
| text-decoration: none; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| .footer-column-headline { | |||
| margin-bottom: 0; | |||
| &:before { | |||
| display: none; | |||
| } | |||
| } | |||
| .footer-link-item { | |||
| a { | |||
| &:after { | |||
| top: 75%; | |||
| } | |||
| } | |||
| } | |||
| @media (min-width: 768px) { | |||
| .footer-link-item { | |||
| padding: 0; | |||
| } | |||
| } | |||
| .social-icons { | |||
| display: flex; | |||
| justify-content: center; | |||
| margin-bottom: 10px; | |||
| .icon { | |||
| border: 2px solid $sw-color-brand-primary; | |||
| border-radius: 50%; | |||
| width: 40px; | |||
| height: 40px; | |||
| margin: 0 8px; | |||
| &.icon-facebook { | |||
| padding: 8px; | |||
| } | |||
| &.icon-instagram { | |||
| padding: 5px; | |||
| } | |||
| } | |||
| svg { | |||
| color: $sw-color-brand-primary; | |||
| } | |||
| } | |||
| @@ -1,3 +1,8 @@ | |||
| .header-row { | |||
| padding-top: 10px; | |||
| border-bottom: 1px solid #B1C3D9; | |||
| } | |||
| .header-main { | |||
| .header-cart-btn { | |||
| .header-cart-total { | |||
| @@ -51,4 +56,18 @@ | |||
| line-height: 18px; | |||
| text-transform: uppercase; | |||
| padding: 0; | |||
| .main-navigation-link-text { | |||
| &:after { | |||
| height: 3px; | |||
| left: 0; | |||
| right: 0; | |||
| bottom: -18px; | |||
| } | |||
| } | |||
| &.active { | |||
| .main-navigation-link-text { | |||
| color: $sw-color-brand-primary; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| .breadcrumb-wrap { | |||
| a { | |||
| &.is-active { | |||
| font-weight: 700; | |||
| border-bottom: 0; | |||
| } | |||
| } | |||
| .breadcrumb-placeholder { | |||
| svg { | |||
| color: #222; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,9 +0,0 @@ | |||
| <div class="sw-cms-preview-swag-image-text-reversed"> | |||
| <div> | |||
| <h2>Lorem ipsum dolor</h2> | |||
| <p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr.</p> | |||
| </div> | |||
| <!-- Alternatively you might e.g. also use a base64 encoded preview image instead of an external resource. --> | |||
| <img src="https://example.com/preview.jpg" alt="Preview image"> | |||
| </div> | |||
| @@ -1,35 +0,0 @@ | |||
| /* | |||
| * Styling of your block preview in the CMS sidebar | |||
| */ | |||
| .sw-cms-preview-swag-image-text-reversed { | |||
| display: grid; | |||
| grid-template-columns: 1fr 1fr; | |||
| grid-column-gap: 20px; | |||
| padding: 15px; | |||
| } | |||
| /* | |||
| * Styling of your block in the CMS editor | |||
| * Pattern: sw-cms-block-${block.name}-component | |||
| */ | |||
| .sw-cms-block-swag-image-text-reversed-component { | |||
| display: grid; | |||
| grid-template-columns: repeat(auto-fit, minmax(195px, 1fr)); | |||
| grid-gap: 40px; | |||
| } | |||
| /* | |||
| * Each slot will have an additional class | |||
| * Pattern: sw-cms-slot-${slot.name} | |||
| */ | |||
| .sw-cms-block-swag-image-text-reversed-component .sw-cms-slot-left { | |||
| } | |||
| /* | |||
| * Each slot will have an additional class | |||
| * Pattern: sw-cms-slot-${slot.name} | |||
| */ | |||
| .sw-cms-block-swag-image-text-reversed-component .sw-cms-slot-right { | |||
| } | |||
| @@ -0,0 +1,56 @@ | |||
| {% sw_extends '@Storefront/storefront/component/listing/filter/filter-multi-select.html.twig' %} | |||
| {% block component_filter_multi_select %} | |||
| <div class="filter-multi-select filter-multi-select-{{ name }} filter-panel-item{% if not sidebar %} dropdown{% endif %}" | |||
| data-{{ pluginSelector }}="true" | |||
| data-{{ pluginSelector }}-options='{{ dataPluginSelectorOptions|json_encode }}'> | |||
| {% block component_filter_multi_select_toggle %} | |||
| <button style="display:none;" class="filter-panel-item-toggle btn{% if sidebar %} btn-block{% endif %}" | |||
| aria-expanded="false" | |||
| {% if sidebar %} | |||
| data-toggle="collapse" | |||
| data-target="#{{ filterItemId }}" | |||
| {% else %} | |||
| data-toggle="dropdown" | |||
| data-boundary="viewport" | |||
| data-offset="0,8" | |||
| aria-haspopup="true" | |||
| {% endif %}> | |||
| {% block component_filter_multi_select_display_name %} | |||
| {{ displayName }} | |||
| {% endblock %} | |||
| {% block component_filter_multi_select_count %} | |||
| <span class="filter-multi-select-count"></span> | |||
| {% endblock %} | |||
| {% block component_filter_multi_select_toggle_icon %} | |||
| {% sw_icon 'arrow-medium-down' style { | |||
| 'pack': 'solid', 'size': 'xs', 'class': 'filter-panel-item-toggle' | |||
| } %} | |||
| {% endblock %} | |||
| </button> | |||
| {% endblock %} | |||
| {% block component_filter_multi_select_dropdown %} | |||
| <div class="filter-multi-select-dropdown" | |||
| id="{{ filterItemId }}"> | |||
| {% block component_filter_multi_select_list %} | |||
| <ul class="filter-multi-select-list"> | |||
| {% for element in elements %} | |||
| {% block component_filter_multi_select_list_item %} | |||
| <li class="filter-multi-select-list-item"> | |||
| {% block component_filter_multi_select_list_item_inner %} | |||
| {% sw_include '@Storefront/storefront/component/listing/filter/filter-multi-select-list-item.html.twig' %} | |||
| {% endblock %} | |||
| </li> | |||
| {% endblock %} | |||
| {% endfor %} | |||
| </ul> | |||
| {% endblock %} | |||
| </div> | |||
| {% endblock %} | |||
| </div> | |||
| {% endblock %} | |||
| @@ -107,6 +107,14 @@ | |||
| {% block component_product_box_info %} | |||
| <div class="product-info"> | |||
| {% if product.customFields.custom_productteaser_logo is defined %} | |||
| {% set logoImageId = product.customFields.custom_productteaser_logo %} | |||
| {% set mediaCollection = searchMedia([logoImageId], context.context) %} | |||
| {% set logoImage = mediaCollection.get(logoImageId) %} | |||
| <figure class="brandLogo"> | |||
| <img src="{{ logoImage.url }}"> | |||
| </figure> | |||
| {% endif %} | |||
| {% block component_product_box_rating %} | |||
| {% if theme_config('zen-product-listing-card-rating-position') is same as ('default') %} | |||
| {{ parent() }} | |||
| @@ -1,5 +1,9 @@ | |||
| {% sw_extends '@Storefront/storefront/layout/footer/footer.html.twig' %} | |||
| {% block layout_footer_payment_shipping_logos %} | |||
| {% endblock %} | |||
| {% block layout_footer_copyright %} | |||
| <div class="footer-copyright"> | |||
| {{ "footer.copyrightInfo"|trans|sw_sanitize }} | |||
| @@ -0,0 +1,141 @@ | |||
| {% sw_extends '@Storefront/storefront/layout/header/header.html.twig' %} | |||
| {% block layout_header_actions %} | |||
| <div class="header-actions-col {{ actionClasses }}"> | |||
| <div class="row no-gutters"> | |||
| {% block layout_header_search_toggle %} | |||
| <div class="col-auto{% if theme_config('zen-search-style') is same as('default') %} d-sm-none"{% endif %}"> | |||
| <div class="search-toggle"> | |||
| <button class="btn header-actions-btn search-toggle-btn js-search-toggle-btn collapsed" | |||
| type="button" | |||
| {% if theme_config('zen-search-style') is same as('overlay') %} | |||
| data-toggle="overlay" | |||
| {% else %} | |||
| data-toggle="collapse" | |||
| data-target="#searchCollapse" | |||
| aria-expanded="false" | |||
| aria-controls="searchCollapse" | |||
| {% endif %} | |||
| aria-label="{{ "header.searchButton"|trans|striptags }}"> | |||
| {% sw_icon 'search' %} | |||
| {% sw_icon 'x' style { 'class': 'search-close d-none' } %} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| {% block layout_header_hotline %} | |||
| <div class="col-auto"> | |||
| <div class="phone-icon"> | |||
| <a href="{{ 'header.hotline'|trans|striptags }}" title="Hotline"> | |||
| <svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | |||
| <g id="Startseite" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> | |||
| <g id="D-DIAEKO-Startseite-z1-v7" transform="translate(-1093.000000, -16.000000)"> | |||
| <rect fill="#FFFFFF" x="0" y="0" width="1400" height="9017"></rect> | |||
| <g id="Elements-/-Header-DIÄKO-Desktop"> | |||
| <rect id="bg" fill="#FFFFFF" x="0" y="0" width="1400" height="120"></rect> | |||
| <g id="meta" transform="translate(1047.000000, 16.000000)" stroke="#222222" stroke-width="2"> | |||
| <g id="hilfe" transform="translate(46.000000, 0.000000)"> | |||
| <g id="icon" transform="translate(9.000000, 10.000000)"> | |||
| <path d="M12.791855,13.7844975 C13.2461579,13.0667754 13.6925461,12.3617189 14.1389343,11.6563986 C14.4389008,11.182227 14.5903351,11.1376332 15.0821008,11.382767 C16.9045888,12.291266 18.7270767,13.2000289 20.5493009,14.1090557 C21.0268201,14.3473289 21.0948864,14.5307174 20.8935896,15.0328591 C20.2896992,16.5400756 19.6860726,18.0475559 19.0808631,19.5542447 C18.91571,19.9653517 18.7766754,20.0505812 18.3498101,19.9753787 C15.0504421,19.3927572 12.0262413,18.1502007 9.34237184,16.1355653 C6.98063007,14.3626332 5.02570307,12.2144803 3.54117213,9.64888753 C2.27693324,7.46352916 1.46646958,5.11694717 1.02351109,2.63790323 C0.950168346,2.22706008 1.03828516,2.08430725 1.43217261,1.92598619 C2.96498312,1.3098534 4.49805745,0.694776078 6.03218708,0.0815458362 C6.44691653,-0.0841635406 6.66272359,-0.00473914189 6.85900775,0.388160957 C7.79452343,2.2603075 8.72819234,4.13298178 9.66001449,6.0069754 C9.84653722,6.38219631 9.78770473,6.57904216 9.42785042,6.80807997 C8.69943565,7.27143294 7.96917413,7.73161949 7.21358561,8.20948522 C8.66540251,10.4808647 10.5082048,12.3377069 12.791855,13.7844975 Z" id="Stroke-1"></path> | |||
| </g> | |||
| </g> | |||
| </g> | |||
| </g> | |||
| </g> | |||
| </g> | |||
| </svg> | |||
| </a> | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| {% if config('core.cart.wishlistEnabled') %} | |||
| {% block layout_header_actions_wishlist %} | |||
| <div class="col-auto"> | |||
| <div class="header-wishlist"> | |||
| <a class="btn header-wishlist-btn header-actions-btn" | |||
| href="{{ path('frontend.wishlist.page') }}" | |||
| title="{{ 'header.wishlist'|trans|striptags }}" | |||
| aria-label="{{ 'header.wishlist'|trans|striptags }}"> | |||
| {% sw_include '@Storefront/storefront/layout/header/actions/wishlist-widget.html.twig' %} | |||
| </a> | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| {% endif %} | |||
| {% block layout_header_actions_account %} | |||
| <div class="col-auto"> | |||
| <div class="account-menu"> | |||
| {% sw_include '@Storefront/storefront/layout/header/actions/account-widget.html.twig' %} | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| {% block layout_header_actions_cart %} | |||
| <div class="col-auto"> | |||
| <div class="header-cart" | |||
| data-offcanvas-cart="true"> | |||
| <a class="btn header-cart-btn header-actions-btn" | |||
| href="{{ path('frontend.checkout.cart.page') }}" | |||
| data-cart-widget="true" | |||
| title="{{ 'checkout.cartTitle'|trans|striptags }}" | |||
| aria-label="{{ 'checkout.cartTitle'|trans|striptags }}"> | |||
| {% sw_include '@Storefront/storefront/layout/header/actions/cart-widget.html.twig' %} | |||
| </a> | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| {% block zen_layout_header_top_bar_toggle %} | |||
| {# marketingBannerId is needed for storagekey invalidation #} | |||
| {% set marketingBanner = [] %} | |||
| {% set marketingBanner = [config('zenitPlatformAtmos.config.marketingActive')]|merge(marketingBanner) %} | |||
| {% set marketingBanner = [config('zenitPlatformAtmos.config.marketingText')]|merge(marketingBanner) %} | |||
| {% set marketingBanner = [config('zenitPlatformAtmos.config.marketingButtonText')]|merge(marketingBanner) %} | |||
| {% set marketingBanner = [config('zenitPlatformAtmos.config.marketingButtonLink')]|merge(marketingBanner) %} | |||
| {% set marketingBanner = [config('zenitPlatformAtmos.config.marketingButtonTarget')]|merge(marketingBanner) %} | |||
| {% set marketingBannerId = marketingBanner|json_encode()|length %} | |||
| {% set zenCollapseTopBarOptions = { | |||
| id: topBarStyle ~ '-' ~ marketingBannerId, | |||
| type: topBarStyle | |||
| } %} | |||
| {% if topBarStyle is not same as ('default') and topBarStyle is not same as ('hidden') %} | |||
| {% if theme_config('zen-header-style') is not same as ('two-line') or (theme_config('zen-header-style') is same as ('two-line') and config('zenitPlatformAtmos.config.marketingActive')) %} | |||
| <div class="col-auto d-none d-lg-block"> | |||
| <div class="top-bar-toggle"> | |||
| <button class="btn header-actions-btn top-bar-toggle-btn" | |||
| type="button" | |||
| aria-label="{{ "zentheme.general.moreLink"|trans|striptags }}" | |||
| {% if topBarStyle is same as ('offcanvas') %} | |||
| data-offcanvas-top-bar="true" | |||
| {% endif %} | |||
| {% if topBarStyle is same as ('collapsible') %} | |||
| data-toggle="collapse" | |||
| data-target="#topBarCollapse" | |||
| aria-expanded="true" | |||
| data-zen-collapse-top-bar-options="{{ zenCollapseTopBarOptions|json_encode }}" | |||
| {% endif %} | |||
| {% if topBarStyle is same as ('expandable') %} | |||
| data-toggle="collapse" | |||
| data-target="#topBarCollapse" | |||
| aria-expanded="false" | |||
| data-zen-collapse-top-bar-options="{{ zenCollapseTopBarOptions|json_encode }}" | |||
| {% endif %}> | |||
| {% if theme_config('zen-main-navigation-style') is same as ('offcanvas-lg') or theme_config('zen-main-navigation-style') is same as ('offcanvas-xl') %} | |||
| {% sw_icon 'more-vertical' %} | |||
| {% else %} | |||
| {% sw_icon 'stack' %} | |||
| {% endif %} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| {% endif %} | |||
| {% endif %} | |||
| {% endblock %} | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| @@ -0,0 +1,118 @@ | |||
| // spawntree | |||
| // overwriting date picker function | |||
| let spwnIncludedDays = [0,2,3,4,5,6]; | |||
| let spawnDateInputValue = ""; | |||
| let fp; | |||
| const inputSameDayDelivery = $("#shippingMethod67f8c7d3ecdb4ad7802bf8eb55c9b13b"); | |||
| const inputShopPickup = $("#shippingMethod0f44c72f7560450caf061922daeb1602"); | |||
| const inputDPD = $("#shippingMethod8c6d913e29c14c3cb3c6429724efaab8"); | |||
| $(document).ready(function() { | |||
| let dpInput = $(".confirm-delivery-date #dtgs-datepicker-inputfield"); | |||
| if (dpInput.length) { | |||
| fp = document.querySelector("#dtgs-datepicker-inputfield")._flatpickr; | |||
| dpInput.prop('disabled', true); | |||
| $("<div id='spwn-delivery'><p></p><div class='spwn-delivery--time'></div></div>").insertAfter(".confirm-delivery-date"); | |||
| fp.config.onChange.push(function() { | |||
| manipulateDateInputValue(); | |||
| }); | |||
| // If Austria and same day delivery is checked change to DPD | |||
| if ($(".confirm-address-shipping").data("iso") === "AT") { | |||
| inputDPD.attr('checked', true).trigger("click"); | |||
| // No same day delivery | |||
| inputSameDayDelivery.parents(".shipping-method").hide(); | |||
| // No shop | |||
| inputShopPickup.parents(".shipping-method").hide(); | |||
| } | |||
| // Call changeDelivery | |||
| $("[name='shippingMethodId']").on('change', () => { | |||
| changeDelivery(fp); | |||
| }); | |||
| changeDelivery(fp); | |||
| } | |||
| }); | |||
| function manipulateDateInputValue() { | |||
| spawnDateInputValue = $(".confirm-delivery-date #dtgs-datepicker-inputfield").val(); | |||
| if (inputSameDayDelivery.is(':checked')) { | |||
| // If same day delivery is checked, select time | |||
| $("#spwn-delivery .spwn-delivery--time span.active").trigger("click"); | |||
| } | |||
| } | |||
| function changeDelivery(fp) { | |||
| $("body").on("click", "#spwn-delivery .spwn-delivery--time span", function() { | |||
| $("#spwn-delivery .spwn-delivery--time span").removeClass("active"); | |||
| $(this).addClass("active"); | |||
| $(".confirm-delivery-date #dtgs-datepicker-inputfield").val(spawnDateInputValue + " " + $(this).text()); | |||
| }); | |||
| if ($(".confirm-address-shipping").data("iso") !== "AT") { | |||
| if (inputShopPickup.is(':checked')) { | |||
| // Abholung im Ladengeschäft | |||
| console.log("Abholung"); | |||
| $(".confirm-delivery-date #dtgs-datepicker-inputfield").val('Abholung im Ladengeschäft'); | |||
| $(".confirm-delivery-date").hide(); | |||
| $("#spwn-delivery p").text("Die bestellten Produkte stehen zur Abholung in unserem Ladengeschäft in Hamburg für Sie bereit."); | |||
| } else if (inputSameDayDelivery.is(':checked')) { | |||
| // Abendzustellung | |||
| console.log("Abendzustellung"); | |||
| $(".confirm-delivery-date #dtgs-datepicker-inputfield").val(''); | |||
| spwnIncludedDays = [1,2,3,4,5]; | |||
| setIncludedDays("evening", fp, 0); | |||
| $("#spwn-delivery p").text("Abendzustellung gewählt. Bitte wählen Sie nun eine Wunsch-Zeit:"); | |||
| $("#spwn-delivery .spwn-delivery--time").append("<span class='active'>18 - 21 Uhr</span><span>20 - 23 Uhr</span>"); | |||
| if (spawnDateInputValue === "" || spawnDateInputValue.indexOf("Uhr") === -1) { | |||
| manipulateDateInputValue(); | |||
| } | |||
| } else { | |||
| // DPD | |||
| console.log("DPD"); | |||
| spwnIncludedDays = [2,3,4,5,6]; | |||
| setIncludedDays("dpd", fp, 1); | |||
| $("#spwn-delivery p").text("DPD gewählt. Die Zustellung erfolgt zwischen 7 und 13 Uhr."); | |||
| } | |||
| } else { | |||
| console.log("DPD AT"); | |||
| spwnIncludedDays = [3,4]; | |||
| setIncludedDays("dpd", fp, 2); | |||
| $("#spwn-delivery p").text("DPD gewählt. Die Zustellung erfolgt zwischen 7 und 18 Uhr."); | |||
| } | |||
| } | |||
| function setIncludedDays(delivery, fp, addDays) { | |||
| let spwnDateTemp = new Date(); | |||
| spwnDateTemp.setDate(spwnDateTemp.getDate() + addDays); | |||
| let spwnTime = parseInt(spwnDateTemp.getHours() + "" + spwnDateTemp.getMinutes()); | |||
| if (delivery === "evening" && spwnTime > 1300) { | |||
| spwnDateTemp.setDate(spwnDateTemp.getDate() + 1); | |||
| } else if (delivery === "dpd" && spwnTime > 1400) { | |||
| spwnDateTemp.setDate(spwnDateTemp.getDate() + 1); | |||
| } | |||
| let spwnDate = fp.formatDate(spwnDateTemp, "d.m.Y"); | |||
| fp.set("minDate", spwnDate); | |||
| fp.set("enable", [ | |||
| function(date) { | |||
| let day = date.getDate(); | |||
| let month = date.getMonth(); | |||
| month = month + 1; | |||
| if((String(day)).length===1) | |||
| day='0'+day; | |||
| if((String(month)).length===1) | |||
| month='0'+month; | |||
| let formattedDate = day + '.' + month + '.' + date.getFullYear(); | |||
| //Excluded Dates | |||
| if($.inArray(formattedDate, dtgsDeliveryDateExcludedDates) > -1) { | |||
| return false; | |||
| } | |||
| // Datum auf Bereich Montag = 0, Sonntag = 7 bringen | |||
| return spwnIncludedDays.indexOf(((date.getDay() + 6) % 7) + 1) !== -1; | |||
| } | |||
| ]); | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| {% block block_gallery_buybox %} | |||
| {% block block_gallery_buybox_column_left %} | |||
| {% set element = block.slots.getSlot('left') %} | |||
| {% set config = element.fieldConfig.elements %} | |||
| <div class="col-lg-7 product-detail-media" data-cms-element-id="{{ element.id }}"> | |||
| {% block block_gallery_buybox_column_left_inner %} | |||
| {% sw_include "@Storefront/storefront/element/cms-element-" ~ element.type ~ ".html.twig" ignore missing | |||
| with { | |||
| 'isProduct': config.sliderItems.value == 'product.media' and config.sliderItems.source == 'mapped' | |||
| } %} | |||
| {% endblock %} | |||
| </div> | |||
| {% endblock %} | |||
| {% block block_gallery_buybox_column_right %} | |||
| {% set element = block.slots.getSlot('right') %} | |||
| <div class="col-lg-5 product-detail-buy js-sticky" data-sticky-buybox="true"> | |||
| {# ... add headline here #} | |||
| {% block page_product_detail_headline %} | |||
| <div class="row product-detail-headline"> | |||
| {% sw_include '@Storefront/storefront/page/product-detail/headline.html.twig' %} | |||
| </div> | |||
| {% endblock %} | |||
| {# ... add short description #} | |||
| {% block zen_page_product_detail_short_description %} | |||
| {% if page.product.translated.metaDescription and theme_config('zen-product-details-short-description') %} | |||
| <div class="product-detail-short-description"> | |||
| {{ page.product.translated.metaDescription|raw }} | |||
| </div> | |||
| {% endif %} | |||
| {% endblock %} | |||
| {% block block_gallery_buybox_column_right_inner %} | |||
| {% sw_include "@Storefront/storefront/element/cms-element-" ~ element.type ~ ".html.twig" ignore missing %} | |||
| {% endblock %} | |||
| </div> | |||
| {% endblock %} | |||
| {% endblock %} | |||
| @@ -0,0 +1,21 @@ | |||
| {% sw_extends '@Storefront/storefront/component/buy-widget/buy-widget.html.twig' %} | |||
| {% block buy_widget_price %} | |||
| <div class="product-detail-price-container"> | |||
| {% if page.product.translated.customFields.deposittype %} | |||
| <div class="product-detail-deposittype"> | |||
| {{ page.product.translated.customFields.deposittype }} | |||
| </div> | |||
| {% endif %} | |||
| {{ parent() }} | |||
| {% if page.product.translated.customFields.deposit %} | |||
| {% set deposit = page.product.translated.customFields.deposit|currency %} | |||
| <div class="product-detail-deposit"> | |||
| {{ "FlowsiteDepositSystemBasic.plusDeposit"|trans({ | |||
| '%deposit%': deposit | |||
| })|sw_sanitize }} | |||
| </div> | |||
| {% endif %} | |||
| </div> | |||
| {% endblock %} | |||
| @@ -0,0 +1,74 @@ | |||
| {% sw_extends '@Storefront/storefront/component/checkout/offcanvas-cart.html.twig' %} | |||
| {# Start added by spawntree #} | |||
| {% block component_offcanvas_cart_items %} | |||
| {% set customizedProductsCount = 0 %} | |||
| {% set customizedProductsCountTemp = 0 %} | |||
| {% set addCustomizedProducts = 0 %} | |||
| {# copy from SpnoSortBasket #} | |||
| {% set sortBasketConfig = context.extensions.SpnoSortBasket %} | |||
| {% for lineItem in page.cart.lineItems %} | |||
| {% if lineItem.payload is defined and lineItem.payload.customFields is defined and | |||
| lineItem.payload.customFields.spwn_discountable_box is defined %} | |||
| {% set customizedProductsCount = customizedProductsCount + lineItem.quantity %} | |||
| {% endif %} | |||
| {% endfor %} | |||
| <div class="offcanvas-cart-items"> | |||
| {# spawntree start #} | |||
| {% if customizedProductsCount == 1 || customizedProductsCount == 2 %} | |||
| <div class="spwn-customized-products"> | |||
| {% sw_icon 'checkmark-circle' %} | |||
| <div class="spwn-customized-products--text"> | |||
| {% if customizedProductsCount == 1 %} | |||
| <p>Ihr Abnehmplan wurde erfolgreich zum Warenkorb hinzugefügt – ein guter Start!</p> | |||
| <p>Wählen Sie bis zu 2 weitere Pläne für Ihr optimales Abnehmprogramm und sparen Sie ab 150 € Bestellsumme die Versandkosten.</p> | |||
| {% elseif customizedProductsCount == 2 %} | |||
| <p>Ihr zweiter Abnehmplan wurde zum Warenkorb hinzugefügt - Sie sparen die Versandkosten!</p> | |||
| <p>Wählen Sie einen weiteren Plan für Ihr optimales, mehrwöchiges Abnehmprogramm.</p> | |||
| {% endif %} | |||
| </div> | |||
| </div> | |||
| {% endif %} | |||
| {# spawntree end #} | |||
| {% for lineItem in page.cart.lineItems %} | |||
| {# spawntree start #} | |||
| {% if lineItem.payload.customFields.spwn_discountable_box %} | |||
| {% if lineItem.payload is defined and lineItem.payload.customFields is defined and | |||
| lineItem.payload.customFields.spwn_discountable_box is defined and customizedProductsCount < 3 %} | |||
| {% set customizedProductsCountTemp = customizedProductsCountTemp + lineItem.quantity %} | |||
| {% if customizedProductsCountTemp == customizedProductsCount %} | |||
| {% set addCustomizedProducts = customizedProductsCount %} | |||
| {% endif %} | |||
| {% endif %} | |||
| {% block component_offcanvas_cart_item %} | |||
| {% sw_include '@Storefront/storefront/component/checkout/offcanvas-item.html.twig' with { | |||
| data: { | |||
| addCustomizedProducts: addCustomizedProducts | |||
| } | |||
| } %} | |||
| {% endblock %} | |||
| {% endif %} | |||
| {# spawntree end #} | |||
| {% endfor %} | |||
| {% set addCustomizedProducts = 0 %} | |||
| {% for lineItem in page.cart.lineItems %} | |||
| {% if not lineItem.payload.customFields.spwn_discountable_box %} | |||
| {{ block ('component_offcanvas_cart_item') }} | |||
| {% endif %} | |||
| {% endfor %} | |||
| </div> | |||
| {% endblock %} | |||
| {% block component_offcanvas_cart_actions_promotion_submit %} | |||
| <div class="input-group-append"> | |||
| <button class="btn btn-secondary" | |||
| type="submit" | |||
| id="addPromotionOffcanvasCart"> | |||
| {% sw_icon 'arrow-head-right' %} | |||
| </button> | |||
| </div> | |||
| {% endblock %} | |||
| {# End added by spawntree #} | |||
| @@ -0,0 +1,69 @@ | |||
| {% sw_extends '@Storefront/storefront/component/checkout/offcanvas-item.html.twig' %} | |||
| {# Start added by spawntree #} | |||
| {% block component_offcanvas_cart_item_container %} | |||
| {% if isCustomizedProduct %} | |||
| {% set isNested = false %} | |||
| {% for child in lineItem.children %} | |||
| {% if child.type == 'product' %} | |||
| {% set label = child.label %} | |||
| {% set referencedId = child.referencedId %} | |||
| {% set productNumber = lineItem.payload|merge({ 'productNumber': child.payload.productNumber }) %} | |||
| {% if child.cover.url %} | |||
| {% set cover = child.cover %} | |||
| {% endif %} | |||
| {% do lineItem.setLabel(label) %} | |||
| {% do lineItem.setCover(cover) %} | |||
| {% do lineItem.setReferencedId(referencedId) %} | |||
| {% do lineItem.setPayload(productNumber) %} | |||
| {% endif %} | |||
| {% endfor %} | |||
| {% endif %} | |||
| {% set isDiscountableItem = false %} | |||
| {% if lineItem.payload is defined and lineItem.payload.customFields is defined and | |||
| lineItem.payload.customFields.spwn_discountable_box is defined %} | |||
| {% set isDiscountableItem = true %} | |||
| {% endif %} | |||
| <div class="cart-item cart-item-{{ type }}{% if isDiscount %} is-discount{% endif %}{{ cartItemClasses }} js-cart-item{% if isDiscountableItem %} is-discountable-item{% endif %}"> | |||
| <div class="row cart-item-row"> | |||
| {{ block('component_offcanvas_product_image') }} | |||
| {{ block('component_offcanvas_product_details') }} | |||
| {{ block('component_offcanvas_product_remove') }} | |||
| {{ block('component_offcanvas_children') }} | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| {% block component_offcanvas_children %} | |||
| {% if isNested %} | |||
| {# @deprecated tag:v6.5.0 - block will be removed, use `component_offcanvas_item_children` in `@Storefront/storefront/component/checkout/offcanvas-item-children.html.twig` instead #} | |||
| {% block component_offcanvas_cart_item_children %} | |||
| {% sw_include childrenTemplate %} | |||
| {% endblock %} | |||
| {% endif %} | |||
| {% set addCustomizedProducts = addCustomizedProducts | default(0) %} | |||
| {% if lineItem.payload is defined and lineItem.payload.customFields is defined and | |||
| lineItem.payload.customFields.spwn_discountable_box is defined and addCustomizedProducts !== 0 %} | |||
| <div class="spwn-add-customized-products"> | |||
| <a href="/Abnehmprogramme/6-Tage-Abnehmprogramme/" class="spwn-add-customized-products-detail">Fügen Sie einen weiteren Plan hinzu für ihr optimales Abnehmprogramm</a> | |||
| {% if addCustomizedProducts == 1 %} | |||
| <a href="/Abnehmprogramme/6-Tage-Abnehmprogramme/" class="spwn-add-customized-products-detail">Fügen Sie einen weiteren Plan hinzu für ihr optimales Abnehmprogramm</a> | |||
| {% endif %} | |||
| </div> | |||
| {% endif %} | |||
| {% endblock %} | |||
| {# coming from AtlProductConfigurator #} | |||
| {% block cart_item_variant_characteristics %} | |||
| <div class="cart-item-characteristics"> | |||
| {% for input in lineItem.payload.atlProductConfigurator %} | |||
| <div> | |||
| <span class="cart-item-characteristics-option">{{ input.value }}</span> | |||
| </div> | |||
| {% endfor %} | |||
| </div> | |||
| {% endblock %} | |||
| {# End added by spawntree #} | |||
| @@ -0,0 +1,56 @@ | |||
| {% sw_extends '@Storefront/storefront/component/listing/filter/filter-multi-select.html.twig' %} | |||
| {% block component_filter_multi_select %} | |||
| <div class="filter-multi-select filter-multi-select-{{ name }} filter-panel-item{% if not sidebar %} dropdown{% endif %}" | |||
| data-{{ pluginSelector }}="true" | |||
| data-{{ pluginSelector }}-options='{{ dataPluginSelectorOptions|json_encode }}'> | |||
| {% block component_filter_multi_select_toggle %} | |||
| <button style="display:none;" class="filter-panel-item-toggle btn{% if sidebar %} btn-block{% endif %}" | |||
| aria-expanded="false" | |||
| {% if sidebar %} | |||
| data-toggle="collapse" | |||
| data-target="#{{ filterItemId }}" | |||
| {% else %} | |||
| data-toggle="dropdown" | |||
| data-boundary="viewport" | |||
| data-offset="0,8" | |||
| aria-haspopup="true" | |||
| {% endif %}> | |||
| {% block component_filter_multi_select_display_name %} | |||
| {{ displayName }} | |||
| {% endblock %} | |||
| {% block component_filter_multi_select_count %} | |||
| <span class="filter-multi-select-count"></span> | |||
| {% endblock %} | |||
| {% block component_filter_multi_select_toggle_icon %} | |||
| {% sw_icon 'arrow-medium-down' style { | |||
| 'pack': 'solid', 'size': 'xs', 'class': 'filter-panel-item-toggle' | |||
| } %} | |||
| {% endblock %} | |||
| </button> | |||
| {% endblock %} | |||
| {% block component_filter_multi_select_dropdown %} | |||
| <div class="filter-multi-select-dropdown" | |||
| id="{{ filterItemId }}"> | |||
| {% block component_filter_multi_select_list %} | |||
| <ul class="filter-multi-select-list"> | |||
| {% for element in elements %} | |||
| {% block component_filter_multi_select_list_item %} | |||
| <li class="filter-multi-select-list-item"> | |||
| {% block component_filter_multi_select_list_item_inner %} | |||
| {% sw_include '@Storefront/storefront/component/listing/filter/filter-multi-select-list-item.html.twig' %} | |||
| {% endblock %} | |||
| </li> | |||
| {% endblock %} | |||
| {% endfor %} | |||
| </ul> | |||
| {% endblock %} | |||
| </div> | |||
| {% endblock %} | |||
| </div> | |||
| {% endblock %} | |||
| @@ -0,0 +1,170 @@ | |||
| {% sw_extends '@Storefront/storefront/component/product/card/box-standard.html.twig' %} | |||
| {% block component_product_box %} | |||
| {% if product %} | |||
| {% set name = product.translated.name %} | |||
| {% set id = product.id %} | |||
| {% set cover = product.cover.media %} | |||
| {% set variation = product.variation %} | |||
| <div class="card product-box box-{{ layout }}{% if product.markAsTopseller %} highlight{% endif %}"> | |||
| {% block component_product_box_content %} | |||
| <div class="card-body"> | |||
| {% block component_product_box_badges %} | |||
| {% sw_include '@Storefront/storefront/component/product/card/badges.html.twig' %} | |||
| {% endblock %} | |||
| {% block component_product_box_rich_snippets %} | |||
| {% sw_include '@Storefront/storefront/component/product/card/meta.html.twig' %} | |||
| {% endblock %} | |||
| {% block component_product_box_image %} | |||
| {# fallback if display mode is not set #} | |||
| {% set displayMode = displayMode ?: 'standard' %} | |||
| {# set display mode 'cover' for box-image with default display mode #} | |||
| {% if layout == 'image' and displayMode == 'standard' %} | |||
| {% set displayMode = 'cover' %} | |||
| {% endif %} | |||
| <div class="product-image-wrapper is-{{ displayMode }}"> | |||
| <a href="{{ seoUrl('frontend.detail.page', {'productId': id}) }}" | |||
| title="{{ name }}" | |||
| class="product-image-link is-{{ displayMode }}"> | |||
| {% if cover.url %} | |||
| {% set attributes = { | |||
| 'class': 'product-image is-'~displayMode, | |||
| 'alt': (cover.translated.alt ?: name), | |||
| 'title': (cover.translated.title ?: name) | |||
| } %} | |||
| {% if displayMode == 'cover' or displayMode == 'contain' %} | |||
| {% set attributes = attributes|merge({ 'data-object-fit': displayMode }) %} | |||
| {% endif %} | |||
| {# ... enables lazy loading for images #} | |||
| {% if config('zenitPlatformAtmos.config.lazyloading') %} | |||
| {% set attributes = attributes|merge({ 'loading': 'lazy' }) %} | |||
| {% endif %} | |||
| {% sw_thumbnails 'product-image-thumbnails' with { | |||
| media: cover, | |||
| sizes: { | |||
| 'xs': '701px', | |||
| 'sm': '515px', | |||
| 'md': '627px', | |||
| 'lg': '533px', | |||
| 'xl': '484px' | |||
| } | |||
| } %} | |||
| {% else %} | |||
| <div class="product-image-placeholder"> | |||
| {% sw_icon 'placeholder' style { | |||
| 'size': 'fluid' | |||
| } %} | |||
| </div> | |||
| {% endif %} | |||
| {% block zen_component_product_box_image_switch %} | |||
| {% sw_include '@Storefront/storefront/component/product/card/zen-cover-switch.html.twig' %} | |||
| {% endblock %} | |||
| </a> | |||
| {% if config('core.cart.wishlistEnabled') %} | |||
| {% if theme_config('zen-product-listing-card-rating-position') is not same as ('overlay-top-right') or (theme_config('zen-product-listing-card-rating-position') is same as ('overlay-top-right') and theme_config('zen-product-listing-card-actions') is not same as ('overlay')) %} | |||
| {% block component_product_box_wishlist_action %} | |||
| {% sw_include '@Storefront/storefront/component/product/card/wishlist.html.twig' with { | |||
| appearance: 'circle', | |||
| productId: id | |||
| } %} | |||
| {% endblock %} | |||
| {% endif %} | |||
| {% endif %} | |||
| {% block zen_component_product_box_action_overlay_rating %} | |||
| {% if config('core.listing.showReview') and theme_config('zen-product-listing-card-rating-position') is not same as ('default') %} | |||
| <div class="product-rating"> | |||
| {% if product.ratingAverage %} | |||
| {% sw_include '@Storefront/storefront/component/review/rating.html.twig' with { | |||
| points: product.ratingAverage, | |||
| style: 'text-primary' | |||
| } %} | |||
| {% endif %} | |||
| </div> | |||
| {% endif %} | |||
| {% endblock %} | |||
| {% block zen_component_product_box_action_overlay %} | |||
| {% if theme_config('zen-product-listing-card-actions') is same as ('overlay') %} | |||
| {% sw_include '@Storefront/storefront/component/product/card/action.html.twig' %} | |||
| {% endif %} | |||
| {% endblock %} | |||
| </div> | |||
| {% endblock %} | |||
| {% block component_product_box_info %} | |||
| <div class="product-info"> | |||
| {% if product.customFields.custom_productteaser_logo is defined %} | |||
| {% set logoImageId = product.customFields.custom_productteaser_logo %} | |||
| {% set mediaCollection = searchMedia([logoImageId], context.context) %} | |||
| {% set logoImage = mediaCollection.get(logoImageId) %} | |||
| <figure class="brandLogo"> | |||
| <img src="{{ logoImage.url }}"> | |||
| </figure> | |||
| {% endif %} | |||
| {% block component_product_box_rating %} | |||
| {% if theme_config('zen-product-listing-card-rating-position') is same as ('default') %} | |||
| {{ parent() }} | |||
| {% endif %} | |||
| {% endblock %} | |||
| {% block component_product_box_name %} | |||
| <a href="{{ seoUrl('frontend.detail.page', {'productId': id}) }}" | |||
| class="product-name" | |||
| title="{{ name }}"> | |||
| {{ name }} | |||
| </a> | |||
| {% endblock %} | |||
| {% block component_product_box_variant_characteristics %} | |||
| {% if theme_config('zen-product-listing-card-variant-characteristics') %} | |||
| {{ parent() }} | |||
| {% endif %} | |||
| {% endblock %} | |||
| {% block component_product_box_description %} | |||
| <div class="product-description"> | |||
| {{ product.customFields.custom_productteaser_category|striptags|raw }} | |||
| </div> | |||
| {% endblock %} | |||
| {% block component_product_box_price %} | |||
| {% sw_include '@Storefront/storefront/component/product/card/price-unit.html.twig' %} | |||
| {% endblock %} | |||
| {% block component_product_box_action %} | |||
| {% if theme_config('zen-product-listing-card-actions') is not same as ('overlay') %} | |||
| {{ parent() }} | |||
| {% endif %} | |||
| {% if config('core.cart.wishlistEnabled') %} | |||
| {% if theme_config('zen-product-listing-card-rating-position') is same as ('overlay-top-right') and theme_config('zen-product-listing-card-actions') is same as ('overlay') %} | |||
| {% block zen_component_product_box_wishlist_action %} | |||
| {% sw_include '@Storefront/storefront/component/product/card/wishlist.html.twig' with { | |||
| appearance: 'circle', | |||
| productId: id | |||
| } %} | |||
| {% endblock %} | |||
| {% endif %} | |||
| {% endif %} | |||
| {% endblock %} | |||
| </div> | |||
| {% endblock %} | |||
| </div> | |||
| {% endblock %} | |||
| </div> | |||
| {% endif %} | |||
| {% endblock %} | |||
| @@ -0,0 +1,55 @@ | |||
| {% sw_extends '@Storefront/storefront/component/product/card/price-unit.html.twig' %} | |||
| {% block component_product_box_price %} | |||
| <div class="product-price-wrapper"> | |||
| {% set price = real %} | |||
| <div class="product-cheapest-price"> | |||
| {% if cheapest.unitPrice != real.unitPrice %} | |||
| <div>{{ "listing.cheapestPriceLabel"|trans|sw_sanitize }}<span class="product-cheapest-price-price"> {{ cheapest.unitPrice|currency }}{{ "general.star"|trans|sw_sanitize }}</span></div> | |||
| {% endif %} | |||
| </div> | |||
| {% if displayFrom %} | |||
| {{ "listing.listingTextFrom"|trans|sw_sanitize }} | |||
| {% endif %} | |||
| {% set isListPrice = price.listPrice.percentage > 0 %} | |||
| <span class="product-price{% if isListPrice and not displayFrom %} with-list-price{% endif %}"> | |||
| {{ price.unitPrice|currency }}{{ "general.star"|trans|sw_sanitize }} | |||
| {% if isListPrice and not displayFrom %} | |||
| {% set afterListPriceSnippetExists = "listing.afterListPrice"|trans|length > 0 %} | |||
| {% set beforeListPriceSnippetExists = "listing.beforeListPrice"|trans|length > 0 %} | |||
| {% set hideStrikeTrough = beforeListPriceSnippetExists or afterListPriceSnippetExists %} | |||
| <span class="list-price{% if hideStrikeTrough %} list-price-no-line-through{% endif %}"> | |||
| {% if beforeListPriceSnippetExists %}{{ "listing.beforeListPrice"|trans|trim|sw_sanitize }}{% endif %} | |||
| <span class="list-price-price">{{ price.listPrice.price|currency }}{{ "general.star"|trans|sw_sanitize }}</span> | |||
| {% if afterListPriceSnippetExists %}{{ "listing.afterListPrice"|trans|trim|sw_sanitize }}{% endif %} | |||
| <span class="list-price-percentage">{{ "detail.listPricePercentage"|trans({'%price%': price.listPrice.percentage })|sw_sanitize }}</span> | |||
| </span> | |||
| {% endif %} | |||
| </span> | |||
| <span>{{ product.customFields.custom_productteaser_price|trans }} € pro Tag</span> | |||
| </div> | |||
| {% if product.translated.customFields.deposittype %} | |||
| <div class="product-deposittype"> | |||
| {{ product.translated.customFields.deposittype }} | |||
| </div> | |||
| {% endif %} | |||
| {% if product.translated.customFields.deposit %} | |||
| {% set deposit = product.translated.customFields.deposit|currency %} | |||
| <div class="product-deposit"> | |||
| <small> | |||
| {{ "FlowsiteDepositSystemBasic.plusDeposit"|trans({ | |||
| '%deposit%': deposit | |||
| })|sw_sanitize }} | |||
| </small> | |||
| </div> | |||
| {% endif %} | |||
| {% endblock %} | |||
| @@ -0,0 +1,41 @@ | |||
| {% sw_extends '@Storefront/storefront/component/product/listing.html.twig' %} | |||
| {% block element_product_listing_row %} | |||
| <div class="row cms-listing-row js-listing-wrapper"> | |||
| {% if page.footer.navigation.active.customFields.custom_listing_textbox_text is defined %} | |||
| <div class="cms-listing-col col-xl-6 col-lg-8 col-12"> | |||
| <div class="card textbox d-flex justify-content-center"> | |||
| <div class="inner"> | |||
| {{ page.footer.navigation.active.customFields.custom_listing_textbox_text|trans|sw_sanitize }} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {% endif %} | |||
| {% if searchResult.total > 0 %} | |||
| {% block element_product_listing_col %} | |||
| {% for product in searchResult %} | |||
| <div class="cms-listing-col {{ listingColumns }}"> | |||
| {% block element_product_listing_box %} | |||
| {% sw_include '@Storefront/storefront/component/product/card/box.html.twig' with { | |||
| 'layout': boxLayout, | |||
| 'displayMode': displayMode | |||
| } %} | |||
| {% endblock %} | |||
| </div> | |||
| {% endfor %} | |||
| {% endblock %} | |||
| {% else %} | |||
| {% block element_product_listing_col_empty %} | |||
| <div class="cms-listing-col col-12"> | |||
| {% block element_product_listing_col_empty_alert %} | |||
| {% sw_include '@Storefront/storefront/utilities/alert.html.twig' with { | |||
| type: 'info', | |||
| content: 'listing.emptyResultMessage'|trans|sw_sanitize | |||
| } %} | |||
| {% endblock %} | |||
| </div> | |||
| {% endblock %} | |||
| {% endif %} | |||
| </div> | |||
| {% endblock %} | |||
| @@ -0,0 +1,11 @@ | |||
| {% sw_extends '@Storefront/storefront/layout/footer/footer.html.twig' %} | |||
| {% block layout_footer_payment_shipping_logos %} | |||
| {% endblock %} | |||
| {% block layout_footer_copyright %} | |||
| <div class="footer-copyright"> | |||
| {{ "footer.copyrightInfo"|trans|sw_sanitize }} | |||
| </div> | |||
| {% endblock %} | |||
| @@ -0,0 +1,141 @@ | |||
| {% sw_extends '@Storefront/storefront/layout/header/header.html.twig' %} | |||
| {% block layout_header_actions %} | |||
| <div class="header-actions-col {{ actionClasses }}"> | |||
| <div class="row no-gutters"> | |||
| {% block layout_header_search_toggle %} | |||
| <div class="col-auto{% if theme_config('zen-search-style') is same as('default') %} d-sm-none"{% endif %}"> | |||
| <div class="search-toggle"> | |||
| <button class="btn header-actions-btn search-toggle-btn js-search-toggle-btn collapsed" | |||
| type="button" | |||
| {% if theme_config('zen-search-style') is same as('overlay') %} | |||
| data-toggle="overlay" | |||
| {% else %} | |||
| data-toggle="collapse" | |||
| data-target="#searchCollapse" | |||
| aria-expanded="false" | |||
| aria-controls="searchCollapse" | |||
| {% endif %} | |||
| aria-label="{{ "header.searchButton"|trans|striptags }}"> | |||
| {% sw_icon 'search' %} | |||
| {% sw_icon 'x' style { 'class': 'search-close d-none' } %} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| {% block layout_header_hotline %} | |||
| <div class="col-auto"> | |||
| <div class="phone-icon"> | |||
| <a href="{{ 'header.hotline'|trans|striptags }}" title="Hotline"> | |||
| <svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | |||
| <g id="Startseite" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> | |||
| <g id="D-DIAEKO-Startseite-z1-v7" transform="translate(-1093.000000, -16.000000)"> | |||
| <rect fill="#FFFFFF" x="0" y="0" width="1400" height="9017"></rect> | |||
| <g id="Elements-/-Header-DIÄKO-Desktop"> | |||
| <rect id="bg" fill="#FFFFFF" x="0" y="0" width="1400" height="120"></rect> | |||
| <g id="meta" transform="translate(1047.000000, 16.000000)" stroke="#222222" stroke-width="2"> | |||
| <g id="hilfe" transform="translate(46.000000, 0.000000)"> | |||
| <g id="icon" transform="translate(9.000000, 10.000000)"> | |||
| <path d="M12.791855,13.7844975 C13.2461579,13.0667754 13.6925461,12.3617189 14.1389343,11.6563986 C14.4389008,11.182227 14.5903351,11.1376332 15.0821008,11.382767 C16.9045888,12.291266 18.7270767,13.2000289 20.5493009,14.1090557 C21.0268201,14.3473289 21.0948864,14.5307174 20.8935896,15.0328591 C20.2896992,16.5400756 19.6860726,18.0475559 19.0808631,19.5542447 C18.91571,19.9653517 18.7766754,20.0505812 18.3498101,19.9753787 C15.0504421,19.3927572 12.0262413,18.1502007 9.34237184,16.1355653 C6.98063007,14.3626332 5.02570307,12.2144803 3.54117213,9.64888753 C2.27693324,7.46352916 1.46646958,5.11694717 1.02351109,2.63790323 C0.950168346,2.22706008 1.03828516,2.08430725 1.43217261,1.92598619 C2.96498312,1.3098534 4.49805745,0.694776078 6.03218708,0.0815458362 C6.44691653,-0.0841635406 6.66272359,-0.00473914189 6.85900775,0.388160957 C7.79452343,2.2603075 8.72819234,4.13298178 9.66001449,6.0069754 C9.84653722,6.38219631 9.78770473,6.57904216 9.42785042,6.80807997 C8.69943565,7.27143294 7.96917413,7.73161949 7.21358561,8.20948522 C8.66540251,10.4808647 10.5082048,12.3377069 12.791855,13.7844975 Z" id="Stroke-1"></path> | |||
| </g> | |||
| </g> | |||
| </g> | |||
| </g> | |||
| </g> | |||
| </g> | |||
| </svg> | |||
| </a> | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| {% if config('core.cart.wishlistEnabled') %} | |||
| {% block layout_header_actions_wishlist %} | |||
| <div class="col-auto"> | |||
| <div class="header-wishlist"> | |||
| <a class="btn header-wishlist-btn header-actions-btn" | |||
| href="{{ path('frontend.wishlist.page') }}" | |||
| title="{{ 'header.wishlist'|trans|striptags }}" | |||
| aria-label="{{ 'header.wishlist'|trans|striptags }}"> | |||
| {% sw_include '@Storefront/storefront/layout/header/actions/wishlist-widget.html.twig' %} | |||
| </a> | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| {% endif %} | |||
| {% block layout_header_actions_account %} | |||
| <div class="col-auto"> | |||
| <div class="account-menu"> | |||
| {% sw_include '@Storefront/storefront/layout/header/actions/account-widget.html.twig' %} | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| {% block layout_header_actions_cart %} | |||
| <div class="col-auto"> | |||
| <div class="header-cart" | |||
| data-offcanvas-cart="true"> | |||
| <a class="btn header-cart-btn header-actions-btn" | |||
| href="{{ path('frontend.checkout.cart.page') }}" | |||
| data-cart-widget="true" | |||
| title="{{ 'checkout.cartTitle'|trans|striptags }}" | |||
| aria-label="{{ 'checkout.cartTitle'|trans|striptags }}"> | |||
| {% sw_include '@Storefront/storefront/layout/header/actions/cart-widget.html.twig' %} | |||
| </a> | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| {% block zen_layout_header_top_bar_toggle %} | |||
| {# marketingBannerId is needed for storagekey invalidation #} | |||
| {% set marketingBanner = [] %} | |||
| {% set marketingBanner = [config('zenitPlatformAtmos.config.marketingActive')]|merge(marketingBanner) %} | |||
| {% set marketingBanner = [config('zenitPlatformAtmos.config.marketingText')]|merge(marketingBanner) %} | |||
| {% set marketingBanner = [config('zenitPlatformAtmos.config.marketingButtonText')]|merge(marketingBanner) %} | |||
| {% set marketingBanner = [config('zenitPlatformAtmos.config.marketingButtonLink')]|merge(marketingBanner) %} | |||
| {% set marketingBanner = [config('zenitPlatformAtmos.config.marketingButtonTarget')]|merge(marketingBanner) %} | |||
| {% set marketingBannerId = marketingBanner|json_encode()|length %} | |||
| {% set zenCollapseTopBarOptions = { | |||
| id: topBarStyle ~ '-' ~ marketingBannerId, | |||
| type: topBarStyle | |||
| } %} | |||
| {% if topBarStyle is not same as ('default') and topBarStyle is not same as ('hidden') %} | |||
| {% if theme_config('zen-header-style') is not same as ('two-line') or (theme_config('zen-header-style') is same as ('two-line') and config('zenitPlatformAtmos.config.marketingActive')) %} | |||
| <div class="col-auto d-none d-lg-block"> | |||
| <div class="top-bar-toggle"> | |||
| <button class="btn header-actions-btn top-bar-toggle-btn" | |||
| type="button" | |||
| aria-label="{{ "zentheme.general.moreLink"|trans|striptags }}" | |||
| {% if topBarStyle is same as ('offcanvas') %} | |||
| data-offcanvas-top-bar="true" | |||
| {% endif %} | |||
| {% if topBarStyle is same as ('collapsible') %} | |||
| data-toggle="collapse" | |||
| data-target="#topBarCollapse" | |||
| aria-expanded="true" | |||
| data-zen-collapse-top-bar-options="{{ zenCollapseTopBarOptions|json_encode }}" | |||
| {% endif %} | |||
| {% if topBarStyle is same as ('expandable') %} | |||
| data-toggle="collapse" | |||
| data-target="#topBarCollapse" | |||
| aria-expanded="false" | |||
| data-zen-collapse-top-bar-options="{{ zenCollapseTopBarOptions|json_encode }}" | |||
| {% endif %}> | |||
| {% if theme_config('zen-main-navigation-style') is same as ('offcanvas-lg') or theme_config('zen-main-navigation-style') is same as ('offcanvas-xl') %} | |||
| {% sw_icon 'more-vertical' %} | |||
| {% else %} | |||
| {% sw_icon 'stack' %} | |||
| {% endif %} | |||
| </button> | |||
| </div> | |||
| </div> | |||
| {% endif %} | |||
| {% endif %} | |||
| {% endblock %} | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| @@ -0,0 +1,63 @@ | |||
| {% sw_extends '@Storefront/storefront/page/checkout/cart/index.html.twig' %} | |||
| {% block page_checkout_cart_add_promotion_submit %} | |||
| <div class="input-group-append"> | |||
| <button class="btn btn-secondary" | |||
| type="submit" | |||
| id="addPromotion"> | |||
| {% sw_icon 'arrow-head-right' %} | |||
| </button> | |||
| </div> | |||
| {% endblock %} | |||
| {% block page_checkout_cart_product_table %} | |||
| {% set customizedProductsCount = 0 %} | |||
| {% set customizedProductsCountTemp = 0 %} | |||
| {% set addCustomizedProducts = 0 %} | |||
| {% for lineItem in page.cart.lineItems %} | |||
| {% if lineItem.payload is defined and lineItem.payload.customFields is defined and | |||
| lineItem.payload.customFields.spwn_discountable_box is defined %} | |||
| {% set customizedProductsCount = customizedProductsCount + lineItem.quantity %} | |||
| {% endif %} | |||
| {% endfor %} | |||
| <div class="card checkout-product-table"> | |||
| <div class="card-body"> | |||
| {% block page_checkout_cart_table_header %} | |||
| {% sw_include '@Storefront/storefront/page/checkout/cart/cart-product-header.html.twig' %} | |||
| {% endblock %} | |||
| {% block page_checkout_cart_table_items %} | |||
| {% for lineItem in page.cart.lineItems %} | |||
| {% if lineItem.payload.customFields.spwn_discountable_box %} | |||
| {% if lineItem.payload is defined and lineItem.payload.customFields is defined and | |||
| lineItem.payload.customFields.spwn_discountable_box is defined and customizedProductsCount < 3 %} | |||
| {% set customizedProductsCountTemp = customizedProductsCountTemp + lineItem.quantity %} | |||
| {% if customizedProductsCountTemp == customizedProductsCount %} | |||
| {% set addCustomizedProducts = customizedProductsCount %} | |||
| {% endif %} | |||
| {% endif %} | |||
| {% block page_checkout_cart_table_item %} | |||
| {% block page_checkout_item %} | |||
| {% sw_include '@Storefront/storefront/page/checkout/checkout-item.html.twig' with { | |||
| data: { | |||
| addCustomizedProducts: addCustomizedProducts | |||
| } | |||
| } %} | |||
| {% endblock %} | |||
| {% endblock %} | |||
| {% endif %} | |||
| {% endfor %} | |||
| {% set addCustomizedProducts = 0 %} | |||
| {% for lineItem in page.cart.lineItems %} | |||
| {% if not lineItem.payload.customFields.spwn_discountable_box %} | |||
| {{ block ('page_checkout_item') }} | |||
| {% endif %} | |||
| {% endfor %} | |||
| {% endblock %} | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| @@ -0,0 +1,50 @@ | |||
| {% sw_extends '@Storefront/storefront/page/checkout/checkout-item.html.twig' %} | |||
| {% block page_checkout_item_info_variant_characteristics %} | |||
| <div class="cart-item-details-characteristics"> | |||
| {% for input in lineItem.payload.atlProductConfigurator %} | |||
| <div> | |||
| <span class="cart-item-details-characteristics-option">{{ input.value }}</span> | |||
| </div> | |||
| {% endfor %} | |||
| </div> | |||
| {% endblock %} | |||
| {% block page_checkout_item_info_label %} | |||
| {% if lineItem.type == PRODUCT_LINE_ITEM_TYPE %} | |||
| <a href="{{ seoUrl('frontend.detail.page', {'productId': lineItem.referencedId}) }}" | |||
| class="cart-item-label" | |||
| title="{{ label }}" | |||
| {% if controllerAction is same as('confirmPage') %} | |||
| data-toggle="modal" | |||
| data-modal-class="quickview-modal" | |||
| data-url="{{ path('widgets.quickview.minimal',{ 'productId': lineItem.referencedId }) }}" | |||
| {% endif %} | |||
| > | |||
| {{ label|u.truncate(60, '...', false) }} | |||
| </a> | |||
| {% else %} | |||
| <div class="cart-item-label"> | |||
| {{ label|u.truncate(60, '...', false) }} | |||
| </div> | |||
| {% endif %} | |||
| {% endblock %} | |||
| {% block page_checkout_item_children_template %} | |||
| {% if isNested %} | |||
| {# @deprecated tag:v6.5.0 - block will be moved to `@Storefront/storefront/page/checkout/checkout-item-children.html.twig` #} | |||
| {% block page_checkout_item_children %} | |||
| {% sw_include childrenTemplate %} | |||
| {% endblock %} | |||
| {% endif %} | |||
| {% set addCustomizedProducts = addCustomizedProducts | default(0) %} | |||
| {% if lineItem.payload is defined and lineItem.payload.customFields is defined and | |||
| lineItem.payload.customFields.spwn_discountable_box is defined and addCustomizedProducts !== 0 %} | |||
| <div class="spwn-add-customized-products"> | |||
| <a href="/Abnehmprogramme/6-Tage-Abnehmprogramme/" class="spwn-add-customized-products-detail"><span>Fügen Sie einen weiteren Plan hinzu für ihr optimales Abnehmprogramm</span></a> | |||
| {% if addCustomizedProducts == 1 %} | |||
| <a href="/Abnehmprogramme/6-Tage-Abnehmprogramme/" class="spwn-add-customized-products-detail"><span>Fügen Sie einen weiteren Plan hinzu für ihr optimales Abnehmprogramm</span></a> | |||
| {% endif %} | |||
| </div> | |||
| {% endif %} | |||
| {% endblock %} | |||
| @@ -0,0 +1,17 @@ | |||
| {% sw_extends '@Storefront/storefront/page/checkout/confirm/confirm-address.html.twig' %} | |||
| {% block page_checkout_confirm_address_shipping_data %} | |||
| <div class="confirm-address-shipping" data-iso="{{ shippingAddress.country.iso }}"> | |||
| {% if billingAddress.id is same as(shippingAddress.id) %} | |||
| {% block page_checkout_confirm_address_shipping_data_equal %} | |||
| <p> | |||
| {{ "checkout.addressEqualText"|trans|sw_sanitize }} | |||
| </p> | |||
| {% endblock %} | |||
| {% else %} | |||
| {% sw_include '@Storefront/storefront/component/address/address.html.twig' with { | |||
| 'address': shippingAddress | |||
| } %} | |||
| {% endif %} | |||
| </div> | |||
| {% endblock %} | |||
| @@ -0,0 +1,112 @@ | |||
| {% sw_extends '@Storefront/storefront/page/checkout/confirm/index.html.twig' %} | |||
| {% block page_checkout_confirm_tos %} | |||
| {% endblock %} | |||
| {% block page_checkout_confirm_product_table %} | |||
| {% endblock %} | |||
| {% block page_checkout_additional %} | |||
| {% if config('core.cart.showCustomerComment') %} | |||
| <div class="checkout-additional"> | |||
| <div class="confirm-product"> | |||
| {% block page_checkout_confirm_table_container %} | |||
| <div class="card"> | |||
| <div class="card-body"> | |||
| {% block page_checkout_confirm_table_header %} | |||
| {% sw_include '@Storefront/storefront/page/checkout/confirm/confirm-product-header.html.twig' %} | |||
| {% endblock %} | |||
| {% block page_checkout_confirm_table_items %} | |||
| {% for lineItem in page.cart.lineItems %} | |||
| {% block page_checkout_confirm_table_item %} | |||
| {% sw_include '@Storefront/storefront/page/checkout/confirm/confirm-item.html.twig' %} | |||
| {% endblock %} | |||
| {% endfor %} | |||
| {% endblock %} | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| </div> | |||
| {% block page_checkout_finish_customer_comment %} | |||
| <div class="card checkout-card"> | |||
| <div class="card-body"> | |||
| {% block page_checkout_confirm_customer_comment_header %} | |||
| <div class="card-title"> | |||
| {{ "checkout.customerCommentHeader"|trans|sw_sanitize }} | |||
| </div> | |||
| {% endblock %} | |||
| {% block page_checkout_confirm_customer_comment_control %} | |||
| <div class="checkout-customer-comment-control"> | |||
| {% block page_checkout_confirm_customer_comment_control_textfield_label %} | |||
| <label class="form-label" for="{{ constant('Shopware\\Core\\Checkout\\Order\\SalesChannel\\OrderService::CUSTOMER_COMMENT_KEY') }}"> | |||
| {{ "checkout.customerCommentLabel"|trans|sw_sanitize }} | |||
| </label> | |||
| {% endblock %} | |||
| {% block page_checkout_confirm_customer_comment_control_textfield %} | |||
| <textarea class="form-control" | |||
| placeholder="{{ "checkout.customerCommentPlaceholder"|trans|sw_sanitize }}" | |||
| id="{{ constant('Shopware\\Core\\Checkout\\Order\\SalesChannel\\OrderService::CUSTOMER_COMMENT_KEY') }}" | |||
| form="confirmOrderForm" | |||
| name="{{ constant('Shopware\\Core\\Checkout\\Order\\SalesChannel\\OrderService::CUSTOMER_COMMENT_KEY') }}"></textarea> | |||
| {% endblock %} | |||
| </div> | |||
| {% endblock %} | |||
| </div> | |||
| </div> | |||
| {% endblock %} | |||
| </div> | |||
| {% endif %} | |||
| {% endblock %} | |||
| {% block page_checkout_aside_actions %} | |||
| <div class="checkout-aside-action"> | |||
| {% block page_checkout_confirm_tos_control %} | |||
| <div class="custom-control custom-checkbox"> | |||
| {% block page_checkout_confirm_tos_control_checkbox %} | |||
| <input type="checkbox" | |||
| class="checkout-confirm-tos-checkbox custom-control-input{% if formViolations.getViolations('/tos') is not empty %} is-invalid{% endif %}" | |||
| required="required" | |||
| id="tos" | |||
| form="confirmOrderForm" | |||
| name="tos"/> | |||
| {% endblock %} | |||
| {% block page_checkout_confirm_tos_control_label %} | |||
| <label for="tos" | |||
| class="checkout-confirm-tos-label custom-control-label"> | |||
| {{ "checkout.confirmTerms"|trans({ | |||
| '%url%': path('frontend.cms.page',{ id: config('core.basicInformation.tosPage') }) | |||
| })|raw }} | |||
| </label> | |||
| {% endblock %} | |||
| </div> | |||
| {% endblock %} | |||
| <form id="confirmOrderForm" | |||
| action="{{ path('frontend.checkout.finish.order') }}" | |||
| data-form-csrf-handler="true" | |||
| data-form-preserver="true" | |||
| data-form-submit-loader="true" | |||
| method="post"> | |||
| {% block page_checkout_aside_actions_csrf %} | |||
| {{ sw_csrf('frontend.checkout.finish.order') }} | |||
| {% endblock %} | |||
| {% block page_checkout_confirm_form_submit %} | |||
| <button id="confirmFormSubmit" | |||
| class="btn btn-primary btn-block btn-lg" | |||
| form="confirmOrderForm" | |||
| {% if page.cart.errors.blockOrder %} | |||
| disabled | |||
| {% endif %} | |||
| type="submit"> | |||
| {{ "checkout.confirmSubmit"|trans|sw_sanitize }} | |||
| </button> | |||
| {% endblock %} | |||
| </form> | |||
| </div> | |||
| {% endblock %} | |||
| @@ -0,0 +1,25 @@ | |||
| {% sw_extends '@Storefront/storefront/page/checkout/summary/summary-shipping.html.twig' %} | |||
| {% block page_checkout_summary_shippings %} | |||
| {% for delivery in summary.deliveries %} | |||
| {% block page_checkout_summary_shipping %} | |||
| {% block page_checkout_summary_shipping_label %} | |||
| <dt class="col-7 checkout-aside-summary-label"> | |||
| <a class="product-detail-tax-link" | |||
| href="{{ path('frontend.cms.page',{ id: config('core.basicInformation.shippingPaymentInfoPage') }) }}" | |||
| title="{{ "checkout.summaryShipping"|trans|sw_sanitize }}" | |||
| data-toggle="modal" | |||
| data-url="{{ path('frontend.cms.page',{ id: config('core.basicInformation.shippingPaymentInfoPage') }) }}"> | |||
| {{ "checkout.summaryShipping"|trans|sw_sanitize }} | |||
| </a> | |||
| </dt> | |||
| {% endblock %} | |||
| {% block page_checkout_summary_shipping_value %} | |||
| <dd class="col-5 checkout-aside-summary-value"> | |||
| {{ delivery.shippingCosts.totalPrice|currency }}{{ "general.star"|trans|sw_sanitize }} | |||
| </dd> | |||
| {% endblock %} | |||
| {% endblock %} | |||
| {% endfor %} | |||
| {% endblock %} | |||
| @@ -0,0 +1,16 @@ | |||
| {% sw_extends '@Storefront/storefront/page/product-detail/description.html.twig' %} | |||
| {% block page_product_detail_description_content_text %} | |||
| {{ parent() }} | |||
| {% if product.customFields.custom_weightlossplan_day1_image is defined %} | |||
| {{ product.customFields.custom_weightlossplan_day1_image }} | |||
| {% endif %} | |||
| {% if product.customFields.custom_weightlossplan_day1_notice is defined %} | |||
| {{ product.customFields.custom_weightlossplan_day1_notice|trans }} | |||
| {% endif %} | |||
| {% if product.customFields.custom_weightlossplan_day1_shake is defined %} | |||
| {{ product.customFields.custom_weightlossplan_day1_shake|trans }} | |||
| {% endif %} | |||
| {% endblock %} | |||
| @@ -0,0 +1,18 @@ | |||
| {% sw_extends '@Storefront/storefront/page/product-detail/index.html.twig' %} | |||
| {% block page_product_detail_content %} | |||
| {{ parent() }} | |||
| {% if page.product.customFields.custom_weightlossplan_day1_image is defined %} | |||
| {% set day1ImageId = page.product.customFields.custom_weightlossplan_day1_image %} | |||
| {% set mediaCollection = searchMedia([day1ImageId], context.context) %} | |||
| {% set day1Image = mediaCollection.get(day1ImageId) %} | |||
| <div class="day1Image"> | |||
| <img src="{{ day1Image.url }}"> | |||
| </div> | |||
| {% endif %} | |||
| {% if page.product.customFields.custom_weightlossplan_day1_shake is defined %} | |||
| Shake: {{ page.product.customFields.custom_weightlossplan_day1_shake|trans }} | |||
| {% endif %} | |||
| {% endblock %} | |||