Browse code

Update

Benjamin Roth authored on05/02/2026 17:13:25
Showing1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,223 @@
1
+import { LitElement, html, css, svg } from 'lit';
2
+import { unsafeHTML } from 'lit/directives/unsafe-html.js';
3
+import './card/winzer-file-card.js';
4
+import './winzer-day-view.js';
5
+import './winzer-menu-group.js';
6
+import './winzer-modal.js';
7
+import './card/winzer-card.js';
8
+import './winzer-header.js';
9
+import './winzer-nav.js';
10
+import './toggle-switch.js';
11
+
12
+export class WinzerApp extends LitElement {
13
+    static properties = {
14
+        isUserModalOpen: { type: Boolean },
15
+        isFilterModalOpen: { type: Boolean },
16
+        currentTab: { type: String },
17
+
18
+        // Configurable properties
19
+        appTitle: { type: String, attribute: 'app-title' },
20
+        appSubtitle: { type: String, attribute: 'app-subtitle' },
21
+        navItems: { type: Array }, // [{ id, label, icon (svg string), url }]
22
+        menuAccount: { type: Array },
23
+        menuSession: { type: Array },
24
+    };
25
+
26
+    constructor() {
27
+        super();
28
+        this.isUserModalOpen = false;
29
+        this.isFilterModalOpen = false;
30
+        this.currentTab = 'news'; // Default tab
31
+
32
+        // Defaults
33
+        this.appTitle = 'WINZER-PORTAL';
34
+        this.appSubtitle = 'Badischer Winzerkeller';
35
+
36
+        // Default Navigation (can be overwritten via property)
37
+        this.navItems = [
38
+            {
39
+                id: 'news',
40
+                label: 'News',
41
+                url: '/news',
42
+                icon: svg`<svg viewBox="0 0 24 24"><path d="M19 20H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h10a2 2 0 0 1 2-2v1m2 13a2 2 0 0 1-2-2V7m2 13a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"/></svg>`
43
+            },
44
+            {
45
+                id: 'anlieferung',
46
+                label: 'Anlieferung',
47
+                url: '/anlieferung',
48
+                icon: svg`<svg viewBox="0 0 24 24"><circle cx="12" cy="10" r="2"/><circle cx="15.5" cy="8" r="2"/><circle cx="8.5" cy="8" r="2"/><circle cx="12" cy="6" r="2"/><circle cx="10" cy="13" r="2"/><circle cx="14" cy="13" r="2"/><circle cx="12" cy="16" r="2"/><path d="M12 2v4" stroke-linecap="round"/></svg>`
49
+            },
50
+            {
51
+                id: 'tresor',
52
+                label: 'Datei-Tresor',
53
+                url: '/tresor',
54
+                icon: svg`<svg viewBox="0 0 24 24"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/><path d="M12 12v3"/><path d="M12 12v-1"/></svg>`
55
+            }
56
+        ];
57
+
58
+        this.menuAccount = [
59
+            { label: 'Persönliche Daten', icon: svg`<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>`, action: 'profile' },
60
+            { label: 'Sicherheits-Einstellungen', icon: svg`<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>`, action: 'security' },
61
+            { label: 'Meine Buchungen', icon: svg`<circle cx="12" cy="10" r="2"/><circle cx="15.5" cy="8" r="2"/><circle cx="8.5" cy="8" r="2"/><circle cx="12" cy="6" r="2"/><circle cx="10" cy="13" r="2"/><circle cx="14" cy="13" r="2"/><circle cx="12" cy="16" r="2"/><path d="M12 2v4"/>`, action: 'bookings' }
62
+        ];
63
+        this.menuSession = [
64
+            { label: 'Abmelden', icon: svg`<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/>`, action: 'logout' }
65
+        ];
66
+    }
67
+
68
+    static styles = css`
69
+        :host {
70
+            display: block;
71
+            background-color: var(--bg-white);
72
+            min-height: 100vh;
73
+            width: 100%;
74
+            position: relative;
75
+        }
76
+        .content { padding: 20px; padding-bottom: 90px; }
77
+        .section-header { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
78
+        h1 { font-size: 1.5rem; font-weight: 800; margin: 0; text-transform: uppercase; }
79
+        .abo-label { font-size: 0.8rem; font-weight: 600; margin-left: auto; margin-right: 8px; }
80
+
81
+        /* Filter Bar */
82
+        .filter-bar {
83
+            display: flex;
84
+            align-items: center;
85
+            font-size: 0.9rem;
86
+            font-weight: 600;
87
+            margin-bottom: 16px;
88
+            cursor: pointer;
89
+        }
90
+        .filter-icon {
91
+            margin-left: 8px;
92
+            width: 24px;
93
+            height: 24px;
94
+            color: var(--text-dark);
95
+        }
96
+
97
+        /* Filter Modal Styles */
98
+        .filter-content {
99
+            background-color: var(--bg-white);
100
+            padding: 16px;
101
+            border-radius: var(--radius-card);
102
+            box-shadow: var(--shadow-sm);
103
+        }
104
+        .filter-row {
105
+            display: flex;
106
+            align-items: center;
107
+            justify-content: space-between;
108
+            margin-bottom: 12px;
109
+        }
110
+        .filter-row label {
111
+            display: flex;
112
+            align-items: center;
113
+            font-weight: 700;
114
+            font-size: 0.9rem;
115
+            color: var(--text-dark);
116
+        }
117
+        .filter-row label svg {
118
+            width: 20px;
119
+            height: 20px;
120
+            margin-right: 8px;
121
+            color: var(--primary-color);
122
+        }
123
+        .filter-select {
124
+            background-color: var(--bg-white);
125
+            border: 1px solid var(--border-color);
126
+            border-radius: 8px;
127
+            padding: 8px 12px;
128
+            font-size: 0.9rem;
129
+            color: var(--text-dark);
130
+            width: 160px;
131
+        }
132
+        .filter-btn {
133
+            width: 100%;
134
+            background-color: var(--primary-color);
135
+            color: white;
136
+            border: none;
137
+            border-radius: var(--radius-sm);
138
+            padding: 12px;
139
+            font-weight: 600;
140
+            font-size: 1rem;
141
+            margin-top: 20px;
142
+            display: flex;
143
+            align-items: center;
144
+            justify-content: center;
145
+            gap: 8px;
146
+            cursor: pointer;
147
+        }
148
+    `;
149
+
150
+    render() {
151
+        return html`
152
+            <!-- Header -->
153
+            <winzer-header
154
+                    .mainTitle="${this.appTitle}"
155
+                    .subTitle="${this.appSubtitle}"
156
+                    @user-click="${() => this.isUserModalOpen = true}">
157
+            </winzer-header>
158
+
159
+            <div class="content">
160
+                <!-- Dynamic Content Slot -->
161
+                <!--
162
+                   The main content is now injected via the default slot.
163
+                   Contao renders the page content, and it gets placed here.
164
+                -->
165
+                <slot></slot>
166
+            </div>
167
+
168
+            <!-- Bottom Nav -->
169
+            <winzer-nav
170
+                    .activeTab="${this.currentTab}"
171
+                    .items="${this.navItems}">
172
+            </winzer-nav>
173
+
174
+            <!-- MODAL: USER -->
175
+            <winzer-modal title="Mein Benutzerkonto" ?open="${this.isUserModalOpen}" @close="${() => this.isUserModalOpen = false}">
176
+                <winzer-menu-group .items="${this.menuAccount}"></winzer-menu-group>
177
+                <winzer-menu-group .items="${this.menuSession}"></winzer-menu-group>
178
+            </winzer-modal>
179
+
180
+            <!-- MODAL: FILTER -->
181
+            <winzer-modal title="FILTER - EINSTELLUNGEN" ?open="${this.isFilterModalOpen}" @close="${() => this.isFilterModalOpen = false}">
182
+                <div class="filter-content">
183
+                    <div class="filter-row">
184
+                        <label>
185
+                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>
186
+                            Kategorie:
187
+                        </label>
188
+                        <select class="filter-select">
189
+                            <option>Traubengeld</option>
190
+                            <option>Rechnungen</option>
191
+                        </select>
192
+                    </div>
193
+                    <div class="filter-row">
194
+                        <label>
195
+                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>
196
+                            Zeitraum:
197
+                        </label>
198
+                        <select class="filter-select">
199
+                            <option>Letzter Monat</option>
200
+                            <option>Dieses Jahr</option>
201
+                        </select>
202
+                    </div>
203
+                    <div class="filter-row" style="margin-top: 20px;">
204
+                        <label>
205
+                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="21" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="21" y1="18" x2="3" y2="18"></line><polyline points="18 20 21 23 24 20"></polyline></svg>
206
+                            Sortierung:
207
+                        </label>
208
+                        <select class="filter-select">
209
+                            <option>Datum absteigend</option>
210
+                            <option>Datum aufsteigend</option>
211
+                        </select>
212
+                    </div>
213
+
214
+                    <button class="filter-btn" @click="${() => this.isFilterModalOpen = false}">
215
+                        Filtern
216
+                        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>
217
+                    </button>
218
+                </div>
219
+            </winzer-modal>
220
+        `;
221
+    }
222
+}
223
+customElements.define('winzer-app', WinzerApp);