added tiles

This commit is contained in:
Tim Lappe 2025-05-27 18:58:31 +02:00
parent a331d1a8da
commit 593972c401
52 changed files with 851 additions and 97 deletions

View File

@ -14,7 +14,6 @@ RUN apk add --no-cache \
autoconf \ autoconf \
g++ \ g++ \
make \ make \
zsh \
wget wget
# Install PHP extensions # Install PHP extensions
@ -27,11 +26,5 @@ RUN pecl install mongodb \
# Install Composer # Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# Install Oh My Zsh
RUN sh -c "$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
# Set zsh as default shell
RUN chsh -s $(which zsh)
# CMD will be executed when container starts # CMD will be executed when container starts
CMD ["php", "-S", "0.0.0.0:3000", "-t", "public"] CMD ["php", "-S", "0.0.0.0:3000", "-t", "public"]

58
docs/SubSectionTile.md Normal file
View File

@ -0,0 +1,58 @@
# SubSectionTile
The `SubSectionTile` allows you to group similar tiles together within a section, providing better organization and visual hierarchy.
## Usage
```php
use App\Domain\Search\Tiles\SubSectionTile;
// Create a sub-section with grouped tiles
$performanceSubSection = new SubSectionTile(
'Performance', // Title
[ // Array of tiles
new PowerTile(new Power(210)),
new AccelerationTile(new Acceleration(6.6)),
new TopSpeedTile(new TopSpeed(180)),
new DrivetrainTile(new Drivetrain('rear')),
],
'Motor and driving performance specifications' // Optional description
);
```
## Parameters
- **`title`** (string): The title of the sub-section
- **`tiles`** (array): Array of tile objects to group together
- **`description`** (string, optional): Optional description text displayed below the title
## Example
```php
new SectionTile('Skoda Elroq 85', [
new BrandTile('Skoda'),
new PriceTile(new Price(43900, Currency::euro())),
new SubSectionTile('Performance', [
new PowerTile(new Power(210)),
new AccelerationTile(new Acceleration(6.6)),
new TopSpeedTile(new TopSpeed(180)),
new DrivetrainTile(new Drivetrain('rear')),
], 'Motor and driving performance specifications'),
new SubSectionTile('Range & Efficiency', [
new RangeTile(new Range(450)),
new BatteryTile(new Battery(77.0, 82.0)),
new ConsumptionTile(new Consumption(171)),
new ChargingTile(new ChargingSpeed(175, 11)),
], 'Battery capacity, range, and charging capabilities'),
]);
```
## Visual Hierarchy
- **Section**: Large heading (h1) with prominent styling
- **Sub-section**: Medium heading (h2) with subtle styling
- **Tiles**: Individual data tiles within each sub-section
The sub-section provides a clear visual separation between different categories of information while maintaining the overall design consistency.

View File

@ -19,6 +19,6 @@ class TileTwigName extends AbstractExtension
public function twigName(object $tile): string public function twigName(object $tile): string
{ {
return (new \ReflectionClass($tile))->getShortName(); return str_replace('tile', '', strtolower((new \ReflectionClass($tile))->getShortName()));
} }
} }

View File

@ -2,16 +2,16 @@
namespace App\Domain\Model; namespace App\Domain\Model;
class Brand final readonly class Brand
{ {
public function __construct( public function __construct(
public private(set) readonly string $id, public readonly string $id,
public private(set) readonly string $name, public readonly string $name,
public private(set) readonly string $logo, public readonly string $logo,
public private(set) readonly string $description, public readonly string $description,
public private(set) readonly int $foundedYear, public readonly int $foundedYear,
public private(set) readonly string $headquarters, public readonly string $headquarters,
public private(set) readonly string $website, public readonly string $website,
public private(set) readonly array $carModels, public readonly array $carModels,
) {} ) {}
} }

View File

@ -2,33 +2,29 @@
namespace App\Domain\Model; namespace App\Domain\Model;
class CarRevision use App\Domain\Model\Value\Acceleration;
use App\Domain\Model\Value\Battery;
use App\Domain\Model\Value\Consumption;
use App\Domain\Model\Value\ChargingSpeed;
use App\Domain\Model\Value\Power;
use App\Domain\Model\Value\Range;
use App\Domain\Model\Value\TopSpeed;
use App\Domain\Model\Value\Price;
final readonly class CarRevision
{ {
private ?string $id = null; public function __construct(
public string $name,
private string $name; public int $releaseYear,
public ?Power $power = null,
private int $releaseYear; public ?Acceleration $acceleration = null,
public ?TopSpeed $topSpeed = null,
private array $engineTypes = []; public ?Range $range = null,
public ?Battery $battery = null,
private ?int $horsePower = null; public ?Consumption $consumption = null,
public ?Price $price = null,
private ?int $torque = null; public ?ChargingSpeed $chargingSpeed = null,
public ?CarModel $carModel = null,
private ?int $topSpeed = null; public ?Image $image = null,
) {}
private ?float $accelerationZeroToHundred = null;
private ?int $range = null;
private ?float $batteryCapacity = null;
private ?int $chargingTime = null;
private ?float $consumption = null;
private ?float $price = null;
private ?CarModel $carModel = null;
} }

View File

@ -0,0 +1,12 @@
<?php
namespace App\Domain\Model;
final readonly class Image
{
public function __construct(
public ?string $externalPublicUrl = null,
public ?string $relativePublicUrl = null,
) {
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Domain\Model\Value;
class Acceleration
{
public function __construct(
public readonly float $secondsFrom0To100,
) {
}
public function __toString(): string
{
return $this->secondsFrom0To100 . ' sec (0-100 km/h)';
}
public function getSeconds(): float
{
return $this->secondsFrom0To100;
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace App\Domain\Model\Value;
class Battery
{
public function __construct(
public readonly Energy $usableCapacity,
public readonly Energy $totalCapacity,
public readonly string $technology = 'Lithium-Ion',
) {
}
public function __toString(): string
{
return $this->usableCapacity->__toString();
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Domain\Model\Value;
class ChargingSpeed
{
public function __construct(
public readonly Power $dcMaxKw,
public readonly Power $acMaxKw,
) {
}
public function __toString(): string
{
return $this->dcMax() . ' DC / ' . $this->acMax() . ' AC';
}
public function dcMax(): Power
{
return $this->dcMaxKw;
}
public function acMax(): Power
{
return $this->acMaxKw;
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Domain\Model\Value;
class Consumption
{
public function __construct(
public readonly Energy $energyPerKm,
) {
}
public function __toString(): string
{
return $this->energyPerKm->kwh() . ' kWh/100km';
}
public function energyPerKm(): Energy
{
return $this->energyPerKm;
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace App\Domain\Model\Value;
class Currency
{
public function __construct(
public readonly string $symbol = '€',
public readonly string $currency = 'EUR',
public readonly string $name = 'Euro',
) {}
public function __toString(): string
{
return $this->symbol;
}
public function symbol(): string
{
return $this->symbol;
}
public function currency(): string
{
return $this->currency;
}
public function name(): string
{
return $this->name;
}
public static function euro(): self
{
return new self('€', 'EUR', 'Euro');
}
public static function usd(): self
{
return new self('$', 'USD', 'US Dollar');
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Domain\Model\Value;
class Drivetrain
{
public function __construct(
public readonly string $type,
) {
}
public function __toString(): string
{
return match($this->type) {
'rear' => 'Heckantrieb',
'front' => 'Frontantrieb',
'awd' => 'Allradantrieb',
default => $this->type,
};
}
public function getType(): string
{
return $this->type;
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Domain\Model\Value;
class Energy
{
public function __construct(
private readonly float $kwh,
) {
}
public function __toString(): string
{
return $this->kwh . ' kWh';
}
public function kwh(): float
{
return $this->kwh;
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Domain\Model\Value;
class Power
{
public function __construct(
public readonly float $kilowatts,
) {
}
public function __toString(): string
{
return $this->kilowatts . ' kW';
}
public function kilowatts(): float
{
return $this->kilowatts;
}
public function horsePower(): int
{
return (int) round($this->kilowatts * 1.36);
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Domain\Model\Value;
class Price
{
public function __construct(
public readonly int $price,
public readonly Currency $currency,
) {}
public function __toString(): string
{
return number_format($this->price, 0, ',', '.') . ' ' . $this->currency->symbol();
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Domain\Model\Value;
class Range
{
public function __construct(
public readonly int $kilometers,
) {
}
public function __toString(): string
{
return $this->kilometers . ' km';
}
public function rangeInKm(): int
{
return $this->kilometers;
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Domain\Model\Value;
class TopSpeed
{
public function __construct(
public readonly int $kmh,
) {
}
public function __toString(): string
{
return $this->kmh . ' km/h';
}
public function getKmh(): int
{
return $this->kmh;
}
}

View File

@ -2,16 +2,72 @@
namespace App\Domain\Search; namespace App\Domain\Search;
use App\Domain\Search\Tiles\Section; use App\Domain\Model\CarRevision;
use App\Domain\Search\Tiles\Brand; use App\Domain\Model\Image;
use App\Domain\Model\Value\Currency;
use App\Domain\Model\Value\Price;
use App\Domain\Model\Value\Range;
use App\Domain\Model\Value\Battery;
use App\Domain\Model\Value\Power;
use App\Domain\Model\Value\Acceleration;
use App\Domain\Model\Value\ChargingSpeed;
use App\Domain\Model\Value\Consumption;
use App\Domain\Model\Value\TopSpeed;
use App\Domain\Model\Value\Drivetrain;
use App\Domain\Model\Value\Energy;
use App\Domain\Search\Tiles\SectionTile;
use App\Domain\Search\Tiles\SubSectionTile;
use App\Domain\Search\Tiles\BrandTile;
use App\Domain\Search\Tiles\PriceTile;
use App\Domain\Search\Tiles\RangeTile;
use App\Domain\Search\Tiles\BatteryTile;
use App\Domain\Search\Tiles\PowerTile;
use App\Domain\Search\Tiles\AccelerationTile;
use App\Domain\Search\Tiles\ChargingTile;
use App\Domain\Search\Tiles\ConsumptionTile;
use App\Domain\Search\Tiles\AvailabilityTile;
use App\Domain\Search\Tiles\CarTile;
use App\Domain\Search\Tiles\TopSpeedTile;
use App\Domain\Search\Tiles\DrivetrainTile;
class Engine class Engine
{ {
public function search(string $query): TileCollection public function search(string $query): TileCollection
{ {
$skodaElroq85 = new CarRevision(
'Skoda Elroq 85', 2024,
new Power(210),
new Acceleration(6.6),
new TopSpeed(180),
new Range(450),
new Battery(new Energy(77.0), new Energy(82.0)),
new Consumption(new Energy(171)),
new Price(43900, Currency::euro()),
new ChargingSpeed(new Power(175), new Power(11)),
image: new Image('https://www.scherer-gruppe.de/media/f3b72d42-4b26-4606-8df4-d840efeff017/01_elroc.jpg?w=1920&h=758&action=crop&scale=both&anchor=middlecenter'),
);
return new TileCollection([ return new TileCollection([
new Section('Hello', [ new SectionTile('Skoda Elroq 85', [
new Brand('Tesla', 'https://www.tesla.com/tesla_theme/assets/img/meta-tags/apple-touch-icon.png'), new CarTile($skodaElroq85->image, [
new BrandTile('Skoda'),
new PriceTile(new Price(43900, Currency::euro())),
new AvailabilityTile('Bestellbar', 'Oktober 2024'),
new RangeTile(new Range(450)),
]),
new SubSectionTile('Performance', [
new PowerTile(new Power(210)),
new AccelerationTile(new Acceleration(6.6)),
new TopSpeedTile(new TopSpeed(180)),
new DrivetrainTile(new Drivetrain('rear')),
], 'Motor and driving performance specifications'),
new SubSectionTile('Range & Efficiency', [
new RangeTile(new Range(450)),
new BatteryTile(new Battery(new Energy(77.0), new Energy(82.0))),
new ConsumptionTile(new Consumption(new Energy(171))),
new ChargingTile(new ChargingSpeed(new Power(175), new Power(11))),
], 'Battery capacity, range, and charging capabilities'),
]), ]),
]); ]);
} }

View File

@ -2,7 +2,7 @@
namespace App\Domain\Search; namespace App\Domain\Search;
use App\Domain\Search\Tiles\Section; use App\Domain\Search\Tiles\SectionTile;
class TileCollection class TileCollection
{ {

View File

@ -0,0 +1,12 @@
<?php
namespace App\Domain\Search\Tiles;
use App\Domain\Model\Value\Acceleration;
class AccelerationTile
{
public function __construct(
public readonly Acceleration $acceleration,
) {}
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Domain\Search\Tiles;
class AvailabilityTile
{
public function __construct(
public readonly string $status,
public readonly ?string $availableSince = null,
) {}
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Domain\Search\Tiles;
use App\Domain\Model\Value\Battery;
class BatteryTile
{
public function __construct(
public readonly Battery $battery,
) {}
}

View File

@ -2,10 +2,10 @@
namespace App\Domain\Search\Tiles; namespace App\Domain\Search\Tiles;
class Brand class BrandTile
{ {
public function __construct( public function __construct(
public readonly string $name, public readonly string $name,
public readonly string $logo, public readonly ?string $logo = null,
) {} ) {}
} }

View File

@ -0,0 +1,13 @@
<?php
namespace App\Domain\Search\Tiles;
use App\Domain\Model\Image;
final readonly class CarTile
{
public function __construct(
public Image $image,
public array $tiles
) { }
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Domain\Search\Tiles;
use App\Domain\Model\Value\ChargingSpeed;
class ChargingTile
{
public function __construct(
public readonly ChargingSpeed $chargingSpeed,
) {}
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Domain\Search\Tiles;
use App\Domain\Model\Value\Consumption;
class ConsumptionTile
{
public function __construct(
public readonly Consumption $consumption,
) {}
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Domain\Search\Tiles;
use App\Domain\Model\Value\Drivetrain;
class DrivetrainTile
{
public function __construct(
public readonly Drivetrain $drivetrain,
) {}
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Domain\Search\Tiles;
use App\Domain\Model\Value\Power;
class PowerTile
{
public function __construct(
public readonly Power $power,
) {}
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Domain\Search\Tiles;
use App\Domain\Model\Value\Price;
class PriceTile
{
public function __construct(
public readonly Price $price,
) {}
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Domain\Search\Tiles;
use App\Domain\Model\Value\Range;
class RangeTile
{
public function __construct(
public readonly Range $range,
) {}
}

View File

@ -2,7 +2,7 @@
namespace App\Domain\Search\Tiles; namespace App\Domain\Search\Tiles;
class Section class SectionTile
{ {
public function __construct( public function __construct(
public readonly string $title, public readonly string $title,

View File

@ -0,0 +1,11 @@
<?php
namespace App\Domain\Search\Tiles;
class SubSectionTile
{
public function __construct(
public readonly string $title,
public readonly array $tiles,
) {}
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Domain\Search\Tiles;
use App\Domain\Model\Value\TopSpeed;
class TopSpeedTile
{
public function __construct(
public readonly TopSpeed $topSpeed,
) {}
}

View File

@ -7,11 +7,7 @@ use MongoDB\Database;
class MongoDBClient class MongoDBClient
{ {
public private(set) Client $client; private Client $client;
public Database $database {
get => $this->client->selectDatabase($this->databaseName);
}
public function __construct( public function __construct(
private readonly string $dsl, private readonly string $dsl,
@ -19,4 +15,14 @@ class MongoDBClient
) { ) {
$this->client = new Client($this->dsl); $this->client = new Client($this->dsl);
} }
public function getClient(): Client
{
return $this->client;
}
public function getDatabase(): Database
{
return $this->client->selectDatabase($this->databaseName);
}
} }

View File

@ -15,7 +15,7 @@ final class MongoDBBrandRepository implements BrandRepository
public function findAll(): BrandCollection public function findAll(): BrandCollection
{ {
$result = $this->client->database->selectCollection('brands')->find(); $result = $this->client->getDatabase()->selectCollection('brands')->find();
$brands = []; $brands = [];
$mapper = new ModelMapper(); $mapper = new ModelMapper();

View File

@ -26,25 +26,25 @@
} }
.header { .header {
text-align: center; display: flex;
margin-bottom: 3rem; justify-content: stretch;
gap: 2rem;
align-items: center;
margin-bottom: 2.5rem;
} }
.title { .title {
font-size: 3.5rem; font-size: 2.5rem;
font-weight: 800; font-weight: 800;
margin-bottom: 2.5rem;
letter-spacing: -1px; letter-spacing: -1px;
background: linear-gradient(90deg, #083d77 0%, #2e4057 35%, #d1495b 100%); background: linear-gradient(90deg, #083d77 0%, #2e4057 35%, #d1495b 100%);
-webkit-background-clip: text;
background-clip: text; background-clip: text;
-webkit-text-fill-color: transparent; -webkit-text-fill-color: transparent;
display: inline-block; display: inline-block;
} }
.search-container { .search-container {
max-width: 600px; flex-grow: 1;
margin: 0 auto 3rem;
position: relative; position: relative;
} }
@ -55,9 +55,12 @@
border: 2px solid #e0e0e0; border: 2px solid #e0e0e0;
border-radius: 50px; border-radius: 50px;
outline: none; outline: none;
transition: all 0.3s ease; transition: all 0.1s ease;
background: white; background: white;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); }
.search-input:hover {
border-color: #083d77;
} }
.search-input:focus { .search-input:focus {
@ -68,8 +71,7 @@
.search-button { .search-button {
position: absolute; position: absolute;
right: 8px; right: 8px;
top: 50%; top: 8px;
transform: translateY(-50%);
background: #083d77; background: #083d77;
color: white; color: white;
border: none; border: none;
@ -84,30 +86,109 @@
background: #2e4057; background: #2e4057;
} }
.tile { /* Tiles Grid Layout */
.tiles-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1px;
padding: 0;
overflow: hidden;
}
.tile-wrapper {
display: flex; display: flex;
border-radius: 10px; flex-direction: column;
padding: 10px; height: 100%;
background-color: #fff;
position: relative;
}
/* Special handling for subsection tiles */
.tile-wrapper:has(.subsection) {
grid-column: 1 / -1;
min-height: auto;
}
.tile {
background: white;
border: none;
border-radius: 0;
padding: 1.5rem;
width: 100%;
height: 100%;
min-height: 120px;
display: flex;
flex-direction: column;
justify-content: space-between;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
flex: 1;
}
.tile::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, #083d77, #2e4057, #d1495b);
opacity: 0;
transition: opacity 0.3s ease;
}
.tile:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(8, 61, 119, 0.15);
background: rgba(8, 61, 119, 0.02);
z-index: 1;
cursor: pointer;
}
.tile:hover::before {
opacity: 1;
} }
.tile-title { .tile-title {
font-size: 1.2rem; font-size: 1.25rem;
font-weight: 600; font-weight: 700;
color: #083d77; color: #083d77;
margin-bottom: 0.5rem;
line-height: 1.3;
} }
.tile-container { .tile-subtitle {
font-size: 0.9rem;
color: #666;
margin-bottom: 0.75rem;
font-weight: 500;
}
.tile small {
font-size: 0.75rem;
color: #888;
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 600;
margin-top: auto;
}
.tile-logo {
width: 40px;
height: 40px;
object-fit: contain;
margin-bottom: 1rem;
border-radius: 8px;
background: rgba(8, 61, 119, 0.05);
padding: 0.5rem;
}
.tile-content {
flex-grow: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-start; justify-content: flex-start;
justify-content: center;
gap: 0.5rem;
}
.tile-container > * {
flex-grow: 0;
flex-shrink: 1;
} }
.results-container { .results-container {
@ -115,12 +196,69 @@
} }
.section-title { .section-title {
font-size: 1.5rem; font-size: 1.75rem;
font-weight: 700; font-weight: 800;
color: #083d77; color: #083d77;
margin-bottom: 1rem; margin: 2.5rem 0 1.5rem 0;
border-bottom: 2px solid #e0e0e0;
padding-bottom: 0.5rem; padding-bottom: 0.5rem;
border-bottom: 2px solid rgba(8, 61, 119, 0.1);
position: relative;
}
.section-title::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 60px;
height: 2px;
background: linear-gradient(90deg, #083d77, #d1495b);
}
.section-title:first-child {
margin-top: 0;
}
.section-tiles-container {
margin-bottom: 2rem;
}
.subsection {
margin-top: 1rem;
}
.subsection-title {
font-size: 1.1rem;
font-weight: 600;
padding: 0.25rem 0.5rem;
position: relative;
border-radius: 2px 2px 0 0;
border-bottom: none;
display: inline-block;
min-width: 120px;
z-index: 1;
width: 100%;
margin-top: 1.5rem;
}
.subsection-description {
font-size: 0.9rem;
margin: 0;
padding: 0.25rem 1rem 0.75rem;
font-style: italic;
background: rgba(255, 255, 255, 0.8);
border: 1px solid rgba(224, 224, 224, 0.5);
border-top: none;
border-bottom: none;
margin-left: 0;
}
.subsection-tiles-container {
background: transparent;
border: none;
border-radius: 0;
overflow: visible;
margin-top: 0rem;
} }
.loading { .loading {
@ -145,8 +283,38 @@
padding: 1rem; padding: 1rem;
} }
.brands-grid { .tiles-grid {
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1px;
}
.tile-wrapper {
min-height: 100px;
}
.tile {
padding: 1.25rem;
min-height: 100px;
}
.tile-title {
font-size: 1.1rem;
}
}
@media (max-width: 480px) {
.tiles-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
gap: 1px;
}
.tile-wrapper {
min-height: 80px;
}
.tile {
padding: 1rem;
min-height: 80px;
} }
} }
</style> </style>

View File

@ -7,5 +7,7 @@
{% include '_components/search.html.twig' with { query: query } %} {% include '_components/search.html.twig' with { query: query } %}
</div> </div>
{% include 'result/tiles/collection.html.twig' with { tiles: tiles } %} <div class="tiles-grid">
{% include 'result/tiles/collection.html.twig' with { tiles: tiles } %}
</div>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,4 @@
<div class="tile">
<div class="tile-title">{{ tile.acceleration }}</div>
<small>Beschleunigung</small>
</div>

View File

@ -0,0 +1,9 @@
<div class="tile">
<div class="tile-content">
<div class="tile-title">{{ tile.status }}</div>
{% if tile.availableSince %}
<div class="tile-subtitle">seit {{ tile.availableSince }}</div>
{% endif %}
</div>
<small>Verfügbarkeit</small>
</div>

View File

@ -0,0 +1,4 @@
<div class="tile">
<div class="tile-title">{{ tile.battery }}</div>
<small>Batterie</small>
</div>

View File

@ -1,4 +1,9 @@
<div class="tile"> <div class="tile">
<img src="{{ tile.logo }}" alt="{{ tile.name }}" class="tile-logo"> {% if tile.logo %}
<div class="tile-title">{{ tile.name }}</div> <img src="{{ tile.logo }}" alt="{{ tile.name }}" class="tile-logo">
{% endif %}
<div class="tile-content">
<div class="tile-title">{{ tile.name }}</div>
</div>
<small>Marke</small>
</div> </div>

View File

@ -0,0 +1,8 @@
<div style="grid-column: span 2; grid-row: span 4;">
{% if tile.image %}
<img src="{{ tile.image.externalPublicUrl }}" style="width: 100%; height: 100%; object-fit: cover;">
{% endif %}
</div>
{% for tile in tile.tiles %}
{{ include('result/tiles/' ~ tile|twig_name ~ '.html.twig', { tile: tile }) }}
{% endfor %}

View File

@ -0,0 +1,4 @@
<div class="tile">
<div class="tile-title">{{ tile.chargingSpeed }}</div>
<small>Laden</small>
</div>

View File

@ -1,5 +1,3 @@
{% for tile in tiles %} {% for tile in tiles %}
<div class="tile-container"> {% include 'result/tiles/' ~ tile|twig_name ~ '.html.twig' with { tile: tile } %}
{% include 'result/tiles/' ~ tile|twig_name ~ '.html.twig' with { tile: tile } %}
</div>
{% endfor %} {% endfor %}

View File

@ -0,0 +1,4 @@
<div class="tile">
<div class="tile-title">{{ tile.consumption }}</div>
<small>Verbrauch</small>
</div>

View File

@ -0,0 +1,4 @@
<div class="tile">
<div class="tile-title">{{ tile.drivetrain }}</div>
<small>Antrieb</small>
</div>

View File

@ -0,0 +1,4 @@
<div class="tile">
<div class="tile-title">{{ tile.power }}</div>
<small>Leistung</small>
</div>

View File

@ -0,0 +1,6 @@
<div class="tile">
<div class="tile-content">
<div class="tile-title">{{ tile.price }}</div>
</div>
<small>Preis</small>
</div>

View File

@ -0,0 +1,4 @@
<div class="tile">
<div class="tile-title">{{ tile.range }}</div>
<small>Reichweite</small>
</div>

View File

@ -1,3 +1,3 @@
<h1>{{ tile.title }}</h1> <h1 class="section-title" style="grid-column: span 4;">{{ tile.title }}</h1>
{% include 'result/tiles/collection.html.twig' with { tiles: tile.tiles } %} {% include 'result/tiles/collection.html.twig' with { tiles: tile.tiles } %}

View File

@ -0,0 +1,2 @@
<h2 class="subsection-title" style="grid-column: span 4">{{ tile.title }}</h2>
{% include 'result/tiles/collection.html.twig' with { tiles: tile.tiles } %}

View File

@ -0,0 +1,4 @@
<div class="tile">
<div class="tile-title">{{ tile.topSpeed }}</div>
<small>Höchstgeschwindigkeit</small>
</div>