chore: release v0.3.3
Build and Push / build (release) Successful in 3m6s

Features:
- Health check endpoint for Portainer monitoring
- Local search filtering in DataGrid
- Container status badges reflect health (green/orange/blue)

Improvements:
- Standardized 56px header heights across panels
- Container grid parses Docker API format correctly
- Search bar styling improvements
- Status badges have consistent width

Fixes:
- Quick links persistence (link type, form refresh)
- Iframe switching closes existing content first
- ContainerState type conflict resolved

Branding:
- Updated favicon and icons with Tatlock bucket logo

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeroen Schweitzer
2026-01-03 16:12:18 +01:00
co-authored by Claude Opus 4.5
parent 0d15d3dbb5
commit bdced8739c
26 changed files with 499 additions and 136 deletions
@@ -51,6 +51,7 @@ class FrontHallPage extends ConsumerWidget {
case FrontHallMode.iframe:
return IframeView(
key: ValueKey(state.activeIframeUrl),
url: state.activeIframeUrl!,
title: state.activeIframeTitle ?? 'External Content',
onClose: () =>
@@ -83,7 +83,7 @@ class _IframeViewState extends State<IframeView> {
children: [
// Header bar
Container(
height: 48,
height: 56,
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: colorScheme.surfaceContainerHighest,
@@ -67,24 +67,21 @@ class _QuickLinkFormState extends ConsumerState<QuickLinkForm> {
try {
final actions = ref.read(quickLinkActionsProvider.notifier);
final category = _categoryController.text.trim();
// Generate ID for new links
final id = widget.quickLink?.id ??
_nameController.text.toLowerCase().replaceAll(RegExp(r'\s+'), '-');
final link = QuickLink(
id: id,
name: _nameController.text.trim(),
url: _urlController.text.trim(),
iconName: _iconName,
category:
_categoryController.text.trim().isEmpty ? null : _categoryController.text.trim(),
type: _type,
isActive: _isActive,
sortOrder: widget.quickLink?.sortOrder ?? 0,
);
final QuickLink link;
if (isCreate) {
// Create new entity
link = QuickLink(
id: _nameController.text.toLowerCase().replaceAll(RegExp(r'\s+'), '-'),
name: _nameController.text.trim(),
url: _urlController.text.trim(),
iconName: _iconName,
category: category.isEmpty ? null : category,
type: _type,
isActive: _isActive,
sortOrder: 0,
);
await actions.create(link);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
@@ -95,6 +92,15 @@ class _QuickLinkFormState extends ConsumerState<QuickLinkForm> {
);
}
} else {
// Update: use copyWith to preserve all existing data
link = widget.quickLink!.copyWith(
name: _nameController.text.trim(),
url: _urlController.text.trim(),
iconName: _iconName,
category: category.isEmpty ? null : category,
type: _type,
isActive: _isActive,
);
await actions.update(link);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
@@ -106,6 +112,9 @@ class _QuickLinkFormState extends ConsumerState<QuickLinkForm> {
}
}
// Refresh the list before exiting
ref.invalidate(quickLinksProvider);
widget.onSaved();
} catch (e) {
if (mounted) {
@@ -53,7 +53,8 @@ class _QuickLinkSettingsContentState
body: QuickLinkForm(
mode: EntityPageMode.create,
onCancel: _stopCreating,
onSaved: _stopCreating,
onSaved: () =>
ref.read(frontHallStateProvider.notifier).exitSettings(),
),
);
}
@@ -131,14 +132,14 @@ class _QuickLinkSettingsContentState
title: Text('Edit: ${link.name}'),
),
body: QuickLinkForm(
key: ValueKey(link.id), // Force recreation when link changes
mode: EntityPageMode.edit,
quickLink: link,
onCancel: () => ref
.read(frontHallStateProvider.notifier)
.clearSelectedLink(),
onSaved: () => ref
.read(frontHallStateProvider.notifier)
.clearSelectedLink(),
onSaved: () =>
ref.read(frontHallStateProvider.notifier).exitSettings(),
),
),
);
@@ -297,7 +297,10 @@ class _QuickLinksList extends ConsumerWidget {
// In settings mode, select link for editing
ref.read(frontHallStateProvider.notifier).selectLink(link.id);
} else {
// In normal mode, open link
// Close any open content first to ensure clean state
ref.read(frontHallStateProvider.notifier).showDashboard();
// Then open the new link
if (link.isIframe) {
ref
.read(frontHallStateProvider.notifier)