diff --git a/project/FrontEnd/collaborative_science_platform/lib/models/basic_user.dart b/project/FrontEnd/collaborative_science_platform/lib/models/basic_user.dart index a8d635af..a98af6e0 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/models/basic_user.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/models/basic_user.dart @@ -3,12 +3,14 @@ class BasicUser { String bio; bool emailNotificationPreference; bool showActivity; + String userType; BasicUser({ - required this.basicUserId, - required this.bio, - required this.emailNotificationPreference, - required this.showActivity, + this.basicUserId = 0, + this.bio = "", + this.emailNotificationPreference = true, + this.showActivity = true, + this.userType = "", }); factory BasicUser.fromJson(Map jsonString) { return BasicUser( @@ -16,7 +18,7 @@ class BasicUser { bio: jsonString["bio"], emailNotificationPreference: jsonString["email_notification_preference"], showActivity: jsonString["show_activity_preference"], + userType: jsonString["user_type"], ); } - } diff --git a/project/FrontEnd/collaborative_science_platform/lib/providers/admin_provider.dart b/project/FrontEnd/collaborative_science_platform/lib/providers/admin_provider.dart new file mode 100644 index 00000000..002c7281 --- /dev/null +++ b/project/FrontEnd/collaborative_science_platform/lib/providers/admin_provider.dart @@ -0,0 +1,132 @@ +import 'dart:convert'; +import 'package:collaborative_science_platform/models/node_details_page/node_detailed.dart'; +import 'package:collaborative_science_platform/models/node_details_page/question.dart'; +import 'package:collaborative_science_platform/models/user.dart'; +import 'package:collaborative_science_platform/utils/constants.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; + +class AdminProvider with ChangeNotifier { + Future banUser(User? user, User? admin, bool isBanned) async { + final Map header = { + "Accept": "application/json", + "content-type": "application/json", + 'Authorization': admin!.token, + }; + + try { + final response = await http.put( + Uri.parse("${Constants.apiUrl}/update_content_status/"), + headers: header, + body: jsonEncode( + { + 'context': "user", + 'content_id': user!.id.toString(), + 'hide': isBanned.toString() + }, + ), + ); + print(response.statusCode); + return response.statusCode; + } catch (e) { + rethrow; + } + } + + Future hideNode(User? admin, NodeDetailed node, bool isHidden) async { + final Map header = { + "Accept": "application/json", + "content-type": "application/json", + 'Authorization': admin!.token, + }; + + try { + final response = await http.put( + Uri.parse("${Constants.apiUrl}/update_content_status/"), + headers: header, + body: jsonEncode( + { + 'context': "node", + 'content_id': node.nodeId.toString(), + 'hide': isHidden.toString() + }, + ), + ); + print(response.statusCode); + } catch (e) { + rethrow; + } + } + + Future hideQuestion(User? admin, Question question, bool isHidden) async { + final Map header = { + "Accept": "application/json", + "content-type": "application/json", + 'Authorization': admin!.token, + }; + + try { + final response = await http.put( + Uri.parse("${Constants.apiUrl}/update_content_status/"), + headers: header, + body: jsonEncode( + { + 'context': "question", + 'content_id': "-1", // TODO question!.id.toString(). + 'hide': isHidden.toString() + }, + ), + ); + print(response.statusCode); + return response.statusCode; + } catch (e) { + rethrow; + } + } + + Future promoteUser(User? user, User? admin) async { + final Map header = { + "Accept": "application/json", + "content-type": "application/json", + 'Authorization': admin!.token, + }; + + try { + final response = await http.post( + Uri.parse("${Constants.apiUrl}/promote_contributor/"), + headers: header, + body: jsonEncode( + { + 'cont_id': user!.id.toString(), //The basic user id of the contributor + }, + ), + ); + print(response.statusCode); + } catch (e) { + rethrow; + } + } + + Future demoteUser(User? user, User? admin) async { + final Map header = { + "Accept": "application/json", + "content-type": "application/json", + 'Authorization': admin!.token, + }; + + try { + final response = await http.delete( + Uri.parse("${Constants.apiUrl}/demote_reviewer/?${user!.id.toString()}"), + headers: header, + body: jsonEncode( + { + 'reviewer_id': user.id.toString(), //The basic user id of the reviewer + }, + ), + ); + print(response.statusCode); + } catch (e) { + rethrow; + } + } +} diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/error_page/error_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/error_page/error_page.dart new file mode 100644 index 00000000..bc78d9b3 --- /dev/null +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/error_page/error_page.dart @@ -0,0 +1,57 @@ +import 'package:collaborative_science_platform/screens/home_page/home_page.dart'; +import 'package:collaborative_science_platform/utils/colors.dart'; +import 'package:collaborative_science_platform/utils/text_styles.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class ErrorPage extends StatelessWidget { + static const routeName = '/page_not_found/'; + const ErrorPage({ + super.key, + }); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 20), + Container( + margin: const EdgeInsets.fromLTRB(15.0, 0.0, 15.0, 0.0), + child: Text( + "404", + style: TextStyles.title4.copyWith(fontSize: 120), + ), + ), + Container( + margin: const EdgeInsets.fromLTRB(15.0, 5.0, 15.0, 0.0), + child: const Text( + "This page doesn't exist.", + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: AppColors.secondaryDarkColor, + ), + ), + ), + const SizedBox(height: 8.0), // Add space between texts + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + context.go(HomePage.routeName); + }, + child: const Text( + "Return to main page", + style: TextStyle( + fontSize: 16.0, + fontWeight: FontWeight.bold, + color: Color.fromARGB(255, 67, 85, 186), + ), + ), + ), + ), + ], + ); + } +} diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/node_details_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/node_details_page.dart index a8a6e10d..a24f98fb 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/node_details_page.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/node_details_page.dart @@ -1,6 +1,11 @@ import 'package:collaborative_science_platform/exceptions/node_details_exceptions.dart'; +import 'package:collaborative_science_platform/models/basic_user.dart'; import 'package:collaborative_science_platform/models/node_details_page/node_detailed.dart'; +import 'package:collaborative_science_platform/models/user.dart'; +import 'package:collaborative_science_platform/providers/auth.dart'; import 'package:collaborative_science_platform/providers/node_provider.dart'; +import 'package:collaborative_science_platform/providers/admin_provider.dart'; +import 'package:collaborative_science_platform/screens/error_page/error_page.dart'; import 'package:collaborative_science_platform/screens/home_page/widgets/home_page_appbar.dart'; import 'package:collaborative_science_platform/screens/node_details_page/widgets/contributors_list_view.dart'; import 'package:collaborative_science_platform/screens/node_details_page/widgets/node_details.dart'; @@ -26,15 +31,21 @@ class _NodeDetailsPageState extends State { ScrollController controller2 = ScrollController(); bool _isFirstTime = true; NodeDetailed node = NodeDetailed(); + BasicUser basicUser = BasicUser(); + + bool isHidden = false; + bool isAuthLoading = false; bool error = false; String errorMessage = ""; + String message = ""; bool isLoading = false; @override void didChangeDependencies() { if (_isFirstTime) { + getAuthUser(); getNodeDetails(); _isFirstTime = false; } @@ -70,32 +81,83 @@ class _NodeDetailsPageState extends State { } } + void getAuthUser() async { + final User? user = Provider.of(context).user; + if (user != null) { + try { + final auth = Provider.of(context); + basicUser = (auth.basicUser ?? {} as BasicUser); + isAuthLoading = true; + // user = (auth.user ?? {} as User); + } catch (e) { + setState(() { + error = true; + errorMessage = "Something went wrong!"; + }); + rethrow; + } finally { + setState(() { + isAuthLoading = false; + }); + } + } + } + + void changeNodeStatus() async { + try { + final User? user = Provider.of(context, listen: false).user; + final adminProvider = Provider.of(context, listen: false); + await adminProvider.hideNode(user, node, isHidden); + error = false; + message = "Node status updated."; + print(message); + } catch (e) { + setState(() { + error = true; + message = "Something went wrong!"; + print(message); + }); + } + } + + void handleButton() { + setState(() { + isHidden = !isHidden; // Toggle the state for example purposes + }); + // changeNodeStatus(); + } + @override Widget build(BuildContext context) { - return PageWithAppBar( - appBar: const HomePageAppBar(), - pageColor: Colors.grey.shade200, - child: isLoading - ? Container( - padding: const EdgeInsets.only(top: 32), - decoration: const BoxDecoration(color: Colors.white), - child: const Center( - child: CircularProgressIndicator(), - ), - ) - : error - ? SelectableText( - errorMessage, - style: const TextStyle(color: Colors.red), - textAlign: TextAlign.center, - ) - : Responsive.isDesktop(context) - ? WebNodeDetails(node: node) - : NodeDetails( - node: node, - controller: controller2, + return (!isHidden || basicUser.userType == "admin") + ? PageWithAppBar( + appBar: const HomePageAppBar(), + pageColor: Colors.grey.shade200, + child: isLoading + ? Container( + padding: const EdgeInsets.only(top: 32), + decoration: const BoxDecoration(color: Colors.white), + child: const Center( + child: CircularProgressIndicator(), ), - ); + ) + : error + ? SelectableText( + errorMessage, + style: const TextStyle(color: Colors.red), + textAlign: TextAlign.center, + ) + : Responsive.isDesktop(context) + ? WebNodeDetails(node: node) + : NodeDetails( + node: node, + controller: controller2, + isHidden: isHidden, + userType: basicUser.userType, + onTap: handleButton, + ), + ) + : const ErrorPage(); } } @@ -111,13 +173,20 @@ class WebNodeDetails extends StatefulWidget { class _WebNodeDetailsState extends State { final ScrollController controller1 = ScrollController(); final ScrollController controller2 = ScrollController(); + BasicUser basicUser = BasicUser(); + bool _isFirstTime = true; bool error = false; bool isLoading = false; + bool isAuthLoading = false; + bool isHidden = false; + + String errorMessage = ""; @override void didChangeDependencies() { if (_isFirstTime) { + getAuthUser(); getNodeSuggestions(); _isFirstTime = false; } @@ -147,6 +216,28 @@ class _WebNodeDetailsState extends State { } } + void getAuthUser() async { + final User? user = Provider.of(context).user; + if (user != null) { + try { + final auth = Provider.of(context); + basicUser = (auth.basicUser ?? {} as BasicUser); + isAuthLoading = true; + // user = (auth.user ?? {} as User); + } catch (e) { + setState(() { + error = true; + errorMessage = "Something went wrong!"; + }); + rethrow; + } finally { + setState(() { + isAuthLoading = false; + }); + } + } + } + @override void dispose() { controller1.dispose(); @@ -161,35 +252,46 @@ class _WebNodeDetailsState extends State { super.initState(); } + void handleButton() { + setState(() { + isHidden = !isHidden; // Toggle the state for example purposes + }); + } + @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 32), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Contributors( - contributors: widget.node.contributors, //widget.inputNode.contributors, - controller: controller1, - ), - const SizedBox(width: 12), - Flexible( - child: NodeDetails( - node: widget.node, - controller: controller2, - ), - ), - const SizedBox(width: 12), - SizedBox( - height: MediaQuery.of(context).size.height - 100, - child: YouMayLike( - isLoading: isLoading, - error: error, + return (!isHidden || basicUser.userType == "admin") + ? Padding( + padding: const EdgeInsets.symmetric(horizontal: 32), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Contributors( + contributors: widget.node.contributors, //widget.inputNode.contributors, + controller: controller1, + ), + const SizedBox(width: 12), + Flexible( + child: NodeDetails( + node: widget.node, + controller: controller2, + isHidden: isHidden, + userType: basicUser.userType, + onTap: handleButton, + ), + ), + const SizedBox(width: 12), + SizedBox( + height: MediaQuery.of(context).size.height - 100, + child: YouMayLike( + isLoading: isLoading, + error: error, + ), + ), + ], ), - ), - ], - ), - ); + ) + : const ErrorPage(); } } diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/confirmation_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/confirmation_page.dart new file mode 100644 index 00000000..02f5f497 --- /dev/null +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/confirmation_page.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; + +class ConfirmationPage extends StatelessWidget { + const ConfirmationPage({super.key}); + + @override + Widget build(BuildContext context) { + return const AlertDialog( + title: SizedBox( + width: 500, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('Are you sure?', style: TextStyle(fontSize: 20.0)), + ], + ), + ), + backgroundColor: Colors.white, + shadowColor: Colors.white, + content: null, + ); + } +} diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart index f761e7ef..a68e4fa1 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/node_details_page/widgets/node_details.dart @@ -14,6 +14,7 @@ import 'package:collaborative_science_platform/widgets/annotation_text.dart'; import 'package:collaborative_science_platform/widgets/app_button.dart'; import 'package:collaborative_science_platform/widgets/card_container.dart'; import 'package:collaborative_science_platform/utils/responsive/responsive.dart'; +import 'package:collaborative_science_platform/screens/error_page/error_page.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_tex/flutter_tex.dart'; @@ -24,10 +25,16 @@ import 'package:provider/provider.dart'; class NodeDetails extends StatefulWidget { final NodeDetailed node; final ScrollController controller; + final bool isHidden; + final String userType; + final Function() onTap; const NodeDetails({ super.key, required this.node, required this.controller, + required this.isHidden, + required this.userType, + required this.onTap, }); @override @@ -86,22 +93,23 @@ class _NodeDetailsState extends State { primary: false, scrollDirection: Axis.vertical, child: Column( - mainAxisAlignment: MainAxisAlignment.start, + // mainAxisAlignment: MainAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), child: CardContainer( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: Responsive.isDesktop(context) - ? const EdgeInsets.all(70.0) - : const EdgeInsets.all(10.0), - child: SelectableText(utf8.decode(widget.node.nodeTitle.codeUnits), - textAlign: TextAlign.center, style: TextStyles.title2)), - Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Column( + child: Column( + //mainAxisAlignment: MainAxisAlignment.start, + children: [ + Padding( + padding: Responsive.isDesktop(context) + ? const EdgeInsets.all(70.0) + : const EdgeInsets.all(10.0), + child: AnnotationText(utf8.decode(widget.node.nodeTitle.codeUnits), + textAlign: TextAlign.center, style: TextStyles.title2)), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, children: [ SelectableText.rich( TextSpan( @@ -109,12 +117,42 @@ class _NodeDetailsState extends State { style: TextStyles.bodyBlack, ), ), - ], - ), - Column( - children: [ Row( + mainAxisAlignment: MainAxisAlignment.end, children: [ + Visibility( + visible: widget.userType == "admin" ? true : false, + child: widget.isHidden + ? SizedBox( + width: 110, + child: AppButton( + text: "Show", + height: 40, + icon: const Icon( + CupertinoIcons.eye, + size: 16, + color: Colors.white, + ), + type: "grey", + onTap: widget.onTap, + ), + ) + : SizedBox( + width: 110, + child: AppButton( + text: "Hide", + height: 40, + icon: const Icon( + CupertinoIcons.eye_slash, + size: 16, + color: Colors.white, + ), + type: "danger", + onTap: widget.onTap, + ), + ), + ), + const SizedBox(width: 10), SizedBox( width: 110, child: AppButton( @@ -149,9 +187,9 @@ class _NodeDetailsState extends State { ) ], ), - ]), - ], - )), + ], + ), + ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart index 082e3972..f3170ca7 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/profile_page.dart @@ -1,9 +1,10 @@ import 'package:collaborative_science_platform/exceptions/profile_page_exceptions.dart'; -import 'package:collaborative_science_platform/models/node_details_page/question.dart'; +import 'package:collaborative_science_platform/models/basic_user.dart'; import 'package:collaborative_science_platform/models/profile_data.dart'; import 'package:collaborative_science_platform/models/user.dart'; import 'package:collaborative_science_platform/providers/auth.dart'; import 'package:collaborative_science_platform/providers/profile_data_provider.dart'; +import 'package:collaborative_science_platform/screens/error_page/error_page.dart'; import 'package:collaborative_science_platform/screens/home_page/widgets/home_page_appbar.dart'; import 'package:collaborative_science_platform/screens/node_details_page/node_details_page.dart'; import 'package:collaborative_science_platform/screens/page_with_appbar/page_with_appbar.dart'; @@ -33,13 +34,19 @@ class ProfilePage extends StatefulWidget { // TODO: add optional parameter to ProfilePage to get others profileData class _ProfilePageState extends State { ProfileData profileData = ProfileData(); + BasicUser basicUser = BasicUser(); int noWorks = 0; bool error = false; String errorMessage = ""; bool isLoading = false; + bool isAuthLoading = false; bool _isFirstTime = true; + bool isBanned = false; + bool isValidUser = false; //visited user's type is contributor or reviewer + bool isReviewer = false; + int currentIndex = 0; void updateIndex(int index) { @@ -52,6 +59,7 @@ class _ProfilePageState extends State { void didChangeDependencies() { if (_isFirstTime) { try { + getAuthUser(); getUserData(); } catch (e) { setState(() { @@ -102,6 +110,40 @@ class _ProfilePageState extends State { } } + void getAuthUser() async { + final User? user = Provider.of(context).user; + if (user != null) { + try { + final auth = Provider.of(context); + basicUser = (auth.basicUser ?? {} as BasicUser); + isAuthLoading = true; + // user = (auth.user ?? {} as User); + } catch (e) { + setState(() { + error = true; + errorMessage = "Something went wrong!"; + }); + rethrow; + } finally { + setState(() { + isAuthLoading = false; + }); + } + } + } + + void handleButtonIsBanned() { + setState(() { + isBanned = !isBanned; // Toggle the state for example purposes + }); + } + + void handleButtonIsReviewer() { + setState(() { + isReviewer = !isReviewer; // Toggle the state for example purposes + }); + } + @override Widget build(BuildContext context) { final User? user = Provider.of(context).user; @@ -112,231 +154,258 @@ class _ProfilePageState extends State { // guest can see profile pages } else if (user.email == profileData.email) { // own profile page, should be editible - return PageWithAppBar( - appBar: const HomePageAppBar(), - pageColor: Colors.grey.shade200, - child: Responsive( - mobile: SingleChildScrollView( - child: SizedBox( - width: Responsive.getGenericPageWidth(context), - child: isLoading - ? Container( - decoration: const BoxDecoration(color: Colors.white), - child: const Center( - child: CircularProgressIndicator(), - ), - ) - : error - ? SelectableText( - errorMessage, - style: const TextStyle(color: Colors.red), - textAlign: TextAlign.center, - ) - : Column( - children: [ - AboutMe( - aboutMe: profileData.aboutMe, - email: profileData.email, - name: profileData.name, - surname: profileData.surname, - noWorks: noWorks, + return (!isBanned || basicUser.userType == "admin") + ? PageWithAppBar( + appBar: const HomePageAppBar(), + pageColor: Colors.grey.shade200, + child: Responsive( + mobile: SingleChildScrollView( + child: SizedBox( + width: Responsive.getGenericPageWidth(context), + child: isLoading && isAuthLoading + ? Container( + decoration: const BoxDecoration(color: Colors.white), + child: const Center( + child: CircularProgressIndicator(), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 10), - child: Row( + ) + : error + ? SelectableText( + errorMessage, + style: const TextStyle(color: Colors.red), + textAlign: TextAlign.center, + ) + : Column( children: [ - const Expanded(child: MobileEditProfileButton()), - Expanded(child: LogOutButton()) + AboutMe( + aboutMe: profileData.aboutMe, + email: profileData.email, + name: profileData.name, + surname: profileData.surname, + noWorks: noWorks, + isBanned: isBanned, + isReviewer: isReviewer, + isValidUser: isValidUser, + userType: basicUser.userType, + onTap: handleButtonIsBanned, + onTapReviewerButton: handleButtonIsReviewer, + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 30, vertical: 10), + child: Row( + children: [ + const Expanded(child: MobileEditProfileButton()), + Expanded(child: LogOutButton()) + ], + ), + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: ProfileActivityTabBar( + callback: updateIndex, + ), + ), + if (currentIndex == 0) + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: CardContainer( + child: ListView.builder( + padding: const EdgeInsets.all(0), + scrollDirection: Axis.vertical, + shrinkWrap: true, + itemCount: profileData.nodes.length, + itemBuilder: (context, index) { + return ProfileNodeCard( + profileNode: profileData.nodes.elementAt(index), + onTap: () { + context.push( + '${NodeDetailsPage.routeName}/${profileData.nodes.elementAt(index).id}'); + }, + ); + }, + ), + ), + ), + if (currentIndex == 1) + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: CardContainer( + child: SizedBox( + height: 400, + child: QuestionActivity(questions: questionList), + ), + ), + ), ], ), + ), + ), + desktop: SingleChildScrollView( + child: SizedBox( + width: Responsive.getGenericPageWidth(context), + child: isLoading + ? Container( + decoration: const BoxDecoration(color: Colors.white), + padding: const EdgeInsets.only(top: 20), + child: const Center( + child: CircularProgressIndicator(), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: ProfileActivityTabBar( - callback: updateIndex, - ), - ), - if (currentIndex == 0) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: CardContainer( - child: ListView.builder( - padding: const EdgeInsets.all(0), - scrollDirection: Axis.vertical, - shrinkWrap: true, - itemCount: profileData.nodes.length, - itemBuilder: (context, index) { - return ProfileNodeCard( - profileNode: profileData.nodes.elementAt(index), - onTap: () { - context.push( - '${NodeDetailsPage.routeName}/${profileData.nodes.elementAt(index).id}'); - }, - ); - }, + ) + : error + ? SelectableText( + errorMessage, + style: const TextStyle(color: Colors.red), + textAlign: TextAlign.center, + ) + : Column( + children: [ + AboutMe( + aboutMe: profileData.aboutMe, + email: profileData.email, + name: profileData.name, + surname: profileData.surname, + noWorks: noWorks, + isBanned: isBanned, + isReviewer: isReviewer, + isValidUser: isValidUser, + userType: basicUser.userType, + onTap: handleButtonIsBanned, + onTapReviewerButton: handleButtonIsReviewer, ), - ), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: DesktopEditProfileButton(), + ), + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: ProfileActivityTabBar( + callback: updateIndex, + ), + ), + if (currentIndex == 0) + CardContainer( + child: ListView.builder( + padding: const EdgeInsets.all(0), + scrollDirection: Axis.vertical, + shrinkWrap: true, + itemCount: profileData.nodes.length, + itemBuilder: (context, index) { + return ProfileNodeCard( + profileNode: profileData.nodes.elementAt(index), + onTap: () { + context.push( + '${NodeDetailsPage.routeName}/${profileData.nodes.elementAt(index).id}'); + }, + ); + }, + ), + ), + if (currentIndex == 1) + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: CardContainer( + child: SizedBox( + height: 400, + child: QuestionActivity(questions: questionList), + ), + ), + ), + ], ), - if (currentIndex == 1) + ), + ), + ), + ) + : const ErrorPage(); + } + + // others profile page, will be same both on desktop and mobile + return (!isBanned || basicUser.userType == "admin") + ? PageWithAppBar( + appBar: const HomePageAppBar(), + pageColor: Colors.grey.shade200, + child: SingleChildScrollView( + child: SizedBox( + width: Responsive.getGenericPageWidth(context), + child: isLoading && isAuthLoading + ? Container( + decoration: const BoxDecoration(color: Colors.white), + padding: const EdgeInsets.only(top: 20), + child: const Center( + child: CircularProgressIndicator(), + ), + ) + : error + ? SelectableText( + errorMessage, + style: const TextStyle(color: Colors.red), + textAlign: TextAlign.center, + ) + : Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + AboutMe( + aboutMe: profileData.aboutMe, + email: profileData.email, + name: profileData.name, + surname: profileData.surname, + noWorks: noWorks, + isBanned: isBanned, + isReviewer: isReviewer, + isValidUser: isValidUser, + userType: basicUser.userType, + onTap: handleButtonIsBanned, + onTapReviewerButton: handleButtonIsReviewer), Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: CardContainer( - child: SizedBox( - height: 400, - child: QuestionActivity(questions: questionList), - ), + child: ProfileActivityTabBar( + callback: updateIndex, ), ), - ], - ), - ), - ), - desktop: SingleChildScrollView( - child: SizedBox( - width: Responsive.getGenericPageWidth(context), - child: isLoading - ? Container( - decoration: const BoxDecoration(color: Colors.white), - padding: const EdgeInsets.only(top: 20), - child: const Center( - child: CircularProgressIndicator(), - ), - ) - : error - ? SelectableText( - errorMessage, - style: const TextStyle(color: Colors.red), - textAlign: TextAlign.center, - ) - : Column( - children: [ - AboutMe( - aboutMe: profileData.aboutMe, - email: profileData.email, - name: profileData.name, - surname: profileData.surname, - noWorks: noWorks, - ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: DesktopEditProfileButton(), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: ProfileActivityTabBar( - callback: updateIndex, - ), - ), - if (currentIndex == 0) - CardContainer( - child: ListView.builder( - padding: const EdgeInsets.all(0), - scrollDirection: Axis.vertical, - shrinkWrap: true, - itemCount: profileData.nodes.length, - itemBuilder: (context, index) { - return ProfileNodeCard( - profileNode: profileData.nodes.elementAt(index), - onTap: () { - context.push( - '${NodeDetailsPage.routeName}/${profileData.nodes.elementAt(index).id}'); + if (currentIndex == 0) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: CardContainer( + child: ListView.builder( + padding: const EdgeInsets.all(0), + physics: + const NeverScrollableScrollPhysics(), // Prevents a conflict with SingleChildScrollView + scrollDirection: Axis.vertical, + shrinkWrap: true, + itemCount: profileData.nodes.length, + itemBuilder: (context, index) { + return ProfileNodeCard( + profileNode: profileData.nodes.elementAt(index), + onTap: () { + context.push( + '${NodeDetailsPage.routeName}/${profileData.nodes.elementAt(index).id}'); + }, + ); }, - ); - }, + ), + ), ), - ), - if (currentIndex == 1) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: CardContainer( - child: SizedBox( - height: 400, - child: QuestionActivity(questions: questionList), + if (currentIndex == 1) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), + child: CardContainer( + child: SizedBox( + height: 400, + child: QuestionActivity(questions: questionList), + ), ), ), - ), - ], - ), - ), - ), - ), - ); - } - - // others profile page, will be same both on desktop and mobile - return PageWithAppBar( - appBar: const HomePageAppBar(), - pageColor: Colors.grey.shade200, - child: SingleChildScrollView( - child: SizedBox( - width: Responsive.getGenericPageWidth(context), - child: isLoading - ? Container( - decoration: const BoxDecoration(color: Colors.white), - padding: const EdgeInsets.only(top: 20), - child: const Center( - child: CircularProgressIndicator(), - ), - ) - : error - ? SelectableText( - errorMessage, - style: const TextStyle(color: Colors.red), - textAlign: TextAlign.center, - ) - : Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - AboutMe( - aboutMe: profileData.aboutMe, - email: profileData.email, - name: profileData.name, - surname: profileData.surname, - noWorks: noWorks, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: ProfileActivityTabBar( - callback: updateIndex, + ], ), - ), - if (currentIndex == 0) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: CardContainer( - child: ListView.builder( - padding: const EdgeInsets.all(0), - physics: - const NeverScrollableScrollPhysics(), // Prevents a conflict with SingleChildScrollView - scrollDirection: Axis.vertical, - shrinkWrap: true, - itemCount: profileData.nodes.length, - itemBuilder: (context, index) { - return ProfileNodeCard( - profileNode: profileData.nodes.elementAt(index), - onTap: () { - context.push( - '${NodeDetailsPage.routeName}/${profileData.nodes.elementAt(index).id}'); - }, - ); - }, - ), - ), - ), - if (currentIndex == 1) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: CardContainer( - child: SizedBox( - height: 400, - child: QuestionActivity(questions: questionList), - ), - ), - ), - ], - ), - ), - ), - ); + ), + ), + ) + : const ErrorPage(); } } diff --git a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/about_me.dart b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/about_me.dart index 77c7e73b..c7174cc9 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/about_me.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/screens/profile_page/widgets/about_me.dart @@ -1,21 +1,40 @@ import 'package:collaborative_science_platform/utils/colors.dart'; import 'package:collaborative_science_platform/utils/responsive/responsive.dart'; +import 'package:collaborative_science_platform/widgets/app_button.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -class AboutMe extends StatelessWidget { +class AboutMe extends StatefulWidget { final String email; final String name; final String surname; final int noWorks; final String aboutMe; + final bool isBanned; + final bool isReviewer; + final String userType; + final bool isValidUser; + final Function() onTap; + final Function() onTapReviewerButton; const AboutMe( {super.key, required this.email, required this.name, required this.surname, required this.noWorks, - required this.aboutMe}); + required this.aboutMe, + required this.isBanned, + required this.isReviewer, + required this.userType, + required this.isValidUser, + required this.onTap, + required this.onTapReviewerButton}); + @override + State createState() => _AboutMeState(); +} + +class _AboutMeState extends State { @override Widget build(BuildContext context) { return SizedBox( @@ -27,15 +46,87 @@ class AboutMe extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SelectableText( - "$name $surname", + "${widget.name} ${widget.surname}", style: const TextStyle( color: AppColors.primaryDarkColor, fontWeight: FontWeight.bold, fontSize: 40, ), ), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Visibility( + visible: ((widget.userType == "admin" ? true : false) && widget.isValidUser), + child: widget.isReviewer + ? SizedBox( + width: 220, + child: AppButton( + text: "Give Reviewer Status", + height: 40, + icon: const Icon( + CupertinoIcons.add_circled_solid, + size: 16, + color: Colors.white, + ), + type: "safe", + onTap: widget.onTapReviewerButton, + ), + ) + : SizedBox( + width: 220, + child: AppButton( + text: "Remove Reviewer Status", + height: 40, + icon: const Icon( + CupertinoIcons.minus_circle_fill, + size: 16, + color: Colors.white, + ), + type: "danger", + onTap: widget.onTapReviewerButton, + ), + ), + ), + const SizedBox(height: 10.0), + Visibility( + visible: (widget.userType == "admin" ? true : false), + child: widget.isBanned + ? SizedBox( + width: 220, + child: AppButton( + text: "Unban User", + height: 40, + icon: const Icon( + CupertinoIcons.lock_open, + size: 16, + color: Colors.white, + ), + type: "grey", + onTap: widget.onTap, + ), + ) + : SizedBox( + width: 220, + child: AppButton( + text: "Ban User", + height: 40, + icon: const Icon( + CupertinoIcons.lock, + size: 16, + color: Colors.white, + ), + type: "danger", + onTap: widget.onTap, + ), + ), + ), + ], + ), ], ), const SizedBox( @@ -48,7 +139,7 @@ class AboutMe extends StatelessWidget { ? MediaQuery.of(context).size.width * 0.9 : MediaQuery.of(context).size.width * 0.5, child: SelectableText( - aboutMe, + widget.aboutMe, style: const TextStyle( fontWeight: FontWeight.normal, fontSize: 20, @@ -71,7 +162,7 @@ class AboutMe extends StatelessWidget { width: 10, ), SelectableText( - email, + "${widget.email}", style: const TextStyle( fontSize: 20, ), @@ -84,7 +175,7 @@ class AboutMe extends StatelessWidget { Row( children: [ SelectableText( - "Published works: $noWorks", + "Published works: ${widget.noWorks}", style: const TextStyle( fontWeight: FontWeight.normal, fontSize: 20, diff --git a/project/FrontEnd/collaborative_science_platform/lib/widgets/app_button.dart b/project/FrontEnd/collaborative_science_platform/lib/widgets/app_button.dart index 53ecd144..2e5151dd 100644 --- a/project/FrontEnd/collaborative_science_platform/lib/widgets/app_button.dart +++ b/project/FrontEnd/collaborative_science_platform/lib/widgets/app_button.dart @@ -29,8 +29,16 @@ class AppButton extends StatelessWidget { ? ElevatedButton.styleFrom( backgroundColor: isActive ? (type == "primary" - ? AppColors.primaryColor - : (type == "secondary" ? AppColors.secondaryColor : Colors.grey[600])) + ? const Color.fromRGBO(8, 155, 171, 1) + : (type == "secondary" + ? AppColors.secondaryColor + : (type == "danger" + ? AppColors.dangerColor + : (type == "grey" + ? Colors.grey[600] + : (type == "safe" + ? Color.fromARGB(255, 141, 208, 141) + : Colors.grey[600]))))) : Colors.grey[600], shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8),