feat: Implement FAQs feature for staff application with updated routing and UI components
This commit is contained in:
@@ -203,7 +203,9 @@ class StaffPaths {
|
|||||||
static const String leaderboard = '/leaderboard';
|
static const String leaderboard = '/leaderboard';
|
||||||
|
|
||||||
/// FAQs - frequently asked questions.
|
/// FAQs - frequently asked questions.
|
||||||
static const String faqs = '/faqs';
|
///
|
||||||
|
/// Access to frequently asked questions about the staff application.
|
||||||
|
static const String faqs = '/worker-main/faqs/';
|
||||||
|
|
||||||
// ==========================================================================
|
// ==========================================================================
|
||||||
// PRIVACY & SECURITY
|
// PRIVACY & SECURITY
|
||||||
|
|||||||
@@ -264,4 +264,7 @@ class UiIcons {
|
|||||||
|
|
||||||
/// Chef hat icon for attire
|
/// Chef hat icon for attire
|
||||||
static const IconData chefHat = _IconLib.chefHat;
|
static const IconData chefHat = _IconLib.chefHat;
|
||||||
|
|
||||||
|
/// Help circle icon for FAQs
|
||||||
|
static const IconData helpCircle = _IconLib.helpCircle;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,6 +198,11 @@ class StaffProfilePage extends StatelessWidget {
|
|||||||
ProfileMenuGrid(
|
ProfileMenuGrid(
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
children: [
|
children: [
|
||||||
|
ProfileMenuItem(
|
||||||
|
icon: UiIcons.helpCircle,
|
||||||
|
label: i18n.header.title.contains("Perfil") ? "Preguntas Frecuentes" : "FAQs",
|
||||||
|
onTap: () => Modular.to.toFaqs(),
|
||||||
|
),
|
||||||
ProfileMenuItem(
|
ProfileMenuItem(
|
||||||
icon: UiIcons.shield,
|
icon: UiIcons.shield,
|
||||||
label: i18n.header.title.contains("Perfil") ? "Privacidad" : "Privacy & Security",
|
label: i18n.header.title.contains("Perfil") ? "Privacidad" : "Privacy & Security",
|
||||||
|
|||||||
@@ -1,122 +0,0 @@
|
|||||||
# Staff FAQs Feature
|
|
||||||
|
|
||||||
A modular feature package providing Frequently Asked Questions functionality for the staff mobile application.
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
This package follows clean architecture principles with clear separation of concerns:
|
|
||||||
|
|
||||||
### Domain Layer (`lib/src/domain/`)
|
|
||||||
- **Entities**: `FaqCategory`, `FaqItem` - Domain models representing FAQ data
|
|
||||||
- **Repositories**: `FaqsRepositoryInterface` - Abstract interface for FAQ data access
|
|
||||||
- **Use Cases**:
|
|
||||||
- `GetFaqsUseCase` - Retrieves all FAQs
|
|
||||||
- `SearchFaqsUseCase` - Searches FAQs by query
|
|
||||||
|
|
||||||
### Data Layer (`lib/src/data/`)
|
|
||||||
- **Repositories Implementation**: `FaqsRepositoryImpl`
|
|
||||||
- Loads FAQs from JSON asset file (`lib/src/assets/faqs/faqs.json`)
|
|
||||||
- Implements caching to avoid repeated file reads
|
|
||||||
- Provides search filtering logic
|
|
||||||
|
|
||||||
### Presentation Layer (`lib/src/presentation/`)
|
|
||||||
- **BLoC**: `FaqsBloc` - State management with events and states
|
|
||||||
- Events: `FetchFaqsEvent`, `SearchFaqsEvent`
|
|
||||||
- State: `FaqsState` - Manages categories, loading, search query, and errors
|
|
||||||
- **Pages**: `FaqsPage` - Full-screen FAQ view with AppBar and contact button
|
|
||||||
- **Widgets**: `FaqsWidget` - Reusable accordion widget with search functionality
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
✅ **Search Functionality** - Real-time search across questions and answers
|
|
||||||
✅ **Accordion UI** - Expandable/collapsible FAQ items
|
|
||||||
✅ **Category Organization** - FAQs grouped by category
|
|
||||||
✅ **Localization** - Support for English and Spanish
|
|
||||||
✅ **Contact Support Button** - Direct link to messaging/support
|
|
||||||
✅ **Empty State** - Helpful message when no results found
|
|
||||||
✅ **Loading State** - Loading indicator while fetching FAQs
|
|
||||||
✅ **Asset-based Data** - FAQ content stored in JSON for easy updates
|
|
||||||
|
|
||||||
## Data Structure
|
|
||||||
|
|
||||||
FAQs are stored in `lib/src/assets/faqs/faqs.json` with the following structure:
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"category": "Getting Started",
|
|
||||||
"questions": [
|
|
||||||
{
|
|
||||||
"q": "How do I apply for shifts?",
|
|
||||||
"a": "Browse available shifts..."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Localization
|
|
||||||
|
|
||||||
Localization strings are defined in:
|
|
||||||
- `packages/core_localization/lib/src/l10n/en.i18n.json`
|
|
||||||
- `packages/core_localization/lib/src/l10n/es.i18n.json`
|
|
||||||
|
|
||||||
Available keys:
|
|
||||||
- `staff_faqs.title` - Page title
|
|
||||||
- `staff_faqs.search_placeholder` - Search input hint
|
|
||||||
- `staff_faqs.no_results` - Empty state message
|
|
||||||
- `staff_faqs.contact_support` - Button label
|
|
||||||
|
|
||||||
## Dependency Injection
|
|
||||||
|
|
||||||
The `FaqsModule` provides all dependencies:
|
|
||||||
|
|
||||||
```dart
|
|
||||||
FaqsModule().binds(injector);
|
|
||||||
```
|
|
||||||
|
|
||||||
Registered singletons:
|
|
||||||
- `FaqsRepositoryInterface` → `FaqsRepositoryImpl`
|
|
||||||
- `GetFaqsUseCase`
|
|
||||||
- `SearchFaqsUseCase`
|
|
||||||
- `FaqsBloc`
|
|
||||||
|
|
||||||
## Routing
|
|
||||||
|
|
||||||
Routes are defined in `FaqsModule.routes()`:
|
|
||||||
- `/` → `FaqsPage`
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
1. Add `FaqsModule` to your modular configuration
|
|
||||||
2. Access the page via routing: `context.push('/faqs')`
|
|
||||||
3. The BLoC will automatically fetch FAQs on page load
|
|
||||||
|
|
||||||
## Asset Configuration
|
|
||||||
|
|
||||||
Update `pubspec.yaml` to include assets:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
flutter:
|
|
||||||
assets:
|
|
||||||
- lib/src/assets/faqs/
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing
|
|
||||||
|
|
||||||
The package includes test support with:
|
|
||||||
- `bloc_test` for BLoC testing
|
|
||||||
- `mocktail` for mocking dependencies
|
|
||||||
|
|
||||||
## Design System Integration
|
|
||||||
|
|
||||||
Uses the common design system components:
|
|
||||||
- `UiColors` - Color constants
|
|
||||||
- `UiConstants` - Sizing and radius constants
|
|
||||||
- `LucideIcons` - Icon library
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- FAQs are cached in memory after first load to improve performance
|
|
||||||
- Search is case-insensitive
|
|
||||||
- The widget state (expanded/collapsed items) is local to the widget and resets on navigation
|
|
||||||
@@ -3,8 +3,6 @@ import 'package:design_system/design_system.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
|
|
||||||
import '../blocs/faqs_bloc.dart';
|
import '../blocs/faqs_bloc.dart';
|
||||||
import '../widgets/faqs_widget.dart';
|
import '../widgets/faqs_widget.dart';
|
||||||
@@ -15,80 +13,19 @@ class FaqsPage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider<FaqsBloc>(
|
return Scaffold(
|
||||||
create: (BuildContext context) => Modular.get<FaqsBloc>()..add(const FetchFaqsEvent()),
|
appBar: UiAppBar(
|
||||||
child: Scaffold(
|
title: t.staff_faqs.title,
|
||||||
backgroundColor: UiColors.background,
|
showBackButton: true,
|
||||||
appBar: AppBar(
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
elevation: 0,
|
|
||||||
leading: GestureDetector(
|
|
||||||
onTap: () => context.pop(),
|
|
||||||
child: const Icon(
|
|
||||||
LucideIcons.chevronLeft,
|
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
t.staff_faqs.title,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: UiColors.textPrimary,
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
bottom: PreferredSize(
|
bottom: PreferredSize(
|
||||||
preferredSize: const Size.fromHeight(1),
|
preferredSize: const Size.fromHeight(1),
|
||||||
child: Container(color: UiColors.border, height: 1),
|
child: Container(color: UiColors.border, height: 1),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: Stack(
|
body: BlocProvider<FaqsBloc>(
|
||||||
children: <Widget>[
|
create: (BuildContext context) =>
|
||||||
const FaqsWidget(),
|
Modular.get<FaqsBloc>()..add(const FetchFaqsEvent()),
|
||||||
// Contact Support Button at Bottom
|
child: const Stack(children: <Widget>[FaqsWidget()]),
|
||||||
Positioned(
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(20),
|
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.white,
|
|
||||||
border: Border(top: BorderSide(color: UiColors.border)),
|
|
||||||
),
|
|
||||||
child: SafeArea(
|
|
||||||
top: false,
|
|
||||||
child: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 48,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: () => context.push('/messages'),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: UiColors.primary,
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
|
||||||
),
|
|
||||||
elevation: 0,
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
const Icon(LucideIcons.messageCircle, size: 20),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
t.staff_faqs.contact_support,
|
|
||||||
style: const TextStyle(fontWeight: FontWeight.w600),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:core_localization/core_localization.dart';
|
|||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
import 'package:staff_faqs/src/presentation/blocs/faqs_bloc.dart';
|
import 'package:staff_faqs/src/presentation/blocs/faqs_bloc.dart';
|
||||||
|
|
||||||
/// Widget displaying FAQs with search functionality and accordion items
|
/// Widget displaying FAQs with search functionality and accordion items
|
||||||
@@ -65,7 +64,7 @@ class _FaqsWidgetState extends State<FaqsWidget> {
|
|||||||
hintText: t.staff_faqs.search_placeholder,
|
hintText: t.staff_faqs.search_placeholder,
|
||||||
hintStyle: const TextStyle(color: UiColors.textPlaceholder),
|
hintStyle: const TextStyle(color: UiColors.textPlaceholder),
|
||||||
prefixIcon: const Icon(
|
prefixIcon: const Icon(
|
||||||
LucideIcons.search,
|
UiIcons.search,
|
||||||
color: UiColors.textSecondary,
|
color: UiColors.textSecondary,
|
||||||
),
|
),
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
@@ -87,7 +86,7 @@ class _FaqsWidgetState extends State<FaqsWidget> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Icon(
|
const Icon(
|
||||||
LucideIcons.helpCircle,
|
UiIcons.helpCircle,
|
||||||
size: 48,
|
size: 48,
|
||||||
color: UiColors.textSecondary,
|
color: UiColors.textSecondary,
|
||||||
),
|
),
|
||||||
@@ -100,7 +99,9 @@ class _FaqsWidgetState extends State<FaqsWidget> {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
...state.categories.asMap().entries.map((MapEntry<int, dynamic> entry) {
|
...state.categories.asMap().entries.map((
|
||||||
|
MapEntry<int, dynamic> entry,
|
||||||
|
) {
|
||||||
final int catIndex = entry.key;
|
final int catIndex = entry.key;
|
||||||
final dynamic categoryItem = entry.value;
|
final dynamic categoryItem = entry.value;
|
||||||
final String categoryName = categoryItem.category;
|
final String categoryName = categoryItem.category;
|
||||||
@@ -118,7 +119,9 @@ class _FaqsWidgetState extends State<FaqsWidget> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
...questions.asMap().entries.map((MapEntry<int, dynamic> qEntry) {
|
...questions.asMap().entries.map((
|
||||||
|
MapEntry<int, dynamic> qEntry,
|
||||||
|
) {
|
||||||
final int qIndex = qEntry.key;
|
final int qIndex = qEntry.key;
|
||||||
final dynamic questionItem = qEntry.value;
|
final dynamic questionItem = qEntry.value;
|
||||||
final String key = '$catIndex-$qIndex';
|
final String key = '$catIndex-$qIndex';
|
||||||
@@ -128,16 +131,18 @@ class _FaqsWidgetState extends State<FaqsWidget> {
|
|||||||
margin: const EdgeInsets.only(bottom: 8),
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius:
|
borderRadius: BorderRadius.circular(
|
||||||
BorderRadius.circular(UiConstants.radiusBase),
|
UiConstants.radiusBase,
|
||||||
|
),
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () => _toggleItem(key),
|
onTap: () => _toggleItem(key),
|
||||||
borderRadius:
|
borderRadius: BorderRadius.circular(
|
||||||
BorderRadius.circular(UiConstants.radiusBase),
|
UiConstants.radiusBase,
|
||||||
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -145,16 +150,13 @@ class _FaqsWidgetState extends State<FaqsWidget> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
questionItem.question,
|
questionItem.question,
|
||||||
style: const TextStyle(
|
style: UiTypography.body1r,
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Icon(
|
Icon(
|
||||||
isOpen
|
isOpen
|
||||||
? LucideIcons.chevronUp
|
? UiIcons.chevronUp
|
||||||
: LucideIcons.chevronDown,
|
: UiIcons.chevronDown,
|
||||||
color: UiColors.textSecondary,
|
color: UiColors.textSecondary,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
@@ -172,21 +174,17 @@ class _FaqsWidgetState extends State<FaqsWidget> {
|
|||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
questionItem.answer,
|
questionItem.answer,
|
||||||
style: const TextStyle(
|
style: UiTypography.body1r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
fontSize: 14,
|
|
||||||
height: 1.5,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}).toList(),
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
|
import 'package:krow_core/core.dart';
|
||||||
|
|
||||||
import 'data/repositories_impl/faqs_repository_impl.dart';
|
import 'data/repositories_impl/faqs_repository_impl.dart';
|
||||||
import 'domain/repositories/faqs_repository_interface.dart';
|
import 'domain/repositories/faqs_repository_interface.dart';
|
||||||
@@ -44,7 +45,7 @@ class FaqsModule extends Module {
|
|||||||
@override
|
@override
|
||||||
void routes(RouteManager r) {
|
void routes(RouteManager r) {
|
||||||
r.child(
|
r.child(
|
||||||
'/',
|
StaffPaths.childRoute(StaffPaths.faqs, StaffPaths.faqs),
|
||||||
child: (_) => const FaqsPage(),
|
child: (_) => const FaqsPage(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ dependencies:
|
|||||||
flutter_bloc: ^8.1.0
|
flutter_bloc: ^8.1.0
|
||||||
flutter_modular: ^6.3.0
|
flutter_modular: ^6.3.0
|
||||||
equatable: ^2.0.5
|
equatable: ^2.0.5
|
||||||
go_router: ^14.0.0
|
|
||||||
lucide_icons: ^0.257.0
|
|
||||||
|
|
||||||
# Architecture Packages
|
# Architecture Packages
|
||||||
krow_core:
|
krow_core:
|
||||||
@@ -25,12 +23,6 @@ dependencies:
|
|||||||
core_localization:
|
core_localization:
|
||||||
path: ../../../../../core_localization
|
path: ../../../../../core_localization
|
||||||
|
|
||||||
dev_dependencies:
|
|
||||||
flutter_test:
|
|
||||||
sdk: flutter
|
|
||||||
bloc_test: ^9.1.0
|
|
||||||
mocktail: ^1.0.0
|
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
assets:
|
assets:
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import 'package:staff_profile_experience/staff_profile_experience.dart';
|
|||||||
import 'package:staff_profile_info/staff_profile_info.dart';
|
import 'package:staff_profile_info/staff_profile_info.dart';
|
||||||
import 'package:staff_shifts/staff_shifts.dart';
|
import 'package:staff_shifts/staff_shifts.dart';
|
||||||
import 'package:staff_tax_forms/staff_tax_forms.dart';
|
import 'package:staff_tax_forms/staff_tax_forms.dart';
|
||||||
|
import 'package:staff_faqs/staff_faqs.dart';
|
||||||
import 'package:staff_time_card/staff_time_card.dart';
|
import 'package:staff_time_card/staff_time_card.dart';
|
||||||
|
|
||||||
class StaffMainModule extends Module {
|
class StaffMainModule extends Module {
|
||||||
@@ -102,5 +103,9 @@ class StaffMainModule extends Module {
|
|||||||
StaffPaths.childRoute(StaffPaths.main, StaffPaths.shiftDetailsRoute),
|
StaffPaths.childRoute(StaffPaths.main, StaffPaths.shiftDetailsRoute),
|
||||||
module: ShiftDetailsModule(),
|
module: ShiftDetailsModule(),
|
||||||
);
|
);
|
||||||
|
r.module(
|
||||||
|
StaffPaths.childRoute(StaffPaths.main, StaffPaths.faqs),
|
||||||
|
module: FaqsModule(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -573,14 +573,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.3"
|
||||||
go_router:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: go_router
|
|
||||||
sha256: f02fd7d2a4dc512fec615529824fdd217fecb3a3d3de68360293a551f21634b3
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "14.8.1"
|
|
||||||
google_fonts:
|
google_fonts:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ The application is broken down into several key functional modules:
|
|||||||
|
|
||||||
| Component | Primary Responsibility | Example Task |
|
| Component | Primary Responsibility | Example Task |
|
||||||
| :--- | :--- | :--- |
|
| :--- | :--- | :--- |
|
||||||
| **Router (GoRouter)** | Navigation traffic cop | Directs the user from the "Login" screen to the "Home" dashboard upon success. |
|
| **Router (Flutter Modular)** | Navigation traffic cop | Directs the user from the "Login" screen to the "Home" dashboard upon success. |
|
||||||
| **Screens (UI)** | Displaying information | Renders the "Create Order" form and captures the user's input for date and time. |
|
| **Screens (UI)** | Displaying information | Renders the "Create Order" form and captures the user's input for date and time. |
|
||||||
| **Providers (Riverpod)** | Data management & State | Holds the list of today's active shifts so multiple screens can access it without reloading. |
|
| **Providers (Riverpod)** | Data management & State | Holds the list of today's active shifts so multiple screens can access it without reloading. |
|
||||||
| **Widgets** | Reusable UI building blocks | A "Shift Card" widget that displays shift details effectively, used in multiple lists throughout the app. |
|
| **Widgets** | Reusable UI building blocks | A "Shift Card" widget that displays shift details effectively, used in multiple lists throughout the app. |
|
||||||
@@ -91,7 +91,7 @@ While currently operating as a high-fidelity prototype with mock data, the archi
|
|||||||
## 8. Key Design Decisions
|
## 8. Key Design Decisions
|
||||||
|
|
||||||
* **Flutter Framework:** chosen for its ability to produce high-performance, native-feeling apps for both iOS and Android from a single codebase, reducing development time and cost.
|
* **Flutter Framework:** chosen for its ability to produce high-performance, native-feeling apps for both iOS and Android from a single codebase, reducing development time and cost.
|
||||||
* **GoRouter for Navigation:** A modern routing package that handles complex navigation scenarios (like deep linking and sub-routes) which are essential for a multi-layered app like this.
|
* **Flutter Modular for Navigation:** A modern routing package that handles complex navigation scenarios (like deep linking and sub-routes) which are essential for a multi-layered app like this.
|
||||||
* **Riverpod for State Management:** A robust solution that catches programming errors at compile-time (while writing code) rather than run-time (while using the app), increasing app stability.
|
* **Riverpod for State Management:** A robust solution that catches programming errors at compile-time (while writing code) rather than run-time (while using the app), increasing app stability.
|
||||||
* **Mock Data Services:** The decision to use extensive mock data allows for rapid UI/UX iteration and testing of business flows without waiting for the full backend infrastructure to be built.
|
* **Mock Data Services:** The decision to use extensive mock data allows for rapid UI/UX iteration and testing of business flows without waiting for the full backend infrastructure to be built.
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ flowchart TD
|
|||||||
direction TB
|
direction TB
|
||||||
subgraph PresentationLayer["Presentation Layer (UI)"]
|
subgraph PresentationLayer["Presentation Layer (UI)"]
|
||||||
direction TB
|
direction TB
|
||||||
Router["GoRouter Navigation"]
|
Router["Flutter Modular Navigation"]
|
||||||
subgraph FeatureModules["Feature Modules"]
|
subgraph FeatureModules["Feature Modules"]
|
||||||
AuthUI["Auth Screens"]
|
AuthUI["Auth Screens"]
|
||||||
DashUI["Dashboard & Home"]
|
DashUI["Dashboard & Home"]
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ flowchart TD
|
|||||||
direction TB
|
direction TB
|
||||||
subgraph PresentationLayer["Presentation Layer (UI)"]
|
subgraph PresentationLayer["Presentation Layer (UI)"]
|
||||||
direction TB
|
direction TB
|
||||||
Router["GoRouter Navigation"]
|
Router["Flutter Modular Navigation"]
|
||||||
subgraph FeatureModules["Feature Modules"]
|
subgraph FeatureModules["Feature Modules"]
|
||||||
AuthUI["Auth & Onboarding"]
|
AuthUI["Auth & Onboarding"]
|
||||||
MarketUI["Marketplace & Jobs"]
|
MarketUI["Marketplace & Jobs"]
|
||||||
|
|||||||
Reference in New Issue
Block a user