feat: Introduce DocumentSelectedCard and refactor DocumentFileSelector for improved display of selected documents, and update upload success navigation.

This commit is contained in:
Achintha Isuru
2026-02-26 17:16:52 -05:00
parent 4995ff435d
commit c113b836f2
5 changed files with 78 additions and 16 deletions

View File

@@ -46,12 +46,25 @@ The workflow follows a 4-step lifecycle:
#### UI — `DocumentUploadPage`
- Accepts `StaffDocument document` and optional `String? initialUrl` as route arguments
- PDF file picker via `FilePickerService.pickFile(allowedExtensions: ['pdf'])`
- File selector card: shows file name when selected or a prompt icon when empty
- **File selector card**: displays the file name with a document icon when selected, otherwise a prompt to upload.
- *Note:* PDF preview/opening functionality is explicitly omitted to maintain a clean, minimal upload flow without relying on external native viewers.
- Attestation checkbox must be checked before the submit button is enabled
- Loading state: shows `CircularProgressIndicator` while uploading (replaces button — mirrors attire pattern)
- On success: shows `UiSnackbar` and calls `Modular.to.pop(updatedDocument)` to return data to caller
- On success: shows `UiSnackbar` and calls `Modular.to.toDocuments()` to return to the list
- On failure: shows `UiSnackbar` with error message; stays on page for retry
#### UI Guidelines (For Documents & Certificates)
To ensure a consistent experience across all compliance uploads (documents, certificates), adhere to the following UI patterns:
1. **Header & Instructions:** Use `UiAppBar` with the item name as the title and description as the subtitle. Provide clear instructions at the top of the body (`UiTypography.body1m.textPrimary`).
2. **File Selection Card:**
- When empty: Show a neutral/primary bordered card inviting the user to pick a file.
- When selected: Show an elegant card with `UiColors.bgPopup`, rounded corners (`UiConstants.radiusLg`), bordered by `UiColors.primary`.
- The selected card must contain an identifying icon, the truncated file name, and an explicit inline action (e.g., "Replace" or "Upload") using `UiTypography.body3m.textSecondary`.
- **Do not** embed native PDF viewers or link out to external readers.
3. **Bottom Footer / Attestation:**
- Fix the attestation checkbox and the submit button to the bottom using `bottomNavigationBar` wrapped in a `SafeArea` and `Padding`.
- The submit button state must be tightly coupled to both the file presence and the attestation state.
#### Module Wiring — `StaffDocumentsModule`
- `UploadDocumentUseCase` bound as lazy singleton
- `DocumentUploadCubit` bound (non-singleton, per-use)

View File

@@ -61,7 +61,7 @@ class _DocumentUploadPageState extends State<DocumentUploadPage> {
message: t.staff_documents.upload.success,
type: UiSnackbarType.success,
);
Modular.to.pop(state.updatedDocument);
Modular.to.toDocuments();
} else if (state.status == DocumentUploadStatus.failure) {
UiSnackbar.show(
context,

View File

@@ -3,6 +3,8 @@ import 'package:flutter/material.dart';
// ignore: depend_on_referenced_packages
import 'package:core_localization/core_localization.dart';
import 'document_selected_card.dart';
/// Displays a tappable card that prompts the user to pick a PDF file.
///
/// Shows the selected file name when a file has been chosen, or an
@@ -24,6 +26,14 @@ class DocumentFileSelector extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (_hasFile) {
return InkWell(
onTap: onTap,
borderRadius: UiConstants.radiusLg,
child: DocumentSelectedCard(selectedFilePath: selectedFilePath!),
);
}
return InkWell(
onTap: onTap,
borderRadius: UiConstants.radiusLg,
@@ -32,27 +42,21 @@ class DocumentFileSelector extends StatelessWidget {
decoration: BoxDecoration(
color: UiColors.bgPopup,
borderRadius: UiConstants.radiusLg,
border: Border.all(
color: _hasFile ? UiColors.primary : UiColors.border,
),
border: Border.all(color: UiColors.border),
),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(
_hasFile ? UiIcons.file : UiIcons.upload,
const Icon(
UiIcons.upload,
size: 48,
color: _hasFile ? UiColors.primary : UiColors.textSecondary,
color: UiColors.textSecondary,
),
const SizedBox(height: UiConstants.space2),
Text(
_hasFile
? selectedFilePath!.split('/').last
: t.staff_documents.upload.select_pdf,
style: UiTypography.body2m.copyWith(
color: _hasFile ? UiColors.primary : UiColors.textSecondary,
),
t.staff_documents.upload.select_pdf,
style: UiTypography.body2m.textError,
textAlign: TextAlign.center,
),
],

View File

@@ -0,0 +1,46 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
/// A card that displays the selected document file name and an icon.
class DocumentSelectedCard extends StatelessWidget {
const DocumentSelectedCard({super.key, required this.selectedFilePath});
/// The local path of the currently selected file.
final String selectedFilePath;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
color: UiColors.bgPopup,
borderRadius: UiConstants.radiusLg,
border: Border.all(color: UiColors.primary),
),
child: Row(
children: <Widget>[
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: UiColors.primary.withOpacity(0.1),
borderRadius: BorderRadius.circular(UiConstants.space2),
),
child: const Center(
child: Icon(UiIcons.file, color: UiColors.primary, size: 20),
),
),
const SizedBox(width: UiConstants.space3),
Expanded(
child: Text(
selectedFilePath.split('/').last,
style: UiTypography.body1m.textPrimary,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
}
}

View File

@@ -17,7 +17,6 @@ dependencies:
equatable: ^2.0.5
firebase_auth: ^6.1.4
firebase_data_connect: ^0.2.2+2
# Architecture Packages
design_system:
path: ../../../../../design_system