This commit is contained in:
Robin Grenzdörfer 2023-09-24 09:38:03 +00:00
parent 7813f0b486
commit eee191955e
32 changed files with 1091 additions and 518 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -361,6 +361,12 @@ subFields:
type: string
meta:
label: Zeilenname
- name: showRowName
type: boolean
meta:
label: Zeilenname anzeigen
- name: columns
type: object[]
@ -598,6 +604,97 @@ subFields:
dependsOn:
eval: $parent?.showNumber
- name: showCheckboxGroup
type: boolean
meta:
label: Checkbox Gruppe Anzeigen
- name: groupTitle
type: string
meta:
label: Checkbox Gruppe Titel
dependsOn:
eval: $parent.showCheckboxGroup
- name: checkboxes
type: object[]
meta:
label: Checkbox Gruppe
dependsOn:
eval: $parent.showCheckboxGroup
direction: row
subFields:
- name: name
type: string
meta:
label: Name
- name: emailName
type: string
meta:
label: Email Name
- name: showDatePicker
type: boolean
meta:
label: Datumauswahl anzeigen
- name: datePickerEmailTitle
type: string
meta:
label: Datumauswahl Email Titel
dependsOn:
eval: $parent.showDatePicker
- name: datePickerNotRequired
type: boolean
meta:
label: nicht Notwendig
dependsOn:
eval: $parent.showDatePicker
- name: datePickerProps
type: object
meta:
label: Datumauswahl Eigenschaften
dependsOn:
eval: $parent.showDatePicker
subFields:
- name: allowedDateRanges
type: object[]
meta:
label: Erlaubte Datumsbereiche
subFields:
- name: from
type: date
meta:
label: Von
widget: date
- name: to
type: date
meta:
label: Bis
widget: date
- name: excludeDays
type: string[]
meta:
label: Auszuschließende Wochentage
widget: selectArray
choices:
- id: monday
name: Montag
- id: tuesday
name: Dienstag
- id: wednesday
name: Mittwoch
- id: thursday
name: Donnerstag
- id: friday
name: Freitag
- id: saturday
name: Samstag
- id: sunday
name: Sonntag
- name: text
type: object[]
meta:
@ -614,6 +711,12 @@ subFields:
meta:
label: Platzhalter für das leere Eingabefeld
- name: textTitle
type: string
meta:
label: Text Titel
helperText: Alternative zu textPlaceholder
- name: textArea
type: boolean
meta:

@ -1,13 +1,14 @@
const { validateFields } = require("./validateFields")
;(function () {
if (context.data.formular.honey) {
if (context?.data?.formular?.honey) {
throw { status: 400, error: "Bot detection" }
}
delete context.data.formular.honey
let values = Object.entries(context.data.formular)
/**
* @type {Array<[string, FormObj]>}
*/
let values = Object.entries(context?.data?.formular)
let validation = validateFields(values)
console.log(validation)
if (validation.length) {
throw { status: 400, error: validation }
}

@ -1,35 +1,54 @@
var utils = require("../lib/utils")
var config = require("../config")
;(function () {
let formular = context.data.formular
/** @type {FormObj} */
let formular = context?.data?.formular
/** @type {TempFormBefore | TempFormAfter} */
let tempForm = {}
let formTitle = `${formular.formTitle}`
delete formular.formTitle
delete formular["agreement"]
delete formular["honey"]
formular.formRows.forEach((rowName, i) => {
tempForm[rowName] = {}
})
formular?.formRows?.forEach(
/** @param {string} rowName */
(rowName) => {
tempForm[rowName] = {}
}
)
/**
* @param {[string, any]} e
* @returns {any}
*/
let getValue = (e) => {
if (e[0].includes("numberLabel")) return e[1][0]
return e[1]
}
/** @type {TempFormIndices} */
let indices = {}
Object.entries(formular).forEach((e) => {
if (e[0] == "formRows") return
if (e[0] === "formRows") return
let key = e[0].split("_")
let newKey = key[key.length - 1]
let rowName = formular.formRows[Number(key[key.length - 2])]
// @ts-ignore
let rowName = formular?.formRows[Number(key[key.length - 2])]
let index = isNaN(Number(key[key.length - 3])) ? 100 : Number(key[key.length - 3])
if (!rowName) return
if (!tempForm[rowName]) tempForm[rowName] = {}
// @ts-ignore
if (tempForm[rowName][newKey]) {
// @ts-ignore
tempForm[rowName][newKey][0] += "\\" + getValue(e)
} else {
tempForm[rowName][newKey] = [getValue(e), newKey[0] == "n", newKey.includes("invalid")]
// @ts-ignore
tempForm[rowName][newKey] = [getValue(e), newKey[0] === "n", newKey.includes("invalid")]
indices[rowName] = { ...indices[rowName], [newKey]: index }
}
})
@ -40,12 +59,12 @@ var config = require("../config")
.sort((a, b) => indices[rowName][a[0]] - indices[rowName][b[0]])
.reduce((acc, [key, val]) => ({ ...acc, [key]: val }), {})
})
console.log(JSON.stringify(tempForm), JSON.stringify(indices))
Object.keys(tempForm).forEach((row) => {
tempForm[row] = Object.entries(tempForm[row])
})
// @ts-ignore
delete tempForm[undefined]
context.smtp.sendMail({
to: "binkrassdufass@gmail.com",

@ -1,5 +1,11 @@
/**
* @param {Array<[string, FormObj]>} fieldsArray
* @returns {(string | (() => void))[][]}
*/
function validateFields(fieldsArray) {
/**@type {(string | (() => void))[][]} */
const errors = []
/**@type {number} */
let selectedGroup
const numberRegex = /^[+]?([.]\d+|\d+([.]\d+)?)$/
@ -14,6 +20,12 @@ function validateFields(fieldsArray) {
blockContainer.classList.add("invalidBlocks")
}
/**
*
* @param {string} value
* @param {any} field
* @param {HTMLElement} element
*/
const validateNumber = (value, field, element) => {
if (!numberRegex.test(`${value}`)) {
errors.push(["block", () => element.classList.add("border-red")])
@ -23,6 +35,7 @@ function validateFields(fieldsArray) {
fieldsArray.forEach(([field, value]) => {
if (field === "blockGroups" || field.includes("numberLabel")) {
if (!field.includes("numberLabel")) return
// @ts-ignore
const [elementValue, element, group] = value
if (!elementValue) return

24
frontend/media/dark.svg Normal file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg version="1.1" id="_x32_" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="800px" height="800px" viewBox="0 0 512 512" xml:space="preserve">
<style type="text/css">
<![CDATA[
.st0{fill:#000000;}
]]>
</style>
<g>
<path class="st0" d="M403.469,395.031c-129.203,0-233.938-104.75-233.938-233.953c0-62.438,24.5-119.125,64.375-161.078
C109.313,17.953,13.563,125.094,13.563,254.656C13.563,396.781,128.781,512,270.906,512c98.688,0,184.359-55.578,227.531-137.125
C469.406,387.781,437.297,395.031,403.469,395.031z"/>
<path class="st0" d="M349.641,179.328c1.047,1.016,1.516,2.484,1.266,3.922l-8.563,49.938c-0.281,1.672,0.406,3.344,1.766,4.344
c1.359,0.984,3.156,1.109,4.656,0.328l44.859-23.578c1.281-0.688,2.813-0.688,4.109,0l44.859,23.578
c1.484,0.781,3.297,0.656,4.656-0.328c1.359-1,2.031-2.672,1.75-4.344l-8.563-49.938c-0.25-1.438,0.219-2.906,1.266-3.922
L478,143.969c1.203-1.172,1.641-2.938,1.125-4.531c-0.531-1.594-1.906-2.781-3.578-3.016l-50.141-7.297
c-1.438-0.203-2.688-1.109-3.344-2.406l-22.422-45.453c-0.734-1.516-2.281-2.453-3.969-2.453c-1.672,0-3.219,0.938-3.953,2.453
l-22.438,45.453c-0.641,1.297-1.891,2.203-3.328,2.406l-50.141,7.297c-1.672,0.234-3.063,1.422-3.578,3.016
s-0.078,3.359,1.125,4.531L349.641,179.328z"/>
</g>
</svg>

After

(image error) Size: 1.5 KiB

2
frontend/media/light.svg Normal file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#ffffff"><circle cx="32" cy="32" r="16"/><line x1="32" y1="8" x2="32" y2="16"/><line x1="32" y1="56" x2="32" y2="48"/><line x1="56" y1="32" x2="48" y2="32"/><line x1="8" y1="32" x2="16" y2="32"/><line x1="48.97" y1="15.03" x2="43.31" y2="20.69"/><line x1="15.03" y1="48.97" x2="20.69" y2="43.31"/><line x1="48.97" y1="48.97" x2="43.31" y2="43.31"/><line x1="15.03" y1="15.03" x2="20.69" y2="20.69"/></svg>

After

(image error) Size: 634 B

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="0 0 1000 1000" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="100%" height="100%" fill="rgba(207,165,149,0)"/>
<g transform="matrix(4.995999813079834, 0, 0, 4.995999813079834, 501.0972900390626, 499.6000061035156)" id="495873">
<g transform="matrix(4.985044002532959, 0, 0, 5, 1419.3128662109375, 549.1923828125)" id="495873" style="">
<filter id="SVGID_40" y="-20%" height="140%" x="-20%" width="140%">
<feGaussianBlur in="SourceAlpha" stdDeviation="0"/>
<feOffset dx="0" dy="0" result="oBlur"/>
@ -12,6 +11,10 @@
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<path style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; is-custom-font: none; font-file-url: none; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;filter: url(#SVGID_40);" vector-effect="non-scaling-stroke" d="M 112.787 80.919 L 134.765 125.275 L 114.985 125.275 L 100.599 95.904 L 77.822 126.074 L 77.822 200 L 59.84 200 L 59.84 110.09 L 77.822 79.52 L 77.822 104.695 L 147.952 0 L 168.731 0 L 131.768 54.545 L 112.787 80.919 Z M 77.822 0 L 77.822 38.162 L 59.84 68.931 L 59.84 0 L 77.822 0 Z M 120.38 38.561 L 109.79 54.346 L 100.2 38.162 L 47.652 127.073 L 58.042 127.073 L 58.042 141.059 L 39.261 141.059 L 19.481 174.625 L -0.3 174.625 L 87.413 25.774 L 99.732 4.555 L 112.587 25.774 L 120.38 38.561 Z M 200.3 174.625 L 180.719 174.625 L 160.939 141.059 L 79.82 141.059 L 79.82 127.073 L 152.547 127.073 L 120.779 73.127 L 131.568 57.942 L 200.3 174.625 Z M 170.929 200 L 150.35 200 L 123.576 142.857 L 143.556 142.857 L 170.929 200 Z" stroke-linecap="round" transform=" translate(-100, -100)"/>
<path style="stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill-rule: nonzero; opacity: 1; filter: url('#SVGID_40'); stroke-width: 0px; stroke: rgb(131, 89, 89); paint-order: stroke; fill: rgb(255, 0, 0);" vector-effect="non-scaling-stroke" d="M -171.628 -28.919 L -149.65 15.437 L -169.43 15.437 L -183.816 -13.934 L -206.593 16.236 L -206.593 90.162 L -224.575 90.162 L -224.575 0.252 L -206.593 -30.318 L -206.593 -5.143 C -206.593 -4.057 -136.463 -109.109 -136.463 -109.838 L -115.684 -109.838 L -152.647 -55.293 L -171.628 -28.919" stroke-linecap="round"/>
<path style="stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill-rule: nonzero; opacity: 1; filter: url('#SVGID_40'); stroke-width: 0px; stroke: rgb(131, 89, 89); paint-order: stroke; fill: rgb(255, 0, 0);" vector-effect="non-scaling-stroke" d="M -113.486 90.162 L -134.065 90.162 L -160.839 33.019 L -140.859 33.019 L -113.486 90.162 Z" stroke-linecap="round"/>
<path style="stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill-rule: nonzero; opacity: 1; filter: url('#SVGID_40'); stroke-width: 0px; stroke: rgb(131, 89, 89); paint-order: stroke; fill: rgb(255, 255, 0);" vector-effect="non-scaling-stroke" d="M -84.115 64.787 L -103.696 64.787 L -123.476 31.221 L -204.595 31.221 L -204.595 17.235 L -131.868 17.235 L -163.636 -36.711 L -152.847 -51.896 L -84.115 64.787 Z" stroke-linecap="round"/>
<path style="stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill-rule: nonzero; opacity: 1; filter: url('#SVGID_40'); stroke-width: 0px; stroke: rgb(131, 89, 89); paint-order: stroke; fill: rgb(255, 255, 0);" vector-effect="non-scaling-stroke" d="M -164.035 -71.277 L -174.625 -55.492 L -184.215 -71.676 L -236.763 17.235 L -226.373 17.235 L -226.373 31.221 L -245.154 31.221 L -264.934 64.787 L -284.715 64.787 L -197.002 -84.064 L -184.683 -105.283 L -171.828 -84.064 L -164.035 -71.277 Z" stroke-linecap="round"/>
<path style="stroke-dasharray: none; stroke-linecap: butt; stroke-dashoffset: 0; stroke-linejoin: miter; stroke-miterlimit: 4; fill-rule: nonzero; opacity: 1; filter: url('#SVGID_40'); stroke-width: 0px; stroke: rgb(131, 89, 89); paint-order: stroke; fill: rgb(255, 0, 0);" vector-effect="non-scaling-stroke" d="M -206.593 -109.838 L -206.593 -71.676 L -224.575 -40.907 L -224.575 -109.838 L -206.593 -109.838 Z" stroke-linecap="round"/>
</g>
</svg>

Before

(image error) Size: 1.8 KiB

After

(image error) Size: 3.2 KiB

@ -17,7 +17,7 @@
<!--HEAD-->
<!--PRELOAD-->
</head>
<body>
<body class="">
<div id="appContainer"><!--HTML--></div>
<script type="module" src="/dist/index.mjs?t=__TIMESTAMP__"></script>
<script nomodule src="/dist/index.es5.js?t=__TIMESTAMP__"></script>

@ -1,6 +1,8 @@
<script lang="ts">
import { location, navigation, sites, temperature, banner, serviceNavigation } from "./lib/stores"
import { location, navigation, sites, banner, serviceNavigation } from "./lib/stores"
import Header from "./lib/components/header/Header.svelte"
import "fluent-svelte/theme.css"
import Footer from "./lib/components/Footer.svelte"
import { Route, Router } from "svelte-routing"
import HomePage from "./routes/HomePage.svelte"
@ -44,7 +46,6 @@
sitesRes[e.id] = e
})
$sites = sitesRes
console.log("ALARM OVER", $sites)
}
getNavigation()

@ -10,7 +10,7 @@
right: 0;
width: 0;
bottom: -2px;
background: var(--background-color);
background: var(--heading-font-color);
height: 4px;
transition: width 0.5s ease-in;
}
@ -29,7 +29,7 @@
}
.fill:hover {
color: var(--background-color) !important;
color: var(--heading-font-color) !important;
div {
position: relative;
z-index: 2;
@ -43,7 +43,7 @@
top: 0px;
bottom: 0px;
width: 0px;
background: var(--opposite-bg-color);
background: var(--heading-font-color);
transition: width 0.5s ease-in;
}
.fill:hover:after,
@ -78,7 +78,7 @@ swiper-slide {
z-index: 10000;
left: 0px;
bottom: -10px;
background: var(--opposite-bg-color);
background: var(--heading-font-color);
height: 5px;
width: 0;
animation: underlineEffect 4s linear forwards;
@ -95,7 +95,7 @@ swiper-slide {
.swiper-button-prev,
.swiper-button-next {
color: var(--normal-font-color) !important;
color: var(--heading-font-color) !important;
height: 70px !important;
width: 70px !important;
bottom: 0px !important;
@ -136,7 +136,7 @@ swiper-slide {
@media @desktop {
.swiper-button-prev,
.swiper-button-next {
top: 125px !important;
top: 155px !important;
transform: scale(1) !important;
}
}

@ -1,25 +1,35 @@
:root {
--background-color: white;
--background-color: rgb(235, 221, 221);
--background-color-90: #ffffffe6;
--normal-font-color: #333333;
--normal-font-color-12: rgba(51, 51, 51, 0.12);
--hover-color: #dee2e6;
--heading-font-color: #672129;
--link-font-color: #672129;
--normal-font-color-80: rgba(51, 51, 51, 0.8);
--normal-font-color-50: rgba(51, 51, 51, 0.5);
--normal-font-color-30: rgba(51, 51, 51, 0.3);
--hover-color: rgb(197, 173, 173);
--heading-font-color: #ff0000;
--top-heading-font-color: rgb(255, 165, 0);
--link-font-color: rgb(255, 165, 0);
--banner-color: #06d6a0;
--opposite-bg-color: #000;
--opposite-bg-color-80: rgba(0, 0, 0, 0.8);
--opposite-bg-color-5: rgba(0, 0, 0, 0.05);
--opposite-bg-color-80: rgba(24, 24, 24, 0.8);
--opposite-bg-color-5: rgba(24, 24, 24, 0.05);
}
body.darkTheme {
--background-color: #1a1a1a; /* softer than #121212 */
--background-color-90: rgba(26, 26, 26, 0.9);
--normal-font-color: #d1d1d1; /* off-white, less harsh */
--normal-font-color-80: rgba(209, 209, 209, 0.8);
--normal-font-color-50: rgba(209, 209, 209, 0.5);
--normal-font-color-30: rgba(209, 209, 209, 0.3);
--normal-font-color-12: rgba(209, 209, 209, 0.12);
--hover-color: #404040; /* subtle change for hover */
--heading-font-color: #9fb3c7; /* slightly brighter for better readability */
--link-font-color: #9fb3c7;
--top-heading-font-color: rgb(200, 106, 0);
--link-font-color: rgb(200, 106, 0);
--banner-color: #04a577; /* a cooler shade for the banner */
--opposite-bg-color: #fafafa; /* off-white */
--opposite-bg-color-80: rgba(250, 250, 250, 0.8);
@ -75,9 +85,8 @@ ol {
/* Links */
a {
color: var(--link-font-color);
text-decoration: underline;
font-weight: 700;
color: var(--normal-font-color);
}
/* Tabellen */
@ -92,16 +101,6 @@ table {
right 0.5s ease, transform 0.5s ease;
}
th,
td {
padding: 10px;
text-align: left;
border: 1px solid #ccc;
}
th {
background-color: var(--background-color-90);
}
button {
background-color: inherit;
border: none;

@ -1,6 +1,6 @@
<script lang="ts">
import { navigate } from "svelte-routing"
import { serviceNavigation, sites } from "../stores"
import { serviceNavigation, sites, darkMode } from "../stores"
</script>
<div class="footer">
@ -37,12 +37,20 @@
{/each}
</div>
<button
class="darklight"
on:click="{() => {
const body = document.querySelector('body')
if (body) body.classList.toggle('darkTheme')
if (body) {
body.classList.toggle('darkTheme')
}
$darkMode = !$darkMode
}}"
>
toggletheme
{#if !$darkMode}
<img src="/media/dark.svg" alt="darkmode" />
{:else}
<img src="/media/light.svg" alt="lightmode" />
{/if}
</button>
</div>
@ -51,6 +59,15 @@
.social {
margin-top: 20px;
}
.darklight {
width: 36px;
height: 36px;
object-fit: contain;
img {
width: 100%;
height: 100%;
}
}
.footer {
margin-top: 60px;
width: 100vw;

@ -1,5 +1,5 @@
<script lang="ts">
import { navigation, sites } from "../../stores"
import { darkMode, navigation, sites } from "../../stores"
import { apiBaseURL } from "../../../config"
import { navigate } from "svelte-routing"
@ -60,9 +60,11 @@
navigate('/')
}}"
>
<button class="img-logo-container"><img src="/media/logo.svg" alt="logo" /></button>
<button class="img-logo-container">
<img src="/media/logo.svg" alt="logo" />
</button>
<div class="logo-text">
<p id="upper">ALL KIDS</p>
<p id="upper"><span class="yellow">ALL</span> <span class="red">KIDS</span></p>
<p id="lower">SO GÜNSTIG WIE NACHHALTIG</p>
</div>
</button>
@ -202,12 +204,22 @@
text-align: left;
}
& > #upper {
font-family: "Orbitron" !important;
font-size: 30px;
font-weight: 700;
span {
font-family: "Orbitron" !important;
font-size: 31px;
font-weight: 700;
&.red {
color: red;
}
&.yellow {
color: yellow;
}
}
}
& > #lower {
font-size: 13px;
font-weight: bold;
color: orange;
}
font-weight: bold;
font-size: 1.2rem;
@ -258,7 +270,7 @@
overflow: hidden;
width: 100vw;
z-index: 2000;
background-color: var(--opposite-bg-color-80);
background-color: rgba(0, 0, 0, 0.849);
opacity: 0;
.inner-container {
width: 100%;

@ -27,7 +27,6 @@
}
function imageSlide(images: HTMLImageElement[]) {
console.log(images)
let currentImage = 0
images[0].classList.add("show-img")
let interval = setInterval(() => {
@ -59,9 +58,11 @@
navigate('/')
}}"
>
<button class="img-logo-container"><img src="/media/logo.svg" alt="logo" /></button>
<button class="img-logo-container">
<img src="/media/logo.svg" alt="logo" />
</button>
<div class="logo-text">
<p id="upper">ALL KIDS</p>
<p id="upper"><span class="yellow">ALL</span> <span class="red">KIDS</span></p>
<p id="lower">SO GÜNSTIG WIE NACHHALTIG</p>
</div>
</button>
@ -241,12 +242,22 @@
text-align: left;
}
& > #upper {
font-family: "Orbitron" !important;
font-size: 24px;
font-weight: 700;
span {
font-family: "Orbitron" !important;
font-size: 31px;
font-weight: 700;
&.red {
color: red;
}
&.yellow {
color: yellow;
}
}
}
& > #lower {
font-size: 10px;
font-size: 13px;
font-weight: bold;
color: orange;
}
font-weight: bold;
font-size: 1.2rem;

@ -23,7 +23,6 @@
}
Object.assign(swiper, params)
console.log(swiper.getElementsByClassName("swiper-button-prev"), "test", swiper)
swiper.initialize()
// Add the 'active' class to the h1 of the first slide
@ -159,6 +158,7 @@
line-height: 1;
font-weight: 500;
position: relative;
color: var(--heading-font-color);
}
h2 {
@ -168,6 +168,7 @@
}
line-height: 1;
font-weight: 500;
color: var(--top-heading-font-color);
}
}
.description {

@ -0,0 +1,399 @@
<script lang="ts">
import Form from "./form.svelte"
import MobileForm from "./mobileForm.svelte"
import { writable } from "svelte/store"
import { validateFields } from "../../../functions/validateFields"
import { sendForm } from "../../../functions/sendForm"
import { navigate } from "svelte-routing"
import { onMount } from "svelte"
export let col: Column
export let siteId: string
export let rowNr: number
export let row: Row
export let rows: Row[]
let formSend = false
let formValues = writable<FormValues>({})
function submitForm() {
const values: Array<ValueEntry> = Object.entries($formValues).map((entry: ObjectEntry) => {
const key: string = entry[0]
const value: CustomHTMLElement = entry[1]
if (!key.includes("numberLabel")) {
return [key, [value.checked || value.value, value.required]] as ValueEntry
} else {
return [key, [value.value, value, value.getAttribute("name"), value.required]] as ValueEntry
}
})
const fields: Array<ValueEntry> = values.filter((entry: ValueEntry) => !entry[0].includes("label"))
const validation = validateFields(fields)
if (validation.length) {
validation.forEach((error) => {
// @ts-ignore
if (error[0].includes("block")) {
// @ts-ignore
error[1]()
} else {
// @ts-ignore
$formValues[error[1]].classList.add("invalid")
const label = $formValues[`${error[1]}_label`]
const errorElement = document.createElement("div")
errorElement.className = "error-message"
errorElement.textContent = error[0] as string
label?.appendChild(errorElement)
}
})
} else {
// @ts-ignore
const formObj: FormObj = {}
fields.forEach((entry) => {
if (Array.isArray(entry[1]) && entry[1].length == 4) {
// @ts-ignore
if (entry[1][0]) formObj[entry[0]] = entry[1]
} else {
if (!entry[1][0] && !entry[1][1]) return
// @ts-ignore
formObj[entry[0]] = entry[1][0]
}
})
let form: any
row.column.forEach((col) => {
if (col.contentType == "form") form = col
})
if (!form) return
formObj["formRows"] = form.formRows.map((r: FormRow) => r.rowName)
formObj["formTitle"] = form.formEmailTitle
formSend = true
const hny = document.getElementById("hny") as HTMLInputElement
if (hny) formObj["honey"] = hny.checked
sendForm(formObj)
}
}
let innerWidth = window.innerWidth
onMount(() => {
const handleResize = () => {
innerWidth = window.innerWidth
}
window.addEventListener("resize", handleResize)
// Cleanup function
return () => {
window.removeEventListener("resize", handleResize)
}
})
</script>
{#if formSend}
<div class="success-message">
<h1>Formular erfolgreich gesendet!</h1>
<p>Vielen Dank für Ihre Anfrage. Wir werden uns in Kürze bei Ihnen melden.</p>
</div>
{:else}
<form
class="form-rows"
on:submit="{(e) => {
e.preventDefault()
submitForm()
}}"
>
{#each col.formRows ?? [] as formRow, i}
{#if innerWidth < 768}
<MobileForm formRow="{formRow}" formValues="{formValues}" index="{i}" />
{:else}
<Form formRow="{formRow}" formValues="{formValues}" index="{i}" />
{/if}
{/each}
<div class="row additional">
<div class="data-protection">
<label bind:this="{$formValues[`agreement_label`]}">
<input
required="{true}"
class="checkit"
type="checkbox"
on:change="{(e) => {
let element = e.currentTarget
element.classList.remove('invalid')
}}"
bind:this="{$formValues['agreement']}"
/>
<span class="checkit-span"></span>
</label>
<div class="datasec">
<button on:click|preventDefault="{() => navigate('/datenschutz')}" class="link">
Datenschutz
</button>
akzeptieren
</div>
</div>
<input
type="checkbox"
name="contact_me_by_fax_only"
id="hny"
value="1"
style="display:none !important"
tabindex="-1"
autocomplete="off"
/>
<button class="submit-request" type="submit">Anfrage absenden</button>
</div>
</form>
{/if}
<style lang="less" global>
@import "../../../assets/css/variables.less";
input,
select,
textarea,
.data-protection {
margin: 5px 0px;
box-shadow: 0 0 25px 10px var(--opposite-bg-color-5);
}
.success-message {
h1 {
color: var(--heading-font-color);
font-size: 36px;
margin-top: 50px;
}
p {
font-size: 20px;
margin-top: 20px;
}
}
.invalidBlocks {
border: 2px solid rgb(255, 0, 0) !important;
position: relative;
&::after {
font-size: 0.9rem !important;
color: red !important;
position: absolute;
bottom: 2px;
content: "Bitte wähle entweder eine Kartenanzahl oder einen Wunschbetrag aus.";
}
}
.border-red {
border-color: red !important;
}
.no-margin {
margin-top: 15px !important;
}
.invalid {
border: 2px solid red !important;
}
.error-message {
font-size: 0.9rem !important;
color: red !important;
position: absolute;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
height: 100%;
}
input[type="number"] {
-moz-appearance: textfield;
appearance: textfield;
}
.checkit {
display: none;
}
.checkit-span {
height: 20px;
width: 20px;
border: 1px solid grey;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
[type="checkbox"]:checked + span:before {
content: "\2714";
position: absolute;
transform-origin: bottom;
}
.form-rows {
display: flex;
flex-direction: column;
gap: 1.5rem;
width: 100%;
h3 {
font-weight: bold !important;
}
.form-row {
display: flex;
flex-direction: column;
width: 100%;
gap: 1.5rem;
.date {
max-width: 100%;
box-sizing: border-box;
}
label {
width: 100% !important;
font-size: inherit;
input,
select,
textarea {
width: 100%;
font-size: inherit;
}
}
.form-cols {
display: flex;
gap: 1.5rem;
width: 100%;
}
}
.form-column {
display: flex;
flex: 1;
flex-direction: column;
justify-content: flex-start;
width: 100%;
gap: 1rem;
border-radius: 4px;
}
h3 {
margin-bottom: 0.5rem;
font-size: 1.2rem;
font-weight: bold;
}
p {
margin-bottom: 0.5rem;
color: var(--hover-color);
}
input,
select,
textarea,
.data-protection {
padding: 10px 20px;
border: 0px solid var(--opposite-bg-color);
border-bottom: 3px solid var(--heading-font-color);
outline: 0px solid var(--opposite-bg-color);
color: var(--opposite-bg-color);
background-color: var(--background-color);
resize: none;
}
.data-protection {
display: flex;
flex-wrap: nowrap;
gap: 5px !important;
justify-content: start;
flex-direction: row !important;
}
input[type="date"] {
font-family: inherit;
width: 100% !important;
position: relative;
}
select {
padding: 10px 20px;
border: 0;
border-bottom: 3px solid var(--heading-font-color);
outline: none;
}
select:focus {
border-bottom-color: var(--heading-font-color);
}
#time-select {
appearance: none;
background-image: url("../../../../../media/clock.svg");
background-repeat: no-repeat;
background-position: right 20px center;
background-size: 18px;
option {
padding: 10px 20px;
background-color: var(--background-color);
}
}
@media @mobile {
.date {
width: 100vw !important;
}
}
@media @tablet {
.date {
width: 100% !important;
}
.form-cols {
flex-direction: row;
}
}
}
i .max-width {
max-width: @body-maxwidth !important;
}
.datasec {
display: flex;
align-items: center !important;
.link {
height: 100%;
display: flex;
align-items: flex-end !important;
text-decoration: underline;
margin-right: 3px;
color: rgb(14, 91, 146);
}
}
.additional {
display: flex;
@media @mobile {
flex-direction: column !important;
}
@media @tablet {
flex-direction: row !important;
}
gap: 2.5rem;
div {
flex: 1;
display: flex;
align-items: center;
justify-content: start;
}
.submit-request {
flex: 1;
box-shadow: 0 0 25px 10px var(--opposite-bg-color-5);
width: 100%;
display: flex;
align-items: center;
justify-content: start;
background-color: var(--heading-font-color);
color: var(--background-color);
padding: 10px 20px;
}
}
</style>

@ -0,0 +1,18 @@
<script lang="ts">
import type { Writable } from "svelte/store"
export let label = "" // Der Text, der neben der Checkbox angezeigt wird
export let bindValue: any // Der Wert der Checkbox
export let formValues: Writable<FormValues> // Das formValues-Objekt aus dem übergeordneten Komponenten
export let id = "" // Eindeutige ID für die Checkbox, um den Label korrekt zuzuweisen
export let name = "" // Name des Formularelements
</script>
<label class="checkbox-container" for="{id}">
<input type="checkbox" bind:checked="{bindValue}" id="{id}" name="{name}" bind:this="{formValues[name]}" />
<span>{label}</span>
</label>
<style>
/* Sie können hier den Stil für Ihre Checkbox anpassen */
</style>

@ -0,0 +1,48 @@
<script lang="ts">
import type { Writable } from "svelte/store"
export let groupTitle: string
export let checkboxes: { name: string; emailName: string }[]
export let formValues: Writable<FormValues>
export let rowNr: number
</script>
<div class="checkbox-group">
<h3>{groupTitle}</h3>
<div class="containerr">
{#each checkboxes as checkbox, i}
<label class="checkbox-label">
<input
type="checkbox"
class="checkbox-input checkit"
bind:this="{$formValues[`checkbox_${groupTitle}_${rowNr}_${checkbox.emailName}`]}"
/>
<span class="checkit-span"></span>
{checkbox.name}
</label>
{/each}
</div>
</div>
<style lang="less">
.checkbox-group {
.checkbox-input {
vertical-align: middle;
margin-right: 8px;
width: fit-content !important;
}
.checkbox-label {
width: 100%;
vertical-align: middle;
align-items: center;
display: flex;
gap: 10px;
}
& > .containerr {
margin-top: 10px;
display: flex;
flex-direction: column;
gap: 10px;
}
}
</style>

@ -0,0 +1,131 @@
<script lang="ts">
import { CalendarView } from "fluent-svelte"
export let groupTitle: string
export let datePickerProps: DatePickerProps
export let formValues: any
export let rowNr: number
export let formCol: FormColumn
const today = new Date()
const oneYearFromNow = new Date(today)
oneYearFromNow.setFullYear(today.getFullYear() + 1)
const indexToDay: Record<number, string> = {
0: "sunday",
1: "monday",
2: "tuesday",
3: "wednesday",
4: "thursday",
5: "friday",
6: "saturday",
}
function generateBlackoutDates(props: DatePickerProps): Date[] {
let blackoutDates = []
// Funktion, um einen ISO-String in ein vereinfachtes Date-Objekt umzuwandeln
const dateFromISOString = (isoString: string) => {
const date = new Date(isoString)
return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()))
}
const dayToIndex: Record<string, number> = {
sunday: 0,
monday: 1,
tuesday: 2,
wednesday: 3,
thursday: 4,
friday: 5,
saturday: 6,
}
// Initialisiere ein aktuelles Datum auf heute
let currentDate = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate()))
// Schleife durch die nächsten 365 Tage
for (let i = 0; i <= 365; i++) {
// Überprüfen, ob dieses Datum erlaubt ist
const isAllowed = props.allowedDateRanges.some((range) => {
const fromDate = dateFromISOString(range.from)
const toDate = dateFromISOString(range.to)
return currentDate >= fromDate && currentDate <= toDate
})
// Überprüfen, ob der Wochentag ausgeschlossen ist
const currentDayIndex = currentDate.getUTCDay()
const currentDayString = indexToDay[currentDayIndex]
const isExcluded = props.excludeDays?.includes(currentDayString)
// Wenn nicht erlaubt oder ausgeschlossen, zu den Blackout-Daten hinzufügen
if (!isAllowed || isExcluded) {
blackoutDates.push(new Date(currentDate))
}
// Zum nächsten Tag wechseln
currentDate.setUTCDate(currentDate.getUTCDate() + 1)
}
return blackoutDates
}
let value = new Date()
const blackoutDates = generateBlackoutDates(datePickerProps)
function setFormValues() {
$formValues[`datepicker_${groupTitle}_${rowNr}_${formCol.datePickerEmailTitle}`] = {
value: value.toLocaleDateString("de-DE"),
required: !formCol.datePickerNotRequired,
}
}
//needs email conntection
$: {
if (value) {
setFormValues()
}
}
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="container">
<div
class="datepicker"
on:click|preventDefault|stopPropagation
on:keydown|stopPropagation
on:submit|stopPropagation|preventDefault
>
<CalendarView
weekStart="{1}"
bind:vaue="{value}"
on:change="{(e) => {
value = e.detail
}}"
min="{today}"
max="{oneYearFromNow}"
blackout="{blackoutDates}"
locale="de-DE"
/>
</div>
</div>
<style lang="less">
@import url("https://unpkg.com/fluent-svelte/theme.css");
.container {
width: 100%;
display: flex;
justify-content: center;
--fds-solid-background-quarternary: var(--background-color);
--fds-text-primary: var(--normal-font-color);
--fds-text-secondary: var(--normal-font-color-80);
--fds-text-tertiary: var(--normal-font-color-50);
--fds-text-disabled: var(--normal-font-color-30);
--fds-accent-disabled: var(--opposite-bg-color-5);
--fds-control-strong-stroke-default: var(--opposite-bg-color-80);
.datepicker {
width: fit-content;
margin: 5px 0px;
box-shadow: 0 0 25px 10px var(--opposite-bg-color-5);
}
}
</style>

@ -1,4 +1,6 @@
<script lang="ts">
import CheckboxGroup from "./checkboxGroup.svelte"
import Datepicker from "./datepicker.svelte"
import FormLabelNumberBlock from "./formLabelNumberBlock.svelte"
import type { Writable } from "svelte/store"
@ -24,6 +26,9 @@
</script>
<div class="form-row">
<h3 style="margin-bottom: -0.5rem;">
{#if formRow.showRowName}{formRow.rowName || ""}{/if}
</h3>
<div class="form-cols">
{#each formRow.columns as column, columnIndex}
<div class="form-column">
@ -48,7 +53,7 @@
on:change="{removeInvalid}"
>
<option value="" disabled selected>Bitte Uhrzeit wählen</option>
{#each column?.times || [] as time}
{#each column?.times ?? [] as time}
<option value="{time?.timeFrom}-{time?.timeTo}">
{time?.timeFrom} - {time?.timeTo}
</option>
@ -114,49 +119,70 @@
</label>
{/if}
{#each column.text || [] as textField, textFieldIndex}
{#if textField?.textArea}
<label bind:this="{$formValues[`textarea_Nachricht_label`]}">
<textarea
placeholder="{textField?.textPlaceholder}"
required="{textField?.notRequired !== true}"
on:change="{removeInvalid}"
{#if column.showCheckboxGroup}
<CheckboxGroup
checkboxes="{column.checkboxes}"
groupTitle="{column.groupTitle}"
formValues="{formValues}"
rowNr="{index}"
/>
{/if}
{#if column.showDatePicker}
<Datepicker
datePickerProps="{column.datePickerProps}"
groupTitle="{column.groupTitle}"
formValues="{formValues}"
rowNr="{index}"
formCol="{column}"
/>
{/if}
{#each column.text ?? [] as textField, textFieldIndex}
<div>
<h3 class="textTitle">{textField.textTitle || ""}</h3>
{#if textField?.textArea}
<label bind:this="{$formValues[`textarea_Nachricht_label`]}">
<textarea
placeholder="{textField?.textPlaceholder}"
required="{textField?.notRequired !== true}"
on:change="{removeInvalid}"
bind:this="{$formValues[
`textarea_Nachricht_${textField.textfieldOrder}_${index}_${
textField.emailName || columnIndex + 'invalidtext' + textFieldIndex
}`
]}"></textarea>
</label>
{:else}
<label
bind:this="{$formValues[
`textarea_Nachricht_${textField.textfieldOrder}_${index}_${
textField.emailName || columnIndex + 'invalidtext' + textFieldIndex
}`
]}"></textarea>
</label>
{:else}
<label
bind:this="{$formValues[
`input_${column.title ? column.title + '_' : ''}${textField?.textPlaceholder}_label`
]}"
>
<input
type="text"
placeholder="{textField?.textPlaceholder}"
on:change="{removeInvalid}"
required="{textField?.notRequired !== true}"
bind:this="{$formValues[
`${
textField?.telValidation
? 'Telefon'
: textField?.emailValidation
? 'Email'
: 'input'
}_${column.title ? column.title + '_' : ''}${textField?.textPlaceholder}_${
textField.textfieldOrder
}_${index}_${textField.emailName || columnIndex + 'invalidtext' + textFieldIndex}`
`input_${column.title ? column.title + '_' : ''}${textField?.textPlaceholder}_label`
]}"
/>
</label>
{/if}
>
<input
type="text"
placeholder="{textField?.textPlaceholder}"
on:change="{removeInvalid}"
required="{textField?.notRequired !== true}"
bind:this="{$formValues[
`${
textField?.telValidation
? 'Telefon'
: textField?.emailValidation
? 'Email'
: 'input'
}_${column.title ? column.title + '_' : ''}${textField?.textPlaceholder}_${
textField.textfieldOrder
}_${index}_${
textField.emailName || columnIndex + 'invalidtext' + textFieldIndex
}`
]}"
/>
</label>
{/if}
</div>
{/each}
</div>
{/each}
</div>
</div>
<style lang="less">
</style>

@ -1,7 +1,8 @@
<script lang="ts">
import FormLabelNumberBlock from "./formLabelNumberBlock.svelte"
import type { Writable } from "svelte/store"
import CheckboxGroup from "./checkboxGroup.svelte"
import Datepicker from "./datepicker.svelte"
export let formRow: FormRow
export let formValues: Writable<FormValues>
export let index: number
@ -126,46 +127,69 @@
</label>
</div>
{/if}
{#if column.showCheckboxGroup}
<CheckboxGroup
checkboxes="{column.checkboxes}"
groupTitle="{column.groupTitle}"
formValues="{formValues}"
rowNr="{index}"
/>
{/if}
{#if column.showDatePicker}
<Datepicker
datePickerProps="{column.datePickerProps}"
groupTitle="{column.groupTitle}"
formValues="{formValues}"
rowNr="{index}"
formCol="{column}"
/>
{/if}
{#each column.text ?? [] as textField, textFieldIndex}
<div class="column-{columnIndex} position-{getPosition(column, 5 + textFieldIndex, textFieldIndex)}">
{#if textField?.textArea}
<label bind:this="{$formValues[`textarea_Nachricht_label`]}">
<textarea
placeholder="{textField?.textPlaceholder}"
required="{textField?.notRequired !== true}"
on:change="{removeInvalid}"
<h3 class="textTitle">{textField.textTitle || ""}</h3>
<div
class="column-{columnIndex} position-{getPosition(column, 5 + textFieldIndex, textFieldIndex)}"
>
{#if textField?.textArea}
<label bind:this="{$formValues[`textarea_Nachricht_label`]}">
<textarea
placeholder="{textField?.textPlaceholder}"
required="{textField?.notRequired !== true}"
on:change="{removeInvalid}"
bind:this="{$formValues[
`textarea_Nachricht_${textField.textfieldOrder}_${index}_${
textField.emailName || columnIndex + 'invalidtext' + textFieldIndex
}`
]}"></textarea>
</label>
{:else}
<label
bind:this="{$formValues[
`textarea_Nachricht_${textField.textfieldOrder}_${index}_${
textField.emailName || columnIndex + 'invalidtext' + textFieldIndex
}`
]}"></textarea>
</label>
{:else}
<label
bind:this="{$formValues[
`input_${column.title ? column.title + '_' : ''}${textField?.textPlaceholder}_label`
]}"
>
<input
type="text"
placeholder="{textField?.textPlaceholder}"
on:change="{removeInvalid}"
required="{textField?.notRequired !== true}"
bind:this="{$formValues[
`${
textField?.telValidation
? 'Telefon'
: textField?.emailValidation
? 'Email'
: 'input'
}_${column.title ? column.title + '_' : ''}${textField?.textPlaceholder}_${
textField.textfieldOrder
}_${index}_${textField.emailName || columnIndex + 'invalidtext' + textFieldIndex}`
`input_${column.title ? column.title + '_' : ''}${textField?.textPlaceholder}_label`
]}"
/>
</label>
{/if}
>
<input
type="text"
placeholder="{textField?.textPlaceholder}"
on:change="{removeInvalid}"
required="{textField?.notRequired !== true}"
bind:this="{$formValues[
`${
textField?.telValidation
? 'Telefon'
: textField?.emailValidation
? 'Email'
: 'input'
}_${column.title ? column.title + '_' : ''}${textField?.textPlaceholder}_${
textField.textfieldOrder
}_${index}_${
textField.emailName || columnIndex + 'invalidtext' + textFieldIndex
}`
]}"
/>
</label>
{/if}
</div>
</div>
{/each}
{/each}

@ -7,81 +7,20 @@
import MainPicture from "./mainPicture.svelte"
import TextContent from "./textContent.svelte"
import Table from "./table.svelte"
import Form from "./form/form.svelte"
import MobileForm from "./form/mobileForm.svelte"
import Formular from "./form/Formular.svelte"
import { writable } from "svelte/store"
import { validateFields } from "../../functions/validateFields"
import { sendForm } from "../../functions/sendForm"
import GoogleMaps from "../googleMaps.svelte"
import { navigate } from "svelte-routing"
import IconInfoBoard from "./iconInfoBoard.svelte"
import Boxlist from "./boxlist.svelte"
import ProductPreview from "./productPreview.svelte"
export let siteId: string
export let rowNr: number
let formSend = false
let formValues = writable<FormValues>({})
function getRowClass(row: Row) {
if (row.maxWidth || row.column.some((col) => col.iconBackgroundImage)) return "max-width"
return row.column.some((col) => col.contentType === "mainPicture") ? "max-width" : "small-max-width"
}
function submitForm() {
const values = Object.entries($formValues).map((entry) => {
return [
entry[0],
!entry[0].includes("numberLabel")
? [entry[1].checked || entry[1].value, entry[1].required]
: [entry[1].value, entry[1], entry[1].getAttribute("name"), entry[1].required],
]
})
const fields = values.filter((entry) => !entry[0].includes("label"))
const validation = validateFields([...fields])
if (validation.length) {
validation.forEach((error) => {
// @ts-ignore
if (error[0].includes("block")) {
// @ts-ignore
error[1]()
} else {
// @ts-ignore
$formValues[error[1]].classList.add("invalid")
const label = $formValues[`${error[1]}_label`]
const errorElement = document.createElement("div")
errorElement.className = "error-message"
errorElement.textContent = error[0] as string
label?.appendChild(errorElement)
}
})
} else {
const formObj: any = {}
fields.forEach((entry) => {
if (Array.isArray(entry[1]) && entry[1].length == 4) {
// @ts-ignore
if (entry[1][0]) formObj[entry[0]] = entry[1]
} else {
if (!entry[1][0] && !entry[1][1]) return
// @ts-ignore
formObj[entry[0]] = entry[1][0]
}
})
let form: any
row.column.forEach((col) => {
if (col.contentType == "form") form = col
})
if (!form) return
formObj["formRows"] = form.formRows.map((r: FormRow) => r.rowName)
formObj["formTitle"] = form.formEmailTitle
formSend = true
const hny = document.getElementById("hny") as HTMLInputElement
if (hny) formObj["honey"] = hny.checked
sendForm(formObj)
}
}
let innerWidth = window.innerWidth
</script>
<main class="row" name="{row.title}" class:max-width="{getRowClass(row) === 'max-width'}" id="{`RowNr` + rowNr}">
@ -112,63 +51,7 @@
<GoogleMaps />
{/if}
{:else if col.contentType == "form"}
{#if formSend}
<div class="success-message">
<h1>Formular erfolgreich gesendet!</h1>
<p>Vielen Dank für Ihre Anfrage. Wir werden uns in Kürze bei Ihnen melden.</p>
</div>
{:else}
<form
class="form-rows"
on:submit="{(e) => {
e.preventDefault()
submitForm()
}}"
>
{#each col.formRows ?? [] as formRow, i}
{#if innerWidth < 768}
<MobileForm formRow="{formRow}" formValues="{formValues}" index="{i}" />
{:else}
<Form formRow="{formRow}" formValues="{formValues}" index="{i}" />
{/if}
{/each}
<div class="row additional">
<div class="data-protection">
<label bind:this="{$formValues[`agreement_label`]}">
<input
required="{true}"
class="checkit"
type="checkbox"
on:change="{(e) => {
let element = e.currentTarget
element.classList.remove('invalid')
}}"
bind:this="{$formValues['agreement']}"
/>
<span class="checkit-span"></span>
</label>
<div class="datasec">
<button on:click|preventDefault="{() => navigate('/datenschutz')}" class="link">
Datenschutz
</button>
akzeptieren
</div>
</div>
<input
type="checkbox"
name="contact_me_by_fax_only"
id="hny"
value="1"
style="display:none !important"
tabindex="-1"
autocomplete="off"
/>
<button class="submit-request" type="submit">Anfrage absenden</button>
</div>
</form>
{/if}
<Formular col="{col}" siteId="{siteId}" rowNr="{rowNr}" row="{row}" rows="{rows}" />
{/if}
{/each}
</div>
@ -176,45 +59,6 @@
<style lang="less" global>
@import "../../assets/css/variables.less";
.wave-placeholder {
height: 10px;
}
input,
select,
textarea,
.data-protection {
margin: 5px 0px;
box-shadow: 0 0 25px 10px var(--opposite-bg-color-5);
}
.success-message {
h1 {
color: var(--heading-font-color);
font-size: 36px;
margin-top: 50px;
}
p {
font-size: 20px;
margin-top: 20px;
}
}
.invalidBlocks {
border: 2px solid rgb(255, 0, 0) !important;
position: relative;
&::after {
font-size: 0.9rem !important;
color: red !important;
position: absolute;
bottom: 2px;
content: "Bitte wähle entweder eine Kartenanzahl oder einen Wunschbetrag aus.";
}
}
.border-red {
border-color: red !important;
}
.row-title {
font-size: 1.6rem;
@ -234,15 +78,6 @@
margin-top: 15px !important;
}
.invalid {
border-bottom: 2px solid red !important;
}
.error-message {
font-size: 0.9rem !important;
color: red !important;
position: absolute;
}
.row {
width: 100%;
padding: 0px 0px 15px 0;
@ -251,18 +86,6 @@
align-items: center;
max-width: @body-small-maxwidth;
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
height: 100%;
}
input[type="number"] {
-moz-appearance: textfield;
appearance: textfield;
}
ul {
list-style-type: none; /* Remove default bullet points */
padding: 0;
@ -290,175 +113,6 @@
& > h3 {
width: 100%;
}
.checkit {
display: none;
}
.checkit-span {
height: 20px;
width: 20px;
border: 1px solid grey;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
[type="checkbox"]:checked + span:before {
content: "\2714";
position: absolute;
transform-origin: bottom;
}
&.additional {
display: flex;
@media @mobile {
flex-direction: column;
}
@media @tablet {
flex-direction: row;
}
gap: 2.5rem;
div {
flex: 1;
display: flex;
align-items: center;
justify-content: start;
}
.submit-request {
flex: 1;
box-shadow: 0 0 25px 10px var(--opposite-bg-color-5);
width: 100%;
display: flex;
align-items: center;
justify-content: start;
background-color: var(--heading-font-color);
color: var(--background-color);
padding: 10px 20px;
}
}
.form-rows {
display: flex;
flex-direction: column;
gap: 1.5rem;
width: 100%;
h3 {
font-weight: bold !important;
}
.form-row {
display: flex;
flex-direction: column;
width: 100%;
gap: 1.5rem;
.date {
max-width: 100%;
box-sizing: border-box;
}
label {
width: 100% !important;
font-size: inherit;
input,
select,
textarea {
width: 100%;
font-size: inherit;
}
}
.form-cols {
display: flex;
gap: 1.5rem;
width: 100%;
}
}
.form-column {
display: flex;
flex: 1;
flex-direction: column;
justify-content: flex-end;
gap: 1rem;
border-radius: 4px;
}
h3 {
margin-bottom: 0.5rem;
font-size: 1.2rem;
font-weight: bold;
}
p {
margin-bottom: 0.5rem;
color: var(--hover-color);
}
input,
select,
textarea,
.data-protection {
padding: 10px 20px;
border: 0px solid var(--opposite-bg-color);
border-bottom: 3px solid var(--heading-font-color);
outline: 0px solid var(--opposite-bg-color);
color: var(--opposite-bg-color);
background-color: var(--background-color);
resize: none;
}
.data-protection {
display: flex;
flex-wrap: nowrap;
gap: 5px !important;
justify-content: start;
flex-direction: row !important;
}
input[type="date"] {
font-family: inherit;
width: 100% !important;
position: relative;
}
select {
padding: 10px 20px;
border: 0;
border-bottom: 3px solid var(--heading-font-color);
outline: none;
}
select:focus {
border-bottom-color: var(--heading-font-color);
}
#time-select {
appearance: none;
background-image: url("../../../../media/clock.svg");
background-repeat: no-repeat;
background-position: right 20px center;
background-size: 18px;
option {
padding: 10px 20px;
background-color: var(--background-color);
}
}
@media @mobile {
.date {
width: 100vw !important;
}
}
@media @tablet {
.date {
width: 100% !important;
}
.form-cols {
flex-direction: row;
}
}
}
@media @mobile {
.no-gap {
@ -507,17 +161,4 @@
.max-width {
max-width: @body-maxwidth !important;
}
.datasec {
display: flex;
align-items: center !important;
.link {
height: 100%;
display: flex;
align-items: flex-end !important;
text-decoration: underline;
margin-right: 3px;
color: rgb(14, 91, 146);
}
}
</style>

@ -7,7 +7,6 @@
register(false)
let swiper: any
console.log("TST RENDERED")
onMount(async () => {
if (swiper) {
const response = await fetch("/dist/index.css")

@ -1,4 +1,4 @@
export function validateFields(fieldsArray: (string | boolean | any[])[][]): (string | (() => void))[][] {
export function validateFields(fieldsArray: ValueEntry[]): (string | (() => void))[][] {
const errors = []
let selectedGroup: number
const numberRegex = /^[+]?([.]\d+|\d+([.]\d+)?)$/
@ -23,18 +23,20 @@ export function validateFields(fieldsArray: (string | boolean | any[])[][]): (st
if (field === "blockGroups" || (typeof field === "string" && field.includes("numberLabel"))) {
if (!field.includes("numberLabel")) return
// @ts-ignore
const [elementValue, element, group, boolean] = value
let [elementValue, element, group, boolean] = value
element = element as HTMLElement
if (!elementValue) return
if (selectedGroup !== undefined) {
if (group !== selectedGroup) errors.push(["block", wholeBlockInvalid])
if (group !== String(selectedGroup)) errors.push(["block", wholeBlockInvalid])
else validateNumber(elementValue, field, element)
} else {
//@ts-ignore
selectedGroup = group
validateNumber(elementValue, field, element)
}
return
}
value = value as any[]
value = value
let required = value[1]
value = value[0]
if (!required && !value) return
@ -43,16 +45,16 @@ export function validateFields(fieldsArray: (string | boolean | any[])[][]): (st
} else if (typeof field === "string") {
if (field.includes("number_")) {
if (!numberRegex.test(`${value}`)) errors.push(["Ungültiger numerischer Wert.", field])
} else if (field.includes("agreement_")) {
} else if (field.includes("agreement_") && typeof value == "boolean") {
if (value !== true) errors.push(["Bitte das Kontrollkästchen anklicken.", field])
} else if (field.includes("Email_")) {
if (!emailRegex.test(value as string)) errors.push(["Ungültiges E-Mail-Format.", field])
} else if (field.includes("date_")) {
if (!dateRegex.test(value as string)) errors.push(["Ungültiges Datumsformat.", field])
} else if (field.includes("times_")) {
if (!timeRegex.test(value as string)) errors.push(["Ungültiges Zeitformat.", field])
} else if (field.includes("Telefon_")) {
if (!phoneRegex.test(value as string)) errors.push(["Ungültiges Telefonnummernformat.", field])
} else if (field.includes("Email_") && typeof value == "string") {
if (!emailRegex.test(value)) errors.push(["Ungültiges E-Mail-Format.", field])
} else if (field.includes("date_") && typeof value == "string") {
if (!dateRegex.test(value)) errors.push(["Ungültiges Datumsformat.", field])
} else if (field.includes("times_") && typeof value == "string") {
if (!timeRegex.test(value) && typeof value == "string") errors.push(["Ungültiges Zeitformat.", field])
} else if (field.includes("Telefon_") && typeof value == "string") {
if (!phoneRegex.test(value)) errors.push(["Ungültiges Telefonnummernformat.", field])
}
}
})

@ -14,4 +14,4 @@ export let navigation = writable<Navigation>()
export let sites = writable<Sites>({})
export let serviceNavigation = writable<Navigation>()
export let banner = writable<Banner[]>([])
export let temperature = writable<Temperature>()
export let darkMode = writable<boolean>(false)

@ -6,7 +6,6 @@
let site: Site
function getSelectedSite(path: string): Site | {} {
let site = {}
console.log($sites, "sites")
if (Object.values($sites).length == 0) return {}
Object.values($sites).forEach((element) => {
if (element.path == "/" + path) site = element

@ -27,6 +27,7 @@
"connect-history-api-fallback": "^2.0.0",
"esbuild": "^0.19.3",
"esbuild-svelte": "^0.8.0",
"fluent-svelte": "^1.6.0",
"http-proxy-middleware": "^2.0.6",
"less": "^4.2.0",
"morgan": "^1.10.0",

52
types/global.d.ts vendored

@ -127,10 +127,45 @@ interface VideoSwitch {
link: string
}
interface FormObj {
formRows?: string[]
index?: number
formTitle?: string
formValues?: Writable<FormValues>
honey?: boolean
agreement?: boolean
}
// Before transformation
interface TempFormBefore {
[rowName: string]: TempFormRowBefore
}
interface TempFormRowBefore {
[newKey: string]: [any, boolean, boolean]
}
// After transformation
interface TempFormAfter {
[rowName: string]: Array<[string, [any, boolean, boolean]]>
}
interface TempFormIndices {
[rowName: string]: {
[newKey: string]: number
}
}
type ValueEntry =
| [string, [boolean | string | any, boolean]]
| [string, [any, CustomHTMLElement, string | null, boolean]]
type ObjectEntry = [string, CustomHTMLElement]
interface FormRow {
title?: string
rowName: string
columns: FormColumn[]
showRowName: boolean
}
interface Block {
@ -174,7 +209,15 @@ interface FormColumn {
emailNameTime: string
emailNameTimes: string
numberfieldOrder?: number
showCheckboxGroup: boolean
groupTitle: string
checkboxes: Checkbox[]
showDatePicker: boolean
datePickerNotRequired: boolean
datePickerProps: DatePickerProps
datePickerEmailTitle: string
}
interface CustomHTMLElement extends HTMLElement {
checked?: boolean
value?: any
@ -182,6 +225,14 @@ interface CustomHTMLElement extends HTMLElement {
getAttribute(attr: string): string | null
}
interface DatePickerProps {
allowedDateRanges: {
from: string
to: string
}[]
excludeDays: "monday" | "tuesday" | "wednesday" | "thursday" | "friday" | "saturday" | "sunday"
}
interface FormValues {
[key: string]: CustomHTMLElement
blockGroups?: Set<number>
@ -210,6 +261,7 @@ interface TextField {
dateSelectNotRequired: boolean
emailName: string
textfieldOrder: string
textTitle: string
}
interface TeaserImage {

@ -3642,6 +3642,25 @@ __metadata:
languageName: node
linkType: hard
"fluent-svelte@npm:^1.6.0":
version: 1.6.0
resolution: "fluent-svelte@npm:1.6.0"
dependencies:
focus-trap: ^6.7.3
tabbable: ^5.2.1
checksum: 1b2ac33343f6e34469e4af9b31e0d446b021b3b2cd64f91b5b010fcf045abdbf7bb5910b9bc4ad90bd5020cf4293e23f625acb01addede3806eb0325514b727d
languageName: node
linkType: hard
"focus-trap@npm:^6.7.3":
version: 6.9.4
resolution: "focus-trap@npm:6.9.4"
dependencies:
tabbable: ^5.3.3
checksum: 0b4cebcc11010bd9397731092bd59a981e838c710c6497374ba70571875a14ab27c0db7d60f36005ec01bdabc3b9cfeda11d30ddf5b8874596dcc95e18913b3b
languageName: node
linkType: hard
"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0":
version: 1.15.2
resolution: "follow-redirects@npm:1.15.2"
@ -6533,6 +6552,13 @@ __metadata:
languageName: node
linkType: hard
"tabbable@npm:^5.2.1, tabbable@npm:^5.3.3":
version: 5.3.3
resolution: "tabbable@npm:5.3.3"
checksum: 1aa56e1bb617cc10616c407f4e756f0607f3e2d30f9803664d70b85db037ca27e75918ed1c71443f3dc902e21dc9f991ce4b52d63a538c9b69b3218d3babcd70
languageName: node
linkType: hard
"tar@npm:^6.1.11, tar@npm:^6.1.2":
version: 6.1.12
resolution: "tar@npm:6.1.12"
@ -6567,6 +6593,7 @@ __metadata:
cssnano: ^6.0.1
esbuild: ^0.19.3
esbuild-svelte: ^0.8.0
fluent-svelte: ^1.6.0
http-proxy-middleware: ^2.0.6
less: ^4.2.0
morgan: ^1.10.0