+ Professionelle Live-Bands in der ganzen Schweiz. Einfach buchen, perfekt performen. +
+ += htmlspecialchars($band['description']) ?>
+Finde die perfekte Band für dein Event mit unseren Suchfiltern.
+Sende eine unverbindliche Anfrage mit deinen Event-Details.
+Bestätige die Buchung und freue dich auf ein unvergessliches Event!
+{$e->getMessage()}
"; + echo "{$e->getTraceAsString()}";
+ } else {
+ http_response_code(500);
+ echo "500 - Internal Server Error";
+ }
+}
diff --git a/public/uploads/.gitkeep b/public/uploads/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/resources/css/app.css b/resources/css/app.css
new file mode 100644
index 0000000..a5ac92a
--- /dev/null
+++ b/resources/css/app.css
@@ -0,0 +1,49 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ html {
+ @apply scroll-smooth;
+ }
+
+ body {
+ @apply bg-gray-50 text-gray-900 antialiased;
+ }
+}
+
+@layer components {
+ .btn {
+ @apply px-4 py-2 rounded-lg font-medium transition-all duration-200 inline-flex items-center justify-center;
+ }
+
+ .btn-primary {
+ @apply bg-primary-500 text-white hover:bg-primary-600 active:bg-primary-700;
+ }
+
+ .btn-secondary {
+ @apply bg-gray-200 text-gray-800 hover:bg-gray-300 active:bg-gray-400;
+ }
+
+ .card {
+ @apply bg-white rounded-xl shadow-md p-6 transition-shadow hover:shadow-lg;
+ }
+
+ .input-field {
+ @apply w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent;
+ }
+
+ .badge {
+ @apply inline-flex items-center px-3 py-1 rounded-full text-sm font-medium;
+ }
+
+ .badge-yellow {
+ @apply bg-accent-100 text-accent-800;
+ }
+}
+
+@layer utilities {
+ .text-balance {
+ text-wrap: balance;
+ }
+}
diff --git a/resources/js/app.js b/resources/js/app.js
new file mode 100644
index 0000000..567a522
--- /dev/null
+++ b/resources/js/app.js
@@ -0,0 +1,99 @@
+import Alpine from 'alpinejs';
+
+// Make Alpine available globally
+window.Alpine = Alpine;
+
+// Alpine Components
+Alpine.data('searchBands', () => ({
+ query: '',
+ filters: {
+ genre: '',
+ location: '',
+ priceMin: '',
+ priceMax: '',
+ },
+ results: [],
+ loading: false,
+
+ init() {
+ console.log('Search component initialized');
+ },
+
+ async search() {
+ this.loading = true;
+ try {
+ const params = new URLSearchParams({
+ q: this.query,
+ ...this.filters
+ });
+ const response = await fetch(`/api/bands/search?${params}`);
+ this.results = await response.json();
+ } catch (error) {
+ console.error('Search error:', error);
+ } finally {
+ this.loading = false;
+ }
+ }
+}));
+
+Alpine.data('bookingForm', () => ({
+ formData: {
+ bandId: '',
+ eventDate: '',
+ location: '',
+ budget: '',
+ eventType: '',
+ message: ''
+ },
+ submitting: false,
+
+ async submit() {
+ this.submitting = true;
+ try {
+ const response = await fetch('/api/bookings', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(this.formData)
+ });
+
+ if (response.ok) {
+ alert('Buchungsanfrage erfolgreich gesendet!');
+ this.reset();
+ }
+ } catch (error) {
+ console.error('Booking error:', error);
+ alert('Es gab einen Fehler. Bitte versuchen Sie es erneut.');
+ } finally {
+ this.submitting = false;
+ }
+ },
+
+ reset() {
+ this.formData = {
+ bandId: '',
+ eventDate: '',
+ location: '',
+ budget: '',
+ eventType: '',
+ message: ''
+ };
+ }
+}));
+
+// Initialize Alpine
+Alpine.start();
+
+// Smooth scroll for anchor links
+document.addEventListener('DOMContentLoaded', () => {
+ document.querySelectorAll('a[href^="#"]').forEach(anchor => {
+ anchor.addEventListener('click', function (e) {
+ e.preventDefault();
+ const target = document.querySelector(this.getAttribute('href'));
+ if (target) {
+ target.scrollIntoView({ behavior: 'smooth' });
+ }
+ });
+ });
+});
diff --git a/routes/web.php b/routes/web.php
new file mode 100644
index 0000000..9363c08
--- /dev/null
+++ b/routes/web.php
@@ -0,0 +1,49 @@
+get('/', [HomeController::class, 'index']);
+$router->get('/bands', [BandController::class, 'index']);
+$router->get('/bands/{slug}', [BandController::class, 'show']);
+
+// Authentication routes
+$router->get('/login', [AuthController::class, 'showLogin']);
+$router->post('/login', [AuthController::class, 'login']);
+$router->get('/register', [AuthController::class, 'showRegister']);
+$router->post('/register', [AuthController::class, 'register']);
+$router->post('/logout', [AuthController::class, 'logout']);
+$router->get('/verify-email/{token}', [AuthController::class, 'verifyEmail']);
+
+// Protected routes (require authentication)
+$router->group(['middleware' => 'auth'], function($router) {
+ // Profile
+ $router->get('/profile', [ProfileController::class, 'show']);
+ $router->post('/profile/update', [ProfileController::class, 'update']);
+
+ // Booking routes
+ $router->post('/bookings/create', [BookingController::class, 'create']);
+ $router->get('/my-bookings', [BookingController::class, 'myBookings']);
+
+ // Band management (for band users)
+ $router->group(['middleware' => 'role:band'], function($router) {
+ $router->get('/band/manage', [BandController::class, 'manage']);
+ $router->post('/band/update', [BandController::class, 'update']);
+ $router->get('/band/bookings', [BookingController::class, 'bandBookings']);
+ $router->post('/band/bookings/{id}/respond', [BookingController::class, 'respond']);
+ });
+
+ // Admin routes
+ $router->group(['middleware' => 'role:admin'], function($router) {
+ $router->get('/admin', [AdminController::class, 'dashboard']);
+ $router->get('/admin/bands', [AdminController::class, 'bands']);
+ $router->post('/admin/bands/{id}/approve', [AdminController::class, 'approveBand']);
+ $router->get('/admin/reviews', [AdminController::class, 'reviews']);
+ $router->post('/admin/reviews/{id}/moderate', [AdminController::class, 'moderateReview']);
+ });
+});
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..bd6b5a3
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,45 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: [
+ "./app/Views/**/*.php",
+ "./public/**/*.js",
+ ],
+ theme: {
+ extend: {
+ colors: {
+ primary: {
+ 50: '#fffbeb',
+ 100: '#fef3c7',
+ 200: '#fde68a',
+ 300: '#fcd34d',
+ 400: '#fbbf24',
+ 500: '#f59e0b',
+ 600: '#d97706',
+ 700: '#b45309',
+ 800: '#92400e',
+ 900: '#78350f',
+ },
+ accent: {
+ 50: '#fefce8',
+ 100: '#fef9c3',
+ 200: '#fef08a',
+ 300: '#fde047',
+ 400: '#facc15',
+ 500: '#eab308',
+ 600: '#ca8a04',
+ 700: '#a16207',
+ 800: '#854d0e',
+ 900: '#713f12',
+ }
+ },
+ fontFamily: {
+ sans: ['Inter', 'system-ui', 'sans-serif'],
+ display: ['Poppins', 'system-ui', 'sans-serif'],
+ },
+ },
+ },
+ plugins: [
+ require('@tailwindcss/forms'),
+ require('@tailwindcss/typography'),
+ ],
+}
diff --git a/vite.config.js b/vite.config.js
new file mode 100644
index 0000000..c28e2f4
--- /dev/null
+++ b/vite.config.js
@@ -0,0 +1,31 @@
+import { defineConfig } from 'vite';
+import path from 'path';
+
+export default defineConfig({
+ root: '.',
+ build: {
+ outDir: 'public/dist',
+ emptyOutDir: true,
+ manifest: true,
+ rollupOptions: {
+ input: {
+ main: path.resolve(__dirname, 'resources/js/app.js'),
+ css: path.resolve(__dirname, 'resources/css/app.css'),
+ },
+ output: {
+ entryFileNames: 'js/[name].[hash].js',
+ chunkFileNames: 'js/[name].[hash].js',
+ assetFileNames: (assetInfo) => {
+ if (assetInfo.name.endsWith('.css')) {
+ return 'css/[name].[hash][extname]';
+ }
+ return 'assets/[name].[hash][extname]';
+ },
+ },
+ },
+ },
+ server: {
+ port: 3000,
+ strictPort: false,
+ },
+});