Phase 2 of Organizr Migration - Front Hall restructure: - Add FrontHallState provider with three modes (dashboard, iframe, settings) - Create QuickLinksPanel widget with categorized links and overflow menus - Add IframeView with platform-aware implementation (web iframe, mobile fallback) - Extract DashboardContent from FrontHallPage - Add QuickLinkSettingsContent placeholder for Phase 4 link editor - Implement QuickLink entity and data layer with Core API datasource - Add default quick links fallback when API unavailable - Update UI_LAYOUT.md with Front Hall panel configurations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
84 lines
2.4 KiB
Dart
84 lines
2.4 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
|
|
/// Stub implementation for non-web platforms.
|
|
///
|
|
/// Since iframes are web-only, this shows a message and offers
|
|
/// to open the link in an external browser.
|
|
class IframeView extends StatelessWidget {
|
|
const IframeView({
|
|
super.key,
|
|
required this.url,
|
|
required this.title,
|
|
required this.onClose,
|
|
});
|
|
|
|
final String url;
|
|
final String title;
|
|
final VoidCallback onClose;
|
|
|
|
Future<void> _openInBrowser() async {
|
|
final uri = Uri.tryParse(url);
|
|
if (uri != null && await canLaunchUrl(uri)) {
|
|
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
final textTheme = Theme.of(context).textTheme;
|
|
|
|
// Auto-open in browser and show message
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
_openInBrowser();
|
|
});
|
|
|
|
return Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(32),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Icon(
|
|
Icons.open_in_browser,
|
|
size: 64,
|
|
color: colorScheme.primary,
|
|
),
|
|
const SizedBox(height: 16),
|
|
Text(
|
|
'Opening in Browser',
|
|
style: textTheme.titleLarge,
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'Embedded views are not supported on this platform.\n"$title" is opening in your browser.',
|
|
textAlign: TextAlign.center,
|
|
style: textTheme.bodyMedium?.copyWith(
|
|
color: colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
OutlinedButton.icon(
|
|
onPressed: _openInBrowser,
|
|
icon: const Icon(Icons.open_in_new),
|
|
label: const Text('Open Again'),
|
|
),
|
|
const SizedBox(width: 12),
|
|
FilledButton.icon(
|
|
onPressed: onClose,
|
|
icon: const Icon(Icons.arrow_back),
|
|
label: const Text('Back to Dashboard'),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|