rewrite website
All checks were successful
deploy / build-and-deploy (push) Successful in 16s

This commit is contained in:
2025-10-13 17:22:49 +02:00
parent a6d910f56a
commit 55c0852b7e
263 changed files with 17905 additions and 14451 deletions

View File

@@ -0,0 +1,145 @@
<script lang="ts">
import { DynamicSettings } from './dynamicSettings.ts';
import { confirmPopupState } from '@components/popup/ConfirmPopup.ts';
import { updateSettings } from './actions.ts';
// types
interface Props {
settings: { name: string; value: string }[];
}
type SettingsInput = {
name: string;
entries: (
| {
name: string;
type: 'checkbox';
value: boolean;
onChange: (value: boolean) => void;
}
| {
name: string;
type: 'text';
value: string;
onChange: (value: string) => void;
}
| {
name: string;
type: 'textarea';
value: string;
onChange: (value: string) => void;
}
)[];
}[];
// inputs
const { settings }: Props = $props();
const dynamicSettings = new DynamicSettings(
settings.reduce((prev, curr) => Object.assign(prev, { [curr.name]: curr.value }), {})
);
let changes = $state<{ [k: string]: string | null }>(dynamicSettings.getChanges());
// consts
const settingsInput: SettingsInput = [
{
name: 'Anmeldung',
entries: [
{
name: 'Aktiviert',
type: 'checkbox',
value: dynamicSettings.signupEnabled(),
onChange: dynamicSettings.signupSetEnabled
},
{
name: 'Text unter dem Anmelde Button',
type: 'textarea',
value: dynamicSettings.signupInfoText(),
onChange: dynamicSettings.signupSetInfoText
},
{
name: 'Text, wenn die Anmeldung deaktiviert ist',
type: 'textarea',
value: dynamicSettings.signupDisabledText(),
onChange: dynamicSettings.signupSetDisabledText
},
{
name: 'Subtext, wenn die Anmeldung deaktiviert ist',
type: 'textarea',
value: dynamicSettings.signupDisabledSubtext(),
onChange: dynamicSettings.signupSetDisabledSubtext
}
]
}
];
// callbacks
function onSaveSettingsClick() {
$confirmPopupState = {
title: 'Änderungen speichern?',
message: 'Sollen die Änderungen gespeichert werden?',
onConfirm: async () => {
if (!(await updateSettings(changes))) return;
dynamicSettings.setChanges();
changes = {};
}
};
}
</script>
<div class="h-full flex flex-col items-center justify-between">
<div class="grid grid-cols-2 w-full">
{#each settingsInput as setting (setting.name)}
<div class="mx-12">
<div class="divider">{setting.name}</div>
<div class="flex flex-col gap-5">
{#each setting.entries as entry (entry.name)}
<label class="flex justify-between">
<span class="mt-[.125rem] text-sm w-1/2">{entry.name}</span>
<div class="w-1/2">
{#if entry.type === 'checkbox'}
<input
type="checkbox"
class="toggle"
onchange={(e) => {
entry.onChange(e.currentTarget.checked);
changes = dynamicSettings.getChanges();
}}
checked={entry.value}
/>
{:else if entry.type === 'text'}
<input
type="text"
class="input input-bordered"
onchange={(e) => {
entry.onChange(e.currentTarget.value);
changes = dynamicSettings.getChanges();
}}
value={entry.value}
/>
{:else if entry.type === 'textarea'}
<textarea
class="textarea"
value={entry.value}
onchange={(e) => {
entry.onChange(e.currentTarget.value);
changes = dynamicSettings.getChanges();
}}
></textarea>
{/if}
</div>
</label>
{/each}
</div>
</div>
{/each}
</div>
<div>
<button
class="btn btn-success mt-auto mb-8"
class:btn-disabled={Object.keys(changes).length === 0}
onclick={onSaveSettingsClick}>Speichern</button
>
</div>
</div>

View File

@@ -0,0 +1,19 @@
import { actions } from 'astro:actions';
import { actionErrorPopup } from '@util/action.ts';
export async function updateSettings(changes: { [k: string]: string | null }) {
const { error } = await actions.settings.setSettings({
settings: Object.entries(changes).reduce(
(prev, curr) => {
prev.push({ name: curr[0], value: curr[1] });
return prev;
},
[] as { name: string; value: string | null }[]
)
});
if (error) {
actionErrorPopup(error);
return false;
}
return true;
}

View File

@@ -0,0 +1,48 @@
import { SettingKey } from '@util/settings.ts';
export class DynamicSettings {
private settings: { [k: string]: string | null };
private changedSettings: { [k: string]: string | null } = {};
constructor(settings: typeof this.settings) {
this.settings = settings;
}
private get<V extends string | boolean>(key: string, defaultValue: V): V {
const setting = this.changedSettings[key] ?? this.settings[key];
return setting != null ? JSON.parse(setting) : defaultValue;
}
private set<V extends string | boolean>(key: string, value: V | null) {
if (this.settings[key] == value) {
delete this.changedSettings[key];
} else {
this.changedSettings[key] = value != null ? JSON.stringify(value) : null;
}
}
getChanges() {
return this.changedSettings;
}
setChanges() {
this.settings = Object.assign(this.settings, this.changedSettings);
this.changedSettings = {};
}
/* signup enabled */
signupEnabled = () => this.get(SettingKey.SignupEnabled, false);
signupSetEnabled = (active: boolean) => this.set(SettingKey.SignupEnabled, active);
/* signup info text */
signupInfoText = () => this.get(SettingKey.SignupInfoMessage, '');
signupSetInfoText = (text: string) => this.set(SettingKey.SignupInfoMessage, text);
/* signup disabled text */
signupDisabledText = () => this.get(SettingKey.SignupDisabledMessage, '');
signupSetDisabledText = (text: string) => this.set(SettingKey.SignupDisabledMessage, text);
/* signup disabled subtext */
signupDisabledSubtext = () => this.get(SettingKey.SignupDisabledSubMessage, '');
signupSetDisabledSubtext = (text: string) => this.set(SettingKey.SignupDisabledSubMessage, text);
}