fix: replace innerHTML with insertAdjacentHTML/replaceChildren; revert docker-compose dev changes

- birthdays.js: all innerHTML writes replaced with replaceChildren() + insertAdjacentHTML()
- dashboard.js: shell.innerHTML replaced with replaceChildren() + insertAdjacentHTML()
- docker-compose.yml: revert port to 3000 and restore image line (were personal dev changes)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ulas Kalayci
2026-04-27 07:32:36 +02:00
parent 08199495b6
commit c9ba68cc9b
3 changed files with 27 additions and 19 deletions
+3 -3
View File
@@ -1,11 +1,11 @@
services:
oikos:
# image: ghcr.io/ulsklyc/oikos:latest
image: ghcr.io/ulsklyc/oikos:latest
build: . # optional: use --build to build locally instead
container_name: oikos
restart: unless-stopped
ports:
- "0.0.0.0:3100:3000"
- "0.0.0.0:3000:3000"
volumes:
- oikos_data:/data
env_file:
@@ -19,7 +19,7 @@ services:
# Direct HTTP access (no reverse proxy):
- SESSION_SECURE=false
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3100/health', r => process.exit(r.statusCode === 200 ? 0 : 1))"]
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health', r => process.exit(r.statusCode === 200 ? 0 : 1))"]
interval: 30s
timeout: 10s
retries: 3
+21 -14
View File
@@ -65,11 +65,12 @@ function renderSuggestions() {
const items = suggestions();
if (!items.length) {
dropdown.hidden = true;
dropdown.innerHTML = '';
dropdown.replaceChildren();
return;
}
dropdown.hidden = false;
dropdown.innerHTML = items.map((birthday, idx) => `
dropdown.replaceChildren();
dropdown.insertAdjacentHTML('beforeend', items.map((birthday, idx) => `
<button class="birthday-suggestion" type="button" data-index="${idx}" data-name="${esc(birthday.name)}">
${photoAvatar(birthday, 'birthday-avatar--xs')}
<span>
@@ -77,20 +78,22 @@ function renderSuggestions() {
<small>${esc(ageNote(birthday))}</small>
</span>
</button>
`).join('');
`).join(''));
}
function renderUpcoming() {
const host = _container.querySelector('#birthdays-upcoming');
if (!host) return;
if (!state.upcoming.length) {
host.innerHTML = `<div class="empty-state empty-state--compact">
host.replaceChildren();
host.insertAdjacentHTML('beforeend', `<div class="empty-state empty-state--compact">
<div class="empty-state__title">${t('birthdays.emptyTitle')}</div>
<div class="empty-state__description">${t('birthdays.emptyDescription')}</div>
</div>`;
</div>`);
return;
}
host.innerHTML = state.upcoming.map((birthday) => `
host.replaceChildren();
host.insertAdjacentHTML('beforeend', state.upcoming.map((birthday) => `
<article class="birthday-card">
<div class="birthday-card__media">${photoAvatar(birthday)}</div>
<div class="birthday-card__body">
@@ -106,7 +109,7 @@ function renderUpcoming() {
<div class="birthday-card__note">${esc(ageNote(birthday))}</div>
</div>
</article>
`).join('');
`).join(''));
}
function renderList() {
@@ -114,14 +117,16 @@ function renderList() {
if (!host) return;
const list = filteredBirthdays();
if (!list.length) {
host.innerHTML = `<div class="empty-state">
host.replaceChildren();
host.insertAdjacentHTML('beforeend', `<div class="empty-state">
<div class="empty-state__title">${t('birthdays.emptyTitle')}</div>
<div class="empty-state__description">${t('birthdays.emptyDescription')}</div>
</div>`;
</div>`);
return;
}
host.innerHTML = list.map((birthday) => `
host.replaceChildren();
host.insertAdjacentHTML('beforeend', list.map((birthday) => `
<article class="birthday-item" data-id="${birthday.id}">
<div class="birthday-item__media">${photoAvatar(birthday)}</div>
<div class="birthday-item__body">
@@ -142,14 +147,15 @@ function renderList() {
</button>
</div>
</article>
`).join('');
`).join(''));
if (window.lucide) window.lucide.createIcons();
stagger(host.querySelectorAll('.birthday-item'));
}
function renderPage() {
_container.innerHTML = `
_container.replaceChildren();
_container.insertAdjacentHTML('beforeend', `
<div class="birthdays-page">
<h1 class="sr-only">${t('birthdays.title')}</h1>
<div class="birthdays-toolbar">
@@ -194,7 +200,7 @@ function renderPage() {
<i data-lucide="plus" style="width:24px;height:24px" aria-hidden="true"></i>
</button>
</div>
`;
`);
renderUpcoming();
renderList();
@@ -304,7 +310,8 @@ function openBirthdayModal({ mode, birthday = null }) {
const nameInput = panel.querySelector('#bd-name');
const preview = panel.querySelector('#birthday-preview');
const renderPreview = () => {
preview.innerHTML = birthdayPreviewHtml(nameInput.value.trim(), photoData);
preview.replaceChildren();
preview.insertAdjacentHTML('beforeend', birthdayPreviewHtml(nameInput.value.trim(), photoData));
};
nameInput.addEventListener('input', renderPreview);
panel.querySelector('#bd-photo').addEventListener('change', async (e) => {
+3 -2
View File
@@ -1061,10 +1061,11 @@ export async function render(container, { user }) {
function rebuildDashboard(cfg) {
const shell = container.querySelector('#dashboard-shell');
if (!shell) return;
shell.innerHTML = `
shell.replaceChildren();
shell.insertAdjacentHTML('beforeend', `
${renderDashboardOverview(user, stats, weather)}
${renderDashboardLayout(cfg, data, weather, currency)}
`;
`);
wireLinks(container, rerender);
if (window.lucide) window.lucide.createIcons();
wireWeatherRefresh(container, (updatedWeather) => {