diff --git a/lib/features/matkul/detail/presentation/pages/_pages.dart b/lib/features/matkul/detail/presentation/pages/_pages.dart index 35de4bf..0287877 100644 --- a/lib/features/matkul/detail/presentation/pages/_pages.dart +++ b/lib/features/matkul/detail/presentation/pages/_pages.dart @@ -15,6 +15,8 @@ import 'package:ulaskelas/features/matkul/form/presentation/states/_states.dart' import 'package:ulaskelas/features/matkul/search/data/models/_models.dart'; import 'package:ulaskelas/features/matkul/search/presentation/widgets/_widgets.dart'; +import '../../../../../services/_services.dart'; + part 'detail_matkul_page.dart'; part 'review_matkul_page.dart'; part 'all_review_matkul_page.dart'; diff --git a/lib/features/matkul/detail/presentation/pages/detail_matkul_page.dart b/lib/features/matkul/detail/presentation/pages/detail_matkul_page.dart index 3cffc58..8ddb555 100644 --- a/lib/features/matkul/detail/presentation/pages/detail_matkul_page.dart +++ b/lib/features/matkul/detail/presentation/pages/detail_matkul_page.dart @@ -59,12 +59,10 @@ class _DetailMatkulPageState extends BaseStateful { bool get _isBottom { if (!scrollController.hasClients) { - print('no client'); return false; } final maxScroll = scrollController.position.maxScrollExtent; final currentScroll = scrollController.offset; - print(currentScroll >= (maxScroll * 0.9)); return currentScroll >= (maxScroll * 0.9); } @@ -127,10 +125,22 @@ class _DetailMatkulPageState extends BaseStateful { const HeightSpace(16), if (course.reviewCount! > 3) InkWell( - onTap: () => nav.goToAllReviewMatkulPage( - courseId: widget.courseId, - courseCode: widget.courseCode, - ), + onTap: () { + nav.goToAllReviewMatkulPage( + courseId: widget.courseId, + courseCode: widget.courseCode, + ); + MixpanelService.track( + 'view_all_reviews', + params: { + 'course_id': course.code.toString(), + 'course_name': course.name.toString(), + 'review_count': course.reviewCount.toString(), + 'course_rating_avg': + course.ratingAverage.toString(), + }, + ); + }, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -229,6 +239,15 @@ class _DetailMatkulPageState extends BaseStateful { review: review, onLiked: () { reviewCourseRM.state.like(review); + MixpanelService.track( + 'like_review', + params: { + 'course_id': review.courseCode.toString(), + 'course_name': review.courseName.toString(), + 'review_count': review.likesCount.toString(), + 'course_rating_avg': review.ratingAverage.toString(), + }, + ); }, ); }, diff --git a/lib/features/matkul/detail/presentation/widgets/review_card.dart b/lib/features/matkul/detail/presentation/widgets/review_card.dart index a8ff4d2..a492059 100644 --- a/lib/features/matkul/detail/presentation/widgets/review_card.dart +++ b/lib/features/matkul/detail/presentation/widgets/review_card.dart @@ -2,7 +2,8 @@ part of '_widgets.dart'; class ReviewCard extends StatelessWidget { const ReviewCard({ - required this.review, super.key, + required this.review, + super.key, this.imgUrl, this.onLiked, this.status, @@ -75,7 +76,8 @@ class ReviewCard extends StatelessWidget { ? TagStatus.pending : TagStatus.rejected, ), - if (status == null && review.rankTop20 != null && + if (status == null && + review.rankTop20 != null && review.rankTop20! > 0 && review.rankTop20! < 21) TagLeaderboard( @@ -94,8 +96,7 @@ class ReviewCard extends StatelessWidget { PopupMenu( username: review.author.toString(), isAnonymous: review.isAnonym ?? false, - reviewId: review.id!, - // userId: review.author, + review: review, ), Container(), ], @@ -187,20 +188,22 @@ class ReviewCard extends StatelessWidget { class PopupMenu extends StatelessWidget { const PopupMenu({ - required this.username, required this.reviewId, super.key, + required this.username, + required this.review, + super.key, this.isAnonymous = false, }); final String username; final bool isAnonymous; - final int reviewId; + final ReviewModel review; @override Widget build(BuildContext context) { return PopupMenuButton( padding: EdgeInsets.zero, iconSize: 18, - onSelected: (value) => _onSelected(context, value), + onSelected: (value) => _onSelected(context, value, review), itemBuilder: (context) => [ _buildItem( value: 1, @@ -238,13 +241,22 @@ class PopupMenu extends StatelessWidget { ); } - void _onSelected(BuildContext context, int value) { + void _onSelected(BuildContext context, int value, ReviewModel review) { switch (value) { case 1: + MixpanelService.track( + 'report_review', + params: { + 'course_id': review.courseCode.toString(), + 'course_name': review.courseName.toString(), + 'previous_likes': review.likesCount.toString(), + 'score_given_by_review': review.courseReviewCount.toString(), + }, + ); LaunchServices.openEmail( 'team@ristek.cs.ui.ac.id', ''' -Report User Content for ${isAnonymous ? 'Review id $reviewId' : username}''', +Report User Content for ${isAnonymous ? 'Review id ${review.id}' : username}''', 'enter report message', ); default: diff --git a/lib/features/matkul/form/presentation/pages/_pages.dart b/lib/features/matkul/form/presentation/pages/_pages.dart index 2773534..fd02e45 100644 --- a/lib/features/matkul/form/presentation/pages/_pages.dart +++ b/lib/features/matkul/form/presentation/pages/_pages.dart @@ -15,6 +15,7 @@ import 'package:ulaskelas/features/matkul/form/presentation/states/_states.dart' import 'package:ulaskelas/features/matkul/form/presentation/widgets/_widgets.dart'; import 'package:ulaskelas/features/matkul/search/data/models/_models.dart'; import 'package:ulaskelas/features/matkul/search/presentation/widgets/_widgets.dart'; +import 'package:ulaskelas/services/_services.dart'; part 'add_review_matkul_tag_page.dart'; part 'review_matkul_form_page.dart'; diff --git a/lib/features/matkul/form/presentation/pages/review_matkul_form_page.dart b/lib/features/matkul/form/presentation/pages/review_matkul_form_page.dart index bbcfb8f..dabe3c8 100644 --- a/lib/features/matkul/form/presentation/pages/review_matkul_form_page.dart +++ b/lib/features/matkul/form/presentation/pages/review_matkul_form_page.dart @@ -4,7 +4,8 @@ part of '_pages.dart'; class ReviewMatkulFormPage extends StatefulWidget { const ReviewMatkulFormPage({ - required this.course, super.key, + required this.course, + super.key, }); final CourseModel course; @@ -132,6 +133,22 @@ class _ReviewMatkulFormPageState extends BaseStateful { await reviewFormRM.state .submitForm(widget.course.code!); await Future.delayed(const Duration(milliseconds: 150)); + MixpanelService.track( + 'write_review', + params: { + 'course_id': widget.course.code.toString(), + 'course_name': widget.course.name.toString(), + 'created_at': DateTime.now().toString(), + 'period_taking': + reviewFormStateData.semester.toString(), + 'year_taking': reviewFormStateData.year.toString(), + 'avg_rating_given': + reviewFormState.getAvgRating().toString(), + 'tags': reviewFormStateData.tagData.toString(), + 'anonymous_review': + reviewFormStateData.isAnonymous.toString(), + }, + ); reviewFormRM.state.cleanForm(); nav.pop(); await nav.replaceToReviewPendingPage(); diff --git a/lib/features/matkul/form/presentation/states/review_course_form_state.dart b/lib/features/matkul/form/presentation/states/review_course_form_state.dart index eb567e9..c1c9df1 100644 --- a/lib/features/matkul/form/presentation/states/review_course_form_state.dart +++ b/lib/features/matkul/form/presentation/states/review_course_form_state.dart @@ -112,6 +112,14 @@ class ReviewCourseFormState { _formData.ratingRecommended = ratingRecommended; } + double getAvgRating() { + return ((_formData.ratingBeneficial ?? 0) + + (_formData.ratingFitToCredit ?? 0) + + (_formData.ratingFitToStudyBook ?? 0) + + (_formData.ratingRecommended ?? 0) + + (_formData.ratingUnderstandable ?? 0)) / 5; + } + /// Cleaning form when success submitting form void cleanForm() { _formData = ReviewMatkulData(); diff --git a/lib/features/profile/presentation/pages/profile_page.dart b/lib/features/profile/presentation/pages/profile_page.dart index 4356971..ea4bb4d 100644 --- a/lib/features/profile/presentation/pages/profile_page.dart +++ b/lib/features/profile/presentation/pages/profile_page.dart @@ -47,34 +47,32 @@ class _ProfilePageState extends BaseStateful { vertical: 10, ), child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - const SizedBox(height: 42), - Icon( - Icons.account_circle, - size: 140, - color: Colors.grey[300], - ), - // CircleAvatar( - // radius: 100, - // backgroundColor: Colors.grey[300], - // ), - const SizedBox(height: 34), - ProfileData( - 'Nama', - profileRM.state.profile.name.toString(), - ), - // TODO(pawpaw): angkatan. - ProfileData( - 'Angkatan', - profileRM.state.profile.generation.toString(), - ), - ProfileData( - 'Jurusan', - profileRM.state.profile.studyProgram.toString(), - ), - const Expanded( - child: SizedBox(), + children: [ + Expanded( + child: ListView( + children: [ + const SizedBox(height: 42), + Icon( + Icons.account_circle, + size: 140, + color: Colors.grey[300], + ), + const SizedBox(height: 24), + ProfileData( + 'Nama', + profileRM.state.profile.name.toString(), + ), + // TODO(pawpaw): angkatan. + ProfileData( + 'Angkatan', + profileRM.state.profile.generation.toString(), + ), + ProfileData( + 'Jurusan', + profileRM.state.profile.studyProgram.toString(), + ), + ], + ), ), Center( child: InkWell( @@ -90,7 +88,7 @@ class _ProfilePageState extends BaseStateful { ), ), ), - const HeightSpace(30), + const HeightSpace(24), Center( child: InkWell( onTap: () { @@ -106,7 +104,7 @@ class _ProfilePageState extends BaseStateful { ), ), ), - const HeightSpace(30), + const HeightSpace(24), SecondaryButton( width: double.infinity, text: 'Keluar', diff --git a/lib/main_page.dart b/lib/main_page.dart index 1a59ecf..60aabde 100644 --- a/lib/main_page.dart +++ b/lib/main_page.dart @@ -72,13 +72,17 @@ class _MainPageState extends BaseStateful { return RistekBotNavBar( initialActiveIndex: _selectedIndex, onTap: (int index) { - setState(() => _selectedIndex = index); switch (index) { + case 1: + MixpanelService.track('open_courses'); + case 2: + MixpanelService.track('open_klasemen'); case 3: MixpanelService.track('open_calculator'); case 4: MixpanelService.track('open_profile'); } + setState(() => _selectedIndex = index); }, items: const [ RistekBotNavItem(