feat: implement device-specific configurations for desktop, tablet, and mobile
All checks were successful
Production Build & Deploy / build-and-deploy (push) Successful in 57s

This commit is contained in:
Daniel S
2026-08-09 12:35:46 +02:00
parent eb4bb601d7
commit b159b061ae
14 changed files with 61 additions and 21 deletions

View File

@@ -2,7 +2,7 @@
// src/components/Footer.astro
import { loadWaasConfigs } from '../utils/configLoader';
const { siteConfig } = loadWaasConfigs();
const { siteConfig } = loadWaasConfigs(Astro.request.headers.get('user-agent') ?? '');
const footer = siteConfig.footer || {
brandDescription: 'Ihr Partner für professionelles Webdesign, Website-Entwicklung und lokale SEO für Kleinbetriebe & KMU.',
copyrightText: 'N&D i-t SOLUTIONS. Alle Rechte vorbehalten.',

View File

@@ -3,7 +3,7 @@
import { FaBars, FaTimes } from 'react-icons/fa';
import { loadWaasConfigs } from '../utils/configLoader';
const { siteConfig } = loadWaasConfigs();
const { siteConfig } = loadWaasConfigs(Astro.request.headers.get('user-agent') ?? '');
const header = siteConfig.header || {
brandName: 'N&D',
brandHighlight: 'i-t SOLUTIONS',

View File

@@ -17,7 +17,7 @@ interface Props {
description?: string;
}
const { siteConfig, themeConfig } = loadWaasConfigs();
const { siteConfig, themeConfig } = loadWaasConfigs(Astro.request.headers.get('user-agent') ?? '');
const activePreset = THEME_PRESETS[themeConfig.activePreset] || THEME_PRESETS.corporateDark;
const colors = activePreset.colors;

View File

@@ -3,7 +3,7 @@ import Layout from '../../layouts/Layout.astro';
import { loadWaasConfigs } from '../../utils/configLoader';
import { THEME_PRESETS } from '../../utils/themePresets';
const { siteConfig, themeConfig } = loadWaasConfigs();
const { siteConfig, themeConfig } = loadWaasConfigs(Astro.request.headers.get('user-agent') ?? '');
---
<Layout title="Live Visual Editor | WaaS Engine">

View File

@@ -3,7 +3,7 @@ import Layout from '../../layouts/Layout.astro';
import { loadWaasConfigs } from '../../utils/configLoader';
import { THEME_PRESETS } from '../../utils/themePresets';
const { siteConfig, themeConfig, smtpConfig } = loadWaasConfigs();
const { siteConfig, themeConfig, smtpConfig } = loadWaasConfigs(Astro.request.headers.get('user-agent') ?? '');
const presets = Object.values(THEME_PRESETS);
---

View File

@@ -11,7 +11,7 @@ export const POST: APIRoute = async ({ request }) => {
fs.mkdirSync(dataDir, { recursive: true });
}
const { siteConfig: currentSite, themeConfig: currentTheme, smtpConfig: currentSmtp } = loadWaasConfigs();
const { siteConfig: currentSite, themeConfig: currentTheme, smtpConfig: currentSmtp } = loadWaasConfigs(Astro.request.headers.get('user-agent') ?? '');
if (body.siteConfig) {
const newSite = {

View File

@@ -13,7 +13,7 @@ export const POST: APIRoute = async ({ request }) => {
return new Response(JSON.stringify({ success: false, message: 'Bitte füllen Sie alle Pflichtfelder aus.' }), { status: 400, headers: { 'Content-Type': 'application/json' } });
}
const { smtpConfig, siteConfig } = loadWaasConfigs();
const { smtpConfig, siteConfig } = loadWaasConfigs(Astro.request.headers.get('user-agent') ?? '');
if (!smtpConfig.isConfigured || smtpConfig.host === 'smtp.placeholder.local') {
console.log('[WaaS Contact API] (Demo Mode) Received Submission:', { name, email, message });

View File

@@ -9,7 +9,7 @@ import FallbackSection from '../components/sections/FallbackSection.astro';
import { loadWaasConfigs } from '../utils/configLoader';
import { THEME_PRESETS } from '../utils/themePresets';
const { siteConfig, themeConfig, smtpConfig } = loadWaasConfigs();
const { siteConfig, themeConfig, smtpConfig } = loadWaasConfigs(Astro.request.headers.get('user-agent') ?? '');
const activePreset = THEME_PRESETS[themeConfig.activePreset] || THEME_PRESETS.corporateDark;
const colors = activePreset.colors;

View File

@@ -1,6 +1,6 @@
import fs from 'fs';
import path from 'path';
import { detectDeviceType } from './deviceDetect';
export interface UIComponent {
id: string; // Eindeutige ID (z.B. uuid / timestamp)
slotId: string; // Platz-ID (z.B. 'hero-badge-slot', 'hero-actions-slot', 'bento-slot-1')
@@ -206,11 +206,20 @@ function readJsonFile<T>(filePath: string, fallback: T): T {
}
}
export function loadWaasConfigs() {
export function loadWaasConfigs(userAgent: string = '') {
const dataDir = process.env.DATA_DIR || path.join(process.cwd(), 'app', 'data');
const sitePath = path.join(dataDir, 'site.config.json');
const themePath = path.join(dataDir, 'theme.config.json');
const smtpPath = path.join(dataDir, 'smtp.config.json');
const device = detectDeviceType(userAgent);
const deviceSitePath = path.join(dataDir, `site.${device}.config.json`);
const genericSitePath = path.join(dataDir, 'site.config.json');
const sitePath = fs.existsSync(deviceSitePath) ? deviceSitePath : genericSitePath;
const deviceThemePath = path.join(dataDir, `theme.${device}.config.json`);
const genericThemePath = path.join(dataDir, 'theme.config.json');
const themePath = fs.existsSync(deviceThemePath) ? deviceThemePath : genericThemePath;
const deviceSmtpPath = path.join(dataDir, `smtp.${device}.config.json`);
const genericSmtpPath = path.join(dataDir, 'smtp.config.json');
const smtpPath = fs.existsSync(deviceSmtpPath) ? deviceSmtpPath : genericSmtpPath;
const rawSite = readJsonFile<Partial<SiteConfig>>(sitePath, FALLBACK_SITE_CONFIG);
const siteConfig: SiteConfig = {

14
src/utils/deviceDetect.ts Normal file
View File

@@ -0,0 +1,14 @@
export type DeviceType = 'desktop' | 'tablet' | 'mobile';
/**
* Simple User-Agent based device detection.
* Returns 'desktop' by default when detection fails.
*/
export function detectDeviceType(userAgent: string): DeviceType {
const ua = userAgent.toLowerCase();
// Tablet detection (including iPad which reports as Mac on iOS 13+)
if (/tablet|ipad/.test(ua)) return 'tablet';
// Mobile detection (phones and Android)
if (/mobile|iphone|android/.test(ua)) return 'mobile';
return 'desktop';
}