initalizing the mobile apps

This commit is contained in:
Achintha Isuru
2026-01-21 15:42:51 -05:00
parent fbadd976cf
commit 4a67b2f541
578 changed files with 28462 additions and 2 deletions

View File

@@ -0,0 +1,4 @@
library core;
export 'src/domain/arguments/usecase_argument.dart';
export 'src/domain/usecases/usecase.dart';

View File

@@ -0,0 +1,12 @@
import 'package:equatable/equatable.dart';
/// Abstract base class for all use case arguments.
///
/// Use case arguments are data transfer objects (DTOs) used to pass data
/// into a use case. They must extend [Equatable] to ensure value equality.
abstract class UseCaseArgument extends Equatable {
const UseCaseArgument();
@override
List<Object?> get props => [];
}

View File

@@ -0,0 +1,25 @@
/// Abstract base class for all use cases in the application.
///
/// Use cases encapsulate application-specific business rules and orchestrate
/// the flow of data to and from the entities. They are typically invoked
/// from the presentation layer (e.g., BLoCs, ViewModels) and interact with
/// repositories from the data layer.
///
/// [Input] represents the type of data passed into the use case.
/// [Output] represents the type of data returned by the use case.
abstract class UseCase<Input, Output> {
/// Executes the use case with the given [input].
///
/// This method should contain the core business logic of the use case.
Future<Output> call(Input input);
}
/// Abstract base class for use cases that do not require any input.
///
/// [Output] represents the type of data returned by the use case.
abstract class NoInputUseCase<Output> {
/// Executes the use case.
///
/// This method should contain the core business logic of the use case.
Future<Output> call();
}

View File

@@ -0,0 +1,13 @@
name: krow_core
description: Core utilities and shared logic.
version: 0.0.1
publish_to: none
resolution: workspace
environment:
sdk: '>=3.10.0 <4.0.0'
flutter: ">=3.0.0"
dependencies:
flutter:
sdk: flutter

View File

@@ -0,0 +1,31 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.flutter-plugins-dependencies
/build/
/coverage/

View File

@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "3b62efc2a3da49882f43c372e0bc53daef7295a6"
channel: "stable"
project_type: package

View File

@@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@@ -0,0 +1,10 @@
export 'src/bloc/locale_bloc.dart';
export 'src/bloc/locale_event.dart';
export 'src/bloc/locale_state.dart';
export 'src/l10n/strings.g.dart';
export 'src/domain/repositories/locale_repository_interface.dart';
export 'src/domain/usecases/get_locale_use_case.dart';
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';

View File

@@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../domain/usecases/get_locale_use_case.dart';
import '../domain/usecases/set_locale_use_case.dart';
import '../l10n/strings.g.dart';
import 'locale_event.dart';
import 'locale_state.dart';
/// A [Bloc] that manages the application's locale state.
///
/// It coordinates the flow between user language requests and persistent storage
/// using [SetLocaleUseCase] and [GetLocaleUseCase].
class LocaleBloc extends Bloc<LocaleEvent, LocaleState> {
final GetLocaleUseCase getLocaleUseCase;
final SetLocaleUseCase setLocaleUseCase;
/// Creates a [LocaleBloc] with the required use cases.
LocaleBloc({required this.getLocaleUseCase, required this.setLocaleUseCase})
: super(LocaleState.initial()) {
on<ChangeLocale>(_onChangeLocale);
on<LoadLocale>(_onLoadLocale);
}
/// Handles the [ChangeLocale] event by saving it via the use case and emitting new state.
Future<void> _onChangeLocale(
ChangeLocale event,
Emitter<LocaleState> emit,
) async {
// 1. Update slang settings
LocaleSettings.setLocaleRaw(event.locale.languageCode);
// 2. Persist using Use Case
await setLocaleUseCase(event.locale);
// 3. Emit new state
emit(
LocaleState(
locale: event.locale,
supportedLocales: state.supportedLocales,
),
);
}
/// Handles the [LoadLocale] event by retrieving it via the use case and updating settings.
Future<void> _onLoadLocale(
LoadLocale event,
Emitter<LocaleState> emit,
) async {
final savedLocale = await getLocaleUseCase();
final locale = const Locale('es');
LocaleSettings.setLocaleRaw(locale.languageCode);
emit(LocaleState(locale: locale, supportedLocales: state.supportedLocales));
}
}

View File

@@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
/// Base class for all locale-related events.
sealed class LocaleEvent {
/// Creates a [LocaleEvent].
const LocaleEvent();
}
/// Event triggered when the user wants to change the application locale.
class ChangeLocale extends LocaleEvent {
/// The new locale to apply.
final Locale locale;
/// Creates a [ChangeLocale] event.
const ChangeLocale(this.locale);
}
/// Event triggered to load the saved locale from persistent storage.
class LoadLocale extends LocaleEvent {
/// Creates a [LoadLocale] event.
const LoadLocale();
}

View File

@@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
import '../l10n/strings.g.dart';
/// Represents the current state of the application's localization.
class LocaleState {
/// The current active locale.
final Locale locale;
/// The list of supported locales for the application.
final List<Locale> supportedLocales;
/// Creates a [LocaleState] with the specified [locale].
const LocaleState({required this.locale, required this.supportedLocales});
/// The initial state of the application, defaulting to English.
factory LocaleState.initial() => LocaleState(
locale: const Locale('es'),
supportedLocales: AppLocaleUtils.supportedLocales,
);
}

View File

@@ -0,0 +1,29 @@
import 'package:shared_preferences/shared_preferences.dart';
/// Interface for the local data source that manages locale persistence.
abstract interface class LocaleLocalDataSource {
/// Saves the language code to local storage.
Future<void> saveLanguageCode(String languageCode);
/// Retrieves the saved language code from local storage.
Future<String?> getLanguageCode();
}
/// Implementation of [LocaleLocalDataSource] using [SharedPreferencesAsync].
class LocaleLocalDataSourceImpl implements LocaleLocalDataSource {
static const String _localeKey = 'app_locale';
final SharedPreferencesAsync _sharedPreferences;
/// Creates a [LocaleLocalDataSourceImpl] with the required [SharedPreferencesAsync] instance.
LocaleLocalDataSourceImpl(this._sharedPreferences);
@override
Future<void> saveLanguageCode(String languageCode) async {
await _sharedPreferences.setString(_localeKey, languageCode);
}
@override
Future<String?> getLanguageCode() async {
return _sharedPreferences.getString(_localeKey);
}
}

View File

@@ -0,0 +1,28 @@
import 'dart:ui';
import '../../domain/repositories/locale_repository_interface.dart';
import '../datasources/locale_local_data_source.dart';
/// Implementation of [LocaleRepositoryInterface] that coordinates with a local data source.
///
/// This class handles the mapping between domain [Locale] objects and the raw
/// strings handled by the [LocaleLocalDataSource].
class LocaleRepositoryImpl implements LocaleRepositoryInterface {
final LocaleLocalDataSource _localDataSource;
/// Creates a [LocaleRepositoryImpl] with the provided [_localDataSource].
LocaleRepositoryImpl(this._localDataSource);
@override
Future<void> saveLocale(Locale locale) {
return _localDataSource.saveLanguageCode(locale.languageCode);
}
@override
Future<Locale?> getSavedLocale() async {
final languageCode = await _localDataSource.getLanguageCode();
if (languageCode != null) {
return Locale(languageCode);
}
return null;
}
}

View File

@@ -0,0 +1,17 @@
import 'dart:ui';
/// Interface for the locale repository.
///
/// This defines the contracts for persisting and retrieving the application's locale.
/// Implementations of this interface should handle the details of the storage mechanism.
abstract interface class LocaleRepositoryInterface {
/// Saves the specified [locale] to persistent storage.
///
/// Throws a [RepositoryException] if the operation fails.
Future<void> saveLocale(Locale locale);
/// Retrieves the saved [locale] from persistent storage.
///
/// Returns `null` if no locale has been previously saved.
Future<Locale?> getSavedLocale();
}

View File

@@ -0,0 +1,19 @@
import 'dart:ui';
import 'package:krow_core/core.dart';
import '../repositories/locale_repository_interface.dart';
/// Use case to retrieve the persisted application locale.
///
/// This class extends [NoInputUseCase] and interacts with [LocaleRepositoryInterface]
/// to fetch the saved locale.
class GetLocaleUseCase extends NoInputUseCase<Locale?> {
final LocaleRepositoryInterface _repository;
/// Creates a [GetLocaleUseCase] with the required [LocaleRepositoryInterface].
GetLocaleUseCase(this._repository);
@override
Future<Locale?> call() {
return _repository.getSavedLocale();
}
}

View File

@@ -0,0 +1,19 @@
import 'dart:ui';
import 'package:krow_core/core.dart';
import '../repositories/locale_repository_interface.dart';
/// Use case to save the application locale to persistent storage.
///
/// This class extends [UseCase] and interacts with [LocaleRepositoryInterface]
/// to save a given locale.
class SetLocaleUseCase extends UseCase<Locale, void> {
final LocaleRepositoryInterface _repository;
/// Creates a [SetLocaleUseCase] with the required [LocaleRepositoryInterface].
SetLocaleUseCase(this._repository);
@override
Future<void> call(Locale input) {
return _repository.saveLocale(input);
}
}

View File

@@ -0,0 +1,190 @@
{
"common": {
"ok": "OK",
"cancel": "Cancel",
"save": "Save",
"delete": "Delete",
"continue_text": "Continue"
},
"settings": {
"language": "Language",
"change_language": "Change Language"
},
"staff_authentication": {
"get_started_page": {
"title_part1": "Work, Grow, ",
"title_part2": "Elevate",
"subtitle": "Build your career in hospitality with \nflexibility and freedom.",
"sign_up_button": "Sign Up",
"log_in_button": "Log In"
},
"phone_verification_page": {
"validation_error": "Please enter a valid 10-digit phone number",
"send_code_button": "Send Code",
"enter_code_title": "Enter verification code",
"code_sent_message": "We sent a 6-digit code to ",
"code_sent_instruction": ". Enter it below to verify your account."
},
"phone_input": {
"title": "Verify your phone number",
"subtitle": "We'll send you a verification code to get started.",
"label": "Phone Number",
"hint": "Enter your number"
},
"otp_verification": {
"did_not_get_code": "Didn't get the code ?",
"resend_in": "Resend in $seconds s",
"resend_code": "Resend code"
},
"profile_setup_page": {
"step_indicator": "Step $current of $total",
"error_occurred": "An error occurred",
"complete_setup_button": "Complete Setup",
"steps": {
"basic": "Basic Info",
"location": "Location",
"experience": "Experience"
},
"basic_info": {
"title": "Let's get to know you",
"subtitle": "Tell us a bit about yourself",
"full_name_label": "Full Name *",
"full_name_hint": "John Smith",
"bio_label": "Short Bio",
"bio_hint": "Experienced hospitality professional..."
},
"location": {
"title": "Where do you want to work?",
"subtitle": "Add your preferred work locations",
"add_location_label": "Add Location *",
"add_location_hint": "City or ZIP code",
"add_button": "Add",
"max_distance": "Max Distance: $distance miles",
"min_dist_label": "5 mi",
"max_dist_label": "50 mi"
},
"experience": {
"title": "What are your skills?",
"subtitle": "Select all that apply",
"skills_label": "Skills *",
"industries_label": "Preferred Industries",
"skills": {
"food_service": "Food Service",
"bartending": "Bartending",
"warehouse": "Warehouse",
"retail": "Retail",
"events": "Events",
"customer_service": "Customer Service",
"cleaning": "Cleaning",
"security": "Security",
"driving": "Driving",
"cooking": "Cooking"
},
"industries": {
"hospitality": "Hospitality",
"food_service": "Food Service",
"warehouse": "Warehouse",
"events": "Events",
"retail": "Retail",
"healthcare": "Healthcare"
}
}
},
"common": {
"trouble_question": "Having trouble? ",
"contact_support": "Contact Support"
}
},
"client_authentication": {
"get_started_page": {
"title": "Take Control of Your\nShifts and Events",
"subtitle": "Streamline your operations with powerful tools to manage schedules, track performance, and keep your team on the same page—all in one place",
"sign_in_button": "Sign In",
"create_account_button": "Create Account"
},
"sign_in_page": {
"title": "Welcome Back",
"subtitle": "Sign in to manage your shifts and workers",
"email_label": "Email",
"email_hint": "Enter your email",
"password_label": "Password",
"password_hint": "Enter your password",
"forgot_password": "Forgot Password?",
"sign_in_button": "Sign In",
"or_divider": "or",
"social_apple": "Sign In with Apple",
"social_google": "Sign In with Google",
"no_account": "Don't have an account? ",
"sign_up_link": "Sign Up"
},
"sign_up_page": {
"title": "Create Account",
"subtitle": "Get started with Krow for your business",
"company_label": "Company Name",
"company_hint": "Enter company name",
"email_label": "Email",
"email_hint": "Enter your email",
"password_label": "Password",
"password_hint": "Create a password",
"confirm_password_label": "Confirm Password",
"confirm_password_hint": "Confirm your password",
"create_account_button": "Create Account",
"or_divider": "or",
"social_apple": "Sign Up with Apple",
"social_google": "Sign Up with Google",
"has_account": "Already have an account? ",
"sign_in_link": "Sign In"
}
},
"client_home": {
"dashboard": {
"welcome_back": "Welcome back",
"edit_mode_active": "Edit Mode Active",
"drag_instruction": "Drag to reorder, toggle visibility",
"reset": "Reset",
"metric_needed": "Needed",
"metric_filled": "Filled",
"metric_open": "Open",
"view_all": "View all",
"insight_lightbulb": "Save $amount/month",
"insight_tip": "Book 48hrs ahead for better rates"
},
"widgets": {
"actions": "Quick Actions",
"reorder": "Reorder",
"coverage": "Today's Coverage",
"spending": "Spending Insights",
"live_activity": "Live Activity"
},
"actions": {
"rapid": "RAPID",
"rapid_subtitle": "Urgent same-day",
"create_order": "Create Order",
"create_order_subtitle": "Schedule shifts"
},
"reorder": {
"title": "REORDER",
"reorder_button": "Reorder",
"per_hr": "$amount/hr"
},
"form": {
"edit_reorder": "Edit & Reorder",
"post_new": "Post a New Shift",
"review_subtitle": "Review and edit the details before posting",
"date_label": "Date *",
"date_hint": "mm/dd/yyyy",
"location_label": "Location *",
"location_hint": "Business address",
"positions_title": "Positions",
"add_position": "Add Position",
"role_label": "Role *",
"role_hint": "Select role",
"start_time": "Start Time *",
"end_time": "End Time *",
"workers_needed": "Workers Needed *",
"hourly_rate": "Hourly Rate (\\$) *",
"post_shift": "Post Shift"
}
}
}

View File

@@ -0,0 +1,189 @@
{
"common": {
"ok": "Aceptar",
"cancel": "Cancelar",
"save": "Guardar",
"delete": "Eliminar",
"continue_text": "Continuar"
},
"settings": {
"language": "Idioma",
"change_language": "Cambiar Idioma"
},
"staff_authentication": {
"get_started_page": {
"title_part1": "Trabaja, Crece, ",
"title_part2": "Elévate",
"subtitle": "Construye tu carrera en hostelería con \nflexibilidad y libertad.",
"sign_up_button": "Registrarse",
"log_in_button": "Iniciar sesión"
},
"phone_verification_page": {
"validation_error": "Por favor, ingresa un número de teléfono válido de 10 dígitos",
"send_code_button": "Enviar código",
"enter_code_title": "Ingresa el código de verificación",
"code_sent_message": "Enviamos un código de 6 dígitos a ",
"code_sent_instruction": ". Ingrésalo a continuación para verificar tu cuenta."
},
"phone_input": {
"title": "Verifica tu número de teléfono",
"subtitle": "Te enviaremos un código de verificación para comenzar.",
"label": "Número de teléfono",
"hint": "Ingresa tu número"
},
"otp_verification": {
"did_not_get_code": "¿No recibiste el código?",
"resend_in": "Reenviar en $seconds s",
"resend_code": "Reenviar código"
},
"profile_setup_page": {
"step_indicator": "Paso $current de $total",
"error_occurred": "Ocurrió un error",
"complete_setup_button": "Completar configuración",
"steps": {
"basic": "Información básica",
"location": "Ubicación",
"experience": "Experiencia"
},
"basic_info": {
"title": "Conozcámonos",
"subtitle": "Cuéntanos un poco sobre ti",
"full_name_label": "Nombre completo *",
"full_name_hint": "Juan Pérez",
"bio_label": "Biografía corta",
"bio_hint": "Profesional experimentado en hostelería..."
},
"location": {
"title": "¿Dónde quieres trabajar?",
"subtitle": "Agrega tus ubicaciones de trabajo preferidas",
"add_location_label": "Agregar ubicación *",
"add_location_hint": "Ciudad o código postal",
"add_button": "Agregar",
"max_distance": "Distancia máxima: $distance millas",
"min_dist_label": "5 mi",
"max_dist_label": "50 mi"
},
"experience": {
"title": "¿Cuáles son tus habilidades?",
"subtitle": "Selecciona todas las que correspondan",
"skills_label": "Habilidades *",
"industries_label": "Industrias preferidas",
"skills": {
"food_service": "Servicio de comida",
"bartending": "Preparación de bebidas",
"warehouse": "Almacén",
"retail": "Venta minorista",
"events": "Eventos",
"customer_service": "Servicio al cliente",
"cleaning": "Limpieza",
"security": "Seguridad",
"driving": "Conducción",
"cooking": "Cocina"
},
"industries": {
"hospitality": "Hostelería",
"food_service": "Servicio de comida",
"warehouse": "Almacén",
"events": "Eventos",
"retail": "Venta minorista",
"healthcare": "Atención médica"
}
}
},
"common": {
"trouble_question": "¿Tienes problemas? ",
"contact_support": "Contactar a soporte"
}
},
"client_authentication": {
"get_started_page": {
"title": "Toma el control de tus\nturnos y eventos",
"subtitle": "Optimiza tus operaciones con potentes herramientas para gestionar horarios, realizar un seguimiento del rendimiento y mantener a tu equipo en la misma página, todo en un solo lugar",
"sign_in_button": "Iniciar sesión",
"create_account_button": "Crear cuenta"
},
"sign_in_page": {
"title": "Bienvenido de nuevo",
"subtitle": "Inicia sesión para gestionar tus turnos y trabajadores",
"email_label": "Correo electrónico",
"email_hint": "Ingresa tu correo electrónico",
"password_label": "Contraseña",
"password_hint": "Ingresa tu contraseña",
"forgot_password": "¿Olvidaste tu contraseña?",
"sign_in_button": "Iniciar sesión",
"or_divider": "o",
"social_apple": "Iniciar sesión con Apple",
"social_google": "Iniciar sesión con Google",
"no_account": "¿No tienes una cuenta? ",
"sign_up_link": "Regístrate"
},
"sign_up_page": {
"title": "Crear cuenta",
"subtitle": "Comienza con Krow para tu negocio",
"company_label": "Nombre de la empresa",
"company_hint": "Ingresa el nombre de la empresa",
"email_label": "Correo electrónico",
"email_hint": "Ingresa tu correo electrónico",
"password_label": "Contraseña",
"password_hint": "Crea una contraseña",
"confirm_password_label": "Confirmar contraseña",
"confirm_password_hint": "Confirma tu contraseña",
"create_account_button": "Crear cuenta",
"or_divider": "o",
"social_apple": "Regístrate con Apple",
"social_google": "Regístrate con Google",
"has_account": "¿Ya tienes una cuenta? ",
"sign_in_link": "Iniciar sesión"
}
},
"client_home": {
"dashboard": {
"welcome_back": "Bienvenido de nuevo",
"edit_mode_active": "Modo Edición Activo",
"drag_instruction": "Arrastra para reordenar, cambia la visibilidad",
"reset": "Restablecer",
"metric_needed": "Necesario",
"metric_filled": "Lleno",
"metric_open": "Abierto",
"view_all": "Ver todo",
"insight_lightbulb": "Ahorra $amount/mes",
"insight_tip": "Reserva con 48h de antelación para mejores tarifas"
},
"widgets": {
"actions": "Acciones Rápidas",
"reorder": "Reordenar",
"coverage": "Cobertura de Hoy",
"spending": "Información de Gastos",
"live_activity": "Actividad en Vivo"
},
"actions": {
"rapid": "RÁPIDO",
"rapid_subtitle": "Urgente mismo día",
"create_order": "Crear Orden",
"create_order_subtitle": "Programar turnos"
},
"reorder": {
"title": "REORDENAR",
"reorder_button": "Reordenar",
"per_hr": "$amount/hr"
},
"form": {
"edit_reorder": "Editar y Reordenar",
"post_new": "Publicar un Nuevo Turno",
"review_subtitle": "Revisa y edita los detalles antes de publicar",
"date_label": "Fecha *",
"date_hint": "mm/dd/aaaa",
"location_label": "Ubicación *",
"location_hint": "Dirección del negocio",
"positions_title": "Posiciones",
"add_position": "Añadir Posición",
"role_label": "Rol *",
"role_hint": "Seleccionar rol",
"start_time": "Hora de Inicio *",
"end_time": "Hora de Fin *",
"workers_needed": "Trabajadores Necesarios *",
"hourly_rate": "Tarifa por hora (\\$) *",
"post_shift": "Publicar Turno"
}
}
}

View File

@@ -0,0 +1,183 @@
/// Generated file. Do not edit.
///
/// Source: lib/src/l10n
/// To regenerate, run: `dart run slang`
///
/// Locales: 2
/// Strings: 276 (138 per locale)
///
/// Built on 2026-01-21 at 18:21 UTC
// coverage:ignore-file
// ignore_for_file: type=lint, unused_import
// dart format off
import 'package:flutter/widgets.dart';
import 'package:intl/intl.dart';
import 'package:slang/generated.dart';
import 'package:slang_flutter/slang_flutter.dart';
export 'package:slang_flutter/slang_flutter.dart';
import 'strings_es.g.dart' deferred as l_es;
part 'strings_en.g.dart';
/// Supported locales.
///
/// Usage:
/// - LocaleSettings.setLocale(AppLocale.en) // set locale
/// - Locale locale = AppLocale.en.flutterLocale // get flutter locale from enum
/// - if (LocaleSettings.currentLocale == AppLocale.en) // locale check
enum AppLocale with BaseAppLocale<AppLocale, Translations> {
en(languageCode: 'en'),
es(languageCode: 'es');
const AppLocale({
required this.languageCode,
this.scriptCode, // ignore: unused_element, unused_element_parameter
this.countryCode, // ignore: unused_element, unused_element_parameter
});
@override final String languageCode;
@override final String? scriptCode;
@override final String? countryCode;
@override
Future<Translations> build({
Map<String, Node>? overrides,
PluralResolver? cardinalResolver,
PluralResolver? ordinalResolver,
}) async {
switch (this) {
case AppLocale.en:
return TranslationsEn(
overrides: overrides,
cardinalResolver: cardinalResolver,
ordinalResolver: ordinalResolver,
);
case AppLocale.es:
await l_es.loadLibrary();
return l_es.TranslationsEs(
overrides: overrides,
cardinalResolver: cardinalResolver,
ordinalResolver: ordinalResolver,
);
}
}
@override
Translations buildSync({
Map<String, Node>? overrides,
PluralResolver? cardinalResolver,
PluralResolver? ordinalResolver,
}) {
switch (this) {
case AppLocale.en:
return TranslationsEn(
overrides: overrides,
cardinalResolver: cardinalResolver,
ordinalResolver: ordinalResolver,
);
case AppLocale.es:
return l_es.TranslationsEs(
overrides: overrides,
cardinalResolver: cardinalResolver,
ordinalResolver: ordinalResolver,
);
}
}
/// Gets current instance managed by [LocaleSettings].
Translations get translations => LocaleSettings.instance.getTranslations(this);
}
/// Method A: Simple
///
/// No rebuild after locale change.
/// Translation happens during initialization of the widget (call of t).
/// Configurable via 'translate_var'.
///
/// Usage:
/// String a = t.someKey.anotherKey;
/// String b = t['someKey.anotherKey']; // Only for edge cases!
Translations get t => LocaleSettings.instance.currentTranslations;
/// Method B: Advanced
///
/// All widgets using this method will trigger a rebuild when locale changes.
/// Use this if you have e.g. a settings page where the user can select the locale during runtime.
///
/// Step 1:
/// wrap your App with
/// TranslationProvider(
/// child: MyApp()
/// );
///
/// Step 2:
/// final t = Translations.of(context); // Get t variable.
/// String a = t.someKey.anotherKey; // Use t variable.
/// String b = t['someKey.anotherKey']; // Only for edge cases!
class TranslationProvider extends BaseTranslationProvider<AppLocale, Translations> {
TranslationProvider({required super.child}) : super(settings: LocaleSettings.instance);
static InheritedLocaleData<AppLocale, Translations> of(BuildContext context) => InheritedLocaleData.of<AppLocale, Translations>(context);
}
/// Method B shorthand via [BuildContext] extension method.
/// Configurable via 'translate_var'.
///
/// Usage (e.g. in a widget's build method):
/// context.t.someKey.anotherKey
extension BuildContextTranslationsExtension on BuildContext {
Translations get t => TranslationProvider.of(this).translations;
}
/// Manages all translation instances and the current locale
class LocaleSettings extends BaseFlutterLocaleSettings<AppLocale, Translations> {
LocaleSettings._() : super(
utils: AppLocaleUtils.instance,
lazy: true,
);
static final instance = LocaleSettings._();
// static aliases (checkout base methods for documentation)
static AppLocale get currentLocale => instance.currentLocale;
static Stream<AppLocale> getLocaleStream() => instance.getLocaleStream();
static Future<AppLocale> setLocale(AppLocale locale, {bool? listenToDeviceLocale = false}) => instance.setLocale(locale, listenToDeviceLocale: listenToDeviceLocale);
static Future<AppLocale> setLocaleRaw(String rawLocale, {bool? listenToDeviceLocale = false}) => instance.setLocaleRaw(rawLocale, listenToDeviceLocale: listenToDeviceLocale);
static Future<AppLocale> useDeviceLocale() => instance.useDeviceLocale();
static Future<void> setPluralResolver({String? language, AppLocale? locale, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver}) => instance.setPluralResolver(
language: language,
locale: locale,
cardinalResolver: cardinalResolver,
ordinalResolver: ordinalResolver,
);
// synchronous versions
static AppLocale setLocaleSync(AppLocale locale, {bool? listenToDeviceLocale = false}) => instance.setLocaleSync(locale, listenToDeviceLocale: listenToDeviceLocale);
static AppLocale setLocaleRawSync(String rawLocale, {bool? listenToDeviceLocale = false}) => instance.setLocaleRawSync(rawLocale, listenToDeviceLocale: listenToDeviceLocale);
static AppLocale useDeviceLocaleSync() => instance.useDeviceLocaleSync();
static void setPluralResolverSync({String? language, AppLocale? locale, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver}) => instance.setPluralResolverSync(
language: language,
locale: locale,
cardinalResolver: cardinalResolver,
ordinalResolver: ordinalResolver,
);
}
/// Provides utility functions without any side effects.
class AppLocaleUtils extends BaseAppLocaleUtils<AppLocale, Translations> {
AppLocaleUtils._() : super(
baseLocale: AppLocale.en,
locales: AppLocale.values,
);
static final instance = AppLocaleUtils._();
// static aliases (checkout base methods for documentation)
static AppLocale parse(String rawLocale) => instance.parse(rawLocale);
static AppLocale parseLocaleParts({required String languageCode, String? scriptCode, String? countryCode}) => instance.parseLocaleParts(languageCode: languageCode, scriptCode: scriptCode, countryCode: countryCode);
static AppLocale findDeviceLocale() => instance.findDeviceLocale();
static List<Locale> get supportedLocales => instance.supportedLocales;
static List<String> get supportedLocalesRaw => instance.supportedLocalesRaw;
}

View File

@@ -0,0 +1,861 @@
///
/// Generated file. Do not edit.
///
// coverage:ignore-file
// ignore_for_file: type=lint, unused_import
// dart format off
part of 'strings.g.dart';
// Path: <root>
typedef TranslationsEn = Translations; // ignore: unused_element
class Translations with BaseTranslations<AppLocale, Translations> {
/// Returns the current translations of the given [context].
///
/// Usage:
/// final t = Translations.of(context);
static Translations of(BuildContext context) => InheritedLocaleData.of<AppLocale, Translations>(context).translations;
/// You can call this constructor and build your own translation instance of this locale.
/// Constructing via the enum [AppLocale.build] is preferred.
Translations({Map<String, Node>? overrides, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver, TranslationMetadata<AppLocale, Translations>? meta})
: assert(overrides == null, 'Set "translation_overrides: true" in order to enable this feature.'),
$meta = meta ?? TranslationMetadata(
locale: AppLocale.en,
overrides: overrides ?? {},
cardinalResolver: cardinalResolver,
ordinalResolver: ordinalResolver,
) {
$meta.setFlatMapFunction(_flatMapFunction);
}
/// Metadata for the translations of <en>.
@override final TranslationMetadata<AppLocale, Translations> $meta;
/// Access flat map
dynamic operator[](String key) => $meta.getTranslation(key);
late final Translations _root = this; // ignore: unused_field
Translations $copyWith({TranslationMetadata<AppLocale, Translations>? meta}) => Translations(meta: meta ?? this.$meta);
// Translations
late final TranslationsCommonEn common = TranslationsCommonEn._(_root);
late final TranslationsSettingsEn settings = TranslationsSettingsEn._(_root);
late final TranslationsStaffAuthenticationEn staff_authentication = TranslationsStaffAuthenticationEn._(_root);
late final TranslationsClientAuthenticationEn client_authentication = TranslationsClientAuthenticationEn._(_root);
late final TranslationsClientHomeEn client_home = TranslationsClientHomeEn._(_root);
}
// Path: common
class TranslationsCommonEn {
TranslationsCommonEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'OK'
String get ok => 'OK';
/// en: 'Cancel'
String get cancel => 'Cancel';
/// en: 'Save'
String get save => 'Save';
/// en: 'Delete'
String get delete => 'Delete';
/// en: 'Continue'
String get continue_text => 'Continue';
}
// Path: settings
class TranslationsSettingsEn {
TranslationsSettingsEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Language'
String get language => 'Language';
/// en: 'Change Language'
String get change_language => 'Change Language';
}
// Path: staff_authentication
class TranslationsStaffAuthenticationEn {
TranslationsStaffAuthenticationEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
late final TranslationsStaffAuthenticationGetStartedPageEn get_started_page = TranslationsStaffAuthenticationGetStartedPageEn._(_root);
late final TranslationsStaffAuthenticationPhoneVerificationPageEn phone_verification_page = TranslationsStaffAuthenticationPhoneVerificationPageEn._(_root);
late final TranslationsStaffAuthenticationPhoneInputEn phone_input = TranslationsStaffAuthenticationPhoneInputEn._(_root);
late final TranslationsStaffAuthenticationOtpVerificationEn otp_verification = TranslationsStaffAuthenticationOtpVerificationEn._(_root);
late final TranslationsStaffAuthenticationProfileSetupPageEn profile_setup_page = TranslationsStaffAuthenticationProfileSetupPageEn._(_root);
late final TranslationsStaffAuthenticationCommonEn common = TranslationsStaffAuthenticationCommonEn._(_root);
}
// Path: client_authentication
class TranslationsClientAuthenticationEn {
TranslationsClientAuthenticationEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
late final TranslationsClientAuthenticationGetStartedPageEn get_started_page = TranslationsClientAuthenticationGetStartedPageEn._(_root);
late final TranslationsClientAuthenticationSignInPageEn sign_in_page = TranslationsClientAuthenticationSignInPageEn._(_root);
late final TranslationsClientAuthenticationSignUpPageEn sign_up_page = TranslationsClientAuthenticationSignUpPageEn._(_root);
}
// Path: client_home
class TranslationsClientHomeEn {
TranslationsClientHomeEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Shift order submitted successfully'
String get shift_created_success => 'Shift order submitted successfully';
late final TranslationsClientHomeDashboardEn dashboard = TranslationsClientHomeDashboardEn._(_root);
late final TranslationsClientHomeWidgetsEn widgets = TranslationsClientHomeWidgetsEn._(_root);
late final TranslationsClientHomeActionsEn actions = TranslationsClientHomeActionsEn._(_root);
late final TranslationsClientHomeReorderEn reorder = TranslationsClientHomeReorderEn._(_root);
late final TranslationsClientHomeFormEn form = TranslationsClientHomeFormEn._(_root);
}
// Path: staff_authentication.get_started_page
class TranslationsStaffAuthenticationGetStartedPageEn {
TranslationsStaffAuthenticationGetStartedPageEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Work, Grow, '
String get title_part1 => 'Work, Grow, ';
/// en: 'Elevate'
String get title_part2 => 'Elevate';
/// en: 'Build your career in hospitality with flexibility and freedom.'
String get subtitle => 'Build your career in hospitality with \nflexibility and freedom.';
/// en: 'Sign Up'
String get sign_up_button => 'Sign Up';
/// en: 'Log In'
String get log_in_button => 'Log In';
}
// Path: staff_authentication.phone_verification_page
class TranslationsStaffAuthenticationPhoneVerificationPageEn {
TranslationsStaffAuthenticationPhoneVerificationPageEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Please enter a valid 10-digit phone number'
String get validation_error => 'Please enter a valid 10-digit phone number';
/// en: 'Send Code'
String get send_code_button => 'Send Code';
/// en: 'Enter verification code'
String get enter_code_title => 'Enter verification code';
/// en: 'We sent a 6-digit code to '
String get code_sent_message => 'We sent a 6-digit code to ';
/// en: '. Enter it below to verify your account.'
String get code_sent_instruction => '. Enter it below to verify your account.';
}
// Path: staff_authentication.phone_input
class TranslationsStaffAuthenticationPhoneInputEn {
TranslationsStaffAuthenticationPhoneInputEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Verify your phone number'
String get title => 'Verify your phone number';
/// en: 'We'll send you a verification code to get started.'
String get subtitle => 'We\'ll send you a verification code to get started.';
/// en: 'Phone Number'
String get label => 'Phone Number';
/// en: 'Enter your number'
String get hint => 'Enter your number';
}
// Path: staff_authentication.otp_verification
class TranslationsStaffAuthenticationOtpVerificationEn {
TranslationsStaffAuthenticationOtpVerificationEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Didn't get the code ?'
String get did_not_get_code => 'Didn\'t get the code ?';
/// en: 'Resend in $seconds s'
String resend_in({required Object seconds}) => 'Resend in ${seconds} s';
/// en: 'Resend code'
String get resend_code => 'Resend code';
}
// Path: staff_authentication.profile_setup_page
class TranslationsStaffAuthenticationProfileSetupPageEn {
TranslationsStaffAuthenticationProfileSetupPageEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Step $current of $total'
String step_indicator({required Object current, required Object total}) => 'Step ${current} of ${total}';
/// en: 'An error occurred'
String get error_occurred => 'An error occurred';
/// en: 'Complete Setup'
String get complete_setup_button => 'Complete Setup';
late final TranslationsStaffAuthenticationProfileSetupPageStepsEn steps = TranslationsStaffAuthenticationProfileSetupPageStepsEn._(_root);
late final TranslationsStaffAuthenticationProfileSetupPageBasicInfoEn basic_info = TranslationsStaffAuthenticationProfileSetupPageBasicInfoEn._(_root);
late final TranslationsStaffAuthenticationProfileSetupPageLocationEn location = TranslationsStaffAuthenticationProfileSetupPageLocationEn._(_root);
late final TranslationsStaffAuthenticationProfileSetupPageExperienceEn experience = TranslationsStaffAuthenticationProfileSetupPageExperienceEn._(_root);
}
// Path: staff_authentication.common
class TranslationsStaffAuthenticationCommonEn {
TranslationsStaffAuthenticationCommonEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Having trouble? '
String get trouble_question => 'Having trouble? ';
/// en: 'Contact Support'
String get contact_support => 'Contact Support';
}
// Path: client_authentication.get_started_page
class TranslationsClientAuthenticationGetStartedPageEn {
TranslationsClientAuthenticationGetStartedPageEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Take Control of Your Shifts and Events'
String get title => 'Take Control of Your\nShifts and Events';
/// en: 'Streamline your operations with powerful tools to manage schedules, track performance, and keep your team on the same page—all in one place'
String get subtitle => 'Streamline your operations with powerful tools to manage schedules, track performance, and keep your team on the same page—all in one place';
/// en: 'Sign In'
String get sign_in_button => 'Sign In';
/// en: 'Create Account'
String get create_account_button => 'Create Account';
}
// Path: client_authentication.sign_in_page
class TranslationsClientAuthenticationSignInPageEn {
TranslationsClientAuthenticationSignInPageEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Welcome Back'
String get title => 'Welcome Back';
/// en: 'Sign in to manage your shifts and workers'
String get subtitle => 'Sign in to manage your shifts and workers';
/// en: 'Email'
String get email_label => 'Email';
/// en: 'Enter your email'
String get email_hint => 'Enter your email';
/// en: 'Password'
String get password_label => 'Password';
/// en: 'Enter your password'
String get password_hint => 'Enter your password';
/// en: 'Forgot Password?'
String get forgot_password => 'Forgot Password?';
/// en: 'Sign In'
String get sign_in_button => 'Sign In';
/// en: 'or'
String get or_divider => 'or';
/// en: 'Sign In with Apple'
String get social_apple => 'Sign In with Apple';
/// en: 'Sign In with Google'
String get social_google => 'Sign In with Google';
/// en: 'Don't have an account? '
String get no_account => 'Don\'t have an account? ';
/// en: 'Sign Up'
String get sign_up_link => 'Sign Up';
}
// Path: client_authentication.sign_up_page
class TranslationsClientAuthenticationSignUpPageEn {
TranslationsClientAuthenticationSignUpPageEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Create Account'
String get title => 'Create Account';
/// en: 'Get started with Krow for your business'
String get subtitle => 'Get started with Krow for your business';
/// en: 'Company Name'
String get company_label => 'Company Name';
/// en: 'Enter company name'
String get company_hint => 'Enter company name';
/// en: 'Email'
String get email_label => 'Email';
/// en: 'Enter your email'
String get email_hint => 'Enter your email';
/// en: 'Password'
String get password_label => 'Password';
/// en: 'Create a password'
String get password_hint => 'Create a password';
/// en: 'Confirm Password'
String get confirm_password_label => 'Confirm Password';
/// en: 'Confirm your password'
String get confirm_password_hint => 'Confirm your password';
/// en: 'Create Account'
String get create_account_button => 'Create Account';
/// en: 'or'
String get or_divider => 'or';
/// en: 'Sign Up with Apple'
String get social_apple => 'Sign Up with Apple';
/// en: 'Sign Up with Google'
String get social_google => 'Sign Up with Google';
/// en: 'Already have an account? '
String get has_account => 'Already have an account? ';
/// en: 'Sign In'
String get sign_in_link => 'Sign In';
}
// Path: client_home.dashboard
class TranslationsClientHomeDashboardEn {
TranslationsClientHomeDashboardEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Welcome back'
String get welcome_back => 'Welcome back';
/// en: 'Edit Mode Active'
String get edit_mode_active => 'Edit Mode Active';
/// en: 'Drag to reorder, toggle visibility'
String get drag_instruction => 'Drag to reorder, toggle visibility';
/// en: 'Reset'
String get reset => 'Reset';
/// en: 'Needed'
String get metric_needed => 'Needed';
/// en: 'Filled'
String get metric_filled => 'Filled';
/// en: 'Open'
String get metric_open => 'Open';
/// en: 'View all'
String get view_all => 'View all';
/// en: 'Save $amount/month'
String insight_lightbulb({required Object amount}) => 'Save ${amount}/month';
/// en: 'Book 48hrs ahead for better rates'
String get insight_tip => 'Book 48hrs ahead for better rates';
}
// Path: client_home.widgets
class TranslationsClientHomeWidgetsEn {
TranslationsClientHomeWidgetsEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Quick Actions'
String get actions => 'Quick Actions';
/// en: 'Reorder'
String get reorder => 'Reorder';
/// en: 'Today's Coverage'
String get coverage => 'Today\'s Coverage';
/// en: 'Spending Insights'
String get spending => 'Spending Insights';
/// en: 'Live Activity'
String get live_activity => 'Live Activity';
}
// Path: client_home.actions
class TranslationsClientHomeActionsEn {
TranslationsClientHomeActionsEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'RAPID'
String get rapid => 'RAPID';
/// en: 'Urgent same-day'
String get rapid_subtitle => 'Urgent same-day';
/// en: 'Create Order'
String get create_order => 'Create Order';
/// en: 'Schedule shifts'
String get create_order_subtitle => 'Schedule shifts';
}
// Path: client_home.reorder
class TranslationsClientHomeReorderEn {
TranslationsClientHomeReorderEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'REORDER'
String get title => 'REORDER';
/// en: 'Reorder'
String get reorder_button => 'Reorder';
/// en: '$amount/hr'
String per_hr({required Object amount}) => '${amount}/hr';
}
// Path: client_home.form
class TranslationsClientHomeFormEn {
TranslationsClientHomeFormEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Edit & Reorder'
String get edit_reorder => 'Edit & Reorder';
/// en: 'Post a New Shift'
String get post_new => 'Post a New Shift';
/// en: 'Review and edit the details before posting'
String get review_subtitle => 'Review and edit the details before posting';
/// en: 'Date *'
String get date_label => 'Date *';
/// en: 'mm/dd/yyyy'
String get date_hint => 'mm/dd/yyyy';
/// en: 'Location *'
String get location_label => 'Location *';
/// en: 'Business address'
String get location_hint => 'Business address';
/// en: 'Positions'
String get positions_title => 'Positions';
/// en: 'Add Position'
String get add_position => 'Add Position';
/// en: 'Role *'
String get role_label => 'Role *';
/// en: 'Select role'
String get role_hint => 'Select role';
/// en: 'Start Time *'
String get start_time => 'Start Time *';
/// en: 'End Time *'
String get end_time => 'End Time *';
/// en: 'Workers Needed *'
String get workers_needed => 'Workers Needed *';
/// en: 'Hourly Rate (\$) *'
String get hourly_rate => 'Hourly Rate (\$) *';
/// en: 'Post Shift'
String get post_shift => 'Post Shift';
}
// Path: staff_authentication.profile_setup_page.steps
class TranslationsStaffAuthenticationProfileSetupPageStepsEn {
TranslationsStaffAuthenticationProfileSetupPageStepsEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Basic Info'
String get basic => 'Basic Info';
/// en: 'Location'
String get location => 'Location';
/// en: 'Experience'
String get experience => 'Experience';
}
// Path: staff_authentication.profile_setup_page.basic_info
class TranslationsStaffAuthenticationProfileSetupPageBasicInfoEn {
TranslationsStaffAuthenticationProfileSetupPageBasicInfoEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Let's get to know you'
String get title => 'Let\'s get to know you';
/// en: 'Tell us a bit about yourself'
String get subtitle => 'Tell us a bit about yourself';
/// en: 'Full Name *'
String get full_name_label => 'Full Name *';
/// en: 'John Smith'
String get full_name_hint => 'John Smith';
/// en: 'Short Bio'
String get bio_label => 'Short Bio';
/// en: 'Experienced hospitality professional...'
String get bio_hint => 'Experienced hospitality professional...';
}
// Path: staff_authentication.profile_setup_page.location
class TranslationsStaffAuthenticationProfileSetupPageLocationEn {
TranslationsStaffAuthenticationProfileSetupPageLocationEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Where do you want to work?'
String get title => 'Where do you want to work?';
/// en: 'Add your preferred work locations'
String get subtitle => 'Add your preferred work locations';
/// en: 'Add Location *'
String get add_location_label => 'Add Location *';
/// en: 'City or ZIP code'
String get add_location_hint => 'City or ZIP code';
/// en: 'Add'
String get add_button => 'Add';
/// en: 'Max Distance: $distance miles'
String max_distance({required Object distance}) => 'Max Distance: ${distance} miles';
/// en: '5 mi'
String get min_dist_label => '5 mi';
/// en: '50 mi'
String get max_dist_label => '50 mi';
}
// Path: staff_authentication.profile_setup_page.experience
class TranslationsStaffAuthenticationProfileSetupPageExperienceEn {
TranslationsStaffAuthenticationProfileSetupPageExperienceEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'What are your skills?'
String get title => 'What are your skills?';
/// en: 'Select all that apply'
String get subtitle => 'Select all that apply';
/// en: 'Skills *'
String get skills_label => 'Skills *';
/// en: 'Preferred Industries'
String get industries_label => 'Preferred Industries';
late final TranslationsStaffAuthenticationProfileSetupPageExperienceSkillsEn skills = TranslationsStaffAuthenticationProfileSetupPageExperienceSkillsEn._(_root);
late final TranslationsStaffAuthenticationProfileSetupPageExperienceIndustriesEn industries = TranslationsStaffAuthenticationProfileSetupPageExperienceIndustriesEn._(_root);
}
// Path: staff_authentication.profile_setup_page.experience.skills
class TranslationsStaffAuthenticationProfileSetupPageExperienceSkillsEn {
TranslationsStaffAuthenticationProfileSetupPageExperienceSkillsEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Food Service'
String get food_service => 'Food Service';
/// en: 'Bartending'
String get bartending => 'Bartending';
/// en: 'Warehouse'
String get warehouse => 'Warehouse';
/// en: 'Retail'
String get retail => 'Retail';
/// en: 'Events'
String get events => 'Events';
/// en: 'Customer Service'
String get customer_service => 'Customer Service';
/// en: 'Cleaning'
String get cleaning => 'Cleaning';
/// en: 'Security'
String get security => 'Security';
/// en: 'Driving'
String get driving => 'Driving';
/// en: 'Cooking'
String get cooking => 'Cooking';
}
// Path: staff_authentication.profile_setup_page.experience.industries
class TranslationsStaffAuthenticationProfileSetupPageExperienceIndustriesEn {
TranslationsStaffAuthenticationProfileSetupPageExperienceIndustriesEn._(this._root);
final Translations _root; // ignore: unused_field
// Translations
/// en: 'Hospitality'
String get hospitality => 'Hospitality';
/// en: 'Food Service'
String get food_service => 'Food Service';
/// en: 'Warehouse'
String get warehouse => 'Warehouse';
/// en: 'Events'
String get events => 'Events';
/// en: 'Retail'
String get retail => 'Retail';
/// en: 'Healthcare'
String get healthcare => 'Healthcare';
}
/// The flat map containing all translations for locale <en>.
/// Only for edge cases! For simple maps, use the map function of this library.
///
/// The Dart AOT compiler has issues with very large switch statements,
/// so the map is split into smaller functions (512 entries each).
extension on Translations {
dynamic _flatMapFunction(String path) {
return switch (path) {
'common.ok' => 'OK',
'common.cancel' => 'Cancel',
'common.save' => 'Save',
'common.delete' => 'Delete',
'common.continue_text' => 'Continue',
'settings.language' => 'Language',
'settings.change_language' => 'Change Language',
'staff_authentication.get_started_page.title_part1' => 'Work, Grow, ',
'staff_authentication.get_started_page.title_part2' => 'Elevate',
'staff_authentication.get_started_page.subtitle' => 'Build your career in hospitality with \nflexibility and freedom.',
'staff_authentication.get_started_page.sign_up_button' => 'Sign Up',
'staff_authentication.get_started_page.log_in_button' => 'Log In',
'staff_authentication.phone_verification_page.validation_error' => 'Please enter a valid 10-digit phone number',
'staff_authentication.phone_verification_page.send_code_button' => 'Send Code',
'staff_authentication.phone_verification_page.enter_code_title' => 'Enter verification code',
'staff_authentication.phone_verification_page.code_sent_message' => 'We sent a 6-digit code to ',
'staff_authentication.phone_verification_page.code_sent_instruction' => '. Enter it below to verify your account.',
'staff_authentication.phone_input.title' => 'Verify your phone number',
'staff_authentication.phone_input.subtitle' => 'We\'ll send you a verification code to get started.',
'staff_authentication.phone_input.label' => 'Phone Number',
'staff_authentication.phone_input.hint' => 'Enter your number',
'staff_authentication.otp_verification.did_not_get_code' => 'Didn\'t get the code ?',
'staff_authentication.otp_verification.resend_in' => ({required Object seconds}) => 'Resend in ${seconds} s',
'staff_authentication.otp_verification.resend_code' => 'Resend code',
'staff_authentication.profile_setup_page.step_indicator' => ({required Object current, required Object total}) => 'Step ${current} of ${total}',
'staff_authentication.profile_setup_page.error_occurred' => 'An error occurred',
'staff_authentication.profile_setup_page.complete_setup_button' => 'Complete Setup',
'staff_authentication.profile_setup_page.steps.basic' => 'Basic Info',
'staff_authentication.profile_setup_page.steps.location' => 'Location',
'staff_authentication.profile_setup_page.steps.experience' => 'Experience',
'staff_authentication.profile_setup_page.basic_info.title' => 'Let\'s get to know you',
'staff_authentication.profile_setup_page.basic_info.subtitle' => 'Tell us a bit about yourself',
'staff_authentication.profile_setup_page.basic_info.full_name_label' => 'Full Name *',
'staff_authentication.profile_setup_page.basic_info.full_name_hint' => 'John Smith',
'staff_authentication.profile_setup_page.basic_info.bio_label' => 'Short Bio',
'staff_authentication.profile_setup_page.basic_info.bio_hint' => 'Experienced hospitality professional...',
'staff_authentication.profile_setup_page.location.title' => 'Where do you want to work?',
'staff_authentication.profile_setup_page.location.subtitle' => 'Add your preferred work locations',
'staff_authentication.profile_setup_page.location.add_location_label' => 'Add Location *',
'staff_authentication.profile_setup_page.location.add_location_hint' => 'City or ZIP code',
'staff_authentication.profile_setup_page.location.add_button' => 'Add',
'staff_authentication.profile_setup_page.location.max_distance' => ({required Object distance}) => 'Max Distance: ${distance} miles',
'staff_authentication.profile_setup_page.location.min_dist_label' => '5 mi',
'staff_authentication.profile_setup_page.location.max_dist_label' => '50 mi',
'staff_authentication.profile_setup_page.experience.title' => 'What are your skills?',
'staff_authentication.profile_setup_page.experience.subtitle' => 'Select all that apply',
'staff_authentication.profile_setup_page.experience.skills_label' => 'Skills *',
'staff_authentication.profile_setup_page.experience.industries_label' => 'Preferred Industries',
'staff_authentication.profile_setup_page.experience.skills.food_service' => 'Food Service',
'staff_authentication.profile_setup_page.experience.skills.bartending' => 'Bartending',
'staff_authentication.profile_setup_page.experience.skills.warehouse' => 'Warehouse',
'staff_authentication.profile_setup_page.experience.skills.retail' => 'Retail',
'staff_authentication.profile_setup_page.experience.skills.events' => 'Events',
'staff_authentication.profile_setup_page.experience.skills.customer_service' => 'Customer Service',
'staff_authentication.profile_setup_page.experience.skills.cleaning' => 'Cleaning',
'staff_authentication.profile_setup_page.experience.skills.security' => 'Security',
'staff_authentication.profile_setup_page.experience.skills.driving' => 'Driving',
'staff_authentication.profile_setup_page.experience.skills.cooking' => 'Cooking',
'staff_authentication.profile_setup_page.experience.industries.hospitality' => 'Hospitality',
'staff_authentication.profile_setup_page.experience.industries.food_service' => 'Food Service',
'staff_authentication.profile_setup_page.experience.industries.warehouse' => 'Warehouse',
'staff_authentication.profile_setup_page.experience.industries.events' => 'Events',
'staff_authentication.profile_setup_page.experience.industries.retail' => 'Retail',
'staff_authentication.profile_setup_page.experience.industries.healthcare' => 'Healthcare',
'staff_authentication.common.trouble_question' => 'Having trouble? ',
'staff_authentication.common.contact_support' => 'Contact Support',
'client_authentication.get_started_page.title' => 'Take Control of Your\nShifts and Events',
'client_authentication.get_started_page.subtitle' => 'Streamline your operations with powerful tools to manage schedules, track performance, and keep your team on the same page—all in one place',
'client_authentication.get_started_page.sign_in_button' => 'Sign In',
'client_authentication.get_started_page.create_account_button' => 'Create Account',
'client_authentication.sign_in_page.title' => 'Welcome Back',
'client_authentication.sign_in_page.subtitle' => 'Sign in to manage your shifts and workers',
'client_authentication.sign_in_page.email_label' => 'Email',
'client_authentication.sign_in_page.email_hint' => 'Enter your email',
'client_authentication.sign_in_page.password_label' => 'Password',
'client_authentication.sign_in_page.password_hint' => 'Enter your password',
'client_authentication.sign_in_page.forgot_password' => 'Forgot Password?',
'client_authentication.sign_in_page.sign_in_button' => 'Sign In',
'client_authentication.sign_in_page.or_divider' => 'or',
'client_authentication.sign_in_page.social_apple' => 'Sign In with Apple',
'client_authentication.sign_in_page.social_google' => 'Sign In with Google',
'client_authentication.sign_in_page.no_account' => 'Don\'t have an account? ',
'client_authentication.sign_in_page.sign_up_link' => 'Sign Up',
'client_authentication.sign_up_page.title' => 'Create Account',
'client_authentication.sign_up_page.subtitle' => 'Get started with Krow for your business',
'client_authentication.sign_up_page.company_label' => 'Company Name',
'client_authentication.sign_up_page.company_hint' => 'Enter company name',
'client_authentication.sign_up_page.email_label' => 'Email',
'client_authentication.sign_up_page.email_hint' => 'Enter your email',
'client_authentication.sign_up_page.password_label' => 'Password',
'client_authentication.sign_up_page.password_hint' => 'Create a password',
'client_authentication.sign_up_page.confirm_password_label' => 'Confirm Password',
'client_authentication.sign_up_page.confirm_password_hint' => 'Confirm your password',
'client_authentication.sign_up_page.create_account_button' => 'Create Account',
'client_authentication.sign_up_page.or_divider' => 'or',
'client_authentication.sign_up_page.social_apple' => 'Sign Up with Apple',
'client_authentication.sign_up_page.social_google' => 'Sign Up with Google',
'client_authentication.sign_up_page.has_account' => 'Already have an account? ',
'client_authentication.sign_up_page.sign_in_link' => 'Sign In',
'client_home.shift_created_success' => 'Shift order submitted successfully',
'client_home.dashboard.welcome_back' => 'Welcome back',
'client_home.dashboard.edit_mode_active' => 'Edit Mode Active',
'client_home.dashboard.drag_instruction' => 'Drag to reorder, toggle visibility',
'client_home.dashboard.reset' => 'Reset',
'client_home.dashboard.metric_needed' => 'Needed',
'client_home.dashboard.metric_filled' => 'Filled',
'client_home.dashboard.metric_open' => 'Open',
'client_home.dashboard.view_all' => 'View all',
'client_home.dashboard.insight_lightbulb' => ({required Object amount}) => 'Save ${amount}/month',
'client_home.dashboard.insight_tip' => 'Book 48hrs ahead for better rates',
'client_home.widgets.actions' => 'Quick Actions',
'client_home.widgets.reorder' => 'Reorder',
'client_home.widgets.coverage' => 'Today\'s Coverage',
'client_home.widgets.spending' => 'Spending Insights',
'client_home.widgets.live_activity' => 'Live Activity',
'client_home.actions.rapid' => 'RAPID',
'client_home.actions.rapid_subtitle' => 'Urgent same-day',
'client_home.actions.create_order' => 'Create Order',
'client_home.actions.create_order_subtitle' => 'Schedule shifts',
'client_home.reorder.title' => 'REORDER',
'client_home.reorder.reorder_button' => 'Reorder',
'client_home.reorder.per_hr' => ({required Object amount}) => '${amount}/hr',
'client_home.form.edit_reorder' => 'Edit & Reorder',
'client_home.form.post_new' => 'Post a New Shift',
'client_home.form.review_subtitle' => 'Review and edit the details before posting',
'client_home.form.date_label' => 'Date *',
'client_home.form.date_hint' => 'mm/dd/yyyy',
'client_home.form.location_label' => 'Location *',
'client_home.form.location_hint' => 'Business address',
'client_home.form.positions_title' => 'Positions',
'client_home.form.add_position' => 'Add Position',
'client_home.form.role_label' => 'Role *',
'client_home.form.role_hint' => 'Select role',
'client_home.form.start_time' => 'Start Time *',
'client_home.form.end_time' => 'End Time *',
'client_home.form.workers_needed' => 'Workers Needed *',
'client_home.form.hourly_rate' => 'Hourly Rate (\$) *',
'client_home.form.post_shift' => 'Post Shift',
_ => null,
};
}
}

View File

@@ -0,0 +1,579 @@
///
/// Generated file. Do not edit.
///
// coverage:ignore-file
// ignore_for_file: type=lint, unused_import
// dart format off
import 'package:flutter/widgets.dart';
import 'package:intl/intl.dart';
import 'package:slang/generated.dart';
import 'strings.g.dart';
// Path: <root>
class TranslationsEs with BaseTranslations<AppLocale, Translations> implements Translations {
/// You can call this constructor and build your own translation instance of this locale.
/// Constructing via the enum [AppLocale.build] is preferred.
TranslationsEs({Map<String, Node>? overrides, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver, TranslationMetadata<AppLocale, Translations>? meta})
: assert(overrides == null, 'Set "translation_overrides: true" in order to enable this feature.'),
$meta = meta ?? TranslationMetadata(
locale: AppLocale.es,
overrides: overrides ?? {},
cardinalResolver: cardinalResolver,
ordinalResolver: ordinalResolver,
) {
$meta.setFlatMapFunction(_flatMapFunction);
}
/// Metadata for the translations of <es>.
@override final TranslationMetadata<AppLocale, Translations> $meta;
/// Access flat map
@override dynamic operator[](String key) => $meta.getTranslation(key);
late final TranslationsEs _root = this; // ignore: unused_field
@override
TranslationsEs $copyWith({TranslationMetadata<AppLocale, Translations>? meta}) => TranslationsEs(meta: meta ?? this.$meta);
// Translations
@override late final _TranslationsCommonEs common = _TranslationsCommonEs._(_root);
@override late final _TranslationsSettingsEs settings = _TranslationsSettingsEs._(_root);
@override late final _TranslationsStaffAuthenticationEs staff_authentication = _TranslationsStaffAuthenticationEs._(_root);
@override late final _TranslationsClientAuthenticationEs client_authentication = _TranslationsClientAuthenticationEs._(_root);
@override late final _TranslationsClientHomeEs client_home = _TranslationsClientHomeEs._(_root);
}
// Path: common
class _TranslationsCommonEs implements TranslationsCommonEn {
_TranslationsCommonEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get ok => 'Aceptar';
@override String get cancel => 'Cancelar';
@override String get save => 'Guardar';
@override String get delete => 'Eliminar';
@override String get continue_text => 'Continuar';
}
// Path: settings
class _TranslationsSettingsEs implements TranslationsSettingsEn {
_TranslationsSettingsEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get language => 'Idioma';
@override String get change_language => 'Cambiar Idioma';
}
// Path: staff_authentication
class _TranslationsStaffAuthenticationEs implements TranslationsStaffAuthenticationEn {
_TranslationsStaffAuthenticationEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override late final _TranslationsStaffAuthenticationGetStartedPageEs get_started_page = _TranslationsStaffAuthenticationGetStartedPageEs._(_root);
@override late final _TranslationsStaffAuthenticationPhoneVerificationPageEs phone_verification_page = _TranslationsStaffAuthenticationPhoneVerificationPageEs._(_root);
@override late final _TranslationsStaffAuthenticationPhoneInputEs phone_input = _TranslationsStaffAuthenticationPhoneInputEs._(_root);
@override late final _TranslationsStaffAuthenticationOtpVerificationEs otp_verification = _TranslationsStaffAuthenticationOtpVerificationEs._(_root);
@override late final _TranslationsStaffAuthenticationProfileSetupPageEs profile_setup_page = _TranslationsStaffAuthenticationProfileSetupPageEs._(_root);
@override late final _TranslationsStaffAuthenticationCommonEs common = _TranslationsStaffAuthenticationCommonEs._(_root);
}
// Path: client_authentication
class _TranslationsClientAuthenticationEs implements TranslationsClientAuthenticationEn {
_TranslationsClientAuthenticationEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override late final _TranslationsClientAuthenticationGetStartedPageEs get_started_page = _TranslationsClientAuthenticationGetStartedPageEs._(_root);
@override late final _TranslationsClientAuthenticationSignInPageEs sign_in_page = _TranslationsClientAuthenticationSignInPageEs._(_root);
@override late final _TranslationsClientAuthenticationSignUpPageEs sign_up_page = _TranslationsClientAuthenticationSignUpPageEs._(_root);
}
// Path: client_home
class _TranslationsClientHomeEs implements TranslationsClientHomeEn {
_TranslationsClientHomeEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get shift_created_success => 'Orden de turno enviada con éxito';
@override late final _TranslationsClientHomeDashboardEs dashboard = _TranslationsClientHomeDashboardEs._(_root);
@override late final _TranslationsClientHomeWidgetsEs widgets = _TranslationsClientHomeWidgetsEs._(_root);
@override late final _TranslationsClientHomeActionsEs actions = _TranslationsClientHomeActionsEs._(_root);
@override late final _TranslationsClientHomeReorderEs reorder = _TranslationsClientHomeReorderEs._(_root);
@override late final _TranslationsClientHomeFormEs form = _TranslationsClientHomeFormEs._(_root);
}
// Path: staff_authentication.get_started_page
class _TranslationsStaffAuthenticationGetStartedPageEs implements TranslationsStaffAuthenticationGetStartedPageEn {
_TranslationsStaffAuthenticationGetStartedPageEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get title_part1 => 'Trabaja, Crece, ';
@override String get title_part2 => 'Elévate';
@override String get subtitle => 'Construye tu carrera en hostelería con \nflexibilidad y libertad.';
@override String get sign_up_button => 'Registrarse';
@override String get log_in_button => 'Iniciar sesión';
}
// Path: staff_authentication.phone_verification_page
class _TranslationsStaffAuthenticationPhoneVerificationPageEs implements TranslationsStaffAuthenticationPhoneVerificationPageEn {
_TranslationsStaffAuthenticationPhoneVerificationPageEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get validation_error => 'Por favor, ingresa un número de teléfono válido de 10 dígitos';
@override String get send_code_button => 'Enviar código';
@override String get enter_code_title => 'Ingresa el código de verificación';
@override String get code_sent_message => 'Enviamos un código de 6 dígitos a ';
@override String get code_sent_instruction => '. Ingrésalo a continuación para verificar tu cuenta.';
}
// Path: staff_authentication.phone_input
class _TranslationsStaffAuthenticationPhoneInputEs implements TranslationsStaffAuthenticationPhoneInputEn {
_TranslationsStaffAuthenticationPhoneInputEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get title => 'Verifica tu número de teléfono';
@override String get subtitle => 'Te enviaremos un código de verificación para comenzar.';
@override String get label => 'Número de teléfono';
@override String get hint => 'Ingresa tu número';
}
// Path: staff_authentication.otp_verification
class _TranslationsStaffAuthenticationOtpVerificationEs implements TranslationsStaffAuthenticationOtpVerificationEn {
_TranslationsStaffAuthenticationOtpVerificationEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get did_not_get_code => '¿No recibiste el código?';
@override String resend_in({required Object seconds}) => 'Reenviar en ${seconds} s';
@override String get resend_code => 'Reenviar código';
}
// Path: staff_authentication.profile_setup_page
class _TranslationsStaffAuthenticationProfileSetupPageEs implements TranslationsStaffAuthenticationProfileSetupPageEn {
_TranslationsStaffAuthenticationProfileSetupPageEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String step_indicator({required Object current, required Object total}) => 'Paso ${current} de ${total}';
@override String get error_occurred => 'Ocurrió un error';
@override String get complete_setup_button => 'Completar configuración';
@override late final _TranslationsStaffAuthenticationProfileSetupPageStepsEs steps = _TranslationsStaffAuthenticationProfileSetupPageStepsEs._(_root);
@override late final _TranslationsStaffAuthenticationProfileSetupPageBasicInfoEs basic_info = _TranslationsStaffAuthenticationProfileSetupPageBasicInfoEs._(_root);
@override late final _TranslationsStaffAuthenticationProfileSetupPageLocationEs location = _TranslationsStaffAuthenticationProfileSetupPageLocationEs._(_root);
@override late final _TranslationsStaffAuthenticationProfileSetupPageExperienceEs experience = _TranslationsStaffAuthenticationProfileSetupPageExperienceEs._(_root);
}
// Path: staff_authentication.common
class _TranslationsStaffAuthenticationCommonEs implements TranslationsStaffAuthenticationCommonEn {
_TranslationsStaffAuthenticationCommonEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get trouble_question => '¿Tienes problemas? ';
@override String get contact_support => 'Contactar a soporte';
}
// Path: client_authentication.get_started_page
class _TranslationsClientAuthenticationGetStartedPageEs implements TranslationsClientAuthenticationGetStartedPageEn {
_TranslationsClientAuthenticationGetStartedPageEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get title => 'Toma el control de tus\nturnos y eventos';
@override String get subtitle => 'Optimiza tus operaciones con potentes herramientas para gestionar horarios, realizar un seguimiento del rendimiento y mantener a tu equipo en la misma página, todo en un solo lugar';
@override String get sign_in_button => 'Iniciar sesión';
@override String get create_account_button => 'Crear cuenta';
}
// Path: client_authentication.sign_in_page
class _TranslationsClientAuthenticationSignInPageEs implements TranslationsClientAuthenticationSignInPageEn {
_TranslationsClientAuthenticationSignInPageEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get title => 'Bienvenido de nuevo';
@override String get subtitle => 'Inicia sesión para gestionar tus turnos y trabajadores';
@override String get email_label => 'Correo electrónico';
@override String get email_hint => 'Ingresa tu correo electrónico';
@override String get password_label => 'Contraseña';
@override String get password_hint => 'Ingresa tu contraseña';
@override String get forgot_password => '¿Olvidaste tu contraseña?';
@override String get sign_in_button => 'Iniciar sesión';
@override String get or_divider => 'o';
@override String get social_apple => 'Iniciar sesión con Apple';
@override String get social_google => 'Iniciar sesión con Google';
@override String get no_account => '¿No tienes una cuenta? ';
@override String get sign_up_link => 'Regístrate';
}
// Path: client_authentication.sign_up_page
class _TranslationsClientAuthenticationSignUpPageEs implements TranslationsClientAuthenticationSignUpPageEn {
_TranslationsClientAuthenticationSignUpPageEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get title => 'Crear cuenta';
@override String get subtitle => 'Comienza con Krow para tu negocio';
@override String get company_label => 'Nombre de la empresa';
@override String get company_hint => 'Ingresa el nombre de la empresa';
@override String get email_label => 'Correo electrónico';
@override String get email_hint => 'Ingresa tu correo electrónico';
@override String get password_label => 'Contraseña';
@override String get password_hint => 'Crea una contraseña';
@override String get confirm_password_label => 'Confirmar contraseña';
@override String get confirm_password_hint => 'Confirma tu contraseña';
@override String get create_account_button => 'Crear cuenta';
@override String get or_divider => 'o';
@override String get social_apple => 'Regístrate con Apple';
@override String get social_google => 'Regístrate con Google';
@override String get has_account => '¿Ya tienes una cuenta? ';
@override String get sign_in_link => 'Iniciar sesión';
}
// Path: client_home.dashboard
class _TranslationsClientHomeDashboardEs implements TranslationsClientHomeDashboardEn {
_TranslationsClientHomeDashboardEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get welcome_back => 'Bienvenido de nuevo';
@override String get edit_mode_active => 'Modo Edición Activo';
@override String get drag_instruction => 'Arrastra para reordenar, cambia la visibilidad';
@override String get reset => 'Restablecer';
@override String get metric_needed => 'Necesario';
@override String get metric_filled => 'Lleno';
@override String get metric_open => 'Abierto';
@override String get view_all => 'Ver todo';
@override String insight_lightbulb({required Object amount}) => 'Ahorra ${amount}/mes';
@override String get insight_tip => 'Reserva con 48h de antelación para mejores tarifas';
}
// Path: client_home.widgets
class _TranslationsClientHomeWidgetsEs implements TranslationsClientHomeWidgetsEn {
_TranslationsClientHomeWidgetsEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get actions => 'Acciones Rápidas';
@override String get reorder => 'Reordenar';
@override String get coverage => 'Cobertura de Hoy';
@override String get spending => 'Información de Gastos';
@override String get live_activity => 'Actividad en Vivo';
}
// Path: client_home.actions
class _TranslationsClientHomeActionsEs implements TranslationsClientHomeActionsEn {
_TranslationsClientHomeActionsEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get rapid => 'RÁPIDO';
@override String get rapid_subtitle => 'Urgente mismo día';
@override String get create_order => 'Crear Orden';
@override String get create_order_subtitle => 'Programar turnos';
}
// Path: client_home.reorder
class _TranslationsClientHomeReorderEs implements TranslationsClientHomeReorderEn {
_TranslationsClientHomeReorderEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get title => 'REORDENAR';
@override String get reorder_button => 'Reordenar';
@override String per_hr({required Object amount}) => '${amount}/hr';
}
// Path: client_home.form
class _TranslationsClientHomeFormEs implements TranslationsClientHomeFormEn {
_TranslationsClientHomeFormEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get edit_reorder => 'Editar y Reordenar';
@override String get post_new => 'Publicar un Nuevo Turno';
@override String get review_subtitle => 'Revisa y edita los detalles antes de publicar';
@override String get date_label => 'Fecha *';
@override String get date_hint => 'mm/dd/aaaa';
@override String get location_label => 'Ubicación *';
@override String get location_hint => 'Dirección del negocio';
@override String get positions_title => 'Posiciones';
@override String get add_position => 'Añadir Posición';
@override String get role_label => 'Rol *';
@override String get role_hint => 'Seleccionar rol';
@override String get start_time => 'Hora de Inicio *';
@override String get end_time => 'Hora de Fin *';
@override String get workers_needed => 'Trabajadores Necesarios *';
@override String get hourly_rate => 'Tarifa por hora (\$) *';
@override String get post_shift => 'Publicar Turno';
}
// Path: staff_authentication.profile_setup_page.steps
class _TranslationsStaffAuthenticationProfileSetupPageStepsEs implements TranslationsStaffAuthenticationProfileSetupPageStepsEn {
_TranslationsStaffAuthenticationProfileSetupPageStepsEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get basic => 'Información básica';
@override String get location => 'Ubicación';
@override String get experience => 'Experiencia';
}
// Path: staff_authentication.profile_setup_page.basic_info
class _TranslationsStaffAuthenticationProfileSetupPageBasicInfoEs implements TranslationsStaffAuthenticationProfileSetupPageBasicInfoEn {
_TranslationsStaffAuthenticationProfileSetupPageBasicInfoEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get title => 'Conozcámonos';
@override String get subtitle => 'Cuéntanos un poco sobre ti';
@override String get full_name_label => 'Nombre completo *';
@override String get full_name_hint => 'Juan Pérez';
@override String get bio_label => 'Biografía corta';
@override String get bio_hint => 'Profesional experimentado en hostelería...';
}
// Path: staff_authentication.profile_setup_page.location
class _TranslationsStaffAuthenticationProfileSetupPageLocationEs implements TranslationsStaffAuthenticationProfileSetupPageLocationEn {
_TranslationsStaffAuthenticationProfileSetupPageLocationEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get title => '¿Dónde quieres trabajar?';
@override String get subtitle => 'Agrega tus ubicaciones de trabajo preferidas';
@override String get add_location_label => 'Agregar ubicación *';
@override String get add_location_hint => 'Ciudad o código postal';
@override String get add_button => 'Agregar';
@override String max_distance({required Object distance}) => 'Distancia máxima: ${distance} millas';
@override String get min_dist_label => '5 mi';
@override String get max_dist_label => '50 mi';
}
// Path: staff_authentication.profile_setup_page.experience
class _TranslationsStaffAuthenticationProfileSetupPageExperienceEs implements TranslationsStaffAuthenticationProfileSetupPageExperienceEn {
_TranslationsStaffAuthenticationProfileSetupPageExperienceEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get title => '¿Cuáles son tus habilidades?';
@override String get subtitle => 'Selecciona todas las que correspondan';
@override String get skills_label => 'Habilidades *';
@override String get industries_label => 'Industrias preferidas';
@override late final _TranslationsStaffAuthenticationProfileSetupPageExperienceSkillsEs skills = _TranslationsStaffAuthenticationProfileSetupPageExperienceSkillsEs._(_root);
@override late final _TranslationsStaffAuthenticationProfileSetupPageExperienceIndustriesEs industries = _TranslationsStaffAuthenticationProfileSetupPageExperienceIndustriesEs._(_root);
}
// Path: staff_authentication.profile_setup_page.experience.skills
class _TranslationsStaffAuthenticationProfileSetupPageExperienceSkillsEs implements TranslationsStaffAuthenticationProfileSetupPageExperienceSkillsEn {
_TranslationsStaffAuthenticationProfileSetupPageExperienceSkillsEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get food_service => 'Servicio de comida';
@override String get bartending => 'Preparación de bebidas';
@override String get warehouse => 'Almacén';
@override String get retail => 'Venta minorista';
@override String get events => 'Eventos';
@override String get customer_service => 'Servicio al cliente';
@override String get cleaning => 'Limpieza';
@override String get security => 'Seguridad';
@override String get driving => 'Conducción';
@override String get cooking => 'Cocina';
}
// Path: staff_authentication.profile_setup_page.experience.industries
class _TranslationsStaffAuthenticationProfileSetupPageExperienceIndustriesEs implements TranslationsStaffAuthenticationProfileSetupPageExperienceIndustriesEn {
_TranslationsStaffAuthenticationProfileSetupPageExperienceIndustriesEs._(this._root);
final TranslationsEs _root; // ignore: unused_field
// Translations
@override String get hospitality => 'Hostelería';
@override String get food_service => 'Servicio de comida';
@override String get warehouse => 'Almacén';
@override String get events => 'Eventos';
@override String get retail => 'Venta minorista';
@override String get healthcare => 'Atención médica';
}
/// The flat map containing all translations for locale <es>.
/// Only for edge cases! For simple maps, use the map function of this library.
///
/// The Dart AOT compiler has issues with very large switch statements,
/// so the map is split into smaller functions (512 entries each).
extension on TranslationsEs {
dynamic _flatMapFunction(String path) {
return switch (path) {
'common.ok' => 'Aceptar',
'common.cancel' => 'Cancelar',
'common.save' => 'Guardar',
'common.delete' => 'Eliminar',
'common.continue_text' => 'Continuar',
'settings.language' => 'Idioma',
'settings.change_language' => 'Cambiar Idioma',
'staff_authentication.get_started_page.title_part1' => 'Trabaja, Crece, ',
'staff_authentication.get_started_page.title_part2' => 'Elévate',
'staff_authentication.get_started_page.subtitle' => 'Construye tu carrera en hostelería con \nflexibilidad y libertad.',
'staff_authentication.get_started_page.sign_up_button' => 'Registrarse',
'staff_authentication.get_started_page.log_in_button' => 'Iniciar sesión',
'staff_authentication.phone_verification_page.validation_error' => 'Por favor, ingresa un número de teléfono válido de 10 dígitos',
'staff_authentication.phone_verification_page.send_code_button' => 'Enviar código',
'staff_authentication.phone_verification_page.enter_code_title' => 'Ingresa el código de verificación',
'staff_authentication.phone_verification_page.code_sent_message' => 'Enviamos un código de 6 dígitos a ',
'staff_authentication.phone_verification_page.code_sent_instruction' => '. Ingrésalo a continuación para verificar tu cuenta.',
'staff_authentication.phone_input.title' => 'Verifica tu número de teléfono',
'staff_authentication.phone_input.subtitle' => 'Te enviaremos un código de verificación para comenzar.',
'staff_authentication.phone_input.label' => 'Número de teléfono',
'staff_authentication.phone_input.hint' => 'Ingresa tu número',
'staff_authentication.otp_verification.did_not_get_code' => '¿No recibiste el código?',
'staff_authentication.otp_verification.resend_in' => ({required Object seconds}) => 'Reenviar en ${seconds} s',
'staff_authentication.otp_verification.resend_code' => 'Reenviar código',
'staff_authentication.profile_setup_page.step_indicator' => ({required Object current, required Object total}) => 'Paso ${current} de ${total}',
'staff_authentication.profile_setup_page.error_occurred' => 'Ocurrió un error',
'staff_authentication.profile_setup_page.complete_setup_button' => 'Completar configuración',
'staff_authentication.profile_setup_page.steps.basic' => 'Información básica',
'staff_authentication.profile_setup_page.steps.location' => 'Ubicación',
'staff_authentication.profile_setup_page.steps.experience' => 'Experiencia',
'staff_authentication.profile_setup_page.basic_info.title' => 'Conozcámonos',
'staff_authentication.profile_setup_page.basic_info.subtitle' => 'Cuéntanos un poco sobre ti',
'staff_authentication.profile_setup_page.basic_info.full_name_label' => 'Nombre completo *',
'staff_authentication.profile_setup_page.basic_info.full_name_hint' => 'Juan Pérez',
'staff_authentication.profile_setup_page.basic_info.bio_label' => 'Biografía corta',
'staff_authentication.profile_setup_page.basic_info.bio_hint' => 'Profesional experimentado en hostelería...',
'staff_authentication.profile_setup_page.location.title' => '¿Dónde quieres trabajar?',
'staff_authentication.profile_setup_page.location.subtitle' => 'Agrega tus ubicaciones de trabajo preferidas',
'staff_authentication.profile_setup_page.location.add_location_label' => 'Agregar ubicación *',
'staff_authentication.profile_setup_page.location.add_location_hint' => 'Ciudad o código postal',
'staff_authentication.profile_setup_page.location.add_button' => 'Agregar',
'staff_authentication.profile_setup_page.location.max_distance' => ({required Object distance}) => 'Distancia máxima: ${distance} millas',
'staff_authentication.profile_setup_page.location.min_dist_label' => '5 mi',
'staff_authentication.profile_setup_page.location.max_dist_label' => '50 mi',
'staff_authentication.profile_setup_page.experience.title' => '¿Cuáles son tus habilidades?',
'staff_authentication.profile_setup_page.experience.subtitle' => 'Selecciona todas las que correspondan',
'staff_authentication.profile_setup_page.experience.skills_label' => 'Habilidades *',
'staff_authentication.profile_setup_page.experience.industries_label' => 'Industrias preferidas',
'staff_authentication.profile_setup_page.experience.skills.food_service' => 'Servicio de comida',
'staff_authentication.profile_setup_page.experience.skills.bartending' => 'Preparación de bebidas',
'staff_authentication.profile_setup_page.experience.skills.warehouse' => 'Almacén',
'staff_authentication.profile_setup_page.experience.skills.retail' => 'Venta minorista',
'staff_authentication.profile_setup_page.experience.skills.events' => 'Eventos',
'staff_authentication.profile_setup_page.experience.skills.customer_service' => 'Servicio al cliente',
'staff_authentication.profile_setup_page.experience.skills.cleaning' => 'Limpieza',
'staff_authentication.profile_setup_page.experience.skills.security' => 'Seguridad',
'staff_authentication.profile_setup_page.experience.skills.driving' => 'Conducción',
'staff_authentication.profile_setup_page.experience.skills.cooking' => 'Cocina',
'staff_authentication.profile_setup_page.experience.industries.hospitality' => 'Hostelería',
'staff_authentication.profile_setup_page.experience.industries.food_service' => 'Servicio de comida',
'staff_authentication.profile_setup_page.experience.industries.warehouse' => 'Almacén',
'staff_authentication.profile_setup_page.experience.industries.events' => 'Eventos',
'staff_authentication.profile_setup_page.experience.industries.retail' => 'Venta minorista',
'staff_authentication.profile_setup_page.experience.industries.healthcare' => 'Atención médica',
'staff_authentication.common.trouble_question' => '¿Tienes problemas? ',
'staff_authentication.common.contact_support' => 'Contactar a soporte',
'client_authentication.get_started_page.title' => 'Toma el control de tus\nturnos y eventos',
'client_authentication.get_started_page.subtitle' => 'Optimiza tus operaciones con potentes herramientas para gestionar horarios, realizar un seguimiento del rendimiento y mantener a tu equipo en la misma página, todo en un solo lugar',
'client_authentication.get_started_page.sign_in_button' => 'Iniciar sesión',
'client_authentication.get_started_page.create_account_button' => 'Crear cuenta',
'client_authentication.sign_in_page.title' => 'Bienvenido de nuevo',
'client_authentication.sign_in_page.subtitle' => 'Inicia sesión para gestionar tus turnos y trabajadores',
'client_authentication.sign_in_page.email_label' => 'Correo electrónico',
'client_authentication.sign_in_page.email_hint' => 'Ingresa tu correo electrónico',
'client_authentication.sign_in_page.password_label' => 'Contraseña',
'client_authentication.sign_in_page.password_hint' => 'Ingresa tu contraseña',
'client_authentication.sign_in_page.forgot_password' => '¿Olvidaste tu contraseña?',
'client_authentication.sign_in_page.sign_in_button' => 'Iniciar sesión',
'client_authentication.sign_in_page.or_divider' => 'o',
'client_authentication.sign_in_page.social_apple' => 'Iniciar sesión con Apple',
'client_authentication.sign_in_page.social_google' => 'Iniciar sesión con Google',
'client_authentication.sign_in_page.no_account' => '¿No tienes una cuenta? ',
'client_authentication.sign_in_page.sign_up_link' => 'Regístrate',
'client_authentication.sign_up_page.title' => 'Crear cuenta',
'client_authentication.sign_up_page.subtitle' => 'Comienza con Krow para tu negocio',
'client_authentication.sign_up_page.company_label' => 'Nombre de la empresa',
'client_authentication.sign_up_page.company_hint' => 'Ingresa el nombre de la empresa',
'client_authentication.sign_up_page.email_label' => 'Correo electrónico',
'client_authentication.sign_up_page.email_hint' => 'Ingresa tu correo electrónico',
'client_authentication.sign_up_page.password_label' => 'Contraseña',
'client_authentication.sign_up_page.password_hint' => 'Crea una contraseña',
'client_authentication.sign_up_page.confirm_password_label' => 'Confirmar contraseña',
'client_authentication.sign_up_page.confirm_password_hint' => 'Confirma tu contraseña',
'client_authentication.sign_up_page.create_account_button' => 'Crear cuenta',
'client_authentication.sign_up_page.or_divider' => 'o',
'client_authentication.sign_up_page.social_apple' => 'Regístrate con Apple',
'client_authentication.sign_up_page.social_google' => 'Regístrate con Google',
'client_authentication.sign_up_page.has_account' => '¿Ya tienes una cuenta? ',
'client_authentication.sign_up_page.sign_in_link' => 'Iniciar sesión',
'client_home.shift_created_success' => 'Orden de turno enviada con éxito',
'client_home.dashboard.welcome_back' => 'Bienvenido de nuevo',
'client_home.dashboard.edit_mode_active' => 'Modo Edición Activo',
'client_home.dashboard.drag_instruction' => 'Arrastra para reordenar, cambia la visibilidad',
'client_home.dashboard.reset' => 'Restablecer',
'client_home.dashboard.metric_needed' => 'Necesario',
'client_home.dashboard.metric_filled' => 'Lleno',
'client_home.dashboard.metric_open' => 'Abierto',
'client_home.dashboard.view_all' => 'Ver todo',
'client_home.dashboard.insight_lightbulb' => ({required Object amount}) => 'Ahorra ${amount}/mes',
'client_home.dashboard.insight_tip' => 'Reserva con 48h de antelación para mejores tarifas',
'client_home.widgets.actions' => 'Acciones Rápidas',
'client_home.widgets.reorder' => 'Reordenar',
'client_home.widgets.coverage' => 'Cobertura de Hoy',
'client_home.widgets.spending' => 'Información de Gastos',
'client_home.widgets.live_activity' => 'Actividad en Vivo',
'client_home.actions.rapid' => 'RÁPIDO',
'client_home.actions.rapid_subtitle' => 'Urgente mismo día',
'client_home.actions.create_order' => 'Crear Orden',
'client_home.actions.create_order_subtitle' => 'Programar turnos',
'client_home.reorder.title' => 'REORDENAR',
'client_home.reorder.reorder_button' => 'Reordenar',
'client_home.reorder.per_hr' => ({required Object amount}) => '${amount}/hr',
'client_home.form.edit_reorder' => 'Editar y Reordenar',
'client_home.form.post_new' => 'Publicar un Nuevo Turno',
'client_home.form.review_subtitle' => 'Revisa y edita los detalles antes de publicar',
'client_home.form.date_label' => 'Fecha *',
'client_home.form.date_hint' => 'mm/dd/aaaa',
'client_home.form.location_label' => 'Ubicación *',
'client_home.form.location_hint' => 'Dirección del negocio',
'client_home.form.positions_title' => 'Posiciones',
'client_home.form.add_position' => 'Añadir Posición',
'client_home.form.role_label' => 'Rol *',
'client_home.form.role_hint' => 'Seleccionar rol',
'client_home.form.start_time' => 'Hora de Inicio *',
'client_home.form.end_time' => 'Hora de Fin *',
'client_home.form.workers_needed' => 'Trabajadores Necesarios *',
'client_home.form.hourly_rate' => 'Tarifa por hora (\$) *',
'client_home.form.post_shift' => 'Publicar Turno',
_ => null,
};
}
}

View File

@@ -0,0 +1,46 @@
import 'package:flutter_modular/flutter_modular.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'data/datasources/locale_local_data_source.dart';
import 'data/repositories_impl/locale_repository_impl.dart';
import 'domain/repositories/locale_repository_interface.dart';
import 'domain/usecases/get_locale_use_case.dart';
import 'domain/usecases/set_locale_use_case.dart';
import 'bloc/locale_bloc.dart';
/// A [ModularModule] that manages localization dependencies.
///
/// This module registers all necessary data sources, repositories, use cases,
/// and the BLoC required for application-wide localization management.
class LocalizationModule extends Module {
@override
void binds(Injector i) {
// External Dependencies
i.addInstance<SharedPreferencesAsync>(SharedPreferencesAsync());
// Data Sources
i.addSingleton<LocaleLocalDataSource>(
() => LocaleLocalDataSourceImpl(i.get<SharedPreferencesAsync>()),
);
// Repositories
i.addSingleton<LocaleRepositoryInterface>(
() => LocaleRepositoryImpl(i.get<LocaleLocalDataSource>()),
);
// Use Cases
i.addSingleton<GetLocaleUseCase>(
() => GetLocaleUseCase(i.get<LocaleRepositoryInterface>()),
);
i.addSingleton<SetLocaleUseCase>(
() => SetLocaleUseCase(i.get<LocaleRepositoryInterface>()),
);
// BLoCs
i.addSingleton<LocaleBloc>(
() => LocaleBloc(
getLocaleUseCase: i.get<GetLocaleUseCase>(),
setLocaleUseCase: i.get<SetLocaleUseCase>(),
),
);
}
}

View File

@@ -0,0 +1,38 @@
name: core_localization
description: "Core localization package using Slang."
version: 0.0.1
publish_to: none
resolution: workspace
environment:
sdk: '>=3.10.0 <4.0.0'
flutter: ">=3.0.0"
dependencies:
flutter:
sdk: flutter
krow_core:
path: ../core
flutter_bloc: ^8.1.0
flutter_modular: ^6.3.2
slang: ^4.12.0
slang_flutter: ^4.12.0
shared_preferences: ^2.5.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
build_runner: ^2.4.15
slang_build_runner: ^4.12.0
flutter:
uses-material-design: true
slang:
base_locale: en
fallback_strategy: base_locale
input_directory: lib/src/l10n
input_file_pattern: .i18n.json
output_directory: lib/src/l10n
output_file_name: strings.g.dart

View File

@@ -0,0 +1,12 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:localization/localization.dart';
void main() {
test('adds one to input values', () {
final calculator = Calculator();
expect(calculator.addOne(2), 3);
expect(calculator.addOne(-7), -6);
expect(calculator.addOne(0), 1);
});
}

View File

@@ -0,0 +1,17 @@
/// The Data Connect layer.
///
/// This package provides mock implementations of domain repository interfaces
/// for development and testing purposes.
///
/// TODO: These mocks currently do not implement any specific interfaces.
/// They will implement interfaces defined in feature packages once those are created.
export 'src/mocks/auth_repository_mock.dart';
export 'src/mocks/staff_repository_mock.dart';
export 'src/mocks/business_repository_mock.dart';
export 'src/mocks/event_repository_mock.dart';
export 'src/mocks/skill_repository_mock.dart';
export 'src/mocks/financial_repository_mock.dart';
export 'src/mocks/rating_repository_mock.dart';
export 'src/mocks/support_repository_mock.dart';
export 'src/data_connect_module.dart';

View File

@@ -0,0 +1,11 @@
import 'package:flutter_modular/flutter_modular.dart';
import 'mocks/auth_repository_mock.dart';
/// A module that provides Data Connect dependencies, including mocks.
class DataConnectModule extends Module {
@override
void exportedBinds(Injector i) {
// Make the AuthRepositoryMock available to any module that imports this one.
i.addLazySingleton(AuthRepositoryMock.new);
}
}

View File

@@ -0,0 +1,48 @@
import 'package:krow_domain/krow_domain.dart';
// TODO: Implement AuthRepositoryInterface once defined in a feature package.
class AuthRepositoryMock {
Stream<User?> get currentUser => Stream.value(
const User(id: 'mock_user_1', email: 'test@krow.com', role: 'staff'),
);
Future<String?> signInWithPhone(String phoneNumber) async {
await Future.delayed(const Duration(milliseconds: 500));
return 'mock_verification_id';
}
Future<User?> verifyOtp(String verificationId, String smsCode) async {
await Future.delayed(const Duration(milliseconds: 500));
return const User(id: 'mock_user_1', email: 'test@krow.com', role: 'staff');
}
Future<void> signOut() async {
await Future.delayed(const Duration(milliseconds: 200));
}
/// Signs in a user with email and password (Mock).
Future<User> signInWithEmail(String email, String password) async {
await Future.delayed(const Duration(milliseconds: 500));
return User(id: 'mock_client_1', email: email, role: 'client_admin');
}
/// Registers a new user with email and password (Mock).
Future<User> signUpWithEmail(
String email,
String password,
String companyName,
) async {
await Future.delayed(const Duration(milliseconds: 500));
return User(id: 'mock_client_new', email: email, role: 'client_admin');
}
/// Authenticates using a social provider (Mock).
Future<User> signInWithSocial(String provider) async {
await Future.delayed(const Duration(milliseconds: 500));
return const User(
id: 'mock_social_user',
email: 'social@example.com',
role: 'client_admin',
);
}
}

View File

@@ -0,0 +1,28 @@
import 'package:krow_domain/krow_domain.dart';
// TODO: Implement BusinessRepositoryInterface once defined in a feature package.
class BusinessRepositoryMock {
Future<Business?> getBusiness(String id) async {
await Future.delayed(const Duration(milliseconds: 300));
return const Business(
id: 'biz_1',
name: 'Acme Events Ltd',
registrationNumber: 'REG123456',
status: BusinessStatus.active,
avatar: 'https://via.placeholder.com/150',
);
}
Future<List<Hub>> getHubs(String businessId) async {
await Future.delayed(const Duration(milliseconds: 300));
return [
const Hub(
id: 'hub_1',
businessId: 'biz_1',
name: 'London HQ',
address: '123 Oxford Street, London',
status: HubStatus.active,
),
];
}
}

View File

@@ -0,0 +1,58 @@
import 'package:krow_domain/krow_domain.dart';
// TODO: Implement EventRepositoryInterface once defined in a feature package.
class EventRepositoryMock {
Future<Assignment> applyForPosition(String positionId, String staffId) async {
await Future.delayed(const Duration(milliseconds: 600));
return Assignment(
id: 'assign_1',
positionId: positionId,
staffId: staffId,
status: AssignmentStatus.assigned,
);
}
Future<Event?> getEvent(String id) async {
await Future.delayed(const Duration(milliseconds: 300));
return _mockEvent;
}
Future<List<EventShift>> getEventShifts(String eventId) async {
await Future.delayed(const Duration(milliseconds: 300));
return [
const EventShift(
id: 'shift_1',
eventId: 'event_1',
name: 'Morning Setup',
address: 'Hyde Park, London',
),
];
}
Future<List<Assignment>> getStaffAssignments(String staffId) async {
await Future.delayed(const Duration(milliseconds: 500));
return [
const Assignment(
id: 'assign_1',
positionId: 'pos_1',
staffId: 'staff_1',
status: AssignmentStatus.confirmed,
),
];
}
Future<List<Event>> getUpcomingEvents() async {
await Future.delayed(const Duration(milliseconds: 800));
return [_mockEvent];
}
static final _mockEvent = Event(
id: 'event_1',
businessId: 'biz_1',
hubId: 'hub_1',
name: 'Summer Festival 2026',
date: DateTime.now().add(const Duration(days: 10)),
status: EventStatus.active,
contractType: 'freelance',
);
}

View File

@@ -0,0 +1,33 @@
import 'package:krow_domain/krow_domain.dart';
// TODO: Implement FinancialRepositoryInterface once defined in a feature package.
class FinancialRepositoryMock {
Future<List<Invoice>> getInvoices(String businessId) async {
await Future.delayed(const Duration(milliseconds: 500));
return [
const Invoice(
id: 'inv_1',
eventId: 'event_1',
businessId: 'biz_1',
status: InvoiceStatus.paid,
totalAmount: 1500.0,
workAmount: 1400.0,
addonsAmount: 100.0,
),
];
}
Future<List<StaffPayment>> getStaffPayments(String staffId) async {
await Future.delayed(const Duration(milliseconds: 500));
return [
StaffPayment(
id: 'pay_1',
staffId: staffId,
assignmentId: 'assign_1',
amount: 120.0,
status: PaymentStatus.paid,
paidAt: DateTime.now().subtract(const Duration(days: 2)),
),
];
}
}

View File

@@ -0,0 +1,22 @@
import 'package:krow_domain/krow_domain.dart';
// TODO: Implement RatingRepositoryInterface once defined in a feature package.
class RatingRepositoryMock {
Future<List<StaffRating>> getStaffRatings(String staffId) async {
await Future.delayed(const Duration(milliseconds: 400));
return [
const StaffRating(
id: 'rate_1',
staffId: 'staff_1',
eventId: 'event_1',
businessId: 'biz_1',
rating: 5,
comment: 'Great work!',
),
];
}
Future<void> submitRating(StaffRating rating) async {
await Future.delayed(const Duration(milliseconds: 500));
}
}

View File

@@ -0,0 +1,40 @@
import 'package:krow_domain/krow_domain.dart';
// TODO: Implement SkillRepositoryInterface once defined in a feature package.
class SkillRepositoryMock {
Future<void> addStaffSkill(StaffSkill skill) async {
await Future.delayed(const Duration(milliseconds: 500));
}
Future<List<Skill>> getAllSkills() async {
await Future.delayed(const Duration(milliseconds: 300));
return [
const Skill(
id: 'skill_1',
categoryId: 'cat_1',
name: 'Bartender',
basePrice: 15.0,
),
const Skill(
id: 'skill_2',
categoryId: 'cat_2',
name: 'Security Guard',
basePrice: 18.0,
),
];
}
Future<List<StaffSkill>> getStaffSkills(String staffId) async {
await Future.delayed(const Duration(milliseconds: 400));
return [
const StaffSkill(
id: 'staff_skill_1',
staffId: 'staff_1',
skillId: 'skill_1',
level: SkillLevel.skilled,
experienceYears: 3,
status: StaffSkillStatus.verified,
),
];
}
}

View File

@@ -0,0 +1,39 @@
import 'package:krow_domain/krow_domain.dart';
// TODO: Implement StaffRepositoryInterface once defined in a feature package.
class StaffRepositoryMock {
Future<Staff> createStaffProfile(Staff staff) async {
await Future.delayed(const Duration(milliseconds: 500));
return staff;
}
Future<List<Membership>> getMemberships(String userId) async {
await Future.delayed(const Duration(milliseconds: 300));
return [
Membership(
id: 'mem_1',
userId: userId,
memberableId: 'biz_1',
memberableType: 'business',
role: 'staff',
),
];
}
Future<Staff?> getStaffProfile(String userId) async {
await Future.delayed(const Duration(milliseconds: 400));
return Staff(
id: 'staff_1',
authProviderId: userId,
name: 'John Doe',
email: 'john@krow.com',
status: StaffStatus.active,
avatar: 'https://i.pravatar.cc/300',
);
}
Future<Staff> updateStaffProfile(Staff staff) async {
await Future.delayed(const Duration(milliseconds: 500));
return staff;
}
}

View File

@@ -0,0 +1,25 @@
import 'package:krow_domain/krow_domain.dart';
// TODO: Implement SupportRepositoryInterface once defined in a feature package.
class SupportRepositoryMock {
Future<List<Tag>> getTags() async {
await Future.delayed(const Duration(milliseconds: 200));
return [
const Tag(id: 'tag_1', label: 'Urgent'),
const Tag(id: 'tag_2', label: 'VIP Event'),
];
}
Future<List<WorkingArea>> getWorkingAreas() async {
await Future.delayed(const Duration(milliseconds: 200));
return [
const WorkingArea(
id: 'area_1',
name: 'Central London',
centerLat: 51.5074,
centerLng: -0.1278,
radiusKm: 10.0,
),
];
}
}

View File

@@ -0,0 +1,16 @@
name: krow_data_connect
description: Firebase Data Connect access layer.
version: 0.0.1
publish_to: none
resolution: workspace
environment:
sdk: '>=3.10.0 <4.0.0'
flutter: ">=3.0.0"
dependencies:
flutter:
sdk: flutter
krow_domain:
path: ../domain
flutter_modular: ^6.3.0

31
apps/packages/design_system/.gitignore vendored Normal file
View File

@@ -0,0 +1,31 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
.flutter-plugins-dependencies
/build/
/coverage/

View File

@@ -0,0 +1,10 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "3b62efc2a3da49882f43c372e0bc53daef7295a6"
channel: "stable"
project_type: package

View File

@@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

View File

@@ -0,0 +1 @@
TODO: Add your license here.

View File

@@ -0,0 +1,39 @@
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/to/develop-packages).
-->
TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
## Features
TODO: List what your package can do. Maybe include images, gifs, or videos.
## Getting started
TODO: List prerequisites and provide or point to information on how to
start using the package.
## Usage
TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
```dart
const like = 'sample';
```
## Additional information
TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.

View File

@@ -0,0 +1 @@
include: ../../analytics_options.yaml

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

@@ -0,0 +1,12 @@
export 'src/ui_colors.dart';
export 'src/ui_typography.dart';
export 'src/ui_constants.dart';
export 'src/ui_theme.dart';
export 'src/ui_icons.dart';
export 'src/ui_images_assets.dart';
export 'src/widgets/ui_app_bar.dart';
export 'src/widgets/ui_text_field.dart';
export 'src/widgets/ui_step_indicator.dart';
export 'src/widgets/ui_icon_button.dart';
export 'src/widgets/ui_button.dart';
export 'src/widgets/ui_chip.dart';

View File

@@ -0,0 +1,322 @@
import 'package:flutter/material.dart';
/// Static definitions of color palettes and semantic colors for the Staff Design System.
/// Values are defined in design_tokens_react.md.
class UiColors {
UiColors._();
// ---------------------------------------------------------------------------
// 1. Base Tokens
// ---------------------------------------------------------------------------
/// Background color (#FAFBFC)
static const Color background = Color(0xFFFAFBFC);
/// Foreground color (#121826)
static const Color foreground = Color(0xFF121826);
/// Primary brand color blue (#0A39DF)
static const Color primary = Color(0xFF0A39DF);
/// Foreground color on primary background (#F7FAFC)
static const Color primaryForeground = Color(0xFFF7FAFC);
/// Inverse primary color (#9FABF1)
static const Color primaryInverse = Color(0xFF9FABF1);
/// Secondary background color (#F1F3F5)
static const Color secondary = Color(0xFFF1F3F5);
/// Foreground color on secondary background (#121826)
static const Color secondaryForeground = Color(0xFF121826);
/// Muted background color (#F1F3F5)
static const Color muted = Color(0xFFF1F3F5);
/// Muted foreground color (#6A7382)
static const Color mutedForeground = Color(0xFF6A7382);
/// Accent yellow color (#F9E547)
static const Color accent = Color(0xFFF9E547);
/// Foreground color on accent background (#4C460D)
static const Color accentForeground = Color(0xFF4C460D);
/// Destructive red color (#F04444)
static const Color destructive = Color(0xFFF04444);
/// Foreground color on destructive background (#FAFAFA)
static const Color destructiveForeground = Color(0xFFFAFAFA);
/// Default border color (#D1D5DB)
static const Color border = Color(0xFFD1D5DB);
/// Default input border color (#E7EAEE)
static const Color input = Color(0xFFF5F6F8);
/// Focus ring color (#0A39DF)
static const Color ring = Color(0xFF0A39DF);
// ---------------------------------------------------------------------------
// 2. Semantic Mappings
// ---------------------------------------------------------------------------
// --- Background Colors ---
/// Primary background (#FAFBFC)
static const Color bgPrimary = background;
/// Secondary background (#F1F3F5)
static const Color bgSecondary = secondary;
/// Tertiary background (#EDF0F2)
static const Color bgThird = Color(0xFFEDF0F2);
/// Popup background (#FFFFFF)
static const Color bgPopup = Color(0xFFFFFFFF);
/// Highlighted background (#FEF9C3)
static const Color bgHighlight = Color(0xFFFEF9C3);
/// Menu background (#F8FAFC)
static const Color bgMenu = Color(0xFFF8FAFC);
/// Banner background (#FFFFFF)
static const Color bgBanner = Color(0xFFFFFFFF);
/// Overlay background (#000000 with 50% opacity)
static const Color bgOverlay = Color(0x80000000);
/// Toast background (#121826)
static const Color toastBg = Color(0xFF121826);
/// Input field background (#E3E6E9)
static const Color bgInputField = input;
/// Footer banner background (#F1F5F9)
static const Color bgFooterBanner = Color(0xFFF1F5F9);
// --- Text Colors ---
/// Primary text (#121826)
static const Color textPrimary = foreground;
/// Secondary text (#6A7382)
static const Color textSecondary = mutedForeground;
/// Inactive text (#9CA3AF)
static const Color textInactive = Color(0xFF9CA3AF);
/// Placeholder text (#9CA3AF)
static const Color textPlaceholder = Color(0xFF9CA3AF);
/// Description text (#6A7382)
static const Color textDescription = mutedForeground;
/// Success text (#10B981)
static const Color textSuccess = Color(0xFF10B981);
/// Error text (#F04444)
static const Color textError = destructive;
/// Deep error text for containers (#450A0A)
static const Color textErrorContainer = Color(0xFF450A0A);
/// Warning text (#D97706)
static const Color textWarning = Color(0xFFD97706);
/// Link text (#0A39DF)
static const Color textLink = primary;
/// Filter text (#4B5563)
static const Color textFilter = Color(0xFF4B5563);
// --- Icon Colors ---
/// Primary icon (#121826)
static const Color iconPrimary = foreground;
/// Secondary icon (#6A7382)
static const Color iconSecondary = mutedForeground;
/// Tertiary icon (#9CA3AF)
static const Color iconThird = Color(0xFF9CA3AF);
/// Inactive icon (#D1D5DB)
static const Color iconInactive = Color(0xFFD1D5DB);
/// Active icon (#0A39DF)
static const Color iconActive = primary;
/// Success icon (#10B981)
static const Color iconSuccess = Color(0xFF10B981);
/// Error icon (#F04444)
static const Color iconError = destructive;
// --- Loader Colors ---
/// Active loader (#0A39DF)
static const Color loaderActive = primary;
/// Inactive loader (#E2E8F0)
static const Color loaderInactive = Color(0xFFE2E8F0);
// --- Pin Input Colors ---
/// Unfilled pin (#E2E8F0)
static const Color pinUnfilled = Color(0xFFE2E8F0);
/// Active pin (#0A39DF)
static const Color pinActive = primary;
/// Inactive pin (#94A3B8)
static const Color pinInactive = Color(0xFF94A3B8);
// --- Separator Colors ---
/// Primary separator (#E3E6E9)
static const Color separatorPrimary = border;
/// Secondary separator (#F1F5F9)
static const Color separatorSecondary = Color(0xFFF1F5F9);
/// Special separator (#F9E547)
static const Color separatorSpecial = accent;
// --- Tag Colors ---
/// Default tag background (#F1F5F9)
static const Color tagValue = Color(0xFFF1F5F9);
/// Pending state tag background (#FEF3C7)
static const Color tagPending = Color(0xFFFEF3C7);
/// In-progress state tag background (#DBEAFE)
static const Color tagInProgress = Color(0xFFDBEAFE);
/// Error state tag background (#FEE2E2)
static const Color tagError = Color(0xFFFEE2E2);
/// Active state tag background (#DCFCE7)
static const Color tagActive = Color(0xFFDCFCE7);
/// Frozen state tag background (#F3F4F6)
static const Color tagFreeze = Color(0xFFF3F4F6);
/// Success state tag background (#DCFCE7)
static const Color tagSuccess = Color(0xFFDCFCE7);
/// Refunded state tag background (#E0E7FF)
static const Color tagRefunded = Color(0xFFE0E7FF);
// --- Border Colors ---
/// Static border (#D1D5DB)
static const Color borderStill = border;
/// Primary border (#D1D5DB)
static const Color borderPrimary = border;
/// Error border (#F04444)
static const Color borderError = destructive;
/// Focus border (#0A39DF)
static const Color borderFocus = ring;
/// Inactive border (#F1F5F9)
static const Color borderInactive = Color(0xFFF1F5F9);
// --- Button Colors ---
/// Primary button default (#0A39DF)
static const Color buttonPrimaryStill = primary;
/// Primary button hover (#082EB2)
static const Color buttonPrimaryHover = Color(0xFF082EB2);
/// Primary button inactive (#F1F3F5)
static const Color buttonPrimaryInactive = secondary;
/// Secondary button default (#F1F3F5)
static const Color buttonSecondaryStill = secondary;
/// Secondary button hover (#E2E8F0)
static const Color buttonSecondaryHover = Color(0xFFE2E8F0);
/// Secondary button inactive (#F3F4F6)
static const Color buttonSecondaryInactive = Color(0xFFF3F4F6);
/// Button inactive state (#94A3B8)
static const Color buttonInactive = Color(0xFF94A3B8);
/// Pin button background (#F8FAFC)
static const Color pinButtonBackground = Color(0xFFF8FAFC);
// --- Switch Colors ---
/// Switch active state (#10B981)
static const Color switchActive = Color(0xFF10B981);
/// Switch inactive state (#CBD5E1)
static const Color switchInactive = Color(0xFFCBD5E1);
/// Switch dot inactive state (#FFFFFF)
static const Color dotInactive = Color(0xFFFFFFFF);
// --- Basic Colors ---
/// Standard white (#FFFFFF)
static const Color white = Color(0xFFFFFFFF);
/// Standard black (#000000)
static const Color black = Color(0xFF000000);
/// Transparent color (0x00000000)
static const Color transparent = Color(0x00000000);
/// Card background (#FFFFFF)
static const Color cardViewBackground = Color(0xFFFFFFFF);
// --- Shadows ---
/// Primary popup shadow (#000000 with 10% opacity)
static const Color popupShadow = Color(0x1A000000);
// ---------------------------------------------------------------------------
// 3. ColorScheme
// ---------------------------------------------------------------------------
/// Generates a ColorScheme based on the tokens.
static ColorScheme get colorScheme => const ColorScheme(
brightness: Brightness.light,
primary: primary,
onPrimary: primaryForeground,
primaryContainer: tagRefunded,
onPrimaryContainer: primary,
secondary: secondary,
onSecondary: secondaryForeground,
secondaryContainer: muted,
onSecondaryContainer: secondaryForeground,
tertiary: accent,
onTertiary: accentForeground,
tertiaryContainer: bgHighlight,
onTertiaryContainer: accentForeground,
error: destructive,
onError: destructiveForeground,
errorContainer: tagError,
onErrorContainer: textErrorContainer,
surface: background,
onSurface: foreground,
surfaceContainerHighest: muted,
onSurfaceVariant: mutedForeground,
outline: border,
outlineVariant: separatorSecondary,
shadow: black,
scrim: black,
inverseSurface: foreground,
onInverseSurface: background,
inversePrimary: primaryInverse,
surfaceTint: primary,
);
}

View File

@@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
/// Design system constants for spacing, radii, and other layout properties.
class UiConstants {
UiConstants._();
// --- Border Radii ---
/// Base radius: 12px
static const double radiusBase = 12.0;
static final BorderRadius radiusLg = BorderRadius.circular(radiusBase);
/// Medium radius: 6px
static const double radiusMdValue = 6.0;
static final BorderRadius radiusMd = BorderRadius.circular(radiusMdValue);
/// Small radius: 4px
static final BorderRadius radiusSm = BorderRadius.circular(4.0);
/// Extra small radius: 2px
static final BorderRadius radiusXs = BorderRadius.circular(2.0);
/// Large/Full radius
static final BorderRadius radiusFull = BorderRadius.circular(999.0);
// --- Spacing ---
static const double space0 = 0.0;
static const double space1 = 4.0;
static const double space2 = 8.0;
static const double space3 = 12.0;
static const double space4 = 16.0;
static const double space5 = 20.0;
static const double space6 = 24.0;
static const double space8 = 32.0;
static const double space10 = 40.0;
static const double space12 = 48.0;
}

View File

@@ -0,0 +1,174 @@
import 'package:flutter/widgets.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:lucide_icons/lucide_icons.dart';
/// The primary icon library used by the design system.
/// This allows for easier swapping of icon libraries in the future.
typedef _IconLib = LucideIcons;
/// The secondary icon library used by the design system.
/// This allows for easier swapping of icon libraries in the future.
typedef _IconLib2 = FontAwesomeIcons;
/// Static definitions of icons for the UI design system.
/// This class wraps the primary icon library to provide a consistent interface.
///
/// example:
/// ```dart
/// Icon(UiIcons.home)
/// ```
class UiIcons {
UiIcons._();
// --- Navigation ---
/// Home icon
static const IconData home = _IconLib.home;
/// Calendar icon for shifts or schedules
static const IconData calendar = _IconLib.calendar;
/// Briefcase icon for jobs
static const IconData briefcase = _IconLib.briefcase;
/// User icon for profile
static const IconData user = _IconLib.user;
/// Settings icon
static const IconData settings = _IconLib.settings;
// --- Actions ---
/// Search icon
static const IconData search = _IconLib.search;
/// Filter icon
static const IconData filter = _IconLib.filter;
/// Plus/Add icon
static const IconData add = _IconLib.plus;
/// Edit icon
static const IconData edit = _IconLib.edit2;
/// Delete/Trash icon
static const IconData delete = _IconLib.trash2;
/// Checkmark icon
static const IconData check = _IconLib.check;
/// X/Cancel icon
static const IconData close = _IconLib.x;
/// Arrow right icon
static const IconData arrowRight = _IconLib.arrowRight;
/// Arrow left icon
static const IconData arrowLeft = _IconLib.arrowLeft;
/// Swap/Transfer icon
static const IconData swap = _IconLib.arrowLeftRight;
/// Chevron right icon
static const IconData chevronRight = _IconLib.chevronRight;
/// Chevron left icon
static const IconData chevronLeft = _IconLib.chevronLeft;
// --- Status & Feedback ---
/// Info icon
static const IconData info = _IconLib.info;
/// Help/Circle icon
static const IconData help = _IconLib.helpCircle;
/// Alert/Triangle icon for warnings
static const IconData warning = _IconLib.alertTriangle;
/// Alert/Circle icon for errors
static const IconData error = _IconLib.alertCircle;
/// Success/Check circle icon
static const IconData success = _IconLib.checkCircle2;
// --- Miscellaneous ---
/// Clock icon
static const IconData clock = _IconLib.clock;
/// Log in icon
static const IconData logIn = _IconLib.logIn;
/// Break icon (Coffee)
static const IconData breakIcon = _IconLib.coffee;
/// Map pin icon for locations
static const IconData mapPin = _IconLib.mapPin;
/// Dollar sign icon for payments/earnings
static const IconData dollar = _IconLib.dollarSign;
/// Wallet icon
static const IconData wallet = _IconLib.wallet;
/// Credit card icon
static const IconData creditCard = _IconLib.creditCard;
/// Bell icon for notifications
static const IconData bell = _IconLib.bell;
/// Log out icon
static const IconData logOut = _IconLib.logOut;
/// File/Document icon
static const IconData file = _IconLib.fileText;
/// Lock icon
static const IconData lock = _IconLib.lock;
/// Shield check icon for compliance/security
static const IconData shield = _IconLib.shieldCheck;
/// Sparkles icon for features or AI
static const IconData sparkles = _IconLib.sparkles;
/// Star icon for ratings
static const IconData star = _IconLib.star;
/// Camera icon for photo upload
static const IconData camera = _IconLib.camera;
/// Mail icon
static const IconData mail = _IconLib.mail;
/// Eye icon for visibility
static const IconData eye = _IconLib.eye;
/// Eye off icon for hidden visibility
static const IconData eyeOff = _IconLib.eyeOff;
/// Building icon for companies
static const IconData building = _IconLib.building2;
/// Zap icon for rapid actions
static const IconData zap = _IconLib.zap;
/// Grip vertical icon for reordering
static const IconData gripVertical = _IconLib.gripVertical;
/// Trending down icon for insights
static const IconData trendingDown = _IconLib.trendingDown;
/// Target icon for metrics
static const IconData target = _IconLib.target;
/// Rotate CCW icon for reordering
static const IconData rotateCcw = _IconLib.rotateCcw;
/// Apple icon
static const IconData apple = _IconLib2.apple;
/// Google icon
static const IconData google = _IconLib2.google;
}

View File

@@ -0,0 +1,14 @@
/// Static definitions of image asset paths for the Design System.
///
/// This class provides a centralized way to access image assets
/// stored within the `design_system` package.
class UiImageAssets {
UiImageAssets._();
/// The path to the yellow version of the logo image.
static const String logoYellow =
'packages/design_system/assets/logo-yellow.png';
/// The path to the blue version of the logo image.
static const String logoBlue = 'packages/design_system/assets/logo-blue.png';
}

View File

@@ -0,0 +1,359 @@
import 'package:flutter/material.dart';
import 'ui_colors.dart';
import 'ui_typography.dart';
import 'ui_constants.dart';
/// The main entry point for the Staff Design System theme.
/// Assembles colors, typography, and constants into a comprehensive Material 3 theme.
///
/// Adheres to the tokens defined in design_tokens_react.md.
class UiTheme {
UiTheme._();
/// Returns the light theme for the Staff application.
static ThemeData get light {
final colorScheme = UiColors.colorScheme;
final textTheme = UiTypography.textTheme;
return ThemeData(
useMaterial3: true,
colorScheme: colorScheme,
scaffoldBackgroundColor: UiColors.background,
primaryColor: UiColors.primary,
canvasColor: UiColors.background,
// Typography
textTheme: textTheme,
// Icon Theme
iconTheme: const IconThemeData(color: UiColors.iconPrimary, size: 24),
// Text Selection Theme
textSelectionTheme: const TextSelectionThemeData(
cursorColor: UiColors.primary,
selectionColor: UiColors.primaryInverse,
selectionHandleColor: UiColors.primary,
),
// Divider Theme
dividerTheme: const DividerThemeData(
color: UiColors.separatorPrimary,
space: 1,
thickness: 1,
),
// Card Theme
cardTheme: CardThemeData(
color: UiColors.white,
elevation: 2,
shadowColor: UiColors.popupShadow,
shape: RoundedRectangleBorder(
borderRadius: UiConstants.radiusLg,
side: const BorderSide(color: UiColors.borderStill),
),
margin: EdgeInsets.zero,
),
// Elevated Button Theme (Primary)
elevatedButtonTheme: ElevatedButtonThemeData(
style:
ElevatedButton.styleFrom(
elevation: 0,
backgroundColor: UiColors.buttonPrimaryStill,
foregroundColor: UiColors.primaryForeground,
disabledBackgroundColor: UiColors.buttonPrimaryInactive,
textStyle: UiTypography.buttonXL,
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusLg),
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space6,
vertical: UiConstants.space3,
),
minimumSize: const Size(double.infinity, 54),
maximumSize: const Size(double.infinity, 54),
).copyWith(
side: WidgetStateProperty.resolveWith<BorderSide?>((states) {
if (states.contains(WidgetState.disabled)) {
return const BorderSide(
color: UiColors.borderPrimary,
width: 0.5,
);
}
return null;
}),
overlayColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.hovered))
return UiColors.buttonPrimaryHover;
return null;
}),
),
),
// Text Button Theme
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: UiColors.textPrimary,
disabledForegroundColor: UiColors.textInactive,
textStyle: UiTypography.buttonXL,
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusLg),
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space4,
vertical: UiConstants.space2,
),
minimumSize: const Size(double.infinity, 52),
maximumSize: const Size(double.infinity, 52),
),
),
// Outlined Button Theme (Secondary)
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
elevation: 0,
backgroundColor: UiColors.buttonSecondaryStill,
foregroundColor: UiColors.primary,
side: const BorderSide(color: UiColors.borderFocus, width: 0.5),
textStyle: UiTypography.buttonXL,
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusLg),
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space4,
vertical: UiConstants.space3,
),
minimumSize: const Size(double.infinity, 52),
maximumSize: const Size(double.infinity, 52),
),
),
// Icon Button Theme
iconButtonTheme: IconButtonThemeData(
style: IconButton.styleFrom(
foregroundColor: UiColors.iconPrimary,
disabledForegroundColor: UiColors.iconInactive,
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusFull),
),
),
// Floating Action Button Theme
floatingActionButtonTheme: const FloatingActionButtonThemeData(
backgroundColor: UiColors.primary,
foregroundColor: UiColors.primaryForeground,
elevation: 4,
shape: CircleBorder(),
),
// Tab Bar Theme
tabBarTheme: TabBarThemeData(
labelColor: UiColors.primary,
unselectedLabelColor: UiColors.textSecondary,
labelStyle: UiTypography.buttonM,
unselectedLabelStyle: UiTypography.buttonM,
indicatorSize: TabBarIndicatorSize.label,
indicator: const UnderlineTabIndicator(
borderSide: BorderSide(color: UiColors.primary, width: 2),
),
),
// Input Theme
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: UiColors.bgInputField,
hintStyle: UiTypography.body2r.textPlaceholder,
labelStyle: UiTypography.body4r.textPrimary,
errorStyle: UiTypography.footnote1r.textError,
contentPadding: const EdgeInsets.symmetric(
horizontal: UiConstants.space3,
vertical: UiConstants.space3,
),
border: OutlineInputBorder(
borderRadius: UiConstants.radiusMd,
borderSide: const BorderSide(color: UiColors.borderStill),
),
enabledBorder: OutlineInputBorder(
borderRadius: UiConstants.radiusMd,
borderSide: const BorderSide(color: UiColors.borderStill),
),
focusedBorder: OutlineInputBorder(
borderRadius: UiConstants.radiusMd,
borderSide: const BorderSide(
color: UiColors.borderFocus,
width: 0.75,
),
),
errorBorder: OutlineInputBorder(
borderRadius: UiConstants.radiusMd,
borderSide: const BorderSide(color: UiColors.textError),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: UiConstants.radiusMd,
borderSide: const BorderSide(color: UiColors.textError, width: 1),
),
),
// List Tile Theme
listTileTheme: ListTileThemeData(
textColor: UiColors.textPrimary,
iconColor: UiColors.iconPrimary,
titleTextStyle: UiTypography.body1m,
subtitleTextStyle: UiTypography.body2r.textSecondary,
contentPadding: const EdgeInsets.symmetric(
horizontal: UiConstants.space4,
),
tileColor: UiColors.transparent,
),
// Badge Theme
badgeTheme: BadgeThemeData(
backgroundColor: UiColors.primary,
textColor: UiColors.primaryForeground,
textStyle: UiTypography.footnote2m,
padding: const EdgeInsets.symmetric(horizontal: 4),
),
// App Bar Theme
appBarTheme: AppBarTheme(
backgroundColor: UiColors.background,
elevation: 0,
titleTextStyle: UiTypography.headline5m.textPrimary,
iconTheme: const IconThemeData(color: UiColors.iconThird, size: 20),
surfaceTintColor: UiColors.transparent,
),
// Dialog Theme
dialogTheme: DialogThemeData(
backgroundColor: UiColors.bgPopup,
elevation: 8,
shadowColor: UiColors.popupShadow,
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusLg),
titleTextStyle: UiTypography.headline2r.textPrimary,
contentTextStyle: UiTypography.body2r.textDescription,
),
// Bottom Navigation Bar Theme
bottomNavigationBarTheme: BottomNavigationBarThemeData(
backgroundColor: UiColors.white,
selectedItemColor: UiColors.primary,
unselectedItemColor: UiColors.textInactive,
selectedLabelStyle: UiTypography.footnote2m,
unselectedLabelStyle: UiTypography.footnote2r,
type: BottomNavigationBarType.fixed,
elevation: 8,
),
// Navigation Bar Theme (Modern M3 Bottom Nav)
navigationBarTheme: NavigationBarThemeData(
backgroundColor: UiColors.white,
indicatorColor: UiColors.primaryInverse.withAlpha(51), // 20% of 255
labelTextStyle: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return UiTypography.footnote2m.textPrimary;
}
return UiTypography.footnote2r.textInactive;
}),
),
// Switch Theme
switchTheme: SwitchThemeData(
trackColor: WidgetStateProperty.resolveWith<Color>((states) {
if (states.contains(WidgetState.selected)) {
return UiColors.switchActive;
}
return UiColors.switchInactive;
}),
thumbColor: const WidgetStatePropertyAll(UiColors.white),
),
// Checkbox Theme
checkboxTheme: CheckboxThemeData(
fillColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) return UiColors.primary;
return null;
}),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)),
),
// Radio Theme
radioTheme: RadioThemeData(
fillColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) return UiColors.primary;
return null;
}),
),
// Slider Theme
sliderTheme: const SliderThemeData(
activeTrackColor: UiColors.primary,
inactiveTrackColor: UiColors.loaderInactive,
thumbColor: UiColors.primary,
overlayColor: UiColors.primaryInverse,
),
// Chip Theme
chipTheme: ChipThemeData(
backgroundColor: UiColors.bgSecondary,
labelStyle: UiTypography.footnote1m,
secondaryLabelStyle: UiTypography.footnote1m.white,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusMd),
side: const BorderSide(color: UiColors.borderStill, width: 0.5),
),
// SnackBar Theme
snackBarTheme: SnackBarThemeData(
backgroundColor: UiColors.toastBg,
contentTextStyle: UiTypography.body2r.white,
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusMd),
elevation: 4,
),
// Bottom Sheet Theme
bottomSheetTheme: const BottomSheetThemeData(
backgroundColor: UiColors.bgSecondary,
modalBackgroundColor: UiColors.bgSecondary,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(32)),
),
),
// Expansion Tile Theme
expansionTileTheme: ExpansionTileThemeData(
iconColor: UiColors.iconSecondary,
collapsedIconColor: UiColors.iconPrimary,
backgroundColor: UiColors.bgPopup,
collapsedBackgroundColor: UiColors.transparent,
textColor: UiColors.textPrimary,
collapsedTextColor: UiColors.textPrimary,
tilePadding: const EdgeInsets.symmetric(horizontal: UiConstants.space4),
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusMd),
collapsedShape: RoundedRectangleBorder(
borderRadius: UiConstants.radiusMd,
),
),
// Menu Theme
menuTheme: MenuThemeData(
style: MenuStyle(
backgroundColor: WidgetStateProperty.all(UiColors.bgPopup),
elevation: WidgetStateProperty.all(4),
shape: WidgetStateProperty.all(
RoundedRectangleBorder(borderRadius: UiConstants.radiusMd),
),
),
),
// Tooltip Theme
tooltipTheme: TooltipThemeData(
decoration: BoxDecoration(
color: UiColors.toastBg.withAlpha(230), // ~90% of 255
borderRadius: UiConstants.radiusMd,
),
textStyle: UiTypography.footnote2r.white,
),
// Progress Indicator Theme
progressIndicatorTheme: const ProgressIndicatorThemeData(
color: UiColors.primary,
linearTrackColor: UiColors.loaderInactive,
linearMinHeight: 4,
),
);
}
}

View File

@@ -0,0 +1,564 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:design_system/design_system.dart';
import 'ui_colors.dart';
/// Static definitions of typography styles for the Staff Design System.
class UiTypography {
UiTypography._();
// ---------------------------------------------------------------------------
// 0. Base Font Styles
// ---------------------------------------------------------------------------
/// The primary font family used throughout the design system.
static final TextStyle _primaryBase = GoogleFonts.instrumentSans();
/// The secondary font family used for display or specialized elements.
static final TextStyle _secondaryBase = GoogleFonts.spaceGrotesk();
// ---------------------------------------------------------------------------
// 1. Primary Typography (Instrument Sans)
// ---------------------------------------------------------------------------
// --- 1.1 Display ---
/// Display Large - Font: Instrument Sans, Size: 36, Height: 1.1 (#121826)
static final TextStyle displayL = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 36,
height: 1.1,
color: UiColors.textPrimary,
);
/// Display medium - Font: Instrument Sans, Size: 32, Height: 1.1 (#121826)
static final TextStyle displayM = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 32,
height: 1.1,
color: UiColors.textPrimary,
);
/// Display small - Font: Instrument Sans, Size: 32, Height: 1.1 (#121826)
static final TextStyle displayMb = _primaryBase.copyWith(
fontWeight: FontWeight.w600,
fontSize: 32,
height: 1.1,
color: UiColors.textPrimary,
);
/// Display 1 Medium - Font: Instrument Sans, Size: 26, Height: 1.1 (#121826)
static final TextStyle display1m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 26,
height: 1.1,
color: UiColors.textPrimary,
);
/// Display 1 Regular - Font: Instrument Sans, Size: 38, Height: 1.3 (#121826)
static final TextStyle display1r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 26,
height: 1.3,
letterSpacing: -1,
color: UiColors.textPrimary,
);
/// Display 1 Bold - Font: Instrument Sans, Size: 38, Height: 1.3 (#121826)
static final TextStyle display1b = _primaryBase.copyWith(
fontWeight: FontWeight.w600,
fontSize: 26,
height: 1.3,
letterSpacing: -1,
color: UiColors.textPrimary,
);
/// Display 2 Medium - Font: Instrument Sans, Size: 16, Height: 1.1 (#121826)
static final TextStyle display2m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16,
height: 1.1,
color: UiColors.textPrimary,
);
/// Display 2 Regular - Font: Instrument Sans, Size: 28, Height: 1.5 (#121826)
static final TextStyle display2r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 16,
height: 1.5,
color: UiColors.textPrimary,
);
/// Display 3 Medium - Font: Instrument Sans, Size: 32, Height: 1.1 (#121826)
static final TextStyle display3m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 14,
height: 1.1,
color: UiColors.textPrimary,
);
/// Display 3 Regular - Font: Instrument Sans, Size: 32, Height: 1.3 (#121826)
static final TextStyle display3r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 14,
height: 1.3,
color: UiColors.textPrimary,
);
/// Display 3 Bold - Font: Instrument Sans, Size: 32, Height: 1.1 (#121826)
static final TextStyle display3b = _primaryBase.copyWith(
fontWeight: FontWeight.w600,
fontSize: 14,
height: 1.1,
color: UiColors.textPrimary,
);
// --- 1.2 Title ---
/// Title 1 Medium - Font: Instrument Sans, Size: 18, Height: 1.5 (#121826)
static final TextStyle title1m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 18,
height: 1.5,
color: UiColors.textPrimary,
);
/// Title 1 Regular - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
static final TextStyle title1r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 18,
height: 1.5,
color: UiColors.textPrimary,
);
/// Title 2 Bold - Font: Instrument Sans, Size: 20, Height: 1.1 (#121826)
static final TextStyle title2b = _primaryBase.copyWith(
fontWeight: FontWeight.w600,
fontSize: 16,
height: 1.1,
color: UiColors.textPrimary,
);
/// Title 2 Medium - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
static final TextStyle title2m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16,
height: 1.5,
color: UiColors.textPrimary,
);
/// Title 2 Regular - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
static final TextStyle title2r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 16,
height: 1.5,
color: UiColors.textPrimary,
);
// --- 1.3 Headline ---
/// Headline 1 Medium - Font: Instrument Sans, Size: 26, Height: 1.5 (#121826)
static final TextStyle headline1m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 26,
height: 1.5,
color: UiColors.textPrimary,
);
/// Headline 1 Regular - Font: Instrument Sans, Size: 26, Height: 1.5 (#121826)
static final TextStyle headline1r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 26,
height: 1.5,
color: UiColors.textPrimary,
);
/// Headline 2 Medium - Font: Instrument Sans, Size: 20, Height: 1.5 (#121826)
static final TextStyle headline2m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 22,
height: 1.5,
color: UiColors.textPrimary,
);
/// Headline 2 Regular - Font: Instrument Sans, Size: 20, Height: 1.5 (#121826)
static final TextStyle headline2r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 22,
height: 1.5,
color: UiColors.textPrimary,
);
/// Headline 3 Medium - Font: Instrument Sans, Size: 22, Height: 1.5 (#121826)
static final TextStyle headline3m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 20,
height: 1.5,
color: UiColors.textPrimary,
);
/// Headline 4 Medium - Font: Instrument Sans, Size: 22, Height: 1.5 (#121826)
static final TextStyle headline4m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 18,
height: 1.5,
color: UiColors.textPrimary,
);
/// Headline 4 Regular - Font: Instrument Sans, Size: 20, Height: 1.5 (#121826)
static final TextStyle headline4r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 18,
height: 1.5,
color: UiColors.textPrimary,
);
/// Headline 5 Regular - Font: Instrument Sans, Size: 18, Height: 1.5 (#121826)
static final TextStyle headline5r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 14,
height: 1.5,
color: UiColors.textPrimary,
);
/// Headline 5 Medium - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
static final TextStyle headline5m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16,
height: 1.5,
color: UiColors.textPrimary,
);
// --- 1.4 Title Uppercase ---
/// Title Uppercase 2 Medium - Font: Instrument Sans, Size: 14, Height: 1.5, Spacing: 0.7 (#121826)
static final TextStyle titleUppercase2m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 14,
height: 1.5,
letterSpacing: 0.7,
color: UiColors.textPrimary,
);
/// Title Uppercase 3 Medium - Font: Instrument Sans, Size: 12, Height: 1.5, Spacing: 1.5 (#121826)
static final TextStyle titleUppercase3m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 12,
height: 1.5,
letterSpacing: 1.5,
color: UiColors.textPrimary,
);
/// Title Uppercase 4 Medium - Font: Instrument Sans, Size: 11, Height: 1.5, Spacing: 2.2 (#121826)
static final TextStyle titleUppercase4m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 11,
height: 1.5,
letterSpacing: 2.2,
color: UiColors.textPrimary,
);
// --- 1.5 Body ---
/// Body 1 Bold - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
static final TextStyle body1b = _primaryBase.copyWith(
fontWeight: FontWeight.w700,
fontSize: 16,
height: 1.5,
color: UiColors.textPrimary,
);
/// Body 1 Medium - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
static final TextStyle body1m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16,
height: 1.5,
letterSpacing: -0.025,
color: UiColors.textPrimary,
);
/// Body 1 Regular - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
static final TextStyle body1r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 16,
height: 1.5,
letterSpacing: -0.05,
color: UiColors.textPrimary,
);
/// Body 2 Bold - Font: Instrument Sans, Size: 14, Height: 1.5 (#121826)
static final TextStyle body2b = _primaryBase.copyWith(
fontWeight: FontWeight.w700,
fontSize: 14,
height: 1.5,
color: UiColors.textPrimary,
);
/// Body 2 Medium - Font: Instrument Sans, Size: 14, Height: 1.5 (#121826)
static final TextStyle body2m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 14,
height: 1.5,
color: UiColors.textPrimary,
);
/// Body 2 Regular - Font: Instrument Sans, Size: 14, Height: 1.5, Spacing: 0.1 (#121826)
static final TextStyle body2r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 14,
height: 1.5,
letterSpacing: 0.1,
color: UiColors.textPrimary,
);
/// Body 3 Regular - Font: Instrument Sans, Size: 14, Height: 1.5, Spacing: -0.1 (#121826)
static final TextStyle body3r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 12,
height: 1.5,
letterSpacing: -0.1,
color: UiColors.textPrimary,
);
/// Body 4 Regular - Font: Instrument Sans, Size: 14, Height: 1.5, Spacing: 0.05 (#121826)
static final TextStyle body4r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 12,
height: 1.5,
letterSpacing: 0.05,
color: UiColors.textPrimary,
);
/// Body 4 Medium - Font: Instrument Sans, Size: 14, Height: 1.5, Spacing: 0.05 (#121826)
static final TextStyle body4m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 12,
height: 1.5,
letterSpacing: 0.05,
color: UiColors.textPrimary,
);
// --- 1.6 Footnote ---
/// Footnote 1 Medium - Font: Instrument Sans, Size: 12, Height: 1.5 (#121826)
static final TextStyle footnote1m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 12,
height: 1.5,
color: UiColors.textPrimary,
);
/// Footnote 1 Regular - Font: Instrument Sans, Size: 12, Height: 1.5, Spacing: 0.05 (#121826)
static final TextStyle footnote1r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 12,
height: 1.5,
letterSpacing: 0.05,
color: UiColors.textPrimary,
);
/// Footnote 1 Bold - Font: Instrument Sans, Size: 12, Height: 1.5, Spacing: 0.05 (#121826)
static final TextStyle footnote1b = _primaryBase.copyWith(
fontWeight: FontWeight.w700,
fontSize: 12,
height: 1.5,
letterSpacing: 0.05,
color: UiColors.textPrimary,
);
/// Footnote 2 Medium - Font: Instrument Sans, Size: 10, Height: 1.5 (#121826)
static final TextStyle footnote2m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 10,
height: 1.5,
color: UiColors.textPrimary,
);
/// Footnote 2 Bold - Font: Instrument Sans, Size: 10, Height: 1.5 (#121826)
static final TextStyle footnote2b = _primaryBase.copyWith(
fontWeight: FontWeight.w700,
fontSize: 10,
height: 1.5,
color: UiColors.textPrimary,
);
/// Footnote 2 Regular - Font: Instrument Sans, Size: 10, Height: 1.5 (#121826)
static final TextStyle footnote2r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 10,
height: 1.5,
color: UiColors.textPrimary,
);
// --- 1.7 Button ---
/// Button S - Font: Instrument Sans, Size: 10, Height: 1.5 (#121826)
static final TextStyle buttonS = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 10,
height: 1.5,
color: UiColors.textPrimary,
);
/// Button Medium - Font: Instrument Sans, Size: 12, Height: 1.5 (#121826)
static final TextStyle buttonM = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 12,
height: 1.5,
color: UiColors.textPrimary,
);
/// Button Large - Font: Instrument Sans, Size: 14, Height: 1.5 (#121826)
static final TextStyle buttonL = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 14,
height: 1.5,
color: UiColors.textPrimary,
);
/// Button XL - Font: Instrument Sans, Size: 16, Height: 1.5 (#121826)
static final TextStyle buttonXL = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16,
height: 1.5,
color: UiColors.textPrimary,
);
// --- 1.8 Link ---
/// Link 1 Regular - Font: Instrument Sans, Size: 16, Height: 1.5, Underlined (#0A39DF)
static final TextStyle link1r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 16,
height: 1.5,
color: UiColors.textLink,
decoration: TextDecoration.underline,
decorationColor: UiColors.textLink,
);
/// Link 2 Medium - Font: Instrument Sans, Size: 14, Height: 1.5, Underlined (#0A39DF)
static final TextStyle link2m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,
fontSize: 14,
height: 1.5,
color: UiColors.textLink,
decoration: TextDecoration.underline,
decorationColor: UiColors.textLink,
);
/// Link 2 Regular - Font: Instrument Sans, Size: 14, Height: 1.5, Underlined (#0A39DF)
static final TextStyle link2r = _primaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 14,
height: 1.5,
color: UiColors.textLink,
decoration: TextDecoration.underline,
decorationColor: UiColors.textLink,
);
// ---------------------------------------------------------------------------
// 2. Secondary Typography (Space Grotesk)
// ---------------------------------------------------------------------------
// --- 2.1 Display ---
/// Display 1 Bold (Secondary) - Font: Space Grotesk, Size: 50, Height: 1.1 (#121826)
static final TextStyle secondaryDisplay1b = _secondaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 50,
height: 1.1,
color: UiColors.textPrimary,
);
/// Display 1 Regular (Secondary) - Font: Space Grotesk, Size: 50, Height: 1.1 (#121826)
static final TextStyle secondaryDisplay1r = _secondaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 50,
height: 1.1,
color: UiColors.textPrimary,
);
/// Display 2 Bold (Secondary) - Font: Space Grotesk, Size: 40, Height: 1.1 (#121826)
static final TextStyle secondaryDisplay2b = _secondaryBase.copyWith(
fontWeight: FontWeight.w700,
fontSize: 40,
height: 1.1,
color: UiColors.textPrimary,
);
/// Display 2 Regular (Secondary) - Font: Space Grotesk, Size: 40, Height: 1.1 (#121826)
static final TextStyle secondaryDisplay2r = _secondaryBase.copyWith(
fontWeight: FontWeight.w400,
fontSize: 40,
height: 1.1,
color: UiColors.textPrimary,
);
// ---------------------------------------------------------------------------
// 3. TextTheme Mapping
// ---------------------------------------------------------------------------
/// Primary TextTheme
static TextTheme get textTheme => TextTheme(
displayLarge: display1r,
displayMedium: displayL,
displaySmall: display3m,
headlineLarge: headline1m,
headlineMedium: headline3m,
headlineSmall: headline2m,
titleLarge: title1m,
titleMedium: title2m,
titleSmall: body2m,
bodyLarge: body1r,
bodyMedium: body2r,
bodySmall: footnote1r,
labelLarge: buttonL,
labelMedium: buttonM,
labelSmall: footnote2r,
);
}
/// Extension to easily color text styles using the Staff Design System color palette.
extension TypographyColors on TextStyle {
/// Primary text color (#121826)
TextStyle get textPrimary => copyWith(color: UiColors.textPrimary);
/// Secondary text color (#6A7382)
TextStyle get textSecondary => copyWith(color: UiColors.textSecondary);
/// Inactive text color (#9CA3AF)
TextStyle get textInactive => copyWith(color: UiColors.textInactive);
/// Tertiary text color (#9CA3AF)
TextStyle get textTertiary => copyWith(color: UiColors.textInactive);
/// Placeholder text color (#9CA3AF)
TextStyle get textPlaceholder => copyWith(color: UiColors.textPlaceholder);
/// Description text color (#6A7382)
TextStyle get textDescription => copyWith(color: UiColors.textDescription);
/// Success text color (#10B981)
TextStyle get textSuccess => copyWith(color: UiColors.textSuccess);
/// Error text color (#F04444)
TextStyle get textError => copyWith(color: UiColors.textError);
/// Warning text color (#D97706)
TextStyle get textWarning => copyWith(color: UiColors.textWarning);
/// Link text color (#0A39DF)
TextStyle get textLink => copyWith(color: UiColors.textLink);
/// White text color (#FFFFFF)
TextStyle get white => copyWith(color: UiColors.white);
/// Black text color (#000000)
TextStyle get black => copyWith(color: UiColors.black);
/// Underline decoration
TextStyle get underline => copyWith(decoration: TextDecoration.underline);
/// Active content color
TextStyle get activeContentColor => copyWith(color: UiColors.textPrimary);
}

View File

@@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import '../ui_icons.dart';
/// A custom AppBar for the Krow UI design system.
///
/// This widget provides a consistent look and feel for top app bars across the application.
class UiAppBar extends StatelessWidget implements PreferredSizeWidget {
/// The title text to display in the app bar.
final String? title;
/// A widget to display instead of the title text.
final Widget? titleWidget;
/// The widget to display before the title.
/// Usually an [IconButton] for navigation.
final Widget? leading;
/// A list of Widgets to display in a row after the [title] widget.
final List<Widget>? actions;
/// The height of the app bar. Defaults to [kToolbarHeight].
final double height;
/// Whether the title should be centered.
final bool centerTitle;
/// Signature for the callback that is called when the leading button is pressed.
/// If [leading] is null, this callback will be used for a default back button.
final VoidCallback? onLeadingPressed;
/// Whether to show a default back button if [leading] is null.
final bool showBackButton;
/// This widget appears across the bottom of the app bar.
/// Typically a [TabBar]. Only widgets that implement [PreferredSizeWidget] can be used at the bottom of an app bar.
final PreferredSizeWidget? bottom;
const UiAppBar({
super.key,
this.title,
this.titleWidget,
this.leading,
this.actions,
this.height = kToolbarHeight,
this.centerTitle = true,
this.onLeadingPressed,
this.showBackButton = true,
this.bottom,
});
@override
Widget build(BuildContext context) {
return AppBar(
title: titleWidget ??
(title != null
? Text(
title!,
)
: null),
leading: leading ??
(showBackButton
? IconButton(
icon: const Icon(UiIcons.chevronLeft, size: 20),
onPressed: onLeadingPressed ?? () => Navigator.of(context).pop(),
)
: null),
actions: actions,
centerTitle: centerTitle,
bottom: bottom,
);
}
@override
Size get preferredSize => Size.fromHeight(height + (bottom?.preferredSize.height ?? 0.0));
}

View File

@@ -0,0 +1,191 @@
import 'package:flutter/material.dart';
import '../ui_constants.dart';
/// A custom button widget with different variants and icon support.
class UiButton extends StatelessWidget {
/// The text to display on the button.
final String? text;
/// Optional custom child widget. If provided, overrides text and icons.
final Widget? child;
/// Callback when the button is tapped.
final VoidCallback? onPressed;
/// Optional leading icon.
final IconData? leadingIcon;
/// Optional trailing icon.
final IconData? trailingIcon;
/// Optional Style
final ButtonStyle? style;
/// The size of the icons. Defaults to 20.
final double iconSize;
/// The size of the button.
final UiButtonSize size;
/// The button widget to use (ElevatedButton, OutlinedButton, or TextButton).
final Widget Function(
BuildContext context,
VoidCallback? onPressed,
ButtonStyle? style,
Widget child,
)
buttonBuilder;
/// Creates a [UiButton] with a custom button builder.
const UiButton({
super.key,
this.text,
this.child,
required this.buttonBuilder,
this.onPressed,
this.leadingIcon,
this.trailingIcon,
this.style,
this.iconSize = 20,
this.size = UiButtonSize.medium,
}) : assert(
text != null || child != null,
'Either text or child must be provided',
);
/// Creates a primary button using [ElevatedButton].
UiButton.primary({
super.key,
this.text,
this.child,
this.onPressed,
this.leadingIcon,
this.trailingIcon,
this.style,
this.iconSize = 20,
this.size = UiButtonSize.medium,
}) : buttonBuilder = _elevatedButtonBuilder,
assert(
text != null || child != null,
'Either text or child must be provided',
);
/// Creates a secondary button using [OutlinedButton].
UiButton.secondary({
super.key,
this.text,
this.child,
this.onPressed,
this.leadingIcon,
this.trailingIcon,
this.style,
this.iconSize = 20,
this.size = UiButtonSize.medium,
}) : buttonBuilder = _outlinedButtonBuilder,
assert(
text != null || child != null,
'Either text or child must be provided',
);
/// Creates a text button using [TextButton].
UiButton.text({
super.key,
this.text,
this.child,
this.onPressed,
this.leadingIcon,
this.trailingIcon,
this.style,
this.iconSize = 20,
this.size = UiButtonSize.medium,
}) : buttonBuilder = _textButtonBuilder,
assert(
text != null || child != null,
'Either text or child must be provided',
);
@override
/// Builds the button UI.
Widget build(BuildContext context) {
return buttonBuilder(context, onPressed, style, _buildButtonContent());
}
/// Builds the button content with optional leading and trailing icons.
Widget _buildButtonContent() {
if (child != null) {
return child!;
}
// Single icon or text case
if (leadingIcon == null && trailingIcon == null) {
return Text(text!);
}
if (leadingIcon != null && text == null && trailingIcon == null) {
return Icon(leadingIcon, size: iconSize);
}
// Multiple elements case
final List<Widget> children = [];
if (leadingIcon != null) {
children.add(Icon(leadingIcon, size: iconSize));
children.add(const SizedBox(width: UiConstants.space2));
}
children.add(Text(text!));
if (trailingIcon != null) {
children.add(const SizedBox(width: UiConstants.space2));
children.add(Icon(trailingIcon, size: iconSize));
}
return Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: children,
);
}
/// Builder for ElevatedButton.
static Widget _elevatedButtonBuilder(
BuildContext context,
VoidCallback? onPressed,
ButtonStyle? style,
Widget child,
) {
return ElevatedButton(onPressed: onPressed, style: style, child: child);
}
/// Builder for OutlinedButton.
static Widget _outlinedButtonBuilder(
BuildContext context,
VoidCallback? onPressed,
ButtonStyle? style,
Widget child,
) {
return OutlinedButton(onPressed: onPressed, style: style, child: child);
}
/// Builder for TextButton.
static Widget _textButtonBuilder(
BuildContext context,
VoidCallback? onPressed,
ButtonStyle? style,
Widget child,
) {
return TextButton(onPressed: onPressed, style: style, child: child);
}
}
/// Defines the size of a [UiButton].
enum UiButtonSize {
/// Small button (compact)
small,
/// Medium button (standard)
medium,
/// Large button (prominent)
large,
}

View File

@@ -0,0 +1,190 @@
import 'package:flutter/material.dart';
import '../ui_colors.dart';
import '../ui_constants.dart';
import '../ui_typography.dart';
/// Sizes for the [UiChip] widget.
enum UiChipSize {
/// Small size (e.g. for tags in tight spaces).
small,
/// Medium size (default).
medium,
/// Large size (e.g. for standalone filters).
large,
}
/// Themes for the [UiChip] widget.
enum UiChipVariant {
/// Primary style with solid background.
primary,
/// Secondary style with light background.
secondary,
/// Accent style with highlight background.
accent,
}
/// A custom chip widget with supports for different sizes, themes, and icons.
class UiChip extends StatelessWidget {
/// The text label to display.
final String label;
/// The size of the chip. Defaults to [UiChipSize.medium].
final UiChipSize size;
/// The theme variant of the chip. Defaults to [UiChipVariant.secondary].
final UiChipVariant variant;
/// Optional leading icon.
final IconData? leadingIcon;
/// Optional trailing icon.
final IconData? trailingIcon;
/// Callback when the chip is tapped.
final VoidCallback? onTap;
/// Callback when the trailing icon is tapped (e.g. for removal).
final VoidCallback? onTrailingIconTap;
/// Whether the chip is currently selected/active.
final bool isSelected;
/// Creates a [UiChip].
const UiChip({
super.key,
required this.label,
this.size = UiChipSize.medium,
this.variant = UiChipVariant.secondary,
this.leadingIcon,
this.trailingIcon,
this.onTap,
this.onTrailingIconTap,
this.isSelected = false,
});
@override
Widget build(BuildContext context) {
final backgroundColor = _getBackgroundColor();
final contentColor = _getContentColor();
final textStyle = _getTextStyle().copyWith(color: contentColor);
final padding = _getPadding();
final iconSize = _getIconSize();
final content = Row(
mainAxisSize: MainAxisSize.min,
children: [
if (leadingIcon != null) ...[
Icon(leadingIcon, size: iconSize, color: contentColor),
SizedBox(width: _getGap()),
],
Text(label, style: textStyle),
if (trailingIcon != null) ...[
SizedBox(width: _getGap()),
GestureDetector(
onTap: onTrailingIconTap,
child: Icon(trailingIcon, size: iconSize, color: contentColor),
),
],
],
);
return GestureDetector(
onTap: onTap,
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: padding,
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: UiConstants.radiusFull,
border: _getBorder(),
),
child: content,
),
);
}
Color _getBackgroundColor() {
if (!isSelected && variant == UiChipVariant.primary) {
return UiColors.white;
}
switch (variant) {
case UiChipVariant.primary:
return UiColors.primary;
case UiChipVariant.secondary:
return UiColors.tagInProgress;
case UiChipVariant.accent:
return UiColors.accent;
}
}
Color _getContentColor() {
if (!isSelected && variant == UiChipVariant.primary) {
return UiColors.textSecondary;
}
switch (variant) {
case UiChipVariant.primary:
return UiColors.white;
case UiChipVariant.secondary:
return UiColors.primary;
case UiChipVariant.accent:
return UiColors.accentForeground;
}
}
TextStyle _getTextStyle() {
switch (size) {
case UiChipSize.small:
return UiTypography.body3r;
case UiChipSize.medium:
return UiTypography.body2m;
case UiChipSize.large:
return UiTypography.body1m;
}
}
EdgeInsets _getPadding() {
switch (size) {
case UiChipSize.small:
return const EdgeInsets.symmetric(horizontal: 10, vertical: 6);
case UiChipSize.medium:
return const EdgeInsets.symmetric(horizontal: 12, vertical: 8);
case UiChipSize.large:
return const EdgeInsets.symmetric(horizontal: 16, vertical: 10);
}
}
double _getIconSize() {
switch (size) {
case UiChipSize.small:
return 12;
case UiChipSize.medium:
return 16;
case UiChipSize.large:
return 20;
}
}
double _getGap() {
switch (size) {
case UiChipSize.small:
return UiConstants.space1;
case UiChipSize.medium:
return UiConstants.space1 + 2;
case UiChipSize.large:
return UiConstants.space2;
}
}
BoxBorder? _getBorder() {
if (!isSelected && variant == UiChipVariant.primary) {
return Border.all(color: UiColors.border);
}
return null;
}
}

View File

@@ -0,0 +1,89 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import '../ui_colors.dart';
import '../ui_constants.dart';
/// A custom icon button with blur effect and different variants.
class UiIconButton extends StatelessWidget {
/// The icon to display.
final IconData icon;
/// The size of the icon button.
final double size;
/// The size of the icon.
final double iconSize;
/// The background color of the button.
final Color backgroundColor;
/// The color of the icon.
final Color iconColor;
/// Whether to apply blur effect.
final bool useBlur;
/// Callback when the button is tapped.
final VoidCallback? onTap;
/// Creates a [UiIconButton] with custom properties.
const UiIconButton({
super.key,
required this.icon,
this.size = 40,
this.iconSize = 20,
required this.backgroundColor,
required this.iconColor,
this.useBlur = false,
this.onTap,
});
/// Creates a primary variant icon button with solid background.
const UiIconButton.primary({
super.key,
required this.icon,
this.size = 40,
this.iconSize = 20,
this.onTap,
}) : backgroundColor = UiColors.primary,
iconColor = UiColors.white,
useBlur = false;
/// Creates a secondary variant icon button with blur effect.
UiIconButton.secondary({
super.key,
required this.icon,
this.size = 40,
this.iconSize = 20,
this.onTap,
}) : backgroundColor = UiColors.primary.withAlpha(96),
iconColor = UiColors.primary,
useBlur = true;
@override
/// Builds the icon button UI.
Widget build(BuildContext context) {
final Widget button = Container(
width: size,
height: size,
decoration: BoxDecoration(color: backgroundColor, shape: BoxShape.circle),
child: Icon(icon, color: iconColor, size: iconSize),
);
final Widget content = useBlur
? ClipRRect(
borderRadius: UiConstants.radiusFull,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: button,
),
)
: button;
if (onTap != null) {
return GestureDetector(onTap: onTap, child: content);
}
return content;
}
}

View File

@@ -0,0 +1,87 @@
import 'package:flutter/material.dart';
import '../ui_colors.dart';
import '../ui_constants.dart';
import '../ui_icons.dart';
/// A widget that displays a horizontal step indicator with icons.
///
/// This widget shows a series of circular step indicators connected by lines,
/// with different visual states for completed, active, and inactive steps.
class UiStepIndicator extends StatelessWidget {
/// The list of icons to display for each step.
final List<IconData> stepIcons;
/// The index of the currently active step (0-based).
final int currentStep;
/// Creates a [UiStepIndicator].
const UiStepIndicator({
super.key,
required this.stepIcons,
required this.currentStep,
});
@override
/// Builds the step indicator UI.
Widget build(BuildContext context) {
// active step color
const Color activeColor = UiColors.primary;
// completed step color
const Color completedColor = UiColors.textSuccess;
// inactive step color
const Color inactiveColor = UiColors.iconSecondary;
return Padding(
padding: const EdgeInsets.symmetric(vertical: UiConstants.space2),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(stepIcons.length, (index) {
final bool isActive = index == currentStep;
final bool isCompleted = index < currentStep;
Color bgColor;
Color iconColor;
if (isCompleted) {
bgColor = completedColor.withAlpha(24);
iconColor = completedColor;
} else if (isActive) {
bgColor = activeColor.withAlpha(24);
iconColor = activeColor;
} else {
bgColor = inactiveColor.withAlpha(24);
iconColor = inactiveColor.withAlpha(128);
}
return Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: bgColor,
shape: BoxShape.circle,
),
child: Icon(
isCompleted ? UiIcons.check : stepIcons[index],
size: 20,
color: iconColor,
),
),
if (index < stepIcons.length - 1)
Container(
width: 30,
height: 2,
margin: const EdgeInsets.symmetric(
horizontal: UiConstants.space1,
),
color: isCompleted
? completedColor.withAlpha(96)
: inactiveColor.withAlpha(96),
),
],
);
}),
),
);
}
}

View File

@@ -0,0 +1,115 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../ui_typography.dart';
import '../ui_constants.dart';
import '../ui_colors.dart';
/// A custom TextField for the Krow UI design system.
///
/// This widget combines a label and a [TextField] with consistent styling.
class UiTextField extends StatelessWidget {
/// The label text to display above the text field.
final String? label;
/// The hint text to display inside the text field when empty.
final String? hintText;
/// Signature for the callback that is called when the text in the field changes.
final ValueChanged<String>? onChanged;
/// The controller for the text field.
final TextEditingController? controller;
/// The type of keyboard to use for editing the text.
final TextInputType? keyboardType;
/// The maximum number of lines for the text field. Defaults to 1.
final int? maxLines;
/// Whether to hide the text being edited (e.g., for passwords). Defaults to false.
final bool obscureText;
/// The type of action button to use for the keyboard.
final TextInputAction? textInputAction;
/// Signature for the callback that is called when the user submits the text field.
final ValueChanged<String>? onSubmitted;
/// Whether the text field should be focused automatically. Defaults to false.
final bool autofocus;
/// Optional input formatters to validate or format the text as it is typed.
final List<TextInputFormatter>? inputFormatters;
/// Optional prefix icon to display at the start of the text field.
final IconData? prefixIcon;
/// Optional suffix icon to display at the end of the text field.
final IconData? suffixIcon;
/// Optional custom suffix widget to display at the end (e.g., password toggle).
final Widget? suffix;
/// Whether the text field should be read-only.
final bool readOnly;
/// Callback when the text field is tapped.
final VoidCallback? onTap;
const UiTextField({
super.key,
this.label,
this.hintText,
this.onChanged,
this.controller,
this.keyboardType,
this.maxLines = 1,
this.obscureText = false,
this.textInputAction,
this.onSubmitted,
this.autofocus = false,
this.inputFormatters,
this.prefixIcon,
this.suffixIcon,
this.suffix,
this.readOnly = false,
this.onTap,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
if (label != null) ...[
Text(label!, style: UiTypography.body4m.textSecondary),
const SizedBox(height: UiConstants.space1),
],
TextField(
controller: controller,
onChanged: onChanged,
keyboardType: keyboardType,
maxLines: maxLines,
obscureText: obscureText,
textInputAction: textInputAction,
onSubmitted: onSubmitted,
autofocus: autofocus,
inputFormatters: inputFormatters,
readOnly: readOnly,
onTap: onTap,
style: UiTypography.body1r.textPrimary,
decoration: InputDecoration(
hintText: hintText,
prefixIcon: prefixIcon != null
? Icon(prefixIcon, size: 20, color: UiColors.iconSecondary)
: null,
suffixIcon: suffixIcon != null
? Icon(suffixIcon, size: 20, color: UiColors.iconSecondary)
: suffix,
),
),
],
);
}
}

View File

@@ -0,0 +1,31 @@
name: design_system
description: "A new Flutter package project."
version: 0.0.1
homepage:
resolution: workspace
environment:
sdk: ^3.10.7
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
google_fonts: ^7.0.2
lucide_icons: ^0.257.0
font_awesome_flutter: ^10.7.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# To add assets to your package, add an assets section, like this:
assets:
- assets/

View File

@@ -0,0 +1,12 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:design_system/design_system.dart';
void main() {
test('adds one to input values', () {
final calculator = Calculator();
expect(calculator.addOne(2), 3);
expect(calculator.addOne(-7), -6);
expect(calculator.addOne(0), 1);
});
}

View File

@@ -0,0 +1,57 @@
/// The Shared Domain Layer.
///
/// This package contains the core business entities and rules.
/// It is pure Dart and has no dependencies on Flutter or Firebase.
///
/// Note: Repository Interfaces are now located in their respective Feature packages.
// Users & Membership
export 'src/entities/users/user.dart';
export 'src/entities/users/staff.dart';
export 'src/entities/users/membership.dart';
export 'src/entities/users/biz_member.dart';
export 'src/entities/users/hub_member.dart';
// Business & Organization
export 'src/entities/business/business.dart';
export 'src/entities/business/business_setting.dart';
export 'src/entities/business/hub.dart';
export 'src/entities/business/hub_department.dart';
export 'src/entities/business/biz_contract.dart';
// Events & Shifts
export 'src/entities/events/event.dart';
export 'src/entities/events/event_shift.dart';
export 'src/entities/events/event_shift_position.dart';
export 'src/entities/events/assignment.dart';
export 'src/entities/events/work_session.dart';
// Skills & Certs
export 'src/entities/skills/skill.dart';
export 'src/entities/skills/skill_category.dart';
export 'src/entities/skills/staff_skill.dart';
export 'src/entities/skills/certificate.dart';
export 'src/entities/skills/skill_kit.dart';
// Financial & Payroll
export 'src/entities/financial/invoice.dart';
export 'src/entities/financial/invoice_item.dart';
export 'src/entities/financial/invoice_decline.dart';
export 'src/entities/financial/staff_payment.dart';
// Ratings & Penalties
export 'src/entities/ratings/staff_rating.dart';
export 'src/entities/ratings/penalty_log.dart';
export 'src/entities/ratings/business_staff_preference.dart';
// Staff Profile
export 'src/entities/profile/emergency_contact.dart';
export 'src/entities/profile/bank_account.dart';
export 'src/entities/profile/accessibility.dart';
export 'src/entities/profile/schedule.dart';
// Support & Config
export 'src/entities/support/addon.dart';
export 'src/entities/support/tag.dart';
export 'src/entities/support/media.dart';
export 'src/entities/support/working_area.dart';

View File

@@ -0,0 +1,36 @@
import 'package:equatable/equatable.dart';
/// Represents a legal or service contract.
///
/// Can be between a business and the platform, or a business and staff.
class BizContract extends Equatable {
/// Unique identifier.
final String id;
/// The [Business] party to the contract.
final String businessId;
/// Descriptive name of the contract.
final String name;
/// Valid from date.
final DateTime startDate;
/// Valid until date (null if indefinite).
final DateTime? endDate;
/// URL to the document content (PDF/HTML).
final String contentUrl;
const BizContract({
required this.id,
required this.businessId,
required this.name,
required this.startDate,
this.endDate,
required this.contentUrl,
});
@override
List<Object?> get props => [id, businessId, name, startDate, endDate, contentUrl];
}

View File

@@ -0,0 +1,47 @@
import 'package:equatable/equatable.dart';
/// The operating status of a [Business].
enum BusinessStatus {
/// Business created but not yet approved.
pending,
/// Fully active and operational.
active,
/// Temporarily suspended (e.g. for non-payment).
suspended,
/// Permanently inactive.
inactive,
}
/// Represents a Client Company / Business.
///
/// This is the top-level organizational entity in the system.
class Business extends Equatable {
/// Unique identifier for the business.
final String id;
/// Display name of the business.
final String name;
/// Legal registration or tax number.
final String registrationNumber;
/// Current operating status.
final BusinessStatus status;
/// URL to the business logo.
final String? avatar;
const Business({
required this.id,
required this.name,
required this.registrationNumber,
required this.status,
this.avatar,
});
@override
List<Object?> get props => [id, name, registrationNumber, status, avatar];
}

View File

@@ -0,0 +1,41 @@
import 'package:equatable/equatable.dart';
/// Represents payroll and operational configuration for a [Business].
class BusinessSetting extends Equatable {
/// Unique identifier for the settings record.
final String id;
/// The [Business] these settings apply to.
final String businessId;
/// Prefix for generated invoices (e.g., "INV-").
final String prefix;
/// Whether overtime calculations are applied.
final bool overtimeEnabled;
/// Requirement method for clocking in (e.g. "qr_code", "geo_fence").
final String? clockInRequirement;
/// Requirement method for clocking out.
final String? clockOutRequirement;
const BusinessSetting({
required this.id,
required this.businessId,
required this.prefix,
required this.overtimeEnabled,
this.clockInRequirement,
this.clockOutRequirement,
});
@override
List<Object?> get props => [
id,
businessId,
prefix,
overtimeEnabled,
clockInRequirement,
clockOutRequirement,
];
}

View File

@@ -0,0 +1,42 @@
import 'package:equatable/equatable.dart';
/// The status of a [Hub].
enum HubStatus {
/// Fully operational.
active,
/// Closed or inactive.
inactive,
/// Not yet ready for operations.
underConstruction,
}
/// Represents a branch location or operational unit within a [Business].
class Hub extends Equatable {
/// Unique identifier.
final String id;
/// The parent [Business].
final String businessId;
/// Display name of the hub (e.g. "Downtown Branch").
final String name;
/// Physical address of this hub.
final String address;
/// Operational status.
final HubStatus status;
const Hub({
required this.id,
required this.businessId,
required this.name,
required this.address,
required this.status,
});
@override
List<Object?> get props => [id, businessId, name, address, status];
}

View File

@@ -0,0 +1,24 @@
import 'package:equatable/equatable.dart';
/// Represents a department within a [Hub].
///
/// Used for more granular organization of staff and events (e.g. "Kitchen", "Service").
class HubDepartment extends Equatable {
/// Unique identifier.
final String id;
/// The [Hub] this department belongs to.
final String hubId;
/// Name of the department.
final String name;
const HubDepartment({
required this.id,
required this.hubId,
required this.name,
});
@override
List<Object?> get props => [id, hubId, name];
}

View File

@@ -0,0 +1,58 @@
import 'package:equatable/equatable.dart';
/// The status of a staff [Assignment].
enum AssignmentStatus {
/// Staff member has been assigned but hasn't confirmed.
assigned,
/// Staff member has accepted the assignment.
confirmed,
/// Work is currently in progress (Clocked In).
ongoing,
/// Work completed successfully (Clocked Out).
completed,
/// Staff rejected the assignment offer.
declinedByStaff,
/// Staff canceled after accepting.
canceledByStaff,
/// Staff did not show up.
noShowed,
}
/// Represents the link between a [Staff] member and an [EventShiftPosition].
class Assignment extends Equatable {
/// Unique identifier.
final String id;
/// The job position being filled.
final String positionId;
/// The staff member filling the position.
final String staffId;
/// Current status of the assignment.
final AssignmentStatus status;
/// Actual timestamp when staff clocked in.
final DateTime? clockIn;
/// Actual timestamp when staff clocked out.
final DateTime? clockOut;
const Assignment({
required this.id,
required this.positionId,
required this.staffId,
required this.status,
this.clockIn,
this.clockOut,
});
@override
List<Object?> get props => [id, positionId, staffId, status, clockIn, clockOut];
}

View File

@@ -0,0 +1,70 @@
import 'package:equatable/equatable.dart';
/// The workflow status of an [Event].
enum EventStatus {
/// Created but incomplete.
draft,
/// Waiting for approval or publication.
pending,
/// Published and staff have been assigned.
assigned,
/// Fully confirmed and ready to start.
confirmed,
/// Currently in progress.
active,
/// Work has finished.
finished,
/// All post-event processes (invoicing) complete.
completed,
/// Archived.
closed,
/// Flagged for administrative review.
underReview,
}
/// Represents a Job Posting or Event.
///
/// This is the central entity for scheduling work. An Event contains [EventShift]s.
class Event extends Equatable {
/// Unique identifier.
final String id;
/// The [Business] hosting the event.
final String businessId;
/// The [Hub] location.
final String hubId;
/// Title of the event.
final String name;
/// Date of the event.
final DateTime date;
/// Current workflow status.
final EventStatus status;
/// Type of employment contract (e.g., 'freelance', 'permanent').
final String contractType;
const Event({
required this.id,
required this.businessId,
required this.hubId,
required this.name,
required this.date,
required this.status,
required this.contractType,
});
@override
List<Object?> get props => [id, businessId, hubId, name, date, status, contractType];
}

View File

@@ -0,0 +1,28 @@
import 'package:equatable/equatable.dart';
/// Represents a specific time block or "shift" within an [Event].
///
/// An Event can have multiple shifts (e.g. "Morning Shift", "Evening Shift").
class EventShift extends Equatable {
/// Unique identifier.
final String id;
/// The [Event] this shift belongs to.
final String eventId;
/// Descriptive name (e.g. "Setup Crew").
final String name;
/// Specific address for this shift (if different from Hub).
final String address;
const EventShift({
required this.id,
required this.eventId,
required this.name,
required this.address,
});
@override
List<Object?> get props => [id, eventId, name, address];
}

View File

@@ -0,0 +1,53 @@
import 'package:equatable/equatable.dart';
/// Represents a specific job opening within a [EventShift].
///
/// Defines the requirement for a specific [Skill], the quantity needed, and the pay.
class EventShiftPosition extends Equatable {
/// Unique identifier.
final String id;
/// The [EventShift] this position is part of.
final String shiftId;
/// The [Skill] required for this position.
final String skillId;
/// Number of staff needed.
final int count;
/// Hourly pay rate.
final double rate;
/// Start time of this specific position.
final DateTime startTime;
/// End time of this specific position.
final DateTime endTime;
/// Deducted break duration in minutes.
final int breakDurationMinutes;
const EventShiftPosition({
required this.id,
required this.shiftId,
required this.skillId,
required this.count,
required this.rate,
required this.startTime,
required this.endTime,
required this.breakDurationMinutes,
});
@override
List<Object?> get props => [
id,
shiftId,
skillId,
count,
rate,
startTime,
endTime,
breakDurationMinutes,
];
}

View File

@@ -0,0 +1,32 @@
import 'package:equatable/equatable.dart';
/// Represents a verified record of time worked.
///
/// Derived from [Assignment] clock-in/out times, used for payroll.
class WorkSession extends Equatable {
/// Unique identifier.
final String id;
/// The [Assignment] this session belongs to.
final String assignmentId;
/// Verified start time.
final DateTime startTime;
/// Verified end time.
final DateTime? endTime;
/// Verified break duration.
final int breakDurationMinutes;
const WorkSession({
required this.id,
required this.assignmentId,
required this.startTime,
this.endTime,
required this.breakDurationMinutes,
});
@override
List<Object?> get props => [id, assignmentId, startTime, endTime, breakDurationMinutes];
}

View File

@@ -0,0 +1,70 @@
import 'package:equatable/equatable.dart';
/// The workflow status of an [Invoice].
enum InvoiceStatus {
/// Generated but not yet sent/finalized.
open,
/// Client has disputed a line item.
disputed,
/// Dispute has been handled.
resolved,
/// Invoice accepted by client.
verified,
/// Payment received.
paid,
/// Payment reconciled in accounting.
reconciled,
/// Payment not received by due date.
overdue,
}
/// Represents a bill sent to a [Business] for services rendered.
class Invoice extends Equatable {
/// Unique identifier.
final String id;
/// The [Event] this invoice covers.
final String eventId;
/// The [Business] being billed.
final String businessId;
/// Current payment/approval status.
final InvoiceStatus status;
/// Grand total amount.
final double totalAmount;
/// Total amount for labor costs.
final double workAmount;
/// Total amount for addons/extras.
final double addonsAmount;
const Invoice({
required this.id,
required this.eventId,
required this.businessId,
required this.status,
required this.totalAmount,
required this.workAmount,
required this.addonsAmount,
});
@override
List<Object?> get props => [
id,
eventId,
businessId,
status,
totalAmount,
workAmount,
addonsAmount,
];
}

View File

@@ -0,0 +1,26 @@
import 'package:equatable/equatable.dart';
/// Represents a reason or log for a declined [Invoice].
class InvoiceDecline extends Equatable {
/// Unique identifier.
final String id;
/// The [Invoice] that was declined.
final String invoiceId;
/// Reason provided by the client.
final String reason;
/// When the decline happened.
final DateTime declinedAt;
const InvoiceDecline({
required this.id,
required this.invoiceId,
required this.reason,
required this.declinedAt,
});
@override
List<Object?> get props => [id, invoiceId, reason, declinedAt];
}

View File

@@ -0,0 +1,36 @@
import 'package:equatable/equatable.dart';
/// Represents a line item in an [Invoice].
///
/// Corresponds to the work done by one [Staff] member.
class InvoiceItem extends Equatable {
/// Unique identifier.
final String id;
/// The [Invoice] this item belongs to.
final String invoiceId;
/// The [Staff] member whose work is being billed.
final String staffId;
/// Total billed hours.
final double workHours;
/// Hourly rate applied.
final double rate;
/// Total line item amount (workHours * rate).
final double amount;
const InvoiceItem({
required this.id,
required this.invoiceId,
required this.staffId,
required this.workHours,
required this.rate,
required this.amount,
});
@override
List<Object?> get props => [id, invoiceId, staffId, workHours, rate, amount];
}

View File

@@ -0,0 +1,49 @@
import 'package:equatable/equatable.dart';
/// Status of a staff payout.
enum PaymentStatus {
/// Payout calculated but not processed.
pending,
/// Submitted to banking provider.
processing,
/// Successfully transferred to staff.
paid,
/// Transfer failed.
failed,
}
/// Represents a payout to a [Staff] member for a completed [Assignment].
class StaffPayment extends Equatable {
/// Unique identifier.
final String id;
/// The recipient [Staff].
final String staffId;
/// The [Assignment] being paid for.
final String assignmentId;
/// Amount to be paid.
final double amount;
/// Processing status.
final PaymentStatus status;
/// When the payment was successfully processed.
final DateTime? paidAt;
const StaffPayment({
required this.id,
required this.staffId,
required this.assignmentId,
required this.amount,
required this.status,
this.paidAt,
});
@override
List<Object?> get props => [id, staffId, assignmentId, amount, status, paidAt];
}

View File

@@ -0,0 +1,20 @@
import 'package:equatable/equatable.dart';
/// Represents accessibility requirements or features.
///
/// Can apply to Staff (needs) or Events (provision).
class Accessibility extends Equatable {
/// Unique identifier.
final String id;
/// Description (e.g. "Wheelchair Access").
final String name;
const Accessibility({
required this.id,
required this.name,
});
@override
List<Object?> get props => [id, name];
}

View File

@@ -0,0 +1,34 @@
import 'package:equatable/equatable.dart';
/// Represents bank account details for payroll.
class BankAccount extends Equatable {
/// Unique identifier.
final String id;
/// The [User] owning the account.
final String userId;
/// Name of the bank.
final String bankName;
/// Account number.
final String accountNumber;
/// Name on the account.
final String accountName;
/// Sort code (if applicable).
final String? sortCode;
const BankAccount({
required this.id,
required this.userId,
required this.bankName,
required this.accountNumber,
required this.accountName,
this.sortCode,
});
@override
List<Object?> get props => [id, userId, bankName, accountNumber, accountName, sortCode];
}

View File

@@ -0,0 +1,24 @@
import 'package:equatable/equatable.dart';
/// Represents an emergency contact for a user.
///
/// Critical for staff safety during shifts.
class EmergencyContact extends Equatable {
/// Full name of the contact.
final String name;
/// Relationship to the user (e.g. "Spouse", "Parent").
final String relationship;
/// Phone number.
final String phone;
const EmergencyContact({
required this.name,
required this.relationship,
required this.phone,
});
@override
List<Object?> get props => [name, relationship, phone];
}

View File

@@ -0,0 +1,32 @@
import 'package:equatable/equatable.dart';
/// Represents general availability schedule for a [Staff] member.
///
/// Defines recurring availability (e.g., "Mondays 9-5").
class Schedule extends Equatable {
/// Unique identifier.
final String id;
/// The [Staff] member.
final String staffId;
/// Day of the week (1 = Monday, 7 = Sunday).
final int dayOfWeek;
/// Start time of availability.
final DateTime startTime;
/// End time of availability.
final DateTime endTime;
const Schedule({
required this.id,
required this.staffId,
required this.dayOfWeek,
required this.startTime,
required this.endTime,
});
@override
List<Object?> get props => [id, staffId, dayOfWeek, startTime, endTime];
}

View File

@@ -0,0 +1,35 @@
import 'package:equatable/equatable.dart';
/// The type of preference a business has for a staff member.
enum PreferenceType {
/// Business wants to prioritize this staff member.
favorite,
/// Business does not want to work with this staff member.
blocked,
}
/// Represents a business's specific preference for a staff member.
class BusinessStaffPreference extends Equatable {
/// Unique identifier.
final String id;
/// The [Business] holding the preference.
final String businessId;
/// The [Staff] member.
final String staffId;
/// Whether they are a favorite or blocked.
final PreferenceType type;
const BusinessStaffPreference({
required this.id,
required this.businessId,
required this.staffId,
required this.type,
});
@override
List<Object?> get props => [id, businessId, staffId, type];
}

View File

@@ -0,0 +1,36 @@
import 'package:equatable/equatable.dart';
/// Represents a penalty issued to a staff member.
///
/// Penalties are issued for no-shows, cancellations, or poor conduct.
class PenaltyLog extends Equatable {
/// Unique identifier.
final String id;
/// The [Staff] member penalized.
final String staffId;
/// The [Assignment] context (if applicable).
final String assignmentId;
/// Reason for the penalty.
final String reason;
/// Score points deducted from staff profile.
final int points;
/// When the penalty was issued.
final DateTime issuedAt;
const PenaltyLog({
required this.id,
required this.staffId,
required this.assignmentId,
required this.reason,
required this.points,
required this.issuedAt,
});
@override
List<Object?> get props => [id, staffId, assignmentId, reason, points, issuedAt];
}

View File

@@ -0,0 +1,34 @@
import 'package:equatable/equatable.dart';
/// Represents a rating given to a staff member by a client.
class StaffRating extends Equatable {
/// Unique identifier.
final String id;
/// The [Staff] being rated.
final String staffId;
/// The [Event] context.
final String eventId;
/// The [Business] leaving the rating.
final String businessId;
/// Star rating (1-5).
final int rating;
/// Optional feedback text.
final String? comment;
const StaffRating({
required this.id,
required this.staffId,
required this.eventId,
required this.businessId,
required this.rating,
this.comment,
});
@override
List<Object?> get props => [id, staffId, eventId, businessId, rating, comment];
}

View File

@@ -0,0 +1,24 @@
import 'package:equatable/equatable.dart';
/// Represents a required certificate definition.
///
/// Examples: "Food Hygiene Level 2", "SIA Badge".
class Certificate extends Equatable {
/// Unique identifier.
final String id;
/// Display name of the certificate.
final String name;
/// Whether this certificate is mandatory for platform access or specific roles.
final bool isRequired;
const Certificate({
required this.id,
required this.name,
required this.isRequired,
});
@override
List<Object?> get props => [id, name, isRequired];
}

View File

@@ -0,0 +1,29 @@
import 'package:equatable/equatable.dart';
/// Represents a job category / skill type.
///
/// Examples: "Waiter", "Security Guard", "Bartender".
/// Linked to a [SkillCategory].
class Skill extends Equatable {
/// Unique identifier.
final String id;
/// The broader category (e.g. "Hospitality").
final String categoryId;
/// Display name of the skill.
final String name;
/// Default hourly rate suggested for this skill.
final double basePrice;
const Skill({
required this.id,
required this.categoryId,
required this.name,
required this.basePrice,
});
@override
List<Object?> get props => [id, categoryId, name, basePrice];
}

View File

@@ -0,0 +1,18 @@
import 'package:equatable/equatable.dart';
/// Represents a broad category of skills (e.g. "Hospitality", "Logistics").
class SkillCategory extends Equatable {
/// Unique identifier.
final String id;
/// Display name.
final String name;
const SkillCategory({
required this.id,
required this.name,
});
@override
List<Object?> get props => [id, name];
}

View File

@@ -0,0 +1,32 @@
import 'package:equatable/equatable.dart';
/// Represents required equipment or uniform for a specific [Skill].
///
/// Examples: "Black Shirt" (Uniform), "Safety Boots" (Equipment).
class SkillKit extends Equatable {
/// Unique identifier.
final String id;
/// The [Skill] this kit applies to.
final String skillId;
/// Description of the item.
final String name;
/// Whether the staff member MUST possess this item.
final bool isRequired;
/// Type of kit ('uniform' or 'equipment').
final String type;
const SkillKit({
required this.id,
required this.skillId,
required this.name,
required this.isRequired,
required this.type,
});
@override
List<Object?> get props => [id, skillId, name, isRequired, type];
}

View File

@@ -0,0 +1,58 @@
import 'package:equatable/equatable.dart';
/// The expertise level of a staff member in a specific skill.
enum SkillLevel {
/// Entry level.
beginner,
/// Experienced.
skilled,
/// Expert / Managerial level.
professional,
}
/// The verification status of a claimed skill.
enum StaffSkillStatus {
/// Claimed by staff, waiting for admin approval.
pending,
/// Verified by admin (documents checked).
verified,
/// Rejected by admin.
rejected,
}
/// Represents a staff member's qualification in a specific [Skill].
class StaffSkill extends Equatable {
/// Unique identifier.
final String id;
/// The [Staff] member.
final String staffId;
/// The [Skill] they possess.
final String skillId;
/// Their expertise level.
final SkillLevel level;
/// Years of experience.
final int experienceYears;
/// Verification status.
final StaffSkillStatus status;
const StaffSkill({
required this.id,
required this.staffId,
required this.skillId,
required this.level,
required this.experienceYears,
required this.status,
});
@override
List<Object?> get props => [id, staffId, skillId, level, experienceYears, status];
}

View File

@@ -0,0 +1,26 @@
import 'package:equatable/equatable.dart';
/// Represents a financial addon/bonus/deduction applied to an Invoice or Payment.
class Addon extends Equatable {
/// Unique identifier.
final String id;
/// Description (e.g. "Travel Expense").
final String name;
/// Monetary value.
final double amount;
/// Type ('credit' or 'debit').
final String type;
const Addon({
required this.id,
required this.name,
required this.amount,
required this.type,
});
@override
List<Object?> get props => [id, name, amount, type];
}

View File

@@ -0,0 +1,24 @@
import 'package:equatable/equatable.dart';
/// Represents a media file reference.
///
/// Used for avatars, certificates, or event photos.
class Media extends Equatable {
/// Unique identifier.
final String id;
/// External URL to the file.
final String url;
/// MIME type or general type (image, pdf).
final String type;
const Media({
required this.id,
required this.url,
required this.type,
});
@override
List<Object?> get props => [id, url, type];
}

View File

@@ -0,0 +1,18 @@
import 'package:equatable/equatable.dart';
/// Represents a descriptive tag used for categorizing events or staff.
class Tag extends Equatable {
/// Unique identifier.
final String id;
/// Text label.
final String label;
const Tag({
required this.id,
required this.label,
});
@override
List<Object?> get props => [id, label];
}

View File

@@ -0,0 +1,30 @@
import 'package:equatable/equatable.dart';
/// Represents a geographical area where a [Staff] member is willing to work.
class WorkingArea extends Equatable {
/// Unique identifier.
final String id;
/// Name of the area (e.g. "London Zone 1").
final String name;
/// Latitude of the center point.
final double centerLat;
/// Longitude of the center point.
final double centerLng;
/// Radius in Kilometers.
final double radiusKm;
const WorkingArea({
required this.id,
required this.name,
required this.centerLat,
required this.centerLng,
required this.radiusKm,
});
@override
List<Object?> get props => [id, name, centerLat, centerLng, radiusKm];
}

View File

@@ -0,0 +1,28 @@
import 'package:equatable/equatable.dart';
/// Represents a member of a Business.
///
/// Grants a user access to business-level operations.
class BizMember extends Equatable {
/// Unique identifier for this membership.
final String id;
/// The [Business] the user belongs to.
final String businessId;
/// The [User] who is a member.
final String userId;
/// The role within the business.
final String role;
const BizMember({
required this.id,
required this.businessId,
required this.userId,
required this.role,
});
@override
List<Object?> get props => [id, businessId, userId, role];
}

View File

@@ -0,0 +1,28 @@
import 'package:equatable/equatable.dart';
/// Represents a member of a Hub.
///
/// Grants a user access to specific [Hub] operations, distinct from [BizMember].
class HubMember extends Equatable {
/// Unique identifier for this membership.
final String id;
/// The [Hub] the user belongs to.
final String hubId;
/// The [User] who is a member.
final String userId;
/// The role within the hub.
final String role;
const HubMember({
required this.id,
required this.hubId,
required this.userId,
required this.role,
});
@override
List<Object?> get props => [id, hubId, userId, role];
}

View File

@@ -0,0 +1,32 @@
import 'package:equatable/equatable.dart';
/// Represents a polymorphic membership to an organization unit.
///
/// Allows a [User] to be a member of either a [Business] or a [Hub].
class Membership extends Equatable {
/// Unique identifier for the membership record.
final String id;
/// The [User] holding this membership.
final String userId;
/// The ID of the organization unit (Business or Hub).
final String memberableId;
/// The type of the organization unit ('business' or 'hub').
final String memberableType;
/// The role within that organization (e.g., 'manager', 'viewer').
final String role;
const Membership({
required this.id,
required this.userId,
required this.memberableId,
required this.memberableType,
required this.role,
});
@override
List<Object?> get props => [id, userId, memberableId, memberableType, role];
}

View File

@@ -0,0 +1,83 @@
import 'package:equatable/equatable.dart';
/// The lifecycle status of a [Staff] account.
enum StaffStatus {
/// Account created but profile not started.
registered,
/// Profile submitted and awaiting verification.
pending,
/// Profile information filled but not submitted for verification.
completedProfile,
/// Profile verified by admin.
verified,
/// Staff is currently active and eligible for work.
active,
/// Account is temporarily suspended.
blocked,
/// Account is permanently inactive.
inactive,
}
/// Represents a worker profile.
///
/// Contains all personal and professional details of a staff member.
/// Linked to a [User] via [authProviderId].
class Staff extends Equatable {
/// Unique identifier for the staff profile.
final String id;
/// Link to the [User] authentication record.
final String authProviderId;
/// Full display name.
final String name;
/// Contact email.
final String email;
/// Contact phone number.
final String? phone;
/// Current workflow status of the staff member.
final StaffStatus status;
/// Physical address string.
final String? address;
/// URL to the avatar image.
final String? avatar;
/// URL to a verified live photo for identity verification.
final String? livePhoto;
const Staff({
required this.id,
required this.authProviderId,
required this.name,
required this.email,
this.phone,
required this.status,
this.address,
this.avatar,
this.livePhoto,
});
@override
List<Object?> get props => [
id,
authProviderId,
name,
email,
phone,
status,
address,
avatar,
livePhoto,
];
}

View File

@@ -0,0 +1,30 @@
import 'package:equatable/equatable.dart';
/// Represents a base authenticated user in the KROW platform.
///
/// This entity corresponds to the Firebase Auth user record and acts as the
/// linkage between the authentication system and the specific [Staff] or Client profiles.
class User extends Equatable {
/// The unique identifier from the authentication provider (e.g., Firebase UID).
final String id;
/// The user's email address.
final String email;
/// The user's phone number, if available.
final String? phone;
/// The primary role of the user (e.g., 'staff', 'client_admin').
/// This determines the initial routing and permissions.
final String role;
const User({
required this.id,
required this.email,
this.phone,
required this.role,
});
@override
List<Object?> get props => [id, email, phone, role];
}

View File

@@ -0,0 +1,11 @@
name: krow_domain
description: Domain entities and business logic.
version: 0.0.1
publish_to: none
resolution: workspace
environment:
sdk: '>=3.10.0 <4.0.0'
dependencies:
equatable: ^2.0.8

View File

View File

@@ -0,0 +1,63 @@
library client_authentication;
import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_data_connect/krow_data_connect.dart';
import 'src/data/repositories_impl/auth_repository_impl.dart';
import 'src/domain/repositories/auth_repository_interface.dart';
import 'src/domain/usecases/sign_in_with_email_use_case.dart';
import 'src/domain/usecases/sign_in_with_social_use_case.dart';
import 'src/domain/usecases/sign_out_use_case.dart';
import 'src/domain/usecases/sign_up_with_email_use_case.dart';
import 'src/presentation/blocs/client_auth_bloc.dart';
import 'src/presentation/pages/client_get_started_page.dart';
import 'src/presentation/pages/client_sign_in_page.dart';
import 'src/presentation/pages/client_sign_up_page.dart';
export 'src/presentation/pages/client_get_started_page.dart';
export 'src/presentation/pages/client_sign_in_page.dart';
export 'src/presentation/pages/client_sign_up_page.dart';
export 'src/presentation/navigation/client_auth_navigator.dart';
export 'package:core_localization/core_localization.dart';
/// A [Module] for the client authentication feature.
class ClientAuthenticationModule extends Module {
@override
List<Module> get imports => [DataConnectModule()];
@override
void binds(Injector i) {
// Repositories
i.addLazySingleton<AuthRepositoryInterface>(
() => AuthRepositoryImpl(dataSource: i.get<AuthRepositoryMock>()),
);
// UseCases
i.addLazySingleton(
() => SignInWithEmailUseCase(i.get<AuthRepositoryInterface>()),
);
i.addLazySingleton(
() => SignUpWithEmailUseCase(i.get<AuthRepositoryInterface>()),
);
i.addLazySingleton(
() => SignInWithSocialUseCase(i.get<AuthRepositoryInterface>()),
);
i.addLazySingleton(() => SignOutUseCase(i.get<AuthRepositoryInterface>()));
// BLoCs
i.addLazySingleton<ClientAuthBloc>(
() => ClientAuthBloc(
signInWithEmail: i.get<SignInWithEmailUseCase>(),
signUpWithEmail: i.get<SignUpWithEmailUseCase>(),
signInWithSocial: i.get<SignInWithSocialUseCase>(),
signOut: i.get<SignOutUseCase>(),
),
);
}
@override
void routes(r) {
r.child('/', child: (_) => const ClientGetStartedPage());
r.child('/client-sign-in', child: (_) => const ClientSignInPage());
r.child('/client-sign-up', child: (_) => const ClientSignUpPage());
}
}

View File

@@ -0,0 +1,43 @@
import 'package:krow_data_connect/krow_data_connect.dart';
import 'package:krow_domain/krow_domain.dart';
import '../../domain/repositories/auth_repository_interface.dart';
/// Production-ready implementation of the [AuthRepositoryInterface].
///
/// This implementation integrates with the [krow_data_connect] package to provide
/// authentication services. It delegates actual data operations to the
/// [AuthRepositoryMock] (or eventually the real implementation) injected from the app layer.
class AuthRepositoryImpl implements AuthRepositoryInterface {
/// The data source used for authentication operations.
final AuthRepositoryMock _dataSource;
/// Creates an [AuthRepositoryImpl] with the injected [dataSource] dependency.
AuthRepositoryImpl({required AuthRepositoryMock dataSource})
: _dataSource = dataSource;
@override
Future<User> signInWithEmail({
required String email,
required String password,
}) {
return _dataSource.signInWithEmail(email, password);
}
@override
Future<User> signUpWithEmail({
required String companyName,
required String email,
required String password,
}) {
return _dataSource.signUpWithEmail(email, password, companyName);
}
@override
Future<User> signInWithSocial({required String provider}) {
return _dataSource.signInWithSocial(provider);
}
@override
Future<void> signOut() => _dataSource.signOut();
}

View File

@@ -0,0 +1,15 @@
import 'package:krow_core/core.dart';
/// Arguments for the [SignInWithEmailUseCase].
class SignInWithEmailArguments extends UseCaseArgument {
/// The user's email address.
final String email;
/// The user's password.
final String password;
const SignInWithEmailArguments({required this.email, required this.password});
@override
List<Object?> get props => [email, password];
}

View File

@@ -0,0 +1,12 @@
import 'package:krow_core/core.dart';
/// Arguments for the [SignInWithSocialUseCase].
class SignInWithSocialArguments extends UseCaseArgument {
/// The social provider name (e.g. 'google' or 'apple').
final String provider;
const SignInWithSocialArguments({required this.provider});
@override
List<Object?> get props => [provider];
}

Some files were not shown because too many files have changed in this diff Show More