This commit is contained in:
		
							
								
								
									
										145
									
								
								src/app/admin/settings/Settings.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								src/app/admin/settings/Settings.svelte
									
									
									
									
									
										Normal 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> | ||||
							
								
								
									
										19
									
								
								src/app/admin/settings/actions.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								src/app/admin/settings/actions.ts
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
							
								
								
									
										48
									
								
								src/app/admin/settings/dynamicSettings.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/app/admin/settings/dynamicSettings.ts
									
									
									
									
									
										Normal 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); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user