Merge branch 'dev' into 312-feature-integrate-google-maps-places-autocomplete-for-hub-address-validation
This commit is contained in:
@@ -39,20 +39,25 @@ melos bootstrap
|
||||
### 3. Running the Apps
|
||||
You can run the applications using Melos scripts or through the `Makefile`:
|
||||
|
||||
First, find your device ID:
|
||||
```bash
|
||||
flutter devices
|
||||
```
|
||||
|
||||
#### Client App
|
||||
```bash
|
||||
# Using Melos
|
||||
melos run start:client -d android # or ios
|
||||
# Using Makefile
|
||||
make mobile-client-dev-android
|
||||
melos run start:client -- -d <device_id>
|
||||
# Using Makefile (DEVICE defaults to 'android' if not specified)
|
||||
make mobile-client-dev-android DEVICE=<device_id>
|
||||
```
|
||||
|
||||
#### Staff App
|
||||
```bash
|
||||
# Using Melos
|
||||
melos run start:staff -d android # or ios
|
||||
# Using Makefile
|
||||
make mobile-staff-dev-android
|
||||
melos run start:staff -- -d <device_id>
|
||||
# Using Makefile (DEVICE defaults to 'android' if not specified)
|
||||
make mobile-staff-dev-android DEVICE=<device_id>
|
||||
```
|
||||
|
||||
## 🛠 Useful Commands
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
import lldb
|
||||
|
||||
def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict):
|
||||
"""Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages."""
|
||||
base = frame.register["x0"].GetValueAsAddress()
|
||||
page_len = frame.register["x1"].GetValueAsUnsigned()
|
||||
|
||||
# Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the
|
||||
# first page to see if handled it correctly. This makes diagnosing
|
||||
# misconfiguration (e.g. missing breakpoint) easier.
|
||||
data = bytearray(page_len)
|
||||
data[0:8] = b'IHELPED!'
|
||||
|
||||
error = lldb.SBError()
|
||||
frame.GetThread().GetProcess().WriteMemory(base, data, error)
|
||||
if not error.Success():
|
||||
print(f'Failed to write into {base}[+{page_len}]', error)
|
||||
return
|
||||
|
||||
def __lldb_init_module(debugger: lldb.SBDebugger, _):
|
||||
target = debugger.GetDummyTarget()
|
||||
# Caveat: must use BreakpointCreateByRegEx here and not
|
||||
# BreakpointCreateByName. For some reasons callback function does not
|
||||
# get carried over from dummy target for the later.
|
||||
bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$")
|
||||
bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__))
|
||||
bp.SetAutoContinue(True)
|
||||
print("-- LLDB integration loaded --")
|
||||
@@ -1,5 +0,0 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
command script import --relative-to-command-file flutter_lldb_helper.py
|
||||
@@ -1 +0,0 @@
|
||||
C:/Users/Admin/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_linux-2.2.1/
|
||||
@@ -1 +0,0 @@
|
||||
C:/Users/Admin/AppData/Local/Pub/Cache/hosted/pub.dev/shared_preferences_linux-2.4.1/
|
||||
@@ -1 +0,0 @@
|
||||
C:/Users/Admin/AppData/Local/Pub/Cache/hosted/pub.dev/url_launcher_linux-3.2.2/
|
||||
@@ -1,18 +0,0 @@
|
||||
// This is a generated file; do not edit or check into version control.
|
||||
<<<<<<< Updated upstream
|
||||
FLUTTER_ROOT=/Users/josesalazar/flutter
|
||||
FLUTTER_APPLICATION_PATH=/Users/josesalazar/Documents/DEV/krow-workforce/apps/mobile/apps/client
|
||||
=======
|
||||
FLUTTER_ROOT=C:\flutter\src\flutter
|
||||
FLUTTER_APPLICATION_PATH=E:\Krow-google\krow-workforce\apps\mobile\apps\client
|
||||
>>>>>>> Stashed changes
|
||||
COCOAPODS_PARALLEL_CODE_SIGN=true
|
||||
FLUTTER_TARGET=/Users/josesalazar/Documents/DEV/krow-workforce/apps/mobile/apps/client/lib/main.dart
|
||||
FLUTTER_BUILD_DIR=build
|
||||
FLUTTER_BUILD_NAME=1.0.0
|
||||
FLUTTER_BUILD_NUMBER=1
|
||||
DART_DEFINES=RkxVVFRFUl9WRVJTSU9OPTMuMzguNw==,RkxVVFRFUl9DSEFOTkVMPXN0YWJsZQ==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049M2I2MmVmYzJhMw==,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049NzhmYzMwMTJlNA==,RkxVVFRFUl9EQVJUX1ZFUlNJT049My4xMC43
|
||||
DART_OBFUSCATION=false
|
||||
TRACK_WIDGET_CREATION=true
|
||||
TREE_SHAKE_ICONS=false
|
||||
PACKAGE_CONFIG=/Users/josesalazar/Documents/DEV/krow-workforce/apps/mobile/.dart_tool/package_config.json
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/bin/sh
|
||||
# This is a generated file; do not edit or check into version control.
|
||||
export "FLUTTER_ROOT=C:\flutter\src\flutter"
|
||||
export "FLUTTER_APPLICATION_PATH=E:\Krow-google\krow-workforce\apps\mobile\apps\client"
|
||||
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
|
||||
export "FLUTTER_TARGET=/Users/josesalazar/Documents/DEV/krow-workforce/apps/mobile/apps/client/lib/main.dart"
|
||||
export "FLUTTER_BUILD_DIR=build"
|
||||
export "FLUTTER_BUILD_NAME=1.0.0"
|
||||
export "FLUTTER_BUILD_NUMBER=1"
|
||||
export "DART_DEFINES=RkxVVFRFUl9WRVJTSU9OPTMuMzguNw==,RkxVVFRFUl9DSEFOTkVMPXN0YWJsZQ==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049M2I2MmVmYzJhMw==,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049NzhmYzMwMTJlNA==,RkxVVFRFUl9EQVJUX1ZFUlNJT049My4xMC43"
|
||||
export "DART_OBFUSCATION=false"
|
||||
export "TRACK_WIDGET_CREATION=true"
|
||||
export "TREE_SHAKE_ICONS=false"
|
||||
export "PACKAGE_CONFIG=/Users/josesalazar/Documents/DEV/krow-workforce/apps/mobile/.dart_tool/package_config.json"
|
||||
@@ -1 +0,0 @@
|
||||
C:/Users/Admin/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_auth-6.1.4/
|
||||
@@ -1 +0,0 @@
|
||||
C:/Users/Admin/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_core-4.4.0/
|
||||
@@ -1 +0,0 @@
|
||||
C:/Users/Admin/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_windows-2.3.0/
|
||||
@@ -1 +0,0 @@
|
||||
C:/Users/Admin/AppData/Local/Pub/Cache/hosted/pub.dev/shared_preferences_windows-2.4.1/
|
||||
@@ -1 +0,0 @@
|
||||
C:/Users/Admin/AppData/Local/Pub/Cache/hosted/pub.dev/url_launcher_windows-3.1.5/
|
||||
@@ -1,32 +0,0 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
import lldb
|
||||
|
||||
def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict):
|
||||
"""Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages."""
|
||||
base = frame.register["x0"].GetValueAsAddress()
|
||||
page_len = frame.register["x1"].GetValueAsUnsigned()
|
||||
|
||||
# Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the
|
||||
# first page to see if handled it correctly. This makes diagnosing
|
||||
# misconfiguration (e.g. missing breakpoint) easier.
|
||||
data = bytearray(page_len)
|
||||
data[0:8] = b'IHELPED!'
|
||||
|
||||
error = lldb.SBError()
|
||||
frame.GetThread().GetProcess().WriteMemory(base, data, error)
|
||||
if not error.Success():
|
||||
print(f'Failed to write into {base}[+{page_len}]', error)
|
||||
return
|
||||
|
||||
def __lldb_init_module(debugger: lldb.SBDebugger, _):
|
||||
target = debugger.GetDummyTarget()
|
||||
# Caveat: must use BreakpointCreateByRegEx here and not
|
||||
# BreakpointCreateByName. For some reasons callback function does not
|
||||
# get carried over from dummy target for the later.
|
||||
bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$")
|
||||
bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__))
|
||||
bp.SetAutoContinue(True)
|
||||
print("-- LLDB integration loaded --")
|
||||
@@ -1,5 +0,0 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
command script import --relative-to-command-file flutter_lldb_helper.py
|
||||
@@ -1,11 +0,0 @@
|
||||
// This is a generated file; do not edit or check into version control.
|
||||
FLUTTER_ROOT=C:\flutter\src\flutter
|
||||
FLUTTER_APPLICATION_PATH=E:\Krow-google\krow-workforce\apps\mobile\apps\design_system_viewer
|
||||
COCOAPODS_PARALLEL_CODE_SIGN=true
|
||||
FLUTTER_BUILD_DIR=build
|
||||
FLUTTER_BUILD_NAME=1.0.0
|
||||
FLUTTER_BUILD_NUMBER=1
|
||||
DART_OBFUSCATION=false
|
||||
TRACK_WIDGET_CREATION=true
|
||||
TREE_SHAKE_ICONS=false
|
||||
PACKAGE_CONFIG=.dart_tool/package_config.json
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/sh
|
||||
# This is a generated file; do not edit or check into version control.
|
||||
export "FLUTTER_ROOT=C:\flutter\src\flutter"
|
||||
export "FLUTTER_APPLICATION_PATH=E:\Krow-google\krow-workforce\apps\mobile\apps\design_system_viewer"
|
||||
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
|
||||
export "FLUTTER_BUILD_DIR=build"
|
||||
export "FLUTTER_BUILD_NAME=1.0.0"
|
||||
export "FLUTTER_BUILD_NUMBER=1"
|
||||
export "DART_OBFUSCATION=false"
|
||||
export "TRACK_WIDGET_CREATION=true"
|
||||
export "TREE_SHAKE_ICONS=false"
|
||||
export "PACKAGE_CONFIG=.dart_tool/package_config.json"
|
||||
@@ -1,32 +0,0 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
import lldb
|
||||
|
||||
def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict):
|
||||
"""Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages."""
|
||||
base = frame.register["x0"].GetValueAsAddress()
|
||||
page_len = frame.register["x1"].GetValueAsUnsigned()
|
||||
|
||||
# Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the
|
||||
# first page to see if handled it correctly. This makes diagnosing
|
||||
# misconfiguration (e.g. missing breakpoint) easier.
|
||||
data = bytearray(page_len)
|
||||
data[0:8] = b'IHELPED!'
|
||||
|
||||
error = lldb.SBError()
|
||||
frame.GetThread().GetProcess().WriteMemory(base, data, error)
|
||||
if not error.Success():
|
||||
print(f'Failed to write into {base}[+{page_len}]', error)
|
||||
return
|
||||
|
||||
def __lldb_init_module(debugger: lldb.SBDebugger, _):
|
||||
target = debugger.GetDummyTarget()
|
||||
# Caveat: must use BreakpointCreateByRegEx here and not
|
||||
# BreakpointCreateByName. For some reasons callback function does not
|
||||
# get carried over from dummy target for the later.
|
||||
bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$")
|
||||
bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__))
|
||||
bp.SetAutoContinue(True)
|
||||
print("-- LLDB integration loaded --")
|
||||
@@ -1,5 +0,0 @@
|
||||
#
|
||||
# Generated file, do not edit.
|
||||
#
|
||||
|
||||
command script import --relative-to-command-file flutter_lldb_helper.py
|
||||
@@ -1 +0,0 @@
|
||||
C:/Users/Admin/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_linux-2.2.1/
|
||||
@@ -1 +0,0 @@
|
||||
C:/Users/Admin/AppData/Local/Pub/Cache/hosted/pub.dev/shared_preferences_linux-2.4.1/
|
||||
@@ -1,13 +0,0 @@
|
||||
// This is a generated file; do not edit or check into version control.
|
||||
FLUTTER_ROOT=/Users/achinthaisuru/Documents/flutter
|
||||
FLUTTER_APPLICATION_PATH=/Users/achinthaisuru/Documents/Github/krow-workforce/apps/mobile/apps/staff
|
||||
COCOAPODS_PARALLEL_CODE_SIGN=true
|
||||
FLUTTER_TARGET=/Users/achinthaisuru/Documents/Github/krow-workforce/apps/mobile/apps/staff/lib/main.dart
|
||||
FLUTTER_BUILD_DIR=build
|
||||
FLUTTER_BUILD_NAME=0.0.13
|
||||
FLUTTER_BUILD_NUMBER=1
|
||||
DART_DEFINES=RkxVVFRFUl9WRVJTSU9OPTMuMzguNw==,RkxVVFRFUl9DSEFOTkVMPXN0YWJsZQ==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049M2I2MmVmYzJhMw==,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049NzhmYzMwMTJlNA==,RkxVVFRFUl9EQVJUX1ZFUlNJT049My4xMC43
|
||||
DART_OBFUSCATION=false
|
||||
TRACK_WIDGET_CREATION=true
|
||||
TREE_SHAKE_ICONS=false
|
||||
PACKAGE_CONFIG=/Users/achinthaisuru/Documents/Github/krow-workforce/apps/mobile/.dart_tool/package_config.json
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/bin/sh
|
||||
# This is a generated file; do not edit or check into version control.
|
||||
export "FLUTTER_ROOT=/Users/achinthaisuru/Documents/flutter"
|
||||
export "FLUTTER_APPLICATION_PATH=/Users/achinthaisuru/Documents/Github/krow-workforce/apps/mobile/apps/staff"
|
||||
export "COCOAPODS_PARALLEL_CODE_SIGN=true"
|
||||
export "FLUTTER_TARGET=/Users/achinthaisuru/Documents/Github/krow-workforce/apps/mobile/apps/staff/lib/main.dart"
|
||||
export "FLUTTER_BUILD_DIR=build"
|
||||
export "FLUTTER_BUILD_NAME=0.0.13"
|
||||
export "FLUTTER_BUILD_NUMBER=1"
|
||||
export "DART_DEFINES=RkxVVFRFUl9WRVJTSU9OPTMuMzguNw==,RkxVVFRFUl9DSEFOTkVMPXN0YWJsZQ==,RkxVVFRFUl9HSVRfVVJMPWh0dHBzOi8vZ2l0aHViLmNvbS9mbHV0dGVyL2ZsdXR0ZXIuZ2l0,RkxVVFRFUl9GUkFNRVdPUktfUkVWSVNJT049M2I2MmVmYzJhMw==,RkxVVFRFUl9FTkdJTkVfUkVWSVNJT049NzhmYzMwMTJlNA==,RkxVVFRFUl9EQVJUX1ZFUlNJT049My4xMC43"
|
||||
export "DART_OBFUSCATION=false"
|
||||
export "TRACK_WIDGET_CREATION=true"
|
||||
export "TREE_SHAKE_ICONS=false"
|
||||
export "PACKAGE_CONFIG=/Users/achinthaisuru/Documents/Github/krow-workforce/apps/mobile/.dart_tool/package_config.json"
|
||||
@@ -1 +0,0 @@
|
||||
C:/Users/Admin/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_auth-6.1.4/
|
||||
@@ -1 +0,0 @@
|
||||
C:/Users/Admin/AppData/Local/Pub/Cache/hosted/pub.dev/firebase_core-4.4.0/
|
||||
@@ -1 +0,0 @@
|
||||
C:/Users/Admin/AppData/Local/Pub/Cache/hosted/pub.dev/path_provider_windows-2.3.0/
|
||||
@@ -1 +0,0 @@
|
||||
C:/Users/Admin/AppData/Local/Pub/Cache/hosted/pub.dev/shared_preferences_windows-2.4.1/
|
||||
@@ -8,3 +8,4 @@ export 'src/domain/usecases/set_locale_use_case.dart';
|
||||
export 'src/data/repositories_impl/locale_repository_impl.dart';
|
||||
export 'src/data/datasources/locale_local_data_source.dart';
|
||||
export 'src/localization_module.dart';
|
||||
export 'src/utils/error_translator.dart';
|
||||
|
||||
@@ -728,6 +728,60 @@
|
||||
"paid": "Paid",
|
||||
"pending": "Pending"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"auth": {
|
||||
"invalid_credentials": "The email or password you entered is incorrect.",
|
||||
"account_exists": "An account with this email already exists. Try signing in instead.",
|
||||
"session_expired": "Your session has expired. Please sign in again.",
|
||||
"user_not_found": "We couldn't find your account. Please check your email and try again.",
|
||||
"unauthorized_app": "This account is not authorized for this app.",
|
||||
"weak_password": "Please choose a stronger password with at least 8 characters.",
|
||||
"sign_up_failed": "We couldn't create your account. Please try again.",
|
||||
"sign_in_failed": "We couldn't sign you in. Please try again.",
|
||||
"not_authenticated": "Please sign in to continue.",
|
||||
"password_mismatch": "This email is already registered. Please use the correct password or tap 'Forgot Password' to reset it.",
|
||||
"google_only_account": "This email is registered via Google. Please use 'Forgot Password' to set a password, then try signing up again with the same information."
|
||||
},
|
||||
"hub": {
|
||||
"has_orders": "This hub has active orders and cannot be deleted.",
|
||||
"not_found": "The hub you're looking for doesn't exist.",
|
||||
"creation_failed": "We couldn't create the hub. Please try again."
|
||||
},
|
||||
"order": {
|
||||
"missing_hub": "Please select a location for your order.",
|
||||
"missing_vendor": "Please select a vendor for your order.",
|
||||
"creation_failed": "We couldn't create your order. Please try again.",
|
||||
"shift_creation_failed": "We couldn't schedule the shift. Please try again.",
|
||||
"missing_business": "Your business profile couldn't be loaded. Please sign in again."
|
||||
},
|
||||
"profile": {
|
||||
"staff_not_found": "Your profile couldn't be loaded. Please sign in again.",
|
||||
"business_not_found": "Your business profile couldn't be loaded. Please sign in again.",
|
||||
"update_failed": "We couldn't update your profile. Please try again."
|
||||
},
|
||||
"shift": {
|
||||
"no_open_roles": "There are no open positions available for this shift.",
|
||||
"application_not_found": "Your application couldn't be found.",
|
||||
"no_active_shift": "You don't have an active shift to clock out from."
|
||||
},
|
||||
"generic": {
|
||||
"unknown": "Something went wrong. Please try again.",
|
||||
"no_connection": "No internet connection. Please check your network and try again."
|
||||
}
|
||||
},
|
||||
"success": {
|
||||
"hub": {
|
||||
"created": "Hub created successfully!",
|
||||
"deleted": "Hub deleted successfully!",
|
||||
"nfc_assigned": "NFC tag assigned successfully!"
|
||||
},
|
||||
"order": {
|
||||
"created": "Order created successfully!"
|
||||
},
|
||||
"profile": {
|
||||
"updated": "Profile updated successfully!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -727,5 +727,59 @@
|
||||
"paid": "Pagado",
|
||||
"pending": "Pendiente"
|
||||
}
|
||||
},
|
||||
"errors": {
|
||||
"auth": {
|
||||
"invalid_credentials": "El correo electrónico o la contraseña que ingresaste es incorrecta.",
|
||||
"account_exists": "Ya existe una cuenta con este correo electrónico. Intenta iniciar sesión.",
|
||||
"session_expired": "Tu sesión ha expirado. Por favor, inicia sesión de nuevo.",
|
||||
"user_not_found": "No pudimos encontrar tu cuenta. Por favor, verifica tu correo electrónico e intenta de nuevo.",
|
||||
"unauthorized_app": "Esta cuenta no está autorizada para esta aplicación.",
|
||||
"weak_password": "Por favor, elige una contraseña más segura con al menos 8 caracteres.",
|
||||
"sign_up_failed": "No pudimos crear tu cuenta. Por favor, intenta de nuevo.",
|
||||
"sign_in_failed": "No pudimos iniciar sesión. Por favor, intenta de nuevo.",
|
||||
"not_authenticated": "Por favor, inicia sesión para continuar.",
|
||||
"password_mismatch": "Este correo ya está registrado. Por favor, usa la contraseña correcta o toca 'Olvidé mi contraseña' para restablecerla.",
|
||||
"google_only_account": "Este correo está registrado con Google. Por favor, usa 'Olvidé mi contraseña' para establecer una contraseña, luego intenta registrarte de nuevo con la misma información."
|
||||
},
|
||||
"hub": {
|
||||
"has_orders": "Este hub tiene órdenes activas y no puede ser eliminado.",
|
||||
"not_found": "El hub que buscas no existe.",
|
||||
"creation_failed": "No pudimos crear el hub. Por favor, intenta de nuevo."
|
||||
},
|
||||
"order": {
|
||||
"missing_hub": "Por favor, selecciona una ubicación para tu orden.",
|
||||
"missing_vendor": "Por favor, selecciona un proveedor para tu orden.",
|
||||
"creation_failed": "No pudimos crear tu orden. Por favor, intenta de nuevo.",
|
||||
"shift_creation_failed": "No pudimos programar el turno. Por favor, intenta de nuevo.",
|
||||
"missing_business": "No se pudo cargar tu perfil de empresa. Por favor, inicia sesión de nuevo."
|
||||
},
|
||||
"profile": {
|
||||
"staff_not_found": "No se pudo cargar tu perfil. Por favor, inicia sesión de nuevo.",
|
||||
"business_not_found": "No se pudo cargar tu perfil de empresa. Por favor, inicia sesión de nuevo.",
|
||||
"update_failed": "No pudimos actualizar tu perfil. Por favor, intenta de nuevo."
|
||||
},
|
||||
"shift": {
|
||||
"no_open_roles": "No hay posiciones abiertas disponibles para este turno.",
|
||||
"application_not_found": "No se pudo encontrar tu solicitud.",
|
||||
"no_active_shift": "No tienes un turno activo para registrar salida."
|
||||
},
|
||||
"generic": {
|
||||
"unknown": "Algo salió mal. Por favor, intenta de nuevo.",
|
||||
"no_connection": "Sin conexión a internet. Por favor, verifica tu red e intenta de nuevo."
|
||||
}
|
||||
},
|
||||
"success": {
|
||||
"hub": {
|
||||
"created": "¡Hub creado exitosamente!",
|
||||
"deleted": "¡Hub eliminado exitosamente!",
|
||||
"nfc_assigned": "¡Etiqueta NFC asignada exitosamente!"
|
||||
},
|
||||
"order": {
|
||||
"created": "¡Orden creada exitosamente!"
|
||||
},
|
||||
"profile": {
|
||||
"updated": "¡Perfil actualizado exitosamente!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
/// To regenerate, run: `dart run slang`
|
||||
///
|
||||
/// Locales: 2
|
||||
/// Strings: 1044 (522 per locale)
|
||||
/// Strings: 1108 (554 per locale)
|
||||
///
|
||||
/// Built on 2026-01-31 at 17:37 UTC
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ class Translations with BaseTranslations<AppLocale, Translations> {
|
||||
late final TranslationsStaffProfileAttireEn staff_profile_attire = TranslationsStaffProfileAttireEn._(_root);
|
||||
late final TranslationsStaffShiftsEn staff_shifts = TranslationsStaffShiftsEn._(_root);
|
||||
late final TranslationsStaffTimeCardEn staff_time_card = TranslationsStaffTimeCardEn._(_root);
|
||||
late final TranslationsErrorsEn errors = TranslationsErrorsEn._(_root);
|
||||
late final TranslationsSuccessEn success = TranslationsSuccessEn._(_root);
|
||||
}
|
||||
|
||||
// Path: common
|
||||
@@ -420,6 +422,33 @@ class TranslationsStaffTimeCardEn {
|
||||
late final TranslationsStaffTimeCardStatusEn status = TranslationsStaffTimeCardStatusEn._(_root);
|
||||
}
|
||||
|
||||
// Path: errors
|
||||
class TranslationsErrorsEn {
|
||||
TranslationsErrorsEn._(this._root);
|
||||
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
late final TranslationsErrorsAuthEn auth = TranslationsErrorsAuthEn._(_root);
|
||||
late final TranslationsErrorsHubEn hub = TranslationsErrorsHubEn._(_root);
|
||||
late final TranslationsErrorsOrderEn order = TranslationsErrorsOrderEn._(_root);
|
||||
late final TranslationsErrorsProfileEn profile = TranslationsErrorsProfileEn._(_root);
|
||||
late final TranslationsErrorsShiftEn shift = TranslationsErrorsShiftEn._(_root);
|
||||
late final TranslationsErrorsGenericEn generic = TranslationsErrorsGenericEn._(_root);
|
||||
}
|
||||
|
||||
// Path: success
|
||||
class TranslationsSuccessEn {
|
||||
TranslationsSuccessEn._(this._root);
|
||||
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
late final TranslationsSuccessHubEn hub = TranslationsSuccessHubEn._(_root);
|
||||
late final TranslationsSuccessOrderEn order = TranslationsSuccessOrderEn._(_root);
|
||||
late final TranslationsSuccessProfileEn profile = TranslationsSuccessProfileEn._(_root);
|
||||
}
|
||||
|
||||
// Path: staff_authentication.get_started_page
|
||||
class TranslationsStaffAuthenticationGetStartedPageEn {
|
||||
TranslationsStaffAuthenticationGetStartedPageEn._(this._root);
|
||||
@@ -1745,6 +1774,183 @@ class TranslationsStaffTimeCardStatusEn {
|
||||
String get pending => 'Pending';
|
||||
}
|
||||
|
||||
// Path: errors.auth
|
||||
class TranslationsErrorsAuthEn {
|
||||
TranslationsErrorsAuthEn._(this._root);
|
||||
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
|
||||
/// en: 'The email or password you entered is incorrect.'
|
||||
String get invalid_credentials => 'The email or password you entered is incorrect.';
|
||||
|
||||
/// en: 'An account with this email already exists. Try signing in instead.'
|
||||
String get account_exists => 'An account with this email already exists. Try signing in instead.';
|
||||
|
||||
/// en: 'Your session has expired. Please sign in again.'
|
||||
String get session_expired => 'Your session has expired. Please sign in again.';
|
||||
|
||||
/// en: 'We couldn't find your account. Please check your email and try again.'
|
||||
String get user_not_found => 'We couldn\'t find your account. Please check your email and try again.';
|
||||
|
||||
/// en: 'This account is not authorized for this app.'
|
||||
String get unauthorized_app => 'This account is not authorized for this app.';
|
||||
|
||||
/// en: 'Please choose a stronger password with at least 8 characters.'
|
||||
String get weak_password => 'Please choose a stronger password with at least 8 characters.';
|
||||
|
||||
/// en: 'We couldn't create your account. Please try again.'
|
||||
String get sign_up_failed => 'We couldn\'t create your account. Please try again.';
|
||||
|
||||
/// en: 'We couldn't sign you in. Please try again.'
|
||||
String get sign_in_failed => 'We couldn\'t sign you in. Please try again.';
|
||||
|
||||
/// en: 'Please sign in to continue.'
|
||||
String get not_authenticated => 'Please sign in to continue.';
|
||||
|
||||
/// en: 'This email is already registered. Please use the correct password or tap 'Forgot Password' to reset it.'
|
||||
String get password_mismatch => 'This email is already registered. Please use the correct password or tap \'Forgot Password\' to reset it.';
|
||||
|
||||
/// en: 'This email is registered via Google. Please use 'Forgot Password' to set a password, then try signing up again with the same information.'
|
||||
String get google_only_account => 'This email is registered via Google. Please use \'Forgot Password\' to set a password, then try signing up again with the same information.';
|
||||
}
|
||||
|
||||
// Path: errors.hub
|
||||
class TranslationsErrorsHubEn {
|
||||
TranslationsErrorsHubEn._(this._root);
|
||||
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
|
||||
/// en: 'This hub has active orders and cannot be deleted.'
|
||||
String get has_orders => 'This hub has active orders and cannot be deleted.';
|
||||
|
||||
/// en: 'The hub you're looking for doesn't exist.'
|
||||
String get not_found => 'The hub you\'re looking for doesn\'t exist.';
|
||||
|
||||
/// en: 'We couldn't create the hub. Please try again.'
|
||||
String get creation_failed => 'We couldn\'t create the hub. Please try again.';
|
||||
}
|
||||
|
||||
// Path: errors.order
|
||||
class TranslationsErrorsOrderEn {
|
||||
TranslationsErrorsOrderEn._(this._root);
|
||||
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
|
||||
/// en: 'Please select a location for your order.'
|
||||
String get missing_hub => 'Please select a location for your order.';
|
||||
|
||||
/// en: 'Please select a vendor for your order.'
|
||||
String get missing_vendor => 'Please select a vendor for your order.';
|
||||
|
||||
/// en: 'We couldn't create your order. Please try again.'
|
||||
String get creation_failed => 'We couldn\'t create your order. Please try again.';
|
||||
|
||||
/// en: 'We couldn't schedule the shift. Please try again.'
|
||||
String get shift_creation_failed => 'We couldn\'t schedule the shift. Please try again.';
|
||||
|
||||
/// en: 'Your business profile couldn't be loaded. Please sign in again.'
|
||||
String get missing_business => 'Your business profile couldn\'t be loaded. Please sign in again.';
|
||||
}
|
||||
|
||||
// Path: errors.profile
|
||||
class TranslationsErrorsProfileEn {
|
||||
TranslationsErrorsProfileEn._(this._root);
|
||||
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
|
||||
/// en: 'Your profile couldn't be loaded. Please sign in again.'
|
||||
String get staff_not_found => 'Your profile couldn\'t be loaded. Please sign in again.';
|
||||
|
||||
/// en: 'Your business profile couldn't be loaded. Please sign in again.'
|
||||
String get business_not_found => 'Your business profile couldn\'t be loaded. Please sign in again.';
|
||||
|
||||
/// en: 'We couldn't update your profile. Please try again.'
|
||||
String get update_failed => 'We couldn\'t update your profile. Please try again.';
|
||||
}
|
||||
|
||||
// Path: errors.shift
|
||||
class TranslationsErrorsShiftEn {
|
||||
TranslationsErrorsShiftEn._(this._root);
|
||||
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
|
||||
/// en: 'There are no open positions available for this shift.'
|
||||
String get no_open_roles => 'There are no open positions available for this shift.';
|
||||
|
||||
/// en: 'Your application couldn't be found.'
|
||||
String get application_not_found => 'Your application couldn\'t be found.';
|
||||
|
||||
/// en: 'You don't have an active shift to clock out from.'
|
||||
String get no_active_shift => 'You don\'t have an active shift to clock out from.';
|
||||
}
|
||||
|
||||
// Path: errors.generic
|
||||
class TranslationsErrorsGenericEn {
|
||||
TranslationsErrorsGenericEn._(this._root);
|
||||
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
|
||||
/// en: 'Something went wrong. Please try again.'
|
||||
String get unknown => 'Something went wrong. Please try again.';
|
||||
|
||||
/// en: 'No internet connection. Please check your network and try again.'
|
||||
String get no_connection => 'No internet connection. Please check your network and try again.';
|
||||
}
|
||||
|
||||
// Path: success.hub
|
||||
class TranslationsSuccessHubEn {
|
||||
TranslationsSuccessHubEn._(this._root);
|
||||
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
|
||||
/// en: 'Hub created successfully!'
|
||||
String get created => 'Hub created successfully!';
|
||||
|
||||
/// en: 'Hub deleted successfully!'
|
||||
String get deleted => 'Hub deleted successfully!';
|
||||
|
||||
/// en: 'NFC tag assigned successfully!'
|
||||
String get nfc_assigned => 'NFC tag assigned successfully!';
|
||||
}
|
||||
|
||||
// Path: success.order
|
||||
class TranslationsSuccessOrderEn {
|
||||
TranslationsSuccessOrderEn._(this._root);
|
||||
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
|
||||
/// en: 'Order created successfully!'
|
||||
String get created => 'Order created successfully!';
|
||||
}
|
||||
|
||||
// Path: success.profile
|
||||
class TranslationsSuccessProfileEn {
|
||||
TranslationsSuccessProfileEn._(this._root);
|
||||
|
||||
final Translations _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
|
||||
/// en: 'Profile updated successfully!'
|
||||
String get updated => 'Profile updated successfully!';
|
||||
}
|
||||
|
||||
// Path: staff_authentication.profile_setup_page.steps
|
||||
class TranslationsStaffAuthenticationProfileSetupPageStepsEn {
|
||||
TranslationsStaffAuthenticationProfileSetupPageStepsEn._(this._root);
|
||||
@@ -3216,6 +3422,38 @@ extension on Translations {
|
||||
'staff_time_card.status.disputed' => 'Disputed',
|
||||
'staff_time_card.status.paid' => 'Paid',
|
||||
'staff_time_card.status.pending' => 'Pending',
|
||||
'errors.auth.invalid_credentials' => 'The email or password you entered is incorrect.',
|
||||
'errors.auth.account_exists' => 'An account with this email already exists. Try signing in instead.',
|
||||
'errors.auth.session_expired' => 'Your session has expired. Please sign in again.',
|
||||
'errors.auth.user_not_found' => 'We couldn\'t find your account. Please check your email and try again.',
|
||||
'errors.auth.unauthorized_app' => 'This account is not authorized for this app.',
|
||||
'errors.auth.weak_password' => 'Please choose a stronger password with at least 8 characters.',
|
||||
'errors.auth.sign_up_failed' => 'We couldn\'t create your account. Please try again.',
|
||||
'errors.auth.sign_in_failed' => 'We couldn\'t sign you in. Please try again.',
|
||||
'errors.auth.not_authenticated' => 'Please sign in to continue.',
|
||||
'errors.auth.password_mismatch' => 'This email is already registered. Please use the correct password or tap \'Forgot Password\' to reset it.',
|
||||
'errors.auth.google_only_account' => 'This email is registered via Google. Please use \'Forgot Password\' to set a password, then try signing up again with the same information.',
|
||||
'errors.hub.has_orders' => 'This hub has active orders and cannot be deleted.',
|
||||
'errors.hub.not_found' => 'The hub you\'re looking for doesn\'t exist.',
|
||||
'errors.hub.creation_failed' => 'We couldn\'t create the hub. Please try again.',
|
||||
'errors.order.missing_hub' => 'Please select a location for your order.',
|
||||
'errors.order.missing_vendor' => 'Please select a vendor for your order.',
|
||||
'errors.order.creation_failed' => 'We couldn\'t create your order. Please try again.',
|
||||
'errors.order.shift_creation_failed' => 'We couldn\'t schedule the shift. Please try again.',
|
||||
'errors.order.missing_business' => 'Your business profile couldn\'t be loaded. Please sign in again.',
|
||||
'errors.profile.staff_not_found' => 'Your profile couldn\'t be loaded. Please sign in again.',
|
||||
'errors.profile.business_not_found' => 'Your business profile couldn\'t be loaded. Please sign in again.',
|
||||
'errors.profile.update_failed' => 'We couldn\'t update your profile. Please try again.',
|
||||
'errors.shift.no_open_roles' => 'There are no open positions available for this shift.',
|
||||
'errors.shift.application_not_found' => 'Your application couldn\'t be found.',
|
||||
'errors.shift.no_active_shift' => 'You don\'t have an active shift to clock out from.',
|
||||
'errors.generic.unknown' => 'Something went wrong. Please try again.',
|
||||
'errors.generic.no_connection' => 'No internet connection. Please check your network and try again.',
|
||||
'success.hub.created' => 'Hub created successfully!',
|
||||
'success.hub.deleted' => 'Hub deleted successfully!',
|
||||
'success.hub.nfc_assigned' => 'NFC tag assigned successfully!',
|
||||
'success.order.created' => 'Order created successfully!',
|
||||
'success.profile.updated' => 'Profile updated successfully!',
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@ class TranslationsEs with BaseTranslations<AppLocale, Translations> implements T
|
||||
@override late final _TranslationsStaffProfileAttireEs staff_profile_attire = _TranslationsStaffProfileAttireEs._(_root);
|
||||
@override late final _TranslationsStaffShiftsEs staff_shifts = _TranslationsStaffShiftsEs._(_root);
|
||||
@override late final _TranslationsStaffTimeCardEs staff_time_card = _TranslationsStaffTimeCardEs._(_root);
|
||||
@override late final _TranslationsErrorsEs errors = _TranslationsErrorsEs._(_root);
|
||||
@override late final _TranslationsSuccessEs success = _TranslationsSuccessEs._(_root);
|
||||
}
|
||||
|
||||
// Path: common
|
||||
@@ -310,6 +312,33 @@ class _TranslationsStaffTimeCardEs implements TranslationsStaffTimeCardEn {
|
||||
@override late final _TranslationsStaffTimeCardStatusEs status = _TranslationsStaffTimeCardStatusEs._(_root);
|
||||
}
|
||||
|
||||
// Path: errors
|
||||
class _TranslationsErrorsEs implements TranslationsErrorsEn {
|
||||
_TranslationsErrorsEs._(this._root);
|
||||
|
||||
final TranslationsEs _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
@override late final _TranslationsErrorsAuthEs auth = _TranslationsErrorsAuthEs._(_root);
|
||||
@override late final _TranslationsErrorsHubEs hub = _TranslationsErrorsHubEs._(_root);
|
||||
@override late final _TranslationsErrorsOrderEs order = _TranslationsErrorsOrderEs._(_root);
|
||||
@override late final _TranslationsErrorsProfileEs profile = _TranslationsErrorsProfileEs._(_root);
|
||||
@override late final _TranslationsErrorsShiftEs shift = _TranslationsErrorsShiftEs._(_root);
|
||||
@override late final _TranslationsErrorsGenericEs generic = _TranslationsErrorsGenericEs._(_root);
|
||||
}
|
||||
|
||||
// Path: success
|
||||
class _TranslationsSuccessEs implements TranslationsSuccessEn {
|
||||
_TranslationsSuccessEs._(this._root);
|
||||
|
||||
final TranslationsEs _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
@override late final _TranslationsSuccessHubEs hub = _TranslationsSuccessHubEs._(_root);
|
||||
@override late final _TranslationsSuccessOrderEs order = _TranslationsSuccessOrderEs._(_root);
|
||||
@override late final _TranslationsSuccessProfileEs profile = _TranslationsSuccessProfileEs._(_root);
|
||||
}
|
||||
|
||||
// Path: staff_authentication.get_started_page
|
||||
class _TranslationsStaffAuthenticationGetStartedPageEs implements TranslationsStaffAuthenticationGetStartedPageEn {
|
||||
_TranslationsStaffAuthenticationGetStartedPageEs._(this._root);
|
||||
@@ -1080,6 +1109,119 @@ class _TranslationsStaffTimeCardStatusEs implements TranslationsStaffTimeCardSta
|
||||
@override String get pending => 'Pendiente';
|
||||
}
|
||||
|
||||
// Path: errors.auth
|
||||
class _TranslationsErrorsAuthEs implements TranslationsErrorsAuthEn {
|
||||
_TranslationsErrorsAuthEs._(this._root);
|
||||
|
||||
final TranslationsEs _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
@override String get invalid_credentials => 'El correo electrónico o la contraseña que ingresaste es incorrecta.';
|
||||
@override String get account_exists => 'Ya existe una cuenta con este correo electrónico. Intenta iniciar sesión.';
|
||||
@override String get session_expired => 'Tu sesión ha expirado. Por favor, inicia sesión de nuevo.';
|
||||
@override String get user_not_found => 'No pudimos encontrar tu cuenta. Por favor, verifica tu correo electrónico e intenta de nuevo.';
|
||||
@override String get unauthorized_app => 'Esta cuenta no está autorizada para esta aplicación.';
|
||||
@override String get weak_password => 'Por favor, elige una contraseña más segura con al menos 8 caracteres.';
|
||||
@override String get sign_up_failed => 'No pudimos crear tu cuenta. Por favor, intenta de nuevo.';
|
||||
@override String get sign_in_failed => 'No pudimos iniciar sesión. Por favor, intenta de nuevo.';
|
||||
@override String get not_authenticated => 'Por favor, inicia sesión para continuar.';
|
||||
@override String get password_mismatch => 'Este correo ya está registrado. Por favor, usa la contraseña correcta o toca \'Olvidé mi contraseña\' para restablecerla.';
|
||||
@override String get google_only_account => 'Este correo está registrado con Google. Por favor, usa \'Olvidé mi contraseña\' para establecer una contraseña, luego intenta registrarte de nuevo con la misma información.';
|
||||
}
|
||||
|
||||
// Path: errors.hub
|
||||
class _TranslationsErrorsHubEs implements TranslationsErrorsHubEn {
|
||||
_TranslationsErrorsHubEs._(this._root);
|
||||
|
||||
final TranslationsEs _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
@override String get has_orders => 'Este hub tiene órdenes activas y no puede ser eliminado.';
|
||||
@override String get not_found => 'El hub que buscas no existe.';
|
||||
@override String get creation_failed => 'No pudimos crear el hub. Por favor, intenta de nuevo.';
|
||||
}
|
||||
|
||||
// Path: errors.order
|
||||
class _TranslationsErrorsOrderEs implements TranslationsErrorsOrderEn {
|
||||
_TranslationsErrorsOrderEs._(this._root);
|
||||
|
||||
final TranslationsEs _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
@override String get missing_hub => 'Por favor, selecciona una ubicación para tu orden.';
|
||||
@override String get missing_vendor => 'Por favor, selecciona un proveedor para tu orden.';
|
||||
@override String get creation_failed => 'No pudimos crear tu orden. Por favor, intenta de nuevo.';
|
||||
@override String get shift_creation_failed => 'No pudimos programar el turno. Por favor, intenta de nuevo.';
|
||||
@override String get missing_business => 'No se pudo cargar tu perfil de empresa. Por favor, inicia sesión de nuevo.';
|
||||
}
|
||||
|
||||
// Path: errors.profile
|
||||
class _TranslationsErrorsProfileEs implements TranslationsErrorsProfileEn {
|
||||
_TranslationsErrorsProfileEs._(this._root);
|
||||
|
||||
final TranslationsEs _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
@override String get staff_not_found => 'No se pudo cargar tu perfil. Por favor, inicia sesión de nuevo.';
|
||||
@override String get business_not_found => 'No se pudo cargar tu perfil de empresa. Por favor, inicia sesión de nuevo.';
|
||||
@override String get update_failed => 'No pudimos actualizar tu perfil. Por favor, intenta de nuevo.';
|
||||
}
|
||||
|
||||
// Path: errors.shift
|
||||
class _TranslationsErrorsShiftEs implements TranslationsErrorsShiftEn {
|
||||
_TranslationsErrorsShiftEs._(this._root);
|
||||
|
||||
final TranslationsEs _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
@override String get no_open_roles => 'No hay posiciones abiertas disponibles para este turno.';
|
||||
@override String get application_not_found => 'No se pudo encontrar tu solicitud.';
|
||||
@override String get no_active_shift => 'No tienes un turno activo para registrar salida.';
|
||||
}
|
||||
|
||||
// Path: errors.generic
|
||||
class _TranslationsErrorsGenericEs implements TranslationsErrorsGenericEn {
|
||||
_TranslationsErrorsGenericEs._(this._root);
|
||||
|
||||
final TranslationsEs _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
@override String get unknown => 'Algo salió mal. Por favor, intenta de nuevo.';
|
||||
@override String get no_connection => 'Sin conexión a internet. Por favor, verifica tu red e intenta de nuevo.';
|
||||
}
|
||||
|
||||
// Path: success.hub
|
||||
class _TranslationsSuccessHubEs implements TranslationsSuccessHubEn {
|
||||
_TranslationsSuccessHubEs._(this._root);
|
||||
|
||||
final TranslationsEs _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
@override String get created => '¡Hub creado exitosamente!';
|
||||
@override String get deleted => '¡Hub eliminado exitosamente!';
|
||||
@override String get nfc_assigned => '¡Etiqueta NFC asignada exitosamente!';
|
||||
}
|
||||
|
||||
// Path: success.order
|
||||
class _TranslationsSuccessOrderEs implements TranslationsSuccessOrderEn {
|
||||
_TranslationsSuccessOrderEs._(this._root);
|
||||
|
||||
final TranslationsEs _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
@override String get created => '¡Orden creada exitosamente!';
|
||||
}
|
||||
|
||||
// Path: success.profile
|
||||
class _TranslationsSuccessProfileEs implements TranslationsSuccessProfileEn {
|
||||
_TranslationsSuccessProfileEs._(this._root);
|
||||
|
||||
final TranslationsEs _root; // ignore: unused_field
|
||||
|
||||
// Translations
|
||||
@override String get updated => '¡Perfil actualizado exitosamente!';
|
||||
}
|
||||
|
||||
// Path: staff_authentication.profile_setup_page.steps
|
||||
class _TranslationsStaffAuthenticationProfileSetupPageStepsEs implements TranslationsStaffAuthenticationProfileSetupPageStepsEn {
|
||||
_TranslationsStaffAuthenticationProfileSetupPageStepsEs._(this._root);
|
||||
@@ -2153,6 +2295,38 @@ extension on TranslationsEs {
|
||||
'staff_time_card.status.disputed' => 'Disputado',
|
||||
'staff_time_card.status.paid' => 'Pagado',
|
||||
'staff_time_card.status.pending' => 'Pendiente',
|
||||
'errors.auth.invalid_credentials' => 'El correo electrónico o la contraseña que ingresaste es incorrecta.',
|
||||
'errors.auth.account_exists' => 'Ya existe una cuenta con este correo electrónico. Intenta iniciar sesión.',
|
||||
'errors.auth.session_expired' => 'Tu sesión ha expirado. Por favor, inicia sesión de nuevo.',
|
||||
'errors.auth.user_not_found' => 'No pudimos encontrar tu cuenta. Por favor, verifica tu correo electrónico e intenta de nuevo.',
|
||||
'errors.auth.unauthorized_app' => 'Esta cuenta no está autorizada para esta aplicación.',
|
||||
'errors.auth.weak_password' => 'Por favor, elige una contraseña más segura con al menos 8 caracteres.',
|
||||
'errors.auth.sign_up_failed' => 'No pudimos crear tu cuenta. Por favor, intenta de nuevo.',
|
||||
'errors.auth.sign_in_failed' => 'No pudimos iniciar sesión. Por favor, intenta de nuevo.',
|
||||
'errors.auth.not_authenticated' => 'Por favor, inicia sesión para continuar.',
|
||||
'errors.auth.password_mismatch' => 'Este correo ya está registrado. Por favor, usa la contraseña correcta o toca \'Olvidé mi contraseña\' para restablecerla.',
|
||||
'errors.auth.google_only_account' => 'Este correo está registrado con Google. Por favor, usa \'Olvidé mi contraseña\' para establecer una contraseña, luego intenta registrarte de nuevo con la misma información.',
|
||||
'errors.hub.has_orders' => 'Este hub tiene órdenes activas y no puede ser eliminado.',
|
||||
'errors.hub.not_found' => 'El hub que buscas no existe.',
|
||||
'errors.hub.creation_failed' => 'No pudimos crear el hub. Por favor, intenta de nuevo.',
|
||||
'errors.order.missing_hub' => 'Por favor, selecciona una ubicación para tu orden.',
|
||||
'errors.order.missing_vendor' => 'Por favor, selecciona un proveedor para tu orden.',
|
||||
'errors.order.creation_failed' => 'No pudimos crear tu orden. Por favor, intenta de nuevo.',
|
||||
'errors.order.shift_creation_failed' => 'No pudimos programar el turno. Por favor, intenta de nuevo.',
|
||||
'errors.order.missing_business' => 'No se pudo cargar tu perfil de empresa. Por favor, inicia sesión de nuevo.',
|
||||
'errors.profile.staff_not_found' => 'No se pudo cargar tu perfil. Por favor, inicia sesión de nuevo.',
|
||||
'errors.profile.business_not_found' => 'No se pudo cargar tu perfil de empresa. Por favor, inicia sesión de nuevo.',
|
||||
'errors.profile.update_failed' => 'No pudimos actualizar tu perfil. Por favor, intenta de nuevo.',
|
||||
'errors.shift.no_open_roles' => 'No hay posiciones abiertas disponibles para este turno.',
|
||||
'errors.shift.application_not_found' => 'No se pudo encontrar tu solicitud.',
|
||||
'errors.shift.no_active_shift' => 'No tienes un turno activo para registrar salida.',
|
||||
'errors.generic.unknown' => 'Algo salió mal. Por favor, intenta de nuevo.',
|
||||
'errors.generic.no_connection' => 'Sin conexión a internet. Por favor, verifica tu red e intenta de nuevo.',
|
||||
'success.hub.created' => '¡Hub creado exitosamente!',
|
||||
'success.hub.deleted' => '¡Hub eliminado exitosamente!',
|
||||
'success.hub.nfc_assigned' => '¡Etiqueta NFC asignada exitosamente!',
|
||||
'success.order.created' => '¡Orden creada exitosamente!',
|
||||
'success.profile.updated' => '¡Perfil actualizado exitosamente!',
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import '../l10n/strings.g.dart';
|
||||
|
||||
/// Translates error message keys to localized strings.
|
||||
///
|
||||
/// This utility function takes a dot-notation key like 'errors.auth.account_exists'
|
||||
/// and returns the corresponding localized string from the translation system.
|
||||
///
|
||||
/// If the key is not found or doesn't match the expected format, the original
|
||||
/// key is returned as a fallback.
|
||||
///
|
||||
/// Example:
|
||||
/// ```dart
|
||||
/// final message = translateErrorKey('errors.auth.account_exists');
|
||||
/// // Returns: "An account with this email already exists. Try signing in instead."
|
||||
/// ```
|
||||
String translateErrorKey(String key) {
|
||||
final List<String> parts = key.split('.');
|
||||
|
||||
// Expected format: errors.{category}.{error_type}
|
||||
if (parts.length != 3 || parts[0] != 'errors') {
|
||||
return key;
|
||||
}
|
||||
|
||||
final String category = parts[1];
|
||||
final String errorType = parts[2];
|
||||
|
||||
switch (category) {
|
||||
case 'auth':
|
||||
return _translateAuthError(errorType);
|
||||
case 'hub':
|
||||
return _translateHubError(errorType);
|
||||
case 'order':
|
||||
return _translateOrderError(errorType);
|
||||
case 'profile':
|
||||
return _translateProfileError(errorType);
|
||||
case 'shift':
|
||||
return _translateShiftError(errorType);
|
||||
case 'generic':
|
||||
return _translateGenericError(errorType);
|
||||
default:
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
String _translateAuthError(String errorType) {
|
||||
switch (errorType) {
|
||||
case 'invalid_credentials':
|
||||
return t.errors.auth.invalid_credentials;
|
||||
case 'account_exists':
|
||||
return t.errors.auth.account_exists;
|
||||
case 'session_expired':
|
||||
return t.errors.auth.session_expired;
|
||||
case 'user_not_found':
|
||||
return t.errors.auth.user_not_found;
|
||||
case 'unauthorized_app':
|
||||
return t.errors.auth.unauthorized_app;
|
||||
case 'weak_password':
|
||||
return t.errors.auth.weak_password;
|
||||
case 'sign_up_failed':
|
||||
return t.errors.auth.sign_up_failed;
|
||||
case 'sign_in_failed':
|
||||
return t.errors.auth.sign_in_failed;
|
||||
case 'not_authenticated':
|
||||
return t.errors.auth.not_authenticated;
|
||||
case 'password_mismatch':
|
||||
return t.errors.auth.password_mismatch;
|
||||
case 'google_only_account':
|
||||
return t.errors.auth.google_only_account;
|
||||
default:
|
||||
return t.errors.generic.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
String _translateHubError(String errorType) {
|
||||
switch (errorType) {
|
||||
case 'has_orders':
|
||||
return t.errors.hub.has_orders;
|
||||
case 'not_found':
|
||||
return t.errors.hub.not_found;
|
||||
case 'creation_failed':
|
||||
return t.errors.hub.creation_failed;
|
||||
default:
|
||||
return t.errors.generic.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
String _translateOrderError(String errorType) {
|
||||
switch (errorType) {
|
||||
case 'missing_hub':
|
||||
return t.errors.order.missing_hub;
|
||||
case 'missing_vendor':
|
||||
return t.errors.order.missing_vendor;
|
||||
case 'creation_failed':
|
||||
return t.errors.order.creation_failed;
|
||||
case 'shift_creation_failed':
|
||||
return t.errors.order.shift_creation_failed;
|
||||
case 'missing_business':
|
||||
return t.errors.order.missing_business;
|
||||
default:
|
||||
return t.errors.generic.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
String _translateProfileError(String errorType) {
|
||||
switch (errorType) {
|
||||
case 'staff_not_found':
|
||||
return t.errors.profile.staff_not_found;
|
||||
case 'business_not_found':
|
||||
return t.errors.profile.business_not_found;
|
||||
case 'update_failed':
|
||||
return t.errors.profile.update_failed;
|
||||
default:
|
||||
return t.errors.generic.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
String _translateShiftError(String errorType) {
|
||||
switch (errorType) {
|
||||
case 'no_open_roles':
|
||||
return t.errors.shift.no_open_roles;
|
||||
case 'application_not_found':
|
||||
return t.errors.shift.application_not_found;
|
||||
case 'no_active_shift':
|
||||
return t.errors.shift.no_active_shift;
|
||||
default:
|
||||
return t.errors.generic.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
String _translateGenericError(String errorType) {
|
||||
switch (errorType) {
|
||||
case 'unknown':
|
||||
return t.errors.generic.unknown;
|
||||
case 'no_connection':
|
||||
return t.errors.generic.no_connection;
|
||||
default:
|
||||
return t.errors.generic.unknown;
|
||||
}
|
||||
}
|
||||
@@ -94,3 +94,6 @@ export 'src/entities/profile/experience_skill.dart';
|
||||
export 'src/adapters/profile/bank_account_adapter.dart';
|
||||
export 'src/adapters/profile/tax_form_adapter.dart';
|
||||
export 'src/adapters/financial/payment_adapter.dart';
|
||||
|
||||
// Exceptions
|
||||
export 'src/exceptions/app_exception.dart';
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
/// Base sealed class for all application exceptions.
|
||||
///
|
||||
/// Provides type-safe error handling with user-friendly message keys.
|
||||
/// Technical details are captured for logging but never shown to users.
|
||||
sealed class AppException implements Exception {
|
||||
const AppException({
|
||||
required this.code,
|
||||
this.technicalMessage,
|
||||
});
|
||||
|
||||
/// Unique error code for logging/tracking (e.g., "AUTH_001")
|
||||
final String code;
|
||||
|
||||
/// Technical details for developers (never shown to users)
|
||||
final String? technicalMessage;
|
||||
|
||||
/// Returns the localization key for user-friendly message
|
||||
String get messageKey;
|
||||
|
||||
@override
|
||||
String toString() => 'AppException($code): $technicalMessage';
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// AUTH EXCEPTIONS
|
||||
// ============================================================
|
||||
|
||||
/// Base class for authentication-related exceptions.
|
||||
sealed class AuthException extends AppException {
|
||||
const AuthException({required super.code, super.technicalMessage});
|
||||
}
|
||||
|
||||
/// Thrown when email/password combination is incorrect.
|
||||
class InvalidCredentialsException extends AuthException {
|
||||
const InvalidCredentialsException({String? technicalMessage})
|
||||
: super(code: 'AUTH_001', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.auth.invalid_credentials';
|
||||
}
|
||||
|
||||
/// Thrown when attempting to register with an email that already exists.
|
||||
class AccountExistsException extends AuthException {
|
||||
const AccountExistsException({String? technicalMessage})
|
||||
: super(code: 'AUTH_002', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.auth.account_exists';
|
||||
}
|
||||
|
||||
/// Thrown when the user session has expired.
|
||||
class SessionExpiredException extends AuthException {
|
||||
const SessionExpiredException({String? technicalMessage})
|
||||
: super(code: 'AUTH_003', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.auth.session_expired';
|
||||
}
|
||||
|
||||
/// Thrown when user profile is not found in database after Firebase auth.
|
||||
class UserNotFoundException extends AuthException {
|
||||
const UserNotFoundException({String? technicalMessage})
|
||||
: super(code: 'AUTH_004', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.auth.user_not_found';
|
||||
}
|
||||
|
||||
/// Thrown when user is not authorized for the current app (wrong role).
|
||||
class UnauthorizedAppException extends AuthException {
|
||||
const UnauthorizedAppException({String? technicalMessage})
|
||||
: super(code: 'AUTH_005', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.auth.unauthorized_app';
|
||||
}
|
||||
|
||||
/// Thrown when password doesn't meet security requirements.
|
||||
class WeakPasswordException extends AuthException {
|
||||
const WeakPasswordException({String? technicalMessage})
|
||||
: super(code: 'AUTH_006', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.auth.weak_password';
|
||||
}
|
||||
|
||||
/// Thrown when sign-up process fails.
|
||||
class SignUpFailedException extends AuthException {
|
||||
const SignUpFailedException({String? technicalMessage})
|
||||
: super(code: 'AUTH_007', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.auth.sign_up_failed';
|
||||
}
|
||||
|
||||
/// Thrown when sign-in process fails.
|
||||
class SignInFailedException extends AuthException {
|
||||
const SignInFailedException({String? technicalMessage})
|
||||
: super(code: 'AUTH_008', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.auth.sign_in_failed';
|
||||
}
|
||||
|
||||
/// Thrown when email exists but password doesn't match.
|
||||
class PasswordMismatchException extends AuthException {
|
||||
const PasswordMismatchException({String? technicalMessage})
|
||||
: super(code: 'AUTH_009', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.auth.password_mismatch';
|
||||
}
|
||||
|
||||
/// Thrown when account exists only with Google provider (no password).
|
||||
class GoogleOnlyAccountException extends AuthException {
|
||||
const GoogleOnlyAccountException({String? technicalMessage})
|
||||
: super(code: 'AUTH_010', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.auth.google_only_account';
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HUB EXCEPTIONS
|
||||
// ============================================================
|
||||
|
||||
/// Base class for hub-related exceptions.
|
||||
sealed class HubException extends AppException {
|
||||
const HubException({required super.code, super.technicalMessage});
|
||||
}
|
||||
|
||||
/// Thrown when attempting to delete a hub that has active orders.
|
||||
class HubHasOrdersException extends HubException {
|
||||
const HubHasOrdersException({String? technicalMessage})
|
||||
: super(code: 'HUB_001', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.hub.has_orders';
|
||||
}
|
||||
|
||||
/// Thrown when hub is not found.
|
||||
class HubNotFoundException extends HubException {
|
||||
const HubNotFoundException({String? technicalMessage})
|
||||
: super(code: 'HUB_002', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.hub.not_found';
|
||||
}
|
||||
|
||||
/// Thrown when hub creation fails.
|
||||
class HubCreationFailedException extends HubException {
|
||||
const HubCreationFailedException({String? technicalMessage})
|
||||
: super(code: 'HUB_003', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.hub.creation_failed';
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// ORDER EXCEPTIONS
|
||||
// ============================================================
|
||||
|
||||
/// Base class for order-related exceptions.
|
||||
sealed class OrderException extends AppException {
|
||||
const OrderException({required super.code, super.technicalMessage});
|
||||
}
|
||||
|
||||
/// Thrown when order creation is attempted without a hub.
|
||||
class OrderMissingHubException extends OrderException {
|
||||
const OrderMissingHubException({String? technicalMessage})
|
||||
: super(code: 'ORDER_001', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.order.missing_hub';
|
||||
}
|
||||
|
||||
/// Thrown when order creation is attempted without a vendor.
|
||||
class OrderMissingVendorException extends OrderException {
|
||||
const OrderMissingVendorException({String? technicalMessage})
|
||||
: super(code: 'ORDER_002', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.order.missing_vendor';
|
||||
}
|
||||
|
||||
/// Thrown when order creation fails.
|
||||
class OrderCreationFailedException extends OrderException {
|
||||
const OrderCreationFailedException({String? technicalMessage})
|
||||
: super(code: 'ORDER_003', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.order.creation_failed';
|
||||
}
|
||||
|
||||
/// Thrown when shift creation fails.
|
||||
class ShiftCreationFailedException extends OrderException {
|
||||
const ShiftCreationFailedException({String? technicalMessage})
|
||||
: super(code: 'ORDER_004', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.order.shift_creation_failed';
|
||||
}
|
||||
|
||||
/// Thrown when order is missing required business context.
|
||||
class OrderMissingBusinessException extends OrderException {
|
||||
const OrderMissingBusinessException({String? technicalMessage})
|
||||
: super(code: 'ORDER_005', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.order.missing_business';
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// PROFILE EXCEPTIONS
|
||||
// ============================================================
|
||||
|
||||
/// Base class for profile-related exceptions.
|
||||
sealed class ProfileException extends AppException {
|
||||
const ProfileException({required super.code, super.technicalMessage});
|
||||
}
|
||||
|
||||
/// Thrown when staff profile is not found.
|
||||
class StaffProfileNotFoundException extends ProfileException {
|
||||
const StaffProfileNotFoundException({String? technicalMessage})
|
||||
: super(code: 'PROFILE_001', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.profile.staff_not_found';
|
||||
}
|
||||
|
||||
/// Thrown when business profile is not found.
|
||||
class BusinessNotFoundException extends ProfileException {
|
||||
const BusinessNotFoundException({String? technicalMessage})
|
||||
: super(code: 'PROFILE_002', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.profile.business_not_found';
|
||||
}
|
||||
|
||||
/// Thrown when profile update fails.
|
||||
class ProfileUpdateFailedException extends ProfileException {
|
||||
const ProfileUpdateFailedException({String? technicalMessage})
|
||||
: super(code: 'PROFILE_003', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.profile.update_failed';
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// SHIFT EXCEPTIONS
|
||||
// ============================================================
|
||||
|
||||
/// Base class for shift-related exceptions.
|
||||
sealed class ShiftException extends AppException {
|
||||
const ShiftException({required super.code, super.technicalMessage});
|
||||
}
|
||||
|
||||
/// Thrown when no open roles are available for a shift.
|
||||
class NoOpenRolesException extends ShiftException {
|
||||
const NoOpenRolesException({String? technicalMessage})
|
||||
: super(code: 'SHIFT_001', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.shift.no_open_roles';
|
||||
}
|
||||
|
||||
/// Thrown when application for shift is not found.
|
||||
class ApplicationNotFoundException extends ShiftException {
|
||||
const ApplicationNotFoundException({String? technicalMessage})
|
||||
: super(code: 'SHIFT_002', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.shift.application_not_found';
|
||||
}
|
||||
|
||||
/// Thrown when no active shift is found for clock out.
|
||||
class NoActiveShiftException extends ShiftException {
|
||||
const NoActiveShiftException({String? technicalMessage})
|
||||
: super(code: 'SHIFT_003', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.shift.no_active_shift';
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// NETWORK/GENERIC EXCEPTIONS
|
||||
// ============================================================
|
||||
|
||||
/// Thrown when there is no network connection.
|
||||
class NetworkException extends AppException {
|
||||
const NetworkException({String? technicalMessage})
|
||||
: super(code: 'NET_001', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.generic.no_connection';
|
||||
}
|
||||
|
||||
/// Thrown when an unexpected error occurs.
|
||||
class UnknownException extends AppException {
|
||||
const UnknownException({String? technicalMessage})
|
||||
: super(code: 'UNKNOWN', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.generic.unknown';
|
||||
}
|
||||
|
||||
/// Thrown when user is not authenticated.
|
||||
class NotAuthenticatedException extends AppException {
|
||||
const NotAuthenticatedException({String? technicalMessage})
|
||||
: super(code: 'AUTH_NOT_LOGGED', technicalMessage: technicalMessage);
|
||||
|
||||
@override
|
||||
String get messageKey => 'errors.auth.not_authenticated';
|
||||
}
|
||||
@@ -1,7 +1,20 @@
|
||||
import 'dart:developer' as developer;
|
||||
|
||||
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
||||
import 'package:firebase_data_connect/firebase_data_connect.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
import 'package:krow_domain/krow_domain.dart' as domain;
|
||||
import 'package:krow_domain/krow_domain.dart'
|
||||
show
|
||||
InvalidCredentialsException,
|
||||
SignInFailedException,
|
||||
SignUpFailedException,
|
||||
WeakPasswordException,
|
||||
AccountExistsException,
|
||||
UserNotFoundException,
|
||||
UnauthorizedAppException,
|
||||
PasswordMismatchException,
|
||||
GoogleOnlyAccountException;
|
||||
|
||||
import '../../domain/repositories/auth_repository_interface.dart';
|
||||
|
||||
@@ -33,7 +46,9 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
|
||||
final firebase.User? firebaseUser = credential.user;
|
||||
if (firebaseUser == null) {
|
||||
throw Exception('Sign-in failed, no Firebase user received.');
|
||||
throw const SignInFailedException(
|
||||
technicalMessage: 'No Firebase user received after sign-in',
|
||||
);
|
||||
}
|
||||
|
||||
return _getUserProfile(
|
||||
@@ -44,12 +59,20 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
|
||||
} on firebase.FirebaseAuthException catch (e) {
|
||||
if (e.code == 'invalid-credential' || e.code == 'wrong-password') {
|
||||
throw Exception('Incorrect email or password.');
|
||||
throw InvalidCredentialsException(
|
||||
technicalMessage: 'Firebase error code: ${e.code}',
|
||||
);
|
||||
} else {
|
||||
throw Exception('Authentication error: ${e.message}');
|
||||
throw SignInFailedException(
|
||||
technicalMessage: 'Firebase auth error: ${e.message}',
|
||||
);
|
||||
}
|
||||
} on domain.AppException {
|
||||
rethrow;
|
||||
} catch (e) {
|
||||
throw Exception('Failed to sign in and fetch user data: ${e.toString()}');
|
||||
throw SignInFailedException(
|
||||
technicalMessage: 'Unexpected error: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,63 +82,225 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
required String email,
|
||||
required String password,
|
||||
}) async {
|
||||
firebase.User? firebaseUser;
|
||||
String? createdBusinessId;
|
||||
|
||||
try {
|
||||
// Step 1: Try to create Firebase Auth user
|
||||
final firebase.UserCredential credential = await _firebaseAuth.createUserWithEmailAndPassword(
|
||||
email: email,
|
||||
password: password,
|
||||
);
|
||||
|
||||
firebaseUser = credential.user;
|
||||
if (firebaseUser == null) {
|
||||
throw const SignUpFailedException(
|
||||
technicalMessage: 'Firebase user could not be created',
|
||||
);
|
||||
}
|
||||
|
||||
// New user created successfully, proceed to create PostgreSQL entities
|
||||
return await _createBusinessAndUser(
|
||||
firebaseUser: firebaseUser,
|
||||
companyName: companyName,
|
||||
email: email,
|
||||
onBusinessCreated: (String businessId) => createdBusinessId = businessId,
|
||||
);
|
||||
|
||||
} on firebase.FirebaseAuthException catch (e) {
|
||||
if (e.code == 'weak-password') {
|
||||
throw WeakPasswordException(
|
||||
technicalMessage: 'Firebase: ${e.message}',
|
||||
);
|
||||
} else if (e.code == 'email-already-in-use') {
|
||||
// Email exists in Firebase Auth - try to sign in and complete registration
|
||||
return await _handleExistingFirebaseAccount(
|
||||
email: email,
|
||||
password: password,
|
||||
companyName: companyName,
|
||||
);
|
||||
} else {
|
||||
throw SignUpFailedException(
|
||||
technicalMessage: 'Firebase auth error: ${e.message}',
|
||||
);
|
||||
}
|
||||
} on domain.AppException {
|
||||
// Rollback for our known exceptions
|
||||
await _rollbackSignUp(firebaseUser: firebaseUser, businessId: createdBusinessId);
|
||||
rethrow;
|
||||
} catch (e) {
|
||||
// Rollback: Clean up any partially created resources
|
||||
await _rollbackSignUp(firebaseUser: firebaseUser, businessId: createdBusinessId);
|
||||
throw SignUpFailedException(
|
||||
technicalMessage: 'Unexpected error: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles the case where email already exists in Firebase Auth.
|
||||
///
|
||||
/// This can happen when:
|
||||
/// 1. User signed up with Google in another app sharing the same Firebase project
|
||||
/// 2. User already has a KROW account
|
||||
///
|
||||
/// The flow:
|
||||
/// 1. Try to sign in with provided password
|
||||
/// 2. If sign-in succeeds, check if BUSINESS user exists in PostgreSQL
|
||||
/// 3. If not, create Business + User (user is new to KROW)
|
||||
/// 4. If yes, they already have a KROW account
|
||||
Future<domain.User> _handleExistingFirebaseAccount({
|
||||
required String email,
|
||||
required String password,
|
||||
required String companyName,
|
||||
}) async {
|
||||
developer.log('Email exists in Firebase, attempting sign-in: $email', name: 'AuthRepository');
|
||||
|
||||
try {
|
||||
// Try to sign in with the provided password
|
||||
final firebase.UserCredential credential = await _firebaseAuth.signInWithEmailAndPassword(
|
||||
email: email,
|
||||
password: password,
|
||||
);
|
||||
|
||||
final firebase.User? firebaseUser = credential.user;
|
||||
if (firebaseUser == null) {
|
||||
throw Exception('Sign-up failed, Firebase user could not be created.');
|
||||
throw const SignUpFailedException(
|
||||
technicalMessage: 'Sign-in succeeded but no user returned',
|
||||
);
|
||||
}
|
||||
|
||||
// Client-specific business logic:
|
||||
// 1. Create a `Business` entity.
|
||||
// 2. Create a `User` entity associated with the business.
|
||||
final OperationResult<dc.CreateBusinessData, dc.CreateBusinessVariables> createBusinessResponse = await _dataConnect.createBusiness(
|
||||
businessName: companyName,
|
||||
userId: firebaseUser.uid,
|
||||
rateGroup: dc.BusinessRateGroup.STANDARD,
|
||||
status: dc.BusinessStatus.PENDING,
|
||||
).execute();
|
||||
// Sign-in succeeded! Check if user already has a BUSINESS account in PostgreSQL
|
||||
final bool hasBusinessAccount = await _checkBusinessUserExists(firebaseUser.uid);
|
||||
|
||||
final dc.CreateBusinessBusinessInsert? businessData = createBusinessResponse.data?.business_insert;
|
||||
if (businessData == null) {
|
||||
await firebaseUser.delete(); // Rollback if business creation fails
|
||||
throw Exception('Business creation failed after Firebase user registration.');
|
||||
if (hasBusinessAccount) {
|
||||
// User already has a KROW Client account
|
||||
developer.log('User already has BUSINESS account: ${firebaseUser.uid}', name: 'AuthRepository');
|
||||
throw AccountExistsException(
|
||||
technicalMessage: 'User ${firebaseUser.uid} already has BUSINESS role',
|
||||
);
|
||||
}
|
||||
|
||||
final OperationResult<dc.CreateUserData, dc.CreateUserVariables> createUserResponse = await _dataConnect.createUser(
|
||||
id: firebaseUser.uid,
|
||||
role: dc.UserBaseRole.USER,
|
||||
)
|
||||
.email(email)
|
||||
.userRole('BUSINESS')
|
||||
.execute();
|
||||
|
||||
final dc.CreateUserUserInsert? newUserData = createUserResponse.data?.user_insert;
|
||||
if (newUserData == null) {
|
||||
await firebaseUser.delete(); // Rollback if user profile creation fails
|
||||
// TO-DO: Also delete the created Business if this fails
|
||||
throw Exception('User profile creation failed after Firebase user registration.');
|
||||
}
|
||||
|
||||
return _getUserProfile(
|
||||
firebaseUserId: firebaseUser.uid,
|
||||
fallbackEmail: firebaseUser.email ?? email,
|
||||
// User exists in Firebase but not in KROW PostgreSQL - create the entities
|
||||
developer.log('Creating BUSINESS account for existing Firebase user: ${firebaseUser.uid}', name: 'AuthRepository');
|
||||
return await _createBusinessAndUser(
|
||||
firebaseUser: firebaseUser,
|
||||
companyName: companyName,
|
||||
email: email,
|
||||
onBusinessCreated: (_) {}, // No rollback needed for existing Firebase user
|
||||
);
|
||||
|
||||
} on firebase.FirebaseAuthException catch (e) {
|
||||
if (e.code == 'weak-password') {
|
||||
throw Exception('The password provided is too weak.');
|
||||
} else if (e.code == 'email-already-in-use') {
|
||||
throw Exception('An account already exists for that email address.');
|
||||
// Sign-in failed - check why
|
||||
developer.log('Sign-in failed with code: ${e.code}', name: 'AuthRepository');
|
||||
|
||||
if (e.code == 'wrong-password' || e.code == 'invalid-credential') {
|
||||
// Password doesn't match - check what providers are available
|
||||
return await _handlePasswordMismatch(email);
|
||||
} else {
|
||||
throw Exception('Sign-up error: ${e.message}');
|
||||
throw SignUpFailedException(
|
||||
technicalMessage: 'Firebase sign-in error: ${e.message}',
|
||||
);
|
||||
}
|
||||
} on domain.AppException {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles the case where the password doesn't match the existing account.
|
||||
///
|
||||
/// Note: fetchSignInMethodsForEmail was deprecated by Firebase for security
|
||||
/// reasons (email enumeration). We show a combined message that covers both
|
||||
/// cases: wrong password OR account uses different sign-in method (Google).
|
||||
Future<Never> _handlePasswordMismatch(String email) async {
|
||||
// We can't distinguish between "wrong password" and "no password provider"
|
||||
// due to Firebase deprecating fetchSignInMethodsForEmail.
|
||||
// The PasswordMismatchException message covers both scenarios.
|
||||
developer.log('Password mismatch or different provider for: $email', name: 'AuthRepository');
|
||||
throw PasswordMismatchException(
|
||||
technicalMessage: 'Email $email: password mismatch or different auth provider',
|
||||
);
|
||||
}
|
||||
|
||||
/// Checks if a user with BUSINESS role exists in PostgreSQL.
|
||||
Future<bool> _checkBusinessUserExists(String firebaseUserId) async {
|
||||
try {
|
||||
final QueryResult<dc.GetUserByIdData, dc.GetUserByIdVariables> response =
|
||||
await _dataConnect.getUserById(id: firebaseUserId).execute();
|
||||
final dc.GetUserByIdUser? user = response.data?.user;
|
||||
return user != null && user.userRole == 'BUSINESS';
|
||||
} catch (e) {
|
||||
throw Exception('Failed to sign up and create user data: ${e.toString()}');
|
||||
developer.log('Error checking business user: $e', name: 'AuthRepository');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates Business and User entities in PostgreSQL for a Firebase user.
|
||||
Future<domain.User> _createBusinessAndUser({
|
||||
required firebase.User firebaseUser,
|
||||
required String companyName,
|
||||
required String email,
|
||||
required void Function(String businessId) onBusinessCreated,
|
||||
}) async {
|
||||
// Create Business entity in PostgreSQL
|
||||
final OperationResult<dc.CreateBusinessData, dc.CreateBusinessVariables> createBusinessResponse =
|
||||
await _dataConnect.createBusiness(
|
||||
businessName: companyName,
|
||||
userId: firebaseUser.uid,
|
||||
rateGroup: dc.BusinessRateGroup.STANDARD,
|
||||
status: dc.BusinessStatus.PENDING,
|
||||
).execute();
|
||||
|
||||
final dc.CreateBusinessBusinessInsert? businessData = createBusinessResponse.data?.business_insert;
|
||||
if (businessData == null) {
|
||||
throw const SignUpFailedException(
|
||||
technicalMessage: 'Business creation failed in PostgreSQL',
|
||||
);
|
||||
}
|
||||
onBusinessCreated(businessData.id);
|
||||
|
||||
// Create User entity in PostgreSQL
|
||||
final OperationResult<dc.CreateUserData, dc.CreateUserVariables> createUserResponse =
|
||||
await _dataConnect.createUser(
|
||||
id: firebaseUser.uid,
|
||||
role: dc.UserBaseRole.USER,
|
||||
)
|
||||
.email(email)
|
||||
.userRole('BUSINESS')
|
||||
.execute();
|
||||
|
||||
final dc.CreateUserUserInsert? newUserData = createUserResponse.data?.user_insert;
|
||||
if (newUserData == null) {
|
||||
throw const SignUpFailedException(
|
||||
technicalMessage: 'User profile creation failed in PostgreSQL',
|
||||
);
|
||||
}
|
||||
|
||||
return _getUserProfile(
|
||||
firebaseUserId: firebaseUser.uid,
|
||||
fallbackEmail: firebaseUser.email ?? email,
|
||||
);
|
||||
}
|
||||
|
||||
/// Rollback helper to clean up partially created resources during sign-up.
|
||||
Future<void> _rollbackSignUp({
|
||||
firebase.User? firebaseUser,
|
||||
String? businessId,
|
||||
}) async {
|
||||
// Delete business first (if created)
|
||||
if (businessId != null) {
|
||||
try {
|
||||
await _dataConnect.deleteBusiness(id: businessId).execute();
|
||||
} catch (_) {
|
||||
// Log but don't throw - we're already in error recovery
|
||||
}
|
||||
}
|
||||
// Delete Firebase user (if created)
|
||||
if (firebaseUser != null) {
|
||||
try {
|
||||
await firebaseUser.delete();
|
||||
} catch (_) {
|
||||
// Log but don't throw - we're already in error recovery
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,17 +327,23 @@ class AuthRepositoryImpl implements AuthRepositoryInterface {
|
||||
final QueryResult<dc.GetUserByIdData, dc.GetUserByIdVariables> response = await _dataConnect.getUserById(id: firebaseUserId).execute();
|
||||
final dc.GetUserByIdUser? user = response.data?.user;
|
||||
if (user == null) {
|
||||
throw Exception('Authenticated user profile not found in database.');
|
||||
throw UserNotFoundException(
|
||||
technicalMessage: 'Firebase UID $firebaseUserId not found in users table',
|
||||
);
|
||||
}
|
||||
if (requireBusinessRole && user.userRole != 'BUSINESS') {
|
||||
await _firebaseAuth.signOut();
|
||||
dc.ClientSessionStore.instance.clear();
|
||||
throw Exception('User is not authorized for this app.');
|
||||
throw UnauthorizedAppException(
|
||||
technicalMessage: 'User role is ${user.userRole}, expected BUSINESS',
|
||||
);
|
||||
}
|
||||
|
||||
final String? email = user.email ?? fallbackEmail;
|
||||
if (email == null || email.isEmpty) {
|
||||
throw Exception('User email is missing in profile data.');
|
||||
throw UserNotFoundException(
|
||||
technicalMessage: 'User email missing for UID $firebaseUserId',
|
||||
);
|
||||
}
|
||||
|
||||
final domain.User domainUser = domain.User(
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:developer' as developer;
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
@@ -56,11 +58,20 @@ class ClientAuthBloc extends Bloc<ClientAuthEvent, ClientAuthState> {
|
||||
SignInWithEmailArguments(email: event.email, password: event.password),
|
||||
);
|
||||
emit(state.copyWith(status: ClientAuthStatus.authenticated, user: user));
|
||||
} catch (e) {
|
||||
} on AppException catch (e) {
|
||||
developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientAuthBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientAuthStatus.error,
|
||||
errorMessage: e.toString(),
|
||||
errorMessage: e.messageKey,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
developer.log('Unexpected error: $e', name: 'ClientAuthBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientAuthStatus.error,
|
||||
errorMessage: 'errors.generic.unknown',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -81,11 +92,20 @@ class ClientAuthBloc extends Bloc<ClientAuthEvent, ClientAuthState> {
|
||||
),
|
||||
);
|
||||
emit(state.copyWith(status: ClientAuthStatus.authenticated, user: user));
|
||||
} catch (e) {
|
||||
} on AppException catch (e) {
|
||||
developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientAuthBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientAuthStatus.error,
|
||||
errorMessage: e.toString(),
|
||||
errorMessage: e.messageKey,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
developer.log('Unexpected error: $e', name: 'ClientAuthBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientAuthStatus.error,
|
||||
errorMessage: 'errors.generic.unknown',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -102,11 +122,20 @@ class ClientAuthBloc extends Bloc<ClientAuthEvent, ClientAuthState> {
|
||||
SignInWithSocialArguments(provider: event.provider),
|
||||
);
|
||||
emit(state.copyWith(status: ClientAuthStatus.authenticated, user: user));
|
||||
} catch (e) {
|
||||
} on AppException catch (e) {
|
||||
developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientAuthBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientAuthStatus.error,
|
||||
errorMessage: e.toString(),
|
||||
errorMessage: e.messageKey,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
developer.log('Unexpected error: $e', name: 'ClientAuthBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientAuthStatus.error,
|
||||
errorMessage: 'errors.generic.unknown',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -121,11 +150,20 @@ class ClientAuthBloc extends Bloc<ClientAuthEvent, ClientAuthState> {
|
||||
try {
|
||||
await _signOut();
|
||||
emit(state.copyWith(status: ClientAuthStatus.signedOut, user: null));
|
||||
} catch (e) {
|
||||
} on AppException catch (e) {
|
||||
developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientAuthBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientAuthStatus.error,
|
||||
errorMessage: e.toString(),
|
||||
errorMessage: e.messageKey,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
developer.log('Unexpected error: $e', name: 'ClientAuthBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientAuthStatus.error,
|
||||
errorMessage: 'errors.generic.unknown',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -45,10 +45,11 @@ class ClientSignInPage extends StatelessWidget {
|
||||
if (state.status == ClientAuthStatus.authenticated) {
|
||||
Modular.to.navigateClientHome();
|
||||
} else if (state.status == ClientAuthStatus.error) {
|
||||
final String errorMessage = state.errorMessage != null
|
||||
? translateErrorKey(state.errorMessage!)
|
||||
: t.errors.generic.unknown;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(state.errorMessage ?? 'Authentication Error'),
|
||||
),
|
||||
SnackBar(content: Text(errorMessage)),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -49,10 +49,11 @@ class ClientSignUpPage extends StatelessWidget {
|
||||
if (state.status == ClientAuthStatus.authenticated) {
|
||||
Modular.to.navigateClientHome();
|
||||
} else if (state.status == ClientAuthStatus.error) {
|
||||
final String errorMessage = state.errorMessage != null
|
||||
? translateErrorKey(state.errorMessage!)
|
||||
: t.errors.generic.unknown;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(state.errorMessage ?? 'Authentication Error'),
|
||||
),
|
||||
SnackBar(content: Text(errorMessage)),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -15,6 +15,9 @@ import 'presentation/pages/billing_page.dart';
|
||||
class BillingModule extends Module {
|
||||
@override
|
||||
void binds(Injector i) {
|
||||
// Mock repositories (TODO: Replace with real implementations)
|
||||
i.addSingleton<FinancialRepositoryMock>(FinancialRepositoryMock.new);
|
||||
|
||||
// Repositories
|
||||
i.addSingleton<BillingRepository>(
|
||||
() => BillingRepositoryImpl(
|
||||
|
||||
@@ -83,7 +83,7 @@ class _BillingViewState extends State<BillingView> {
|
||||
leading: Center(
|
||||
child: UiIconButton.secondary(
|
||||
icon: UiIcons.arrowLeft,
|
||||
onTap: () => Modular.to.pop(),
|
||||
onTap: () => Modular.to.navigate('/client-main/home/'),
|
||||
),
|
||||
),
|
||||
title: AnimatedSwitcher(
|
||||
|
||||
@@ -68,7 +68,7 @@ class _CoveragePageState extends State<CoveragePage> {
|
||||
expandedHeight: 300.0,
|
||||
backgroundColor: UiColors.primary,
|
||||
leading: IconButton(
|
||||
onPressed: () => Modular.to.pop(),
|
||||
onPressed: () => Modular.to.navigate('/client-main/home/'),
|
||||
icon: Container(
|
||||
padding: const EdgeInsets.all(UiConstants.space2),
|
||||
decoration: BoxDecoration(
|
||||
|
||||
@@ -67,7 +67,7 @@ class CoverageHeader extends StatelessWidget {
|
||||
Row(
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
onTap: () => Modular.to.pop(),
|
||||
onTap: () => Modular.to.navigate('/client-main/home/'),
|
||||
child: Container(
|
||||
width: UiConstants.space10,
|
||||
height: UiConstants.space10,
|
||||
|
||||
@@ -18,7 +18,7 @@ class PermanentOrderPage extends StatelessWidget {
|
||||
backgroundColor: UiColors.bgPrimary,
|
||||
appBar: UiAppBar(
|
||||
title: labels.title,
|
||||
onLeadingPressed: () => Modular.to.pop(),
|
||||
onLeadingPressed: () => Modular.to.navigate('/client/create-order/'),
|
||||
),
|
||||
body: Center(
|
||||
child: Padding(
|
||||
|
||||
@@ -18,7 +18,7 @@ class RecurringOrderPage extends StatelessWidget {
|
||||
backgroundColor: UiColors.bgPrimary,
|
||||
appBar: UiAppBar(
|
||||
title: labels.title,
|
||||
onLeadingPressed: () => Modular.to.pop(),
|
||||
onLeadingPressed: () => Modular.to.navigate('/client/create-order/'),
|
||||
),
|
||||
body: Center(
|
||||
child: Padding(
|
||||
|
||||
@@ -43,7 +43,7 @@ class CreateOrderView extends StatelessWidget {
|
||||
backgroundColor: UiColors.bgPrimary,
|
||||
appBar: UiAppBar(
|
||||
title: t.client_create_order.title,
|
||||
onLeadingPressed: () => Modular.to.pop(),
|
||||
onLeadingPressed: () => Modular.to.navigate('/client-main/home/'),
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
|
||||
@@ -50,7 +50,7 @@ class OneTimeOrderView extends StatelessWidget {
|
||||
OneTimeOrderHeader(
|
||||
title: labels.title,
|
||||
subtitle: labels.subtitle,
|
||||
onBack: () => Modular.to.pop(),
|
||||
onBack: () => Modular.to.navigate('/client/create-order/'),
|
||||
),
|
||||
Expanded(
|
||||
child: Center(
|
||||
@@ -89,7 +89,7 @@ class OneTimeOrderView extends StatelessWidget {
|
||||
OneTimeOrderHeader(
|
||||
title: labels.title,
|
||||
subtitle: labels.subtitle,
|
||||
onBack: () => Modular.to.pop(),
|
||||
onBack: () => Modular.to.navigate('/client/create-order/'),
|
||||
),
|
||||
Expanded(
|
||||
child: Stack(
|
||||
|
||||
@@ -28,7 +28,7 @@ class RapidOrderView extends StatelessWidget {
|
||||
title: labels.success_title,
|
||||
message: labels.success_message,
|
||||
buttonLabel: labels.back_to_orders,
|
||||
onDone: () => Modular.to.pop(),
|
||||
onDone: () => Modular.to.navigate('/client-main/orders/'),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class _RapidOrderFormState extends State<_RapidOrderForm> {
|
||||
subtitle: labels.subtitle,
|
||||
date: dateStr,
|
||||
time: timeStr,
|
||||
onBack: () => Modular.to.pop(),
|
||||
onBack: () => Modular.to.navigate('/client/create-order/'),
|
||||
),
|
||||
|
||||
// Content
|
||||
|
||||
@@ -5,6 +5,12 @@ import 'package:firebase_data_connect/firebase_data_connect.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
import 'package:krow_domain/krow_domain.dart' as domain;
|
||||
import 'package:krow_domain/krow_domain.dart'
|
||||
show
|
||||
HubHasOrdersException,
|
||||
HubCreationFailedException,
|
||||
BusinessNotFoundException,
|
||||
NotAuthenticatedException;
|
||||
|
||||
import '../../domain/repositories/hub_repository_interface.dart';
|
||||
import '../../util/hubs_constants.dart';
|
||||
@@ -67,7 +73,9 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
.execute();
|
||||
final String? createdId = result.data?.teamHub_insert.id;
|
||||
if (createdId == null) {
|
||||
throw Exception('Hub creation failed.');
|
||||
throw HubCreationFailedException(
|
||||
technicalMessage: 'teamHub_insert returned null for hub: $name',
|
||||
);
|
||||
}
|
||||
|
||||
final List<domain.Hub> hubs = await _fetchHubsForTeam(
|
||||
@@ -97,7 +105,9 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
await _firebaseAuth.signOut();
|
||||
throw Exception('Business is missing. Please sign in again.');
|
||||
throw const BusinessNotFoundException(
|
||||
technicalMessage: 'Business ID missing from session',
|
||||
);
|
||||
}
|
||||
|
||||
final QueryResult<
|
||||
@@ -110,7 +120,9 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
.execute();
|
||||
|
||||
if (result.data.orders.isNotEmpty) {
|
||||
throw Exception("Sorry this hub has orders, it can't be deleted.");
|
||||
throw HubHasOrdersException(
|
||||
technicalMessage: 'Hub $id has ${result.data.orders.length} orders',
|
||||
);
|
||||
}
|
||||
|
||||
await _dataConnect.deleteTeamHub(id: id).execute();
|
||||
@@ -151,7 +163,9 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
|
||||
final firebase.User? user = _firebaseAuth.currentUser;
|
||||
if (user == null) {
|
||||
throw Exception('User is not authenticated.');
|
||||
throw const NotAuthenticatedException(
|
||||
technicalMessage: 'No Firebase user in currentUser',
|
||||
);
|
||||
}
|
||||
|
||||
final QueryResult<dc.GetBusinessesByUserIdData, dc.GetBusinessesByUserIdVariables> result = await _dataConnect.getBusinessesByUserId(
|
||||
@@ -159,7 +173,9 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
).execute();
|
||||
if (result.data.businesses.isEmpty) {
|
||||
await _firebaseAuth.signOut();
|
||||
throw Exception('No business found for this user. Please sign in again.');
|
||||
throw BusinessNotFoundException(
|
||||
technicalMessage: 'No business found for user ${user.uid}',
|
||||
);
|
||||
}
|
||||
|
||||
final dc.GetBusinessesByUserIdBusinesses business = result.data.businesses.first;
|
||||
@@ -206,7 +222,9 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
final OperationResult<dc.CreateTeamData, dc.CreateTeamVariables> createTeamResult = await createTeamBuilder.execute();
|
||||
final String? teamId = createTeamResult.data?.team_insert.id;
|
||||
if (teamId == null) {
|
||||
throw Exception('Team creation failed.');
|
||||
throw HubCreationFailedException(
|
||||
technicalMessage: 'Team creation failed for business ${business.id}',
|
||||
);
|
||||
}
|
||||
|
||||
return teamId;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:developer' as developer;
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
@@ -67,11 +69,20 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
||||
try {
|
||||
final List<Hub> hubs = await _getHubsUseCase();
|
||||
emit(state.copyWith(status: ClientHubsStatus.success, hubs: hubs));
|
||||
} catch (e) {
|
||||
} on AppException catch (e) {
|
||||
developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.failure,
|
||||
errorMessage: e.toString(),
|
||||
errorMessage: e.messageKey,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
developer.log('Unexpected error: $e', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.failure,
|
||||
errorMessage: 'errors.generic.unknown',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -106,11 +117,20 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
||||
showAddHubDialog: false,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
} on AppException catch (e) {
|
||||
developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.actionFailure,
|
||||
errorMessage: e.toString(),
|
||||
errorMessage: e.messageKey,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
developer.log('Unexpected error: $e', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.actionFailure,
|
||||
errorMessage: 'errors.generic.unknown',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -131,11 +151,20 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
||||
successMessage: 'Hub deleted successfully',
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
} on AppException catch (e) {
|
||||
developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.actionFailure,
|
||||
errorMessage: e.toString(),
|
||||
errorMessage: e.messageKey,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
developer.log('Unexpected error: $e', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.actionFailure,
|
||||
errorMessage: 'errors.generic.unknown',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -159,11 +188,20 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
||||
clearHubToIdentify: true,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
} on AppException catch (e) {
|
||||
developer.log('Error ${e.code}: ${e.technicalMessage}', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.actionFailure,
|
||||
errorMessage: e.toString(),
|
||||
errorMessage: e.messageKey,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
developer.log('Unexpected error: $e', name: 'ClientHubsBloc');
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ClientHubsStatus.actionFailure,
|
||||
errorMessage: 'errors.generic.unknown',
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -175,8 +213,8 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
||||
) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
errorMessage: null,
|
||||
successMessage: null,
|
||||
clearErrorMessage: true,
|
||||
clearSuccessMessage: true,
|
||||
status:
|
||||
state.status == ClientHubsStatus.actionSuccess ||
|
||||
state.status == ClientHubsStatus.actionFailure
|
||||
|
||||
@@ -43,12 +43,18 @@ class ClientHubsState extends Equatable {
|
||||
bool? showAddHubDialog,
|
||||
Hub? hubToIdentify,
|
||||
bool clearHubToIdentify = false,
|
||||
bool clearErrorMessage = false,
|
||||
bool clearSuccessMessage = false,
|
||||
}) {
|
||||
return ClientHubsState(
|
||||
status: status ?? this.status,
|
||||
hubs: hubs ?? this.hubs,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
successMessage: successMessage ?? this.successMessage,
|
||||
errorMessage: clearErrorMessage
|
||||
? null
|
||||
: (errorMessage ?? this.errorMessage),
|
||||
successMessage: clearSuccessMessage
|
||||
? null
|
||||
: (successMessage ?? this.successMessage),
|
||||
showAddHubDialog: showAddHubDialog ?? this.showAddHubDialog,
|
||||
hubToIdentify: clearHubToIdentify
|
||||
? null
|
||||
|
||||
@@ -33,9 +33,10 @@ class ClientHubsPage extends StatelessWidget {
|
||||
},
|
||||
listener: (BuildContext context, ClientHubsState state) {
|
||||
if (state.errorMessage != null && state.errorMessage!.isNotEmpty) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(state.errorMessage!)));
|
||||
final String errorMessage = translateErrorKey(state.errorMessage!);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(errorMessage)),
|
||||
);
|
||||
BlocProvider.of<ClientHubsBloc>(
|
||||
context,
|
||||
).add(const ClientHubsMessageCleared());
|
||||
@@ -178,7 +179,7 @@ class ClientHubsPage extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
onTap: () => Modular.to.pop(),
|
||||
onTap: () => Modular.to.navigate('/client-main/home/'),
|
||||
child: Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
|
||||
@@ -83,7 +83,7 @@ class SettingsActions extends StatelessWidget {
|
||||
// Cancel button
|
||||
UiButton.secondary(
|
||||
text: t.common.cancel,
|
||||
onPressed: () => Modular.to.pop(),
|
||||
onPressed: () => Navigator.of(dialogContext).pop(),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -30,7 +30,7 @@ class SettingsProfileHeader extends StatelessWidget {
|
||||
shape: const Border(bottom: BorderSide(color: UiColors.border, width: 1)),
|
||||
leading: IconButton(
|
||||
icon: const Icon(UiIcons.chevronLeft, color: UiColors.textSecondary),
|
||||
onPressed: () => Modular.to.pop(),
|
||||
onPressed: () => Modular.to.navigate('/client-main/home/'),
|
||||
),
|
||||
flexibleSpace: FlexibleSpaceBar(
|
||||
background: Container(
|
||||
|
||||
@@ -202,21 +202,38 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
// Address
|
||||
// Location (Hub name + Address)
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const Icon(
|
||||
UiIcons.mapPin,
|
||||
size: 14,
|
||||
color: UiColors.iconSecondary,
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 2),
|
||||
child: Icon(
|
||||
UiIcons.mapPin,
|
||||
size: 14,
|
||||
color: UiColors.iconSecondary,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Expanded(
|
||||
child: Text(
|
||||
order.locationAddress,
|
||||
style: UiTypography.footnote2r.textSecondary,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
if (order.location.isNotEmpty)
|
||||
Text(
|
||||
order.location,
|
||||
style: UiTypography.footnote1b.textPrimary,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
if (order.locationAddress.isNotEmpty)
|
||||
Text(
|
||||
order.locationAddress,
|
||||
style: UiTypography.footnote2r.textSecondary,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user