feat: add the admin layout shell
This commit is contained in:
1 parent
da8af9ff08
commit
ad5c93049c
21 files changed
+441
-15
No files matched your search
@@ -0,0 +1,22 @@
|
||||
import { Controller } from '@hotwired/stimulus';
|
||||
|
||||
/*
|
||||
* Off-canvas mobile navigation drawer. Ported from myep.
|
||||
*
|
||||
* Two instances cooperate through a Stimulus outlet: the opener button in the header and the
|
||||
* drawer itself. Purely client-side -- htmx is not involved.
|
||||
*/
|
||||
export default class extends Controller {
|
||||
static outlets = ['mobilenav'];
|
||||
static classes = ['closed'];
|
||||
|
||||
trigger({ params: { mode } }) {
|
||||
this.mobilenavOutlet.toggle(mode);
|
||||
}
|
||||
|
||||
toggle(mode) {
|
||||
// Added only now, so the drawer does not animate into place on first paint.
|
||||
this.element.classList.add('transition-transform', 'duration-300');
|
||||
this.element.classList.toggle(this.closedClass, 'close' === mode);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M8 2v3m8-3v3"/><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M3 9h18M8 13h.01M12 13h.01M16 13h.01M8 17h.01M12 17h.01M16 17h.01"/></g></svg>
|
||||
|
After Width: | Height: | Size: 321 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><rect width="7" height="7" x="3" y="3" rx="1"/><rect width="7" height="7" x="14" y="3" rx="1"/><rect width="7" height="7" x="14" y="14" rx="1"/><rect width="7" height="7" x="3" y="14" rx="1"/></g></svg>
|
||||
|
After Width: | Height: | Size: 363 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m16 17l5-5l-5-5m5 5H9m0 9H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/></svg>
|
||||
|
After Width: | Height: | Size: 235 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5h16M4 12h16M4 19h16"/></svg>
|
||||
|
After Width: | Height: | Size: 199 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 6L6 18M6 6l12 12"/></svg>
|
||||
|
After Width: | Height: | Size: 196 B |
+3
-1
@@ -12,6 +12,7 @@
|
||||
"doctrine/orm": "^3.7",
|
||||
"phpdocumentor/reflection-docblock": "^6.0",
|
||||
"phpstan/phpdoc-parser": "^2.3",
|
||||
"symfony/apache-pack": "*",
|
||||
"symfony/asset": "7.4.*",
|
||||
"symfony/asset-mapper": "7.4.*",
|
||||
"symfony/console": "7.4.*",
|
||||
@@ -37,6 +38,7 @@
|
||||
"symfony/string": "7.4.*",
|
||||
"symfony/translation": "7.4.*",
|
||||
"symfony/twig-bundle": "7.4.*",
|
||||
"symfony/ux-icons": "^3.4",
|
||||
"symfony/validator": "7.4.*",
|
||||
"symfony/web-link": "7.4.*",
|
||||
"symfony/yaml": "7.4.*",
|
||||
@@ -90,7 +92,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"symfony": {
|
||||
"allow-contrib": false,
|
||||
"allow-contrib": true,
|
||||
"require": "7.4.*"
|
||||
}
|
||||
},
|
||||
|
||||
Generated
+120
-1
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "9cd4746d1b88efb4f3d1ecf392a958a9",
|
||||
"content-hash": "ef19a7def0151d8b452378a05ec4c324",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/semver",
|
||||
@@ -1903,6 +1903,32 @@
|
||||
},
|
||||
"time": "2024-09-11T13:17:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/apache-pack",
|
||||
"version": "v1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/apache-pack.git",
|
||||
"reference": "3aa5818d73ad2551281fc58a75afd9ca82622e6c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/apache-pack/zipball/3aa5818d73ad2551281fc58a75afd9ca82622e6c",
|
||||
"reference": "3aa5818d73ad2551281fc58a75afd9ca82622e6c",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "symfony-pack",
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "A pack for Apache support in Symfony",
|
||||
"support": {
|
||||
"issues": "https://github.com/symfony/apache-pack/issues",
|
||||
"source": "https://github.com/symfony/apache-pack/tree/master"
|
||||
},
|
||||
"time": "2017-12-12T01:46:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/asset",
|
||||
"version": "v7.4.8",
|
||||
@@ -7260,6 +7286,99 @@
|
||||
],
|
||||
"time": "2026-08-21T15:52:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/ux-icons",
|
||||
"version": "v3.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/ux-icons.git",
|
||||
"reference": "117b5d9bc9652f364495d586235b9e1f1f7c9d52"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/ux-icons/zipball/117b5d9bc9652f364495d586235b9e1f1f7c9d52",
|
||||
"reference": "117b5d9bc9652f364495d586235b9e1f1f7c9d52",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.4",
|
||||
"symfony/framework-bundle": "^7.4|^8.0",
|
||||
"symfony/twig-bundle": "^7.4|^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/flex": "<1.13",
|
||||
"symfony/ux-twig-component": "<2.21"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^11.1|^12.0",
|
||||
"psr/log": "^2|^3",
|
||||
"symfony/asset-mapper": "^7.4|^8.0",
|
||||
"symfony/console": "^7.4|^8.0",
|
||||
"symfony/http-client": "^7.4|^8.0",
|
||||
"symfony/ux-twig-component": "^2.14|^3.0",
|
||||
"zenstruck/console-test": "^1.5"
|
||||
},
|
||||
"type": "symfony-bundle",
|
||||
"extra": {
|
||||
"thanks": {
|
||||
"url": "https://github.com/symfony/ux",
|
||||
"name": "symfony/ux"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\UX\\Icons\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Kevin Bond",
|
||||
"email": "[email protected]"
|
||||
},
|
||||
{
|
||||
"name": "Simon André",
|
||||
"email": "[email protected]"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Renders local and remote SVG icons in your Twig templates.",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"icons",
|
||||
"svg",
|
||||
"symfony-ux",
|
||||
"twig"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/ux-icons/tree/v3.4.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-07-29T16:44:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/validator",
|
||||
"version": "v7.4.18",
|
||||
|
||||
@@ -12,4 +12,5 @@ return [
|
||||
Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
||||
Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
|
||||
Symfony\UX\Icons\UXIconsBundle::class => ['all' => true],
|
||||
];
|
||||
@@ -0,0 +1,27 @@
|
||||
# Full configuration reference: https://symfony.com/bundles/ux-icons/current/index.html#configuration
|
||||
ux_icons:
|
||||
|
||||
# Default HTML attributes to add to all icons
|
||||
default_icon_attributes:
|
||||
|
||||
# Use current text color to fill the icon
|
||||
fill: currentColor
|
||||
|
||||
# Default dimensions
|
||||
height: '1em'
|
||||
width: '1em'
|
||||
|
||||
# Throw an exception if an icon is not
|
||||
ignore_not_found: false
|
||||
|
||||
|
||||
when@dev:
|
||||
ux_icons:
|
||||
iconify:
|
||||
# Persist each on-demand icon to disk on first render, so you don't forget to commit it before deploying (like `ux:icons:lock`)
|
||||
auto_lock: true
|
||||
|
||||
when@prod:
|
||||
ux_icons:
|
||||
# Suppress exceptions for missing icons in production
|
||||
ignore_not_found: true
|
||||
@@ -1470,6 +1470,26 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* generate_final_classes?: bool|Param, // Default: true
|
||||
* generate_final_entities?: bool|Param, // Default: false
|
||||
* }
|
||||
* @psalm-type UxIconsConfig = array{
|
||||
* icon_dir?: scalar|Param|null, // The local directory where icons are stored. // Default: "%kernel.project_dir%/assets/icons"
|
||||
* default_icon_attributes?: array<string, scalar|Param|null>,
|
||||
* icon_sets?: array<string, array{ // the icon set prefix (e.g. "acme") // Default: []
|
||||
* path?: scalar|Param|null, // The local icon set directory path. (cannot be used with 'alias')
|
||||
* alias?: scalar|Param|null, // The remote icon set identifier. (cannot be used with 'path')
|
||||
* icon_attributes?: array<string, scalar|Param|null>,
|
||||
* suffixes?: array<string, array{ // The suffix name (e.g. "solid", "20-solid") // Default: []
|
||||
* icon_attributes?: array<string, scalar|Param|null>,
|
||||
* }>,
|
||||
* }>,
|
||||
* aliases?: array<string, string|Param>,
|
||||
* iconify?: bool|array{ // Configuration for the remote icon service.
|
||||
* enabled?: bool|Param, // Default: true
|
||||
* on_demand?: bool|Param, // Whether to download icons "on demand". // Default: true
|
||||
* auto_lock?: bool|Param, // Persist "on demand" icons to the local icon directory (see "icon_dir"). Recommended in dev only. Requires "on_demand" to be enabled. // Default: false
|
||||
* endpoint?: scalar|Param|null, // The endpoint for the Iconify icons API. // Default: "https://api.iconify.design"
|
||||
* },
|
||||
* ignore_not_found?: bool|Param, // Ignore error when an icon is not found. Set to 'true' to fail silently. // Default: false
|
||||
* }
|
||||
* @psalm-type ConfigType = array{
|
||||
* imports?: ImportsConfig,
|
||||
* parameters?: ParametersConfig,
|
||||
@@ -1482,6 +1502,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* twig_extra?: TwigExtraConfig,
|
||||
* security?: SecurityConfig,
|
||||
* monolog?: MonologConfig,
|
||||
* ux_icons?: UxIconsConfig,
|
||||
* "when@dev"?: array{
|
||||
* imports?: ImportsConfig,
|
||||
* parameters?: ParametersConfig,
|
||||
@@ -1497,6 +1518,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* security?: SecurityConfig,
|
||||
* monolog?: MonologConfig,
|
||||
* maker?: MakerConfig,
|
||||
* ux_icons?: UxIconsConfig,
|
||||
* },
|
||||
* "when@prod"?: array{
|
||||
* imports?: ImportsConfig,
|
||||
@@ -1510,6 +1532,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* twig_extra?: TwigExtraConfig,
|
||||
* security?: SecurityConfig,
|
||||
* monolog?: MonologConfig,
|
||||
* ux_icons?: UxIconsConfig,
|
||||
* },
|
||||
* "when@test"?: array{
|
||||
* imports?: ImportsConfig,
|
||||
@@ -1524,6 +1547,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||
* twig_extra?: TwigExtraConfig,
|
||||
* security?: SecurityConfig,
|
||||
* monolog?: MonologConfig,
|
||||
* ux_icons?: UxIconsConfig,
|
||||
* },
|
||||
* ...<string, ExtensionType|array{ // extra keys must follow the when@%env% pattern or match an extension alias
|
||||
* imports?: ImportsConfig,
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# Use the front controller as index file. It serves as a fallback solution when
|
||||
# every other rewrite/redirect fails (e.g. in an aliased environment without
|
||||
# mod_rewrite). Additionally, this reduces the matching process for the
|
||||
# start page (path "/") because otherwise Apache will apply the rewriting rules
|
||||
# to each configured DirectoryIndex file (e.g. index.php, index.html, index.pl).
|
||||
DirectoryIndex index.php
|
||||
|
||||
# By default, Apache does not evaluate symbolic links if you did not enable this
|
||||
# feature in your server configuration. Uncomment the following line if you
|
||||
# install assets as symlinks or if you experience problems related to symlinks
|
||||
# when compiling LESS/Sass/CoffeScript assets.
|
||||
# Options +SymLinksIfOwnerMatch
|
||||
|
||||
# Disabling MultiViews prevents unwanted negotiation, e.g. "/index" should not resolve
|
||||
# to the front controller "/index.php" but be rewritten to "/index.php/index".
|
||||
<IfModule mod_negotiation.c>
|
||||
Options -MultiViews
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
# This Option needs to be enabled for RewriteRule, otherwise it will show an error like
|
||||
# 'Options FollowSymLinks or SymLinksIfOwnerMatch is off which implies that RewriteRule directive is forbidden'
|
||||
Options +SymLinksIfOwnerMatch
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
# Determine the RewriteBase automatically and set it as environment variable.
|
||||
# If you are using Apache aliases to do mass virtual hosting or installed the
|
||||
# project in a subdirectory, the base path will be prepended to allow proper
|
||||
# resolution of the index.php file and to redirect to the correct URI. It will
|
||||
# work in environments without path prefix as well, providing a safe, one-size
|
||||
# fits all solution. But as you do not need it in this case, you can comment
|
||||
# the following 2 lines to eliminate the overhead.
|
||||
RewriteCond %{REQUEST_URI}::$0 ^(/.+)/(.*)::\2$
|
||||
RewriteRule .* - [E=BASE:%1]
|
||||
|
||||
# Sets the HTTP_AUTHORIZATION header removed by Apache
|
||||
RewriteCond %{HTTP:Authorization} .+
|
||||
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]
|
||||
|
||||
# Redirect to URI without front controller to prevent duplicate content
|
||||
# (with and without `/index.php`). Only do this redirect on the initial
|
||||
# rewrite by Apache and not on subsequent cycles. Otherwise we would get an
|
||||
# endless redirect loop (request -> rewrite to front controller ->
|
||||
# redirect -> request -> ...).
|
||||
# So in case you get a "too many redirects" error or you always get redirected
|
||||
# to the start page because your Apache does not expose the REDIRECT_STATUS
|
||||
# environment variable, you have 2 choices:
|
||||
# - disable this feature by commenting the following 2 lines or
|
||||
# - use Apache >= 2.3.9 and replace all L flags by END flags and remove the
|
||||
# following RewriteCond (best solution)
|
||||
RewriteCond %{ENV:REDIRECT_STATUS} =""
|
||||
RewriteRule ^index\.php(?:/(.*)|$) %{ENV:BASE}/$1 [R=308,L]
|
||||
|
||||
# If the requested filename exists, simply serve it.
|
||||
# We only want to let Apache serve files and not directories.
|
||||
# Rewrite all other queries to the front controller.
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ %{ENV:BASE}/index.php [L]
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mod_rewrite.c>
|
||||
<IfModule mod_alias.c>
|
||||
# When mod_rewrite is not available, we instruct a temporary redirect of
|
||||
# the start page to the front controller explicitly so that the website
|
||||
# and the generated links can still be used.
|
||||
RedirectMatch 307 ^/$ /index.php/
|
||||
# RedirectTemp cannot be used instead
|
||||
</IfModule>
|
||||
</IfModule>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
final class BookingController extends AbstractController
|
||||
{
|
||||
#[Route('/buchungen', name: 'app_booking', methods: ['GET'])]
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->render('booking/index.html.twig');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
final class DashboardController extends AbstractController
|
||||
{
|
||||
#[Route('/', name: 'app_dashboard', methods: ['GET'])]
|
||||
public function index(): Response
|
||||
{
|
||||
return $this->render('dashboard/index.html.twig');
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,18 @@
|
||||
"bin/phpunit"
|
||||
]
|
||||
},
|
||||
"symfony/apache-pack": {
|
||||
"version": "1.0",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes-contrib",
|
||||
"branch": "main",
|
||||
"version": "1.0",
|
||||
"ref": "5d454ec6cc4c700ed3d963f3803e1d427d9669fb"
|
||||
},
|
||||
"files": [
|
||||
"public/.htaccess"
|
||||
]
|
||||
},
|
||||
"symfony/asset-mapper": {
|
||||
"version": "7.4",
|
||||
"recipe": {
|
||||
@@ -270,6 +282,19 @@
|
||||
"templates/base.html.twig"
|
||||
]
|
||||
},
|
||||
"symfony/ux-icons": {
|
||||
"version": "3.4",
|
||||
"recipe": {
|
||||
"repo": "github.com/symfony/recipes",
|
||||
"branch": "main",
|
||||
"version": "3.4",
|
||||
"ref": "16a3114526e46593b494471d1654dfa46831beb5"
|
||||
},
|
||||
"files": [
|
||||
"assets/icons/symfony.svg",
|
||||
"config/packages/ux_icons.yaml"
|
||||
]
|
||||
},
|
||||
"symfony/validator": {
|
||||
"version": "7.4",
|
||||
"recipe": {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{# Flat inline alerts for now. myep renders these as Toastify toasts, but that means adopting
|
||||
toastify-js as a dependency -- deferred until the toast/OOB work (plan step 7). #}
|
||||
{% set levels = { error: 'bg-red-600', warning: 'bg-red-600', success: 'bg-emerald-600', info: 'bg-primary' } %}
|
||||
|
||||
{% for level, messages in app.flashes(levels|keys) %}
|
||||
<div class="fixed bottom-4 right-4 z-50 space-y-2">
|
||||
{% for message in messages %}
|
||||
<div class="{{ levels[level] }} text-gray-100 rounded shadow-lg px-4 py-3 text-sm">
|
||||
{{ message|trans }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
@@ -0,0 +1,25 @@
|
||||
{# Plain nav instead of myep's knp-menu builder: a handful of links does not justify the
|
||||
indirection. Active state is a prefix match on the current route name, which mirrors what
|
||||
myep's menu matcher does with its 'routes' extras. #}
|
||||
{% set current_route = app.request.attributes.get('_route') %}
|
||||
{% set items = [
|
||||
{ route: 'app_dashboard', label: 'Übersicht', icon: 'lucide:layout-grid' },
|
||||
{ route: 'app_booking', label: 'Meine Buchungen', icon: 'lucide:calendar-days' },
|
||||
] %}
|
||||
|
||||
<div class="min-h-full flex grow flex-col gap-y-5 lg:border-r border-gray-200 bg-white md:pr-6">
|
||||
<nav class="flex flex-1 flex-col">
|
||||
<ul role="list" class="space-y-1">
|
||||
{% for item in items %}
|
||||
{% set is_active = current_route starts with item.route %}
|
||||
<li>
|
||||
<a href="{{ path(item.route) }}"
|
||||
class="group flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold {{ is_active ? 'bg-gray-100 text-gray-900' : 'text-gray-700 hover:bg-gray-50' }}">
|
||||
{{ ux_icon(item.icon, { class: 'h-5 w-5 shrink-0 ' ~ (is_active ? 'text-gray-600' : 'text-gray-400') }) }}
|
||||
<span class="flex-1">{{ item.label }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
+12
-13
@@ -1,24 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% block title %}Welcome!{% endblock %}</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text><text y=%221.3em%22 x=%220.2em%22 font-size=%2276%22 fill=%22%23fff%22>sf</text></svg>">
|
||||
{% block stylesheets %}
|
||||
{% endblock %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
{# Internal tool -- keep it out of search engines. #}
|
||||
<meta name="robots" content="noindex,nofollow">
|
||||
<title>{% block title %}ephub{% endblock %}</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.15em%22 font-size=%2296%22>🪑</text></svg>">
|
||||
|
||||
{# The Tailwind build is pulled in by assets/app.js's `import './styles/app.css'`,
|
||||
so importmap() emits the <link> for it. This block is for anything outside that graph. #}
|
||||
{% block stylesheets %}{% endblock %}
|
||||
|
||||
{% block javascripts %}
|
||||
{% block importmap %}{{ importmap('app') }}{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% set frankenphp_hot_reload = app.request.server.get('FRANKENPHP_HOT_RELOAD') %}
|
||||
{% if frankenphp_hot_reload %}
|
||||
<meta name="frankenphp-hot-reload:url" content="{{ frankenphp_hot_reload }}">
|
||||
<script src="https://cdn.jsdelivr.net/npm/idiomorph"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/frankenphp-hot-reload/+esm" type="module"></script>
|
||||
{% endif %}
|
||||
</head>
|
||||
<body>
|
||||
{# No hx-boost: htmx handles fragments only, the browser owns navigation. #}
|
||||
<body class="bg-white text-gray-700 font-sans antialiased">
|
||||
{% block body %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
{% extends 'layout.html.twig' %}
|
||||
|
||||
{% block title %}Meine Buchungen · ephub{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Meine Buchungen</h1>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,8 @@
|
||||
{% extends 'layout.html.twig' %}
|
||||
|
||||
{% block title %}Übersicht · ephub{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Übersicht</h1>
|
||||
<p class="subline">Schreibtische und Parkplätze buchen.</p>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,43 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block body %}
|
||||
<div class="max-w-screen-2xl mx-auto px-4 lg:px-8 lg:grid lg:grid-cols-4 gap-8 py-8">
|
||||
<div class="lg:col-span-4 flex items-center justify-between pb-4 lg:pb-0">
|
||||
<a href="{{ path('app_dashboard') }}" class="text-2xl font-black tracking-tight text-primary-dark">
|
||||
ep<span class="text-pink">hub</span>
|
||||
</a>
|
||||
<button type="button"
|
||||
class="lg:hidden"
|
||||
{{ stimulus_controller('mobilenav', [], [], { 'mobilenav': '#mobilenav' }) }}
|
||||
{{ stimulus_action('mobilenav', 'trigger', null, { 'mode': 'open' }) }}>
|
||||
{{ ux_icon('lucide:menu', { class: 'w-8 h-8 text-gray-700' }) }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="hidden lg:block sticky top-0 h-screen">
|
||||
{% include '_partials/_nav.html.twig' %}
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-3">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mobilenav"
|
||||
class="fixed z-50 top-0 left-0 inset-0 pr-16 bg-black/70 -translate-x-full"
|
||||
{{ stimulus_controller('mobilenav', [], { 'closed': '-translate-x-full' }, { 'mobilenav': '#mobilenav' }) }}>
|
||||
<div class="h-screen w-full bg-white">
|
||||
<div class="flex justify-between p-8">
|
||||
<span class="text-2xl font-black tracking-tight text-primary-dark">ep<span class="text-pink">hub</span></span>
|
||||
<button type="button" {{ stimulus_action('mobilenav', 'trigger', null, { 'mode': 'close' }) }}>
|
||||
{{ ux_icon('lucide:x', { class: 'w-8 h-8' }) }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="w-full h-full p-8 pt-0 overflow-y-scroll">
|
||||
{% include '_partials/_nav.html.twig' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include '_partials/_flashes.html.twig' %}
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user