You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

648 lines
23 KiB
Dart

import 'package:flutter/material.dart';
import '../../theme/app_theme.dart';
import '../models/auth_result_ui_model.dart';
/// Full-screen view that displays the authentication result returned by the
/// verification API. Redesigned with a "futuristic hospital, KSA" theme:
/// dark glassy panels, glowing cyan/green accents, Material 3 components,
/// and a responsive two-column layout on tablets.
class AuthResultScreen extends StatelessWidget {
final AuthResultUiModel result;
const AuthResultScreen({super.key, required this.result});
@override
Widget build(BuildContext context) {
final accent = result.isSuccess ? AppTheme.saudiGreen : AppTheme.redAlert;
return Scaffold(
backgroundColor: AppTheme.ink,
appBar: AppBar(
leading: const BackButton(),
title: const Text('Authentication Result'),
actions: [
Padding(
padding: const EdgeInsets.only(right: 16),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: accent.withValues(alpha: 0.12),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: accent.withValues(alpha: 0.4)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
result.isSuccess ? Icons.verified_outlined : Icons.error_outline,
size: 14,
color: accent,
),
const SizedBox(width: 5),
Text(
result.isSuccess ? 'VERIFIED' : 'FAILED',
style: TextStyle(fontSize: 11, color: accent, fontWeight: FontWeight.w700),
),
],
),
),
),
],
),
body: TechGridBackground(
child: SafeArea(
child: LayoutBuilder(
builder: (context, constraints) {
final tablet = constraints.maxWidth >= 760;
return Column(
children: [
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
child: Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: tablet ? 1000 : 720),
child: tablet ? _TabletBody(result: result) : _PhoneBody(result: result),
),
),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 4, 16, 16),
child: Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: tablet ? 1000 : 720),
child: FilledButton.icon(
onPressed: () => Navigator.of(context).pop(),
icon: const Icon(Icons.fingerprint),
label: const Text('Return to Fingerprint'),
),
),
),
),
],
);
},
),
),
),
);
}
}
// ── Phone body: stacked sections ────────────────────────────────────────────
class _PhoneBody extends StatelessWidget {
final AuthResultUiModel result;
const _PhoneBody({required this.result});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_StatusBanner(result: result),
const SizedBox(height: 16),
if (result.isSuccess) _PersonInfoPanel(result: result),
if (!result.isSuccess) _ErrorPanel(result: result),
],
);
}
}
// ── Tablet body: status banner full width, info/error two-column grid ─────
class _TabletBody extends StatelessWidget {
final AuthResultUiModel result;
const _TabletBody({required this.result});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
_StatusBanner(result: result),
const SizedBox(height: 20),
if (result.isSuccess) _PersonInfoPanel(result: result, twoColumn: true),
if (!result.isSuccess) _ErrorPanel(result: result),
],
);
}
}
// ── Status banner ───────────────────────────────────────────────────────────
class _StatusBanner extends StatelessWidget {
final AuthResultUiModel result;
const _StatusBanner({required this.result});
@override
Widget build(BuildContext context) {
final accent = result.isSuccess ? AppTheme.saudiGreen : AppTheme.redAlert;
final message = result.statusMessage.isEmpty
? (result.isSuccess ? 'Authentication successful!' : 'Authentication failed')
: result.statusMessage;
return GlowPanel(
glowColor: accent,
padding: const EdgeInsets.all(20),
child: Row(
children: [
Container(
width: 52,
height: 52,
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: accent.withValues(alpha: 0.12),
border: Border.all(color: accent.withValues(alpha: 0.4)),
),
child: Icon(
result.isSuccess ? Icons.check_circle_outline : Icons.cancel_outlined,
color: accent,
size: 28,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'STATUS',
style: TextStyle(
color: AppTheme.textMuted,
fontSize: 11,
fontWeight: FontWeight.w700,
letterSpacing: 1.1,
),
),
const SizedBox(height: 4),
Text(
message,
style: TextStyle(color: accent, fontWeight: FontWeight.w700, fontSize: 16),
),
],
),
),
],
),
);
}
}
// ── Person information panel ────────────────────────────────────────────────
class _PersonInfoPanel extends StatelessWidget {
final AuthResultUiModel result;
final bool twoColumn;
const _PersonInfoPanel({required this.result, this.twoColumn = false});
@override
Widget build(BuildContext context) {
final fullName = [result.firstName, result.lastName].where((s) => s.isNotEmpty).join(' ');
final rows = <_DetailRow>[
_DetailRow(label: 'Father', value: result.fatherName, icon: Icons.man_outlined),
_DetailRow(label: 'Grandfather', value: result.grandfatherName, icon: Icons.elderly_outlined),
_DetailRow(label: 'Occupation', value: result.occupation, icon: Icons.work_outline),
_DetailRow(label: 'Gender', value: result.gender, icon: Icons.wc_outlined),
_DetailRow(label: 'Sponsor', value: result.sponsor, icon: Icons.shield_outlined),
_DetailRow(label: 'ID Number', value: result.idNumber, icon: Icons.badge_outlined),
_DetailRow(label: 'Birth Date', value: result.birthDate, icon: Icons.cake_outlined),
_DetailRow(label: 'Nationality', value: result.nationality, icon: Icons.flag_outlined),
];
return GlowPanel(
glowColor: AppTheme.saudiGreen,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 44,
height: 44,
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppTheme.saudiGreen.withValues(alpha: 0.12),
border: Border.all(color: AppTheme.saudiGreen.withValues(alpha: 0.4)),
),
child: const Icon(Icons.person_outline, color: AppTheme.saudiGreen, size: 22),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'PERSON INFORMATION',
style: TextStyle(
color: AppTheme.saudiGreen,
fontSize: 12,
fontWeight: FontWeight.w700,
letterSpacing: 1.0,
),
),
const SizedBox(height: 2),
Text(
fullName.isEmpty ? '' : fullName,
style: const TextStyle(
color: AppTheme.textPrimary,
fontSize: 18,
fontWeight: FontWeight.w700,
),
),
],
),
),
],
),
const SizedBox(height: 8),
const Divider(height: 1),
const SizedBox(height: 8),
if (twoColumn)
GridView.count(
crossAxisCount: 2,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
mainAxisSpacing: 4,
crossAxisSpacing: 16,
childAspectRatio: 5.4,
children: rows,
)
else
Column(children: rows),
],
),
);
}
}
// ── Error panel ──────────────────────────────────────────────────────────────
class _ErrorPanel extends StatelessWidget {
final AuthResultUiModel result;
const _ErrorPanel({required this.result});
@override
Widget build(BuildContext context) {
return GlowPanel(
glowColor: AppTheme.redAlert,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
width: 44,
height: 44,
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppTheme.redAlert.withValues(alpha: 0.12),
border: Border.all(color: AppTheme.redAlert.withValues(alpha: 0.4)),
),
child: const Icon(Icons.error_outline, color: AppTheme.redAlert, size: 22),
),
const SizedBox(width: 12),
const Text(
'ERROR DETAILS',
style: TextStyle(
color: AppTheme.redAlert,
fontSize: 12,
fontWeight: FontWeight.w700,
letterSpacing: 1.0,
),
),
],
),
const SizedBox(height: 8),
const Divider(height: 1),
const SizedBox(height: 8),
_DetailRow(label: 'Type', value: result.errorType ?? '', icon: Icons.category_outlined),
_DetailRow(label: 'Message', value: result.errorMessage ?? '', icon: Icons.notes_outlined),
],
),
);
}
}
// ── Reusable detail row (icon + label + value) ──────────────────────────────
class _DetailRow extends StatelessWidget {
final String label;
final String value;
final IconData icon;
const _DetailRow({required this.label, required this.value, required this.icon});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(icon, size: 16, color: AppTheme.textMuted),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w700,
color: AppTheme.textMuted,
letterSpacing: 0.4,
),
),
const SizedBox(height: 2),
Text(
value.isEmpty ? '' : value,
style: const TextStyle(
fontSize: 14,
color: AppTheme.textPrimary,
fontWeight: FontWeight.w600,
),
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
);
}
}
// import 'package:flutter/material.dart';
// import '../models/auth_result_ui_model.dart';
//
// /// Full-screen view that displays the authentication result returned by the
// /// verification API. Designed with the Material 3 design system.
// class AuthResultScreen extends StatelessWidget {
// final AuthResultUiModel result;
//
// const AuthResultScreen({super.key, required this.result});
//
// @override
// Widget build(BuildContext context) {
// final cs = Theme.of(context).colorScheme;
//
// return Scaffold(
// backgroundColor: cs.surfaceContainerLowest,
// appBar: AppBar(
// backgroundColor: cs.surface,
// surfaceTintColor: cs.surfaceTint,
// leading: const BackButton(),
// title: const Text('Authentication Result'),
// titleTextStyle: Theme.of(context).textTheme.titleLarge?.copyWith(
// fontWeight: FontWeight.bold,
// ),
// centerTitle: false,
// elevation: 0,
// scrolledUnderElevation: 1,
// ),
// body: SafeArea(
// child: Column(
// children: [
// // ── Scrollable content ───────────────────────────────────────────
// Expanded(
// child: SingleChildScrollView(
// padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
// child: Card(
// elevation: 0,
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(16),
// side: BorderSide(color: cs.outlineVariant),
// ),
// child: Padding(
// padding: const EdgeInsets.symmetric(
// vertical: 20, horizontal: 16),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// // ── Status ────────────────────────────────────────
// _StatusSection(result: result),
//
// _divider(),
//
// // ── Person information ───────────────────────────
// _PersonInfoSection(result: result),
//
// _divider(),
//
// // ── Error ─────────────────────────────────────────
// _ErrorSection(result: result),
// ],
// ),
// ),
// ),
// ),
// ),
//
// // ── Return button ────────────────────────────────────────────────
// Padding(
// padding: const EdgeInsets.fromLTRB(16, 4, 16, 16),
// child: FilledButton.icon(
// onPressed: () => Navigator.of(context).pop(),
// icon: const Icon(Icons.fingerprint),
// label: const Text('Return to Fingerprint'),
// style: FilledButton.styleFrom(
// minimumSize: const Size.fromHeight(52),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(12),
// ),
// ),
// ),
// ),
// ],
// ),
// ),
// );
// }
//
// Widget _divider() => const Padding(
// padding: EdgeInsets.symmetric(vertical: 12),
// child: Divider(height: 1),
// );
// }
//
// // ── Status section ─────────────────────────────────────────────────────────────
//
// class _StatusSection extends StatelessWidget {
// final AuthResultUiModel result;
//
// const _StatusSection({required this.result});
//
// @override
// Widget build(BuildContext context) {
// final tt = Theme.of(context).textTheme;
// final successColor = const Color(0xFF2E7D32); // green-800
// final errorColor = Theme.of(context).colorScheme.error;
//
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text('Status:', style: tt.titleSmall?.copyWith(fontWeight: FontWeight.bold)),
// const SizedBox(height: 4),
// Text(
// result.statusMessage.isEmpty
// ? (result.isSuccess ? 'Authentication successful!' : 'Authentication failed')
// : result.statusMessage,
// style: tt.bodyMedium?.copyWith(
// color: result.isSuccess ? successColor : errorColor,
// fontWeight: FontWeight.w600,
// ),
// ),
// ],
// );
// }
// }
//
// // ── Person information section ─────────────────────────────────────────────────
//
// class _PersonInfoSection extends StatelessWidget {
// final AuthResultUiModel result;
//
// const _PersonInfoSection({required this.result});
//
// @override
// Widget build(BuildContext context) {
// final tt = Theme.of(context).textTheme;
// const successGreen = Color(0xFF2E7D32);
//
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// // Section header
// Row(
// children: [
// const Icon(Icons.check, color: successGreen, size: 18),
// const SizedBox(width: 6),
// Text(
// 'Person Information:',
// style: tt.titleSmall?.copyWith(
// fontWeight: FontWeight.bold,
// color: successGreen,
// ),
// ),
// ],
// ),
// const SizedBox(height: 12),
//
// // ── Full Name (two visual lines: first name + last name) ──────────
// _DetailRow(
// label: 'Full Name:',
// value: result.firstName,
// valueBold: true,
// ),
// if (result.lastName.isNotEmpty) ...[
// const SizedBox(height: 2),
// Padding(
// padding: const EdgeInsets.only(bottom: 8),
// child: Text(
// result.lastName,
// style: tt.bodyMedium?.copyWith(fontWeight: FontWeight.bold),
// ),
// ),
// ],
//
// _DetailRow(label: 'Father:', value: result.fatherName),
// _DetailRow(label: 'Grandfather:', value: result.grandfatherName),
// _DetailRow(label: 'Occupation:', value: result.occupation),
// _DetailRow(label: 'Gender:', value: result.gender),
// _DetailRow(label: 'Sponsor:', value: result.sponsor),
//
// const SizedBox(height: 4),
//
// _DetailRow(label: 'ID Number:', value: result.idNumber),
// _DetailRow(label: 'Birth Date:', value: result.birthDate),
// _DetailRow(label: 'Nationality:', value: result.nationality),
// ],
// );
// }
// }
//
// // ── Error section ──────────────────────────────────────────────────────────────
//
// class _ErrorSection extends StatelessWidget {
// final AuthResultUiModel result;
//
// const _ErrorSection({required this.result});
//
// @override
// Widget build(BuildContext context) {
// final tt = Theme.of(context).textTheme;
// final errorColor = Theme.of(context).colorScheme.error;
//
// return Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// // Section header
// Row(
// children: [
// Icon(Icons.close, color: errorColor, size: 18),
// const SizedBox(width: 6),
// Text(
// 'Error:',
// style: tt.titleSmall?.copyWith(
// fontWeight: FontWeight.bold,
// color: errorColor,
// ),
// ),
// ],
// ),
// const SizedBox(height: 12),
// _DetailRow(label: 'Type:', value: result.errorType ?? ''),
// _DetailRow(label: 'Message:', value: result.errorMessage ?? ''),
// ],
// );
// }
// }
//
// // ── Reusable detail row (label + value) ────────────────────────────────────────
//
// class _DetailRow extends StatelessWidget {
// final String label;
// final String value;
// final bool valueBold;
//
// const _DetailRow({
// required this.label,
// required this.value,
// this.valueBold = false,
// });
//
// @override
// Widget build(BuildContext context) {
// final tt = Theme.of(context).textTheme;
//
// return Padding(
// padding: const EdgeInsets.only(bottom: 8),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// label,
// style: tt.bodySmall?.copyWith(
// fontWeight: FontWeight.bold,
// color: Theme.of(context).colorScheme.onSurfaceVariant,
// ),
// ),
// const SizedBox(height: 2),
// Text(
// value.isEmpty ? '—' : value,
// style: tt.bodyMedium?.copyWith(
// fontWeight: valueBold ? FontWeight.bold : FontWeight.normal,
// ),
// ),
// ],
// ),
// );
// }
// }