diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml new file mode 100644 index 00000000..3383c717 --- /dev/null +++ b/.github/workflows/dart.yml @@ -0,0 +1,42 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Dart + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + # Note: This workflow uses the latest stable version of the Dart SDK. + # You can specify other versions if desired, see documentation here: + # https://github.com/dart-lang/setup-dart/blob/main/README.md + # - uses: dart-lang/setup-dart@v1 + - uses: dart-lang/setup-dart@9a04e6d73cca37bd455e0608d7e5092f881fd603 + + - name: Install dependencies + run: dart pub get + + # Uncomment this step to verify the use of 'dart format' on each commit. + # - name: Verify formatting + # run: dart format --output=none --set-exit-if-changed . + + # Consider passing '--fatal-infos' for slightly stricter analysis. + - name: Analyze project source + run: dart analyze + + # Your project will need to have tests in test/ and a dependency on + # package:test for this step to succeed. Note that Flutter projects will + # want to change this to 'flutter test'. + - name: Run tests + run: dart test diff --git a/android/build.gradle b/android/build.gradle index b3a325d5..1cc04d89 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,12 +1,12 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.8.21' repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.2.2' + classpath 'com.android.tools.build:gradle:7.3.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index cc5527d7..6b665338 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/clone.sh b/clone.sh deleted file mode 100644 index 3a3780c4..00000000 --- a/clone.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -#branches=("ac-1" "ac-2" "ac-3" "ac-4" "ac-5" "ps-1" "ps-2" "gad-1" "gad-2" "gad-3" "gad-4" "gad-5" "hr" "sa-1" "sa-2" "sa-3" "sa-4" "os-1" "os-2" "os-3" "os-4" "rspc") -branches=("ac") -for branch in "${branches[@]}" -do - echo "Creating $branch" - - git checkout -b "$branch" - git push origin "$branch" - - echo "Pushed new branch $branch to remote" -done \ No newline at end of file diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 00000000..7e7e7f67 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1 @@ +extensions: diff --git a/flutter b/flutter new file mode 160000 index 00000000..7f20e5d1 --- /dev/null +++ b/flutter @@ -0,0 +1 @@ +Subproject commit 7f20e5d18ce4cb80c621533090a7c5113f5bdc52 diff --git a/lib/Components/appBar2.dart b/lib/Components/appBar2.dart new file mode 100644 index 00000000..a1f63393 --- /dev/null +++ b/lib/Components/appBar2.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:fusion/constants.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; + +class CustomAppBar extends StatefulWidget implements PreferredSizeWidget { + final String curr_desig; + final String headerTitle; + + final ValueChanged onDesignationChanged; + + const CustomAppBar({ + Key? key, + required this.curr_desig, + required this.headerTitle, + + required this.onDesignationChanged, + }) : super(key: key); + + @override + _CustomAppBarState createState() => _CustomAppBarState(); + + @override + Size get preferredSize => Size.fromHeight(kToolbarHeight); +} + +class _CustomAppBarState extends State { + late List designations; + late String current; + var service = locator(); + + @override + void initState() { + super.initState(); + designations = (service!.getFromDisk('designations') as List) + .map((dynamic item) => item.toString()) + .toList(); + + current = service!.getFromDisk( + 'Current_designation'); // Ensure designations is not null before accessing index 0 + } + + @override + Widget build(BuildContext context) { + return AppBar( + iconTheme: IconThemeData(color: Colors.white), + backgroundColor: kPrimaryColor, + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Padding( + padding: EdgeInsets.only(right: 20.0), // Add some right padding to ensure space for the dropdown + child: Text( + widget.headerTitle, // Example of a long title + overflow: TextOverflow.ellipsis, // Prevents overflow by adding ellipsis + style: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + DropdownButtonHideUnderline( + child: DropdownButton( + padding: const EdgeInsets.all(15), + borderRadius: BorderRadius.circular(5), + value: current, + icon: Icon(Icons.arrow_drop_down, color: Colors.white), + iconSize: 24, + style: TextStyle(color: Colors.white, fontSize: 18), + dropdownColor: + kPrimaryColor, // Set the dropdown background color to orange + onChanged: (String? newValue) { + widget.onDesignationChanged(newValue!); + setState(() { + current = newValue!; + service!.saveToDisk('Current_designation', current); + }); + }, + items: designations.map>((String value) { + return DropdownMenuItem( + value: value, + child: Text( + value, + style: TextStyle( + color: Colors.white), // Set the text color to white + ), + ); + }).toList(), + onTap: () { + // Find the index of the selected value + int index = designations.indexOf(current); + // Scroll the dropdown to the selected value + Scrollable.ensureVisible(context, + alignment: 0.5, duration: Duration(milliseconds: 300)); + }, + ), + ), + ], + ), + actions: [], + ); + } +} \ No newline at end of file diff --git a/lib/Components/bottom_navigation_bar.dart b/lib/Components/bottom_navigation_bar.dart new file mode 100644 index 00000000..f95ad3c3 --- /dev/null +++ b/lib/Components/bottom_navigation_bar.dart @@ -0,0 +1,148 @@ +import 'package:flutter/material.dart'; + +class MyBottomNavigationBar extends StatefulWidget { + @override + _MyBottomNavigationBarState createState() => _MyBottomNavigationBarState(); +} + +class _MyBottomNavigationBarState extends State { + bool _notificationsBool = false; + bool _announcementsBool = false; + bool _newsBool = false; + bool _homeBool = false; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 100.0, + child: Padding( + padding: EdgeInsets.only(bottom: 40), + child: Card( + color: Colors.deepOrangeAccent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(17.0), + ), + child: Padding( + padding: const EdgeInsets.only( + left: 13.0, right: 10.0, top: 5.0, bottom: 5.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + GestureDetector( + onTap: () { + _notificationsBool = false; + _announcementsBool = false; + _newsBool = false; + _homeBool = true; + setState(() { + _notificationsBool = false; + _announcementsBool = false; + _newsBool = false; + _homeBool = true; + }); + Navigator.pushReplacementNamed(context, "/dashboard"); + + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Icon( + Icons.home_rounded, + color: Colors.white, + size: _homeBool ? 30.0 : 25.0, + ), + ], + ), + ), + GestureDetector( + onTap: () { + _newsBool = true; + _announcementsBool = false; + _notificationsBool = false; + _homeBool = false; + + setState(() { + _newsBool = true; + _announcementsBool = false; + _notificationsBool = false; + _homeBool = false; + }); + Navigator.pushReplacementNamed(context, "/news"); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Icon( + Icons.newspaper_rounded, + color: Colors.white, + size: _newsBool ? 30.0 : 25.0, + ), + ], + ), + ), + GestureDetector( + onTap: () { + _announcementsBool = false; + _newsBool = false; + _notificationsBool = true; + _homeBool = false; + + setState(() { + _announcementsBool = false; + _newsBool = false; + _notificationsBool = true; + _homeBool = false; + }); + Navigator.pushReplacementNamed(context, "/notification"); + }, + child: Padding( + padding: const EdgeInsets.only(right: 16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Icon( + Icons.notifications_active_rounded, + color: Colors.white, + size: _notificationsBool ? 30.0 : 25.0, + ), + ], + ), + ), + ), + GestureDetector( + onTap: () { + _announcementsBool = true; + _newsBool = false; + _notificationsBool = false; + _homeBool = false; + + setState(() { + _announcementsBool = true; + _newsBool = false; + _notificationsBool = false; + _homeBool = false; + }); + Navigator.pushReplacementNamed(context, "/announcement"); + }, + child: Padding( + padding: const EdgeInsets.only(right: 16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Icon( + Icons.campaign_rounded, + color: Colors.white, + size: _announcementsBool ? 30.0 : 25.0, + ), + ], + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/Components/side_drawer.dart b/lib/Components/side_drawer.dart index be08db0e..f6cfd0bc 100644 --- a/lib/Components/side_drawer.dart +++ b/lib/Components/side_drawer.dart @@ -142,7 +142,8 @@ class _SideDrawerState extends State { ModulesPadding(line: 'Leave Module'), ModulesPadding(line: 'Placement Module'), ModulesPadding(line: 'Visitors Hostel Module'), - ModulesPadding(line: 'File Tracking Module'), + ModulesPadding(line: 'File Tracking Module', pageMover: '/fts', + isActive: true,), ], ), ) diff --git a/lib/Components/side_drawer2.dart b/lib/Components/side_drawer2.dart new file mode 100644 index 00000000..97c80e8d --- /dev/null +++ b/lib/Components/side_drawer2.dart @@ -0,0 +1,264 @@ +import 'package:flutter/material.dart'; +import 'package:fusion/services/login_service.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; + +class SideDrawer extends StatefulWidget { + final String curr_desig; + + const SideDrawer({ + Key? key, + required this.curr_desig, + }) : super(key: key); + + @override + _SideDrawerState createState() => _SideDrawerState(); +} + +class _SideDrawerState extends State { + bool _loading = false; + int count = 0; + late String name; + late String depttype; + late String type; + @override + void initState() { + super.initState(); + var service = locator(); + print(service.profileData); + name = service.profileData.user!["first_name"] + + " " + + service.profileData.user!["last_name"]; + depttype = service.profileData.profile!['department']!['name']; + + type = service.profileData.profile!['user_type']; + print(depttype); + } + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Container( + margin: const EdgeInsets.only(right: 50.0), + height: MediaQuery.of(context).size.height, + color: Colors.white, + child: ListView( + shrinkWrap: true, + physics: ClampingScrollPhysics(), + children: [ + Column( + children: [ + Card( + elevation: 2.0, + margin: + EdgeInsets.symmetric(horizontal: 12.0, vertical: 30.0), + // shadowColor: Colors.black, + color: Colors.white, + + child: Column( + children: [ + Container( + margin: EdgeInsets.only(top: 0.0), + width: 270.0, + height: 120.0, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/profile_pic.png'), + fit: BoxFit.contain, + ), + ), + ), + SizedBox( + height: 10.0, + ), + Text( + name, //Display name of User + style: TextStyle( + fontSize: 20.0, + color: Colors.black, + fontWeight: FontWeight.bold), + ), + SizedBox( + height: 10.0, + ), + Text( + depttype + + " " + + widget.curr_desig, // Display Type of User + style: TextStyle( + fontSize: 17.0, + color: Colors.black, + fontWeight: FontWeight.bold), + ), + SizedBox( + height: 10.0, + ), + ], + ), + ), + ], + ), + ModulesCard(cardLine: 'DashBoard', pageMover: '/dashboard'), + + if ((type != "staff" || widget.curr_desig=="acadmin"|| widget.curr_desig== "corelabcaretaker")) + ModulesCard( + cardLine: 'Academics Module', + pageMover: '/academic_home_page', + ), + + if ((type == "student" || widget.curr_desig=="acadmin")) + ModulesCard( + cardLine: 'Programme Curriculum', + pageMover: '/programme_curriculum_home', + ), + + if ((type == "student") || widget.curr_desig== "Dean_s" || widget.curr_desig== "DeanPnD" || widget.curr_desig== "dean_rspc" || widget.curr_desig== "dean_s" ) + ModulesCard( + cardLine: 'Gymkhana Module', + pageMover: '/gymkhana_homepage', + ), + + if ((type == "student" || widget.curr_desig== "mess_manager" || widget.curr_desig== "mess_warden")) + ModulesCard(cardLine: 'Central Mess Module',pageMover: '/central_mess_home'), + + + ModulesCard( + cardLine: 'Health Center Module', + pageMover: '/health_center', + ), + if ((type == "student" )) + ModulesCard(cardLine: 'Leave Module'), + + if ((type == "student" )) + ModulesCard(cardLine: 'Purchase and Store'), + + if ((type == "student" )) + ModulesCard(cardLine: 'Human Resource'), + + if(type == "student"|| widget.curr_desig=="placement chairman" || widget.curr_desig=="placement officer") + ModulesCard(cardLine: 'Placement Module'), + + ModulesCard(cardLine: 'Visitors Hostel Module',pageMover: '/visitor_hostel'), + + if(type != "student") + ModulesCard(cardLine: 'File Tracking Module',pageMover: '/compose_file'), + + ModulesCard( + cardLine: 'Establishment Module', pageMover: '/establishment'), + + ModulesCard( + cardLine: 'Library Module', pageMover: '/library_homepage'), + + if(type == "student" || widget.curr_desig== "spacsconvenor"|| widget.curr_desig== "spacsassistant") + ModulesCard(cardLine: 'Awards & Scholarship Module'), + + ModulesCard(cardLine: 'Complaint Module', pageMover: '/complaint'), + + ModulesCard(cardLine: 'Research Module'), + + ModulesCard(cardLine: 'Counselling Cell'), + + if ((type == "faculty" ||widget.curr_desig== "acadadmin" )) + ModulesCard(cardLine: 'Examination Module',pageMover: '/examination',), + + + + if ((widget.curr_desig== "Executive Engineer (Civil)" ||widget.curr_desig== "EE" || widget.curr_desig== "Admin IWD" || widget.curr_desig== "Electrical_AE" || widget.curr_desig== "mess_manager" || widget.curr_desig== "Electrical_JE" || widget.curr_desig== "Civil_AE" || widget.curr_desig== "Civil_JE" || widget.curr_desig== "Director" || widget.curr_desig== "dean_s" || widget.curr_desig== "Dean_s" || widget.curr_desig== "DeanPnD" )) + ModulesCard(cardLine: 'IWD',pageMover: '/iwd/home_page'), + + + ModulesCard(cardLine: 'Courses Module', pageMover: '/registered_courses', + ), + ModulesCard(cardLine: 'HR Module', pageMover: '/hr_homepage', + ), + + + // ModulesCard( + // cardLine: 'Profile', + // icon: Icons.account_circle, + // pageMover: '/profile'), + + // ModulesCard(cardLine: 'Office Of Dean Students'), + // ModulesCard(cardLine: 'Office Of Dean Academics'), + // ModulesCard(cardLine: 'Director Office'), + // ModulesCard(cardLine: 'Office Of Purchase Officer'), + // ModulesCard(cardLine: 'Office Of Registrar'), + // ModulesCard(cardLine: 'Office Of P&D'), + // ModulesCard(cardLine: 'Office Of HOD (Branch)'), + // ModulesCard(cardLine: 'Finance & Accounts'), + // ModulesCard(cardLine: 'Meet Our Team'), + ModulesCard(cardLine: 'Log Out', icon: Icons.logout), + ], + ), + ), + ); + } + +String _getGymkhanaPage() { + + // Determine the pageMover based on designation + print(widget.curr_desig); + if (widget.curr_desig == 'co-ordinator') { + + return '/gymkhana_coordinator'; + } + else if(widget.curr_desig == 'Counsellor'){ + return '/gymkhana_counsellor'; + } + else if(widget.curr_desig == 'Convenor'){ + return '/gymkhana_convenor'; + } + else if(widget.curr_desig == 'Dean Academic'){ + return '/gymkhana_dean'; + } + + else + return '/gymkhana_homepage'; + } +// ignore: must_be_immutable +} + +class ModulesCard extends StatelessWidget { + final String? cardLine; + final String? pageMover; + IconData? icon; + ModulesCard({this.cardLine, this.icon, this.pageMover}); + @override + Widget build(BuildContext context) { + return GestureDetector( + //behaviour to translucent to get Tap even on blank or empty space within container + behavior: HitTestBehavior.translucent, + child: Card( + color: Colors.white, + margin: const EdgeInsets.all(10.0), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + cardLine!, + style: TextStyle(fontSize: 16.0, color: Colors.black), + ), + Icon( + icon, + color: Colors.deepOrangeAccent, + ), + ], + ), + ), + ), + onTap: () async { + var _prefs = await StorageService.getInstance(); + String token = _prefs!.userInDB?.token ?? ""; + if (cardLine == 'Log Out') { + LoginService auth = LoginService(); + auth.logout(); + Navigator.pushReplacementNamed(context, "/landing"); + } + if (pageMover != null) + Navigator.pushReplacementNamed(context, pageMover!, arguments: token); + }, + ); + } +} \ No newline at end of file diff --git a/lib/DesignationProvider.dart b/lib/DesignationProvider.dart new file mode 100644 index 00000000..34031248 --- /dev/null +++ b/lib/DesignationProvider.dart @@ -0,0 +1,12 @@ +import 'package:flutter/material.dart'; + +class DesignationProvider extends ChangeNotifier { + late String _designation; + + String get designation => _designation; + + void updateDesignation(String newDesignation) { + _designation = newDesignation; + notifyListeners(); // Notify listeners about the change + } +} \ No newline at end of file diff --git a/lib/api.dart b/lib/api.dart index 802bee2d..4ec51192 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -1,10 +1,11 @@ //Server and local links -String klocalLink = "127.0.0.1:8000"; -String kserverLink = "172.27.16.215:80"; +String klocalLink = "10.0.2.2:8000"; +String kserverLink = "10.0.2.2:8000"; //Login Service -String kAuthUrl = "172.27.16.215:80"; +String kAuthUrl = "10.0.2.2:8000"; String kAuthLogin = "/api/auth/login/"; +// String kAuthLogin = "/accounts/login"; //Profile Service String kProfile = "/api/profile/"; @@ -20,6 +21,7 @@ String kComplaintRemove = "/complaint/api/removecomplain/"; //Dashboard String kDashboard = "/api/dashboard/"; +String kNotification = "/api/notification/"; String kNotificationRead = "/api/notification/read/"; //Gymkhana diff --git a/lib/main.dart b/lib/main.dart index 9ca81ff8..3e3ab423 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,7 +9,10 @@ import 'package:fusion/screens/Library/Book_Search.dart'; import 'package:fusion/screens/Library/dues.dart'; import 'package:fusion/screens/Library/issued_items.dart'; import 'package:fusion/screens/Library/lib_home_screen.dart'; +import 'package:fusion/screens/LoginandDashboard/DashboardComponents/news.dart'; import 'package:fusion/screens/LoginandDashboard/dashboard.dart'; +import 'package:fusion/screens/LoginandDashboard/DashboardComponents/notify.dart'; +import 'package:fusion/screens/LoginandDashboard/DashboardComponents/announcement.dart'; import 'package:fusion/screens/LoginandDashboard/login_page.dart'; import 'package:fusion/screens/Academic/academic_home_page.dart'; import 'package:fusion/screens/Academic/Current_Semester/current_semester_home_page.dart'; @@ -42,6 +45,21 @@ import 'package:fusion/screens/Healthcenter/viewschedule.dart'; import 'package:fusion/screens/Healthcenter/history.dart'; import 'package:fusion/screens/Healthcenter/HealthCenter.dart'; import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/screens/FileTracking/fts/fts.dart'; +import 'package:fusion/screens/FileTracking/Create_file/create_file.dart'; +import 'package:fusion/screens/FileTracking/View_drafts/view_drafts.dart'; +import 'package:fusion/screens/FileTracking/View_inbox/view_inbox.dart'; +import 'package:fusion/screens/FileTracking/View_outbox/view_outbox.dart'; +import 'package:fusion/screens/FileTracking/Track_file/track_file.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class UserService { + Future getUsername() async { + final prefs = await SharedPreferences.getInstance(); + final username = prefs.getString('username'); + return username ?? ''; + } +} void main() { WidgetsFlutterBinding.ensureInitialized(); @@ -57,12 +75,19 @@ void main() { } class MyApp extends StatelessWidget { + + Future getUsername() async { + final userService = UserService(); + final username = await userService.getUsername(); + return username; + } + @override Widget build(BuildContext context) { MediaQueryData windowData = - MediaQueryData.fromWindow(WidgetsBinding.instance.window); + MediaQueryData.fromView(WidgetsBinding.instance.window); windowData = windowData.copyWith( - textScaleFactor: 1, + textScaler: TextScaler.linear(1), ); return MediaQuery( data: windowData, @@ -81,6 +106,9 @@ class MyApp extends StatelessWidget { routes: { '/landing': (context) => LandingPage(), '/login_page': (context) => LoginPage(), + '/notification':(context)=>Notify(), + '/news':(context)=>News(), + '/announcement':(context)=>Announcement(), '/dashboard': (context) => Dashboard(), '/academic_home_page': (context) => AcademicHomePage( ModalRoute.of(context)!.settings.arguments.toString()), @@ -127,8 +155,14 @@ class MyApp extends StatelessWidget { '/health_center/feedback': (context) => FeedBack(), '/health_center/viewschedule': (context) => ViewSchedule(), '/health_center/history': (context) => History(), + '/fts': (context) => RoundedListView(), + // '/fts/create_file': (context) => CreateFilePage(), + // '/fts/view_drafts': (context) => DraftsPage(), + // '/fts/view_inbox': (context) => InboxPage(), + // '/fts/view_outbox': (context) => OutboxPage(), + // '/fts/tack_file': (context) => FileTrackingPage(), }, ), ); } -} +} \ No newline at end of file diff --git a/lib/screens/Academic/Registration/registration_home_page.dart b/lib/screens/Academic/Registration/registration_home_page.dart index 24dfac50..8cb48fd6 100644 --- a/lib/screens/Academic/Registration/registration_home_page.dart +++ b/lib/screens/Academic/Registration/registration_home_page.dart @@ -65,6 +65,7 @@ class _RegistrationHomePageState extends State { ), ), ), + ], ), ), diff --git a/lib/screens/FileTracking/Archive_file/archive_file.dart b/lib/screens/FileTracking/Archive_file/archive_file.dart new file mode 100644 index 00000000..af27b4f7 --- /dev/null +++ b/lib/screens/FileTracking/Archive_file/archive_file.dart @@ -0,0 +1,139 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; +import 'package:http/http.dart' as http; +import '../View_File/view_archive.dart'; + +class ArchivePage extends StatefulWidget { + final String username; + ArchivePage({required this.username}); + + @override + _ArchivePageState createState() => _ArchivePageState(); +} + +class _ArchivePageState extends State { + final _designationController = TextEditingController(); + List fileIDsList = []; + List jsonData = []; + String? errorMessage; + + Future _submitForm() async { + try { + if (_designationController.text.isEmpty == true) { + throw Exception('Designation required.'); + } + + var storageService = locator(); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); + } + + Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; + + final queryParams = { + 'username': widget.username, + 'designation': _designationController.text, + 'src_module': 'filetracking', + }; + + final Uri url = Uri.http('10.0.2.2:8000', '/filetracking/api/archive/', queryParams); + + final client = http.Client(); + + final response = await client.get(url, headers: headers); + + if (response.statusCode == 200) { + jsonData = json.decode(response.body); + List fileIDs = jsonData.map((data) => data['id'].toString()).toList(); + + setState(() { + fileIDsList = fileIDs; + errorMessage=null; + }); + } else { + print('Error fetching outbox data: ${response.statusCode}'); + setState(() { + errorMessage = 'Invalid designation'; + }); + } + } catch (e) { + print('An error occurred: $e'); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Archive Page'), + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(16.0), + child: Column( + children: [ + Divider(thickness: 1.0, color: Colors.grey[300]), + Column( + children: [ + TextField( + controller: _designationController, + decoration: InputDecoration( + labelText: 'View As', + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: () async { + await _submitForm(); + }, + child: Text('View'), + ), + ], + ), + ], + ), + if (errorMessage != null) + Text( + errorMessage!, + style: TextStyle(color: Colors.red), + ), + Column( + children: fileIDsList.map((fileID) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('File ID: $fileID'), + Row( + children: [ + TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MessageDetailPage(messageDetails: jsonData[fileIDsList.indexOf(fileID)], username: '',), + ), + ); + }, + child: Text('View'), + ) + ], + ), + ], + ), + ); + }).toList(), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/FileTracking/Create_file/create_file.dart b/lib/screens/FileTracking/Create_file/create_file.dart new file mode 100644 index 00000000..09daddcc --- /dev/null +++ b/lib/screens/FileTracking/Create_file/create_file.dart @@ -0,0 +1,248 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; +import 'package:http/http.dart' as http; + +class CreateFilePage extends StatefulWidget { + final String username; + CreateFilePage({required this.username}); + + @override + _CreateFilePageState createState() => _CreateFilePageState(); +} + +class _CreateFilePageState extends State { + final _titleController = TextEditingController(); + final _descriptionController = TextEditingController(); + final _sendAsController = TextEditingController(); + final _forwardToController = TextEditingController(); + final _designationController = TextEditingController(); + + File? _attachedFile; + + Future _submitForm() async { + try { + if (_titleController.text?.isEmpty == true || + _descriptionController.text?.isEmpty == true) { + throw Exception('Title and description are required.'); + } + var storageService = locator(); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); + } + + Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; + + + var request = http.MultipartRequest( + 'POST', + Uri.http( + '10.0.2.2:8000', '/filetracking/api/file/' + ), + ); + + request.fields['subject'] = _titleController.text; + request.fields['designation'] = _sendAsController.text; + request.fields['receiver_username'] = _forwardToController.text; + request.fields['receiver_designation'] = _designationController.text; + request.fields['description'] = _descriptionController.text; + + if(_attachedFile != null) { + var filePart = http.MultipartFile( + 'file', + _attachedFile!.readAsBytes().asStream(), + _attachedFile!.lengthSync(), + filename: _attachedFile!.path.split('/').last, + ); + request.files.add(filePart); + } + + request.headers['Authorization'] = 'Token ' + (storageService.userInDB?.token ?? ""); + request.headers['Content-Type'] = 'application/json'; + + var response = await http.Response.fromStream(await request.send()); + + print(response.statusCode); + if (response.statusCode == HttpStatus.created) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('File sent'), + backgroundColor: Colors.green, + ), + ); + + Navigator.pop(context); + } else { + final errorMessage = await _parseErrorMessage(response); + print('Error: $errorMessage'); + } + } catch (e) { + print('An error occurred: $e'); + } + } + + Future _draftForm() async { + try { + // Validate required fields + if (_titleController.text?.isEmpty == true) { + throw Exception('Title is required.'); + } + + // Prepare headers + var storageService = locator(); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); + } + + Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; + + // Prepare request body + print(_titleController.text); + final data = jsonEncode({ + 'subject': _titleController.text, + 'description': _descriptionController.text, + 'uploader': widget.username, + 'uploader_designation': _sendAsController.text, + 'src_module': 'filetracking' + }); + // Make POST request + final response = await http.post( + Uri.http('10.0.2.2:8000', '/filetracking/api/createdraft/'), + headers: headers, + body: data, + ); + + // Handle response + if (response.statusCode == HttpStatus.created) { + // Show success message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Draft saved'), + backgroundColor: Colors.green, + ), + ); + Navigator.pop(context); + } else { + // Handle other status codes (e.g., show error message) + final errorMessage = await _parseErrorMessage(response); + print('Error: $errorMessage'); + } + } catch (e) { + // Handle other exceptions + print('An error occurred: $e'); + } +} + + Future _parseErrorMessage(http.Response response) async { + try { + final decodedResponse = jsonDecode(response.body); + return decodedResponse['message'] ?? 'Unknown error'; + } catch (error) { + return 'Failed to parse error message'; + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Create File'), + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(16.0), + child: Column( + children: [ + Divider(thickness: 1.0, color: Colors.grey[300]), + Column( + children: [ + TextField( + controller: _titleController, + decoration: InputDecoration( + labelText: 'Title*', + errorText: + _titleController.text.isEmpty ? 'Required' : null, + ), + ), + TextField( + controller: _descriptionController, + decoration: InputDecoration( + labelText: 'Description*', + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text('Attach File'), + SizedBox(width: 16.0), + ElevatedButton( + onPressed: () async { + FilePickerResult? result = + await FilePicker.platform.pickFiles(); + + if (result != null) { + setState(() { + _attachedFile = File(result.files.single.path ?? ''); + }); + } else { + // User canceled the file picking + } + }, + child: Text('Browse'), + ), + SizedBox(width: 10), + Text(_attachedFile?.path.split('/').last ?? ''), + ], + ), + TextField( + controller: _sendAsController, + decoration: InputDecoration( + labelText: 'Send as', + ), + ), + TextField( + controller: _forwardToController, + decoration: InputDecoration( + labelText: 'Forward To', + ), + ), + TextField( + controller: _designationController, + decoration: InputDecoration( + labelText: 'Receiver\'s Designation', + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: () async { + await _submitForm(); + }, + child: Text('Send'), + ), + ElevatedButton( + onPressed: () async { + await _draftForm(); + }, + child: Text('Draft'), + ), + ], + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/FileTracking/Forward_file/forward_file.dart b/lib/screens/FileTracking/Forward_file/forward_file.dart new file mode 100644 index 00000000..2abbd024 --- /dev/null +++ b/lib/screens/FileTracking/Forward_file/forward_file.dart @@ -0,0 +1,179 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; +import 'package:http/http.dart' as http; + +class ForwardFilePage extends StatefulWidget { + final Map fileDetails; + + const ForwardFilePage({Key? key, required this.fileDetails}) : super(key: key); + + @override + _ForwardFilePageState createState() => _ForwardFilePageState(); +} + +class _ForwardFilePageState extends State { + String receiver = ''; // Receiver username + String receiverDesignation = ''; // Receiver designation + String remarks = ''; // Remarks entered by the user + bool forwarded = false; // Track if the file has been forwarded + + Future forwardFile({ + required String receiver, + required String receiverDesignation, + required String remarks, + required BuildContext context, + }) async { + try { + var storageService = locator(); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); + } + + Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; + + final data = jsonEncode({ + 'receiver': receiver, + 'receiver_designation': receiverDesignation, + 'remarks': remarks, + }); + + final file_id = widget.fileDetails['id']; + + final client = http.Client(); + final response = await client.post( + Uri.http('10.0.2.2:8000', '/filetracking/api/forwardfile/$file_id/'), + headers: headers, + body: data, + ); + + if (response.statusCode == 201) { + print('File forwarded successfully'); + + // Show a green prompt indicating that the file has been forwarded + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + backgroundColor: Colors.green, + content: Text('File forwarded successfully to $receiver'), + ), + ); + + setState(() { + forwarded = true; // Update the forwarded status + }); + + // Pop out of the page + Navigator.pop(context); + } else { + print('Error forwarding file: ${response.statusCode}'); + } + } catch (error) { + print('Failed to forward file. Error: $error'); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Forward File'), + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + 'File Details', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + ), + ListTile( + title: Text( + 'Subject', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(widget.fileDetails['subject']), + ), + ListTile( + title: Text( + 'Uploader', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(widget.fileDetails['uploader']), + ), + ListTile( + title: Text( + 'Sent By', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(widget.fileDetails['sent_by_user']), + ), + SizedBox(height: 16), + TextField( + enabled: !forwarded, // Disable text field if file has been forwarded + decoration: InputDecoration( + labelText: 'Remark', + border: OutlineInputBorder(), + ), + onChanged: (value) { + remarks = value; + }, + ), + SizedBox(height: 16), + TextField( + enabled: !forwarded, // Disable text field if file has been forwarded + decoration: InputDecoration( + labelText: 'Receiver Username', + border: OutlineInputBorder(), + ), + onChanged: (value) { + receiver = value; + }, + ), + SizedBox(height: 16), + TextField( + enabled: !forwarded, // Disable text field if file has been forwarded + decoration: InputDecoration( + labelText: 'Receiver Designation', + border: OutlineInputBorder(), + ), + onChanged: (value) { + receiverDesignation = value; + }, + ), + SizedBox(height: 16), + ElevatedButton( + onPressed: !forwarded + ? () { + forwardFile( + receiver: receiver, + receiverDesignation: receiverDesignation, + remarks: remarks, + context: context, + ); + } + : null, // Disable button if file has been forwarded + child: Text('Forward'), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/FileTracking/Track_file/track_file.dart b/lib/screens/FileTracking/Track_file/track_file.dart new file mode 100644 index 00000000..93a1d412 --- /dev/null +++ b/lib/screens/FileTracking/Track_file/track_file.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; + +// Assuming you have a model class for file data +class TrackFile { + final int id; + final String name; + final DateTime createdDate; + final String sender; + final String recipient; + final String department; + + TrackFile({ + required this.id, + required this.name, + required this.createdDate, + required this.sender, + required this.recipient, + required this.department, + }); +} + +class FileTrackingPage extends StatefulWidget { + @override + _FileTrackingPageState createState() => _FileTrackingPageState(); +} + +class _FileTrackingPageState extends State { + List exampleFiles = [ + TrackFile( + id: 1, + name: "Document 1", + createdDate: DateTime.now().subtract(Duration(days: 2)), + sender: "Aishwarya", + recipient: "Yash", + department: "IT", + ), + TrackFile( + id: 2, + name: "Report Q3", + createdDate: DateTime.now().subtract(Duration(days: 5)), + sender: "Yash", + recipient: "Department Head", + department: "Finance", + ), + ]; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('File Tracking'), + ), + body: ListView.builder( + itemCount: exampleFiles.length, + itemBuilder: (context, index) { + final trackFile = exampleFiles[index]; + return ListTile( + title: Text(trackFile.name), + subtitle: Text( + "Sent by: ${trackFile.sender} | Sent to: ${trackFile.recipient}", + ), + trailing: TextButton( + onPressed: () { + // Handle "View File" action + // ... navigation or action to view file details + Navigator.pushNamed( + context, + '/fts/view_file_details', // Replace with your details page route + arguments: trackFile, // Pass the TrackFile object for details + ); + }, + child: Text('View File'), + ), + ); + }, + ), + ); + } +} diff --git a/lib/screens/FileTracking/View_File/view_archive.dart b/lib/screens/FileTracking/View_File/view_archive.dart new file mode 100644 index 00000000..63e039c5 --- /dev/null +++ b/lib/screens/FileTracking/View_File/view_archive.dart @@ -0,0 +1,183 @@ +import 'package:flutter/material.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; + +class MessageDetailPage extends StatefulWidget { + final Map messageDetails; + final String username; + + const MessageDetailPage({Key? key, required this.messageDetails, required this.username}) : super(key: key); + + @override + _MessageDetailPageState createState() => _MessageDetailPageState(); +} + +class _MessageDetailPageState extends State { + String? historyResponse; + String? messageResponse; + String? receiver; + + Future fetchHistory() async { + try { + var storageService = locator(); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); + } + + Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; + + final Uri url = Uri.http('10.0.2.2:8000', '/filetracking/api/file/${widget.messageDetails['id']}'); + final Uri historyUrl = Uri.http('10.0.2.2:8000', '/filetracking/api/history/${widget.messageDetails['id']}'); + + final client = http.Client(); + final response = await client.get(url, headers: headers); + final historyResponse = await client.get(historyUrl, headers: headers); + + print(response.body); + if (response.statusCode == 200 && historyResponse.statusCode == 200) { + setState(() { + this.historyResponse = historyResponse.body; + this.messageResponse = response.body; + final List historyData = json.decode(historyResponse.body); + if (historyData.isNotEmpty) { + receiver = historyData[0]['receiver_id']; + } + }); + } else { + print('Failed to fetch file history. Error: ${response.reasonPhrase}'); + } + } catch (error) { + print('Failed to fetch file history. Error: $error'); + } + } + + @override + void initState() { + super.initState(); + fetchHistory(); + } + + Widget build(BuildContext context) { + final bool isUploader = widget.messageDetails['uploader'] == widget.username; + + return Scaffold( + appBar: AppBar( + title: Text('Archive Details'), + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + 'Message Details', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + ), + if (messageResponse != null) ...[ + Text('File ID: ${widget.messageDetails['id'] ?? 'N/A'}'), + Text('Subject: ${widget.messageDetails['subject'] ?? 'N/A'}'), + SizedBox(height: 16), + Text( + 'History', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + if (historyResponse != null) + buildMessageThread(json.decode(historyResponse!)), + ], + ], + ), + ), + ); + } + +Widget buildMessageThread(List historyData) { + // Organize messages based on sender and receiver + Map>> messageThreads = {}; + for (var message in historyData) { + String sender = message['current_id']; + String receiver = message['receiver_id']; + messageThreads.putIfAbsent(sender, () => []); + messageThreads[sender]!.add(message); + } + + // Sort message threads by date + messageThreads.forEach((key, value) { + value.sort((a, b) => a['receive_date'].compareTo(b['receive_date'])); + }); + + // Build UI for message threads + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: messageThreads.keys.map((sender) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (sender == messageThreads.keys.last) // Check if it's the last sender + Text( + 'Uploader: ${sender}', // Display "Uploader" instead of actual sender + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ) + else + Text( + 'Sent By: ${sender}', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 8), + Column( + children: messageThreads[sender]!.map((message) { + String formattedDate = ''; + String forwardDate = message['forward_date'] ?? 'N/A'; + if (forwardDate != 'N/A') { + DateTime date = DateTime.parse(forwardDate); + String formattedTime = '${date.hour}:${date.minute}:${date.second}'; + formattedDate = '${date.year}-${date.month}-${date.day}'; + formattedDate = '$formattedDate & Time: $formattedTime'; // Combining date and time + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + title: Text('Sent To: ${message['receiver_id']}'), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Remarks: ${message['remarks']}'), + Text('Date: $formattedDate'), // Display formatted date and time + if (message['upload_file'] != null) + Text('Uploaded File: ${message['upload_file']}'), // Display uploaded file if it exists + // Add more details here as needed + ], + ), + ), + SizedBox(height: 8), + Divider(), + ], + ); + }).toList(), + ), + SizedBox(height: 8), + + ], + ); + }).toList(), + ); +} +} \ No newline at end of file diff --git a/lib/screens/FileTracking/View_File/view_draftfile.dart b/lib/screens/FileTracking/View_File/view_draftfile.dart new file mode 100644 index 00000000..cdc5c977 --- /dev/null +++ b/lib/screens/FileTracking/View_File/view_draftfile.dart @@ -0,0 +1,209 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; +import 'package:http/http.dart' as http; + +class ViewDraftFilePage extends StatefulWidget { + final Map draftDetails; + final Function(String) onDelete; + + const ViewDraftFilePage({Key? key, required this.draftDetails, required this.onDelete}) : super(key: key); + + @override + _ViewDraftFilePageState createState() => _ViewDraftFilePageState(); +} + +class _ViewDraftFilePageState extends State { + String receiver = ''; // Receiver username + String receiverDesignation = ''; // Receiver designation + File? _attachedFile; + + void _submitForm(BuildContext context, String receiver, String receiverDesignation) async { + try { + // Validate receiver and receiver designation + if (receiver.isEmpty || receiverDesignation.isEmpty) { + throw Exception('Receiver username and designation are required.'); + } + + var storageService = locator(); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); + } + + Map headers = { + 'Authorization': 'Token ${storageService.userInDB?.token ?? ""}', + 'Content-Type': 'application/json' + }; + var request = http.MultipartRequest( + 'POST', + Uri.http( + '10.0.2.2:8000', '/filetracking/api/file/' + ), + ); + + request.fields['subject'] = widget.draftDetails['subject'].toString(); + request.fields['designation'] = 'student'; + request.fields['receiver_username'] = receiver; + request.fields['receiver_designation'] = receiverDesignation; + request.fields['description'] = widget.draftDetails['description'].toString(); + + // Prepare request body + if(_attachedFile != null) { + var filePart = await http.MultipartFile.fromPath( + 'file', + _attachedFile!.path, + ); + request.files.add(filePart); + } + + request.headers['Authorization'] = 'Token ' + (storageService.userInDB?.token ?? ""); + request.headers['Content-Type'] = 'application/json'; + + var response = await http.Response.fromStream(await request.send()); + + print(response.statusCode); + // Handle response + if (response.statusCode == HttpStatus.created) { + // Show success message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Draft sent successfully to $receiver'), + backgroundColor: Colors.green, + ), + ); + + // Delete the current draft + widget.onDelete(widget.draftDetails['id'].toString()); + + // Close current page and go back to the previous screen + Navigator.pop(context); // Pop out the current page + } else { + // Show error message with server response + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Failed to send draft: ${response.body}'), + backgroundColor: Colors.red, + ), + ); + } + } catch (e) { + // Show error message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Failed to send draft: $e'), + backgroundColor: Colors.red, + ), + ); + } +} + + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Draft File Details'), + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + 'Draft Details', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + ), + ListTile( + title: Text( + 'ID', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(widget.draftDetails['id'].toString()), + ), + ListTile( + title: Text( + 'Subject', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(widget.draftDetails['subject'] ?? 'No subject available'), + ), + ListTile( + title: Text( + 'Description', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(widget.draftDetails['description'] ?? 'No description available'), + ), + SizedBox(height: 16), + ElevatedButton( + onPressed: () async { + FilePickerResult? result = + await FilePicker.platform.pickFiles(); + + if (result != null) { + setState(() { + _attachedFile = File(result.files.single.path ?? ''); + }); + } else { + // User canceled the file picking + } + }, + child: Text('Browse'), + ), + SizedBox(height: 16), + _attachedFile != null + ? Text(_attachedFile!.path) + : Container(), // Display selected file path + SizedBox(height: 16), + TextField( + decoration: InputDecoration( + labelText: 'Receiver Username', + border: OutlineInputBorder(), + ), + onChanged: (value) { + setState(() { + receiver = value; + }); + }, + ), + SizedBox(height: 16), + TextField( + decoration: InputDecoration( + labelText: 'Receiver Designation', + border: OutlineInputBorder(), + ), + onChanged: (value) { + setState(() { + receiverDesignation = value; + }); + }, + ), + SizedBox(height: 16), + ElevatedButton( + onPressed: () { + _submitForm(context, receiver, receiverDesignation); + }, + child: Text('Send'), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/FileTracking/View_File/view_inboxfile.dart b/lib/screens/FileTracking/View_File/view_inboxfile.dart new file mode 100644 index 00000000..a5af4e97 --- /dev/null +++ b/lib/screens/FileTracking/View_File/view_inboxfile.dart @@ -0,0 +1,261 @@ +import 'package:flutter/material.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import '../Forward_file/forward_file.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class MessageDetailPage extends StatefulWidget { + final Map messageDetails; + final String username; + + const MessageDetailPage({Key? key, required this.messageDetails, required this.username}) : super(key: key); + + _MessageDetailPageState createState() => _MessageDetailPageState(); +} + +class _MessageDetailPageState extends State { + String? historyResponse; + String? messageResponse; + String? receiver; + String? filename; + + Future fetchHistory() async { + try { + var storageService = locator(); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); + } + + Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; + + final Uri url = Uri.http('10.0.2.2:8000', '/filetracking/api/file/${widget.messageDetails['id']}'); + final Uri historyUrl = Uri.http('10.0.2.2:8000', '/filetracking/api/history/${widget.messageDetails['id']}'); + + final client = http.Client(); + final response = await client.get(url, headers: headers); + final historyResponse = await client.get(historyUrl, headers: headers); + final fileResponse = json.decode(response.body); // Parse response body + setState(() { + filename = fileResponse["upload_file"]; + }); + if (response.statusCode == 200 && historyResponse.statusCode == 200) { + setState(() { + this.historyResponse = historyResponse.body; + this.messageResponse = response.body; + final List historyData = json.decode(historyResponse.body); + if (historyData.isNotEmpty) { + receiver = historyData[0]['receiver_id']; + } + }); + } else { + print('Failed to fetch file history. Error: ${response.reasonPhrase}'); + } + } catch (error) { + print('Failed to fetch file history. Error: $error'); + } + } + + Future _archiveFile(String fileID) async { + try { + var storageService = locator(); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); + } + + Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; + + final Uri url = Uri.http('10.0.2.2:8000', '/filetracking/api/createarchive/'); + + final client = http.Client(); + + final response = await client.post(url, headers: headers, body: json.encode({'file_id': fileID})); + + if (response.statusCode == 200) { + print('File $fileID archived successfully'); + + // Show a green prompt indicating that the file has been archived + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + backgroundColor: Colors.green, + content: Text('File $fileID archived successfully'), + ), + ); + + // Pop out to the parent page + Navigator.pop(context); + } else { + print('Error archiving file $fileID: ${response.statusCode}'); + } + } catch (e) { + print('An error occurred while archiving file $fileID: $e'); + } + } + + @override + void initState() { + super.initState(); + fetchHistory(); + } + +Widget build(BuildContext context) { + final bool isUploader = widget.messageDetails['uploader'] == widget.username; + final Uri fileUrl = Uri.http('10.0.2.2:8000', filename ?? ""); + + return Scaffold( + appBar: AppBar( + title: Text('Inbox Details'), + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + 'Message Details', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + ), + if (messageResponse != null) ...[ + Text('File ID: ${widget.messageDetails['id'] ?? 'N/A'}'), + Text('Subject: ${widget.messageDetails['subject'] ?? 'N/A'}'), + if (filename != null) + ElevatedButton( + onPressed: () async { + if (await canLaunch(fileUrl.toString())) { + await launch(fileUrl.toString()); + } else { + throw 'Could not launch $fileUrl'; + } + }, + child: Text('File Attached'), + ), + SizedBox(height: 16), + Text( + 'History', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + if (historyResponse != null) + buildMessageThread(json.decode(historyResponse!)), + ], + // **Integration of buttons here** + if (!isUploader) // Show Forward button if not uploader + ElevatedButton( + onPressed: () { + // Navigate to ForwardFilePage + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ForwardFilePage(fileDetails: widget.messageDetails), + ), + ); + }, + child: Text('Forward'), + ), + if (isUploader) // Show Archive button if uploader + ElevatedButton( + onPressed: () { + // Archive the file + _archiveFile(widget.messageDetails['id'].toString()); + }, + child: Text('Archive'), + ), + ], + ), + ), + ); +} + + +Widget buildMessageThread(List historyData) { + // Organize messages based on sender and receiver + Map>> messageThreads = {}; + for (var message in historyData) { + String sender = message['current_id']; + String receiver = message['receiver_id']; + messageThreads.putIfAbsent(sender, () => []); + messageThreads[sender]!.add(message); + } + + // Sort message threads by date + messageThreads.forEach((key, value) { + value.sort((a, b) => a['receive_date'].compareTo(b['receive_date'])); + }); + + // Build UI for message threads + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: messageThreads.keys.map((sender) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (sender == messageThreads.keys.last) // Check if it's the last sender + Text( + 'Uploader: ${sender}', // Display "Uploader" instead of actual sender + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ) + else + Text( + 'Sent By: ${sender}', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 8), + Column( + children: messageThreads[sender]!.map((message) { + String formattedDate = ''; + String forwardDate = message['forward_date'] ?? 'N/A'; + if (forwardDate != 'N/A') { + DateTime date = DateTime.parse(forwardDate); + String formattedTime = '${date.hour}:${date.minute}:${date.second}'; + formattedDate = '${date.year}-${date.month}-${date.day}'; + formattedDate = '$formattedDate & Time: $formattedTime'; // Combining date and time + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + title: Text('Sent To: ${message['receiver_id']}'), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Remarks: ${message['remarks']}'), + Text('Date: $formattedDate'), // Display formatted date and time + if (message['upload_file'] != null) + Text('Uploaded File: ${message['upload_file']}'), // Display uploaded file if it exists + // Add more details here as needed + ], + ), + ), + SizedBox(height: 8), + Divider(), + ], + ); + }).toList(), + ), + SizedBox(height: 8), + + ], + ); + }).toList(), + ); +} +} \ No newline at end of file diff --git a/lib/screens/FileTracking/View_File/view_outboxfile.dart b/lib/screens/FileTracking/View_File/view_outboxfile.dart new file mode 100644 index 00000000..76902da2 --- /dev/null +++ b/lib/screens/FileTracking/View_File/view_outboxfile.dart @@ -0,0 +1,204 @@ +import 'package:flutter/material.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; +import 'package:http/http.dart' as http; +import 'package:url_launcher/url_launcher.dart'; +import 'dart:convert'; + +class MessageDetailPage extends StatefulWidget { + final Map messageDetails; + final String username; + + const MessageDetailPage({Key? key, required this.messageDetails, required this.username}) : super(key: key); + + @override + _MessageDetailPageState createState() => _MessageDetailPageState(); +} + +class _MessageDetailPageState extends State { + String? historyResponse; + String? messageResponse; + String? receiver; + String? filename; + + Future fetchHistory() async { + try { + var storageService = locator + (); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); + } + + Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; + + final Uri url = Uri.http('10.0.2.2:8000', '/filetracking/api/file/${widget.messageDetails['id']}'); + final Uri historyUrl = Uri.http('10.0.2.2:8000', '/filetracking/api/history/${widget.messageDetails['id']}'); + + final client = http.Client(); + final response = await client.get(url, headers: headers); + final historyResponse = await client.get(historyUrl, headers: headers); + final fileResponse = json.decode(response.body); // Parse response body + setState(() { + filename = fileResponse["upload_file"]; + }); + if (response.statusCode == 200 && historyResponse.statusCode == 200) { + setState(() { + this.historyResponse = historyResponse.body; + this.messageResponse = response.body; + final List historyData = json.decode(historyResponse.body); + if (historyData.isNotEmpty) { + receiver = historyData[0]['receiver_id']; + } + }); + } else { + print('Failed to fetch file history. Error: ${response.reasonPhrase}'); + } + } catch (error) { + print('Failed to fetch file history. Error: $error'); + } + } + + @override + void initState() { + super.initState(); + fetchHistory(); + } + + @override + Widget build(BuildContext context) { + final bool isUploader = widget.messageDetails['uploader'] == widget.username; + final Uri fileUrl = Uri.http('10.0.2.2:8000', filename ?? ""); + print(fileUrl); + + return Scaffold( + appBar: AppBar( + title: Text('Outbox Details'), + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + 'Message Details', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + ), + if (messageResponse != null) ...[ + Text('File ID: ${widget.messageDetails['id'] ?? 'N/A'}'), + Text('Subject: ${widget.messageDetails['subject'] ?? 'N/A'}'), + // Text('Attachments: ${filename ?? 'None'}'), + if (filename != null) + ElevatedButton( + onPressed: () async { + if (await canLaunch(fileUrl.toString())) { + await launch(fileUrl.toString()); + } else { + throw 'Could not launch $fileUrl'; + } + }, + child: Text('File Attached'), + ), + SizedBox(height: 16), + Text( + 'History', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + if (historyResponse != null) + buildMessageThread(json.decode(historyResponse!)), + SizedBox(height: 16), + ], + ], + ), + ), + ); + } + + Widget buildMessageThread(List historyData) { + // Organize messages based on sender and receiver + Map>> messageThreads = {}; + for (var message in historyData) { + String sender = message['current_id']; + String receiver = message['receiver_id']; + messageThreads.putIfAbsent(sender, () => []); + messageThreads[sender]!.add(message); + } + + // Sort message threads by date + messageThreads.forEach((key, value) { + value.sort((a, b) => a['receive_date'].compareTo(b['receive_date'])); + }); + + // Build UI for message threads + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: messageThreads.keys.map((sender) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (sender == messageThreads.keys.last) // Check if it's the last sender + Text( + 'Uploader: ${sender}', // Display "Uploader" instead of actual sender + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ) + else + Text( + 'Sent By: ${sender}', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 8), + Column( + children: messageThreads[sender]!.map((message) { + String formattedDate = ''; + String forwardDate = message['forward_date'] ?? 'N/A'; + if (forwardDate != 'N/A') { + DateTime date = DateTime.parse(forwardDate); + String formattedTime = '${date.hour}:${date.minute}:${date.second}'; + formattedDate = '${date.year}-${date.month}-${date.day}'; + formattedDate = '$formattedDate & Time: $formattedTime'; // Combining date and time + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + title: Text('Sent To: ${message['receiver_id']}'), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Remarks: ${message['remarks']}'), + Text('Date: $formattedDate'), // Display formatted date and time + if (message['upload_file'] != null) + Text('Uploaded File: ${message['upload_file']}'), // Display uploaded file if it exists + // Add more details here as needed + ], + ), + ), + SizedBox(height: 8), + Divider(), + ], + ); + }).toList(), + ), + SizedBox(height: 8), + + ], + ); + }).toList(), + ); + } +} diff --git a/lib/screens/FileTracking/View_drafts/view_drafts.dart b/lib/screens/FileTracking/View_drafts/view_drafts.dart new file mode 100644 index 00000000..1a506128 --- /dev/null +++ b/lib/screens/FileTracking/View_drafts/view_drafts.dart @@ -0,0 +1,219 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; +import 'package:http/http.dart' as http; +import '../View_File/view_draftfile.dart'; + +class DraftFileData { + final String id; + + DraftFileData({ + required this.id, + }); +} + +class DraftsPage extends StatefulWidget { + final String username; + + DraftsPage({required this.username}); + + @override + _DraftsPageState createState() => _DraftsPageState(); +} + +class _DraftsPageState extends State { + final TextEditingController _designationController = TextEditingController(); + List fileIDsList = []; + List jsonData = []; + String? errorMessage; + + @override + void dispose() { + _designationController.dispose(); + super.dispose(); + } + + Future _fetchDrafts() async { + try { + var storageService = locator(); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); + } + + final Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; + + final queryParams = { + 'username': widget.username, + 'designation': _designationController.text, + 'src_module': 'filetracking', + }; + + final Uri url = Uri.http('10.0.2.2:8000', '/filetracking/api/draft/', queryParams); + + final client = http.Client(); + final response = await client.get(url, headers: headers); + + if (response.statusCode == 200) { + jsonData = json.decode(response.body); + print(response.body); + // Extract file IDs + List fileIDs = jsonData.map((data) => data['id'].toString()).toList(); + + // Update the UI with file IDs + setState(() { + fileIDsList = fileIDs; + errorMessage = null; + }); + } else { + setState(() { + errorMessage = 'Error fetching draft files: ${response.reasonPhrase}'; + }); + } + } catch (error) { + setState(() { + errorMessage = 'An error occurred: $error'; + }); + } + } + + void _deleteDraftAndUpdateList(String id) async { + // Call the function to delete the draft + await _deleteDraft(id); + // Fetch the drafts again to update the list + await _fetchDrafts(); +} + + + Future _deleteDraft(String id) async { + try { + var storageService = locator(); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); + } + + final Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; + + final Uri url = Uri.http('10.0.2.2:8000', '/filetracking/api/file/$id/'); + + final client = http.Client(); + final response = await client.delete(url, headers: headers); + + if (response.statusCode == 204) { + // Draft file deleted successfully, refresh the list + await _fetchDrafts(); + + // Show success message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Draft file deleted successfully'), + backgroundColor: Colors.green, + ), + ); + } else { + throw Exception('Failed to delete draft file: ${response.reasonPhrase}'); + } + } catch (error) { + setState(() { + errorMessage = 'An error occurred while deleting draft file: $error'; + }); + } +} + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Draft'), + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(16.0), + child: Column( + children: [ + // Divider + Divider(thickness: 1.0, color: Colors.grey[300]), + + // Form content + Column( + children: [ + // Designation field + TextField( + controller: _designationController, + decoration: InputDecoration( + labelText: 'View As', + ), + ), + + // Send button + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: () async { + await _fetchDrafts(); + }, + child: Text('View'), + ), + ], + ), + ], + ), + if (errorMessage != null) + Text( + errorMessage!, + style: TextStyle(color: Colors.red), + ), + + // Display file IDs and buttons + Column( + children: fileIDsList.map((fileID) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('File ID: $fileID'), + Row( + children: [ + TextButton( + onPressed: () { + // Handle view button pressed + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ViewDraftFilePage( + draftDetails: jsonData[fileIDsList.indexOf(fileID)], + onDelete: _deleteDraftAndUpdateList, + ), + ), + ); + }, + child: Text('View'), + ), + SizedBox(width: 8), + TextButton( + onPressed: () { + // Handle delete button pressed + _deleteDraft(fileID); + }, + child: Text('Delete'), + ), + ], + ), + ], + ), + ); + }).toList(), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/FileTracking/View_inbox/view_inbox.dart b/lib/screens/FileTracking/View_inbox/view_inbox.dart new file mode 100644 index 00000000..91bcb6df --- /dev/null +++ b/lib/screens/FileTracking/View_inbox/view_inbox.dart @@ -0,0 +1,180 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; +import 'package:http/http.dart' as http; +import '../View_File/view_inboxfile.dart'; +import '../Forward_file/forward_file.dart'; + + +class InboxMessage { + final String sender; + final String subject; + final String snippet; + final DateTime timestamp; + + InboxMessage({ + required this.sender, + required this.subject, + required this.snippet, + required this.timestamp, + }); +} + +class InboxPage extends StatefulWidget { + final String username; + InboxPage({required this.username}); + + @override + _InboxPageState createState() => _InboxPageState(); +} + +class _InboxPageState extends State { + final _designationController = TextEditingController(); + List fileIDsList = []; + List jsonData = []; + String? errorMessage; + + @override + void initState() { + super.initState(); + } + + Future _submitForm() async { + try { + if (_designationController.text?.isEmpty == true) { + throw Exception('Designation required.'); + } + + var storageService = locator(); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); + } + + Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; + + // Prepare query parameters + final queryParams = { + 'username': widget.username, + 'designation': _designationController.text, + 'src_module': 'filetracking', + }; + + // Construct URL with encoded query parameters + + final Uri url = Uri.http('10.0.2.2:8000', '/filetracking/api/inbox/', queryParams); + + final client = http.Client(); + + // Make GET request + final response = await client.get(url, headers: headers); + // Handle response + if (response.statusCode == 200) { + // Parse the JSON response + jsonData = json.decode(response.body); + // Extract file IDs + List fileIDs = jsonData.map((data) => data['id'].toString()).toList(); + + // Update the UI with file IDs + setState(() { + fileIDsList = fileIDs; + errorMessage=null; + }); + } else { + + // Handle error (e.g., print error message) + print('Error fetching inbox data: ${response.statusCode}'); + setState(() { + errorMessage = 'Invalid designation'; + }); + } + } catch (e) { + // Handle other exceptions (e.g., network errors, token errors) + print('An error occurred: $e'); + } + } + + @override +Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Inbox'), + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(16.0), + child: Column( + children: [ + // Divider + Divider(thickness: 1.0, color: Colors.grey[300]), + + // Form content + Column( + children: [ + // Designation field + TextField( + controller: _designationController, + decoration: InputDecoration( + labelText: 'View As', + ), + ), + + // Send button + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: () async { + await _submitForm(); + }, + child: Text('View'), + ), + ], + ), + ], + ), + if (errorMessage != null) + Text( + errorMessage!, + style: TextStyle(color: Colors.red), + ), + + // Display file IDs and buttons + Column( + children: fileIDsList.map((fileID) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('File ID: $fileID'), + TextButton( + onPressed: () { + // print(jsonData[fileIDsList.indexOf(fileID)]); + // Handle view button pressed + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MessageDetailPage( + messageDetails: jsonData[fileIDsList.indexOf(fileID)], + username: widget.username, + ), + ), + ); + }, + child: Text('View'), + ), + ], + ), + ); + }).toList(), + ), + ], + ), + ), + ); +} +} \ No newline at end of file diff --git a/lib/screens/FileTracking/View_outbox/view_outbox.dart b/lib/screens/FileTracking/View_outbox/view_outbox.dart new file mode 100644 index 00000000..c6e985cf --- /dev/null +++ b/lib/screens/FileTracking/View_outbox/view_outbox.dart @@ -0,0 +1,140 @@ +import 'package:flutter/material.dart'; +import 'package:fusion/models/user.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; +import '../View_File/view_outboxfile.dart'; + +class OutboxPage extends StatefulWidget { + final String username; + OutboxPage({required this.username}); + + @override + _OutboxPageState createState() => _OutboxPageState(); +} + +class _OutboxPageState extends State { + final _designationController = TextEditingController(); + List fileIDsList = []; + List jsonData = []; + String? errorMessage; + + Future _submitForm() async { + try { + if (_designationController.text?.isEmpty == true) { + throw Exception('Designation required.'); + } + + var storageService = locator(); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); + } + + Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; + + final queryParams = { + 'username': widget.username, + 'designation': _designationController.text, + 'src_module': 'filetracking', + }; + + final Uri url = Uri.http('10.0.2.2:8000', '/filetracking/api/outbox/', queryParams); + + final client = http.Client(); + + final response = await client.get(url, headers: headers); + + if (response.statusCode == 200) { + jsonData = json.decode(response.body); + List fileIDs = jsonData.map((data) => data['id'].toString()).toList(); + + setState(() { + fileIDsList = fileIDs; + errorMessage = null; + }); + } else { + print('Error fetching outbox data: ${response.statusCode}'); + setState(() { + errorMessage = 'Invalid designation'; + }); + } + } catch (e) { + print('An error occurred: $e'); + } + } + + @override + Widget build(BuildContext context) { + var storageService = locator(); + var currentUser = widget.username; + + return Scaffold( + appBar: AppBar( + title: Text('Outbox'), + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(16.0), + child: Column( + children: [ + Divider(thickness: 1.0, color: Colors.grey[300]), + Column( + children: [ + TextField( + controller: _designationController, + decoration: InputDecoration( + labelText: 'View As', + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: () async { + await _submitForm(); + }, + child: Text('View'), + ), + ], + ), + ], + ), + if (errorMessage != null) + Text( + errorMessage!, + style: TextStyle(color: Colors.red), + ), + Column( + children: fileIDsList.map((fileID) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('File ID: $fileID'), + TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MessageDetailPage(messageDetails: jsonData[fileIDsList.indexOf(fileID)], + username: widget.username,), + ), + ); + }, + child: Text('View'), + ), + ], + ), + ); + }).toList(), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/FileTracking/fts/fts.dart b/lib/screens/FileTracking/fts/fts.dart new file mode 100644 index 00000000..8af5b2af --- /dev/null +++ b/lib/screens/FileTracking/fts/fts.dart @@ -0,0 +1,150 @@ +import 'package:flutter/material.dart'; +import 'package:fusion/Components/appBar.dart'; +import 'package:fusion/Components/side_drawer.dart'; +import 'package:fusion/screens/FileTracking/Create_file/create_file.dart'; +import 'package:fusion/screens/FileTracking/View_drafts/view_drafts.dart'; +import 'package:fusion/screens/FileTracking/Archive_File/archive_file.dart'; +import 'package:fusion/screens/FileTracking/View_inbox/view_inbox.dart'; +import 'package:fusion/screens/FileTracking/View_outbox/view_outbox.dart'; +import 'package:fusion/screens/FileTracking/Track_file/track_file.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class UserService { + Future getUsername() async { + final prefs = await SharedPreferences.getInstance(); + final username = prefs.getString('username'); + return username ?? ''; + } +} + +class RoundedListView extends StatelessWidget { + @override + Widget build(BuildContext context) { + Future getUsername() async { + final userService = UserService(); + final username = await userService.getUsername(); + return username; + } + + return Scaffold( + appBar: DefaultAppBar().buildAppBar(), + drawer: SideDrawer(), + body: Column( + children: [ + FutureBuilder( + future: getUsername(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return CircularProgressIndicator(); // Show loading indicator while fetching username + } else if (snapshot.hasError) { + return Text('Error: ${snapshot.error}'); + } else { + final username = snapshot.data.toString(); + return Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + + SizedBox(width: 16.0), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + username, // Display fetched username + style: TextStyle( + fontSize: 18.0, + color: Colors.grey, + fontWeight: FontWeight.bold, + decoration: TextDecoration.none, + ), + ), + ], + ), + ], + ), + ); + } + }, + ), + + // Divider + Divider(thickness: 1.0, color: Colors.grey[300]), + + // Rounded list view + ListView.builder( + shrinkWrap: true, // Prevent scrolling + itemCount: 5, + itemBuilder: (context, index) { + final items = ['Compose File', 'Drafts', 'Archive', 'Outbox', 'Inbox']; + final paths = [ + '/create_file', + '/view_drafts', + '/archive_file', + '/view_outbox', + '/view_inbox', + ]; + + return Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + decoration: BoxDecoration( + color: Color(0xffCC7231), // Color code for #CC7231 + borderRadius: BorderRadius.circular(10.0), + ), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(items[index]), + IconButton( + icon: Icon(Icons.chevron_right), + onPressed: () async { + final username = await getUsername(); + switch (paths[index]) { + case '/create_file': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => CreateFilePage(username: username)), + ); + break; + case '/view_drafts': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => DraftsPage(username: username)), + ); + break; + case '/view_inbox': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => InboxPage(username: username)), + ); + break; + case '/archive_file': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => ArchivePage(username: username,)), + ); + break; + case '/view_outbox': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => OutboxPage(username: username)), + ); + break; + } + }, + ), // Icon color white + ], + ), + ), + ), + ); + }, + ), + ], + ), + ); + } +} diff --git a/lib/screens/LoginandDashboard/DashboardComponents/announcement.dart b/lib/screens/LoginandDashboard/DashboardComponents/announcement.dart new file mode 100644 index 00000000..c0db276f --- /dev/null +++ b/lib/screens/LoginandDashboard/DashboardComponents/announcement.dart @@ -0,0 +1,200 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:fusion/models/profile.dart'; +import 'package:fusion/services/profile_service.dart'; +import 'package:flutter/material.dart'; +import 'package:fusion/Components/appBar2.dart'; +import 'package:fusion/Components/side_drawer2.dart'; +import 'package:fusion/models/dashboard.dart'; +import 'package:fusion/screens/LoginandDashboard/DashboardComponents/cardItems.dart'; +import 'package:fusion/services/dashboard_service.dart'; +import 'package:http/http.dart'; +import 'package:fusion/Components/bottom_navigation_bar.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; + +class Announcement extends StatefulWidget { + static String tag = 'home-page'; + @override + _AnnouncementState createState() => _AnnouncementState(); +} + +class _AnnouncementState extends State { + bool _notificationsBool = false; + bool _newsBool = false; + bool _announcementsBool = true; + bool _homeBool = false; + + bool _loading = true; + late String name; + late String studentType; + // Stream Controller for API + late StreamController _dashboardController; + late DashboardService dashboardService; + late DashboardData data; + late StreamController _profileController; + late ProfileService profileService; + late ProfileData data2; + var service = locator(); + late String curr_desig = service.getFromDisk("Current_designation"); + @override + void initState() { + super.initState(); + _dashboardController = StreamController(); + dashboardService = DashboardService(); + _profileController = StreamController(); + profileService = ProfileService(); + getData(); + } + + getData() async { + try { + print("gfsgsgd"); + Response response = await dashboardService.getDashboard(); + print("1"); + Response response2 = await profileService.getProfile(); + print("2"); + print(response); + print(response2); + + setState(() { + data = DashboardData.fromJson(jsonDecode(response.body)); + data2 = ProfileData.fromJson(jsonDecode(response2.body)); + _loading = false; + }); + name = data2.user!['first_name'] + ' ' + data2.user!['last_name']; + studentType = data2.profile!['department']!['name'] + + ' ' + + data2.profile!['user_type']; + } catch (e) { + print(e); + } + } + + loadData() async { + getData().then((res) { + _dashboardController.add(res); + _profileController.add(res); + }); + } + + final GlobalKey scaffoldKey = new GlobalKey(); + showSnack() { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: const Text('New Content Loaded'), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + curr_desig: curr_desig, + headerTitle: "Announcement", + + onDesignationChanged: (newValue) { + setState(() { + curr_desig = newValue; + }); + }, + ), + bottomNavigationBar: MyBottomNavigationBar(), + drawer: SideDrawer(curr_desig: curr_desig), + body: _loading == true + ? Center(child: CircularProgressIndicator()) + : StreamBuilder( + stream: _dashboardController.stream, + builder: (context, AsyncSnapshot snapshot) { + return Stack(children: [ + Positioned( + left: 0, + child: Column( + children: [ + Card( + elevation: 0, + margin: EdgeInsets.symmetric( + horizontal: 10.0, vertical: 10.0), + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.only( + top: 10.0, + bottom: 10.0, + left: 13.0, + right: 10.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + GestureDetector( + onTap: () { + _notificationsBool = true; + _announcementsBool = false; + _newsBool = false; + setState(() { + _notificationsBool = true; + _announcementsBool = false; + _newsBool = false; + }); + Navigator.pushReplacementNamed( + context, "/notification"); + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + // Only handle navigation when the icon is clicked + _notificationsBool = false; + _announcementsBool = true; + _newsBool = false; + setState(() { + _notificationsBool = false; + _announcementsBool = true; + _newsBool = false; + }); + Navigator.pushReplacementNamed( + context, "/landing"); + }, + child: Icon( + Icons.navigate_before_rounded, + color: Colors.black, + size: 25.0, + ), + ), + SizedBox(width: 20.0), + Text( + 'Announcement', + style: TextStyle( + fontSize: 22.0, + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(width: 170.0), + ], + ), + ), + ], + ), + ), + ), + + + ], + ), + ), + ]); + }, + ), + ); + } + + @override + void dispose() { + _dashboardController.close(); + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/screens/LoginandDashboard/DashboardComponents/cardItems.dart b/lib/screens/LoginandDashboard/DashboardComponents/cardItems.dart index fffcde98..63afcdd9 100644 --- a/lib/screens/LoginandDashboard/DashboardComponents/cardItems.dart +++ b/lib/screens/LoginandDashboard/DashboardComponents/cardItems.dart @@ -1,36 +1,34 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:fusion/models/notification.dart' as notif; import 'package:fusion/services/dashboard_service.dart'; class NotificationCard extends StatelessWidget { final List? notifications; - const NotificationCard({Key? key, required this.notifications}) - : super(key: key); + const NotificationCard({Key? key, required this.notifications}) : super(key: key); @override Widget build(BuildContext context) { return Card( elevation: 2.0, - margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0), + margin: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0), shadowColor: Colors.black, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: getCards(), + child: SingleChildScrollView( // Added to allow scrolling + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: getCards(), + ), ), ); } - getCards() { - List cards = []; - for (int i = 0; i < notifications!.length; i++) { - cards.add(InfoCard( - notification: notifications![i], - )); - } - return cards; + List getCards() { + // Transforming the notifications into InfoCard widgets + return notifications!.map((notif.Notification notification) { + return InfoCard( + notification: notification, + ); + }).toList(); } } @@ -38,19 +36,20 @@ class NewsCard extends StatelessWidget { @override Widget build(BuildContext context) { return Card( - elevation: 2.0, - margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0), + elevation: 0, + margin: EdgeInsets.symmetric( + horizontal: 10.0, vertical: 10.0), shadowColor: Colors.black, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Card( elevation: 3.0, - margin: EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0), + margin: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0), shadowColor: Colors.black, - child: Center( + child: const Center( child: Padding( - padding: const EdgeInsets.all(18.0), + padding: EdgeInsets.all(18.0), child: Text('Work in progress'), ), ), @@ -64,9 +63,10 @@ class NewsCard extends StatelessWidget { class InfoCard extends StatefulWidget { final notif.Notification notification; - InfoCard({ + const InfoCard({ + Key? key, required this.notification, - }); + }) : super(key: key); @override _InfoCardState createState() => _InfoCardState(); @@ -77,46 +77,38 @@ class _InfoCardState extends State { Widget build(BuildContext context) { return Card( elevation: 3.0, - margin: EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0), + margin: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0), shadowColor: Colors.black, child: Column( children: [ - SizedBox( - height: 10.0, - ), + const SizedBox(height: 10.0), Text( widget.notification.data!["module"], textAlign: TextAlign.left, - style: TextStyle( + style: const TextStyle( fontSize: 20.0, color: Colors.deepOrangeAccent, fontWeight: FontWeight.bold, ), ), - SizedBox( - height: 10.0, - ), + const SizedBox(height: 10.0), Padding( padding: const EdgeInsets.all(8.0), child: Text( widget.notification.verb!, - style: TextStyle(fontSize: 15.0, color: Colors.black), + style: const TextStyle(fontSize: 15.0, color: Colors.black), ), ), - SizedBox( - height: 10.0, - ), + const SizedBox(height: 10.0), ElevatedButton( - child: widget.notification.unread! - ? Text('Mark As Read') - : Text('Mark As Unread'), + child: Text(widget.notification.unread! ? 'Mark As Read' : 'Mark As Unread'), onPressed: () { // Respond to button press DashboardService service = DashboardService(); setState(() { try { service.markRead(widget.notification.id!.toString()); - }catch(e){ + } catch (e) { print(e); } }); @@ -124,10 +116,8 @@ class _InfoCardState extends State { style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith( (Set states) { - if (states.contains(MaterialState.pressed)) - return Colors.deepOrange; - return Colors - .deepOrangeAccent; // Use the component's default. + if (states.contains(MaterialState.pressed)) return Colors.deepOrange; + return Colors.deepOrangeAccent; // Default Color }, ), ), diff --git a/lib/screens/LoginandDashboard/DashboardComponents/news.dart b/lib/screens/LoginandDashboard/DashboardComponents/news.dart new file mode 100644 index 00000000..268ae36a --- /dev/null +++ b/lib/screens/LoginandDashboard/DashboardComponents/news.dart @@ -0,0 +1,201 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:fusion/models/profile.dart'; +import 'package:fusion/services/profile_service.dart'; +import 'package:flutter/material.dart'; +import 'package:fusion/Components/appBar2.dart'; +import 'package:fusion/Components/side_drawer2.dart'; +import 'package:fusion/models/dashboard.dart'; +import 'package:fusion/screens/LoginandDashboard/DashboardComponents/cardItems.dart'; +import 'package:fusion/services/dashboard_service.dart'; +import 'package:http/http.dart'; +import 'package:fusion/Components/bottom_navigation_bar.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; + +class News extends StatefulWidget { + static String tag = 'home-page'; + @override + _NewsState createState() => _NewsState(); +} + +class _NewsState extends State { + bool _notificationsBool = false; + bool _newsBool = true; + bool _announcementsBool = false; + bool _homeBool = false; + + bool _loading = true; + late String name; + late String studentType; + // Stream Controller for API + late StreamController _dashboardController; + late DashboardService dashboardService; + late DashboardData data; + late StreamController _profileController; + late ProfileService profileService; + late ProfileData data2; + var service = locator(); + late String curr_desig = service.getFromDisk("Current_designation"); + + @override + void initState() { + super.initState(); + _dashboardController = StreamController(); + dashboardService = DashboardService(); + _profileController = StreamController(); + profileService = ProfileService(); + getData(); + } + + getData() async { + try { + print("gfsgsgd"); + Response response = await dashboardService.getDashboard(); + print("1"); + Response response2 = await profileService.getProfile(); + print("2"); + print(response); + print(response2); + + setState(() { + data = DashboardData.fromJson(jsonDecode(response.body)); + data2 = ProfileData.fromJson(jsonDecode(response2.body)); + _loading = false; + }); + name = data2.user!['first_name'] + ' ' + data2.user!['last_name']; + studentType = data2.profile!['department']!['name'] + + ' ' + + data2.profile!['user_type']; + } catch (e) { + print(e); + } + } + + loadData() async { + getData().then((res) { + _dashboardController.add(res); + _profileController.add(res); + }); + } + + final GlobalKey scaffoldKey = new GlobalKey(); + showSnack() { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: const Text('New Content Loaded'), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + curr_desig: curr_desig, + headerTitle: "News", + + onDesignationChanged: (newValue) { + setState(() { + curr_desig = newValue; + }); + }, + ), + drawer: SideDrawer(curr_desig: curr_desig), + bottomNavigationBar: MyBottomNavigationBar(), + body: _loading == true + ? Center(child: CircularProgressIndicator()) + : StreamBuilder( + stream: _dashboardController.stream, + builder: (context, AsyncSnapshot snapshot) { + return Stack(children: [ + Positioned( + left: 0, + child: Column( + children: [ + Card( + elevation: 0, + margin: EdgeInsets.symmetric( + horizontal: 10.0, vertical: 10.0), + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.only( + top: 10.0, + bottom: 10.0, + left: 13.0, + right: 10.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + GestureDetector( + onTap: () { + _notificationsBool = true; + _announcementsBool = false; + _newsBool = false; + setState(() { + _notificationsBool = true; + _announcementsBool = false; + _newsBool = false; + }); + Navigator.pushReplacementNamed( + context, "/notification"); + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + onTap: () { + // Only handle navigation when the icon is clicked + _notificationsBool = true; + _announcementsBool = false; + _newsBool = false; + setState(() { + _notificationsBool = true; + _announcementsBool = false; + _newsBool = false; + }); + Navigator.pushReplacementNamed( + context, "/landing"); + }, + child: Icon( + Icons.navigate_before_rounded, + color: Colors.black, + size: 25.0, + ), + ), + SizedBox(width: 20.0), + Text( + 'News', + style: TextStyle( + fontSize: 22.0, + color: Colors.black, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(width: 170.0), + ], + ), + ), + ], + ), + ), + ), + + + ], + ), + ), + ]); + }, + ), + ); + } + + @override + void dispose() { + _dashboardController.close(); + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/screens/LoginandDashboard/DashboardComponents/notify.dart b/lib/screens/LoginandDashboard/DashboardComponents/notify.dart new file mode 100644 index 00000000..198820b5 --- /dev/null +++ b/lib/screens/LoginandDashboard/DashboardComponents/notify.dart @@ -0,0 +1,152 @@ +import 'dart:async'; +import 'dart:convert'; +import 'package:fusion/models/profile.dart'; +import 'package:fusion/services/profile_service.dart'; +import 'package:flutter/material.dart'; +import 'package:fusion/Components/appBar2.dart'; +import 'package:fusion/Components/side_drawer2.dart'; +import 'package:fusion/models/dashboard.dart'; +import 'package:fusion/screens/LoginandDashboard/DashboardComponents/cardItems.dart'; +import 'package:fusion/services/dashboard_service.dart'; +import 'package:http/http.dart'; +import 'package:fusion/Components/bottom_navigation_bar.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; +import 'package:fusion/models/notification.dart' as notif; +import 'package:intl/intl.dart'; + +class Notify extends StatefulWidget { + static String tag = 'home-page'; + @override + _NotifyState createState() => _NotifyState(); +} + +class _NotifyState extends State { + List _notifications = []; + bool _loading = true; + late String name; + late String studentType; + + // Stream Controller for API + late StreamController _dashboardController; + late DashboardService dashboardService; + late DashboardData data; + late StreamController _profileController; + late ProfileService profileService; + late ProfileData data2; + var service = locator(); + late String curr_desig = service.getFromDisk("Current_designation"); + @override + void initState() { + super.initState(); + _dashboardController = StreamController(); + dashboardService = DashboardService(); + _profileController = StreamController(); + profileService = ProfileService(); + getData(); + } + + getData() async { + try { + print("gfsgsgd"); + Response response = await dashboardService.getNotification(); + print("1"); + Response response2 = await profileService.getProfile(); + print("2"); + print(response); + print(response2); + + setState(() { + data = DashboardData.fromJson(jsonDecode(response.body)); + _notifications = notif.Notification.fromListJson(jsonDecode(response.body)['notifications']); + data2 = ProfileData.fromJson(jsonDecode(response2.body)); + _loading = false; + }); + name = data2.user!['first_name'] + ' ' + data2.user!['last_name']; + studentType = data2.profile!['department']!['name'] + + ' ' + + data2.profile!['user_type']; + } catch (e) { + print(e); + } + } + + loadData() async { + getData().then((res) { + _dashboardController.add(res); + _profileController.add(res); + }); + } + + final GlobalKey scaffoldKey = new GlobalKey(); + showSnack() { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: const Text('New Content Loaded'), + ), + ); + } + + @override +Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + curr_desig: curr_desig, + headerTitle: "Notifications", + + onDesignationChanged: (newValue) { + setState(() { + curr_desig = newValue; + }); + }, + ), // This is default app bar used in all modules + drawer: SideDrawer(curr_desig: curr_desig), + bottomNavigationBar: + MyBottomNavigationBar(), // This is sideDrawer used in all modules + body: _loading + ? Center(child: CircularProgressIndicator()) + : ListView.builder( + itemCount: _notifications.length, + padding: EdgeInsets.all(8.0), // Add padding around the list for better spacing + itemBuilder: (context, index) { + final notification = _notifications[index]; + final formattedDate = notification.timestamp != null + ? DateFormat('yyyy-MM-dd – kk:mm').format(notification.timestamp!) + : "No Date"; + + return Card( + elevation: 4.0, // Adjust the shadow's elevation + margin: EdgeInsets.symmetric(vertical: 4.0, horizontal: 0), // Spacing between cards + child: ListTile( + leading: Icon(notification.unread ?? false ? Icons.notifications_active : Icons.notifications_off), + title: Text(notification.verb ?? "No Title"), + subtitle: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + notification.description ?? "No Description", + overflow: TextOverflow.ellipsis, + ), + ), + Text(formattedDate, textAlign: TextAlign.right), + ], + ), + onTap: () { + // Handle tap + }, + ), + ); + }, + ), + + + ); + } + + @override + void dispose() { + _dashboardController.close(); + super.dispose(); + } +} \ No newline at end of file diff --git a/lib/screens/LoginandDashboard/dashboard.dart b/lib/screens/LoginandDashboard/dashboard.dart index 80c2719f..24f372d5 100644 --- a/lib/screens/LoginandDashboard/dashboard.dart +++ b/lib/screens/LoginandDashboard/dashboard.dart @@ -2,15 +2,17 @@ import 'dart:async'; import 'dart:convert'; import 'package:fusion/models/profile.dart'; import 'package:fusion/services/profile_service.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:fusion/Components/appBar.dart'; -import 'package:fusion/Components/side_drawer.dart'; +import 'package:fusion/Components/appBar2.dart'; +import 'package:fusion/Components/side_drawer2.dart'; import 'package:fusion/models/dashboard.dart'; import 'package:fusion/screens/LoginandDashboard/DashboardComponents/cardItems.dart'; import 'package:fusion/services/dashboard_service.dart'; import 'package:http/http.dart'; +import 'package:fusion/services/appBar_services.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; +import 'package:fusion/Components/bottom_navigation_bar.dart'; class Dashboard extends StatefulWidget { static String tag = 'home-page'; @@ -20,9 +22,11 @@ class Dashboard extends StatefulWidget { } class _DashboardState extends State { - bool _notificationsBool = true; + bool _notificationsBool = false; bool _newsBool = false; bool _announcementsBool = false; + bool _homeBool = true; + bool _loading = true; late String name; late String studentType; @@ -33,6 +37,12 @@ class _DashboardState extends State { late StreamController _profileController; late ProfileService profileService; late ProfileData data2; + late List designationsArray; + var service = locator(); + late String curr_desig = service.getFromDisk("Current_designation"); + bool isStudent = false; + + final appBarServices _appBarServices = appBarServices(); @override void initState() { super.initState(); @@ -45,17 +55,28 @@ class _DashboardState extends State { getData() async { try { + print("gfsgsgd"); Response response = await dashboardService.getDashboard(); + print("1"); Response response2 = await profileService.getProfile(); + print("2"); + print(response); + print(response2); + setState(() { data = DashboardData.fromJson(jsonDecode(response.body)); data2 = ProfileData.fromJson(jsonDecode(response2.body)); _loading = false; }); + print(data2.user!); + print( + '-----------------------------------=---------------------------------------'); name = data2.user!['first_name'] + ' ' + data2.user!['last_name']; - studentType = data2.profile!['department']!['name'] + - ' ' + - data2.profile!['user_type']; + studentType = data2.profile!['department']!['name']; + + if (data2.profile!['user_type'] == 'student') { + isStudent = true; + } } catch (e) { print(e); } @@ -68,6 +89,15 @@ class _DashboardState extends State { }); } + fetchDesignations() async { + try { + designationsArray = await _appBarServices.getDesignations(); + } catch (e) { + print("Error fetching designations: $e"); + return null; + } + } + final GlobalKey scaffoldKey = new GlobalKey(); showSnack() { ScaffoldMessenger.of(context).showSnackBar( @@ -80,174 +110,221 @@ class _DashboardState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: DefaultAppBar() - .buildAppBar(), // This is default app bar used in all modules - drawer: SideDrawer(), // This is sideDrawer used in all modules - body: _loading == true - ? Center(child: CircularProgressIndicator()) - : StreamBuilder( - stream: _dashboardController.stream, - builder: (context, AsyncSnapshot snapshot) { - return ListView( - shrinkWrap: true, - physics: ClampingScrollPhysics(), - children: [ - Card( - elevation: 2.0, - margin: EdgeInsets.symmetric( - horizontal: 50.0, vertical: 20.0), - shadowColor: Colors.black, - child: Column( + appBar: CustomAppBar( + curr_desig: curr_desig, + headerTitle: "Dashboard", + onDesignationChanged: (newValue) { + setState(() { + curr_desig = newValue; + }); + }, + ), // This is default app bar used in all modules + drawer: SideDrawer(curr_desig: curr_desig), + bottomNavigationBar: + MyBottomNavigationBar(), // This is sideDrawer used in all modules + body: Column( + children: [ + Expanded( + child: _loading == true + ? Center(child: CircularProgressIndicator()) + : StreamBuilder( + stream: _dashboardController.stream, + builder: (context, AsyncSnapshot snapshot) { + return ListView( + shrinkWrap: true, + physics: ClampingScrollPhysics(), children: [ - Container( - margin: EdgeInsets.only(top: 20.0), - width: 170.0, - height: 170.0, - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/profile_pic.png'), - fit: BoxFit.cover, - ), - ), - ), - SizedBox( - height: 10.0, - ), - Text( - name, //Display name of User - style: - TextStyle(fontSize: 20.0, color: Colors.black), - ), - SizedBox( - height: 10.0, - ), - Text( - studentType, // Display Type of User - style: - TextStyle(fontSize: 15.0, color: Colors.black), - ), - SizedBox( - height: 10.0, - ), - ], - ), - ), - Card( - color: Colors.black, - child: Padding( - padding: const EdgeInsets.all(10.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - GestureDetector( - onTap: () { - _notificationsBool = true; - _announcementsBool = false; - _newsBool = false; - setState(() { - _notificationsBool = true; - _announcementsBool = false; - _newsBool = false; - }); - }, - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Notifications', - style: TextStyle( - fontSize: 16.0, - color: Colors.white, + Card( + elevation: 2.0, + margin: EdgeInsets.symmetric( + horizontal: 20.0, vertical: 30.0), + // shadowColor: Colors.black, + color: Colors.white, + + child: Column( + children: [ + Container( + margin: EdgeInsets.only(top: 20.0), + width: 170.0, + height: 190.0, + decoration: BoxDecoration( + image: DecorationImage( + image: + AssetImage('assets/profile_pic.png'), + fit: BoxFit.cover, ), ), - Icon( - Icons.notifications_active_rounded, - color: _notificationsBool - ? Colors.deepOrangeAccent - : Colors.white, - ), - ], - ), + ), + SizedBox( + height: 10.0, + ), + Text( + name, //Display name of User + style: TextStyle( + fontSize: 20.0, + color: Colors.black, + fontWeight: FontWeight.bold), + ), + SizedBox( + height: 10.0, + ), + Text( + studentType + + " " + + curr_desig, // Display Type of User + style: TextStyle( + fontSize: 17.0, + color: Colors.black, + fontWeight: FontWeight.bold), + ), + SizedBox( + height: 10.0, + ), + ], ), - GestureDetector( - onTap: () { - _newsBool = true; - _announcementsBool = false; - _notificationsBool = false; - setState(() { - _newsBool = true; - _announcementsBool = false; - _notificationsBool = false; - }); - }, + ), + + Card( + margin: EdgeInsets.symmetric( + horizontal: 20.0, vertical: 10.0), + color: Colors.deepOrangeAccent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 50.0), // Set the border radius here + ), + child: Padding( + padding: const EdgeInsets.only( + top: 10.0, + bottom: 10.0, + left: 13.0, + right: 10.0), child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment + .spaceEvenly, // Align the children along the main axis with space between them + crossAxisAlignment: CrossAxisAlignment + .center, // Align the children along the cross axis (vertically by default) + // mainAxisSize: MainAxisSize.max, children: [ - Text( - 'News', - style: TextStyle( - fontSize: 16.0, - color: Colors.white, + GestureDetector( + onTap: () { + _notificationsBool = true; + _announcementsBool = false; + _newsBool = false; + setState(() { + _notificationsBool = true; + _announcementsBool = false; + _newsBool = false; + }); + Navigator.pushReplacementNamed( + context, "/profile"); + }, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceEvenly, + children: [ + Icon( + Icons.account_circle, + color: Colors.white, + size: 30.0, + ), + SizedBox(width: 40.0), + Text( + 'Professsional Profile', + style: TextStyle( + fontSize: 20.0, + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(width: 40.0), + Icon( + Icons.arrow_forward_ios_rounded, + color: Colors.white, + ), + ], ), ), - Icon( - Icons.email, - color: _newsBool - ? Colors.deepOrangeAccent - : Colors.white, - ), ], ), ), - GestureDetector( - onTap: () { - _announcementsBool = true; - _newsBool = false; - _notificationsBool = false; - setState(() { - _announcementsBool = true; - _newsBool = false; - _notificationsBool = false; - }); - }, + ), + + if (!isStudent) + Card( + margin: EdgeInsets.symmetric( + horizontal: 20.0, vertical: 10.0), + color: Colors.deepOrangeAccent, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 50.0), // Set the border radius here + ), child: Padding( - padding: const EdgeInsets.only(right: 16.0), + padding: const EdgeInsets.only( + top: 10.0, + bottom: 10.0, + left: 13.0, + right: 10.0), child: Row( mainAxisAlignment: - MainAxisAlignment.spaceBetween, + MainAxisAlignment.spaceEvenly, children: [ - Text( - 'Announcements', - style: TextStyle( - fontSize: 16.0, - color: Colors.white, + GestureDetector( + onTap: () { + _notificationsBool = true; + _announcementsBool = false; + _newsBool = false; + setState(() { + _notificationsBool = true; + _announcementsBool = false; + _newsBool = false; + }); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment + .spaceEvenly, // Align the children along the main axis with space between them + crossAxisAlignment: CrossAxisAlignment + .center, // Align the children along the cross axis (vertically by default) + mainAxisSize: MainAxisSize.max, + children: [ + Icon( + Icons.notifications_active_rounded, + color: Colors.white, + ), + SizedBox(width: 40.0), + Text( + 'Admistrative Profile', + style: TextStyle( + fontSize: 20.0, + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(width: 40.0), + Icon( + Icons.arrow_forward_ios_rounded, + color: Colors.white, + ), + ], ), ), - Icon( - Icons.announcement, - color: _announcementsBool - ? Colors.deepOrangeAccent - : Colors.white, - ), ], ), ), ), - ], - ), - ), - ), - _notificationsBool - ? NotificationCard( - notifications: data.notifications, - ) - : NewsCard(), - ], - ); - }, - ), + + // _notificationsBool + // ? NotificationCard( + // notifications: data.notifications, + // ) + // : NewsCard(), + ], + ); + }, + ), + ), + // Place the BottomNavigationBar here + ], + ), ); } @@ -256,4 +333,4 @@ class _DashboardState extends State { _dashboardController.close(); super.dispose(); } -} +} \ No newline at end of file diff --git a/lib/screens/LoginandDashboard/login_page.dart b/lib/screens/LoginandDashboard/login_page.dart index ca09fee2..d60e7b3e 100644 --- a/lib/screens/LoginandDashboard/login_page.dart +++ b/lib/screens/LoginandDashboard/login_page.dart @@ -1,8 +1,8 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:fusion/constants.dart'; import 'package:fusion/services/login_service.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class LoginPage extends StatefulWidget { static String tag = 'login-page'; @@ -10,7 +10,9 @@ class LoginPage extends StatefulWidget { _LoginPageState createState() => _LoginPageState(); } -bool checkBoxValue = false; +bool _focused = false; +bool _focused2 = false; +double bottom =10; class _LoginPageState extends State { final GlobalKey _formKey = GlobalKey(); @@ -23,21 +25,45 @@ class _LoginPageState extends State { final Widget logoWidget = CircleAvatar( backgroundColor: Colors.transparent, - radius: 54.0, - child: Image.asset('assets/logo.jpg'), + radius: (_focused || _focused2) ? 110.0 : 150.0, + child:Container( + + child: Image.asset('assets/logo.jpg'), + ), ); - final Widget emailFormField = TextFormField( + final Widget emailFormField = Focus( + onFocusChange: (focus) { + setState(() { + _focused = focus; + if (focus==true){ + bottom=400; + } + + }); + }, + child:TextFormField( keyboardType: TextInputType.emailAddress, autofocus: false, decoration: InputDecoration( label: Text('Username', style: TextStyle( - fontSize: 12.0, + fontSize: 18.0, ),), contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0), - border: OutlineInputBorder( - // borderRadius: BorderRadius.circular(32.0), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(width: 0, color: Color(0xFFf4f4f4)), + borderRadius: BorderRadius.circular(20.0), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(width: 0, color: Color(0xFFf4f4f4)), + borderRadius: BorderRadius.circular(20.0), ), + // enabledBorder: InputBorder.none, + fillColor: Color(0xFFf4f4f4), // Green color + filled: true, + floatingLabelBehavior: FloatingLabelBehavior.never, ), + cursorColor: Colors.black, + onChanged: (input) { username = input; }, @@ -48,23 +74,45 @@ class _LoginPageState extends State { else if (value?.contains('@') == true) { return 'Please enter username only'; } + return null; }, autofillHints: [AutofillHints.username], - ); + )); + + final Widget passwordFormField = Focus( + onFocusChange: (focus) { + setState(() { + _focused2 = focus; + if (focus==true){ + bottom=400; + } - final Widget passwordFormField = TextFormField( + }); + }, + child: TextFormField( autofocus: false, obscureText: true, decoration: InputDecoration( label: Text('Password', style: TextStyle( - fontSize: 12.0, + fontSize: 18.0, ),), contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0), - border: OutlineInputBorder( - // borderRadius: BorderRadius.circular(32.0), + + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(width: 0, color: Color(0xFFf4f4f4)), + borderRadius: BorderRadius.circular(20.0), ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(width: 0, color: Color(0xFFf4f4f4)), + borderRadius: BorderRadius.circular(20.0), + ), + // enabledBorder: InputBorder.none, + fillColor: Color(0xFFf4f4f4), // Green color + filled: true, + floatingLabelBehavior: FloatingLabelBehavior.never, ), + cursorColor: Colors.black, onChanged: (input) { pass = input; }, @@ -74,12 +122,13 @@ class _LoginPageState extends State { } else if (value.length < 6) { return 'Password must be at least 6 characters'; } + return null; }, autofillHints: [AutofillHints.password], - ); + )); final loginButton = Padding( - padding: EdgeInsets.symmetric(vertical: 16.0), + padding: EdgeInsets.only(top: 16.0), child: ElevatedButton( style: ButtonStyle( backgroundColor: MaterialStateProperty.all(kPrimaryColor), @@ -87,10 +136,13 @@ class _LoginPageState extends State { onPressed: () async { if (!(_formKey.currentState?.validate() ?? false)) { } else { + LoginService auth = LoginService(); bool complete = await auth.login(username ?? "", pass ?? ""); TextInput.finishAutofillContext(); if (complete == true) { + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString('username', username!); Navigator.pushReplacementNamed(context, "/landing"); } Navigator.pushReplacementNamed(context, "/landing"); @@ -102,7 +154,8 @@ class _LoginPageState extends State { style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, - fontSize: 14.0, + fontSize: 22.0, + ), ), ), @@ -124,13 +177,16 @@ class _LoginPageState extends State { key: _formKey, child: ListView( shrinkWrap: true, - padding: EdgeInsets.only(left: 24.0, right: 24.0), + padding: EdgeInsets.only(top:8.0,left: 24.0, right: 24.0), children: [ - logoWidget, + Padding( + padding: const EdgeInsets.only(top: 0.0), // Change the top padding here + child: logoWidget, + ), Padding( padding: const EdgeInsets.only(bottom: 30.0), child: Text( - 'Fusion Login', + 'Fusion', style: TextStyle( color: kPrimaryColor, fontWeight: FontWeight.bold, @@ -141,14 +197,21 @@ class _LoginPageState extends State { ), Padding( padding: EdgeInsets.only(bottom: 15), - child: emailFormField, + child: emailFormField, ), Padding( padding: EdgeInsets.only(bottom: 15), child: passwordFormField, ), - loginButton, - forgotLabel, + Padding( + padding: EdgeInsets.only(bottom: 0.0), + child: loginButton, + ), + Padding( + padding: EdgeInsets.only(top:0.0,bottom: bottom), + child: forgotLabel, + ), + ], ), ), @@ -157,29 +220,29 @@ class _LoginPageState extends State { ); } - // void _showDialog() { - // // flutter defined function - // showDialog( - // context: context, - // builder: (BuildContext context) { - // // return object of type Dialog - // return AlertDialog( - // title: Text("Invalid Username/Password"), - // content: Text("Please enter correct Username or Password"), - // actions: [ - // // usually buttons at the bottom of the dialog - // new TextButton( - // child: new Text( - // "Close", - // style: TextStyle(color: Colors.deepOrangeAccent), - // ), - // onPressed: () { - // Navigator.of(context).pop(); - // }, - // ), - // ], - // ); - // }, - // ); - // } -} +// void _showDialog() { +// // flutter defined function +// showDialog( +// context: context, +// builder: (BuildContext context) { +// // return object of type Dialog +// return AlertDialog( +// title: Text("Invalid Username/Password"), +// content: Text("Please enter correct Username or Password"), +// actions: [ +// // usually buttons at the bottom of the dialog +// new TextButton( +// child: new Text( +// "Close", +// style: TextStyle(color: Colors.deepOrangeAccent), +// ), +// onPressed: () { +// Navigator.of(context).pop(); +// }, +// ), +// ], +// ); +// }, +// ); +// } +} \ No newline at end of file diff --git a/lib/services/appBar_services.dart b/lib/services/appBar_services.dart new file mode 100644 index 00000000..bc7ba3c0 --- /dev/null +++ b/lib/services/appBar_services.dart @@ -0,0 +1,14 @@ +import 'package:fusion/services/storage_service.dart'; + +class appBarServices { + getDesignations() async { + try { + var storageService = await StorageService.getInstance(); + List? designations = storageService!.getFromDisk('designations'); + + return designations; + } catch (e) { + rethrow; + } + } +} \ No newline at end of file diff --git a/lib/services/dashboard_service.dart b/lib/services/dashboard_service.dart index 8e4520a9..ef348125 100644 --- a/lib/services/dashboard_service.dart +++ b/lib/services/dashboard_service.dart @@ -7,12 +7,12 @@ import 'package:http/http.dart' as http; class DashboardService { getDashboard() async { try { - var storage_service = locator(); - if (storage_service.userInDB?.token == null) + var storageService = locator(); + if (storageService.userInDB?.token == null) throw Exception('Token Error'); Map headers = { - 'Authorization': 'Token ' + (storage_service.userInDB?.token ?? "") + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? "") }; var client = http.Client(); http.Response response = await client.get( @@ -24,6 +24,43 @@ class DashboardService { ); if (response.statusCode == 200) { print("success"); + print(response); + return response; + } + throw Exception('Can\'t load'); + } catch (e) { + rethrow; + } + } + + + getNotification() async { + try { + print("gett"); + var storageService = locator(); + if (storageService.userInDB?.token == null) + throw Exception('Token Error'); + + + Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? "") + }; + print("gett2"); + + var client = http.Client(); + http.Response response = await client.get( + Uri.http( + getLink(), + kNotification, // constant dashboard path + ), + headers: headers, + ); + + print("gett3"); + + if (response.statusCode == 200) { + print("success"); + print(response); return response; } throw Exception('Can\'t load'); @@ -34,12 +71,12 @@ class DashboardService { markRead(String id) async { try { - StorageService? storage_service = await StorageService.getInstance(); + StorageService? storageService = await StorageService.getInstance(); - if (storage_service?.userInDB?.token == null) + if (storageService?.userInDB?.token == null) throw Exception('Token Error'); - String token = storage_service?.userInDB?.token ?? ""; + String token = storageService?.userInDB?.token ?? ""; Map headers = {'Authorization': 'Token ' + token}; Map body = {"id": id}; var client = http.Client(); @@ -60,4 +97,4 @@ class DashboardService { rethrow; } } -} +} \ No newline at end of file diff --git a/lib/services/filetracking_service.dart b/lib/services/filetracking_service.dart new file mode 100644 index 00000000..d7440a39 --- /dev/null +++ b/lib/services/filetracking_service.dart @@ -0,0 +1,32 @@ +import 'package:http/http.dart' as http; +import 'dart:convert'; + +Future createFile( + String title, + String description, + { + String? remarks, + String? forwardTo, + String? designation, + String? token, // Optional authorization token (if applicable) + }) async { + final url = Uri.parse('http://localhost:8000/filetrcking/api/file'); + final body = jsonEncode({ + 'title': title, + 'description': description, + 'remarks': remarks, + 'forwardTo': forwardTo, + 'designation': designation, + }); + + final headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + + if (token != null) { + headers['Authorization'] = '3c94443764b7b0f2b4aec6d5c143378ac1457e57'; // Replace `Bearer $token` with your actual authentication scheme + } + + final response = await http.post(url, headers: headers, body: body); + return response; +} diff --git a/lib/services/login_service.dart b/lib/services/login_service.dart index 17d79b42..97277ffc 100644 --- a/lib/services/login_service.dart +++ b/lib/services/login_service.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:fusion/constants.dart'; import 'package:fusion/api.dart'; import 'package:fusion/models/user.dart'; import 'package:fusion/services/storage_service.dart'; @@ -25,8 +24,15 @@ class LoginService { var prefs = await StorageService.getInstance(); print("response.body: ${response.body}"); - var storage_service = await StorageService.getInstance(); - storage_service!.saveUserInDB(User((jsonDecode(response.body))["token"])); + var storageService = await StorageService.getInstance(); + storageService!.saveUserInDB(User((jsonDecode(response.body))["token"])); + storageService.saveToDisk>( + 'designations', + (jsonDecode(response.body)["designations"] as List) + .map((dynamic item) => item.toString()) + .toList(), + ); + storageService.saveStringToDisk("Current_designation",jsonDecode(response.body)["designations"][0]); return true; } catch (e) { rethrow; @@ -35,8 +41,8 @@ class LoginService { void logout() async { try { - var storage_service = await StorageService.getInstance(); - storage_service!.deleteKey("user"); + var storageService = await StorageService.getInstance(); + storageService!.deleteKey("user"); } catch (e) { rethrow; } diff --git a/lib/services/storage_service.dart b/lib/services/storage_service.dart index 94d39ea4..9d118326 100644 --- a/lib/services/storage_service.dart +++ b/lib/services/storage_service.dart @@ -13,19 +13,19 @@ class StorageService with ChangeNotifier { static const String ProfileKey = "ProfileKey"; User? get userInDB { - var userJson = _getFromDisk(UserKey); + var userJson = getFromDisk(UserKey); return userJson == null ? null : User.fromJson(jsonDecode(userJson)); } ProfileData get profileData { - var profileJson = _getFromDisk(ProfileKey); + var profileJson = getFromDisk(ProfileKey); // print(jsonDecode(profileJson)); return ProfileData.fromJson(jsonDecode(profileJson)); } AcademicData get academicData { - var profileJson = _getFromDisk(ProfileKey); + var profileJson = getFromDisk(ProfileKey); // print(jsonDecode(profileJson)); return AcademicData.fromJson(jsonDecode(profileJson)); } @@ -49,9 +49,9 @@ class StorageService with ChangeNotifier { return _instance; } - dynamic _getFromDisk(String key) { + dynamic getFromDisk(String key) { var value = _sharedPreferences?.get(key); - // print('(TRACE) LocalStorageService:_getFromDisk. key: $key value: $value'); + // print('(TRACE) LocalStorageService:getFromDisk. key: $key value: $value'); return value; } @@ -62,7 +62,7 @@ class StorageService with ChangeNotifier { void deleteKey(String key) { print( - '(TRACE) StorageService: deleteKey. key: $key value: ${_getFromDisk(key)}'); + '(TRACE) StorageService: deleteKey. key: $key value: ${getFromDisk(key)}'); _sharedPreferences!.remove(key); } @@ -84,4 +84,4 @@ class StorageService with ChangeNotifier { _sharedPreferences!.setStringList(key, content); } } -} +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 49030f0b..acafa3e8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -21,6 +21,7 @@ environment: sdk: '>=2.12.0 <3.0.0' dependencies: + flutter: sdk: flutter cupertino_icons: ^1.0.0 @@ -33,12 +34,14 @@ dependencies: date_field: ^2.0.0 datetime_picker_formfield: ^2.0.0 get_it: ^6.1.0 - url_launcher: ^6.0.18 + url_launcher: ^6.0.6 syncfusion_flutter_pdfviewer: ^19.4.47-beta pdf: ^3.7.1 path_provider: ^2.0.8 open_file: ^3.2.1 flutter_html: ^2.2.1 + file_picker: ^5.2.10 + # syncfusion_flutte8u8ur_datagrid: ^24.2.6 diff --git a/scripts/clone.sh b/scripts/clone.sh new file mode 100644 index 00000000..20bf0276 --- /dev/null +++ b/scripts/clone.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +branches=("ac" "ac-1" "ac-2" "ac-3" "ac-4" "ac-5" "ps-1" "ps-2" "gad-1" "gad-2" "gad-3" "gad-4" "gad-5" "hr" "sa-1" "sa-2" "sa-3" "sa-4" "os-1" "os-2" "os-3" "os-4" "rspc") + +for branch in "${branches[@]}" +do + echo "Creating $branch..." + + git checkout -b "$branch" + git push origin "$branch" + + echo "Pushed new branch $branch to remote" +done \ No newline at end of file diff --git a/test/widget_test.dart b/test/widget_test.dart index 0e355aad..a7a68bc1 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -6,7 +6,7 @@ // tree, read text, and verify that the values of widget properties are correct. import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_test/flutterz_test.dart'; import 'package:fusion/main.dart';