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.
276 lines
9.8 KiB
Dart
276 lines
9.8 KiB
Dart
import 'package:finger_print_verification/theme/app_theme.dart';
|
|
import 'package:flutter/material.dart';
|
|
import '../models/auth_result_ui_model.dart';
|
|
|
|
/// ─────────────────────────────────────────────────────────────────────────
|
|
/// Shared, public building blocks for rendering an [AuthResultUiModel].
|
|
///
|
|
/// These used to live as private widgets inside `auth_result_screen.dart`.
|
|
/// They're now public and standalone so the SAME widgets can be reused:
|
|
/// • full-screen, on phones, via [AuthResultScreen] (pushed route)
|
|
/// • inline, on tablets, embedded directly in [FingerprintView]'s result
|
|
/// pane — no navigation needed, the result just appears next to the form.
|
|
/// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
class ResultStatusBanner extends StatelessWidget {
|
|
final AuthResultUiModel result;
|
|
const ResultStatusBanner({super.key, 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),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class ResultPersonInfoPanel extends StatelessWidget {
|
|
final AuthResultUiModel result;
|
|
final bool twoColumn;
|
|
const ResultPersonInfoPanel({super.key, required this.result, this.twoColumn = false});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final fullName = [result.firstName, result.lastName].where((s) => s.isNotEmpty).join(' ');
|
|
|
|
final rows = <ResultDetailRow>[
|
|
ResultDetailRow(label: 'Father', value: result.fatherName, icon: Icons.man_outlined),
|
|
ResultDetailRow(label: 'Grandfather', value: result.grandfatherName, icon: Icons.elderly_outlined),
|
|
ResultDetailRow(label: 'Occupation', value: result.occupation, icon: Icons.work_outline),
|
|
ResultDetailRow(label: 'Gender', value: result.gender, icon: Icons.wc_outlined),
|
|
ResultDetailRow(label: 'Sponsor', value: result.sponsor, icon: Icons.shield_outlined),
|
|
ResultDetailRow(label: 'ID Number', value: result.idNumber, icon: Icons.badge_outlined),
|
|
ResultDetailRow(label: 'Birth Date', value: result.birthDate, icon: Icons.cake_outlined),
|
|
ResultDetailRow(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),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class ResultErrorPanel extends StatelessWidget {
|
|
final AuthResultUiModel result;
|
|
const ResultErrorPanel({super.key, 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),
|
|
ResultDetailRow(label: 'Type', value: result.errorType ?? '', icon: Icons.category_outlined),
|
|
ResultDetailRow(label: 'Message', value: result.errorMessage ?? '', icon: Icons.notes_outlined),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Reusable detail row (icon + label + value).
|
|
class ResultDetailRow extends StatelessWidget {
|
|
final String label;
|
|
final String value;
|
|
final IconData icon;
|
|
|
|
const ResultDetailRow({super.key, 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,
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Full result body (banner + person-info-or-error), used by both the
|
|
/// pushed phone route and the inline tablet pane.
|
|
class ResultBody extends StatelessWidget {
|
|
final AuthResultUiModel result;
|
|
final bool twoColumn;
|
|
const ResultBody({super.key, required this.result, this.twoColumn = false});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
ResultStatusBanner(result: result),
|
|
const SizedBox(height: 16),
|
|
if (result.isSuccess) ResultPersonInfoPanel(result: result, twoColumn: twoColumn),
|
|
if (!result.isSuccess) ResultErrorPanel(result: result),
|
|
],
|
|
);
|
|
}
|
|
} |