Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
08ccaed
💄 Improve Todo list item styling with consistent buttons and row high…
Mar 22, 2026
54f9d44
✨ Indent todo category headers by half the default padding.
Mar 22, 2026
67b7593
✨ Replace todo view-toggle buttons with Carbon components and add div…
Mar 22, 2026
c83de50
🐛 Add time-based greeting component to homepage and fix stale identif…
Mar 22, 2026
69d3939
🐛 Fix global letter-spacing not applying to text elements due to Carb…
Mar 22, 2026
8b473ad
🚀 Replace emoji storage indicator in header with colored Carbon Cloud…
Mar 22, 2026
a8c184f
🚀 Decreased letter-spacing for better readability
Mar 22, 2026
8bfd51d
🚀 Increase header route icon size and align with flexbox gap.
Mar 22, 2026
20e1249
🐛 Fix GlobalMenu showing apps with inactive toggles by defaulting to …
Mar 23, 2026
d4acf6b
🚀 Hide header and redirect to home when identifier is missing or inva…
Mar 24, 2026
fe93868
♻️ Refactor login PasswordInput placeholder and showPasswordLabel int…
Mar 24, 2026
d11fd2a
✨ Add primary login button with icon next to password input field.
Mar 24, 2026
cdb5cbd
♻️ Refactor Carbon icon button centering into shared mixin.
Mar 24, 2026
d2c39de
🚚 Remove footer image buttons from GlobalMenu and simplify footer lay…
Mar 24, 2026
39081ae
📱 Widen login input row responsively across all screen sizes.
Mar 24, 2026
9dbe4fc
🐛 Fix admin accordion list items not spanning full width.
Mar 24, 2026
db4f519
🚀 Replace broken date/ID emojis with Carbon icons across admin and fe…
Mar 24, 2026
542a4bb
🚀 Redesign login screen with branding block above input field.
Mar 24, 2026
b709d03
✨ Added playwright-mcp to gitignore
Mar 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,6 @@ e2e/playwright-report
e2e/.test-state.json
e2e/.env.test
frontend/static/config/config.json

# Playwright MCP
.playwright-mcp
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- [home] Replace Navigation grid on homepage with a time-based greeting component that addresses the current user by their identifier (morning/afternoon/evening/night, DE + EN).

### Changed

- [global] Replace broken date/ID emojis with Carbon `EventSchedule` and `Identification` icons across admin and feature components (Identifiers, Toggles, LinkPresets, Activities, Jokes, JokeOfTheDay).
- [global] Redesign login screen: add logo, `tilloh.dev` title, and tagline above the login input; left-align branding with the input field; compensate PNG transparent padding via negative margin.
- [global] Hide header and redirect to home route when no valid identifier is set; login input is shown full-screen without navigation elements.
- [global] Replace emoji storage-state indicator (🌐/📴) in header with colored Carbon Cloud/CloudOffline icons displayed inline after the page title.
- [global] Increase header route icon size to `1em` (relative to h2 font-size) and align it using flexbox with `gap: 0.3em`.

### Fixed

- [global] Fix GlobalMenu showing apps with inactive toggles: `getToggleValue` was defaulting to `true` when a toggle key was missing or unreachable, causing all apps to appear. Changed default to `false` so apps are only shown when their toggle is explicitly enabled.
- [global] Fix letter-spacing not applying to text elements: Carbon CSS explicitly sets `letter-spacing` via its own tokens on `p`, `h1`, etc., overriding the inherited `body` value. Added `body * { letter-spacing: var(--letter-spacing) !important }` to enforce the global spacing across all child elements.

### Changed

- [todo] Replace raw view-toggle buttons with Carbon Buttons (ghost/secondary/danger); add vertical divider between view toggles and cleanup button; replace raw mobile-edit button with Carbon ghost Button; edit button now always visible directly before delete button; add subtle dark row background with hover highlight; add `--default_padding` gap between edit and delete buttons; indent category headers by `--default_padding / 2`.
- [global] Translate all German source code comments to English across e2e specs, frontend components, and utility files.

### Added
Expand Down
15 changes: 15 additions & 0 deletions e2e/tests/auth/login.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,19 @@ test.describe('Login-Gate', () => {
await expect(notification).toBeVisible({ timeout: 10_000 });
await expect(passwordInput).toBeVisible();
});

test('ungültiger Identifier im localStorage zeigt Login-Gate', async ({ page }) => {
// Set an invalid identifier in localStorage before navigation
await page.goto(FRONTEND_URL);
await page.evaluate(() =>
localStorage.setItem('identifier', 'invalid-stale-identifier-xyz'),
);
await page.reload();

// Layout verifies on mount and clears invalid identifier → login gate appears
await expect(page.locator('input[type="password"]')).toBeVisible({
timeout: 15_000,
});
await expect(page.locator('main')).not.toBeVisible();
});
});
10 changes: 4 additions & 6 deletions frontend/src/lib/components/admin/Activities.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import AccordionItem from 'carbon-components-svelte/src/Accordion/AccordionItem.svelte';
import CopyButton from 'carbon-components-svelte/src/CopyButton/CopyButton.svelte';
import InlineNotification from 'carbon-components-svelte/src/Notification/InlineNotification.svelte';
import EventSchedule from 'carbon-icons-svelte/lib/EventSchedule.svelte';
import FaceActivated from 'carbon-icons-svelte/lib/FaceActivated.svelte';
import Identification from 'carbon-icons-svelte/lib/Identification.svelte';
import Information from 'carbon-icons-svelte/lib/Information.svelte';
import Link from 'carbon-icons-svelte/lib/Link.svelte';
import User from 'carbon-icons-svelte/lib/User.svelte';
Expand Down Expand Up @@ -56,12 +58,8 @@
</svelte:fragment>
<div class="admin_list_item_content">
<div>
<p>
📅{new Date(activity.updated).toLocaleString('de-DE')}
</p>
<p>
🆔{activity.id}
</p>
<p><EventSchedule />{new Date(activity.updated).toLocaleString('de-DE')}</p>
<p><Identification />{activity.id}</p>
</div>
<CopyButton
text={activity.id}
Expand Down
12 changes: 5 additions & 7 deletions frontend/src/lib/components/admin/Identifiers.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
import CopyButton from 'carbon-components-svelte/src/CopyButton/CopyButton.svelte';
import InlineNotification from 'carbon-components-svelte/src/Notification/InlineNotification.svelte';
import Add from 'carbon-icons-svelte/lib/Add.svelte';
import EventSchedule from 'carbon-icons-svelte/lib/EventSchedule.svelte';
import FingerprintRecognition from 'carbon-icons-svelte/lib/FingerprintRecognition.svelte';
import Identification from 'carbon-icons-svelte/lib/Identification.svelte';
import TrashCan from 'carbon-icons-svelte/lib/TrashCan.svelte';
import InputWithButton from '../shared/custom-carbon-components/InputWithButton.svelte';

Expand Down Expand Up @@ -78,13 +80,9 @@
</svelte:fragment>
<div class="admin_list_item_content">
<div>
<p>
✨{new Date(identifier.created).toLocaleString('de-DE')}
📅{new Date(identifier.updated).toLocaleString('de-DE')}
</p>
<p>
🆔{identifier._id}
</p>
<p><EventSchedule />{new Date(identifier.created).toLocaleString('de-DE')}</p>
<p><EventSchedule />{new Date(identifier.updated).toLocaleString('de-DE')}</p>
<p><Identification />{identifier._id}</p>
</div>
<div class="admin_list_button_group">
<CopyButton
Expand Down
12 changes: 5 additions & 7 deletions frontend/src/lib/components/admin/Jokes.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
import Button from 'carbon-components-svelte/src/Button/Button.svelte';
import CopyButton from 'carbon-components-svelte/src/CopyButton/CopyButton.svelte';
import InlineNotification from 'carbon-components-svelte/src/Notification/InlineNotification.svelte';
import EventSchedule from 'carbon-icons-svelte/lib/EventSchedule.svelte';
import FaceActivated from 'carbon-icons-svelte/lib/FaceActivated.svelte';
import Identification from 'carbon-icons-svelte/lib/Identification.svelte';
import TaskApproved from 'carbon-icons-svelte/lib/TaskApproved.svelte';
import ThumbsUp from 'carbon-icons-svelte/lib/ThumbsUp.svelte';
import TrashCan from 'carbon-icons-svelte/lib/TrashCan.svelte';
Expand Down Expand Up @@ -76,13 +78,9 @@
</svelte:fragment>
<div class="admin_list_item_content">
<div>
<p>
✨{formatDate(joke.created)}
📅{formatDate(joke.updated)}
</p>
<p>
🆔{joke._id}
</p>
<p><EventSchedule />{formatDate(joke.created)}</p>
<p><EventSchedule />{formatDate(joke.updated)}</p>
<p><Identification />{joke._id}</p>
</div>
<div class="admin_list_button_group">
{#if !joke.verified && joke.verified !== undefined}
Expand Down
12 changes: 5 additions & 7 deletions frontend/src/lib/components/admin/LinkPresets.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import Button from 'carbon-components-svelte/src/Button/Button.svelte';
import CopyButton from 'carbon-components-svelte/src/CopyButton/CopyButton.svelte';
import InlineNotification from 'carbon-components-svelte/src/Notification/InlineNotification.svelte';
import EventSchedule from 'carbon-icons-svelte/lib/EventSchedule.svelte';
import Identification from 'carbon-icons-svelte/lib/Identification.svelte';
import Link from 'carbon-icons-svelte/lib/Link.svelte';
import TrashCan from 'carbon-icons-svelte/lib/TrashCan.svelte';
import User from 'carbon-icons-svelte/lib/User.svelte';
Expand Down Expand Up @@ -50,13 +52,9 @@
</svelte:fragment>
<div class="admin_list_item_content">
<div>
<p>
✨{new Date(linkPreset.created).toLocaleString('de-DE')}
📅{new Date(linkPreset.updated).toLocaleString('de-DE')}
</p>
<p>
🆔{linkPreset._id}
</p>
<p><EventSchedule />{new Date(linkPreset.created).toLocaleString('de-DE')}</p>
<p><EventSchedule />{new Date(linkPreset.updated).toLocaleString('de-DE')}</p>
<p><Identification />{linkPreset._id}</p>
</div>
<div class="admin_list_button_group">
<CopyButton
Expand Down
12 changes: 5 additions & 7 deletions frontend/src/lib/components/admin/Toggles.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import InlineNotification from 'carbon-components-svelte/src/Notification/InlineNotification.svelte';
import Toggle from 'carbon-components-svelte/src/Toggle/Toggle.svelte';
import Add from 'carbon-icons-svelte/lib/Add.svelte';
import EventSchedule from 'carbon-icons-svelte/lib/EventSchedule.svelte';
import Identification from 'carbon-icons-svelte/lib/Identification.svelte';
import RadioButtonChecked from 'carbon-icons-svelte/lib/RadioButtonChecked.svelte';
import TrashCan from 'carbon-icons-svelte/lib/TrashCan.svelte';
import { fade } from 'svelte/transition';
Expand Down Expand Up @@ -111,13 +113,9 @@
</svelte:fragment>
<div class="admin_list_item_content">
<div>
<p>
✨{new Date(toggle.created).toLocaleString('de-DE')}
📅{new Date(toggle.updated).toLocaleString('de-DE')}
</p>
<p>
🆔{toggle._id}
</p>
<p><EventSchedule />{new Date(toggle.created).toLocaleString('de-DE')}</p>
<p><EventSchedule />{new Date(toggle.updated).toLocaleString('de-DE')}</p>
<p><Identification />{toggle._id}</p>
</div>
<div class="admin_list_button_group">
<Toggle
Expand Down
88 changes: 88 additions & 0 deletions frontend/src/lib/components/home/Greeting.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<script lang="ts">
// 1. IMPORTS
import { getIdentifier } from '$lib/api/identifiers.api';
import type { IdentifierDto } from '$lib/types/identifiers.dto';
import { identifierStore } from '$lib/util/stores/store-identifier';
import { t } from '$lib/util/translations';
import { onMount } from 'svelte';

// 3. CONST
const VARIANTS = 3;

// 4. STATE
let identifier = $state<IdentifierDto | null>(null);
let nameLoaded = $state(false);

// 5. DERIVED
const timeOfDay = $derived.by(() => {
const hour = new Date().getHours();
if (hour >= 5 && hour < 12) return 'Morning';
if (hour >= 12 && hour < 18) return 'Afternoon';
if (hour >= 18 && hour < 22) return 'Evening';
return 'Night';
});

const greetingVariant = Math.floor(Math.random() * VARIANTS);

const greetingHtml = $derived.by(() => {
if (!nameLoaded) return '';

const hasName = !!identifier?.name;
const prefix = hasName ? '' : 'Anon';
const key =
`page.home.greeting${prefix}${timeOfDay}${greetingVariant}` as Parameters<
typeof $t
>[0];

if (!hasName) return $t(key);

const escapedName = identifier!.name
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
const highlighted = `<span class="username">${escapedName}</span>`;
return $t(key, { username: highlighted });
});

// 7. LIFECYCLE
onMount(async () => {
if ($identifierStore) {
try {
const result = await getIdentifier($identifierStore);
if (result?.name) {
identifier = result;
}
} catch {
// fall through to anonymous greeting
}
}
nameLoaded = true;
});
</script>

{#if nameLoaded}
<div class="greeting">
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
<p class="greeting-text">{@html greetingHtml}</p>
</div>
{/if}

<style lang="scss">
.greeting {
text-align: center;
padding: 1rem 0;
}

.greeting-text {
font-size: 1.5rem;
font-weight: 300;
color: rgba(255, 255, 255, 0.85);
margin: 0;

:global(.username) {
font-weight: 600;
color: var(--color_bg_light_0);
}
}
</style>
6 changes: 5 additions & 1 deletion frontend/src/lib/components/home/JokeOfTheDay.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { formatDate } from '$lib/util/format';
import { initialized, t } from '$lib/util/translations';
import Tile from 'carbon-components-svelte/src/Tile/Tile.svelte';
import EventSchedule from 'carbon-icons-svelte/lib/EventSchedule.svelte';
import { onMount } from 'svelte';

// 2. PROPS
Expand All @@ -25,7 +26,7 @@
<Tile style="padding:2rem;" class="joke_tile">
<h3>{$t('page.home.jokeTitle')}</h3>
<i>{joke.text}</i>
<p>📅{formatDate(joke.created)}</p>
<p><EventSchedule />{formatDate(joke.created)}</p>
</Tile>
{:else}
<p>{$t('page.home.jokeLoading')}</p>
Expand Down Expand Up @@ -66,6 +67,9 @@
}

p {
display: flex;
align-items: center;
gap: 0.35rem;
font-size: 0.75rem;
margin-bottom: 0;
margin-top: 0.75rem;
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/lib/components/jokes/Jokes.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import Tile from 'carbon-components-svelte/src/Tile/Tile.svelte';
import Add from 'carbon-icons-svelte/lib/Add.svelte';
import CloudDownload from 'carbon-icons-svelte/lib/CloudDownload.svelte';
import EventSchedule from 'carbon-icons-svelte/lib/EventSchedule.svelte';
import { onMount } from 'svelte';
import CreateJokeOverlay from './CreateJokeOverlay.svelte';

Expand All @@ -34,7 +35,7 @@
{#if randomJoke?.created}
<Tile class="random_joke mt2">
<i>{randomJoke.text}</i>
<p>📅{formatDate(randomJoke.created)}</p>
<p><EventSchedule />{formatDate(randomJoke.created)}</p>
</Tile>
{/if}

Expand Down Expand Up @@ -85,6 +86,12 @@
padding: 2rem;
}

:global(.random_joke p) {
display: flex;
align-items: center;
gap: 0.35rem;
}

:global(#add_joke_button) {
position: fixed;
bottom: calc(var(--default_padding) + env(safe-area-inset-bottom, 0px));
Expand Down
Loading
Loading