${t('settings.languageTitle')}
@@ -514,6 +565,58 @@ function bindEvents(container, user, categories, icsSubscriptions, apiTokens) {
});
}
+ const dateFormatSelect = container.querySelector('#date-format-select');
+ if (dateFormatSelect) {
+ dateFormatSelect.addEventListener('change', async () => {
+ try {
+ await api.put('/preferences', { date_format: dateFormatSelect.value });
+ try { localStorage.setItem('oikos-date-format', dateFormatSelect.value); } catch (_) {}
+ window.dispatchEvent(new CustomEvent('date-format-changed', { detail: { dateFormat: dateFormatSelect.value } }));
+ window.oikos?.showToast(t('settings.dateFormatSavedToast'), 'success');
+ } catch (err) {
+ window.oikos?.showToast(err.message ?? t('common.errorGeneric'), 'danger');
+ }
+ });
+ }
+
+ const appNameForm = container.querySelector('#app-name-form');
+ if (appNameForm) {
+ appNameForm.addEventListener('submit', async (e) => {
+ e.preventDefault();
+ const errorEl = container.querySelector('#app-name-error');
+ const input = container.querySelector('#app-name-input');
+ errorEl.hidden = true;
+ const value = input.value.trim();
+ try {
+ await api.put('/preferences', { app_name: value });
+ try {
+ if (value) localStorage.setItem(APP_NAME_STORAGE_KEY, value);
+ else localStorage.removeItem(APP_NAME_STORAGE_KEY);
+ } catch (_) {}
+ input.value = value || DEFAULT_APP_NAME;
+ window.dispatchEvent(new CustomEvent('app-name-changed', { detail: { appName: value || DEFAULT_APP_NAME } }));
+ window.oikos?.showToast(t('settings.appNameSavedToast'), 'success');
+ } catch (err) {
+ showError(errorEl, err.message ?? t('common.errorGeneric'));
+ }
+ });
+
+ container.querySelector('#app-name-reset-btn')?.addEventListener('click', async () => {
+ const errorEl = container.querySelector('#app-name-error');
+ const input = container.querySelector('#app-name-input');
+ errorEl.hidden = true;
+ input.value = DEFAULT_APP_NAME;
+ try {
+ await api.put('/preferences', { app_name: '' });
+ try { localStorage.removeItem(APP_NAME_STORAGE_KEY); } catch (_) {}
+ window.dispatchEvent(new CustomEvent('app-name-changed', { detail: { appName: DEFAULT_APP_NAME } }));
+ window.oikos?.showToast(t('settings.appNameSavedToast'), 'success');
+ } catch (err) {
+ showError(errorEl, err.message ?? t('common.errorGeneric'));
+ }
+ });
+ }
+
// Passwort ändern
const passwordForm = container.querySelector('#password-form');
if (passwordForm) {
diff --git a/server/index.js b/server/index.js
index 75dceb3..be4759d 100644
--- a/server/index.js
+++ b/server/index.js
@@ -39,6 +39,7 @@ const logOikos = createLogger('Oikos');
const { version: APP_VERSION } = JSON.parse(
readFileSync(new URL('../package.json', import.meta.url), 'utf-8')
);
+const DEFAULT_APP_NAME = 'Oikos';
const app = express();
const PORT = process.env.PORT || 3000;
@@ -165,7 +166,14 @@ app.use('/api/v1/auth', authRouter);
// Versionsinformation - keine Authentifizierung erforderlich (Login-Seite benötigt diese)
app.get('/api/v1/version', (req, res) => {
- res.json({ version: APP_VERSION });
+ let appName = DEFAULT_APP_NAME;
+ try {
+ const row = db.get().prepare('SELECT value FROM sync_config WHERE key = ?').get('app_name');
+ if (row?.value) appName = row.value;
+ } catch {
+ // fall back to default
+ }
+ res.json({ version: APP_VERSION, app_name: appName });
});
function sendOpenApi(req, res) {