puntacion

This commit is contained in:
Felipe
2024-04-17 00:48:46 -05:00
parent 61d517aaea
commit 141d11a141
20 changed files with 1078 additions and 77 deletions
+4 -1
View File
@@ -6,7 +6,7 @@ import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart';
import 'package:prosappco/blocs/professional_bloc/professional_bloc.dart';
import 'package:prosappco/blocs/professional_profile_bloc/professional_profile_bloc.dart';
import 'package:prosappco/blocs/profile_bloc/profile_bloc.dart';
import 'package:prosappco/blocs/service_bloc/service_bloc.dart';
import 'package:prosappco/blocs/score_bloc/score_bloc.dart';
import 'app_view.dart';
@@ -23,6 +23,9 @@ class MainApp extends StatelessWidget {
BlocProvider<MyUserBloc>(
create: (context) => Injector.appInstance.get<MyUserBloc>(),
),
BlocProvider<ScoreBloc>(
create: (context) => Injector.appInstance.get(),
),
BlocProvider<ProfileBloc>(
create: (context) => Injector.appInstance.get<ProfileBloc>(),
),
+41
View File
@@ -0,0 +1,41 @@
import 'dart:developer';
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:score_repository/score_repository.dart';
part 'score_event.dart';
part 'score_state.dart';
class ScoreBloc extends Bloc<ScoreEvent, ScoreState> {
final FirebaseScoreRepository _firebaseScoreRepository;
ScoreBloc({
required FirebaseScoreRepository firebaseScoreRepository,
}) : _firebaseScoreRepository = firebaseScoreRepository,
super(ScoreInitial()) {
try {
emit(ScoreSuccess(_firebaseScoreRepository.getReputation()));
} catch (e) {
log("siteriespierdes1" + e.toString());
}
_firebaseScoreRepository.streamReputation().listen((event) {
try {
emit(ScoreSuccess(event));
} catch (e) {
// log
log("siteriespierdes2" + e.toString());
}
});
on<SendScoreEvent>(_onSendScoreEvent);
}
void _onSendScoreEvent(SendScoreEvent event, Emitter<ScoreState> emit) async {
try {
await _firebaseScoreRepository.addComment(event.comment);
} catch (e) {
log("siteriespierdes3" + e.toString());
}
}
}
+17
View File
@@ -0,0 +1,17 @@
part of 'score_bloc.dart';
abstract class ScoreEvent extends Equatable {
const ScoreEvent();
@override
List<Object> get props => [];
}
class SendScoreEvent extends ScoreEvent {
final CommentEntity comment;
const SendScoreEvent({required this.comment});
@override
List<Object> get props => [comment];
}
+22
View File
@@ -0,0 +1,22 @@
part of 'score_bloc.dart';
abstract class ScoreState extends Equatable {
const ScoreState();
@override
List<Object> get props => [];
}
class ScoreInitial extends ScoreState {}
class ScoreFailure extends ScoreState {}
class ScoreLoading extends ScoreState {}
class ScoreSuccess extends ScoreState {
final ReputationEntity reputation;
const ScoreSuccess(this.reputation);
@override
List<Object> get props => [reputation];
}
+23
View File
@@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:prosappco/blocs/score_bloc/score_bloc.dart';
import 'package:score_repository/score_repository.dart';
class DrawerReputation extends StatelessWidget {
final Widget Function(ReputationEntity) builder;
const DrawerReputation({super.key, required this.builder});
@override
Widget build(BuildContext context) {
return BlocBuilder<ScoreBloc, ScoreState>(builder: (context, state) {
if (state is ScoreSuccess) {
return builder(state.reputation);
} else if (state is ScoreFailure) {
return const Center(child: Text('Error'));
} else {
return const Center(child: CircularProgressIndicator());
}
});
}
}
+67
View File
@@ -1,9 +1,14 @@
import 'dart:developer';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:prosappco/blocs/my_user_bloc/my_user_bloc.dart';
import 'package:prosappco/blocs/professional_bloc/professional_bloc.dart';
import 'package:prosappco/components/drawer_reputation.dart';
import 'package:prosappco/components/general_drawer_header.dart';
import 'package:prosappco/components/general_drawer_item.dart';
import 'package:prosappco/screens/configuration/configuration_screen.dart';
@@ -209,6 +214,51 @@ class GeneralDrawer extends StatelessWidget {
),
),
),
DrawerReputation(builder: (reputation) {
final isProModeActive =
(professionalState is LoadedModeProState) &&
professionalState.isProModeActive;
final total = isProModeActive
? reputation.totalPro
: reputation.total;
final average = isProModeActive
? reputation.averagePro
: reputation.average;
return ListTile(
onTap: () {},
trailing: const Icon(Icons.keyboard_arrow_right,
color: Colors.black),
title: const Text(
'Reputación',
style: TextStyle(color: Colors.black),
),
subtitle: Row(
children: [
RatingBar.builder(
initialRating: calculoRating(average),
minRating: 1,
direction: Axis.horizontal,
allowHalfRating: true,
itemCount: 5,
itemSize: 25,
maxRating: 5,
itemBuilder: (context, _) => const Icon(
Icons.star,
color: Color(0xFF2BA4EC),
),
onRatingUpdate: (rating) {},
ignoreGestures: true,
),
const SizedBox(width: 5),
Text(
'${average.toStringAsFixed(1)} (${total.toString()})',
),
],
));
}),
Padding(
padding: const EdgeInsets.only(top: 10),
child: Text(
@@ -246,6 +296,23 @@ class GeneralDrawer extends StatelessWidget {
);
}
double calculoRating(double average) {
String numeroString = average.toString();
List<String> partes = numeroString.split('.');
int parteEntera = int.parse(partes[0]);
int parteFraccionaria = partes.length > 1 ? int.parse(partes[1]) : 0;
if (parteFraccionaria >= 3) {
parteFraccionaria = 5;
} else {
parteFraccionaria = 0;
}
// Unir la parte entera y fraccionaria y convertirlo nuevamente a double
double resultado = double.parse('$parteEntera.$parteFraccionaria');
return resultado;
}
buttonOfState(BuildContext context, ProfessionalState state) {
return ElevatedButton(
onPressed: () {
+31
View File
@@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:injector/injector.dart';
import 'package:score_repository/score_repository.dart';
class GeneralReputation extends StatelessWidget {
final String userId;
final Widget Function(BuildContext, ReputationEntity) builder;
const GeneralReputation({
super.key,
required this.userId,
required this.builder,
});
@override
Widget build(BuildContext context) {
final repository = Injector.appInstance.get<FirebaseScoreRepository>();
return FutureBuilder(
future: repository.getReputationByUserId(userId),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
final reputation = snapshot.data!;
return builder(context, reputation);
}
});
}
}
+5 -1
View File
@@ -11,10 +11,12 @@ import 'package:prosappco/blocs/professional_bloc/professional_bloc.dart';
import 'package:prosappco/blocs/professional_list_bloc/professional_list_bloc.dart';
import 'package:prosappco/blocs/professional_profile_bloc/professional_profile_bloc.dart';
import 'package:prosappco/blocs/profile_bloc/profile_bloc.dart';
import 'package:prosappco/blocs/score_bloc/score_bloc.dart';
import 'package:prosappco/blocs/service_bloc/service_bloc.dart';
import 'package:prosappco/blocs/setting_bloc/setting_bloc.dart';
import 'package:prosappco/blocs/sign_up_bloc/sign_up_bloc.dart';
import 'package:prosappco/blocs/sing_in_bloc/sign_in_bloc.dart';
import 'package:score_repository/score_repository.dart';
import 'package:service_repository/service_repository.dart';
import 'package:user_repository/user_repository.dart';
import 'package:city_repository/city_repository.dart';
@@ -38,13 +40,15 @@ class AppDI {
injector.registerSingleton(() => FirebaseProfessionalRepository());
injector.registerSingleton(() => FirebaseServiceRepository());
injector.registerSingleton(() => FirebaseChatRepository());
injector.registerSingleton(() => FirebaseScoreRepository());
injector.registerSingleton<AuthenticationBloc>((() =>
AuthenticationBloc(myUserRepository: injector.get<UserRepository>())));
injector.registerSingleton<MyUserBloc>(
(() => MyUserBloc(myUserRepository: injector.get<UserRepository>())));
injector.registerSingleton(
(() => ScoreBloc(firebaseScoreRepository: injector.get())));
injector.registerSingleton<ProfileBloc>(
(() => ProfileBloc(userRepository: injector.get<UserRepository>())));
+1 -2
View File
@@ -59,8 +59,7 @@ class _ChatScreenState extends State<ChatScreen> {
children: [
FutureBuilder(
future: _getUserAndProfessionalInfo(widget.service),
builder: (BuildContext context,
AsyncSnapshot<List<dynamic>> snapshot) {
builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else {
+228
View File
@@ -0,0 +1,228 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
import 'package:prosappco/blocs/score_bloc/score_bloc.dart';
import 'package:score_repository/score_repository.dart';
import 'package:service_repository/service_repository.dart';
import 'package:user_repository/user_repository.dart';
class ScoreScreen extends StatefulWidget {
final ServiceEntity service;
const ScoreScreen({Key? key, required this.service}) : super(key: key);
@override
State<ScoreScreen> createState() => _ScoreScreenState();
}
class _ScoreScreenState extends State<ScoreScreen> {
double _rating = 1.0;
TextEditingController commentController = TextEditingController();
late Future<List<dynamic>> _userInfoFuture;
@override
void initState() {
super.initState();
_userInfoFuture = _getUserAndProfessionalInfo(widget.service);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Calificación'),
),
body: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
FutureBuilder(
future: _userInfoFuture,
builder: (context, AsyncSnapshot<List<dynamic>> 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 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,
),
),
),
);
}
}
},
),
const SizedBox(height: 10),
const Text(
'Calificación',
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.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 Divider(
height: 1,
thickness: 0.5,
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 10,
horizontal: 15,
),
child: FilledButton(
onPressed: () {
final CommentEntity comment = widget.service.userId ==
FirebaseAuth.instance.currentUser!.uid
? CommentEntity(
serviceId: widget.service.id!,
authorId: FirebaseAuth.instance.currentUser!.uid,
isFromUser: true,
score: _rating,
destinationId: widget.service.professionalId,
content: commentController.text,
createdAt: Timestamp.now(),
)
: CommentEntity(
serviceId: widget.service.id!,
authorId: FirebaseAuth.instance.currentUser!.uid,
isFromUser: false,
score: _rating,
destinationId: widget.service.userId,
content: commentController.text,
createdAt: Timestamp.now(),
);
BlocProvider.of<ScoreBloc>(context)
.add(SendScoreEvent(comment: comment));
},
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,
),
),
),
),
),
],
),
);
}
Future<List<dynamic>> _getUserAndProfessionalInfo(
ServiceEntity service) async {
final userRepo = FirebaseUserRepository(FirebaseAuth.instance);
final MyUser? userInfo;
if (FirebaseAuth.instance.currentUser!.uid == service.userId) {
userInfo = await userRepo.getMyUser(service.userId);
} else {
userInfo = await userRepo.getMyUser(service.professionalId);
}
return [userInfo];
}
}
@@ -9,6 +9,7 @@ import 'package:professional_repository/professional_repository.dart';
import 'package:prosappco/blocs/service_bloc/service_bloc.dart';
import 'package:prosappco/components/general_secondary_button.dart';
import 'package:prosappco/screens/chat/chat_screen.dart';
import 'package:prosappco/screens/score/score_screen.dart';
import 'package:service_repository/service_repository.dart';
import 'package:setting_repository/setting_repository.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -56,7 +57,8 @@ class _ProfessionalServiceScreenState extends State<ProfessionalServiceScreen> {
final service = state.service;
return FutureBuilder(
future: _getUserAndProfessionalInfo(service),
builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) {
builder: (BuildContext context,
AsyncSnapshot<List<dynamic>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else {
@@ -515,73 +517,91 @@ class _ProfessionalServiceScreenState extends State<ProfessionalServiceScreen> {
);
}
// if (service.status == ServiceStatus.active) {
// return Stack(
// alignment: AlignmentDirectional.topCenter,
// clipBehavior: Clip.none,
// children: [
// Card(
// color: Colors.green.shade50,
// child: const Padding(
// padding: EdgeInsets.fromLTRB(32, 56, 32, 32),
// child: Text(
// 'En proceso...',
// style: TextStyle(fontSize: 32, color: Colors.green),
// ),
// ),
// ),
// Positioned(
// top: -40,
// child: Container(
// padding: const EdgeInsets.all(16),
// decoration: BoxDecoration(
// color: Colors.white,
// border: Border.all(color: Colors.green.shade50, width: 4),
// shape: BoxShape.circle,
// ),
// child: const Icon(
// Icons.access_time_rounded,
// color: Colors.green,
// size: 48,
// ),
// ),
// )
// ],
// );
// }
if (service.status == ServiceStatus.completed) {
return Stack(
alignment: AlignmentDirectional.topCenter,
clipBehavior: Clip.none,
children: [
Card(
color: Colors.green.shade50,
child: const Padding(
padding: EdgeInsets.fromLTRB(32, 56, 32, 32),
child: Text(
'Completado',
style: TextStyle(fontSize: 32, color: Colors.green),
if (service.userScored) {
return Stack(
alignment: AlignmentDirectional.topCenter,
clipBehavior: Clip.none,
children: [
Card(
color: Colors.green.shade50,
child: const Padding(
padding: EdgeInsets.fromLTRB(32, 56, 32, 32),
child: Text(
'Completado',
style: TextStyle(fontSize: 32, color: Colors.green),
),
),
),
),
Positioned(
top: -40,
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.green.shade50, width: 4),
shape: BoxShape.circle,
Positioned(
top: -40,
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.green.shade50, width: 4),
shape: BoxShape.circle,
),
child: const Icon(
Icons.check_rounded,
color: Colors.green,
size: 48,
),
),
child: const Icon(
Icons.check_rounded,
color: Colors.green,
size: 48,
)
],
);
} else {
return Stack(
alignment: AlignmentDirectional.topCenter,
clipBehavior: Clip.none,
children: [
Card(
color: Colors.green.shade50,
child: Padding(
padding: EdgeInsets.fromLTRB(32, 56, 32, 32),
child: Column(
children: [
Text(
'Completado',
style: TextStyle(fontSize: 32, color: Colors.green),
),
SizedBox(height: 16),
FilledButton(
onPressed: () {
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => ScoreScreen(
service: service,
),
),
);
},
child: Text('Calificar'))
],
),
),
),
)
],
);
Positioned(
top: -40,
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: Colors.green.shade50, width: 4),
shape: BoxShape.circle,
),
child: const Icon(
Icons.star,
color: Colors.green,
size: 48,
),
),
)
],
);
}
}
return const SizedBox();
@@ -627,16 +647,6 @@ class _ProfessionalServiceScreenState extends State<ProfessionalServiceScreen> {
}
},
);
// customStatusButton(
// label: 'Iniciar servicio',
// onPressed: () {
// final currentState = context.read<ServiceBloc>().state;
// if (currentState is ServiceLoaded) {
// context.read<ServiceBloc>().add(
// UpdateServiceStatus(widget.serviceId, ServiceStatus.active));
// }
// },
// );
}
if (service.status == ServiceStatus.active) {
@@ -670,7 +680,8 @@ class _ProfessionalServiceScreenState extends State<ProfessionalServiceScreen> {
return '\$${formatter.format(number)}';
}
Future<List<dynamic>> _getUserAndProfessionalInfo(ServiceEntity service) async {
Future<List<dynamic>> _getUserAndProfessionalInfo(
ServiceEntity service) async {
final userRepo = FirebaseUserRepository(FirebaseAuth.instance);
final userInfo = await userRepo.getMyUser(service.userId);