diff --git a/project.inlang/messages/de.json b/project.inlang/messages/de.json index 2c0cfbf..709e1ed 100644 --- a/project.inlang/messages/de.json +++ b/project.inlang/messages/de.json @@ -47,12 +47,21 @@ "bothPassSet": "Nur Passphrase oder Passkey darf gesetzt sein", "name": "Muss mindestens 2 Zeichen lang sein", "shortname": "Muss zwischen 4 und 15 Zeichen lang sein", + "longname": "Name hinzufügen", "deleteConfirmation": "Muss exakt »{expectedValue}« sein", "noAgentsSelected": "Wähle einen Akteur", "noStartDate": "Beginn ist erforderlich", "noEndDate": "Ende ist erforderlich", "endDateBeforeStartDate": "Das Ende muss nach dem Beginn sein", - "endDateTooEarly": "Das Ende muss in der Zukunft liegen" + "endDateTooEarly": "Das Ende muss in der Zukunft liegen", + "street": "Straße hinzufügen", + "houseNo": "Pflichtfeld", + "zip": "Postleitzahl hinzufügen", + "city": "Stadt hinzufügen", + "url": "Ungültige URL", + "languages": "Wähle mindestens eine Sprache", + "language": "Wähle eine Sprache", + "deleteAfterDays": "Wähle einen Zeitpunkt" }, "passkey": "Passkey", "name": "Name" @@ -549,5 +558,86 @@ "off.site": "Außer Haus", "other": "Andere" } + }, + "settings": { + "loading": "Lade Einstellungen...", + "form": { + "sections": { + "general": "Allgemein", + "address": "Adresse", + "legal": "Rechtliches", + "advanced": "Erweitert" + }, + "fields": { + "languages": { + "title": "Sprachen", + "description": "Wähle die Sprachen, die Kunden angezeigt bekommen sollen. Du musst mindestens Eine auswählen.", + "placeholder": "Sprachen auswählen" + }, + "defaultLanguage": { + "title": "Standardsprache", + "description": "Diese Sprache wird angezeigt, wenn die Sprache des Kunden nicht unterstützt wird. In den meisten Fällen sollte dies Englisch sein.", + "placeholder": "Standardsprache auswählen", + "errors": { + "notInLanguages": "Muss eine der ausgewählten Sprachen sein" + } + }, + "longName": { + "title": "Name", + "description": "Der öffentliche Name Deiner Organisation." + }, + "logo": { + "title": "Logo (optional)" + }, + "descriptions": { + "title": "Beschreibung (optional)", + "description": "Kurze Beschreibung deiner Organisation. Wird auf der Startseite angezeigt." + }, + "street": { + "title": "Straße" + }, + "number": { + "title": "Nr." + }, + "additionalAddressInfo": { + "title": "Adresszusatz (optional)", + "description": "z.B. Gebäude, Stockwerk, Abteilung" + }, + "zip": { + "title": "PLZ" + }, + "city": { + "title": "Stadt" + }, + "website": { + "title": "Webseite (optional)", + "description": "Wird auf der Homepage angezeigt." + }, + "imprint": { + "title": "Impressum URL (optional)", + "description": "Wird in der Fußzeile angezeigt." + }, + "privacyStatement": { + "title": "Datenschutzerklärung URL (optional)", + "description": "Wird in der Fußzeile angezeigt." + }, + "autoDeleteDays": { + "title": "Automatisches Löschen von Termindaten", + "description": "Löscht Termindetails nach dem ausgewählten Zeitraum.", + "placeholder": "Zeitraum auswählen", + "options": { + "30": "nach 30 Tagen", + "60": "nach 60 Tagen", + "90": "nach 90 Tagen", + "180": "nach 180 Tagen", + "365": "nach 365 Tagen" + } + }, + "requirePhone": { + "title": "Telefonnummer erforderlich", + "label": "Kunden müssen eine Telefonnummer angeben, wenn sie einen Termin buchen." + } + } + } } } diff --git a/project.inlang/messages/en.json b/project.inlang/messages/en.json index c50ff73..c4e50e8 100644 --- a/project.inlang/messages/en.json +++ b/project.inlang/messages/en.json @@ -48,12 +48,21 @@ "bothPassSet": "Only one of passphrase or passkey should be provided", "name": "Must be at least 2 characters", "shortname": "Must be between 4 and 15 characters", + "longname": "Add a name", "deleteConfirmation": "Must be exactly »{expectedValue}«", "noAgentsSelected": "Select an agent", "noStartDate": "Start date is required", "noEndDate": "End date is required", "endDateBeforeStartDate": "End date must be after start date", - "endDateTooEarly": "End date must be in the future" + "endDateTooEarly": "End date must be in the future", + "street": "Add a street", + "houseNo": "Required", + "zip": "Add a zip code", + "city": "Add a city", + "url": "Invalid URL Format", + "languages": "Select at least one language", + "language": "Select a default language", + "deleteAfterDays": "Choose a point in time" }, "noPassAtAll": "Either passphrase or passkey is required", "passkey": "Passkey", @@ -558,5 +567,86 @@ "off.site": "Off-Site", "other": "Other" } + }, + "settings": { + "loading": "Loading settings...", + "form": { + "sections": { + "general": "General", + "address": "Address", + "legal": "Legal", + "advanced": "Advanced" + }, + "fields": { + "languages": { + "title": "Languages", + "description": "Define the languages your customers will see. You need to select at least one.", + "placeholder": "Select languages" + }, + "defaultLanguage": { + "title": "Default Language", + "description": "This is the language your clients will see, if you don't support their language. Often times this should be English.", + "placeholder": "Select default language", + "errors": { + "notInLanguages": "Default language must be one of the selected languages" + } + }, + "longName": { + "title": "Name", + "description": "Public name of your organization." + }, + "logo": { + "title": "Logo (optional)" + }, + "descriptions": { + "title": "Description (optional)", + "description": "Short description of your organization. Will be shown on the home page." + }, + "street": { + "title": "Street" + }, + "number": { + "title": "No." + }, + "additionalAddressInfo": { + "title": "Additional Address Info (optional)", + "description": "Apartment, suite, unit, building, floor, etc." + }, + "zip": { + "title": "ZIP Code" + }, + "city": { + "title": "City" + }, + "website": { + "title": "Website (optional)", + "description": "Will be shown on the home page." + }, + "imprint": { + "title": "Imprint URL (optional)", + "description": "Will be linked in the footer." + }, + "privacyStatement": { + "title": "Privacy Statement URL (optional)", + "description": "Will be linked in the footer." + }, + "autoDeleteDays": { + "title": "Delete appointments", + "description": "Automatically delete appointments details after a certain period of time.", + "placeholder": "Select retention period", + "options": { + "30": "after 30 days", + "60": "after 60 days", + "90": "after 90 days", + "180": "after 180 days", + "365": "after 365 days" + } + }, + "requirePhone": { + "title": "Require Phone Number", + "label": "Require clients to provide a phone number when booking an appointment." + } + } + } } } diff --git a/src/lib/components/templates/form-grid/form-grid-item.svelte b/src/lib/components/templates/form-grid/form-grid-item.svelte new file mode 100644 index 0000000..2b61dd7 --- /dev/null +++ b/src/lib/components/templates/form-grid/form-grid-item.svelte @@ -0,0 +1,22 @@ + + +
+ {#if title} + + {title} + + {/if} + {@render children?.()} +
diff --git a/src/lib/components/templates/form-grid/form-grid.svelte b/src/lib/components/templates/form-grid/form-grid.svelte new file mode 100644 index 0000000..89cc135 --- /dev/null +++ b/src/lib/components/templates/form-grid/form-grid.svelte @@ -0,0 +1,10 @@ + + +
+ {@render children?.()} +
diff --git a/src/lib/components/templates/form-grid/index.ts b/src/lib/components/templates/form-grid/index.ts new file mode 100644 index 0000000..c7e9c3c --- /dev/null +++ b/src/lib/components/templates/form-grid/index.ts @@ -0,0 +1,4 @@ +import FormGrid from "./form-grid.svelte"; +import FormGridItem from "./form-grid-item.svelte"; + +export { FormGrid, FormGridItem }; diff --git a/src/lib/components/templates/loading/center.svelte b/src/lib/components/templates/loading/center.svelte new file mode 100644 index 0000000..de83eaa --- /dev/null +++ b/src/lib/components/templates/loading/center.svelte @@ -0,0 +1,12 @@ + + +
+ + {title} +
diff --git a/src/lib/components/templates/loading/index.ts b/src/lib/components/templates/loading/index.ts index 878d69c..1d9e257 100644 --- a/src/lib/components/templates/loading/index.ts +++ b/src/lib/components/templates/loading/index.ts @@ -1,3 +1,4 @@ -import LoadingList from "./loading-list.svelte"; +import LoadingList from "./list.svelte"; +import LoadingCenter from "./center.svelte"; -export { LoadingList }; +export { LoadingList, LoadingCenter }; diff --git a/src/lib/components/templates/loading/loading-list.svelte b/src/lib/components/templates/loading/list.svelte similarity index 100% rename from src/lib/components/templates/loading/loading-list.svelte rename to src/lib/components/templates/loading/list.svelte diff --git a/src/lib/components/ui/form/form-description.svelte b/src/lib/components/ui/form/form-description.svelte index 2f3f2aa..b3234c2 100644 --- a/src/lib/components/ui/form/form-description.svelte +++ b/src/lib/components/ui/form/form-description.svelte @@ -1,4 +1,9 @@ {#if tenantLanguages.length > 1} diff --git a/src/lib/components/ui/typography/headline.svelte b/src/lib/components/ui/typography/headline.svelte index e8a7de4..c9f30fe 100644 --- a/src/lib/components/ui/typography/headline.svelte +++ b/src/lib/components/ui/typography/headline.svelte @@ -11,6 +11,7 @@ h2: "text-3xl font-semibold transition-colors first:mt-0", h3: "text-2xl font-semibold", h4: "text-xl font-semibold", + h5: "text-lg font-semibold", }, }, }); diff --git a/src/lib/server/services/tenant-admin-service.ts b/src/lib/server/services/tenant-admin-service.ts index 4f215b0..3c46beb 100644 --- a/src/lib/server/services/tenant-admin-service.ts +++ b/src/lib/server/services/tenant-admin-service.ts @@ -208,6 +208,7 @@ export class TenantAdminService { try { const tenant = new TenantAdminService(id); tenant.#config = await TenantConfig.create(id); + console.log("tenant config >", tenant.#config); const data = await tenant.#db ?.select() .from(centralSchema.tenant) @@ -215,6 +216,9 @@ export class TenantAdminService { .limit(1); if (data) { tenant.#tenant = data[0]; + console.log("data >", data); + } else { + console.log("no data!!!", data); } log.debug("Tenant service loaded successfully", { tenantId: id }); diff --git a/src/lib/types/tenant.ts b/src/lib/types/tenant.ts index 9c74674..3a2f57d 100644 --- a/src/lib/types/tenant.ts +++ b/src/lib/types/tenant.ts @@ -1,3 +1,25 @@ import type { SelectTenant } from "$lib/server/db/central-schema"; export type TTenant = Pick; + +export type TTenantSettings = Omit & { + logo: string | undefined; + address: { + street: string; + number: string; + additionalAddressInfo?: string; + zip: string; + city: string; + }; + legal: { + website?: string; + imprint?: string; + privacyStatement?: string; + }; + settings: { + languages: string[]; + defaultLanguage: string; + autoDeleteDays: number; + requirePhone: boolean; + }; +}; diff --git a/src/routes/(pages)/+page.svelte b/src/routes/(pages)/+page.svelte index b62c132..a299a51 100644 --- a/src/routes/(pages)/+page.svelte +++ b/src/routes/(pages)/+page.svelte @@ -1,4 +1,5 @@ + + + + + + + + {#snippet children({ props })} + {m["settings.form.fields.languages.title"]()} + ($formData.settings.languages = v)} + > + + {$formData.settings.languages.length > 0 + ? $formData.settings.languages + .map((id) => availableLocales.find((x) => x.key === id)?.label) + .join(", ") + : m["settings.form.fields.languages.placeholder"]()} + + + {#each availableLocales as locale (locale.key)} + {locale.label} + {/each} + + + + {m["settings.form.fields.languages.description"]()} + + {/snippet} + + + + + + {#snippet children({ props })} + {m["settings.form.fields.defaultLanguage.title"]()} + ($formData.settings.defaultLanguage = v)} + > + + {$formData.settings.defaultLanguage + ? availableLocales.find((x) => x.key === $formData.settings.defaultLanguage) + ?.label + : m["settings.form.fields.defaultLanguage.placeholder"]()} + + + {#each $formData.settings.languages as language (language)} + + {availableLocales.find((x) => x.key === language)?.label} + + {/each} + + + + {m["settings.form.fields.defaultLanguage.description"]()} + + {/snippet} + + + + + + {#snippet children({ props })} + {m["form.name"]()} + + {/snippet} + + + {m["settings.form.fields.longName.description"]()} + + + + + + {#snippet children({ props })} + {m["settings.form.fields.logo.title"]()} + + {/snippet} + + + + + {#snippet children({ locale })} + + + {#snippet children({ props })} + {m["settings.form.fields.descriptions.title"]()} +