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
@@ -52,7 +52,7 @@ class QuickLinksDatasource {
final model = QuickLinkModel.fromEntity(link);
final response = await _dio.post<Map<String, dynamic>>(
_basePath,
data: model.toCreateJson(),
data: model.toJson(),
);
final data = response.data;
@@ -69,7 +69,7 @@ class QuickLinksDatasource {
final id = int.tryParse(link.id) ?? 0;
final response = await _dio.put<Map<String, dynamic>>(
'$_basePath/$id',
data: model.toCreateJson(),
data: model.toJson(),
);
final data = response.data;
@@ -4,17 +4,18 @@ import 'package:tatlock_ui/features/front_hall/domain/entities/quick_link.dart';
part 'quick_link_model.freezed.dart';
part 'quick_link_model.g.dart';
/// Quick link data model for API serialization.
/// Quick link data model matching Core API dashboard/quick-links endpoint.
///
/// Maps to Core API dashboard/quick-links endpoint:
/// Field mapping:
/// - title (API) ↔ name (Flutter entity)
/// - icon (API) ↔ iconName (Flutter entity)
/// - position (API) ↔ sortOrder (Flutter entity)
/// - is_visible (API) ↔ isActive (Flutter entity)
/// - link_type (API) ↔ type (Flutter entity)
@freezed
sealed class QuickLinkModel with _$QuickLinkModel {
const factory QuickLinkModel({
required int id,
@Default(0) int id,
required String title,
required String url,
String? icon,
@@ -22,6 +23,7 @@ sealed class QuickLinkModel with _$QuickLinkModel {
String? category,
@Default(0) int position,
@JsonKey(name: 'is_visible') @Default(true) bool isVisible,
@JsonKey(name: 'link_type') @Default('iframe') String linkType,
String? color,
@JsonKey(name: 'background_color') String? backgroundColor,
}) = _QuickLinkModel;
@@ -39,7 +41,7 @@ sealed class QuickLinkModel with _$QuickLinkModel {
url: url,
iconName: icon ?? 'link',
category: category,
type: QuickLinkType.iframe,
type: _parseQuickLinkType(linkType),
sortOrder: position,
isActive: isVisible,
);
@@ -55,21 +57,23 @@ sealed class QuickLinkModel with _$QuickLinkModel {
category: entity.category,
position: entity.sortOrder,
isVisible: entity.isActive,
linkType: _formatQuickLinkType(entity.type),
);
}
}
/// Creates a JSON map for creating a new quick link (without id).
Map<String, dynamic> toCreateJson() {
return {
'title': title,
'url': url,
if (icon != null) 'icon': icon,
if (description != null) 'description': description,
if (category != null) 'category': category,
'position': position,
'is_visible': isVisible,
if (color != null) 'color': color,
if (backgroundColor != null) 'background_color': backgroundColor,
};
}
/// Parses API link_type string to QuickLinkType enum.
QuickLinkType _parseQuickLinkType(String linkType) {
return switch (linkType) {
'new_tab' => QuickLinkType.newTab,
_ => QuickLinkType.iframe,
};
}
/// Formats QuickLinkType enum to API link_type string.
String _formatQuickLinkType(QuickLinkType type) {
return switch (type) {
QuickLinkType.newTab => 'new_tab',
QuickLinkType.iframe => 'iframe',
};
}