feat: add AI recommendations flow for tenders
Implement company AI onboarding/recommendation models, services, repository, and tender UI integration with supporting tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import 'package:provider/single_child_widget.dart';
|
||||
import 'package:tm_app/core/network/network_manager.dart';
|
||||
import 'package:tm_app/core/services/tab_navigation_service.dart';
|
||||
import 'package:tm_app/core/theme/theme_provider.dart';
|
||||
import 'package:tm_app/data/repositories/company_ai_repository.dart';
|
||||
import 'package:tm_app/data/repositories/home_repository.dart';
|
||||
import 'package:tm_app/data/repositories/liked_tenders_repository.dart';
|
||||
import 'package:tm_app/data/repositories/notification_repository.dart';
|
||||
@@ -13,6 +14,7 @@ import 'package:tm_app/data/services/home_service.dart';
|
||||
import 'package:tm_app/data/services/liked_tenders_service.dart';
|
||||
import 'package:tm_app/data/services/notification_check_service.dart';
|
||||
import 'package:tm_app/data/services/notification_service.dart';
|
||||
import 'package:tm_app/data/services/company_ai_service.dart';
|
||||
import 'package:tm_app/data/services/notification_state_service.dart';
|
||||
import 'package:tm_app/data/services/tender_detail_service.dart';
|
||||
import 'package:tm_app/data/services/your_tenders_service.dart';
|
||||
@@ -99,6 +101,10 @@ List<SingleChildWidget> get apiClients {
|
||||
create: (context) => BoardService(networkManager: context.read()),
|
||||
lazy: true,
|
||||
),
|
||||
Provider(
|
||||
create: (context) => CompanyAiService(networkManager: context.read()),
|
||||
lazy: true,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -155,6 +161,14 @@ List<SingleChildWidget> get repositories {
|
||||
create: (context) => BoardRepository(boardService: context.read()),
|
||||
lazy: true,
|
||||
),
|
||||
Provider(
|
||||
create:
|
||||
(context) => CompanyAiRepository(
|
||||
companyAiService: context.read(),
|
||||
prefs: context.read(),
|
||||
),
|
||||
lazy: true,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -9,4 +9,13 @@ class PrefKeys {
|
||||
static const String bearer = 'bearer';
|
||||
static const String refreshToken = 'refresh_token';
|
||||
static const String customerData = 'customer_data';
|
||||
|
||||
/// Fingerprint (hash of the company's document ids + website) of the last
|
||||
/// profile version we triggered AI onboarding for. Lets us skip redundant
|
||||
/// `POST /api/v1/onboarding` calls until the company's data actually changes.
|
||||
static const String onboardingFingerprint = 'ai_onboarding_fingerprint';
|
||||
|
||||
/// Epoch milliseconds when AI onboarding was last started. Used by the
|
||||
/// Recommended tab to show a "still learning" hint for a short grace period.
|
||||
static const String onboardingStartedAt = 'ai_onboarding_started_at';
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../data/repositories/auth_repository.dart';
|
||||
import '../../data/repositories/company_ai_repository.dart';
|
||||
import '../../data/repositories/profile_repository.dart';
|
||||
import '../../view_models/profile_view_model.dart';
|
||||
|
||||
@@ -13,6 +14,7 @@ Widget profileProvider({required Widget child}) {
|
||||
(context) => ProfileViewModel(
|
||||
profileRepository: context.read<ProfileRepository>(),
|
||||
authRepository: context.read<AuthRepository>(),
|
||||
companyAiRepository: context.read<CompanyAiRepository>(),
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
|
||||
@@ -1,17 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../data/repositories/company_ai_repository.dart';
|
||||
import '../../data/repositories/home_repository.dart';
|
||||
import '../../data/repositories/tenders_repository.dart';
|
||||
import '../../view_models/ai_recommendations_view_model.dart';
|
||||
import '../../view_models/tenders_view_model.dart';
|
||||
|
||||
/// Lazy TendersViewModel provider
|
||||
/// Wraps screens that need TendersViewModel
|
||||
/// Lazy providers for the tenders section.
|
||||
///
|
||||
/// Provides [TendersViewModel] (the "All" tab) and [AiRecommendationsViewModel]
|
||||
/// (the "Recommended" AI tab) to screens that need them.
|
||||
Widget tendersProvider({required Widget child}) {
|
||||
return ChangeNotifierProvider(
|
||||
create:
|
||||
(context) => TendersViewModel(
|
||||
tendersRepository: context.read<TendersRepository>(),
|
||||
),
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider(
|
||||
create:
|
||||
(context) => TendersViewModel(
|
||||
tendersRepository: context.read<TendersRepository>(),
|
||||
),
|
||||
),
|
||||
ChangeNotifierProvider(
|
||||
create:
|
||||
(context) => AiRecommendationsViewModel(
|
||||
companyAiRepository: context.read<CompanyAiRepository>(),
|
||||
homeRepository: context.read<HomeRepository>(),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user