feat: Introduce DocumentSelectedCard and refactor DocumentFileSelector for improved display of selected documents, and update upload success navigation.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user