fix: port 7 web features and repair the endless-loading screens
Root cause behind most "stuck loading" reports: the backend changed shape (schedules became an array, location_preferences a string) while the mobile parser still hard-cast to Map/int. The TypeError was swallowed by a silent catch that returned null, and screens only handled the success state, so a parse failure rendered as a permanent spinner. Same class of bug appeared across service lists via non-null map lookups and a total absence of request timeouts. Ported from prosappweb: - in-app suggestions (POST /suggestions) - policies/terms from GET /settings/policies - configurable appointment length (slot_duration_minutes) - block/unblock calendar slots (POST /services/block) - GPS city detection on the profile (Nominatim) - server-side professional search with haversine distance - retry cooldown after a rejected professional application Reliability: - parse schedules array (day_of_week 0=Mon) and string location_preferences - read times as wall clock, so 08:00 stays 08:00 across timezones - carry minutes into hours in TimeOfDay.add; a minute-based step used to loop forever and freeze the calendar (covered by test/time_slots_test.dart) - semver update check instead of string equality, which blocked every build that did not exactly match the configured version - request timeouts across all repositories - surface HTTP >= 400 instead of reporting failed writes as success - error states with retry instead of an indefinite shimmer Includes pre-existing uncommitted work from the UI redesign. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
06a89df690
commit
8631e6f729
+275
-328
@@ -10,9 +10,21 @@ import 'package:score_repository/score_repository.dart';
|
||||
import 'package:service_repository/service_repository.dart';
|
||||
import 'package:user_repository/user_repository.dart';
|
||||
|
||||
const _kPrimary = Color(0xFF1565C0);
|
||||
|
||||
extension _Th on BuildContext {
|
||||
ThemeData get _t => Theme.of(this);
|
||||
Color get bg => _t.scaffoldBackgroundColor;
|
||||
Color get card => _t.cardColor;
|
||||
Color get onSurface => _t.colorScheme.onSurface;
|
||||
Color get muted => _t.colorScheme.onSurface.withOpacity(0.55);
|
||||
bool get isDark => _t.brightness == Brightness.dark;
|
||||
Color get shadowSm =>
|
||||
isDark ? Colors.transparent : Colors.black.withOpacity(0.05);
|
||||
}
|
||||
|
||||
class ScoreScreen extends StatefulWidget {
|
||||
final ServiceEntity service;
|
||||
|
||||
const ScoreScreen({Key? key, required this.service}) : super(key: key);
|
||||
|
||||
@override
|
||||
@@ -21,7 +33,7 @@ class ScoreScreen extends StatefulWidget {
|
||||
|
||||
class _ScoreScreenState extends State<ScoreScreen> {
|
||||
double _rating = 1.0;
|
||||
TextEditingController commentController = TextEditingController();
|
||||
final TextEditingController _commentController = TextEditingController();
|
||||
late Future<List<dynamic>> _userInfoFuture;
|
||||
late bool isProfessional;
|
||||
late String userId;
|
||||
@@ -29,12 +41,9 @@ class _ScoreScreenState extends State<ScoreScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_userInfoFuture = _getUserAndProfessionalInfo(widget.service);
|
||||
_userInfoFuture = _getUserInfo(widget.service);
|
||||
isProfessional =
|
||||
ApiUserRepository.currentUserId ?? '' == widget.service.userId
|
||||
? true
|
||||
: false;
|
||||
|
||||
(ApiUserRepository.currentUserId ?? '') == widget.service.userId;
|
||||
userId =
|
||||
isProfessional ? widget.service.professionalId : widget.service.userId;
|
||||
}
|
||||
@@ -42,10 +51,14 @@ class _ScoreScreenState extends State<ScoreScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => Injector.appInstance.get<ScoreBloc>(),
|
||||
create: (_) => Injector.appInstance.get<ScoreBloc>(),
|
||||
child: Scaffold(
|
||||
backgroundColor: context.bg,
|
||||
appBar: AppBar(
|
||||
title: const Text('Calificación'),
|
||||
title: const Text('Calificar servicio'),
|
||||
backgroundColor: _kPrimary,
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
@@ -53,311 +66,42 @@ class _ScoreScreenState extends State<ScoreScreen> {
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
FutureBuilder(
|
||||
FutureBuilder<List<dynamic>>(
|
||||
future: _userInfoFuture,
|
||||
builder:
|
||||
(context, AsyncSnapshot<List<dynamic>> snapshot) {
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.waiting) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator());
|
||||
} else {
|
||||
if (snapshot.hasError) {
|
||||
return Center(
|
||||
child:
|
||||
Text('Error inesperado: ${snapshot.error}'),
|
||||
);
|
||||
} else {
|
||||
final userInfo = snapshot.data![0] as MyUser;
|
||||
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 95,
|
||||
height: 95,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade300,
|
||||
shape: BoxShape.circle,
|
||||
image: userInfo.picture == null
|
||||
? null
|
||||
: DecorationImage(
|
||||
image: NetworkImage(
|
||||
userInfo.picture!),
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
child: userInfo.picture == null
|
||||
? Icon(
|
||||
CupertinoIcons.person,
|
||||
color: Colors.grey.shade400,
|
||||
size: 50,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'${userInfo.name}',
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
)
|
||||
],
|
||||
),
|
||||
GeneralReputation(
|
||||
userId: userId,
|
||||
builder: (BuildContext context,
|
||||
ReputationEntity reputation) {
|
||||
final double average;
|
||||
|
||||
if (isProfessional) {
|
||||
average = reputation.averagePro;
|
||||
} else {
|
||||
average = reputation.average;
|
||||
}
|
||||
|
||||
return Positioned(
|
||||
top: 3,
|
||||
right:
|
||||
MediaQuery.of(context).size.width *
|
||||
0.3,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 3,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius:
|
||||
BorderRadius.circular(20),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black
|
||||
.withOpacity(0.1),
|
||||
spreadRadius: 1,
|
||||
blurRadius: 2,
|
||||
offset: const Offset(
|
||||
0,
|
||||
1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.star,
|
||||
color: Colors.yellow,
|
||||
size: 20,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Flexible(
|
||||
child: Text(
|
||||
average.toStringAsFixed(2),
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
// child: ListTile(
|
||||
// leading: Container(
|
||||
// width: 60,
|
||||
// height: 60,
|
||||
// decoration: BoxDecoration(
|
||||
// color: Colors.grey.shade300,
|
||||
// shape: BoxShape.circle,
|
||||
// image: userInfo.picture == null
|
||||
// ? null
|
||||
// : DecorationImage(
|
||||
// image: NetworkImage(
|
||||
// userInfo.picture ?? ''),
|
||||
// fit: BoxFit.contain,
|
||||
// ),
|
||||
// ),
|
||||
// child: userInfo.picture == null
|
||||
// ? Icon(
|
||||
// CupertinoIcons.person,
|
||||
// color: Colors.grey.shade400,
|
||||
// size: 40,
|
||||
// )
|
||||
// : null,
|
||||
// ),
|
||||
// title: Text(
|
||||
// userInfo.name ?? '',
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// style: const TextStyle(
|
||||
// fontSize: 15,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// ),
|
||||
// ),
|
||||
// subtitle: const Text(
|
||||
// 'Rating: 5.0',
|
||||
// style: TextStyle(
|
||||
// fontSize: 13,
|
||||
// color: Colors.blue,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
return const Padding(
|
||||
padding: EdgeInsets.all(40),
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
if (snapshot.hasError) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Text('Error: ${snapshot.error}'),
|
||||
);
|
||||
}
|
||||
final userInfo = snapshot.data![0] as MyUser;
|
||||
return _profileHeader(context, userInfo);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
const Text(
|
||||
'Califica el servicio',
|
||||
style:
|
||||
TextStyle(fontSize: 20, fontWeight: FontWeight.w600),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
RatingBar.builder(
|
||||
initialRating: _rating,
|
||||
minRating: 1,
|
||||
direction: Axis.horizontal,
|
||||
allowHalfRating: true,
|
||||
itemCount: 5,
|
||||
itemSize: 40,
|
||||
glow: false,
|
||||
maxRating: 5,
|
||||
itemPadding: const EdgeInsets.symmetric(horizontal: 5),
|
||||
itemBuilder: (context, _) => const Icon(
|
||||
Icons.star,
|
||||
color: Color(0xFF2BA4EC),
|
||||
),
|
||||
onRatingUpdate: (rating) {
|
||||
setState(() {
|
||||
_rating = rating;
|
||||
});
|
||||
},
|
||||
ignoreGestures: false,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: Text(
|
||||
customMessage(_rating),
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
color: Color(0xFF2BA4EC),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 40, vertical: 40),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Comentario',
|
||||
style: TextStyle(
|
||||
fontSize: 20, fontWeight: FontWeight.w600),
|
||||
),
|
||||
TextFormField(
|
||||
maxLines: null,
|
||||
maxLength: 400,
|
||||
keyboardType: TextInputType.multiline,
|
||||
controller: commentController,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
_ratingSection(context),
|
||||
_commentSection(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
height: 1,
|
||||
thickness: 0.5,
|
||||
),
|
||||
const Divider(height: 1, thickness: 0.5),
|
||||
BlocProvider<ServiceBloc>(
|
||||
create: (context) => Injector.appInstance.get<ServiceBloc>(),
|
||||
create: (_) => Injector.appInstance.get<ServiceBloc>(),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
horizontal: 15,
|
||||
),
|
||||
vertical: 12, horizontal: 16),
|
||||
child: BlocBuilder<ServiceBloc, ServiceState>(
|
||||
builder: (context, serviceState) {
|
||||
return FilledButton(
|
||||
onPressed: () {
|
||||
final CommentEntity comment = widget.service.userId ==
|
||||
ApiUserRepository.currentUserId ?? ''
|
||||
? CommentEntity(
|
||||
serviceId: widget.service.id!,
|
||||
authorId:
|
||||
ApiUserRepository.currentUserId ?? '',
|
||||
isFromUser: true,
|
||||
score: _rating,
|
||||
destinationId: widget.service.professionalId,
|
||||
content: commentController.text.trim(),
|
||||
createdAt: DateTime.now().toIso8601String(),
|
||||
)
|
||||
: CommentEntity(
|
||||
serviceId: widget.service.id!,
|
||||
authorId:
|
||||
ApiUserRepository.currentUserId ?? '',
|
||||
isFromUser: false,
|
||||
score: _rating,
|
||||
destinationId: widget.service.userId,
|
||||
content: commentController.text.trim(),
|
||||
createdAt: DateTime.now().toIso8601String(),
|
||||
);
|
||||
|
||||
BlocProvider.of<ScoreBloc>(context)
|
||||
.add(SendScoreEvent(comment: comment));
|
||||
|
||||
if (widget.service.userId ==
|
||||
ApiUserRepository.currentUserId ?? '') {
|
||||
context.read<ServiceBloc>().add(
|
||||
UpdateProfessionalScored(widget.service.id!));
|
||||
} else {
|
||||
context
|
||||
.read<ServiceBloc>()
|
||||
.add(UpdateUserScored(widget.service.id!));
|
||||
}
|
||||
|
||||
Navigator.pop(context);
|
||||
},
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
padding: const EdgeInsets.symmetric(vertical: 15),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
width: double.infinity,
|
||||
child: const Text(
|
||||
'Enviar calificación',
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return _sendButton(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -368,38 +112,241 @@ class _ScoreScreenState extends State<ScoreScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
String customMessage(double rating) {
|
||||
if (rating > 4.0) {
|
||||
return '¡Excelente! 👏🌟';
|
||||
}
|
||||
|
||||
if (rating < 2.0) {
|
||||
return '¡Malo! 😔❌';
|
||||
}
|
||||
|
||||
if (rating <= 4.0 && rating >= 3.0) {
|
||||
return '¡Bueno! 👍😊';
|
||||
}
|
||||
|
||||
if (rating < 3.0 && rating >= 2.0) {
|
||||
return '¡Regular! 😐🔄';
|
||||
}
|
||||
|
||||
return '';
|
||||
Widget _profileHeader(BuildContext context, MyUser user) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.all(16),
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: context.card,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.shadowSm,
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, 2))
|
||||
],
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 84,
|
||||
height: 84,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: _kPrimary.withOpacity(0.25), width: 3),
|
||||
color: Colors.grey.shade200,
|
||||
image: user.picture != null && user.picture!.isNotEmpty
|
||||
? DecorationImage(
|
||||
image: NetworkImage(user.picture!),
|
||||
fit: BoxFit.cover)
|
||||
: null,
|
||||
),
|
||||
child: user.picture == null || user.picture!.isEmpty
|
||||
? Icon(CupertinoIcons.person,
|
||||
color: Colors.grey.shade500, size: 38)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(user.name ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: context.onSurface)),
|
||||
],
|
||||
),
|
||||
GeneralReputation(
|
||||
userId: userId,
|
||||
builder: (_, ReputationEntity reputation) {
|
||||
final average = isProfessional
|
||||
? reputation.averagePro
|
||||
: reputation.average;
|
||||
return Positioned(
|
||||
top: 0,
|
||||
right: MediaQuery.of(context).size.width * 0.18,
|
||||
child: Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: context.card,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: context.shadowSm,
|
||||
blurRadius: 6,
|
||||
spreadRadius: 1)
|
||||
],
|
||||
),
|
||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
const Icon(Icons.star, color: Colors.amber, size: 14),
|
||||
const SizedBox(width: 3),
|
||||
Text(average.toStringAsFixed(1),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: context.onSurface)),
|
||||
]),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<dynamic>> _getUserAndProfessionalInfo(
|
||||
ServiceEntity service) async {
|
||||
Widget _ratingSection(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: context.card,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [BoxShadow(color: context.shadowSm, blurRadius: 8)],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text('¿Cómo fue el servicio?',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: context.onSurface)),
|
||||
const SizedBox(height: 14),
|
||||
RatingBar.builder(
|
||||
initialRating: _rating,
|
||||
minRating: 1,
|
||||
direction: Axis.horizontal,
|
||||
allowHalfRating: true,
|
||||
itemCount: 5,
|
||||
itemSize: 42,
|
||||
glow: false,
|
||||
maxRating: 5,
|
||||
itemPadding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
itemBuilder: (_, __) =>
|
||||
const Icon(Icons.star_rounded, color: _kPrimary),
|
||||
onRatingUpdate: (r) => setState(() => _rating = r),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
_ratingMessage(_rating),
|
||||
style:
|
||||
TextStyle(fontSize: 14, color: _kPrimary, fontWeight: FontWeight.w500),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _commentSection(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.fromLTRB(16, 12, 16, 16),
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: context.card,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: [BoxShadow(color: context.shadowSm, blurRadius: 8)],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Comentario (opcional)',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: context.onSurface)),
|
||||
const SizedBox(height: 10),
|
||||
TextFormField(
|
||||
controller: _commentController,
|
||||
maxLines: 4,
|
||||
maxLength: 400,
|
||||
keyboardType: TextInputType.multiline,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Cuéntanos tu experiencia...',
|
||||
hintStyle: TextStyle(color: context.muted, fontSize: 13),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide(color: Colors.grey.shade300),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(color: _kPrimary, width: 2),
|
||||
),
|
||||
contentPadding: const EdgeInsets.all(12),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _sendButton(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
final isUser = widget.service.userId ==
|
||||
(ApiUserRepository.currentUserId ?? '');
|
||||
final comment = CommentEntity(
|
||||
serviceId: widget.service.id!,
|
||||
authorId: ApiUserRepository.currentUserId ?? '',
|
||||
isFromUser: isUser,
|
||||
score: _rating,
|
||||
destinationId: isUser
|
||||
? widget.service.professionalId
|
||||
: widget.service.userId,
|
||||
content: _commentController.text.trim(),
|
||||
createdAt: DateTime.now().toIso8601String(),
|
||||
);
|
||||
BlocProvider.of<ScoreBloc>(context)
|
||||
.add(SendScoreEvent(comment: comment));
|
||||
if (isUser) {
|
||||
context
|
||||
.read<ServiceBloc>()
|
||||
.add(UpdateProfessionalScored(widget.service.id!));
|
||||
} else {
|
||||
context
|
||||
.read<ServiceBloc>()
|
||||
.add(UpdateUserScored(widget.service.id!));
|
||||
}
|
||||
Navigator.pop(context);
|
||||
},
|
||||
icon: const Icon(Icons.send_rounded, size: 18),
|
||||
label: const Text('Enviar calificación',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: _kPrimary,
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _ratingMessage(double rating) {
|
||||
if (rating > 4.0) return '¡Excelente!';
|
||||
if (rating < 2.0) return 'Necesita mejorar';
|
||||
if (rating >= 3.0) return '¡Bien!';
|
||||
return 'Regular';
|
||||
}
|
||||
|
||||
Future<List<dynamic>> _getUserInfo(ServiceEntity service) async {
|
||||
final userRepo = Injector.appInstance.get<UserRepository>();
|
||||
|
||||
final MyUser? userInfo;
|
||||
|
||||
if (ApiUserRepository.currentUserId ?? '' == service.userId) {
|
||||
userInfo = await userRepo.getMyUser(service.professionalId);
|
||||
} else {
|
||||
userInfo = await userRepo.getMyUser(service.userId);
|
||||
}
|
||||
|
||||
final currentId = ApiUserRepository.currentUserId ?? '';
|
||||
final targetId =
|
||||
currentId == service.userId ? service.professionalId : service.userId;
|
||||
final userInfo = await userRepo.getMyUser(targetId);
|
||||
return [userInfo];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user