19 Commits

Author SHA1 Message Date
4c36900314 remove space
All checks were successful
CI / update (push) Successful in 28s
2025-12-17 22:04:45 +01:00
80186fe737 fix Freudenreiche Geheimnisse spelling
All checks were successful
CI / update (push) Successful in 26s
2025-12-16 20:19:48 +01:00
50eaf2787d refactor: remove verbose debug logging from cospend API endpoints
All checks were successful
CI / update (push) Successful in 26s
Removed excessive console.log statements from recurring payments processing and monthly expenses aggregation. Error logging (console.error) is retained for troubleshooting.
2025-12-16 16:40:39 +01:00
7e3cbb0397 remove mario-kart route
All checks were successful
CI / update (push) Successful in 1m59s
2025-12-16 16:01:25 +01:00
b788a615ba fix: ensure Bible verses are prerendered and served statically
Some checks failed
CI / update (push) Has been cancelled
- Fetch full verse data at build time in +page.server.ts
- Pass preloaded verseData to BibleModal instead of fetching client-side
- Remove onMount fetch logic from BibleModal component
- Add VerseData interface to type definitions
- Update handleCitationClick to pass verseData prop

This ensures all Bible verses are embedded in static HTML during build,
eliminating runtime API calls and improving performance.
2025-12-16 16:01:04 +01:00
2be2e1977b feat: enhance rosary with interactive Bible citations and improved mystery selection
All checks were successful
CI / update (push) Successful in 25s
- Add clickable Bible reference buttons that open modal with full verses
- Create BibleModal component with backdrop blur and styled close button
- Implement build-time data fetching for Bible texts while maintaining reactivity
- Redesign mystery selector with responsive grid (3-in-row/4-in-row/2×2)
- Add "Heutige" badge to indicate today's auto-selected mystery
- Reposition luminous mysteries toggle below mystery selector
- Integrate Bible reference and counter buttons side-by-side
- Restructure Bible API under /api/glaube/bibel/ for better organization
2025-12-16 15:45:40 +01:00
01dd736bbc ß -> ss
All checks were successful
CI / update (push) Successful in 23s
2025-12-16 13:47:25 +01:00
48e89305e0 feat: enhance rosary with final prayer and mystery titles
All checks were successful
CI / update (push) Successful in 25s
- Add RosaryFinalPrayer component with Latin and German text
- Display short mystery titles in decade headings (e.g., "5. Gesätz: Kreuzigung")
- Add descriptive titles to initial three Ave Marias (Glaube, Hoffnung, Liebe)
- Add closing cross symbol to signal final sign of the cross
- Mystery titles update dynamically when switching between rosary types
2025-12-16 13:42:38 +01:00
f4d6f195b3 basic CLAUDE.md for mcp 2025-12-16 11:32:38 +01:00
21f130e280 fix latin rosary secrets to FSSP Ordo Missæ
All checks were successful
CI / update (push) Successful in 27s
2025-12-15 22:43:00 +01:00
f5f199f510 fix: remove scale transform on homepage icon hover
All checks were successful
CI / update (push) Successful in 23s
2025-12-12 22:55:43 +01:00
7d2b3555f5 fix: adjust z-index values to prevent recipe card elements from overlapping header and add button
Some checks failed
CI / update (push) Failing after 1m50s
2025-12-12 22:48:40 +01:00
ea6eecc00b fix: add width constraints to prevent horizontal overflow on mobile
All checks were successful
CI / update (push) Successful in 24s
Added max-width: 100% and overflow-x: hidden to main-content and cospend-main containers to prevent child elements from forcing horizontal scroll on mobile devices.
2025-12-09 14:35:43 +01:00
aa0942ba82 fix: improve mobile responsiveness of cospend page
All checks were successful
CI / update (push) Successful in 23s
Reduced padding on mobile screens (max-width: 600px) to prevent horizontal overflow and ensure header spans full width. Updated BarChart, DebtBreakdown, EnhancedBalance components and recent activity section.
2025-12-09 14:31:27 +01:00
146aeb9d38 refactor: extract prayers into reusable components in gebete page
All checks were successful
CI / update (push) Successful in 28s
Extract inline prayer content into dedicated components in $lib/components/prayers/
for better code organization and reusability. This reduces the gebete page from ~339
to ~95 lines while maintaining the same functionality.
2025-12-08 00:48:10 +01:00
f9d2c2e367 fix Gloria Patri
Some checks failed
CI / update (push) Has been cancelled
2025-12-08 00:13:30 +01:00
be5342f006 update symbols for rosary
Some checks failed
CI / update (push) Has been cancelled
2025-12-07 12:06:56 +01:00
3a3e29e288 fix: apply crosses font to rosary visualization cross symbol
Some checks failed
CI / update (push) Has been cancelled
The cross at the top of the rosary visualization now uses the Crosses font
for consistent typography with other prayer symbols.
2025-12-06 12:23:37 +01:00
7f663e4368 feat: add crosses font with WOFF2 support for prayer symbols
Some checks failed
CI / update (push) Has been cancelled
- Add crosses.ttf and crosses.woff2 font files to static/fonts/
- Load crosses font globally in app.css with WOFF2 and TTF fallback
- Apply crosses font to italic elements in prayers (christ.css)
- Ensures consistent cross symbol rendering across prayers and rosary
2025-12-06 11:58:02 +01:00
47 changed files with 1411 additions and 3244 deletions

23
CLAUDE.md Normal file
View File

@@ -0,0 +1,23 @@
You are able to use the Svelte MCP server, where you have access to comprehensive Svelte 5 and SvelteKit documentation. Here's how to use the available tools effectively:
## Available MCP Tools:
### 1. list-sections
Use this FIRST to discover all available documentation sections. Returns a structured list with titles, use_cases, and paths.
When asked about Svelte or SvelteKit topics, ALWAYS use this tool at the start of the chat to find relevant sections.
### 2. get-documentation
Retrieves full documentation content for specific sections. Accepts single or multiple sections.
After calling the list-sections tool, you MUST analyze the returned documentation sections (especially the use_cases field) and then use the get-documentation tool to fetch ALL documentation sections that are relevant for the user's task.
### 3. svelte-autofixer
Analyzes Svelte code and returns issues and suggestions.
You MUST use this tool whenever writing Svelte code before sending it to the user. Keep calling it until no issues or suggestions are returned.
### 4. playground-link
Generates a Svelte Playground link with the provided code.
After completing the code, ask the user if they want a playground link. Only call this tool after user confirmation and NEVER if code was written to files in their project.

View File

@@ -44,5 +44,10 @@
"mongoose": "^8.0.0",
"node-cron": "^4.2.1",
"sharp": "^0.33.0"
},
"pnpm": {
"onlyBuiltDependencies": [
"esbuild"
]
}
}

272
src/app.css Normal file
View File

@@ -0,0 +1,272 @@
/* ============================================
BOCKEN.ORG CENTRALIZED STYLES
============================================ */
/* ============================================
FONT DEFINITIONS
============================================ */
@font-face {
font-family: 'crosses';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(/fonts/crosses.woff2) format('woff2'),
url(/fonts/crosses.ttf) format('truetype');
}
/* ============================================
COLOR SYSTEM
Based on Nord Theme with semantic naming
============================================ */
:root {
/* ============================================
BASE NORD COLORS
Keep original Nord colors for reference
============================================ */
--nord0: #2E3440;
--nord1: #3B4252;
--nord2: #434C5E;
--nord3: #4C566A;
--nord4: #D8DEE9;
--nord5: #E5E9F0;
--nord6: #ECEFF4;
--nord7: #8FBCBB;
--nord8: #88C0D0;
--nord9: #81A1C1;
--nord10: #5E81AC;
--nord11: #BF616A;
--nord12: #D08770;
--nord13: #EBCB8B;
--nord14: #A3BE8C;
--nord15: #B48EAD;
/* Named color aliases (backward compatibility) */
--lightblue: var(--nord9);
--blue: var(--nord10);
--red: var(--nord11);
--orange: var(--nord12);
--yellow: var(--nord13);
--green: var(--nord14);
--purple: var(--nord15);
/* ============================================
SEMANTIC COLOR SYSTEM - LIGHT MODE
============================================ */
/* Primary Color - Main interactive elements */
--color-primary: var(--nord10);
--color-primary-hover: var(--nord9);
--color-primary-active: var(--nord8);
/* Accent Color - Call-to-action, emphasis */
--color-accent: var(--nord11);
--color-accent-hover: #d07179;
--color-accent-active: #a04e56;
/* Secondary Accent - Alternative emphasis */
--color-secondary: var(--nord12);
--color-secondary-hover: #e09880;
--color-secondary-active: #b87060;
/* Background Colors */
--color-bg-primary: #fbf9f3;
--color-bg-secondary: var(--nord5);
--color-bg-tertiary: var(--nord6);
--color-bg-elevated: var(--nord4);
/* Surface Colors (cards, panels, etc.) */
--color-surface: var(--nord6);
--color-surface-hover: var(--nord5);
/* Text Colors */
--color-text-primary: var(--nord0);
--color-text-secondary: var(--nord3);
--color-text-tertiary: var(--nord2);
--color-text-inverse: white;
--color-text-on-primary: white;
--color-text-on-accent: white;
--color-text-muted: var(--nord4);
/* UI Element Colors */
--color-ui-dark: var(--nord0);
--color-ui-mid: var(--nord3);
--color-ui-light: var(--nord4);
--color-ui-hover: var(--nord3);
/* Border Colors */
--color-border: var(--nord4);
--color-border-hover: var(--nord3);
/* Link Colors */
--color-link: var(--nord11);
--color-link-visited: var(--nord15);
--color-link-hover: var(--color-accent-hover);
/* Status Colors */
--color-success: var(--nord14);
--color-warning: var(--nord13);
--color-error: var(--nord11);
--color-info: var(--nord10);
}
/* ============================================
DARK MODE COLOR OVERRIDES
============================================ */
@media (prefers-color-scheme: dark) {
:root {
/* Dark mode custom colors */
--nord6-dark: #292c31;
--accent-dark: #1f1f21;
--background-dark: #21201b;
--font-default-dark: #ffffff;
/* Primary Color - Same but adjusted for dark backgrounds */
--color-primary: var(--nord9);
--color-primary-hover: var(--nord8);
--color-primary-active: var(--nord7);
/* Accent Color - Slightly lighter for dark mode */
--color-accent: #d07179;
--color-accent-hover: #e08189;
--color-accent-active: var(--nord11);
/* Secondary Accent */
--color-secondary: #e09880;
--color-secondary-hover: #f0a890;
--color-secondary-active: var(--nord12);
/* Background Colors */
--color-bg-primary: var(--background-dark);
--color-bg-secondary: var(--accent-dark);
--color-bg-tertiary: var(--nord6-dark);
--color-bg-elevated: var(--nord0);
/* Surface Colors */
--color-surface: var(--nord0);
--color-surface-hover: var(--nord1);
/* Text Colors */
--color-text-primary: var(--font-default-dark);
--color-text-secondary: var(--nord4);
--color-text-tertiary: var(--nord5);
--color-text-inverse: var(--nord0);
--color-text-on-primary: white;
--color-text-on-accent: white;
--color-text-muted: var(--nord3);
/* UI Element Colors */
--color-ui-dark: var(--nord6);
--color-ui-mid: var(--nord4);
--color-ui-light: var(--nord3);
--color-ui-hover: var(--nord2);
/* Border Colors */
--color-border: var(--nord2);
--color-border-hover: var(--nord3);
/* Link Colors */
--color-link: #d07179;
--color-link-visited: #c89fb6;
--color-link-hover: var(--color-accent-hover);
}
}
/* ============================================
BASE STYLES
============================================ */
* {
box-sizing: border-box;
font-family: Helvetica, Arial, "Noto Sans", sans-serif;
}
body {
margin: 0;
padding: 0;
background-color: var(--color-bg-primary);
color: var(--color-text-primary);
overflow-x: hidden;
}
/* ============================================
LINK STYLES
============================================ */
a:not(:visited) {
color: var(--color-link);
}
a:visited {
color: var(--color-link-visited);
}
a:hover,
a:focus-visible {
color: var(--color-link-hover);
}
/* ============================================
FORM STYLES
============================================ */
form {
background-color: var(--color-bg-secondary);
display: flex;
flex-direction: column;
max-width: 600px;
gap: 0.5em;
margin-inline: auto;
justify-content: center;
align-items: center;
padding-block: 2rem;
margin-block: 2rem;
}
form label {
font-size: 1.2em;
}
form input {
display: block;
font-size: 1.2rem;
}
form:not(.search) button {
background-color: var(--color-accent);
color: var(--color-text-on-accent);
border: none;
padding: 0.5em 1em;
font-size: 1.3em;
border-radius: 1000px;
margin-top: 1em;
transition: 100ms;
cursor: pointer;
}
form:not(.search) button:hover,
form:not(.search) button:focus-visible {
background-color: var(--color-accent-hover);
scale: 1.1;
}
form:not(.search) button:active {
background-color: var(--color-accent-active);
}
form p {
max-width: 400px;
margin-top: 0;
}
form h4 {
margin-bottom: 0;
}
@media screen and (max-width: 600px) {
form {
margin-top: 0;
}
}

View File

@@ -70,7 +70,7 @@ async function authorization({ event, resolve }) {
// Bible verse functionality for error pages
async function getRandomVerse(fetch: typeof globalThis.fetch): Promise<any> {
try {
const response = await fetch('/api/bible-quote');
const response = await fetch('/api/glaube/bibel/zufallszitat');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}

View File

@@ -19,6 +19,7 @@ background-color: var(--red);
display: grid;
justify-content: center;
align-content: center;
z-index: 100;
}
@media screen and (max-width: 500px) {
.container{

View File

@@ -323,6 +323,12 @@
}
}
@media (max-width: 600px) {
.chart-container {
padding: 0.75rem;
}
}
canvas {
max-width: 100%;
height: 100% !important;

View File

@@ -0,0 +1,259 @@
<script lang="ts">
import type { VerseData } from '$lib/data/mysteryDescriptions';
export let reference: string = '';
export let title: string = '';
export let verseData: VerseData | null = null;
export let onClose: () => void;
let book: string = verseData?.book || '';
let chapter: number = verseData?.chapter || 0;
let verses: Array<{ verse: number; text: string }> = verseData?.verses || [];
let loading = false;
let error = verseData ? '' : 'Keine Versdaten verfügbar';
function handleBackdropClick(event: MouseEvent) {
if (event.target === event.currentTarget) {
onClose();
}
}
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape') {
onClose();
}
}
</script>
<svelte:window on:keydown={handleKeydown} />
<div class="modal-backdrop" on:click={handleBackdropClick} role="presentation">
<div class="modal-content">
<div class="modal-header">
<div class="header-content">
{#if title}
<h3 class="modal-title">
{#if title.includes(':')}
{title.split(':')[0]}:<br>{title.split(':')[1]}
{:else}
{title}
{/if}
</h3>
{/if}
<p class="modal-reference">{reference}</p>
</div>
<button class="close-button" on:click={onClose} aria-label="Schließen">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
<div class="modal-body">
{#if loading}
<p class="loading">Lädt...</p>
{:else if error}
<p class="error">{error}</p>
{:else if verses.length > 0}
<div class="verses">
{#each verses as verse}
<p class="verse">
<span class="verse-number">{verse.verse}</span>
<span class="verse-text">{verse.text}</span>
</p>
{/each}
</div>
{:else}
<p class="error">Keine Verse gefunden</p>
{/if}
</div>
</div>
</div>
<style>
.modal-backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
backdrop-filter: blur(10px);
background: rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: 1rem;
animation: show-backdrop 200ms ease forwards;
}
@keyframes show-backdrop {
from {
backdrop-filter: blur(0px);
background: rgba(0, 0, 0, 0);
}
to {
backdrop-filter: blur(10px);
background: rgba(0, 0, 0, 0.3);
}
}
@media(prefers-color-scheme: light) {
.modal-backdrop {
background: rgba(255, 255, 255, 0.3);
}
@keyframes show-backdrop {
from {
backdrop-filter: blur(0px);
background: rgba(255, 255, 255, 0);
}
to {
backdrop-filter: blur(10px);
background: rgba(255, 255, 255, 0.3);
}
}
}
.modal-content {
background: var(--nord0);
border-radius: 12px;
max-width: 600px;
width: 100%;
max-height: 80vh;
display: flex;
flex-direction: column;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
position: relative;
}
@media(prefers-color-scheme: light) {
.modal-content {
background: var(--nord6);
}
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 1.5rem;
border-bottom: 1px solid var(--nord3);
}
@media(prefers-color-scheme: light) {
.modal-header {
border-bottom: 1px solid var(--nord4);
}
}
.header-content {
flex: 1;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.modal-title {
margin: 0;
color: var(--nord10);
font-size: 1.3rem;
font-weight: 700;
}
.modal-reference {
margin: 0;
color: var(--nord8);
font-size: 1rem;
font-weight: 600;
}
.close-button {
position: absolute;
top: -1rem;
right: -1rem;
background-color: var(--nord11);
border: none;
cursor: pointer;
padding: 1rem;
border-radius: 1000px;
color: white;
transition: 200ms;
box-shadow: 0 0 1em 0.2em rgba(0, 0, 0, 0.3);
display: flex;
align-items: center;
justify-content: center;
z-index: 1001;
}
.close-button svg {
width: 2rem;
height: 2rem;
}
.close-button:hover {
background-color: var(--nord0);
transform: scale(1.2, 1.2);
box-shadow: 0 0 1em 0.4em rgba(0, 0, 0, 0.3);
}
.close-button:active {
transition: 50ms;
scale: 0.8 0.8;
}
.modal-body {
padding: 1rem;
overflow-y: auto;
}
.loading,
.error {
text-align: center;
color: var(--nord4);
font-style: italic;
}
@media(prefers-color-scheme: light) {
.loading,
.error {
color: var(--nord2);
}
}
.error {
color: var(--nord11);
}
.verses {
display: flex;
flex-direction: column;
gap: 0;
}
.verse {
display: flex;
gap: 0.75rem;
line-height: 1.6;
color: var(--nord4);
}
@media(prefers-color-scheme: light) {
.verse {
color: var(--nord0);
}
}
.verse-number {
color: var(--nord10);
font-weight: 700;
min-width: 2rem;
font-size: 0.9rem;
}
.verse-text {
flex: 1;
font-size: 1.1rem;
}
</style>

View File

@@ -92,7 +92,7 @@ const img_name=recipe.short_name + ".webp?v=" + recipe.dateModified
font-size: 1.5em;
box-shadow: 0em 0em 1em 0.1em rgba(0, 0, 0, 0.6);
transition: 100ms;
z-index: 10;
z-index: 5;
text-decoration: none;
}
#image{
@@ -179,7 +179,7 @@ const img_name=recipe.short_name + ".webp?v=" + recipe.dateModified
box-shadow: 0em 0em 0.2em 0.05em rgba(0, 0, 0, 0.3);
border: none;
position: relative;
z-index: 10;
z-index: 2;
display: inline-block;
}
.tag:hover,
@@ -207,7 +207,7 @@ const img_name=recipe.short_name + ".webp?v=" + recipe.dateModified
transition: 100ms;
border: none;
cursor: pointer;
z-index: 10;
z-index: 2;
display: inline-block;
}
.card_title .category:hover,

View File

@@ -10,9 +10,6 @@ export let onClick;
<style>
.counter-button {
position: absolute;
bottom: 0.5rem;
right: 0.5rem;
width: 3rem;
height: 3rem;
border-radius: 50%;

View File

@@ -242,4 +242,14 @@
font-size: 0.85rem;
text-align: right;
}
@media (max-width: 600px) {
.debt-breakdown {
padding: 0.75rem;
}
.debt-section {
padding: 0.75rem;
}
}
</style>

View File

@@ -333,6 +333,7 @@
.balance-card {
min-width: unset;
width: 100%;
padding: 1rem;
}
.balance-card.enhanced {
@@ -343,6 +344,7 @@
flex-direction: column;
gap: 0.75rem;
align-items: flex-start;
padding: 0.75rem;
}
.transaction-count {

View File

@@ -61,7 +61,6 @@ nav[hidden]{
{
cursor: pointer;
color: var(--red);
transform: scale(1.1,1.1);
}
:global(.site_header) {
padding-block: 1.5rem;

View File

@@ -0,0 +1,15 @@
<p>
<v lang="de">Mein Herr und mein Gott,</v>
<v lang="de">nimm alles von mir,</v>
<v lang="de">was mich hindert zu Dir.</v>
</p>
<p>
<v lang="de">Mein Herr und mein Gott,</v>
<v lang="de">gib alles mir,</v>
<v lang="de">was mich führet zu Dir.</v>
</p>
<p>
<v lang="de">Mein Herr und mein Gott,</v>
<v lang="de">nimm mich mir</v>
<v lang="de">und gib mich ganz zu eigen Dir.</v>
</p>

View File

@@ -47,7 +47,7 @@
<v lang="la">Et resurréxit tértia die,</v>
<v lang="de">Er ist auferstanden am dritten Tage,</v>
<v lang="la">secúndum Scriptúras.</v>
<v lang="de">gemäß der Schrift;</v>
<v lang="de">gemäss der Schrift;</v>
<v lang="la">Et ascéndit in cáelum:</v>
<v lang="de">Er ist aufgefahren in den Himmel</v>
<v lang="la">sedet ad déxteram Patris.</v>

View File

@@ -0,0 +1,52 @@
<p>
<v lang="la">Glória in excélsis <i><sup></sup></i> Deo.</v>
<v lang="de">Ehre sei <i><sup></sup></i> Gott in der Höhe.</v>
<v lang="la">Et in terra pax homínibus</v>
<v lang="de">Und auf Erden Friede den Mesnchen,</v>
<v lang="la">bonæ voluntátis.</v>
<v lang="de">die guten Willens sind.</v>
<v lang="la">Laudámus te.</v>
<v lang="de">Wir loben Dich.</v>
<v lang="la">Benedícimus te.</v>
<v lang="de">Wir preisen Dich.</v>
<v lang="la"><i><sup></sup></i> Adorámus te.</v>
<v lang="de"><i><sup></sup></i> Wir beten Dich an.</v>
<v lang="la">Glorificámus te.</v>
<v lang="de">Wir verherrlichen Dich.</v>
<v lang="la"><i><sup></sup></i> Grátias ágimus tibi</v>
<v lang="de"><i><sup></sup></i> Wir sagen Dir Dank</v>
<v lang="la">propter magnam glóriam tuam.</v>
<v lang="de">ob Deiner grossen Herrlichkeit.</v>
<v lang="la">Dómine Deus, Rex cæléstis,</v>
<v lang="de">Herr und Gott, König des Himmels,</v>
<v lang="la">Deus Pater omnípotens.</v>
<v lang="de">Gott allmächtiger Vater!</v>
<v lang="la">Dómine Fili unigénite, <i><sup></sup></i> Jesu Christe.</v>
<v lang="de">Herr <i><sup></sup></i> Jesus Christus, eingeborener Sohn!</v>
<v lang="la">Dómine Deus, Agnus Dei,</v>
<v lang="de">Herr und Gott, Lamm Gottes,</v>
<v lang="la">Fílius Patris.</v>
<v lang="de">Sohn des Vaters!</v>
<v lang="la">Qui tollis peccáta mundi,</v>
<v lang="de">Du nimmst hinweg die Sünden der Welt:</v>
<v lang="la">miserére nobis.</v>
<v lang="de">erbarme Dich unser.</v>
<v lang="la">Qui tollis peccáta mundi,</v>
<v lang="de">Du nimmst hinwerg die Sünden der Welt.</v>
<v lang="la"><i><sup></sup></i> súscipe depreciatiónem nostram.</v>
<v lang="de"><i><sup></sup></i> nimm unser Flehen gnädig auf.</v>
<v lang="la">Qui sedes ad déxteram Patris,</v>
<v lang="de">Du sitzt zur Rechten des Vaters:</v>
<v lang="la">miserére nobis.</v>
<v lang="de">erbarme Dich unser.</v>
<v lang="la">Quóniam tu solus Sanctus.</v>
<v lang="de">Denn Du allein bist der Heilige.</v>
<v lang="la">Tu solus Altíssimus,</v>
<v lang="de">Du allein der Höchste,</v>
<v lang="la"><i><sup></sup></i> Jesu Christe.</v>
<v lang="de"><i><sup></sup></i> Jesus Christus,</v>
<v lang="la">Cum Sancto Spíritu</v>
<v lang="de">Mit dem Hl. Geiste,</v>
<v lang="la"><i></i> in glória Dei Patris. Amen.</v>
<v lang="de"><i></i> in der Herrlichkeit Gottes des Vaters. Amen.</v>
</p>

View File

@@ -1,7 +1,7 @@
<p>
<v lang="la">Glória Patri, et Fílio, et Spirítui Sancto.</v>
<v lang="de">Ehre sei dem Vater und dem Sohne und dem Hl. Geiste.</v>
<v lang="la">Sicute erat in princípio, et nunc, et semper:</v>
<v lang="la">Sicut erat in princípio, et nunc, et semper:</v>
<v lang="de">Wie es war am Anfang, so auch jetzt und allezeit</v>
<v lang="la">et in sǽcula sæculórum. Amen.</v>
<v lang="de">und in Ewigkeit. Amen.</v>

View File

@@ -0,0 +1,8 @@
<p>
<v>Jungfräulicher Vater Jesu,</v>
<v>Reinster Bräutigam Mariä,</v>
<v>Sankt Joseph, bitte Tag für Tag bei Jesus, dem Sohn Gottes.</v>
<v>Seine Kraft und Gnade soll uns stärken,</v>
<v>dass wir siegreich streiten im Leben</v>
<v>und die Krone von Ihm erhalten im Sterben.</v>
</p>

View File

@@ -0,0 +1,22 @@
<p>
<v lang="la">Sáncte Míchael Archángele,</v>
<v lang="de">Heiliger Erzengel Michael,</v>
<v lang="la">defénde nos in proélio,</v>
<v lang="de">verteidige uns im Kampfe!</v>
<v lang="la">cóntra nequítam et insídias</v>
<v lang="de">Gegen die Bosheit und Nachstellungen</v>
<v lang="la">diáboli ésto præsídium.</v>
<v lang="de">des Teufels sei unser Schutz. </v>
<v lang="la">Ímperet ílli Déus, súpplices deprecámur:</v>
<v lang="de">»Gott gebiete ihm!«, so bitten wir flehentlich.</v>
<v lang="la">tuque, Prínceps milítæ cæléstis,</v>
<v lang="de">Du aber, Fürst der himmlischen Heerscharen,</v>
<v lang="la">Sátanam aliósque spíritus malígnos,</v>
<v lang="de">stosse den Satan und die anderen bösen Geister,</v>
<v lang="la">qui ad perditiónem animárum</v>
<v lang="la">pervagántur in múndo,</v>
<v lang="de">die in der Welt umhergehen,</v>
<v lang="de">um die Seelen zu verderben,</v>
<v lang="la">divína virtúte, in inférnum detrúde. Amen.</v>
<v lang="de">durch die Kraft Gottes in die Hölle. Amen.</v>
</p>

View File

@@ -0,0 +1,24 @@
<p>
<v lang="la">Orémus:</v>
<v lang="de">Lasset uns beten:</v>
</p>
<p>
<v lang="la">Déus, cújus Unigénitus,</v>
<v lang="de">O Gott, dessen eingeborner Sohn</v>
<v lang="la">pér vítam, mórtem ét resurrectiónem súam</v>
<v lang="de">durch sein Leben, seinen Tod und seine Auferstehung</v>
<v lang="la">nóbis salútis ætérnæ præmia comparávit:</v>
<v lang="de">uns die Belohnung des ewigen Lebens verdient hat,</v>
<v lang="la">concéde, quæsumus;</v>
<v lang="de">verleihe uns, wir bitten dich,</v>
<v lang="la">út, hæc mystéria sanctíssimo beátæ Maríæ Vírginis Rosário recoléntes;</v>
<v lang="de">dass wir, indem wir die Geheimisse des heiligen Rosenkranzes der allerseligsten Jungfrau ehren,</v>
<v lang="la">ét imitémur quód cóntinent,</v>
<v lang="de">was sie enthalten nachahmen</v>
<v lang="la">ét quód promíttunt, assequámur.</v>
<v lang="de">und dadurch erlangen, was uns in denselben verheissen ist.</v>
<v lang="la">Pér eúmdem Chrístum Dóminum nóstrum.</v>
<v lang="de">Durch unsern Herrn Jesus Christus.</v>
<v lang="la">Ámen.</v>
<v lang="de">Amen.</v>
</p>

View File

@@ -1,11 +1,11 @@
<p>
<v lang="la">Salve, Regína,</v>
<v lang="de">Sei gegrüßt, o Königin,</v>
<v lang="de">Sei gegrüsst, o Königin,</v>
<v lang="la">máter misericórdiae;</v>
<v lang="de">Mutter der Barmherzigkeit,</v>
<v lang="la">Víta, dulcédo et spes nóstra, sálve.</v>
<v lang="de">unser Leben, unsre Wonne</v>
<v lang="de">und unsere Hoffnung, sei gegrüßt!</v>
<v lang="de">und unsere Hoffnung, sei gegrüsst!</v>
</p>
<p>
<v lang="la">Ad te clamámus, éxsules fílii Hévae.</v>
@@ -23,5 +23,5 @@
<v lang="la">nóbis post hoc exsílíum osténde.</v>
<v lang="de">die gebenedeite Frucht deines Leibes.</v>
<v lang="la">O clémens, o pía, o dúlcis Vírgo María.</v>
<v lang="de">O gütige, o milde, o süße Jungfrau Maria.</v>
<v lang="de">O gütige, o milde, o süsse Jungfrau Maria.</v>
</p>

View File

@@ -19,6 +19,7 @@ i{
font-style: normal;
color: var(--nord11);
font-weight: 900;
font-family: 'crosses', sans-serif;
}
i.txt {
font-size: 70%;

View File

@@ -0,0 +1,107 @@
export interface MysteryReference {
title: string;
reference: string;
}
export interface VerseData {
book: string;
chapter: number;
verses: Array<{ verse: number; text: string }>;
}
export interface MysteryDescription extends MysteryReference {
text: string;
verseData?: VerseData | null;
}
// Only store references - texts will be fetched at build time
export const mysteryReferences = {
lichtreichen: [
{
title: "Das erste lichtreiche Geheimnis: Die Taufe im Jordan.",
reference: "Mt 3, 16-17"
},
{
title: "Das zweite lichtreiche Geheimnis: Die Hochzeit von Kana.",
reference: "Joh 2, 1-5"
},
{
title: "Das dritte lichtreiche Geheimnis: Die Verkündigung des Reiches Gottes.",
reference: "Mk 1, 15"
},
{
title: "Das vierte lichtreiche Geheimnis: Die Verklärung.",
reference: "Mt 17, 1-2"
},
{
title: "Das fünfte lichtreiche Geheimnis: Die heiligste Eucharistie (Das Altarssakrament).",
reference: "Mt 26, 26"
}
],
freudenreich: [
{
title: "Das erste freudenreiche Geheimnis: Die Verkündigung des Erzengles Gabriel an die Jungfrau Maria.",
reference: "Lk 1, 26-27"
},
{
title: "Das zweite freudenreiche Geheimnis: Der Besuch Marias bei Elisabeth.",
reference: "Lk 1, 39-42"
},
{
title: "Das dritte freudenreiche Geheimnis: Die Geburt Jesu im Stall von Bethlehem.",
reference: "Lk 2, 1-7"
},
{
title: "Das vierte freudenreiche Geheimnis: Jesus wird von Maria und Josef im Tempel dargebracht.",
reference: "Lk 2, 21-24"
},
{
title: "Das fünfte freudenreiche Geheimnis: Jesus wird im Tempel wiedergefunden.",
reference: "Lk 2, 41-47"
}
],
schmerzhaften: [
{
title: "Das erste schmerzhafte Geheimnis: Die Todesangst Jesu.",
reference: "Mt 26, 36-39"
},
{
title: "Das zweite schmerzhafte Geheimnis: Die Geißelung Jesu.",
reference: "Mt 27, 26"
},
{
title: "Das dritte schmerzhafte Geheimnis: Die Dornenkrönung.",
reference: "Mt 27, 27-29"
},
{
title: "Das vierte schmerzhafte Geheimnis: Jesus trägt das schwere Kreuz.",
reference: "Mk 15, 21-22"
},
{
title: "Das fünfte schmerzhafte Geheimnis: Die Kreuzigung Jesu.",
reference: "Lk 23, 33-46"
}
],
glorreichen: [
{
title: "Das erste glorreiche Geheimnis: Die Auferstehung Jesu.",
reference: "Lk 24, 1-6"
},
{
title: "Das zweite glorreiche Geheimnis: Die Himmerfahrt Jesu.",
reference: "Mk 16, 19"
},
{
title: "Das dritte glorreiche Geheimnis: Die Herabkunft des Heiligen Geistes im Abendmahlssaal.",
reference: "Apg 2, 1-4"
},
{
title: "Das vierte glorreiche Geheimnis: Die Aufnahme Marias in den Himmel.",
reference: "Lk 1, 48-49"
},
{
title: "Das fünfte glorreiche Geheimnis: Die Krönung Marias zur Königin des Himmels und der Erde.",
reference: "Offb 12, 1"
}
]
} as const;

View File

@@ -77,7 +77,6 @@ export const GET: RequestHandler = async ({ url, locals }) => {
];
const results = await Payment.aggregate(pipeline);
console.log('Aggregation results:', results);
// Transform data into chart-friendly format
const monthsMap = new Map();

View File

@@ -21,7 +21,6 @@ export const POST: RequestHandler = async ({ request }) => {
try {
const now = new Date();
console.log(`[Cron] Starting recurring payments processing at ${now.toISOString()}`);
// Find all active recurring payments that are due
const duePayments = await RecurringPayment.find({
@@ -34,16 +33,12 @@ export const POST: RequestHandler = async ({ request }) => {
]
});
console.log(`[Cron] Found ${duePayments.length} due recurring payments`);
const results = [];
let successCount = 0;
let failureCount = 0;
for (const recurringPayment of duePayments) {
try {
console.log(`[Cron] Processing recurring payment: ${recurringPayment.title} (${recurringPayment._id})`);
// Create the payment
const payment = await Payment.create({
title: `${recurringPayment.title} (Auto)`,
@@ -89,8 +84,6 @@ export const POST: RequestHandler = async ({ request }) => {
success: true
});
console.log(`[Cron] Successfully processed: ${recurringPayment.title}, next execution: ${nextExecutionDate.toISOString()}`);
} catch (paymentError) {
console.error(`[Cron] Error processing recurring payment ${recurringPayment._id}:`, paymentError);
failureCount++;
@@ -104,8 +97,6 @@ export const POST: RequestHandler = async ({ request }) => {
}
}
console.log(`[Cron] Completed processing. Success: ${successCount}, Failures: ${failureCount}`);
return json({
success: true,
timestamp: now.toISOString(),

View File

@@ -33,7 +33,6 @@ export const POST: RequestHandler = async ({ request, locals }) => {
switch (action) {
case 'execute':
console.log(`[API] Manual execution requested by ${auth.user.nickname}`);
await recurringPaymentScheduler.executeNow();
return json({
success: true,

View File

@@ -0,0 +1,122 @@
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
interface BibleVerse {
bookName: string;
abbreviation: string;
bookNumber: number;
chapter: number;
verseNumber: number;
text: string;
}
// Cache for parsed verses to avoid reading file repeatedly
let cachedVerses: BibleVerse[] | null = null;
async function loadVerses(fetch: typeof globalThis.fetch): Promise<BibleVerse[]> {
if (cachedVerses) {
return cachedVerses;
}
try {
const response = await fetch('/allioli.tsv');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const content = await response.text();
const lines = content.trim().split('\n');
cachedVerses = lines.map(line => {
const [bookName, abbreviation, bookNumber, chapter, verseNumber, text] = line.split('\t');
return {
bookName,
abbreviation,
bookNumber: parseInt(bookNumber),
chapter: parseInt(chapter),
verseNumber: parseInt(verseNumber),
text
};
});
return cachedVerses;
} catch (err) {
console.error('Error loading Bible verses:', err);
throw new Error('Failed to load Bible verses');
}
}
function parseReference(reference: string): { bookRef: string; isFullName: boolean; chapter: number; startVerse: number; endVerse: number } | null {
// Parse various reference formats:
// "Mt 3, 16-17", "Mt3:16-17", "Mt 3:16-17", "Lk1:3", "Matthäus 3, 16-17"
// Match book name (letters and umlauts), optional space, chapter, separator (: or ,), optional space, verse(s)
const match = reference.match(/^([A-Za-zäöüÄÖÜß]+)\s*(\d+)[\s,:]+(\d+)(?:[-:](\d+))?$/);
if (!match) {
return null;
}
const [, bookRef, chapterStr, startVerseStr, endVerseStr] = match;
// If book reference is longer than 5 characters, assume it's a full name
// Otherwise, assume it's an abbreviation
const isFullName = bookRef.length > 5;
return {
bookRef,
isFullName,
chapter: parseInt(chapterStr),
startVerse: parseInt(startVerseStr),
endVerse: endVerseStr ? parseInt(endVerseStr) : parseInt(startVerseStr)
};
}
function getVersesByReference(verses: BibleVerse[], reference: string): BibleVerse[] {
const parsed = parseReference(reference);
if (!parsed) {
return [];
}
return verses.filter(v => {
// Match based on whether we're using full name or abbreviation
const bookMatches = parsed.isFullName
? v.bookName === parsed.bookRef
: v.abbreviation === parsed.bookRef;
return bookMatches &&
v.chapter === parsed.chapter &&
v.verseNumber >= parsed.startVerse &&
v.verseNumber <= parsed.endVerse;
});
}
export const GET: RequestHandler = async ({ params, fetch }) => {
const reference = params.reference;
if (!reference) {
return error(400, 'Missing reference parameter');
}
try {
const verses = await loadVerses(fetch);
const matchedVerses = getVersesByReference(verses, reference);
if (matchedVerses.length === 0) {
return error(404, 'No verses found for the given reference');
}
// Extract book and chapter from first verse (they're all the same)
const firstVerse = matchedVerses[0];
return json({
reference,
book: firstVerse.bookName,
chapter: firstVerse.chapter,
verses: matchedVerses.map(v => ({
verse: v.verseNumber,
text: v.text
}))
});
} catch (err) {
console.error('Error fetching Bible verses:', err);
return error(500, 'Failed to fetch Bible verses');
}
};

View File

@@ -4,8 +4,8 @@ import type { RequestHandler } from './$types';
interface BibleVerse {
bookName: string;
abbreviation: string;
bookNumber: number;
chapter: number;
verse: number;
verseNumber: number;
text: string;
}
@@ -27,12 +27,12 @@ async function loadVerses(fetch: typeof globalThis.fetch): Promise<BibleVerse[]>
const lines = content.trim().split('\n');
cachedVerses = lines.map(line => {
const [bookName, abbreviation, chapter, verse, verseNumber, text] = line.split('\t');
const [bookName, abbreviation, bookNumber, chapter, verseNumber, text] = line.split('\t');
return {
bookName,
abbreviation,
bookNumber: parseInt(bookNumber),
chapter: parseInt(chapter),
verse: parseInt(verse),
verseNumber: parseInt(verseNumber),
text
};

View File

@@ -81,6 +81,8 @@
.main-content {
flex: 1;
max-width: 100%;
overflow-x: hidden;
transition: margin-right 0.3s ease-out;
}

View File

@@ -275,6 +275,8 @@
.cospend-main {
margin: 0 auto;
padding: 2rem;
max-width: 100%;
overflow-x: hidden;
}
h1 {
@@ -676,6 +678,10 @@
margin-bottom: 1rem;
}
.recent-activity {
padding: 0.75rem;
}
.actions {
flex-direction: column;
align-items: center;

View File

@@ -2,6 +2,17 @@
import "$lib/css/christ.css";
import "$lib/css/nordtheme.css";
import Gebet from "./Gebet.svelte";
import Kreuzzeichen from "$lib/components/prayers/Kreuzzeichen.svelte";
import GloriaPatri from "$lib/components/prayers/GloriaPatri.svelte";
import Paternoster from "$lib/components/prayers/Paternoster.svelte";
import Credo from "$lib/components/prayers/Credo.svelte";
import AveMaria from "$lib/components/prayers/AveMaria.svelte";
import SalveRegina from "$lib/components/prayers/SalveRegina.svelte";
import FatimaGebet from "$lib/components/prayers/FatimaGebet.svelte";
import Gloria from "$lib/components/prayers/Gloria.svelte";
import MichaelGebet from "$lib/components/prayers/MichaelGebet.svelte";
import BruderKlausGebet from "$lib/components/prayers/BruderKlausGebet.svelte";
import JosephGebet from "$lib/components/prayers/JosephGebet.svelte";
</script>
<style>
.ccontainer{
@@ -35,304 +46,49 @@ h1{
<div class=container>
<Gebet name={"Das heilige Kreuzzeichen"} is_bilingue={true}>
<p>
<v lang=la>In nómine <i></i> Patris, et Fílii, et Spíritus Sancti. Amen.</v>
<v lang=de>Im Namen des <i></i> Vaters und des Sohnes und des Heiligen Geistes. Amen.</v>
</p>
<Kreuzzeichen />
</Gebet>
<Gebet name={"Glória Patri"} is_bilingue={true}>
<p>
<v lang=la>Glória Patri, et Fílio, et Spirítui Sancto.</v>
<v lang=de>Ehre sei dem Vater und dem Sohne und dem Hl. Geiste.</v>
<v lang=la>Sicute erat in princípio, et nunc, et semper:</v>
<v lang=de>Wie es war am Anfang, so auch jetzt und allezeit</v>
<v lang=la>et in sǽcula sæculórum. Amen.</v>
<v lang=de>und in Ewigkeit. Amen.</v>
</p>
<GloriaPatri />
</Gebet>
<Gebet name={"Paternoster"} is_bilingue={true}>
<p>
<v lang=la>Pater noster, qui es in cælis</v>
<v lang=de>Vater unser, der Du bist im Himmel,</v>
<v lang=la>Sanctificétur nomen tuum</v>
<v lang=de>geheiligt werde Dein Name;</v>
<v lang=la>Advéniat regnum tuum</v>
<v lang=de>zu uns komme Dein Reich;</v>
<v lang=la>Fiat volúntas tua, sicut in cælo, et in terra.</v>
<v lang=de>Dein Wille geschehe, wie im Himmel, also auch auf Erden!</v>
<v lang=la>Panem nostrum quotidiánum da nobis hódie.</v>
<v lang=de>Unser tägliches Brot gib uns heute;</v>
<v lang=la>Et dimítte nobis debíta nostra,</v>
<v lang=de>und vergib uns unsere Schulden,</v>
<v lang=la>sicut et nos dimíttimus debitóribus nostris.</v>
<v lang=de>wie auch wir vergeben unsern Schuldigern;</v>
<v lang=la>Et ne nos indúcas in tentatiónem.</v>
<v lang=de>und führe uns nicht in Versuchung.</v>
<v lang=la>Sed líbera nos a malo. Amen.</v>
<v lang=de>Sondern erlöse uns von dem Übel. Amen.</v>
</p>
<Paternoster />
</Gebet>
<Gebet name={"Credo"} is_bilingue={true}>
<p>
<v lang=la>Credo in unum <i><sup></sup></i> Deum, Patrem omnipoténtem,</v>
<v lang=de>Ich glaub an den einen <i><sup></sup></i> Gott. Den allmächtigen Vater,</v>
<v lang=la>factórem cæli et terræ,</v>
<v lang=de>Schöpfer des Himmels und der Erde,</v>
<v lang=la>visibílium ómnium et invisibílium.</v>
<v lang=de>aller sichtbaren und unsichtbaren Dinge.</v>
<v lang=la>Et in unum Dóminum <i><sup></sup></i> Jesum Christum,</v>
<v lang=de>Und an den einen Herrn <i><sup></sup></i> Jesus Christus,</v>
<v lang=la>Fílium Dei unigénitum.</v>
<v lang=de>Gottes eingeborenen Sohn.</v>
<v lang=la>Et ex Patre natum ante ómnia sǽcula.</v>
<v lang=de>Er ist aus dem Vater geboren vor aller Zeit.</v>
<v lang=la>Deum de Deo,</v>
<v lang=de>Gott von Gott,</v>
<v lang=la>lumen de lúmine,</v>
<v lang=de>Licht vom Lichte,</v>
<v lang=la>Deum verum de Deo vero.</v>
<v lang=de>wahrer Gott vom wahren Gott;</v>
<v lang=la>Génitum, non factum,</v>
<v lang=de>Gezeugt, nicht geschaffen,</v>
<v lang=la>consubstantiálem Patri:</v>
<v lang=de>eines Wesens mit dem Vater;</v>
<v lang=la>per quem ómnia facta sunt.</v>
<v lang=de>durch Ihn ist alles geschaffen.</v>
<v lang=la>Qui propter nos hómines</v>
<v lang=de>Für uns Menschen</v>
<v lang=la>et propter nostram salútem</v>
<v lang=de>und um unsres Heiles willen</v>
<v lang=la>descéndit de cælis.</v>
<v lang=de>ist Er vom Himmel herabgestiegen.</v>
</p>
<p>
<v lang=la>Et incarnátus est de Spíritu Sancto</v>
<v lang=de>Er hat Fleisch angenommen durch den Hl. Geist</v>
<v lang=la>ex <i><sup></sup></i> María Vírgine:</v>
<v lang=de>aus <i><sup></sup></i> Maria, der Jungfrau</v>
<v lang=la>Et homo factus est.</v>
<v lang=de>und ist Mensch geworden.</v>
<v lang=la>Crucifíxus étiam pro nobis:</v>
<v lang=de>Gekreuzigt wurde Er sogar für uns;</v>
<v lang=la>sub Póntio Piláto passus, et sepúltus est.</v>
<v lang=de>unter Pontius Pilatus hat Er den Tod erlitten</v>
<v lang=de>und ist begraben worden</v>
</p>
<p>
<v lang=la>Et resurréxit tértia die,</v>
<v lang=de>Er ist auferstanden am dritten Tage,</v>
<v lang=la>secúndum Scriptúras.</v>
<v lang=de>gemäß der Schrift;</v>
<v lang=la>Et ascéndit in cáelum:</v>
<v lang=de>Er ist aufgefahren in den Himmel</v>
<v lang=la>sedet ad déxteram Patris.</v>
<v lang=de>und sitzet zur Rechten des Vaters.</v>
</p>
<p>
<v lang=la>Et íterum ventúrus est cum glória</v>
<v lang=de>Er wird wiederkommen in Herrlichkeit,</v>
<v lang=la>judicáre vivos et mórtuos:</v>
<v lang=de>Gericht zu halten über Lebende und Tote:</v>
<v lang=la>cujus regni non erit finis.</v>
<v lang=de>und Seines Reiches wird kein Endes sein.</v>
</p>
<p>
<v lang=la>Et in Spíritum Sanctum,</v>
<v lang=de>Ich glaube an den Heiligen Geist,</v>
<v lang=la>Dóminum et vivificántem:</v>
<v lang=de>den Herrn und Lebensspender,</v>
<v lang=la>qui ex Patre Filióque procédit.</v>
<v lang=de>der vom Vater und vom Sohne ausgeht.</v>
<v lang=la>Qui cum Patre et Fílio simul <i><sup></sup></i> adorátur et conglorificátur:</v>
<v lang=de>zugleich <i><sup></sup></i> angebetet und verherrlicht;</v>
<v lang=la>qui locútus est per Prophétas.</v>
<v lang=de>Er hat gesprochen durch die Propheten.</v>
<v lang=la>Et unam sanctam cathólicam et apostólicam Ecclésiam.</v>
<v lang=de>Ich glaube an die eine, heilige, katholische und apostolische Kirche.</v>
<v lang=la>Confíteor unum baptísma</v>
<v lang=de>Ich bekenne die eine Taufe</v>
<v lang=la>in remissiónem peccatórum.</v>
<v lang=de>zur Vergebung der Sünden.</v>
<v lang=la>Et exspécto resurrectiónem mortuórum.</v>
<v lang=de>Ich erwarte die Auferstehung der Toten.</v>
<v lang=la><i></i> Et vitam ventúri sǽculi. Amen.</v>
<v lang=de><i></i> Und das Leben der zukünftigen Welt. Amen.</v>
</p>
<Credo />
</Gebet>
<Gebet name={"Ave Maria"} is_bilingue={true}>
<p>
<v lang=la>Ave María, grátia plena. Dóminus tecum,</v>
<v lang=de>Gegrüsset seist du Maria, voll der Gnade; der Herr ist mit dir;</v>
<v lang=la>benedícta tu in muliéribus,</v>
<v lang=de>du bist gebenedeit unter den Weibern,</v>
<v lang=la>et benedíctus fructus ventris tui, Jesus.</v>
<v lang=de>und gebenedeit ist die Frucht deines Leibes, Jesus.</v>
</p>
<p>
<v lang=la>Sancta María, mater Dei, ora pro nobis peccatóribus,</v>
<v lang=de>Heilige Maria, Mutter Gottes, bitte für uns Sünder</v>
<v lang=la>nunc, et in hora mortis nostræ. Amen.</v>
<v lang=de>jetzt und in der Stunde unseres Todes! Amen.</v>
</p>
<AveMaria />
</Gebet>
<Gebet name={"Salve Regina"} is_bilingue={true}>
<p>
<v lang=la>Salve, Regína,</v>
<v lang=de>Sei gegrüßt, o Königin,</v>
<v lang=la>máter misericórdiae;</v>
<v lang=de>Mutter der Barmherzigkeit,</v>
<v lang=la>Víta, dulcédo et spes nóstra, sálve.</v>
<v lang=de>unser Leben, unsre Wonne</v>
<v lang=de>und unsere Hoffnung, sei gegrüßt!</v>
</p>
<p>
<v lang=la>Ad te clamámus, éxsules fílii Hévae.</v>
<v lang=de>Zu dir rufen wir verbannte Kinder Evas;</v>
<v lang=la>Ad te suspirámus,</v>
<v lang=de>zu dir seufzen wir</v>
<v lang=la>geméntes et fléntes in hac lacrimárum válle.</v>
<v lang=de>trauernd und weinend in diesem Tal der Tränen.</v>
<v lang=la>Eia ergo, Advocáta nóstra,</v>
<v lang=de>Wohlan denn, unsre Fürsprecherin,</v>
<v lang=la>íllos túos misericórdes óculos ad nos convérte.</v>
<v lang=de>deine barmherzigen Augen wende zu uns</v>
<v lang=la>Et Jésum, benedíctum frúctum véntris túi,</v>
<v lang=de>und nach diesem Elend zeige uns Jesus,</v>
<v lang=la>nóbis post hoc exsílíum osténde.</v>
<v lang=de>die gebenedeite Frucht deines Leibes.</v>
<v lang=la>O clémens, o pía, o dúlcis Vírgo María.</v>
<v lang=de>O gütige, o milde, o süße Jungfrau Maria.</v>
</p>
<SalveRegina />
</Gebet>
<Gebet name={"Das Fatimagebet"} is_bilingue={true}>
<v lang=la>Ó mí Jésú, dímitte nóbís débita nostra,</v>
<v lang=de>O mein Jesus, verzeih' uns unsere Sünden,</v>
<v lang=la>líberá nós ab igne ínferní,</v>
<v lang=de>bewahre uns vor den Feuern der Hölle</v>
<v lang=la>condúc in cælum omnés animás, </v>
<v lang=de>und führe alle Seelen in den Himmel,</v>
<v lang=la>præsertim illás,</v>
<v lang=de>besonders jene,</v>
<v lang=la>quæ maximé indigent misericordiá tuá. Amen.</v>
<v lang=de>die Deiner Barmherzigkeit am meisten bedürfen. Amen.</v>
<FatimaGebet />
</Gebet>
<Gebet name={"Glória"} is_bilingue={true}>
<p slot="intro">Der uralte Gesang beginnt mit den Worten, mit denen die Engelscharen den neugeborenen Welterlöser feierten. Er preist zunächst Gott Vater, dann Gott Sohn; er schließt mit einer Huldigung an die Heiligste Dreifaltigkeit, wobei man sich mit dem großen Kreuze bezeichnet.</p>
<p>
<v lang=la>Glória in excélsis <i><sup></sup></i> Deo.</v>
<v lang=de>Ehre sei <i><sup></sup></i> Gott in der Höhe.</v>
<v lang=la>Et in terra pax homínibus</v>
<v lang=de>Und auf Erden Friede den Mesnchen,</v>
<v lang=la>bonæ voluntátis.</v>
<v lang=de>die guten Willens sind.</v>
<v lang=la>Laudámus te.</v>
<v lang=de>Wir loben Dich.</v>
<v lang=la>Benedícimus te.</v>
<v lang=de>Wir preisen Dich.</v>
<v lang=la><i><sup></sup></i> Adorámus te.</v>
<v lang=de><i><sup></sup></i> Wir beten Dich an.</v>
<v lang=la>Glorificámus te.</v>
<v lang=de>Wir verherrlichen Dich.</v>
<v lang=la><i><sup></sup></i> Grátias ágimus tibi</v>
<v lang=de><i><sup></sup></i> Wir sagen Dir Dank</v>
<v lang=la>propter magnam glóriam tuam.</v>
<v lang=de>ob Deiner großen Herrlichkeit.</v>
<v lang=la>Dómine Deus, Rex cæléstis,</v>
<v lang=de>Herr und Gott, König des Himmels,</v>
<v lang=la>Deus Pater omnípotens.</v>
<v lang=de>Gott allmächtiger Vater!</v>
<v lang=la>Dómine Fili unigénite, <i><sup></sup></i> Jesu Christe.</v>
<v lang=de>Herr <i><sup></sup></i> Jesus Christus, eingeborener Sohn!</v>
<v lang=la>Dómine Deus, Agnus Dei,</v>
<v lang=de>Herr und Gott, Lamm Gottes,</v>
<v lang=la>Fílius Patris.</v>
<v lang=de>Sohn des Vaters!</v>
<v lang=la>Qui tollis peccáta mundi,</v>
<v lang=de>Du nimmst hinweg die Sünden der Welt:</v>
<v lang=la>miserére nobis.</v>
<v lang=de>erbarme Dich unser.</v>
<v lang=la>Qui tollis peccáta mundi,</v>
<v lang=de>Du nimmst hinwerg die Sünden der Welt.</v>
<v lang=la><i><sup></sup></i> súscipe depreciatiónem nostram.</v>
<v lang=de><i><sup></sup></i> nimm unser Flehen gnädig auf.</v>
<v lang=la>Qui sedes ad déxteram Patris,</v>
<v lang=de>Du sitzt zur Rechten des Vaters:</v>
<v lang=la>miserére nobis.</v>
<v lang=de>erbarme Dich unser.</v>
<v lang=la>Quóniam tu solus Sanctus.</v>
<v lang=de>Denn Du allein bist der Heilige.</v>
<v lang=la>Tu solus Altíssimus,</v>
<v lang=de>Du allein der Höchste,</v>
<v lang=la><i><sup></sup></i> Jesu Christe.</v>
<v lang=de><i><sup></sup></i> Jesus Christus,</v>
<v lang=la>Cum Sancto Spíritu</v>
<v lang=de>Mit dem Hl. Geiste,</v>
<v lang=la><i></i> in glória Dei Patris. Amen.</v>
<v lang=de><i></i> in der Herrlichkeit Gottes des Vaters. Amen.</v>
</p>
<p slot="intro">Der uralte Gesang beginnt mit den Worten, mit denen die Engelscharen den neugeborenen Welterlöser feierten. Er preist zunächst Gott Vater, dann Gott Sohn; er schliesst mit einer Huldigung an die Heiligste Dreifaltigkeit, wobei man sich mit dem grossen Kreuze bezeichnet.</p>
<Gloria />
</Gebet>
<Gebet name={"Gebet zum hl. Erzengel Michael"} is_bilingue={true}>
<p>
<v lang=la>Sáncte Míchael Archángele,</v>
<v lang=de>Heiliger Erzengel Michael,</v>
<v lang=la>defénde nos in proélio,</v>
<v lang=de>verteidige uns im Kampfe!</v>
<v lang=la>cóntra nequítam et insídias</v>
<v lang=de>Gegen die Bosheit und Nachstellungen</v>
<v lang=la>diáboli ésto præsídium.</v>
<v lang=de>des Teufels sei unser Schutz. </v>
<v lang=la>Ímperet ílli Déus, súpplices deprecámur:</v>
<v lang=de>»Gott gebiete ihm!«, so bitten wir flehentlich.</v>
<v lang=la>tuque, Prínceps milítæ cæléstis,</v>
<v lang=de>Du aber, Fürst der himmlischen Heerscharen,</v>
<v lang=la>Sátanam aliósque spíritus malígnos,</v>
<v lang=de>stoße den Satan und die anderen bösen Geister,</v>
<v lang=la>qui ad perditiónem animárum</v>
<v lang=la>pervagántur in múndo,</v>
<v lang=de>die in der Welt umhergehen,</v>
<v lang=de>um die Seelen zu verderben,</v>
<v lang=la>divína virtúte, in inférnum detrúde. Amen.</v>
<v lang=de>durch die Kraft Gottes in die Hölle. Amen.</v>
</p>
<MichaelGebet />
</Gebet>
<Gebet name={"Bruder Klaus Gebet"} is_bilingue={false}>
<p>
<v lang=de>Mein Herr und mein Gott,</v>
<v lang=de>nimm alles von mir,</v>
<v lang=de>was mich hindert zu Dir.</v>
</p>
<p>
<v lang=de>Mein Herr und mein Gott,</v>
<v lang=de>gib alles mir,</v>
<v lang=de>was mich führet zu Dir.</v>
</p>
<p>
<v lang=de>Mein Herr und mein Gott,</v>
<v lang=de>nimm mich mir</v>
<v lang=de>und gib mich ganz zu eigen Dir.</v>
</p>
<BruderKlausGebet />
</Gebet>
<Gebet name={"Josephgebet des hl. Papst Pius X"} is_bilingue={false}>
<p slot="intro">Wenn man mehr zum hl. Joseph als <q>Patrone Morientium</q> wissen möchte kann man <a href="predigten/20220319-hl._joseph">hier</a> die Predigt zum Festtag des hl. Joseph nachlesen.</p>
<p>
<v>Jungfräulicher Vater Jesu,</v>
<v>Reinster Bräutigam Mariä,</v>
<v>Sankt Joseph, bitte Tag für Tag bei Jesus, dem Sohn Gottes.</v>
<v>Seine Kraft und Gnade soll uns stärken,</v>
<v>dass wir siegreich streiten im Leben</v>
<v>und die Krone von Ihm erhalten im Sterben.</v>
</p>
<JosephGebet />
</Gebet>
</div>
</div>

View File

@@ -74,16 +74,16 @@ h4{
<h2>Der Osterfestkreis</h2>
<div class=schott>
<p>
Der Weihnachtsfestkreis schließt mit der Woche vor Septuagesima.
Der Weihnachtsfestkreis schliesst mit der Woche vor Septuagesima.
Der ersehnte Erlöser ist gekommen und hat in seiner ersten Ankunft zugleich seine zweite, die am Gerichtstage erfolgen wird, begründet und begonnen.
</p>
<p>
Jetzt ist die Zeit des anstrengten Kampfes gegen Sünde, Welt und Fleisch gekommen, die Zeit der mühevollen Aussaat, des sturmumtobten Wachsens.
Durch Kampf zum Sieg, durch Sterben zum Leben, zur Auferstehung, zur Vollherrschaft Christi und schließlich zur Verklärung im Osterlichte!
Christus soll in uns den Thron seiner Herrschaft errichten, einer Herrschaft, die uns nicht erdrückt, sondern erhöht; nicht beraubt, sondern bereichert; nicht einschränkt, sondern innerlich weitet und uns einmal mitherrschen läßt im ewigen Ostern des Himmels.
Durch Kampf zum Sieg, durch Sterben zum Leben, zur Auferstehung, zur Vollherrschaft Christi und schliesslich zur Verklärung im Osterlichte!
Christus soll in uns den Thron seiner Herrschaft errichten, einer Herrschaft, die uns nicht erdrückt, sondern erhöht; nicht beraubt, sondern bereichert; nicht einschränkt, sondern innerlich weitet und uns einmal mitherrschen lässt im ewigen Ostern des Himmels.
</p>
<p>
Der Osterfestkreis umfaßt drei Abschnitte:
Der Osterfestkreis umfasst drei Abschnitte:
die Zeit der Vorbereitung: Vorfasten- und Fastenzeit;
die eigentliche Festzeit: Oster- und Pfingstfest;
endlich die Zeit nach Pfingsten
@@ -94,15 +94,15 @@ endlich die Zeit nach Pfingsten
<h3> 1. Die Vorfastenzeit</h3>
<div class=schott >
<p>
Sie umfaßt die Sonntage Septuagesima, Sexagesima und Quinquagesima.
Sie umfasst die Sonntage Septuagesima, Sexagesima und Quinquagesima.
Diese Namen bezeichnen nicht die genauen Abstände bis zum Osterfest, sonder deuten auf die rund berechnete 70tägige, 60tägige, 50tägige Vorbereitungzeit auf Ostern.
</p>
<p>
Der Name Septuagesima weckt die Erinnerung an die 70 Jahre der Gefangenschaft, welche die Juden zur Strafe für ihre Untreue fern von Jerusalem, zu Babylon, verbringen mußten, bevor sie wieder ins Gelobte Land zurückkerhen durften.
Der Name Septuagesima weckt die Erinnerung an die 70 Jahre der Gefangenschaft, welche die Juden zur Strafe für ihre Untreue fern von Jerusalem, zu Babylon, verbringen mussten, bevor sie wieder ins Gelobte Land zurückkerhen durften.
So mahnt uns diese Zeit an unsre eigene Pilgerschaft aus der Fremde, aus der gottfernen Welt (Babylon), zum wahren Vaterland (Jerusalem).
Diese Pilgerschaft ist für uns eine beständiger Kampf gegen die Feinde unsres Heiles.
Für den göttlichen Heiland bedeutet das öffentliche Wirken Mühsal und Leiden und schließlich den Tod;
so muß sich auch unser Leben, soll es dem seinen nachgebildet werden, auf Kämpfe, selbst auf ein geistiges Sterben gefaßt machen;
Für den göttlichen Heiland bedeutet das öffentliche Wirken Mühsal und Leiden und schliesslich den Tod;
so muss sich auch unser Leben, soll es dem seinen nachgebildet werden, auf Kämpfe, selbst auf ein geistiges Sterben gefasst machen;
erst dann wird es mit dem Heiland zum endlichen Triumph gelangen.
</p>
</div>
@@ -123,14 +123,14 @@ endlich die Zeit nach Pfingsten
<h3> Epistel </h3>
<div class="epistel bibel">
<ol><i>1 Cor. 9</i>
<li value=24>Wisset ihr nicht, daß die, welche in der Rennbahn laufen, zwar alle laufen, aber nur einer erlangt den Preis? Laufet fo, daß ihr ihn erlanget!</li>
<li value=24>Wisset ihr nicht, dass die, welche in der Rennbahn laufen, zwar alle laufen, aber nur einer erlangt den Preis? Laufet fo, dass ihr ihn erlanget!</li>
<li>Jeder aber, der im Kampfspiele ringt, enthält sich von allem, und zwar jene, um eine vergängliche Krone zu empfangen, wir aber eine unvergängliche.</li>
<li>Ich laufe demnach, nicht wie in's Ungewisse; ich kämpfe, nicht indem ich Luftstreiche thue,</li>
<li>sondern ich züchtige meinen Leib, und bringe ihn in die Botmäßigkeit, damit ich nicht etwa, nachdem ich anderen gepredigt habe, selbst verworfen werde.</li>
<li>sondern ich züchtige meinen Leib, und bringe ihn in die Botmässigkeit, damit ich nicht etwa, nachdem ich anderen gepredigt habe, selbst verworfen werde.</li>
<i>1 Cor. 10</i>
<li value=1>Denn ich will euch nicht in Unwissenheit lassen, Brüder! Daß unsere Väter alle unter der Wolke waren, und alle durch das Meer hindurch gingen,</li>
<li value=1>Denn ich will euch nicht in Unwissenheit lassen, Brüder! Dass unsere Väter alle unter der Wolke waren, und alle durch das Meer hindurch gingen,</li>
<li>und alle auf Moses getauft wurden, in der Wolke und in dem Meere,</li>
<li>und alle dieselbe geistige Speise aßen,</li>
<li>und alle dieselbe geistige Speise assen,</li>
<li>und alle dieselbe geistigen Trank tranken (sie tranken nämlich aus einem geistigen, sie begleitenden Felsen, der Felsen aber war Christus);</li>
<li>aber an der Mehrzahl von ihnen hatte Gott kein Wohlgefallen; denn sie wurden niedergestreckt in der Wüste.</li>
</ol>
@@ -141,14 +141,14 @@ endlich die Zeit nach Pfingsten
<ol><i>Matth. 20</i>
<li>Das Himmelreich ist gleich einem Hausvater, der am frühen Morgen ausging, um Arbeiter in seinen Weinberg zu dingen.</li>
<li>Nachdem er nun mit den Arbeitern um einen Denar für den Tag übereingekommen war, sandte er sie in seinen Weinberg.</li>
<li>Und als er um die dritte Stunde ausging, sah er andere au dem Markte müßig stehen,</li>
<li>Und als er um die dritte Stunde ausging, sah er andere au dem Markte müssig stehen,</li>
<li>und sprach zu ihnen: Gehet auch ihr in meinen Weinberg, und was recht ist, werde ich euch geben.</li>
<li>Sie aber gingen hin. Abermals ging er um die sechste und neunte Stunde aus, und that ebenso.</li>
<li>Um die elfte Stunde aber ging er aus, und fand andere andere stehen, und sprach zu ihnen: Was stehet ihr hier den ganzen Tag müßig?</li>
<li>Um die elfte Stunde aber ging er aus, und fand andere andere stehen, und sprach zu ihnen: Was stehet ihr hier den ganzen Tag müssig?</li>
<li>Sie antworteten ihm: Weil uns niemand gedungen hat. Da sprach er zu ihnen: Gehet auch ihr in meinen Weinberg,</li>
<li>Als es nun Abend geworden, sagte der Herr des Weinberges zu seinem Verwalter: Rufe die Arbeiter, und gib ihnen den Lohn, von den letzten anfangend, bis zu den ersten.</li>
<li>Da nun die kamen, welche um die elfte Stunde eingetreten waren, empfingen sie jeder einen Denar.</li>
<li>Wie aber auch die ersten kamen, meinten sie, daß sie mehr empfangen würden, aber auch sie erhielten jeder einen Denar.</li>
<li>Wie aber auch die ersten kamen, meinten sie, dass sie mehr empfangen würden, aber auch sie erhielten jeder einen Denar.</li>
<li>Und da sie ihn empfingen, murrten sie wider den Hausvater.</li>
<li>und sprachen: Siese letzten haben eine einzige Stunde gearbeitet, und du hast sie uns gleich gehalten, die wir die Last und Hitze des Tages getragen haben.</li>
<li>Er aber antowrtete einem aus ihnen, und sprach: Freund! ich thue dir nicht Unrecht; bist du nicht auf einen Denar mit mir eins geworden?</li>
@@ -168,7 +168,7 @@ endlich die Zeit nach Pfingsten
Wir sollen gleich den Sportlern verzichten für das Heil der Seelen, dem Heil der eigenen Seele.
</p>
<p>
Diese Bildnis der Spiele im Stadion waren vermutlich ein gutes Bildnis für die Korinter. So hatten sie alle zwei Jahre Sportspiele von April bis Anfang Mai, welche wie auch Fußball heute noch, vieles der Gesellschaft lahmlegte.
Diese Bildnis der Spiele im Stadion waren vermutlich ein gutes Bildnis für die Korinter. So hatten sie alle zwei Jahre Sportspiele von April bis Anfang Mai, welche wie auch Fussball heute noch, vieles der Gesellschaft lahmlegte.
Auch die Gläubigen gehörten damals wie heute zu den Begeisterten solcher Spiele.
</p>
@@ -194,7 +194,7 @@ endlich die Zeit nach Pfingsten
So beging er zwei Todsünden durch einen Blick auf eine Frau: Ehebruch und Mord am Manne dieser Frau.
</p>
<p>
Als Kind hat er Löwen und Bären mit bloßen Händen besiegt, aber nun wird dieser einst mutige und starke Mann bezwungen wegen seinem <em>ßiggang</em>.
Als Kind hat er Löwen und Bären mit blossen Händen besiegt, aber nun wird dieser einst mutige und starke Mann bezwungen wegen seinem <em>ssiggang</em>.
Wie man zu sagen pflegt: Wer man kein Beschäftigugn welche Platz einnimmt so wird der Teufel selbst den ganzen Platz einnehmen, den wir freigelassen haben.
Es wäre besser gewesen für David, noch in der Ängstlichkeit der Flucht vor Saulus zu sein, als in seinem Palast in Jerusalem.
</p>
@@ -208,10 +208,10 @@ endlich die Zeit nach Pfingsten
</div>
<p>
ßiggang hat ihn besiegt und der Gedanke, dass er nichts mehr zu erobern hatte.
ssiggang hat ihn besiegt und der Gedanke, dass er nichts mehr zu erobern hatte.
Es gibt immer etwas weiteres zu erobern. Es gibt immer eine Ecke in unserer Seele die uns, und damit Gott, nicht gehört.
Es gibt immer etwas, was man besser tun kann, es gibt immer schlechte Gewohnheiten die man ablegen muss.
In dieser Vorfastenzeit geht es darum sich diesem deutlicher bewusst zu werden und seinen Kampf gegen diese Müßigkeit für die kommende Fastenzeit zu planen.
In dieser Vorfastenzeit geht es darum sich diesem deutlicher bewusst zu werden und seinen Kampf gegen diese Müssigkeit für die kommende Fastenzeit zu planen.
</p>
<p>
Als Beispiel hilft hier das Königreich Spanien. Jahrhunderte von Kampf um die Anwesenheit der Muslime zu bekämpfen. Als sie endlich die letzte Stadt, welche unter der Vollmacht der Muslime war, erobert hatten, hat die Vorsehung ihnen noch etwas zu erobern gegeben:
@@ -226,7 +226,7 @@ endlich die Zeit nach Pfingsten
<p>
Von den Ägyptern durch die Wunder Gottes befreit, sind sie nun auf dem weiten Weg zum versprochenen Land.
Sie schicken Kundschafter in dieses Land. Diese Kundschafter gehen und verbringen 40 Tage dort.
Diese Kundschafter finden ein wunderbares Land. Es fließt Honig und Milch. Aber es ist nicht unbevölkert. Es gibt viele, starke Stämme.
Diese Kundschafter finden ein wunderbares Land. Es fliesst Honig und Milch. Aber es ist nicht unbevölkert. Es gibt viele, starke Stämme.
Die Kundschafter haben Angst, sie verbreiteten Lügen über dieses Land da sie Angst haben zu fallen im Versuch es einzunehmen.
Das Volk will murren, beklagen. Sie wollen nicht kämpfen.
</p>
@@ -251,7 +251,7 @@ endlich die Zeit nach Pfingsten
Diese Knechtschaft soll abgelegt werden um so zu einem kämpferischen Geist zu kommen. Das wird auch bestätigt im Johannesevangelium wo steht:
<div class=bibel>
<ol><i> Johannes 15</i>
<li value=15>Ich nenne euch nun nicht mehr Knechte, denn der Knecht weiß nicht, was sein Herr tut; euch aber habe ich Freunde genannt; denn alles, was ich von meinem Vater gehört, habe ich euch kundgetan.
<li value=15>Ich nenne euch nun nicht mehr Knechte, denn der Knecht weiss nicht, was sein Herr tut; euch aber habe ich Freunde genannt; denn alles, was ich von meinem Vater gehört, habe ich euch kundgetan.
</li>
</ol>
</div>
@@ -280,7 +280,7 @@ endlich die Zeit nach Pfingsten
<li value=10>Simon Petrus also, der sein Schwert hatte, zog es und schlug den Knecht des Hohenpriesters, und hieb ihm sein rechtes Ohr ab. Der Name des Knechtes aber war Malchus.</li>
<i>Matthäus 26</i>
<li value=35>Da sprach Petrus zu ihm: Wenn ich auch mit dir sterben müsste, werde ich dich doch nicht verleugnen. In gleicher Weise sprachen auch alle Jünger.</li>
<li value=69>Petrus aber saß draußen in dem Hofe; und eine Magd trat zu ihm hin, und sprach: Du warest auch bei Jesus, dem Galiläer!</li>
<li value=69>Petrus aber sass draussen in dem Hofe; und eine Magd trat zu ihm hin, und sprach: Du warest auch bei Jesus, dem Galiläer!</li>
<li>Doch er leugnete vor allen, und sprach: Ich weiss nicht, was du sagst.</li>
</ol>
</div>

View File

@@ -0,0 +1,64 @@
import { mysteryReferences, type MysteryDescription, type VerseData } from '$lib/data/mysteryDescriptions';
import type { PageServerLoad } from './$types';
export const prerender = true;
async function fetchBibleData(reference: string, fetch: typeof globalThis.fetch): Promise<{ text: string; verseData: VerseData | null }> {
try {
const response = await fetch(`/api/glaube/bibel/${encodeURIComponent(reference)}`);
if (!response.ok) {
console.error(`Failed to fetch reference ${reference}:`, response.status);
return { text: '', verseData: null };
}
const data = await response.json();
// Format the verses into a single text with guillemets
let text = '';
if (data.verses && data.verses.length > 0) {
text = `«${data.verses.map((v: { verse: number; text: string }) => v.text).join(' ')}»`;
}
// Store the full verse data for the modal
const verseData: VerseData = {
book: data.book,
chapter: data.chapter,
verses: data.verses
};
return { text, verseData };
} catch (err) {
console.error(`Error fetching reference ${reference}:`, err);
return { text: '', verseData: null };
}
}
export const load: PageServerLoad = async ({ fetch }) => {
// Fetch Bible texts for all mysteries at build time
const mysteryDescriptions: Record<string, MysteryDescription[]> = {
lichtreichen: [],
freudenreich: [],
schmerzhaften: [],
glorreichen: []
};
// Process each mystery type
for (const [mysteryType, references] of Object.entries(mysteryReferences)) {
const descriptions: MysteryDescription[] = [];
for (const ref of references) {
const { text, verseData } = await fetchBibleData(ref.reference, fetch);
descriptions.push({
title: ref.title,
reference: ref.reference,
text,
verseData
});
}
mysteryDescriptions[mysteryType] = descriptions;
}
return {
mysteryDescriptions
};
};

View File

@@ -9,8 +9,12 @@ import AveMaria from "$lib/components/prayers/AveMaria.svelte";
import GloriaPatri from "$lib/components/prayers/GloriaPatri.svelte";
import FatimaGebet from "$lib/components/prayers/FatimaGebet.svelte";
import SalveRegina from "$lib/components/prayers/SalveRegina.svelte";
import RosaryFinalPrayer from "$lib/components/prayers/RosaryFinalPrayer.svelte";
import BenedictusMedal from "$lib/components/BenedictusMedal.svelte";
import CounterButton from "$lib/components/CounterButton.svelte";
import BibleModal from "$lib/components/BibleModal.svelte";
export let data;
// Mystery variations for each type of rosary
const mysteries = {
@@ -23,7 +27,7 @@ const mysteries = {
],
schmerzhaften: [
"Jesus, der für uns Blut geschwitzt hat.",
"Jesus, der für uns gegeißelt worden ist.",
"Jesus, der für uns gegeisselt worden ist.",
"Jesus, der für uns mit Dornen gekrönt worden ist.",
"Jesus, der für uns das schwere Kreuz getragen hat.",
"Jesus, der für uns gekreuzigt worden ist."
@@ -46,32 +50,64 @@ const mysteries = {
const mysteriesLatin = {
freudenreich: [
"Jesus, quem tu, Virgo, de Spiritu Sancto concepisti.",
"Jesus, quem tu, Virgo, ad Elisabeth portasti.",
"Jesus, quem tu, Virgo, Bethlehemi peperisti.",
"Jesus, quem tu, Virgo, in templo praesentasti.",
"Jesus, quem tu, Virgo, in templo invenisti."
"Jesus, quem, virgo, concepísti.",
"Jesus, quem visitándo Elísabeth portásti.",
"Jesus, quem, virgo, genuísti.",
"Jesus, quem in templo præsentásti.",
"Jesus, quem in templo invenisti."
],
schmerzhaften: [
"Jesus, qui pro nobis sanguinem sudavit.",
"Jesus, qui pro nobis flagellatus est.",
"Jesus, qui pro nobis spinis coronatus est.",
"Jesus, qui pro nobis crucem baiulavit.",
"Jesus, qui pro nobis sánguinem sudavit.",
"Jesus, qui pro nobis flagellátus est.",
"Jesus, qui pro nobis spinis coronátus est.",
"Jesus, qui pro nobis crucem baiulávit.",
"Jesus, qui pro nobis crucifixus est."
],
glorreichen: [
"Jesus, qui resurrexit a mortuis.",
"Jesus, qui in caelum ascendit.",
"Jesus, qui Spiritum Sanctum misit.",
"Jesus, qui te, Virgo, in caelum assumpsit.",
"Jesus, qui te, Virgo, in caelis coronavit."
"Jesus, qui resurréxit a mórtuis.",
"Jesus, qui ascendit in cælum.",
"Jesus, qui misit Spíritum Sanctum.",
"Jesus, qui te, virgo, in cælum assúmpsit.",
"Jesus, qui te, virgo, in cælo coronávit."
],
lichtreichen: [
"Jesus, qui a Ioanne baptizatus est.",
"Jesus, qui in Cana se manifestavit.",
"Jesus, qui regnum Dei proclamavit.",
"Jesus, qui in monte transfiguratus est.",
"Jesus, qui Eucharistiam donavit."
"Jesus, qui a Ioánne baptizátus est.",
"Jesus, qui se in Cana revelávit.",
"Jesus, qui regnum Dei prædicávit.",
"Jesus, qui in monte transfigurátus est.",
"Jesus, Sacraméntum Altáris instítuit."
]
};
// Short titles for mysteries (for display in headings)
const mysteryTitles = {
freudenreich: [
"Verkündigung",
"Heimsuchung",
"Geburt",
"Darstellung",
"Wiederfindung"
],
schmerzhaften: [
"Todesangst",
"Geisselung",
"Dornenkrönung",
"Kreuzweg",
"Kreuzigung"
],
glorreichen: [
"Auferstehung",
"Himmelfahrt",
"Geistsendung",
"Aufnahme Mariens",
"Krönung Mariens"
],
lichtreichen: [
"Taufe",
"Hochzeit zu Kana",
"Verkündigung des Reiches",
"Verklärung",
"Einsetzung der Eucharistie"
]
};
@@ -111,20 +147,27 @@ function getMysteryForWeekday(date, includeLuminous) {
// Determine which mystery to use based on current weekday
let selectedMystery = getMysteryForWeekday(new Date(), includeLuminous);
let todaysMystery = selectedMystery; // Track today's auto-selected mystery
let currentMysteries = mysteries[selectedMystery];
let currentMysteriesLatin = mysteriesLatin[selectedMystery];
let currentMysteryTitles = mysteryTitles[selectedMystery];
let currentMysteryDescriptions = data.mysteryDescriptions[selectedMystery] || [];
// Reactive statement to update mystery descriptions when selectedMystery changes
$: currentMysteryDescriptions = data.mysteryDescriptions[selectedMystery] || [];
// Function to switch mysteries
function selectMystery(mysteryType) {
selectedMystery = mysteryType;
currentMysteries = mysteries[mysteryType];
currentMysteriesLatin = mysteriesLatin[mysteryType];
currentMysteryTitles = mysteryTitles[mysteryType];
}
// Function to handle toggle change
function handleToggleChange() {
// Recalculate the default mystery for today
const todaysMystery = getMysteryForWeekday(new Date(), includeLuminous);
todaysMystery = getMysteryForWeekday(new Date(), includeLuminous);
// Update to today's mystery
selectMystery(todaysMystery);
}
@@ -143,6 +186,12 @@ let decadeCounters = {
secret5: 0
};
// Modal state for displaying Bible citations
let showModal = false;
let selectedReference = '';
let selectedTitle = '';
let selectedVerseData = null;
// Function to advance the counter for a specific decade
function advanceDecade(decadeNum) {
const key = `secret${decadeNum}`;
@@ -176,6 +225,14 @@ function advanceDecade(decadeNum) {
}
}
// Function to handle citation click
function handleCitationClick(reference, title = '', verseData = null) {
selectedReference = reference;
selectedTitle = title;
selectedVerseData = verseData;
showModal = true;
}
// Map sections to their vertical positions in the SVG
const sectionPositions = {
cross: 35,
@@ -545,9 +602,9 @@ onMount(() => {
justify-content: center;
}
/* Make SVG beads larger on mobile by scaling up */
/* Make SVG beads larger on mobile by scaling up and center it */
.rosary-visualization svg {
transform: scale(3.5) translateX(-5px);
transform: scale(3.5);
transform-origin: center top;
}
@@ -561,6 +618,11 @@ onMount(() => {
max-width: 100%;
padding-left: 1rem;
}
/* Reduce padding in prayer cards for mobile */
.prayer-section {
padding: 10rem;
}
}
.rosary-visualization {
@@ -604,7 +666,6 @@ onMount(() => {
}
.prayer-section {
min-height: 50vh;
scroll-snap-align: start;
padding: 2rem;
margin-bottom: 2rem;
@@ -622,6 +683,17 @@ onMount(() => {
.prayer-section.decade {
scroll-snap-align: start;
min-height: 50vh; /* Only decades need minimum height for scroll-snap */
padding-bottom: 2rem;
}
@media (max-width: 1023px) {
.prayer-section.decade {
padding-bottom: 1.5rem;
}
.prayer-section {
padding: 0.5rem;
}
}
.prayer-section h2 {
@@ -719,6 +791,7 @@ onMount(() => {
.rosary-visualization :global(.cross-symbol) {
fill: var(--nord4);
transition: all 0.3s ease;
font-family: crosses;
}
/* Active states */
@@ -745,35 +818,26 @@ h1 {
/* Luminous mysteries toggle */
.luminous-toggle {
text-align: center;
display: flex;
justify-content: center;
margin-bottom: 2rem;
padding: 1rem;
background: var(--nord1);
border-radius: 8px;
max-width: 600px;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
}
@media(prefers-color-scheme: light) {
.luminous-toggle {
background: var(--nord5);
}
}
.luminous-toggle label {
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
gap: 0.75rem;
cursor: pointer;
font-size: 1.1rem;
font-size: 0.95rem;
color: var(--nord4);
}
@media(prefers-color-scheme: light) {
.luminous-toggle label {
color: var(--nord0);
color: var(--nord2);
}
}
@@ -794,6 +858,7 @@ h1 {
transition: background 0.3s ease;
outline: none;
border: none;
flex-shrink: 0;
}
@media(prefers-color-scheme: light) {
@@ -823,34 +888,43 @@ h1 {
transform: translateX(20px);
}
.luminous-toggle .toggle-description {
margin-top: 1rem;
font-size: 0.95rem;
color: var(--nord8);
line-height: 1.6;
text-align: center;
max-width: 500px;
margin-left: auto;
margin-right: auto;
}
@media(prefers-color-scheme: light) {
.luminous-toggle .toggle-description {
color: var(--nord3);
}
}
/* Mystery selector grid */
.mystery-selector {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
margin-bottom: 3rem;
max-width: 1000px;
max-width: 750px;
margin-left: auto;
margin-right: auto;
}
.mystery-selector.four-mysteries {
grid-template-columns: repeat(2, 1fr);
max-width: 500px;
}
@media (min-width: 1024px) {
.mystery-selector.four-mysteries {
grid-template-columns: repeat(4, 1fr);
max-width: 900px;
}
}
@media (max-width: 768px) {
.mystery-selector:not(.four-mysteries) {
grid-template-columns: 1fr;
max-width: 400px;
}
}
@media (max-width: 500px) {
.mystery-selector.four-mysteries {
grid-template-columns: 1fr;
max-width: 400px;
}
}
.mystery-button {
background: var(--nord1);
border: 2px solid transparent;
@@ -863,6 +937,7 @@ h1 {
flex-direction: column;
align-items: center;
gap: 1rem;
position: relative;
}
@media(prefers-color-scheme: light) {
@@ -878,20 +953,18 @@ h1 {
.mystery-button.selected {
border-color: var(--nord10);
background: var(--nord2);
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
}
@media(prefers-color-scheme: light) {
.mystery-button.selected {
border-color: var(--nord10);
background: var(--nord5);
}
}
.mystery-button:nth-child(1):hover { background: var(--nord15); }
.mystery-button:nth-child(2):hover { background: var(--nord13); }
.mystery-button:nth-child(3):hover { background: var(--nord14); }
.mystery-button:nth-child(4):hover { background: var(--nord12); }
.mystery-button:nth-child(1):hover,
.mystery-button:nth-child(1).selected { background: var(--nord15); }
.mystery-button:nth-child(2):hover,
.mystery-button:nth-child(2).selected { background: var(--nord13); }
.mystery-button:nth-child(3):hover,
.mystery-button:nth-child(3).selected { background: var(--nord14); }
.mystery-button:nth-child(4):hover,
.mystery-button:nth-child(4).selected { background: var(--nord12); }
.mystery-button svg {
width: 80px;
@@ -927,49 +1000,135 @@ h1 {
font-weight: 700;
}
/* Today's mystery badge */
.today-badge {
position: absolute;
top: 1rem;
right: 1rem;
background: var(--nord11);
color: white;
padding: 0.4rem 0.8rem;
border-radius: 4px;
font-size: 0.85rem;
font-weight: 600;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
z-index: 10;
}
/* Highlighted bead (orange for counting) */
.rosary-visualization :global(.counted-bead) {
fill: var(--nord13) !important;
filter: drop-shadow(0 0 8px var(--nord13));
}
/* Mystery description styling */
.mystery-description {
margin: 1.5rem 0 1.5rem 0;
display: flex;
flex-direction: column;
gap: 0.75rem;
align-items: center;
}
.mystery-title {
font-weight: 700;
color: var(--nord10);
font-size: 1.1rem;
text-align: center;
}
.decade-buttons {
display: flex;
flex-direction: row;
gap: 1rem;
justify-content: flex-end;
align-items: center;
margin-top: 1.5rem;
}
.bible-reference-text {
color: var(--nord8);
font-size: 0.9rem;
font-weight: 600;
}
@media(prefers-color-scheme: light) {
.bible-reference-text {
color: var(--nord10);
}
}
.bible-reference-button {
background: var(--nord3);
border: 2px solid var(--nord2);
color: var(--nord6);
font-size: 1.2rem;
cursor: pointer;
padding: 0;
width: 3rem;
height: 3rem;
border-radius: 50%;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.bible-reference-button:hover {
background: var(--nord8);
border-color: var(--nord9);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.bible-reference-button:active {
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
@media(prefers-color-scheme: light) {
.bible-reference-button {
background: var(--nord5);
border-color: var(--nord4);
color: var(--nord0);
}
.bible-reference-button:hover {
background: var(--nord4);
border-color: var(--nord3);
}
}
</style>
<svelte:head>
<title>Rosenkranz - Interaktiv</title>
<title>Interaktiver Rosenkranz</title>
<meta name="description" content="Interaktive digitale Version des Rosenkranzes zum Mitbeten. Scrolle durch die Gebete und folge der Visualisierung.">
</svelte:head>
<div class="page-container">
<h1>Interaktiver Rosenkranz</h1>
<!-- Luminous Mysteries Toggle -->
<div class="luminous-toggle">
<label>
<input type="checkbox" bind:checked={includeLuminous} on:change={handleToggleChange} />
<span>Lichtreiche Geheimnisse einbeziehen</span>
</label>
<p class="toggle-description">
Die Geheimnisse werden automatisch nach dem Wochenplan ausgewählt.
{#if includeLuminous}
Mit lichtreichen Geheimnissen: Do=Lichtreich, andere Tage folgen dem traditionellen Plan.
{:else}
Traditioneller Plan ohne lichtreiche Geheimnisse.
{/if}
Sie können jederzeit manuell ein anderes Geheimnis wählen.
</p>
</div>
<h2 style="text-align:center;">Geheimnisse</h2>
<!-- Mystery Selector -->
<div class="mystery-selector">
<div class="mystery-selector" class:four-mysteries={includeLuminous}>
<button
class="mystery-button"
class:selected={selectedMystery === 'freudenreich'}
on:click={() => selectMystery('freudenreich')}
>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<!-- Placeholder: Star for joyful mysteries -->
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
{#if todaysMystery === 'freudenreich'}
<span class="today-badge">Heutige</span>
{/if}
<svg viewBox="-10 0 2058 2048">
<path d="M1935 90q0 32 -38 91q-21 29 -56 90q-20 55 -63 164q-35 86 -95 143q-22 -21 -43 -45q51 -49 85 -139q49 -130 61 -152q-126 48 -152 63q-76 46 -95 128q-27 -18 -58 -25q28 -104 97 -149q31 -20 138 -52q90 -28 137 -74l29 -39q22 -30 32 -30q21 0 21 26zM1714 653 q-90 30 -113 43q-65 36 -65 90q0 19 20 119q23 116 23 247q0 169 -103 299q-111 141 -275 141q-254 0 -283 87q-16 104 -31 207q-27 162 -76 162q-21 0 -41 -20q-16 -19 -32 -37q-10 3 -33 22q-18 15 -39 15q-28 0 -50 -44.5t-30 -44.5q-10 0 -35.5 11.5t-41.5 11.5 q-47 0 -58.5 -45.5t-21.5 -45.5t-29.5 2.5t-29.5 2.5q-46 0 -46 -30q0 -16 14 -44.5t14 -44.5q0 -8 -46.5 -25.5t-46.5 -48.5q0 -34 35.5 -52t99.5 -31q91 -19 103 -22q113 -32 171 -93q37 -39 105 -165q34 -64 43 -82q26 -53 31 -85q-129 -67 -224 -76q-33 0 -96 -11 q-36 -13 -36 -41q0 -7 2 -19.5t2 -19.5q0 -20 -67.5 -42t-67.5 -64q0 -11 8.5 -30t8.5 -30q0 -15 -79 -39t-79 -63q0 -16 9 -45t9 -45q0 -20 -29 -43q-23 -17 -46 -33q-49 -44 -49 -215q0 -8 1 -15q91 53 194 68l282 16q202 12 304 59q143 65 143 210q0 15 -2 44t-2 44 q0 122 78 122q73 0 108 -133q16 -70 32 -139q21 -81 57 -119q46 -51 130 -51q71 0 122 61q90 107 154 149zM1597 636q-25 -22 -77 -91q-30 -40 -75 -40q-91 0 -131 115q-30 106 -59 213q-44 115 -144 115q-146 0 -146 -180q0 -16 2.5 -46.5t2.5 -46.5q0 -62 -19 -87 q-70 -92 -303 -115q-173 -9 -347 -18q-55 -6 -116 -30v34q0 27 57.5 73.5t57.5 91.5q0 16 -10.5 45t-10.5 44q1 1 7 1q3 0 7 1q146 36 146 105q0 13 -8.5 32.5t-8.5 27.5h10q5 0 9 1q61 15 86 36q32 28 28 85q173 15 372 107q-7 77 -80 215q-67 128 -127 195 q-67 74 -169 104q-96 24 -193 47q-10 3 -29 13q86 18 86 70q0 19 -19 62q15 -5 33 -5q42 0 59 26q8 11 22 61l-1 3q10 0 34.5 -11.5t42.5 -11.5q55 0 88 84q38 -32 64 -32q37 0 66 41q25 -53 33 -151q10 -112 23 -154q43 -136 337 -136q116 0 215 -108q105 -114 105 -277 q0 -23 -12 -112l-28 -207q-4 -30 -4 -42q0 -97 124 -147zM1506 605q0 38 -38 38q-39 0 -39 -38t39 -38q38 0 38 38z" />
<path d="m 1724.44,1054.6641 c -31.1769,-18 -37.7653,-42.5884 -19.7653,-73.76528 5.3333,-9.2376 12.354,-16.7312 21.0621,-22.4808 6.2201,-4.1068 44.7886,-7.2427 115.7055,-9.4077 70.9168,-2.1649 110.128,-1.0807 117.6336,3.2526 30.0222,17.3334 35.5333,42.45448 16.5333,75.36348 -7.3333,12.7017 -16.1754,20.6833 -26.5263,23.9448 -24.5645,1.2137 -56.7805,3.0135 -96.648,5.3994 -72.6282,5.7957 -115.2931,5.0269 -127.9949,-2.3065 z" />
<path d="m 386.57764,1262.0569 c 53.44793,-14.3214 85.17574,-2.8075 95.18337,34.5417 9.83517,36.7052 -12.29319,62.3047 -66.38503,76.7986 l -82.1037,21.9996 c -54.09184,14.4939 -86.05533,3.3882 -95.89047,-33.317 -10.00766,-37.3491 12.67841,-63.4432 68.05807,-78.2821 z"/>
<path d="m 1115.7599,372.22724 c 14.3213,53.44793 2.8073,85.17581 -34.5418,95.18323 -36.705,9.83527 -62.3047,-12.29323 -76.7986,-66.38485 l -21.99962,-82.10394 c -14.4939,-54.09162 -3.3882,-86.05531 33.31712,-95.89019 37.349,-10.00765 63.4431,12.67818 78.2821,68.05802 z" />
<path d="m 1184.6228,1956.284 c -4.807,-8.0003 -6.8298,-42.7561 -6.0684,-104.2674 0.7614,-61.5113 2.7093,-100.0139 5.8437,-115.508 3.1343,-15.4941 11.8445,-27.5329 26.1306,-36.117 30.2866,-18.198 54.7006,-11.868 73.242,18.99 5.4937,9.1432 8.145,43.3269 7.9537,102.5512 -0.081,52.9359 -1.4296,89.5231 -4.0464,109.7617 -2.276,16.9226 -11.1284,30.0192 -26.5575,39.29 -33.1439,19.9148 -58.643,15.0146 -76.4977,-14.7005 z" />
<path d="m 1773.3127,1737.6952 c -9.0153,-2.4157 -34.6139,-26.0118 -76.7955,-70.7882 -42.1816,-44.7764 -67.5266,-73.826 -76.035,-87.1489 -8.5084,-13.3228 -10.6057,-28.0334 -6.2922,-44.1323 9.145,-34.1293 31.1041,-46.5353 65.8774,-37.2179 10.3033,2.7609 35.9565,25.5088 76.9595,68.2441 36.7142,38.1352 61.1596,65.3907 73.3362,81.7668 10.1182,13.7541 12.8479,29.3245 8.1892,46.7113 -10.0077,37.3492 -31.7542,51.5375 -65.2396,42.5651 z" />
</svg>
<h3>Freudenreich</h3>
<h3>Freudenreiche</h3>
</button>
<button
@@ -977,9 +1136,11 @@ h1 {
class:selected={selectedMystery === 'schmerzhaften'}
on:click={() => selectMystery('schmerzhaften')}
>
{#if todaysMystery === 'schmerzhaften'}
<span class="today-badge">Heutige</span>
{/if}
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<!-- Placeholder: Cross for sorrowful mysteries -->
<path d="M10 2h4v7h7v4h-7v9h-4v-9H3v-4h7V2z"/>
<svg viewBox="0 0 512 512" ><path d="M255.094 24.875c-16.73 9.388-34.47 42.043-41.688 59.47-14.608-2.407-28.87-3.664-42.562-3.75-11.446-.074-22.49.68-33.03 2.218-16.34-8.284-34.766-29.065-42.626-50-9.324 15.704-9.558 42.313-5.782 64.593-19.443 9.72-35.107 23.633-45.53 41.688-7.262 12.577-11.5 26.34-12.97 40.875 13.294-25.904 35-46.957 65.656-54.345-34.99 31.783-59.85 87.186-51.5 129.406-1.2 22.87-9.48 37.647-24.75 44.595 16.335 4.59 35.497 3.343 49.438-1.28 24.94 34.82 60.818 67.882 105.063 94.342-6.952 17.613-16.677 49.21-16.47 66.032 10.846-13.178 37.433-40.585 61.72-42.783 23.656 10.27 47.35 17.698 70.312 22.313 12.423 17.25 12.895 38.867 7.375 53.594 16.402-9.2 33.82-33.187 39.938-48 47.1 1.423 88.046-10.534 114.718-35.563 17.536 5.52 30.744 15.707 39.813 30.5.243-19.578-8.05-44.353-18-60.31 13.42-28.268 12.786-61.81.5-96.158l.405.47c9.976-11.804 18.304-33.19 18.063-52.907-8.535 10.373-20.727 15.14-36.75 14.188-13.56-22.597-31.81-44.812-54.032-65.375 10.56-19.27 30.402-36.43 44.156-47.97-18.985-5.337-67.794 5.2-80.78 17.782l5.906 8.5c5.637 11.99 9.503 24.423 11.093 37.063-26.323-37.275-70.72-74.72-114.905-95.625-15.894-25.424-19.322-56.118-12.78-73.563zm-82.875 97.063c1.13-.015 2.258-.008 3.405 0 31.56.2 68.888 8.842 107 25.656-8.8 20.095-14.74 44.482-10 61.344 13.33-18.637 37.313-34.22 55.406-37.5 55.904 34.315 96.215 78.718 111.658 118.718l.093.22c16.088 37.88 13.36 85.186-26.56 117.312 4.79-11.41 7.986-23.828 9.5-36.438-14.078 10.012-33.524 15.304-56.314 15.97-1.954-17.242-9.117-52.874-22.28-65.72 1.565 16.122-8.11 46.272-26.22 61.063-31.916-6.495-66.794-19.67-101.03-39.438-9.538-5.506-18.65-11.307-27.314-17.344-3.444-23.614 7.842-53.562 20.563-64.03-18.967-.234-46.71 22.156-59.313 32.75-40.974-38.47-64.14-81.11-61.25-115 16.275-1.708 36.144.927 51.72 8-3.92-15.382-18.553-31.733-34.407-44.344 14.757-13.826 37.7-20.852 65.344-21.22z"/></svg>
</svg>
<h3>Schmerzhaften</h3>
</button>
@@ -989,9 +1150,19 @@ h1 {
class:selected={selectedMystery === 'glorreichen'}
on:click={() => selectMystery('glorreichen')}
>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<!-- Placeholder: Crown for glorious mysteries -->
<path d="M5 16l3-6.5 4 4 4-4 3 6.5v4H5v-4zm0-13l2 2-2 2V5zm14 0l-2 2 2 2V5zM12 3l-2 2 2 2 2-2-2-2z"/>
{#if todaysMystery === 'glorreichen'}
<span class="today-badge">Heutige</span>
{/if}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="-10 0 2060 2048">
<path
d="M1968 505l-119 632q101 61 101 163q0 149 -228 212q-171 47 -356 47h-682q-47 0 -111 -8q-210 -26 -293 -55q-180 -62 -180 -196q0 -124 101 -163l-119 -632h37q87 0 170 43q-18 85 -18 103q0 116 75 130q31 -47 77 -129l40 147q49 -37 95 -37t100 37q9 -38 31 -113
q34 29 68 57q47 38 75 38q34 0 60 -27.5t26 -61.5q0 -26 -31 -74l-46 -72q46 -13 91 -26q55 -15 93 -15t93 15q45 13 91 26l-46 72q-31 51 -31 74q0 34 26 61.5t60 27.5q26 0 75 -38q34 -28 68 -57l31 113q66 -37 97 -37q56 0 95 37q14 -48 43 -145q39 66 77 127
q75 -14 75 -130q0 17 -18 -103q89 -43 207 -43zM1889 557h-29q-10 0 -17 7q0 94 -9 130q-14 63 -67 110q-33 29 -63 29q-28 0 -59 -41q-31 115 -31 169q57 -36 77 -36q75 0 75 119q0 78 -32 126h-183q-54 -79 -54 -198v-5q64 -28 64 -80q0 -30 -20 -52.5t-50 -22.5
q-33 0 -55 22.5t-22 55.5q0 53 46 74q-10 44 -21 86.5t-45.5 81t-39.5 38.5h-271q-21 -52 -21 -81q0 -65 47 -114.5t112 -49.5q29 0 106 36q7 -33 7 -82q0 -26 -7 -89q-42 43 -106 43q-65 0 -112 -49.5t-47 -114.5q0 -40 33 -105q-26 -7 -70 -7q-48 0 -70 7q33 63 33 105
q0 65 -47 114.5t-112 49.5q-60 0 -106 -43q-7 63 -7 87q0 53 7 84q70 -36 106 -36q65 0 112 49.5t47 114.5q0 32 -21 81h-271q-16 0 -57 -58q-21 -30 -32 -72q-8 -38 -17 -76q46 -14 46 -74q0 -78 -77 -78q-30 0 -50 22t-20 53q0 48 64 80v4q0 125 -54 199h-183
q-32 -54 -32 -124q0 -121 75 -121q19 0 77 36v-20q0 -27 -31 -151q-27 43 -59 43q-19 0 -51 -19q-40 -24 -67 -87q-24 -57 -24 -109q0 -10 1 -29t1 -28q-18 -1 -23 -1q-13 0 -22 1l46 241q64 17 64 101q0 51 -30 51q-3 0 -6 -1q19 83 39 212l-2 4q-102 20 -102 110
q0 141 342 175q132 13 150 13h726q-9 0 55 -5q437 -34 437 -183q0 -88 -105 -111l40 -215q-2 0 -5 1q-31 0 -31 -51q0 -32 16 -62q19 -34 48 -39zM1518 888q0 34 -30 34q-34 0 -34 -34t32 -34t32 34zM1099 880q0 30 -22 51t-52 21q-29 0 -51.5 -21.5t-22.5 -50.5
q0 -31 22 -54.5t52 -23.5q31 0 52.5 23.5t21.5 54.5zM596 888q0 34 -34 34q-30 0 -30 -34t32 -34t32 34z" />
</svg>
<h3>Glorreichen</h3>
</button>
@@ -1002,92 +1173,107 @@ h1 {
class:selected={selectedMystery === 'lichtreichen'}
on:click={() => selectMystery('lichtreichen')}
>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<!-- Placeholder: Candle/Light for luminous mysteries -->
<path d="M9 2h6v2H9V2zm3 3c-2.76 0-5 2.24-5 5 0 2.04 1.23 3.79 3 4.58V21h4v-6.42c1.77-.79 3-2.54 3-4.58 0-2.76-2.24-5-5-5zm0 2c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3z"/>
{#if todaysMystery === 'lichtreichen'}
<span class="today-badge">Heutige</span>
{/if}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" viewBox="-10 0 2156 2048">
<path
d="M1668 383q0 14 -48.5 92.5t-64.5 96t-41 17.5q-53 0 -53 -54q0 -16 46 -92q41 -68 60 -92q16 -20 43 -20q58 0 58 52zM688 535q0 54 -54 54q-16 0 -30 -7q-10 -5 -66 -95.5t-56 -103.5q0 -52 57 -52q22 0 34 11q20 31 53 81q62 90 62 112zM2064 842q0 59 -56 100
q-231 162 -468 342l190 586q1 4 -5 28q-22 84 -110 84q-23 0 -45 -11q-18 -9 -203 -146l-291 -213q-125 89 -328 238q-51 39 -156 114q-28 18 -63 18q-46 0 -78.5 -32t-34.5 -78l194 -589q-76 -58 -197 -144q-81 -57 -163 -114q-126 -91 -147 -118t-21 -65q0 -36 29.5 -75.5
t64.5 -39.5h604q33 -94 126 -375q19 -62 61 -184q29 -73 108 -73t110 83q4 11 58 177l123 372h607q34 0 64 41q27 38 27 74zM1129 1958q0 83 -58 83q-57 0 -57 -84v-85q0 -84 57 -84q58 0 58 86v84zM1943 849h-659l-211 -636l-207 629h-663l541 397l-206 621l537 -386
l536 389l-209 -629zM1671 934l-370 267l150 436l-378 -271l-371 271q8 -34 15 -68q10 -41 28 -62q46 -53 144 -120q80 -53 159 -106l296 210l-112 -344l299 -213h140z" />
</svg>
<h3>Lichtreichen</h3>
</button>
{/if}
</div>
<!-- Luminous Mysteries Toggle -->
<div class="luminous-toggle">
<label>
<input type="checkbox" bind:checked={includeLuminous} on:change={handleToggleChange} />
<span>Lichtreiche Geheimnisse einbeziehen</span>
</label>
</div>
<div class="rosary-layout">
<!-- Sidebar: Rosary Visualization -->
<div class="rosary-sidebar">
<div class="rosary-visualization" bind:this={svgContainer}>
<svg class="linear-rosary" viewBox="-100 -100 250 2200" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMin meet">
<!-- Vertical chain -->
<line x1="50" y1="35" x2="50" y2="1655" class="chain" />
<line x1="25" y1="35" x2="25" y2="1655" class="chain" />
<!-- Cross (at top) -->
<g id="cross-section" data-section="cross">
<text x="50" y="35" text-anchor="middle" font-size="80"
<text x="25" y="35" text-anchor="middle" font-size="80"
class="cross-symbol" class:active-cross={activeSection === 'cross'}>♱</text>
</g>
<!-- First large bead (Paternoster) -->
<circle cx="50" cy="80" r="15" class="large-bead" class:active-large-bead={activeSection === 'lbead1'} data-section="lbead1" />
<circle cx="25" cy="80" r="15" class="large-bead" class:active-large-bead={activeSection === 'lbead1'} data-section="lbead1" />
<!-- Three small beads -->
<circle cx="50" cy="110" r="10" class="bead" class:active-bead={activeSection === 'start1'} data-section="start1" />
<circle cx="50" cy="135" r="10" class="bead" class:active-bead={activeSection === 'start2'} data-section="start2" />
<circle cx="50" cy="160" r="10" class="bead" class:active-bead={activeSection === 'start3'} data-section="start3" />
<circle cx="25" cy="110" r="10" class="bead" class:active-bead={activeSection === 'start1'} data-section="start1" />
<circle cx="25" cy="135" r="10" class="bead" class:active-bead={activeSection === 'start2'} data-section="start2" />
<circle cx="25" cy="160" r="10" class="bead" class:active-bead={activeSection === 'start3'} data-section="start3" />
<!-- Large bead before decades -->
<circle cx="50" cy="200" r="15" class="large-bead" class:active-large-bead={activeSection === 'lbead2'} data-section="lbead2" />
<circle cx="25" cy="200" r="15" class="large-bead" class:active-large-bead={activeSection === 'lbead2'} data-section="lbead2" />
<!-- Benedictus Medal -->
<BenedictusMedal x={30} y={220} size={40} />
<BenedictusMedal x={5} y={220} size={40} />
<!-- Decade 1: Ave Maria (10 beads) -->
{#each Array(10) as _, i}
<circle cx="50" cy={280 + i * 22} r="10" class="bead"
<circle cx="25" cy={280 + i * 22} r="10" class="bead"
class:active-bead={activeSection === 'secret1'}
class:counted-bead={i < decadeCounters.secret1}
data-section="secret1" />
{/each}
<!-- Transition 1: Gloria + Fatima + Paternoster (large bead) -->
<circle cx="50" cy="520" r="15" class="large-bead" class:active-large-bead={activeSection === 'secret1_transition'} data-section="secret1_transition" />
<circle cx="25" cy="520" r="15" class="large-bead" class:active-large-bead={activeSection === 'secret1_transition'} data-section="secret1_transition" />
<!-- Decade 2: Ave Maria (10 beads) -->
{#each Array(10) as _, i}
<circle cx="50" cy={560 + i * 22} r="10" class="bead"
<circle cx="25" cy={560 + i * 22} r="10" class="bead"
class:active-bead={activeSection === 'secret2'}
class:counted-bead={i < decadeCounters.secret2}
data-section="secret2" />
{/each}
<!-- Transition 2: Gloria + Fatima + Paternoster (large bead) -->
<circle cx="50" cy="800" r="15" class="large-bead" class:active-large-bead={activeSection === 'secret2_transition'} data-section="secret2_transition" />
<circle cx="25" cy="800" r="15" class="large-bead" class:active-large-bead={activeSection === 'secret2_transition'} data-section="secret2_transition" />
<!-- Decade 3: Ave Maria (10 beads) -->
{#each Array(10) as _, i}
<circle cx="50" cy={840 + i * 22} r="10" class="bead"
<circle cx="25" cy={840 + i * 22} r="10" class="bead"
class:active-bead={activeSection === 'secret3'}
class:counted-bead={i < decadeCounters.secret3}
data-section="secret3" />
{/each}
<!-- Transition 3: Gloria + Fatima + Paternoster (large bead) -->
<circle cx="50" cy="1080" r="15" class="large-bead" class:active-large-bead={activeSection === 'secret3_transition'} data-section="secret3_transition" />
<circle cx="25" cy="1080" r="15" class="large-bead" class:active-large-bead={activeSection === 'secret3_transition'} data-section="secret3_transition" />
<!-- Decade 4: Ave Maria (10 beads) -->
{#each Array(10) as _, i}
<circle cx="50" cy={1120 + i * 22} r="10" class="bead"
<circle cx="25" cy={1120 + i * 22} r="10" class="bead"
class:active-bead={activeSection === 'secret4'}
class:counted-bead={i < decadeCounters.secret4}
data-section="secret4" />
{/each}
<!-- Transition 4: Gloria + Fatima + Paternoster (large bead) -->
<circle cx="50" cy="1360" r="15" class="large-bead" class:active-large-bead={activeSection === 'secret4_transition'} data-section="secret4_transition" />
<circle cx="25" cy="1360" r="15" class="large-bead" class:active-large-bead={activeSection === 'secret4_transition'} data-section="secret4_transition" />
<!-- Decade 5: Ave Maria (10 beads) -->
{#each Array(10) as _, i}
<circle cx="50" cy={1400 + i * 22} r="10" class="bead"
<circle cx="25" cy={1400 + i * 22} r="10" class="bead"
class:active-bead={activeSection === 'secret5'}
class:counted-bead={i < decadeCounters.secret5}
data-section="secret5" />
{/each}
<!-- Final transition: Gloria + Fatima -->
<circle cx="50" cy="1640" r="15" class="large-bead" class:active-large-bead={activeSection === 'final_transition'} data-section="final_transition" />
<circle cx="25" cy="1640" r="15" class="large-bead" class:active-large-bead={activeSection === 'final_transition'} data-section="final_transition" />
</svg>
</div>
</div>
@@ -1123,9 +1309,9 @@ h1 {
bind:this={sectionElements.start1}
data-section="start1"
>
<h3>Ave Maria</h3>
<h3>Ave Maria: Glaube</h3>
<AveMaria
mysteryLatin="Jesus, qui fidem in nobis augeat"
mysteryLatin="Jesus, qui adáugeat nobis fidem"
mystery="Jesus, der in uns den Glauben vermehre"
/>
</div>
@@ -1136,9 +1322,9 @@ h1 {
bind:this={sectionElements.start2}
data-section="start2"
>
<h3>Ave Maria</h3>
<h3>Ave Maria: Hoffnung</h3>
<AveMaria
mysteryLatin="Jesus, qui spem in nobis firmet"
mysteryLatin="Jesus, qui corróboret nobis spem"
mystery="Jesus, der in uns die Hoffnung stärke"
/>
</div>
@@ -1149,9 +1335,9 @@ h1 {
bind:this={sectionElements.start3}
data-section="start3"
>
<h3>Ave Maria</h3>
<h3>Ave Maria: Liebe</h3>
<AveMaria
mysteryLatin="Jesus, qui caritatem in nobis accendat"
mysteryLatin="Jesus, qui perficiat in nobis caritátem"
mystery="Jesus, der in uns die Liebe entzünde"
/>
</div>
@@ -1176,16 +1362,31 @@ h1 {
bind:this={sectionElements[`secret${decadeNum}`]}
data-section="secret{decadeNum}"
>
<h2>{decadeNum}. Gesätz</h2>
<h2>{decadeNum}. Gesätz: {currentMysteryTitles[decadeNum - 1]}</h2>
<!-- Mystery description with Bible reference button -->
<h3>Ave Maria <span class="repeat-count">(10×)</span></h3>
<AveMaria
mysteryLatin={currentMysteriesLatin[decadeNum - 1]}
mystery={currentMysteries[decadeNum - 1]}
/>
<!-- Counter button -->
<!-- Bible reference and counter buttons -->
<div class="decade-buttons">
{#if currentMysteryDescriptions[decadeNum - 1]}
{@const description = currentMysteryDescriptions[decadeNum - 1]}
<span class="bible-reference-text">{description.reference}</span>
<button
class="bible-reference-button"
on:click={() => handleCitationClick(description.reference, description.title, description.verseData)}
aria-label="Bibelstelle anzeigen"
>
📖
</button>
{/if}
<CounterButton onClick={() => advanceDecade(decadeNum)} />
</div>
</div>
<!-- Transition prayers (Gloria, Fatima, Paternoster) -->
{#if decadeNum < 5}
@@ -1222,6 +1423,11 @@ h1 {
<h3>Salve Regina</h3>
<SalveRegina />
<h3>Schlussgebet</h3>
<RosaryFinalPrayer />
<h3 style="text-align: center; font-size: 2.5rem; margin-top: 2rem;"></h3>
</div>
</div>
</div>
@@ -1318,7 +1524,7 @@ Der Plan ohne lichtreiche Geheimnisse ist wie folgt:
<h3>Die schmerzhaften Geheimnisse <i>(über das Leiden und Sterben Jesu)</i></h3>
<ol><!-- dolorosa -->
<li>... Jesus, der für uns Blut geschwitzt hat.</li>
<li>... Jesus, der für uns gegeißelt worden ist.</li>
<li>... Jesus, der für uns gegeisselt worden ist.</li>
<li>... Jesus, der für uns mit Dornen gekrönt worden ist.</li>
<li>... Jesus, der für uns das schwere Kreuz getragen hat.</li>
<li>... Jesus, der für uns gekreuzigt worden ist.</li>
@@ -1367,3 +1573,8 @@ Anders als die Geheimnisse in Deutsch ist es üblich beim beten des Rosenkranzes
</ol>
</div>
</div>
<!-- Bible citation modal -->
{#if showModal}
<BibleModal reference={selectedReference} title={selectedTitle} verseData={selectedVerseData} onClose={() => showModal = false} />
{/if}

View File

@@ -1,24 +0,0 @@
import type { PageServerLoad } from './$types';
import { error } from '@sveltejs/kit';
import { dbConnect } from '$utils/db';
import { MarioKartTournament } from '$models/MarioKartTournament';
export const load: PageServerLoad = async () => {
try {
await dbConnect();
const tournaments = await MarioKartTournament.find()
.sort({ createdAt: -1 })
.lean({ flattenMaps: true });
// Convert MongoDB documents to plain objects for serialization
const serializedTournaments = JSON.parse(JSON.stringify(tournaments));
return {
tournaments: serializedTournaments
};
} catch (err) {
console.error('Error loading tournaments:', err);
throw error(500, 'Failed to load tournaments');
}
};

View File

@@ -1,569 +0,0 @@
<script>
import { goto } from '$app/navigation';
import { invalidateAll } from '$app/navigation';
let { data } = $props();
let tournaments = $state(data.tournaments);
let showCreateModal = $state(false);
let newTournamentName = $state('');
let roundsPerMatch = $state(3);
let matchSize = $state(2);
let loading = $state(false);
async function createTournament() {
if (!newTournamentName.trim()) {
alert('Please enter a tournament name');
return;
}
loading = true;
try {
const response = await fetch('/api/mario-kart/tournaments', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: newTournamentName,
roundsPerMatch,
matchSize
})
});
if (response.ok) {
const data = await response.json();
showCreateModal = false;
newTournamentName = '';
goto(`/mario-kart/${data.tournament._id}`);
} else {
const error = await response.json();
alert(error.error || 'Failed to create tournament');
}
} catch (error) {
console.error('Failed to create tournament:', error);
alert('Failed to create tournament');
} finally {
loading = false;
}
}
async function deleteTournament(id, name) {
if (!confirm(`Are you sure you want to delete "${name}"?`)) {
return;
}
try {
const response = await fetch(`/api/mario-kart/tournaments/${id}`, {
method: 'DELETE'
});
if (response.ok) {
await invalidateAll();
} else {
const error = await response.json();
alert(error.error || 'Failed to delete tournament');
}
} catch (error) {
console.error('Failed to delete tournament:', error);
alert('Failed to delete tournament');
}
}
function getStatusBadge(status) {
const badges = {
setup: { text: 'Setup', class: 'badge-blue' },
group_stage: { text: 'Group Stage', class: 'badge-yellow' },
bracket: { text: 'Bracket', class: 'badge-purple' },
completed: { text: 'Completed', class: 'badge-green' }
};
return badges[status] || badges.setup;
}
function formatDate(dateString) {
return new Date(dateString).toLocaleDateString();
}
</script>
<div class="container">
<div class="header">
<div class="header-content">
<h1>Mario Kart Tournament Tracker</h1>
<p>Manage your company Mario Kart tournaments</p>
</div>
<button class="btn-primary" onclick={() => showCreateModal = true}>
Create Tournament
</button>
</div>
{#if tournaments.length === 0}
<div class="empty-state">
<div class="empty-icon">🏁</div>
<h2>No tournaments yet</h2>
<p>Create your first Mario Kart tournament to get started!</p>
<button class="btn-primary" onclick={() => showCreateModal = true}>
Create Your First Tournament
</button>
</div>
{:else}
<div class="tournaments-grid">
{#each tournaments as tournament}
<div class="tournament-card">
<div class="card-header">
<h3>{tournament.name}</h3>
<span class="badge {getStatusBadge(tournament.status).class}">
{getStatusBadge(tournament.status).text}
</span>
</div>
<div class="card-stats">
<div class="stat">
<span class="stat-icon">👥</span>
<span>{tournament.contestants.length} contestants</span>
</div>
{#if tournament.groups.length > 0}
<div class="stat">
<span class="stat-icon">🎮</span>
<span>{tournament.groups.length} groups</span>
</div>
{/if}
<div class="stat">
<span class="stat-icon">🔄</span>
<span>{tournament.roundsPerMatch} rounds/match</span>
</div>
</div>
<div class="card-footer">
<span class="date">Created {formatDate(tournament.createdAt)}</span>
<div class="actions">
<a href="/mario-kart/{tournament._id}" class="btn-view">View</a>
<button
class="btn-delete"
onclick={() => deleteTournament(tournament._id, tournament.name)}
>
Delete
</button>
</div>
</div>
</div>
{/each}
</div>
{/if}
</div>
{#if showCreateModal}
<div class="modal-overlay" onclick={() => showCreateModal = false}>
<div class="modal" onclick={(e) => e.stopPropagation()}>
<div class="modal-header">
<h2>Create New Tournament</h2>
<button class="close-btn" onclick={() => showCreateModal = false}>×</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="tournament-name">Tournament Name</label>
<input
id="tournament-name"
type="text"
bind:value={newTournamentName}
placeholder="e.g., Company Championship 2024"
class="input"
/>
</div>
<div class="form-group">
<label for="rounds-per-match">Rounds per Match</label>
<input
id="rounds-per-match"
type="number"
bind:value={roundsPerMatch}
min="1"
max="10"
class="input"
/>
<small>How many races should each match have?</small>
</div>
<div class="form-group">
<label for="match-size">Match Size (Contestants per Match)</label>
<input
id="match-size"
type="number"
bind:value={matchSize}
min="2"
max="12"
class="input"
/>
<small>How many contestants compete simultaneously? (2 for 1v1, 4 for 4-player matches)</small>
</div>
</div>
<div class="modal-footer">
<button class="btn-secondary" onclick={() => showCreateModal = false}>
Cancel
</button>
<button
class="btn-primary"
onclick={createTournament}
disabled={loading}
>
{loading ? 'Creating...' : 'Create Tournament'}
</button>
</div>
</div>
</div>
{/if}
<style>
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
flex-wrap: wrap;
gap: 1rem;
}
.header-content h1 {
font-size: 2rem;
font-weight: 800;
color: #1f2937;
margin: 0 0 0.5rem 0;
}
.header-content p {
color: #6b7280;
margin: 0;
}
.empty-state {
text-align: center;
padding: 4rem 2rem;
background: white;
border-radius: 1rem;
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1);
}
.empty-icon {
font-size: 4rem;
margin-bottom: 1rem;
}
.empty-state h2 {
font-size: 1.5rem;
color: #1f2937;
margin-bottom: 0.5rem;
}
.empty-state p {
color: #6b7280;
margin-bottom: 2rem;
}
.tournaments-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 1.5rem;
}
.tournament-card {
background: white;
border-radius: 1rem;
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1);
padding: 1.5rem;
transition: transform 0.2s, box-shadow 0.2s;
}
.tournament-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: start;
margin-bottom: 1rem;
gap: 1rem;
}
.card-header h3 {
font-size: 1.25rem;
font-weight: 600;
color: #1f2937;
margin: 0;
flex: 1;
}
.badge {
padding: 0.25rem 0.75rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 600;
white-space: nowrap;
}
.badge-blue {
background: #dbeafe;
color: #1e40af;
}
.badge-yellow {
background: #fef3c7;
color: #92400e;
}
.badge-purple {
background: #e9d5ff;
color: #6b21a8;
}
.badge-green {
background: #d1fae5;
color: #065f46;
}
.card-stats {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 1rem;
padding: 1rem;
background: #f9fafb;
border-radius: 0.5rem;
}
.stat {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.875rem;
color: #4b5563;
}
.stat-icon {
font-size: 1.25rem;
}
.card-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 1rem;
border-top: 1px solid #e5e7eb;
}
.date {
font-size: 0.875rem;
color: #6b7280;
}
.actions {
display: flex;
gap: 0.5rem;
}
.btn-primary {
background: #3b82f6;
color: white;
border: none;
padding: 0.625rem 1.25rem;
border-radius: 0.5rem;
font-weight: 500;
cursor: pointer;
transition: background 0.2s;
}
.btn-primary:hover:not(:disabled) {
background: #2563eb;
}
.btn-primary:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.btn-secondary {
background: white;
color: #374151;
border: 1px solid #d1d5db;
padding: 0.625rem 1.25rem;
border-radius: 0.5rem;
font-weight: 500;
cursor: pointer;
transition: background 0.2s;
}
.btn-secondary:hover {
background: #f9fafb;
}
.btn-view {
background: #10b981;
color: white;
padding: 0.5rem 1rem;
border-radius: 0.375rem;
text-decoration: none;
font-size: 0.875rem;
font-weight: 500;
transition: background 0.2s;
}
.btn-view:hover {
background: #059669;
}
.btn-delete {
background: #ef4444;
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 0.375rem;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: background 0.2s;
}
.btn-delete:hover {
background: #dc2626;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 50;
padding: 1rem;
}
.modal {
background: white;
border-radius: 1rem;
max-width: 500px;
width: 100%;
box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
border-bottom: 1px solid #e5e7eb;
}
.modal-header h2 {
font-size: 1.5rem;
font-weight: 600;
color: #1f2937;
margin: 0;
}
.close-btn {
background: none;
border: none;
font-size: 2rem;
color: #9ca3af;
cursor: pointer;
line-height: 1;
padding: 0;
width: 2rem;
height: 2rem;
}
.close-btn:hover {
color: #4b5563;
}
.modal-body {
padding: 1.5rem;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group:last-child {
margin-bottom: 0;
}
.form-group label {
display: block;
font-weight: 500;
color: #374151;
margin-bottom: 0.5rem;
}
.input {
width: 100%;
padding: 0.625rem;
border: 1px solid #d1d5db;
border-radius: 0.5rem;
font-size: 1rem;
box-sizing: border-box;
}
.input:focus {
outline: none;
border-color: #3b82f6;
ring: 2px;
ring-color: rgba(59, 130, 246, 0.5);
}
.form-group small {
display: block;
color: #6b7280;
font-size: 0.875rem;
margin-top: 0.25rem;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 0.75rem;
padding: 1.5rem;
border-top: 1px solid #e5e7eb;
}
@media (max-width: 768px) {
.container {
padding: 1rem;
}
.header {
flex-direction: column;
align-items: stretch;
}
.tournaments-grid {
grid-template-columns: 1fr;
}
.card-footer {
flex-direction: column;
gap: 1rem;
align-items: stretch;
}
.actions {
justify-content: stretch;
}
.btn-view,
.btn-delete {
flex: 1;
text-align: center;
}
}
</style>

View File

@@ -1,43 +0,0 @@
import type { PageServerLoad } from './$types';
import { error } from '@sveltejs/kit';
import { dbConnect } from '$utils/db';
import { MarioKartTournament } from '$models/MarioKartTournament';
export const load: PageServerLoad = async ({ params }) => {
try {
await dbConnect();
// Use lean with flattenMaps option to convert Map objects to plain objects
const tournament = await MarioKartTournament.findById(params.id).lean({ flattenMaps: true });
if (!tournament) {
throw error(404, 'Tournament not found');
}
console.log('=== SERVER LOAD DEBUG ===');
console.log('Raw tournament bracket:', tournament.bracket);
if (tournament.bracket?.rounds) {
console.log('First bracket round matches:', tournament.bracket.rounds[0]?.matches);
}
console.log('=== END SERVER LOAD DEBUG ===');
// Convert _id and other MongoDB ObjectIds to strings for serialization
const serializedTournament = JSON.parse(JSON.stringify(tournament));
console.log('=== SERIALIZED DEBUG ===');
if (serializedTournament.bracket?.rounds) {
console.log('Serialized first bracket round matches:', serializedTournament.bracket.rounds[0]?.matches);
}
console.log('=== END SERIALIZED DEBUG ===');
return {
tournament: serializedTournament
};
} catch (err: any) {
if (err.status === 404) {
throw err;
}
console.error('Error loading tournament:', err);
throw error(500, 'Failed to load tournament');
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -52,7 +52,7 @@ h1{
<p>
Die Fensterprobe ist eine Methode um den optimalen Knetzustand eines Teiges zu bestimmen.
Dazu wird ein kleines, ca. Walnussgrosses Stück Teig zwischen den Fingern auseinandergezogen. Ist der Teig elastisch und reißt nicht bis der Teig so dünn ist, dass man leicht licht durchsehen kann, so ist der Teig optimal verknetet.
Dazu wird ein kleines, ca. Walnussgrosses Stück Teig zwischen den Fingern auseinandergezogen. Ist der Teig elastisch und reisst nicht bis der Teig so dünn ist, dass man leicht licht durchsehen kann, so ist der Teig optimal verknetet.
</p>
<p>
Teig lässt sich leichter verkneten wenn er noch trockener ist. Daher lohnt es sich zunächst etwa 10% der Flüssigkeit zurückzuhalten und erst nach und nach zuzugeben nachdem der Teig bereits für einige Minuten geknetet wurde.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
static/fonts/crosses.ttf Normal file

Binary file not shown.

BIN
static/fonts/crosses.woff2 Normal file

Binary file not shown.

Binary file not shown.