From 1505674033beac7ab77dc5a54cdda87def08d9ea Mon Sep 17 00:00:00 2001 From: Arindam Upadhyay <20bec018@iiitdmj.ac.in> Date: Mon, 27 Mar 2023 13:11:13 +0530 Subject: [PATCH 01/10] script structured --- clone.sh | 13 ------------- scripts/clone.sh | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) delete mode 100644 clone.sh create mode 100644 scripts/clone.sh 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/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 From aa2e5791c9de58bf6f19f7220e74d8c2306c14d5 Mon Sep 17 00:00:00 2001 From: AgPriyanshu18 Date: Fri, 17 Nov 2023 17:02:40 +0530 Subject: [PATCH 02/10] Server Url updated --- android/build.gradle | 4 ++-- android/gradle/wrapper/gradle-wrapper.properties | 2 +- lib/api.dart | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index b3a325d5..a78be8cb 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.4.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } 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/lib/api.dart b/lib/api.dart index 802bee2d..73d1c6bc 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 kserverLink = "172.27.16.214:8000"; //Login Service -String kAuthUrl = "172.27.16.215:80"; +String kAuthUrl = "172.27.16.214:8000"; String kAuthLogin = "/api/auth/login/"; +// String kAuthLogin = "/accounts/login"; //Profile Service String kProfile = "/api/profile/"; From 86cdbe512f3c5e739ae907f095df635f58339346 Mon Sep 17 00:00:00 2001 From: aish0749 Date: Mon, 19 Feb 2024 15:00:16 +0530 Subject: [PATCH 03/10] add file tracking module --- android/build.gradle | 4 +- flutter | 1 + lib/Components/side_drawer.dart | 3 +- lib/main.dart | 12 ++ .../Registration/registration_home_page.dart | 1 + .../FileTracking/Create_file/create_file.dart | 184 ++++++++++++++++++ .../FileTracking/Track_file/track_file.dart | 79 ++++++++ .../FileTracking/View_drafts/view_drafts.dart | 83 ++++++++ .../FileTracking/View_inbox/view_inbox.dart | 88 +++++++++ .../FileTracking/View_outbox/view_outbox.dart | 137 +++++++++++++ lib/screens/FileTracking/fts/fts.dart | 124 ++++++++++++ pubspec.yaml | 3 + test/widget_test.dart | 2 +- 13 files changed, 717 insertions(+), 4 deletions(-) create mode 160000 flutter create mode 100644 lib/screens/FileTracking/Create_file/create_file.dart create mode 100644 lib/screens/FileTracking/Track_file/track_file.dart create mode 100644 lib/screens/FileTracking/View_drafts/view_drafts.dart create mode 100644 lib/screens/FileTracking/View_inbox/view_inbox.dart create mode 100644 lib/screens/FileTracking/View_outbox/view_outbox.dart create mode 100644 lib/screens/FileTracking/fts/fts.dart diff --git a/android/build.gradle b/android/build.gradle index a78be8cb..1cc04d89 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.4.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/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/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/main.dart b/lib/main.dart index 9ca81ff8..37adc309 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -42,6 +42,12 @@ 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'; void main() { WidgetsFlutterBinding.ensureInitialized(); @@ -127,6 +133,12 @@ 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(), // Replace with your Track File page + '/fts/view_drafts': (context) => DraftsPage(), // Replace with your Drafts page + '/fts/view_inbox': (context) => InboxPage(), // Replace with your Inbox page + '/fts/view_outbox': (context) => OutboxPage(), // Replace with your Outbox page + '/fts/tack_file': (context) => FileTrackingPage(), // Replace with your Outbox page }, ), ); 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/Create_file/create_file.dart b/lib/screens/FileTracking/Create_file/create_file.dart new file mode 100644 index 00000000..0fb0e542 --- /dev/null +++ b/lib/screens/FileTracking/Create_file/create_file.dart @@ -0,0 +1,184 @@ +import 'package:flutter/material.dart'; + +class CreateFilePage extends StatefulWidget { + @override + _CreateFilePageState createState() => _CreateFilePageState(); +} + +class _CreateFilePageState extends State { + // Form controllers + final _titleController = TextEditingController(); + final _descriptionController = TextEditingController(); + final _attachController = TextEditingController(); + final _remarksController = TextEditingController(); + final _forwardToController = TextEditingController(); + final _designationController = TextEditingController(); + + // Submit form with placeholder logic + Future _submitForm() async { + // Handle form data validation and submission (replace with your logic) + if (_titleController.text.isEmpty || _descriptionController.text.isEmpty) { + print('Error: Title and Description are required.'); + return; + } + + print('Form data:'); + print('Title: ${_titleController.text}'); + print('Description: ${_descriptionController.text}'); + print('Remarks: ${_remarksController.text}'); + print('Forward To: ${_forwardToController.text}'); + print('Designation: ${_designationController.text}'); + + // Clear form fields after submission + _titleController.clear(); + _descriptionController.clear(); + _remarksController.clear(); + _forwardToController.clear(); + _designationController.clear(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Create File'), + ), + body: SingleChildScrollView( + padding: EdgeInsets.all(16.0), + child: Column( + children: [ + // User profile view (adapt from your previous code) + Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // TODO: Add your user profile image and details + CircleAvatar( + radius: 30.0, + ), + SizedBox(width: 16.0), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // TODO: Replace with user name and designation + Text( + "Rohit Sharma", + style: TextStyle( + fontSize: 18.0, + color: Colors.grey, + fontWeight: FontWeight.bold, + ), + ), + Text( + "21BCS329", + style: TextStyle( + fontSize: 18.0, + color: Colors.grey, + fontWeight: FontWeight.bold, + ), + ), + Text( + "Designation", + style: TextStyle( + fontSize: 14.0, + color: Colors.grey, + ), + ), + ], + ), + ], + ), + ), + + // Divider + Divider(thickness: 1.0, color: Colors.grey[300]), + + // Form content + Column( + children: [ + // Title field + TextField( + controller: _titleController, + decoration: InputDecoration( + labelText: 'Title', + // errorText: _titleController.text.isEmpty ? 'Required' : null, + ), + ), + + // Description field + TextField( + controller: _descriptionController, + decoration: InputDecoration( + labelText: 'Description', + // errorText: _descriptionController.text.isEmpty ? 'Required' : null, + ), + ), + + // Attach field + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + // Text 'Attach File' + Text('Attach File'), + SizedBox(width: 16.0), // Add spacing between text and button + + // ElevatedButton with 'Upload' text + ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('File Attachment'), + content: Text('File attachment is not currently supported in this web app.'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text('OK'), + ), + ], + ), + ); + }, + child: Text('Upload'), + ), + ], + ), + + // Remarks field + TextField( + controller: _remarksController, + decoration: InputDecoration( + labelText: 'Remarks', + ), + ), + + // Forward to field + TextField( + controller: _forwardToController, + decoration: InputDecoration( + labelText: 'Forward To', + ), + ), + + // Designation field + TextField( + controller: _designationController, + decoration: InputDecoration( + labelText: 'Receiver\'s Designation', + ), + ), + + // Send button + ElevatedButton( + onPressed: _submitForm, + child: Text('Send'), + ), + ], + ), + ], + ), + ), + ); + } +} 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_drafts/view_drafts.dart b/lib/screens/FileTracking/View_drafts/view_drafts.dart new file mode 100644 index 00000000..887a5c4f --- /dev/null +++ b/lib/screens/FileTracking/View_drafts/view_drafts.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; + +// Assuming you have a model class for drafted files +class DraftFileData { + final String id; + final String name; + final String description; + // No need for createdAt since we won't display it + + DraftFileData({ + required this.id, + required this.name, + required this.description, + }); +} + +class DraftsPage extends StatefulWidget { + @override + _DraftsPageState createState() => _DraftsPageState(); +} + +class _DraftsPageState extends State { + // Replace with your actual logic to fetch draft files + Future> _fetchDrafts() async { + // Simulate data fetching (replace with your API call or service) + return [ + DraftFileData( + id: "draft1", + name: "Important Document", + description: "Draft for meeting agenda", + ), + DraftFileData( + id: "draft2", + name: "Personal Notes", + description: "Unpublished thoughts and ideas", + ), + ]; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Drafts'), + ), + body: FutureBuilder>( + future: _fetchDrafts(), + builder: (context, snapshot) { + if (snapshot.hasData) { + final drafts = snapshot.data!; + return ListView.builder( + itemCount: drafts.length, + itemBuilder: (context, index) { + final draft = drafts[index]; + return ListTile( + title: Text(draft.name), + subtitle: Text(draft.description), + // Removed date and time + trailing: IconButton( + icon: Icon(Icons.delete), + onPressed: () { + // Handle draft deletion (e.g., show confirmation dialog) + // Your logic to delete the draft (API call, service) + // Update the list if deletion is successful + }, + ), + onTap: () { + // Handle draft item tap (e.g., open editor or view details) + Navigator.pushNamed(context, '/fts/view_drafts', arguments: draft); + }, + ); + }, + ); + } else if (snapshot.hasError) { + return Center(child: Text('Error fetching drafts: ${snapshot.error}')); + } else { + return Center(child: CircularProgressIndicator()); + } + }, + ), + ); + } +} 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..96ab16cd --- /dev/null +++ b/lib/screens/FileTracking/View_inbox/view_inbox.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; + +// Assuming you have a model class for inbox message data +class InboxMessage { + final String sender; + final String subject; + final String snippet; + final DateTime timestamp; + // ... other message data properties + + InboxMessage({ + required this.sender, + required this.subject, + required this.snippet, + required this.timestamp, + // ... other required or optional properties + }); +} + +class InboxPage extends StatefulWidget { + @override + _InboxPageState createState() => _InboxPageState(); +} + +class _InboxPageState extends State { + Future> _fetchInboxMessages() async { + // Replace with your actual logic to fetch inbox messages + // Simulate data fetching (replace with your API call or service) + return [ + InboxMessage( + sender: "University Administration", + subject: "Important Announcement", + snippet: "Regarding upcoming semester registration...", + timestamp: DateTime.now().subtract(Duration(days: 2)), + ), + InboxMessage( + sender: "Department of CS", + subject: "Course Materials Update", + snippet: "New materials uploaded for Data Structures...", + timestamp: DateTime.now().subtract(Duration(hours: 1)), + ), + ]; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Inbox'), + ), + body: FutureBuilder>( + future: _fetchInboxMessages(), + builder: (context, snapshot) { + if (snapshot.hasData) { + final messages = snapshot.data!; + return ListView.builder( + itemCount: messages.length, + itemBuilder: (context, index) { + final message = messages[index]; + return ListTile( + leading: CircleAvatar( + backgroundColor: Colors.grey[200], // Placeholder for sender avatar + child: Text(message.sender[0].toUpperCase()), // Initials from sender name + ), + title: Text(message.subject), + subtitle: Text(message.snippet), + trailing: Text(message.timestamp.toString()), + onTap: () { + // Handle message item tap (e.g., open details page) + Navigator.pushNamed( + context, + '/fts/view_message', // Replace with your message details page route + arguments: message, // Pass the message object for details + ); + }, + ); + }, + ); + } else if (snapshot.hasError) { + return Center(child: Text('Error fetching inbox messages: ${snapshot.error}')); + } else { + return Center(child: CircularProgressIndicator()); + } + }, + ), + ); + } +} 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..0e9d17e0 --- /dev/null +++ b/lib/screens/FileTracking/View_outbox/view_outbox.dart @@ -0,0 +1,137 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +// Assuming you have a model class for message or file data +class OutboxMessage { + final String recipient; + final String subject; + final String snippet; + final DateTime sentDate; + final String? messageType; // Optional for message type (e.g., email, chat) + + OutboxMessage({ + required this.recipient, + required this.subject, + required this.snippet, + required this.sentDate, + this.messageType, + }); +} + +class OutboxPage extends StatefulWidget { + @override + _OutboxPageState createState() => _OutboxPageState(); +} + +class _OutboxPageState extends State { + // Example data for two outbox messages + List exampleMessages = [ + OutboxMessage( + recipient: "Siddhant", + subject: "Project Update", + snippet: "The project is on track and we're making good progress...", + sentDate: DateTime.now().subtract(Duration(days: 2)), + messageType: "Email", + ), + OutboxMessage( + recipient: "Divyansh", + subject: "Meeting Reminder", + snippet: "Don't forget about the meeting tomorrow at 10 AM...", + sentDate: DateTime.now().subtract(Duration(days: 1)), + messageType: "Chat", + ), + ]; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Outbox'), + ), + body: ListView.builder( + itemCount: exampleMessages.length, // Use length of exampleMessages + itemBuilder: (context, index) { + final message = exampleMessages[index]; + return CustomListItem( + message: message, + onTap: () { + // Handle message tap (e.g., navigate to details page) + }, + ); + }, + ), + ); + } +} + +class CustomListItem extends StatelessWidget { + final OutboxMessage message; + final void Function() onTap; + + const CustomListItem({Key? key, required this.message, required this.onTap}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return ListTile( + contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), + leading: CircleAvatar( + backgroundColor: Colors.grey[200], + child: Text( + message.recipient[0].toUpperCase(), + style: TextStyle(color: Colors.white, fontSize: 16), + ), + ), + title: Text( + message.recipient, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + message.subject, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + SizedBox(height: 4), + Text( + message.snippet, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 12, + color: Colors.grey[600], + ), + ), + ], + ), + trailing: Wrap( + spacing: 8, + children: [ + TextButton( + onPressed: () { + // Handle view file action + }, + child: Text( + 'View File', + style: TextStyle( + color: Color.fromARGB(255, 241, 114, 3), + fontSize: 14, + fontWeight: FontWeight.w500, + ), + ), + ), + ], + ), + onTap: onTap, // Handle item tap (e.g., navigate to details page) + ); +} + + +} diff --git a/lib/screens/FileTracking/fts/fts.dart b/lib/screens/FileTracking/fts/fts.dart new file mode 100644 index 00000000..78c3d076 --- /dev/null +++ b/lib/screens/FileTracking/fts/fts.dart @@ -0,0 +1,124 @@ +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/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'; + +class RoundedListView extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: DefaultAppBar().buildAppBar(), + drawer: SideDrawer(), + body: Column( + children: [ + // User profile view (modify as needed) + Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + CircleAvatar( + backgroundImage: NetworkImage("https://picsum.photos/id/237/200/300"), + radius: 30.0, + ), + SizedBox(width: 16.0), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("Rohit Sharma", style: TextStyle(fontSize: 18.0,color: Colors.grey, fontWeight: FontWeight.bold, decoration: TextDecoration.none, +)), + Text("21BCS329", style: TextStyle(fontSize: 14.0, color: Colors.grey, 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', 'Track File', 'Outbox', 'Inbox']; + final paths = [ + '/create_file', // Path for Compose File + '/view_drafts', // Path for Drafts + '/track_file', // Path for Track File + '/view_outbox', // Path for Outbox + '/view_inbox', // Path for 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: () { + switch (paths[index]) { + case '/create_file': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => CreateFilePage()), + ); + break; + case '/view_drafts': + Navigator.push( + context, + MaterialPageRoute(builder: (context) => DraftsPage()), + ); + break; + case '/view_inbox': + // Navigate to your "Inbox" page here + Navigator.push( + context, + MaterialPageRoute(builder: (context) => InboxPage()), + ); + break; + case '/track_file': + // Navigate to your "Track File" page here + Navigator.push( + context, + MaterialPageRoute(builder: (context) => FileTrackingPage()), + ); + break; + case '/view_outbox': + // Navigate to your "Outbox" page here + Navigator.push( + context, + MaterialPageRoute(builder: (context) => OutboxPage()), + ); + break; + } + }, + ), // Icon color white + ], + ), + ), + ), + ); + }, + ), + ], + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 49030f0b..5c92b0ac 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 @@ -39,6 +40,8 @@ dependencies: 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/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'; From 2651ffdc7b2cf25eef5f144cd51ad925b79db900 Mon Sep 17 00:00:00 2001 From: aish0749 Date: Sun, 10 Mar 2024 23:27:00 +0530 Subject: [PATCH 04/10] update api --- devtools_options.yaml | 1 + .../Archive_file/archive_file.dart | 0 lib/services/filetracking_service.dart | 32 +++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 devtools_options.yaml create mode 100644 lib/screens/FileTracking/Archive_file/archive_file.dart create mode 100644 lib/services/filetracking_service.dart 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/lib/screens/FileTracking/Archive_file/archive_file.dart b/lib/screens/FileTracking/Archive_file/archive_file.dart new file mode 100644 index 00000000..e69de29b 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; +} From cbf94fd2d6f5bd462a01352a87d8e7a7b2e76a05 Mon Sep 17 00:00:00 2001 From: aish0749 Date: Mon, 11 Mar 2024 01:29:48 +0530 Subject: [PATCH 05/10] add api --- lib/main.dart | 26 +- .../FileTracking/Create_file/create_file.dart | 295 ++++++++++++------ .../FileTracking/View_inbox/view_inbox.dart | 155 +++++---- .../FileTracking/View_outbox/view_outbox.dart | 186 ++++++----- lib/screens/FileTracking/fts/fts.dart | 27 +- lib/screens/LoginandDashboard/login_page.dart | 4 + 6 files changed, 440 insertions(+), 253 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 37adc309..f8166623 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -48,6 +48,15 @@ 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(); @@ -63,6 +72,13 @@ 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 = @@ -134,11 +150,11 @@ class MyApp extends StatelessWidget { '/health_center/viewschedule': (context) => ViewSchedule(), '/health_center/history': (context) => History(), '/fts': (context) => RoundedListView(), - '/fts/create_file': (context) => CreateFilePage(), // Replace with your Track File page - '/fts/view_drafts': (context) => DraftsPage(), // Replace with your Drafts page - '/fts/view_inbox': (context) => InboxPage(), // Replace with your Inbox page - '/fts/view_outbox': (context) => OutboxPage(), // Replace with your Outbox page - '/fts/tack_file': (context) => FileTrackingPage(), // Replace with your Outbox page + '/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(), }, ), ); diff --git a/lib/screens/FileTracking/Create_file/create_file.dart b/lib/screens/FileTracking/Create_file/create_file.dart index 0fb0e542..43923552 100644 --- a/lib/screens/FileTracking/Create_file/create_file.dart +++ b/lib/screens/FileTracking/Create_file/create_file.dart @@ -1,4 +1,16 @@ +import 'dart:io'; + 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 'dart:core'; +import 'package:fusion/api.dart'; +import 'package:fusion/constants.dart'; + + +import 'package:shared_preferences/shared_preferences.dart'; class CreateFilePage extends StatefulWidget { @override @@ -9,33 +21,145 @@ class _CreateFilePageState extends State { // Form controllers final _titleController = TextEditingController(); final _descriptionController = TextEditingController(); - final _attachController = TextEditingController(); - final _remarksController = TextEditingController(); + final _sendAsController = TextEditingController(); final _forwardToController = TextEditingController(); final _designationController = TextEditingController(); - // Submit form with placeholder logic - Future _submitForm() async { - // Handle form data validation and submission (replace with your logic) - if (_titleController.text.isEmpty || _descriptionController.text.isEmpty) { - print('Error: Title and Description are required.'); - return; + // API endpoint + // static const String _apiUrl = '172.27.16.214:8000/filetracking/api/file/'; + + // Submit form with POST API call and store data locally +Future _submitForm( +) async { + try { + // Validate required fields + if (_titleController.text?.isEmpty == true || _descriptionController.text?.isEmpty == true) { + throw Exception('Title and description are required.'); + } + + // Retrieve token securely (replace with appropriate mechanism) + // final token = await _retrieveToken(); + + // Prepare headers + var storageService = locator(); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); } - print('Form data:'); - print('Title: ${_titleController.text}'); - print('Description: ${_descriptionController.text}'); - print('Remarks: ${_remarksController.text}'); - print('Forward To: ${_forwardToController.text}'); - print('Designation: ${_designationController.text}'); - - // Clear form fields after submission - _titleController.clear(); - _descriptionController.clear(); - _remarksController.clear(); - _forwardToController.clear(); - _designationController.clear(); + Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; + + // Prepare request body + final data = jsonEncode({ + 'subject': _titleController.text, + // 'remarks': _remarksController.text, (Assuming not used) + 'designation': _sendAsController.text, // Replace with appropriate value + 'receiver_username': _forwardToController.text, + 'receiver_designation': _designationController.text, + 'description': _descriptionController.text, + }); + + // Make POST request + + final client = http.Client(); + client.post( + Uri.http('10.0.2.2:8000', '/filetracking/api/file/'), + headers: headers, + body: data, + ).then((response) { + print(response.statusCode); + }).catchError((error) { + print(error); + }); + // Handle response + // print(response.statusCode); + // if (response.statusCode == HttpStatus.created) { + // // Handle successful file creation (e.g., show success message) + // print('File created successfully!'); + // } else { + // // Handle other status codes (e.g., show error message) + // final errorMessage = await _parseErrorMessage(response); + // print('Error: $errorMessage'); + // } + } catch (e) { + // Handle other exceptions (e.g., network errors, token errors) + print('An error occurred: $e'); } +} + +Future _draftForm( +) async { + try { + // Validate required fields + if (_titleController.text?.isEmpty == true || _descriptionController.text?.isEmpty == true) { + throw Exception('Title and description are required.'); + } + + // Retrieve token securely (replace with appropriate mechanism) + // final token = await _retrieveToken(); + + // 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 + final data = jsonEncode({ + 'file_extra_JSON':{ + 'subject': _titleController.text, + }, + // 'remarks': _remarksController.text, (Assuming not used) + 'uploader': '21BCS078', // Replace with appropriate value + 'uploader_designation': _sendAsController.text, // Replace with appropriate value + 'src_module': 'filetracking' + }); + + // Make POST request + + final client = http.Client(); + client.post( + Uri.http('10.0.2.2:8000', '/filetracking/api/createdraft/'), + headers: headers, + body: data, + ).then((response) { + print(response.statusCode); + }).catchError((error) { + print(error); + }); + // Handle response + // print(response.statusCode); + // if (response.statusCode == HttpStatus.created) { + // // Handle successful file creation (e.g., show success message) + // print('File created successfully!'); + // } else { + // // Handle other status codes (e.g., show error message) + // final errorMessage = await _parseErrorMessage(response); + // print('Error: $errorMessage'); + // } + } catch (e) { + // Handle other exceptions (e.g., network errors, token errors) + print('An error occurred: $e'); + } +} + +Future _parseErrorMessage(http.Response response) async { + // Implement your error parsing logic here (e.g., from JSON response body) + try { + final decodedResponse = jsonDecode(response.body); + return decodedResponse['message'] ?? 'Unknown error'; + } catch (error) { + return 'Failed to parse error message'; + } +} + @override Widget build(BuildContext context) { @@ -52,42 +176,6 @@ class _CreateFilePageState extends State { padding: const EdgeInsets.all(16.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, - children: [ - // TODO: Add your user profile image and details - CircleAvatar( - radius: 30.0, - ), - SizedBox(width: 16.0), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // TODO: Replace with user name and designation - Text( - "Rohit Sharma", - style: TextStyle( - fontSize: 18.0, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), - ), - Text( - "21BCS329", - style: TextStyle( - fontSize: 18.0, - color: Colors.grey, - fontWeight: FontWeight.bold, - ), - ), - Text( - "Designation", - style: TextStyle( - fontSize: 14.0, - color: Colors.grey, - ), - ), - ], - ), - ], ), ), @@ -97,59 +185,59 @@ class _CreateFilePageState extends State { // Form content Column( children: [ - // Title field + // Title field (required) TextField( controller: _titleController, decoration: InputDecoration( - labelText: 'Title', - // errorText: _titleController.text.isEmpty ? 'Required' : null, + labelText: 'Title*', + errorText: _titleController.text.isEmpty ? 'Required' : null, ), ), - // Description field + // Description field (required) TextField( controller: _descriptionController, decoration: InputDecoration( - labelText: 'Description', + labelText: 'Description*', // errorText: _descriptionController.text.isEmpty ? 'Required' : null, ), ), // Attach field - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - // Text 'Attach File' - Text('Attach File'), - SizedBox(width: 16.0), // Add spacing between text and button - - // ElevatedButton with 'Upload' text - ElevatedButton( - onPressed: () { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('File Attachment'), - content: Text('File attachment is not currently supported in this web app.'), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: Text('OK'), - ), - ], - ), - ); - }, - child: Text('Upload'), - ), - ], - ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + // Text 'Attach File' + Text('Attach File'), + SizedBox(width: 16.0), // Add spacing between text and button + + // ElevatedButton with informative text + ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('File Attachment'), + content: Text('File attachment is not currently supported in this web app.'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: Text('OK'), + ), + ], + ), + ); + }, + child: Text('Browse'), // Changed text to "Browse" + ), + ], + ), // Remarks field TextField( - controller: _remarksController, + controller: _sendAsController, decoration: InputDecoration( - labelText: 'Remarks', + labelText: 'Send as', ), ), @@ -170,9 +258,26 @@ class _CreateFilePageState extends State { ), // Send button - ElevatedButton( - onPressed: _submitForm, - child: Text('Send'), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: () async { + await _submitForm( + ); + }, + child: Text('Send'), + ), + ElevatedButton( + onPressed: () async { + // Handle "Draft" functionality (implementation optional) + await _draftForm( + + ); + }, + child: Text('Draft'), + ), + ], ), ], ), diff --git a/lib/screens/FileTracking/View_inbox/view_inbox.dart b/lib/screens/FileTracking/View_inbox/view_inbox.dart index 96ab16cd..fb68ab14 100644 --- a/lib/screens/FileTracking/View_inbox/view_inbox.dart +++ b/lib/screens/FileTracking/View_inbox/view_inbox.dart @@ -1,88 +1,137 @@ +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; + -// Assuming you have a model class for inbox message data class InboxMessage { final String sender; final String subject; final String snippet; final DateTime timestamp; - // ... other message data properties InboxMessage({ required this.sender, required this.subject, required this.snippet, required this.timestamp, - // ... other required or optional properties }); } class InboxPage extends StatefulWidget { + + final String username; + InboxPage({required this.username}); + @override _InboxPageState createState() => _InboxPageState(); } class _InboxPageState extends State { - Future> _fetchInboxMessages() async { - // Replace with your actual logic to fetch inbox messages - // Simulate data fetching (replace with your API call or service) - return [ - InboxMessage( - sender: "University Administration", - subject: "Important Announcement", - snippet: "Regarding upcoming semester registration...", - timestamp: DateTime.now().subtract(Duration(days: 2)), - ), - InboxMessage( - sender: "Department of CS", - subject: "Course Materials Update", - snippet: "New materials uploaded for Data Structures...", - timestamp: DateTime.now().subtract(Duration(hours: 1)), - ), - ]; + final _designationController = TextEditingController(); + + @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', + }; + + print(headers['Authorization']); + // 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) { + // Assuming the response body directly contains inbox data (no parsing needed) + var inboxData = response.body; + print(inboxData); + // Print the inbox data for debugging or logging + // Use the inboxData to display or process the fetched inbox files in your UI + } else { + // Handle error (e.g., print error message) + print('Error fetching inbox data: ${response.statusCode}'); + } + } 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: FutureBuilder>( - future: _fetchInboxMessages(), - builder: (context, snapshot) { - if (snapshot.hasData) { - final messages = snapshot.data!; - return ListView.builder( - itemCount: messages.length, - itemBuilder: (context, index) { - final message = messages[index]; - return ListTile( - leading: CircleAvatar( - backgroundColor: Colors.grey[200], // Placeholder for sender avatar - child: Text(message.sender[0].toUpperCase()), // Initials from sender name + body: SingleChildScrollView( + padding: EdgeInsets.all(16.0), + child: Column( + children: [ + + + // Divider + Divider(thickness: 1.0, color: Colors.grey[300]), + + // Form content + Column( + children: [ + // Title field (require + // Designation field + TextField( + controller: _designationController, + decoration: InputDecoration( + labelText: 'View As', ), - title: Text(message.subject), - subtitle: Text(message.snippet), - trailing: Text(message.timestamp.toString()), - onTap: () { - // Handle message item tap (e.g., open details page) - Navigator.pushNamed( - context, - '/fts/view_message', // Replace with your message details page route - arguments: message, // Pass the message object for details - ); - }, - ); - }, - ); - } else if (snapshot.hasError) { - return Center(child: Text('Error fetching inbox messages: ${snapshot.error}')); - } else { - return Center(child: CircularProgressIndicator()); - } - }, + ), + + // Send button + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: () async { + await _submitForm( + ); + }, + child: Text('View'), + ), + ], + ), + ], + ), + ], + ), ), ); } } + diff --git a/lib/screens/FileTracking/View_outbox/view_outbox.dart b/lib/screens/FileTracking/View_outbox/view_outbox.dart index 0e9d17e0..906841c2 100644 --- a/lib/screens/FileTracking/View_outbox/view_outbox.dart +++ b/lib/screens/FileTracking/View_outbox/view_outbox.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:fusion/services/service_locator.dart'; +import 'package:fusion/services/storage_service.dart'; import 'package:intl/intl.dart'; +import 'package:http/http.dart' as http; // Assuming you have a model class for message or file data class OutboxMessage { @@ -24,23 +27,57 @@ class OutboxPage extends StatefulWidget { } class _OutboxPageState extends State { + final _designationController = TextEditingController(); // Example data for two outbox messages - List exampleMessages = [ - OutboxMessage( - recipient: "Siddhant", - subject: "Project Update", - snippet: "The project is on track and we're making good progress...", - sentDate: DateTime.now().subtract(Duration(days: 2)), - messageType: "Email", - ), - OutboxMessage( - recipient: "Divyansh", - subject: "Meeting Reminder", - snippet: "Don't forget about the meeting tomorrow at 10 AM...", - sentDate: DateTime.now().subtract(Duration(days: 1)), - messageType: "Chat", - ), - ]; + Future _submitForm() async { + try { + // Validate required fields + if (_designationController.text?.isEmpty == true) { + throw Exception('Designation required.'); + } + + // Retrieve token securely (replace with appropriate mechanism) + + 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': '21BCS013', + 'designation': _designationController.text, + 'src_module': 'filetracking', + }; + + // Construct URL with encoded query parameters + final Uri url = Uri.http('10.0.2.2:8000', '/filetracking/api/outbox/', queryParams); + + final client = http.Client(); + + // Make GET request + final response = await client.get(url, headers: headers); + + // Handle response + if (response.statusCode == 200) { + // Assuming the response body directly contains inbox data (no parsing needed) + final outboxData = response.body; + print(outboxData); // Print the inbox data for debugging or logging + // Use the inboxData to display or process the fetched inbox files in your UI + } else { + // Handle error (e.g., print error message) + print('Error fetching inbox data: ${response.statusCode}'); + } + } catch (e) { + // Handle other exceptions (e.g., network errors, token errors) + print('An error occurred: $e'); + } +} @override Widget build(BuildContext context) { @@ -48,90 +85,47 @@ class _OutboxPageState extends State { appBar: AppBar( title: Text('Outbox'), ), - body: ListView.builder( - itemCount: exampleMessages.length, // Use length of exampleMessages - itemBuilder: (context, index) { - final message = exampleMessages[index]; - return CustomListItem( - message: message, - onTap: () { - // Handle message tap (e.g., navigate to details page) - }, - ); - }, - ), - ); - } -} + body: SingleChildScrollView( + padding: EdgeInsets.all(16.0), + child: Column( + children: [ + -class CustomListItem extends StatelessWidget { - final OutboxMessage message; - final void Function() onTap; + // Divider + Divider(thickness: 1.0, color: Colors.grey[300]), - const CustomListItem({Key? key, required this.message, required this.onTap}) - : super(key: key); + // Form content + Column( + children: [ + // Title field (require + // Designation field + TextField( + controller: _designationController, + decoration: InputDecoration( + labelText: 'View As', + ), + ), - @override - Widget build(BuildContext context) { - return ListTile( - contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), - leading: CircleAvatar( - backgroundColor: Colors.grey[200], - child: Text( - message.recipient[0].toUpperCase(), - style: TextStyle(color: Colors.white, fontSize: 16), - ), - ), - title: Text( - message.recipient, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - message.subject, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - ), - ), - SizedBox(height: 4), - Text( - message.snippet, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 12, - color: Colors.grey[600], - ), - ), - ], - ), - trailing: Wrap( - spacing: 8, - children: [ - TextButton( - onPressed: () { - // Handle view file action - }, - child: Text( - 'View File', - style: TextStyle( - color: Color.fromARGB(255, 241, 114, 3), - fontSize: 14, - fontWeight: FontWeight.w500, + // Send button + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: () async { + await _submitForm( + + ); + }, + child: Text('View'), + ), + ], + ), + ], ), - ), + ], ), - ], - ), - onTap: onTap, // Handle item tap (e.g., navigate to details page) - ); + ), + ); + } } - -} diff --git a/lib/screens/FileTracking/fts/fts.dart b/lib/screens/FileTracking/fts/fts.dart index 78c3d076..7d7db18b 100644 --- a/lib/screens/FileTracking/fts/fts.dart +++ b/lib/screens/FileTracking/fts/fts.dart @@ -5,11 +5,27 @@ 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: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(), @@ -29,7 +45,7 @@ class RoundedListView extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("Rohit Sharma", style: TextStyle(fontSize: 18.0,color: Colors.grey, fontWeight: FontWeight.bold, decoration: TextDecoration.none, + Text("PP", style: TextStyle(fontSize: 18.0,color: Colors.grey, fontWeight: FontWeight.bold, decoration: TextDecoration.none, )), Text("21BCS329", style: TextStyle(fontSize: 14.0, color: Colors.grey, decoration: TextDecoration.none, )), @@ -72,12 +88,15 @@ class RoundedListView extends StatelessWidget { items[index]), IconButton( icon: Icon(Icons.chevron_right), - onPressed: () { + onPressed: () async { + final username = await getUsername(); + print(username); switch (paths[index]) { case '/create_file': Navigator.push( context, MaterialPageRoute(builder: (context) => CreateFilePage()), + ); break; case '/view_drafts': @@ -90,7 +109,7 @@ class RoundedListView extends StatelessWidget { // Navigate to your "Inbox" page here Navigator.push( context, - MaterialPageRoute(builder: (context) => InboxPage()), + MaterialPageRoute(builder: (context) => InboxPage(username: username)), ); break; case '/track_file': diff --git a/lib/screens/LoginandDashboard/login_page.dart b/lib/screens/LoginandDashboard/login_page.dart index ca09fee2..067820f3 100644 --- a/lib/screens/LoginandDashboard/login_page.dart +++ b/lib/screens/LoginandDashboard/login_page.dart @@ -3,6 +3,7 @@ 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'; @@ -87,10 +88,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"); From 35456302579b5014cc34191428d1afdb7913bd18 Mon Sep 17 00:00:00 2001 From: aish0749 Date: Mon, 8 Apr 2024 13:49:07 +0530 Subject: [PATCH 06/10] add the api and flutter widget to display information regarding files --- lib/api.dart | 6 +- lib/main.dart | 8 +- .../FileTracking/Create_file/create_file.dart | 221 +++++++---------- .../Forward_file/forward_file.dart | 154 ++++++++++++ .../View_File/view_draftfile.dart | 61 +++++ .../View_File/view_inboxfile.dart | 155 ++++++++++++ .../View_File/view_outboxfile.dart | 79 ++++++ .../FileTracking/View_drafts/view_drafts.dart | 233 ++++++++++++++---- .../FileTracking/View_inbox/view_inbox.dart | 168 ++++++++----- .../FileTracking/View_outbox/view_outbox.dart | 158 +++++++----- lib/screens/FileTracking/fts/fts.dart | 86 ++++--- 11 files changed, 989 insertions(+), 340 deletions(-) create mode 100644 lib/screens/FileTracking/Forward_file/forward_file.dart create mode 100644 lib/screens/FileTracking/View_File/view_draftfile.dart create mode 100644 lib/screens/FileTracking/View_File/view_inboxfile.dart create mode 100644 lib/screens/FileTracking/View_File/view_outboxfile.dart diff --git a/lib/api.dart b/lib/api.dart index 73d1c6bc..c8c53b22 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -1,9 +1,9 @@ //Server and local links -String klocalLink = "127.0.0.1:8000"; -String kserverLink = "172.27.16.214:8000"; +String klocalLink = "10.0.2.2:8000"; +String kserverLink = "10.0.2.2:8000"; //Login Service -String kAuthUrl = "172.27.16.214:8000"; +String kAuthUrl = "10.0.2.2:8000"; String kAuthLogin = "/api/auth/login/"; // String kAuthLogin = "/accounts/login"; diff --git a/lib/main.dart b/lib/main.dart index f8166623..2fa1523f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -150,11 +150,11 @@ class MyApp extends StatelessWidget { '/health_center/viewschedule': (context) => ViewSchedule(), '/health_center/history': (context) => History(), '/fts': (context) => RoundedListView(), - '/fts/create_file': (context) => CreateFilePage(), - '/fts/view_drafts': (context) => DraftsPage(), + // '/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(), + // '/fts/view_outbox': (context) => OutboxPage(), + // '/fts/tack_file': (context) => FileTrackingPage(), }, ), ); diff --git a/lib/screens/FileTracking/Create_file/create_file.dart b/lib/screens/FileTracking/Create_file/create_file.dart index 43923552..10c14743 100644 --- a/lib/screens/FileTracking/Create_file/create_file.dart +++ b/lib/screens/FileTracking/Create_file/create_file.dart @@ -5,14 +5,11 @@ 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 'dart:core'; -import 'package:fusion/api.dart'; -import 'package:fusion/constants.dart'; - - import 'package:shared_preferences/shared_preferences.dart'; class CreateFilePage extends StatefulWidget { + final String username; + CreateFilePage({required this.username}); @override _CreateFilePageState createState() => _CreateFilePageState(); } @@ -25,81 +22,68 @@ class _CreateFilePageState extends State { final _forwardToController = TextEditingController(); final _designationController = TextEditingController(); - // API endpoint - // static const String _apiUrl = '172.27.16.214:8000/filetracking/api/file/'; - - // Submit form with POST API call and store data locally -Future _submitForm( -) async { - try { - // Validate required fields - if (_titleController.text?.isEmpty == true || _descriptionController.text?.isEmpty == true) { - throw Exception('Title and description are required.'); + Future _submitForm() async { + try { + // Validate required fields + if (_titleController.text?.isEmpty == true || + _descriptionController.text?.isEmpty == true) { + throw Exception('Title and description are 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 + final data = jsonEncode({ + 'subject': _titleController.text, + 'designation': _sendAsController.text, + 'receiver_username': _forwardToController.text, + 'receiver_designation': _designationController.text, + 'description': _descriptionController.text, + }); + + // Make POST request + final response = await http.post( + Uri.http('10.0.2.2:8000', '/filetracking/api/file/'), + headers: headers, + body: data, + ); + + // Handle response + if (response.statusCode == HttpStatus.created) { + // Show success message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('File sent'), + backgroundColor: Colors.green, + ), + ); + } 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'); } - - // Retrieve token securely (replace with appropriate mechanism) - // final token = await _retrieveToken(); - - // 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 - final data = jsonEncode({ - 'subject': _titleController.text, - // 'remarks': _remarksController.text, (Assuming not used) - 'designation': _sendAsController.text, // Replace with appropriate value - 'receiver_username': _forwardToController.text, - 'receiver_designation': _designationController.text, - 'description': _descriptionController.text, - }); - - // Make POST request - - final client = http.Client(); - client.post( - Uri.http('10.0.2.2:8000', '/filetracking/api/file/'), - headers: headers, - body: data, - ).then((response) { - print(response.statusCode); - }).catchError((error) { - print(error); - }); - // Handle response - // print(response.statusCode); - // if (response.statusCode == HttpStatus.created) { - // // Handle successful file creation (e.g., show success message) - // print('File created successfully!'); - // } else { - // // Handle other status codes (e.g., show error message) - // final errorMessage = await _parseErrorMessage(response); - // print('Error: $errorMessage'); - // } - } catch (e) { - // Handle other exceptions (e.g., network errors, token errors) - print('An error occurred: $e'); } -} -Future _draftForm( -) async { + Future _draftForm() async { try { // Validate required fields - if (_titleController.text?.isEmpty == true || _descriptionController.text?.isEmpty == true) { - throw Exception('Title and description are required.'); + if (_titleController.text?.isEmpty == true) { + throw Exception('Title is required.'); } - // Retrieve token securely (replace with appropriate mechanism) - // final token = await _retrieveToken(); - // Prepare headers var storageService = locator(); if (storageService.userInDB?.token == null) { @@ -112,54 +96,50 @@ Future _draftForm( }; // Prepare request body + print(_titleController.text); final data = jsonEncode({ - 'file_extra_JSON':{ 'subject': _titleController.text, - }, - // 'remarks': _remarksController.text, (Assuming not used) - 'uploader': '21BCS078', // Replace with appropriate value - 'uploader_designation': _sendAsController.text, // Replace with appropriate value + 'uploader': widget.username, + 'uploader_designation': _sendAsController.text, 'src_module': 'filetracking' }); - // Make POST request - - final client = http.Client(); - client.post( + final response = await http.post( Uri.http('10.0.2.2:8000', '/filetracking/api/createdraft/'), headers: headers, body: data, - ).then((response) { - print(response.statusCode); - }).catchError((error) { - print(error); - }); + ); + // Handle response - // print(response.statusCode); - // if (response.statusCode == HttpStatus.created) { - // // Handle successful file creation (e.g., show success message) - // print('File created successfully!'); - // } else { - // // Handle other status codes (e.g., show error message) - // final errorMessage = await _parseErrorMessage(response); - // print('Error: $errorMessage'); - // } + if (response.statusCode == HttpStatus.created) { + // Show success message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Draft saved'), + backgroundColor: Colors.green, + ), + ); + } else { + // Handle other status codes (e.g., show error message) + final errorMessage = await _parseErrorMessage(response); + print('Error: $errorMessage'); + } } catch (e) { - // Handle other exceptions (e.g., network errors, token errors) + // Handle other exceptions print('An error occurred: $e'); } } -Future _parseErrorMessage(http.Response response) async { - // Implement your error parsing logic here (e.g., from JSON response body) - try { - final decodedResponse = jsonDecode(response.body); - return decodedResponse['message'] ?? 'Unknown error'; - } catch (error) { - return 'Failed to parse error message'; - } -} + Future _parseErrorMessage(http.Response response) async { + // Implement your error parsing logic here (e.g., from JSON response body) + try { + final decodedResponse = jsonDecode(response.body); + return decodedResponse['message'] ?? 'Unknown error'; + } catch (error) { + return 'Failed to parse error message'; + } + } @override Widget build(BuildContext context) { @@ -171,14 +151,6 @@ Future _parseErrorMessage(http.Response response) async { padding: EdgeInsets.all(16.0), child: Column( children: [ - // User profile view (adapt from your previous code) - Padding( - padding: const EdgeInsets.all(16.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - ), - ), - // Divider Divider(thickness: 1.0, color: Colors.grey[300]), @@ -190,7 +162,8 @@ Future _parseErrorMessage(http.Response response) async { controller: _titleController, decoration: InputDecoration( labelText: 'Title*', - errorText: _titleController.text.isEmpty ? 'Required' : null, + errorText: + _titleController.text.isEmpty ? 'Required' : null, ), ), @@ -199,7 +172,6 @@ Future _parseErrorMessage(http.Response response) async { controller: _descriptionController, decoration: InputDecoration( labelText: 'Description*', - // errorText: _descriptionController.text.isEmpty ? 'Required' : null, ), ), @@ -218,7 +190,8 @@ Future _parseErrorMessage(http.Response response) async { context: context, builder: (context) => AlertDialog( title: Text('File Attachment'), - content: Text('File attachment is not currently supported in this web app.'), + content: Text( + 'File attachment is not currently supported in this web app.'), actions: [ TextButton( onPressed: () => Navigator.pop(context), @@ -263,17 +236,13 @@ Future _parseErrorMessage(http.Response response) async { children: [ ElevatedButton( onPressed: () async { - await _submitForm( - ); - }, - child: Text('Send'), - ), + await _submitForm(); + }, + child: Text('Send'), + ), ElevatedButton( onPressed: () async { - // Handle "Draft" functionality (implementation optional) - await _draftForm( - - ); + 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..5f379a5d --- /dev/null +++ b/lib/screens/FileTracking/Forward_file/forward_file.dart @@ -0,0 +1,154 @@ +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 StatelessWidget { + final Map fileDetails; + + const ForwardFilePage({Key? key, required this.fileDetails}) : super(key: key); + + Future forwardFile({ + required String receiver, + required String receiverDesignation, + required String remarks, + }) async { + try{ + // Your API endpoint for forwarding the file + var storageService = locator(); + if (storageService.userInDB?.token == null) { + throw Exception('Token Error'); + } + + Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; + // Prepare the request body + final data = jsonEncode({ + 'receiver': receiver, + 'receiver_designation': receiverDesignation, + 'remarks': remarks, + }); + final file_id=fileDetails['id']; + // Make the POST request + print(data); + final client = http.Client(); + client.post( + Uri.http('10.0.2.2:8000', '/filetracking/api/forwardfile/$file_id/'), + headers: headers, + body: data, + ).then((response) { + print(response.statusCode); + }).catchError((error) { + print(error); + }); + // Check if the request was successful + } catch (error) { + // Handle network errors + print('Failed to forward file. Error: $error'); + } + } + + @override + Widget build(BuildContext context) { + String receiver = ''; // Receiver username + String receiverDesignation = ''; // Receiver designation + String remarks = ''; // Remarks entered by the user + + 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(fileDetails['subject']), + ), + ListTile( + title: Text( + 'Uploader', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(fileDetails['uploader']), + ), + ListTile( + title: Text( + 'Sent By', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(fileDetails['sent_by_user']), + ), + SizedBox(height: 16), + TextField( + decoration: InputDecoration( + labelText: 'Remark', + border: OutlineInputBorder(), + ), + onChanged: (value) { + remarks = value; + }, + ), + SizedBox(height: 16), + TextField( + decoration: InputDecoration( + labelText: 'Receiver Username', + border: OutlineInputBorder(), + ), + onChanged: (value) { + receiver = value; + }, + ), + SizedBox(height: 16), + TextField( + decoration: InputDecoration( + labelText: 'Receiver Designation', + border: OutlineInputBorder(), + ), + onChanged: (value) { + receiverDesignation = value; + }, + ), + SizedBox(height: 16), + ElevatedButton( + onPressed: () { + forwardFile( + receiver: receiver, + receiverDesignation: receiverDesignation, + remarks: remarks, + ); + }, + child: Text('Forward'), + ), + ], + ), + ), + ); + } +} \ 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..0bc2ece2 --- /dev/null +++ b/lib/screens/FileTracking/View_File/view_draftfile.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; + +class ViewDraftFilePage extends StatelessWidget { + final Map draftDetails; + + const ViewDraftFilePage({Key? key, required this.draftDetails}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Draft 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(draftDetails['id'].toString()), + ), + ListTile( + title: Text( + 'Subject', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(draftDetails['subject'] ?? 'No subject available'), + ), + ListTile( + title: Text( + 'Description', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(draftDetails['description'] ?? 'No description available'), + ), + ], + ), + ), + ); + } +} 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..92d2aafd --- /dev/null +++ b/lib/screens/FileTracking/View_File/view_inboxfile.dart @@ -0,0 +1,155 @@ +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 MessageDetailPage extends StatefulWidget { + final Map messageDetails; + + const MessageDetailPage({Key? key, required this.messageDetails}) : super(key: key); + + @override + _MessageDetailPageState createState() => _MessageDetailPageState(); +} + +class _MessageDetailPageState extends State { + String? historyResponse; + + @override + void initState() { + super.initState(); + fetchHistory(); + } + + 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/history/${widget.messageDetails['id']}'); + final client = http.Client(); + final response = await client.get(url, headers: headers); + if (response.statusCode == 200) { + setState(() { + historyResponse = response.body; + }); + } else { + print('Failed to fetch file history. Error: ${response.reasonPhrase}'); + } + } catch (error) { + print('Failed to fetch file history. Error: $error'); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('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( + 'Message Details', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + ), + ListTile( + title: Text( + 'ID', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(widget.messageDetails['id'].toString()), + ), + ListTile( + title: Text( + 'Subject', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(widget.messageDetails['subject']), + ), + ListTile( + title: Text( + 'Description', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(widget.messageDetails['description']), + ), + ListTile( + title: Text( + 'Sent By', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(widget.messageDetails['sent_by_user']), + ), + ListTile( + title: Text( + 'Upload Date', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(widget.messageDetails['upload_date']), + ), + ListTile( + title: Text( + 'Uploader', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(widget.messageDetails['uploader']), + ), + SizedBox(height: 16), + if (historyResponse != null) + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'File History Response', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 8), + Text( + JsonEncoder.withIndent(' ').convert(jsonDecode(historyResponse!)), + style: TextStyle( + fontSize: 14.0, + fontFamily: 'Courier', + ), + ), + ], + ), + ], + ), + ), + ); + } +} 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..bc797ee6 --- /dev/null +++ b/lib/screens/FileTracking/View_File/view_outboxfile.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; + +class MessageDetailPage extends StatelessWidget { + final Map messageDetails; + + const MessageDetailPage({Key? key, required this.messageDetails}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('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( + 'Message Details', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + ), + ListTile( + title: Text( + 'ID', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(messageDetails['id'].toString()), + ), + ListTile( + title: Text( + 'Subject', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(messageDetails['subject']), + ), + ListTile( + title: Text( + 'Description', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(messageDetails['description']), + ), + ListTile( + title: Text( + 'Upload Date', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(messageDetails['upload_date']), + ), + ListTile( + title: Text( + 'Uploader', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(messageDetails['uploader']), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/FileTracking/View_drafts/view_drafts.dart b/lib/screens/FileTracking/View_drafts/view_drafts.dart index 887a5c4f..912167aa 100644 --- a/lib/screens/FileTracking/View_drafts/view_drafts.dart +++ b/lib/screens/FileTracking/View_drafts/view_drafts.dart @@ -1,82 +1,207 @@ +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'; -// Assuming you have a model class for drafted files class DraftFileData { final String id; - final String name; - final String description; - // No need for createdAt since we won't display it DraftFileData({ required this.id, - required this.name, - required this.description, }); } class DraftsPage extends StatefulWidget { + final String username; + + DraftsPage({required this.username}); + @override _DraftsPageState createState() => _DraftsPageState(); } class _DraftsPageState extends State { - // Replace with your actual logic to fetch draft files - Future> _fetchDrafts() async { - // Simulate data fetching (replace with your API call or service) - return [ - DraftFileData( - id: "draft1", - name: "Important Document", - description: "Draft for meeting agenda", - ), - DraftFileData( - id: "draft2", - name: "Personal Notes", - description: "Unpublished thoughts and ideas", - ), - ]; + 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'; + }); + } + } + + 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('Drafts'), + title: Text('Draft'), ), - body: FutureBuilder>( - future: _fetchDrafts(), - builder: (context, snapshot) { - if (snapshot.hasData) { - final drafts = snapshot.data!; - return ListView.builder( - itemCount: drafts.length, - itemBuilder: (context, index) { - final draft = drafts[index]; - return ListTile( - title: Text(draft.name), - subtitle: Text(draft.description), - // Removed date and time - trailing: IconButton( - icon: Icon(Icons.delete), - onPressed: () { - // Handle draft deletion (e.g., show confirmation dialog) - // Your logic to delete the draft (API call, service) - // Update the list if deletion is successful - }, + 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)]), + ), + ); + }, + child: Text('View'), + ), + SizedBox(width: 8), + TextButton( + onPressed: () { + // Handle delete button pressed + _deleteDraft(fileID); + }, + child: Text('Delete'), + ), + ], + ), + ], ), - onTap: () { - // Handle draft item tap (e.g., open editor or view details) - Navigator.pushNamed(context, '/fts/view_drafts', arguments: draft); - }, ); - }, - ); - } else if (snapshot.hasError) { - return Center(child: Text('Error fetching drafts: ${snapshot.error}')); - } else { - return Center(child: CircularProgressIndicator()); - } - }, + }).toList(), + ), + ], + ), ), ); } diff --git a/lib/screens/FileTracking/View_inbox/view_inbox.dart b/lib/screens/FileTracking/View_inbox/view_inbox.dart index fb68ab14..d1e5020e 100644 --- a/lib/screens/FileTracking/View_inbox/view_inbox.dart +++ b/lib/screens/FileTracking/View_inbox/view_inbox.dart @@ -4,6 +4,8 @@ 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 { @@ -21,7 +23,6 @@ class InboxMessage { } class InboxPage extends StatefulWidget { - final String username; InboxPage({required this.username}); @@ -31,60 +32,71 @@ class InboxPage extends StatefulWidget { 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', - }; - - print(headers['Authorization']); - // 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) { - // Assuming the response body directly contains inbox data (no parsing needed) - var inboxData = response.body; - print(inboxData); - // Print the inbox data for debugging or logging - // Use the inboxData to display or process the fetched inbox files in your UI - } else { - // Handle error (e.g., print error message) - print('Error fetching inbox data: ${response.statusCode}'); + 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'); } - } catch (e) { - // Handle other exceptions (e.g., network errors, token errors) - print('An error occurred: $e'); } -} @override Widget build(BuildContext context) { @@ -96,15 +108,12 @@ Future _submitForm() async { padding: EdgeInsets.all(16.0), child: Column( children: [ - - // Divider Divider(thickness: 1.0, color: Colors.grey[300]), // Form content Column( children: [ - // Title field (require // Designation field TextField( controller: _designationController, @@ -119,19 +128,66 @@ Future _submitForm() async { children: [ ElevatedButton( onPressed: () async { - await _submitForm( - ); - }, - child: Text('View'), - ), + 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'), + Row( + children: [ + TextButton( + onPressed: () { + print(jsonData[fileIDsList.indexOf(fileID)]); + // Handle view button pressed + Navigator.push( context, + MaterialPageRoute( + builder: (context) => MessageDetailPage(messageDetails: jsonData[fileIDsList.indexOf(fileID)]), + ), + ); + }, + child: Text('View'), + ), + SizedBox(width: 8), + TextButton( + onPressed: () { + print(jsonData[fileIDsList.indexOf(fileID)]); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ForwardFilePage(fileDetails: jsonData[fileIDsList.indexOf(fileID)]), + ), + ); + }, + child: Text('Forward'), + ), + ], + ), + ], + ), + ); + }).toList(), + ), ], ), ), ); } } - diff --git a/lib/screens/FileTracking/View_outbox/view_outbox.dart b/lib/screens/FileTracking/View_outbox/view_outbox.dart index 906841c2..c824aae2 100644 --- a/lib/screens/FileTracking/View_outbox/view_outbox.dart +++ b/lib/screens/FileTracking/View_outbox/view_outbox.dart @@ -3,6 +3,8 @@ import 'package:fusion/services/service_locator.dart'; import 'package:fusion/services/storage_service.dart'; import 'package:intl/intl.dart'; import 'package:http/http.dart' as http; +import 'dart:convert'; +import '../View_File/view_outboxfile.dart'; // Assuming you have a model class for message or file data class OutboxMessage { @@ -22,62 +24,74 @@ class OutboxMessage { } class OutboxPage extends StatefulWidget { + final String username; + OutboxPage({required this.username}); @override _OutboxPageState createState() => _OutboxPageState(); } class _OutboxPageState extends State { final _designationController = TextEditingController(); - // Example data for two outbox messages - Future _submitForm() async { - try { - // Validate required fields - if (_designationController.text?.isEmpty == true) { - throw Exception('Designation required.'); - } - - // Retrieve token securely (replace with appropriate mechanism) + List fileIDsList = []; + List jsonData = []; + String ?errorMessage; - 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': '21BCS013', - 'designation': _designationController.text, - 'src_module': 'filetracking', - }; - - // Construct URL with encoded query parameters - final Uri url = Uri.http('10.0.2.2:8000', '/filetracking/api/outbox/', queryParams); - - final client = http.Client(); - - // Make GET request - final response = await client.get(url, headers: headers); - - // Handle response - if (response.statusCode == 200) { - // Assuming the response body directly contains inbox data (no parsing needed) - final outboxData = response.body; - print(outboxData); // Print the inbox data for debugging or logging - // Use the inboxData to display or process the fetched inbox files in your UI - } else { - // Handle error (e.g., print error message) - print('Error fetching inbox data: ${response.statusCode}'); + Future _submitForm() async { + try { + // Validate required fields + 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/outbox/', 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); + 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 { + // Handle error (e.g., print error message) + print('Error fetching outbox data: ${response.statusCode}'); + setState(() { + errorMessage = 'Invalid designation';}); + } + } catch (e) { + // Handle other exceptions (e.g., network errors, token errors) + print('An error occurred: $e'); } - } catch (e) { - // Handle other exceptions (e.g., network errors, token errors) - print('An error occurred: $e'); } -} @override Widget build(BuildContext context) { @@ -89,15 +103,12 @@ class _OutboxPageState extends State { padding: EdgeInsets.all(16.0), child: Column( children: [ - - // Divider Divider(thickness: 1.0, color: Colors.grey[300]), // Form content Column( children: [ - // Title field (require // Designation field TextField( controller: _designationController, @@ -112,15 +123,49 @@ class _OutboxPageState extends State { children: [ ElevatedButton( onPressed: () async { - await _submitForm( - - ); - }, - child: Text('View'), - ), + 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'), + Row( + children: [ + TextButton( + onPressed: () { + // Handle view button pressed + Navigator.push( context, + MaterialPageRoute( + builder: (context) => MessageDetailPage(messageDetails: jsonData[fileIDsList.indexOf(fileID)]), + ), + ); + }, + child: Text('View'), + ), + SizedBox(width: 8), + + ], + ), + ], + ), + ); + }).toList(), ), ], ), @@ -128,4 +173,3 @@ class _OutboxPageState extends State { ); } } - diff --git a/lib/screens/FileTracking/fts/fts.dart b/lib/screens/FileTracking/fts/fts.dart index 7d7db18b..e13ff4e6 100644 --- a/lib/screens/FileTracking/fts/fts.dart +++ b/lib/screens/FileTracking/fts/fts.dart @@ -2,11 +2,11 @@ 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/View_inbox/view_inbox.dart'; -import 'package:fusion/screens/FileTracking/View_outbox/view_outbox.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'; +import 'package:shared_preferences/shared_preferences.dart'; class UserService { Future getUsername() async { @@ -19,7 +19,6 @@ class UserService { class RoundedListView extends StatelessWidget { @override Widget build(BuildContext context) { - Future getUsername() async { final userService = UserService(); final username = await userService.getUsername(); @@ -31,28 +30,41 @@ class RoundedListView extends StatelessWidget { drawer: SideDrawer(), body: Column( children: [ - // User profile view (modify as needed) - Padding( - padding: const EdgeInsets.all(16.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - CircleAvatar( - backgroundImage: NetworkImage("https://picsum.photos/id/237/200/300"), - radius: 30.0, - ), - SizedBox(width: 16.0), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text("PP", style: TextStyle(fontSize: 18.0,color: Colors.grey, fontWeight: FontWeight.bold, decoration: TextDecoration.none, -)), - Text("21BCS329", style: TextStyle(fontSize: 14.0, color: Colors.grey, decoration: TextDecoration.none, -)), - ], - ), - ], - ), + 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 @@ -63,7 +75,7 @@ class RoundedListView extends StatelessWidget { shrinkWrap: true, // Prevent scrolling itemCount: 5, itemBuilder: (context, index) { - final items = ['Compose File', 'Drafts', 'Track File', 'Outbox', 'Inbox']; + final items = ['Compose File', 'Drafts', 'Archive', 'Outbox', 'Inbox']; final paths = [ '/create_file', // Path for Compose File '/view_drafts', // Path for Drafts @@ -84,46 +96,40 @@ class RoundedListView extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - items[index]), + Text(items[index]), IconButton( icon: Icon(Icons.chevron_right), onPressed: () async { final username = await getUsername(); - print(username); switch (paths[index]) { case '/create_file': Navigator.push( context, - MaterialPageRoute(builder: (context) => CreateFilePage()), - + MaterialPageRoute(builder: (context) => CreateFilePage(username: username)), ); break; case '/view_drafts': Navigator.push( context, - MaterialPageRoute(builder: (context) => DraftsPage()), + MaterialPageRoute(builder: (context) => DraftsPage(username: username)), ); break; case '/view_inbox': - // Navigate to your "Inbox" page here - Navigator.push( + Navigator.push( context, MaterialPageRoute(builder: (context) => InboxPage(username: username)), ); break; case '/track_file': - // Navigate to your "Track File" page here Navigator.push( context, MaterialPageRoute(builder: (context) => FileTrackingPage()), ); break; case '/view_outbox': - // Navigate to your "Outbox" page here - Navigator.push( + Navigator.push( context, - MaterialPageRoute(builder: (context) => OutboxPage()), + MaterialPageRoute(builder: (context) => OutboxPage(username: username)), ); break; } From d329bf366a06f21c4acea0b74852b8a843557da7 Mon Sep 17 00:00:00 2001 From: Praneki <97080887+PraneGIT@users.noreply.github.com> Date: Wed, 17 Apr 2024 21:51:24 +0530 Subject: [PATCH 07/10] Gad 5 to main (#162) * Ui Changes and Added functionality of designation * Updated kserverLink,kAuthUrl and gradle ver (7.4.2) --------- Co-authored-by: TusharGupta03 --- android/build.gradle | 2 +- lib/Components/appBar2.dart | 106 +++++ lib/Components/bottom_navigation_bar.dart | 148 +++++++ lib/Components/side_drawer2.dart | 264 ++++++++++++ lib/DesignationProvider.dart | 12 + lib/api.dart | 1 + lib/main.dart | 12 +- .../DashboardComponents/announcement.dart | 200 +++++++++ .../DashboardComponents/cardItems.dart | 76 ++-- .../DashboardComponents/news.dart | 201 +++++++++ .../DashboardComponents/notify.dart | 152 +++++++ lib/screens/LoginandDashboard/dashboard.dart | 389 +++++++++++------- lib/screens/LoginandDashboard/login_page.dart | 155 ++++--- lib/services/appBar_services.dart | 14 + lib/services/dashboard_service.dart | 51 ++- lib/services/login_service.dart | 16 +- lib/services/storage_service.dart | 14 +- 17 files changed, 1543 insertions(+), 270 deletions(-) create mode 100644 lib/Components/appBar2.dart create mode 100644 lib/Components/bottom_navigation_bar.dart create mode 100644 lib/Components/side_drawer2.dart create mode 100644 lib/DesignationProvider.dart create mode 100644 lib/screens/LoginandDashboard/DashboardComponents/announcement.dart create mode 100644 lib/screens/LoginandDashboard/DashboardComponents/news.dart create mode 100644 lib/screens/LoginandDashboard/DashboardComponents/notify.dart create mode 100644 lib/services/appBar_services.dart diff --git a/android/build.gradle b/android/build.gradle index a78be8cb..881e7e55 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } 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_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 73d1c6bc..1f76a52b 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -21,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..4cd5af87 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'; @@ -60,9 +63,9 @@ class MyApp extends StatelessWidget { @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 +84,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()), @@ -131,4 +137,4 @@ class MyApp extends StatelessWidget { ), ); } -} +} \ No newline at end of file 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..aff38280 100644 --- a/lib/screens/LoginandDashboard/login_page.dart +++ b/lib/screens/LoginandDashboard/login_page.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:fusion/constants.dart'; @@ -10,7 +9,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 +24,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 +73,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 +121,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), @@ -102,7 +150,8 @@ class _LoginPageState extends State { style: TextStyle( color: Colors.white, fontWeight: FontWeight.bold, - fontSize: 14.0, + fontSize: 22.0, + ), ), ), @@ -124,13 +173,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 +193,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 +216,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/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 From 6caea6f51261118e3f51dc5399303ff99d644a25 Mon Sep 17 00:00:00 2001 From: aish0749 Date: Tue, 16 Apr 2024 14:36:43 +0530 Subject: [PATCH 08/10] fts without file upload functionality --- .../Archive_file/archive_file.dart | 139 ++++++++++++++++ .../FileTracking/Create_file/create_file.dart | 97 +++++------ .../Forward_file/forward_file.dart | 109 ++++++++----- .../FileTracking/View_File/view_archive.dart | 79 +++++++++ .../View_File/view_draftfile.dart | 104 +++++++++++- .../View_File/view_inboxfile.dart | 86 ++++++++-- .../View_File/view_outboxfile.dart | 2 +- .../FileTracking/View_drafts/view_drafts.dart | 19 ++- .../FileTracking/View_inbox/view_inbox.dart | 153 ++++++++---------- .../FileTracking/View_outbox/view_outbox.dart | 76 +++------ lib/screens/FileTracking/fts/fts.dart | 15 +- 11 files changed, 627 insertions(+), 252 deletions(-) create mode 100644 lib/screens/FileTracking/View_File/view_archive.dart diff --git a/lib/screens/FileTracking/Archive_file/archive_file.dart b/lib/screens/FileTracking/Archive_file/archive_file.dart index e69de29b..e16d8a69 100644 --- a/lib/screens/FileTracking/Archive_file/archive_file.dart +++ 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)]), + ), + ); + }, + child: Text('View'), + ) + ], + ), + ], + ), + ); + }).toList(), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/FileTracking/Create_file/create_file.dart b/lib/screens/FileTracking/Create_file/create_file.dart index 10c14743..76c11c37 100644 --- a/lib/screens/FileTracking/Create_file/create_file.dart +++ b/lib/screens/FileTracking/Create_file/create_file.dart @@ -6,6 +6,7 @@ import 'package:fusion/services/storage_service.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:file_picker/file_picker.dart'; class CreateFilePage extends StatefulWidget { final String username; @@ -23,59 +24,63 @@ class _CreateFilePageState extends State { final _designationController = TextEditingController(); Future _submitForm() async { - try { - // Validate required fields - if (_titleController.text?.isEmpty == true || - _descriptionController.text?.isEmpty == true) { - throw Exception('Title and description are required.'); - } + try { + // Validate required fields + if (_titleController.text?.isEmpty == true || + _descriptionController.text?.isEmpty == true) { + throw Exception('Title and description are required.'); + } - // Prepare headers - var storageService = locator(); - if (storageService.userInDB?.token == null) { - throw Exception('Token Error'); - } + // 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' - }; + Map headers = { + 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), + 'Content-Type': 'application/json' + }; - // Prepare request body - final data = jsonEncode({ - 'subject': _titleController.text, - 'designation': _sendAsController.text, - 'receiver_username': _forwardToController.text, - 'receiver_designation': _designationController.text, - 'description': _descriptionController.text, - }); + // Prepare request body + final data = jsonEncode({ + 'subject': _titleController.text, + 'designation': _sendAsController.text, + 'receiver_username': _forwardToController.text, + 'receiver_designation': _designationController.text, + 'description': _descriptionController.text, + }); - // Make POST request - final response = await http.post( - Uri.http('10.0.2.2:8000', '/filetracking/api/file/'), - headers: headers, - body: data, + // Make POST request + final response = await http.post( + Uri.http('10.0.2.2:8000', '/filetracking/api/file/'), + headers: headers, + body: data, + ); + + // Handle response + if (response.statusCode == HttpStatus.created) { + // Show success message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('File sent'), + backgroundColor: Colors.green, + ), ); - // Handle response - if (response.statusCode == HttpStatus.created) { - // Show success message - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('File sent'), - backgroundColor: Colors.green, - ), - ); - } 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'); + // Close current page and go back to outbox page + 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 _draftForm() async { try { @@ -99,6 +104,7 @@ class _CreateFilePageState extends State { print(_titleController.text); final data = jsonEncode({ 'subject': _titleController.text, + 'description': _descriptionController.text, 'uploader': widget.username, 'uploader_designation': _sendAsController.text, 'src_module': 'filetracking' @@ -119,6 +125,7 @@ class _CreateFilePageState extends State { backgroundColor: Colors.green, ), ); + Navigator.pop(context); } else { // Handle other status codes (e.g., show error message) final errorMessage = await _parseErrorMessage(response); diff --git a/lib/screens/FileTracking/Forward_file/forward_file.dart b/lib/screens/FileTracking/Forward_file/forward_file.dart index 5f379a5d..2abbd024 100644 --- a/lib/screens/FileTracking/Forward_file/forward_file.dart +++ b/lib/screens/FileTracking/Forward_file/forward_file.dart @@ -1,24 +1,32 @@ 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 StatelessWidget { +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{ - // Your API endpoint for forwarding the file - var storageService = locator(); + try { + var storageService = locator(); if (storageService.userInDB?.token == null) { throw Exception('Token Error'); } @@ -27,38 +35,49 @@ class ForwardFilePage extends StatelessWidget { 'Authorization': 'Token ' + (storageService.userInDB?.token ?? ""), 'Content-Type': 'application/json' }; - // Prepare the request body - final data = jsonEncode({ - 'receiver': receiver, - 'receiver_designation': receiverDesignation, - 'remarks': remarks, - }); - final file_id=fileDetails['id']; - // Make the POST request - print(data); - final client = http.Client(); - client.post( - Uri.http('10.0.2.2:8000', '/filetracking/api/forwardfile/$file_id/'), - headers: headers, - body: data, - ).then((response) { - print(response.statusCode); - }).catchError((error) { - print(error); - }); - // Check if the request was successful + + 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) { - // Handle network errors print('Failed to forward file. Error: $error'); } } @override Widget build(BuildContext context) { - String receiver = ''; // Receiver username - String receiverDesignation = ''; // Receiver designation - String remarks = ''; // Remarks entered by the user - return Scaffold( appBar: AppBar( title: Text('Forward File'), @@ -85,7 +104,7 @@ class ForwardFilePage extends StatelessWidget { fontWeight: FontWeight.bold, ), ), - subtitle: Text(fileDetails['subject']), + subtitle: Text(widget.fileDetails['subject']), ), ListTile( title: Text( @@ -94,7 +113,7 @@ class ForwardFilePage extends StatelessWidget { fontWeight: FontWeight.bold, ), ), - subtitle: Text(fileDetails['uploader']), + subtitle: Text(widget.fileDetails['uploader']), ), ListTile( title: Text( @@ -103,10 +122,11 @@ class ForwardFilePage extends StatelessWidget { fontWeight: FontWeight.bold, ), ), - subtitle: Text(fileDetails['sent_by_user']), + 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(), @@ -117,6 +137,7 @@ class ForwardFilePage extends StatelessWidget { ), SizedBox(height: 16), TextField( + enabled: !forwarded, // Disable text field if file has been forwarded decoration: InputDecoration( labelText: 'Receiver Username', border: OutlineInputBorder(), @@ -127,6 +148,7 @@ class ForwardFilePage extends StatelessWidget { ), SizedBox(height: 16), TextField( + enabled: !forwarded, // Disable text field if file has been forwarded decoration: InputDecoration( labelText: 'Receiver Designation', border: OutlineInputBorder(), @@ -137,13 +159,16 @@ class ForwardFilePage extends StatelessWidget { ), SizedBox(height: 16), ElevatedButton( - onPressed: () { - forwardFile( - receiver: receiver, - receiverDesignation: receiverDesignation, - remarks: remarks, - ); - }, + onPressed: !forwarded + ? () { + forwardFile( + receiver: receiver, + receiverDesignation: receiverDesignation, + remarks: remarks, + context: context, + ); + } + : null, // Disable button if file has been forwarded child: Text('Forward'), ), ], @@ -151,4 +176,4 @@ class ForwardFilePage extends StatelessWidget { ), ); } -} \ No newline at end of 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..36018198 --- /dev/null +++ b/lib/screens/FileTracking/View_File/view_archive.dart @@ -0,0 +1,79 @@ +import 'package:flutter/material.dart'; + +class MessageDetailPage extends StatelessWidget { + final Map messageDetails; + + const MessageDetailPage({Key? key, required this.messageDetails}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Archived 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( + 'Message Details', + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + ), + ), + ), + ListTile( + title: Text( + 'ID', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(messageDetails['id'].toString()), + ), + ListTile( + title: Text( + 'Subject', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(messageDetails['subject']), + ), + ListTile( + title: Text( + 'Description', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(messageDetails['description']), + ), + ListTile( + title: Text( + 'Upload Date', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(messageDetails['upload_date']), + ), + ListTile( + title: Text( + 'Uploader', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text(messageDetails['uploader']), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screens/FileTracking/View_File/view_draftfile.dart b/lib/screens/FileTracking/View_File/view_draftfile.dart index 0bc2ece2..693d7765 100644 --- a/lib/screens/FileTracking/View_File/view_draftfile.dart +++ b/lib/screens/FileTracking/View_File/view_draftfile.dart @@ -1,15 +1,88 @@ 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 'dart:io'; class ViewDraftFilePage extends StatelessWidget { final Map draftDetails; + final Function(String) onDelete; - const ViewDraftFilePage({Key? key, required this.draftDetails}) : super(key: key); + const ViewDraftFilePage({Key? key, required this.draftDetails, required this.onDelete}) : super(key: key); @override Widget build(BuildContext context) { + String receiver = ''; // Receiver username + String receiverDesignation = ''; // Receiver designation + + 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' + }; + + // Prepare request body + final data = jsonEncode({ + 'subject': draftDetails['subject'], + 'designation': 'student', + 'receiver_username': receiver, + 'receiver_designation': receiverDesignation, + 'description': draftDetails['description'], + }); + // Make HTTP POST request to send the draft + final response = await http.post( + Uri.http('10.0.2.2:8000', '/filetracking/api/file/'), + headers: headers, + body: data, + ); + + // 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, + ), + ); + + // Close current page and go back to the previous screen + Navigator.pop(context); + } 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, + ), + ); + } + } + return Scaffold( appBar: AppBar( - title: Text('Draft Details'), + title: Text('Draft File Details'), ), body: SingleChildScrollView( padding: EdgeInsets.all(16.0), @@ -53,6 +126,33 @@ class ViewDraftFilePage extends StatelessWidget { ), subtitle: Text(draftDetails['description'] ?? 'No description available'), ), + SizedBox(height: 16), + TextField( + decoration: InputDecoration( + labelText: 'Receiver Username', + border: OutlineInputBorder(), + ), + onChanged: (value) { + receiver = value; + }, + ), + SizedBox(height: 16), + TextField( + decoration: InputDecoration( + labelText: 'Receiver Designation', + border: OutlineInputBorder(), + ), + onChanged: (value) { + receiverDesignation = value; + }, + ), + ElevatedButton( + onPressed: () { + _submitForm(context, receiver, receiverDesignation); + onDelete(draftDetails['id'].toString()); + }, + child: Text('Send'), + ), ], ), ), diff --git a/lib/screens/FileTracking/View_File/view_inboxfile.dart b/lib/screens/FileTracking/View_File/view_inboxfile.dart index 92d2aafd..5b6fd388 100644 --- a/lib/screens/FileTracking/View_File/view_inboxfile.dart +++ b/lib/screens/FileTracking/View_File/view_inboxfile.dart @@ -1,28 +1,21 @@ -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 'dart:convert'; +import '../Forward_file/forward_file.dart'; class MessageDetailPage extends StatefulWidget { final Map messageDetails; + final String username; - const MessageDetailPage({Key? key, required this.messageDetails}) : super(key: key); + const MessageDetailPage({Key? key, required this.messageDetails, required this.username}) : super(key: key); - @override _MessageDetailPageState createState() => _MessageDetailPageState(); } class _MessageDetailPageState extends State { String? historyResponse; - - @override - void initState() { - super.initState(); - fetchHistory(); - } - Future fetchHistory() async { try { var storageService = locator(); @@ -50,11 +43,58 @@ class _MessageDetailPageState extends State { } } + 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(); + } + @override Widget build(BuildContext context) { + final bool isUploader = widget.messageDetails['uploader'] == widget.username; + return Scaffold( appBar: AppBar( - title: Text('File Details'), + title: Text('InboxFile Details'), ), body: SingleChildScrollView( padding: EdgeInsets.all(16.0), @@ -126,6 +166,28 @@ class _MessageDetailPageState extends State { subtitle: Text(widget.messageDetails['uploader']), ), SizedBox(height: 16), + // Conditional rendering of buttons based on uploader + 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'), + ), if (historyResponse != null) Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/screens/FileTracking/View_File/view_outboxfile.dart b/lib/screens/FileTracking/View_File/view_outboxfile.dart index bc797ee6..4e5ea7b9 100644 --- a/lib/screens/FileTracking/View_File/view_outboxfile.dart +++ b/lib/screens/FileTracking/View_File/view_outboxfile.dart @@ -9,7 +9,7 @@ class MessageDetailPage extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('File Details'), + title: Text('Outbox File Details'), ), body: SingleChildScrollView( padding: EdgeInsets.all(16.0), diff --git a/lib/screens/FileTracking/View_drafts/view_drafts.dart b/lib/screens/FileTracking/View_drafts/view_drafts.dart index 912167aa..1a506128 100644 --- a/lib/screens/FileTracking/View_drafts/view_drafts.dart +++ b/lib/screens/FileTracking/View_drafts/view_drafts.dart @@ -80,6 +80,14 @@ class _DraftsPageState extends State { } } + 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(); @@ -177,11 +185,14 @@ class _DraftsPageState extends State { onPressed: () { // Handle view button pressed Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ViewDraftFilePage(draftDetails: jsonData[fileIDsList.indexOf(fileID)]), + context, + MaterialPageRoute( + builder: (context) => ViewDraftFilePage( + draftDetails: jsonData[fileIDsList.indexOf(fileID)], + onDelete: _deleteDraftAndUpdateList, ), - ); + ), + ); }, child: Text('View'), ), diff --git a/lib/screens/FileTracking/View_inbox/view_inbox.dart b/lib/screens/FileTracking/View_inbox/view_inbox.dart index d1e5020e..4dbce340 100644 --- a/lib/screens/FileTracking/View_inbox/view_inbox.dart +++ b/lib/screens/FileTracking/View_inbox/view_inbox.dart @@ -98,96 +98,83 @@ class _InboxPageState extends State { } } - @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', - ), + @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, + // 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: [ - ElevatedButton( - onPressed: () async { - await _submitForm(); + 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'), ), ], ), - ], - ), - 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: () { - print(jsonData[fileIDsList.indexOf(fileID)]); - // Handle view button pressed - Navigator.push( context, - MaterialPageRoute( - builder: (context) => MessageDetailPage(messageDetails: jsonData[fileIDsList.indexOf(fileID)]), - ), - ); - }, - child: Text('View'), - ), - SizedBox(width: 8), - TextButton( - onPressed: () { - print(jsonData[fileIDsList.indexOf(fileID)]); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ForwardFilePage(fileDetails: jsonData[fileIDsList.indexOf(fileID)]), - ), - ); - }, - child: Text('Forward'), - ), - ], - ), - ], - ), - ); - }).toList(), - ), - ], - ), + ); + }).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 index c824aae2..204ddf2d 100644 --- a/lib/screens/FileTracking/View_outbox/view_outbox.dart +++ b/lib/screens/FileTracking/View_outbox/view_outbox.dart @@ -1,31 +1,15 @@ 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:intl/intl.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import '../View_File/view_outboxfile.dart'; -// Assuming you have a model class for message or file data -class OutboxMessage { - final String recipient; - final String subject; - final String snippet; - final DateTime sentDate; - final String? messageType; // Optional for message type (e.g., email, chat) - - OutboxMessage({ - required this.recipient, - required this.subject, - required this.snippet, - required this.sentDate, - this.messageType, - }); -} - class OutboxPage extends StatefulWidget { final String username; OutboxPage({required this.username}); + @override _OutboxPageState createState() => _OutboxPageState(); } @@ -34,11 +18,10 @@ class _OutboxPageState extends State { final _designationController = TextEditingController(); List fileIDsList = []; List jsonData = []; - String ?errorMessage; + String? errorMessage; Future _submitForm() async { try { - // Validate required fields if (_designationController.text?.isEmpty == true) { throw Exception('Designation required.'); } @@ -53,48 +36,42 @@ class _OutboxPageState extends State { '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/outbox/', 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); - 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; + errorMessage = null; }); } else { - // Handle error (e.g., print error message) print('Error fetching outbox data: ${response.statusCode}'); setState(() { - errorMessage = 'Invalid designation';}); + errorMessage = 'Invalid designation'; + }); } } catch (e) { - // Handle other exceptions (e.g., network errors, token errors) print('An error occurred: $e'); } } @override Widget build(BuildContext context) { + var storageService = locator(); + var currentUser = widget.username; + return Scaffold( appBar: AppBar( title: Text('Outbox'), @@ -103,21 +80,15 @@ class _OutboxPageState extends State { 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: [ @@ -131,12 +102,11 @@ class _OutboxPageState extends State { ), ], ), - if (errorMessage != null) + if (errorMessage != null) Text( errorMessage!, style: TextStyle(color: Colors.red), ), - // Display file IDs and buttons Column( children: fileIDsList.map((fileID) { return Padding( @@ -145,22 +115,16 @@ class _OutboxPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('File ID: $fileID'), - Row( - children: [ - TextButton( - onPressed: () { - // Handle view button pressed - Navigator.push( context, - MaterialPageRoute( - builder: (context) => MessageDetailPage(messageDetails: jsonData[fileIDsList.indexOf(fileID)]), - ), - ); - }, - child: Text('View'), - ), - SizedBox(width: 8), - - ], + TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MessageDetailPage(messageDetails: jsonData[fileIDsList.indexOf(fileID)]), + ), + ); + }, + child: Text('View'), ), ], ), diff --git a/lib/screens/FileTracking/fts/fts.dart b/lib/screens/FileTracking/fts/fts.dart index e13ff4e6..8af5b2af 100644 --- a/lib/screens/FileTracking/fts/fts.dart +++ b/lib/screens/FileTracking/fts/fts.dart @@ -3,6 +3,7 @@ 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'; @@ -77,11 +78,11 @@ class RoundedListView extends StatelessWidget { itemBuilder: (context, index) { final items = ['Compose File', 'Drafts', 'Archive', 'Outbox', 'Inbox']; final paths = [ - '/create_file', // Path for Compose File - '/view_drafts', // Path for Drafts - '/track_file', // Path for Track File - '/view_outbox', // Path for Outbox - '/view_inbox', // Path for Inbox + '/create_file', + '/view_drafts', + '/archive_file', + '/view_outbox', + '/view_inbox', ]; return Padding( @@ -120,10 +121,10 @@ class RoundedListView extends StatelessWidget { MaterialPageRoute(builder: (context) => InboxPage(username: username)), ); break; - case '/track_file': + case '/archive_file': Navigator.push( context, - MaterialPageRoute(builder: (context) => FileTrackingPage()), + MaterialPageRoute(builder: (context) => ArchivePage(username: username,)), ); break; case '/view_outbox': From fbe41b7800bc157f263c99c191c527cdcf1ef247 Mon Sep 17 00:00:00 2001 From: aish0749 Date: Sat, 27 Apr 2024 19:44:40 +0530 Subject: [PATCH 09/10] before dashboard integration --- .../Archive_file/archive_file.dart | 2 +- .../FileTracking/Create_file/create_file.dart | 177 ++++++------ .../FileTracking/View_File/view_archive.dart | 192 ++++++++++--- .../View_File/view_draftfile.dart | 198 ++++++++----- .../View_File/view_inboxfile.dart | 268 ++++++++++-------- .../View_File/view_outboxfile.dart | 199 ++++++++++--- .../FileTracking/View_inbox/view_inbox.dart | 2 +- .../FileTracking/View_outbox/view_outbox.dart | 3 +- pubspec.yaml | 2 +- 9 files changed, 674 insertions(+), 369 deletions(-) diff --git a/lib/screens/FileTracking/Archive_file/archive_file.dart b/lib/screens/FileTracking/Archive_file/archive_file.dart index e16d8a69..af27b4f7 100644 --- a/lib/screens/FileTracking/Archive_file/archive_file.dart +++ b/lib/screens/FileTracking/Archive_file/archive_file.dart @@ -118,7 +118,7 @@ class _ArchivePageState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => MessageDetailPage(messageDetails: jsonData[fileIDsList.indexOf(fileID)]), + builder: (context) => MessageDetailPage(messageDetails: jsonData[fileIDsList.indexOf(fileID)], username: '',), ), ); }, diff --git a/lib/screens/FileTracking/Create_file/create_file.dart b/lib/screens/FileTracking/Create_file/create_file.dart index 76c11c37..09daddcc 100644 --- a/lib/screens/FileTracking/Create_file/create_file.dart +++ b/lib/screens/FileTracking/Create_file/create_file.dart @@ -1,86 +1,92 @@ +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; -import 'dart:convert'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:file_picker/file_picker.dart'; class CreateFilePage extends StatefulWidget { final String username; CreateFilePage({required this.username}); + @override _CreateFilePageState createState() => _CreateFilePageState(); } class _CreateFilePageState extends State { - // Form controllers final _titleController = TextEditingController(); final _descriptionController = TextEditingController(); final _sendAsController = TextEditingController(); final _forwardToController = TextEditingController(); final _designationController = TextEditingController(); - Future _submitForm() async { - try { - // Validate required fields - if (_titleController.text?.isEmpty == true || - _descriptionController.text?.isEmpty == true) { - throw Exception('Title and description are 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 - final data = jsonEncode({ - 'subject': _titleController.text, - 'designation': _sendAsController.text, - 'receiver_username': _forwardToController.text, - 'receiver_designation': _designationController.text, - 'description': _descriptionController.text, - }); - - // Make POST request - final response = await http.post( - Uri.http('10.0.2.2:8000', '/filetracking/api/file/'), - headers: headers, - body: data, - ); - - // Handle response - if (response.statusCode == HttpStatus.created) { - // Show success message - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('File sent'), - backgroundColor: Colors.green, - ), - ); + File? _attachedFile; - // Close current page and go back to outbox page - Navigator.pop(context); - } else { - // Handle other status codes (e.g., show error message) - final errorMessage = await _parseErrorMessage(response); - print('Error: $errorMessage'); + 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'); } - } catch (e) { - // Handle other exceptions - print('An error occurred: $e'); } -} - Future _draftForm() async { try { @@ -137,9 +143,7 @@ class _CreateFilePageState extends State { } } - Future _parseErrorMessage(http.Response response) async { - // Implement your error parsing logic here (e.g., from JSON response body) try { final decodedResponse = jsonDecode(response.body); return decodedResponse['message'] ?? 'Unknown error'; @@ -158,13 +162,9 @@ class _CreateFilePageState extends State { padding: EdgeInsets.all(16.0), child: Column( children: [ - // Divider Divider(thickness: 1.0, color: Colors.grey[300]), - - // Form content Column( children: [ - // Title field (required) TextField( controller: _titleController, decoration: InputDecoration( @@ -173,71 +173,54 @@ class _CreateFilePageState extends State { _titleController.text.isEmpty ? 'Required' : null, ), ), - - // Description field (required) TextField( controller: _descriptionController, decoration: InputDecoration( labelText: 'Description*', ), ), - - // Attach field Row( mainAxisAlignment: MainAxisAlignment.start, children: [ - // Text 'Attach File' Text('Attach File'), - SizedBox(width: 16.0), // Add spacing between text and button - - // ElevatedButton with informative text + SizedBox(width: 16.0), ElevatedButton( - onPressed: () { - showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text('File Attachment'), - content: Text( - 'File attachment is not currently supported in this web app.'), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: Text('OK'), - ), - ], - ), - ); + 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'), // Changed text to "Browse" + child: Text('Browse'), ), + SizedBox(width: 10), + Text(_attachedFile?.path.split('/').last ?? ''), ], ), - - // Remarks field TextField( controller: _sendAsController, decoration: InputDecoration( labelText: 'Send as', ), ), - - // Forward to field TextField( controller: _forwardToController, decoration: InputDecoration( labelText: 'Forward To', ), ), - - // Designation field TextField( controller: _designationController, decoration: InputDecoration( labelText: 'Receiver\'s Designation', ), ), - - // Send button Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ diff --git a/lib/screens/FileTracking/View_File/view_archive.dart b/lib/screens/FileTracking/View_File/view_archive.dart index 36018198..63e039c5 100644 --- a/lib/screens/FileTracking/View_File/view_archive.dart +++ b/lib/screens/FileTracking/View_File/view_archive.dart @@ -1,15 +1,73 @@ 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 StatelessWidget { +class MessageDetailPage extends StatefulWidget { final Map messageDetails; + final String username; - const MessageDetailPage({Key? key, required this.messageDetails}) : super(key: key); + 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('Archived File Details'), + title: Text('Archive Details'), ), body: SingleChildScrollView( padding: EdgeInsets.all(16.0), @@ -26,54 +84,100 @@ class MessageDetailPage extends StatelessWidget { ), ), ), - ListTile( - title: Text( - 'ID', - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text(messageDetails['id'].toString()), - ), - ListTile( - title: Text( - 'Subject', - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text(messageDetails['subject']), - ), - ListTile( - title: Text( - 'Description', - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text(messageDetails['description']), - ), - ListTile( - title: Text( - 'Upload Date', - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text(messageDetails['upload_date']), - ), - ListTile( - title: Text( - 'Uploader', + 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, ), ), - subtitle: Text(messageDetails['uploader']), - ), + 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 index 693d7765..cdc5c977 100644 --- a/lib/screens/FileTracking/View_File/view_draftfile.dart +++ b/lib/screens/FileTracking/View_File/view_draftfile.dart @@ -1,85 +1,109 @@ +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; -import 'dart:convert'; -import 'dart:io'; -class ViewDraftFilePage extends StatelessWidget { +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 - Widget build(BuildContext context) { - String receiver = ''; // Receiver username - String receiverDesignation = ''; // Receiver designation - - 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' - }; - - // Prepare request body - final data = jsonEncode({ - 'subject': draftDetails['subject'], - 'designation': 'student', - 'receiver_username': receiver, - 'receiver_designation': receiverDesignation, - 'description': draftDetails['description'], - }); - // Make HTTP POST request to send the draft - final response = await http.post( - Uri.http('10.0.2.2:8000', '/filetracking/api/file/'), - headers: headers, - body: data, - ); - - // 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, - ), - ); - - // Close current page and go back to the previous screen - Navigator.pop(context); - } 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, - ), - ); - } + _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'), @@ -106,7 +130,7 @@ class ViewDraftFilePage extends StatelessWidget { fontWeight: FontWeight.bold, ), ), - subtitle: Text(draftDetails['id'].toString()), + subtitle: Text(widget.draftDetails['id'].toString()), ), ListTile( title: Text( @@ -115,7 +139,7 @@ class ViewDraftFilePage extends StatelessWidget { fontWeight: FontWeight.bold, ), ), - subtitle: Text(draftDetails['subject'] ?? 'No subject available'), + subtitle: Text(widget.draftDetails['subject'] ?? 'No subject available'), ), ListTile( title: Text( @@ -124,16 +148,38 @@ class ViewDraftFilePage extends StatelessWidget { fontWeight: FontWeight.bold, ), ), - subtitle: Text(draftDetails['description'] ?? 'No description available'), + 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) { - receiver = value; + setState(() { + receiver = value; + }); }, ), SizedBox(height: 16), @@ -143,13 +189,15 @@ class ViewDraftFilePage extends StatelessWidget { border: OutlineInputBorder(), ), onChanged: (value) { - receiverDesignation = value; + setState(() { + receiverDesignation = value; + }); }, ), + SizedBox(height: 16), ElevatedButton( onPressed: () { _submitForm(context, receiver, receiverDesignation); - onDelete(draftDetails['id'].toString()); }, child: Text('Send'), ), diff --git a/lib/screens/FileTracking/View_File/view_inboxfile.dart b/lib/screens/FileTracking/View_File/view_inboxfile.dart index 5b6fd388..a5af4e97 100644 --- a/lib/screens/FileTracking/View_File/view_inboxfile.dart +++ b/lib/screens/FileTracking/View_File/view_inboxfile.dart @@ -4,6 +4,7 @@ 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; @@ -16,6 +17,10 @@ class MessageDetailPage extends StatefulWidget { class _MessageDetailPageState extends State { String? historyResponse; + String? messageResponse; + String? receiver; + String? filename; + Future fetchHistory() async { try { var storageService = locator(); @@ -28,12 +33,24 @@ class _MessageDetailPageState extends State { 'Content-Type': 'application/json' }; - final Uri url = Uri.http('10.0.2.2:8000', '/filetracking/api/history/${widget.messageDetails['id']}'); + 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); - if (response.statusCode == 200) { + 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(() { - historyResponse = response.body; + 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}'); @@ -88,130 +105,157 @@ class _MessageDetailPageState extends State { fetchHistory(); } - @override - Widget build(BuildContext context) { - final bool isUploader = widget.messageDetails['uploader'] == widget.username; +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('InboxFile 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, - ), + 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, ), ), - ListTile( - title: Text( - 'ID', - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text(widget.messageDetails['id'].toString()), - ), - ListTile( - title: Text( - 'Subject', - style: TextStyle( - 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, ), - subtitle: Text(widget.messageDetails['subject']), ), - ListTile( - title: Text( - 'Description', - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text(widget.messageDetails['description']), + 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'), ), - ListTile( - title: Text( - 'Sent By', - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text(widget.messageDetails['sent_by_user']), + if (isUploader) // Show Archive button if uploader + ElevatedButton( + onPressed: () { + // Archive the file + _archiveFile(widget.messageDetails['id'].toString()); + }, + child: Text('Archive'), ), - ListTile( - title: Text( - 'Upload Date', - style: TextStyle( - fontWeight: FontWeight.bold, - ), + ], + ), + ), + ); +} + + +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, ), - subtitle: Text(widget.messageDetails['upload_date']), - ), - ListTile( - title: Text( - 'Uploader', - style: TextStyle( - fontWeight: FontWeight.bold, - ), + ) + else + Text( + 'Sent By: ${sender}', + style: TextStyle( + fontWeight: FontWeight.bold, ), - subtitle: Text(widget.messageDetails['uploader']), ), - SizedBox(height: 16), - // Conditional rendering of buttons based on uploader - 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'), - ), - if (historyResponse != null) - Column( + 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: [ - Text( - 'File History Response', - style: TextStyle( - fontSize: 18.0, - fontWeight: FontWeight.bold, + 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), - Text( - JsonEncoder.withIndent(' ').convert(jsonDecode(historyResponse!)), - style: TextStyle( - fontSize: 14.0, - fontFamily: 'Courier', - ), - ), + 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 index 4e5ea7b9..76902da2 100644 --- a/lib/screens/FileTracking/View_File/view_outboxfile.dart +++ b/lib/screens/FileTracking/View_File/view_outboxfile.dart @@ -1,15 +1,81 @@ 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 StatelessWidget { +class MessageDetailPage extends StatefulWidget { final Map messageDetails; + final String username; - const MessageDetailPage({Key? key, required this.messageDetails}) : super(key: key); + 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 File Details'), + title: Text('Outbox Details'), ), body: SingleChildScrollView( padding: EdgeInsets.all(16.0), @@ -26,54 +92,113 @@ class MessageDetailPage extends StatelessWidget { ), ), ), - ListTile( - title: Text( - 'ID', - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text(messageDetails['id'].toString()), - ), - ListTile( - title: Text( - 'Subject', - style: TextStyle( - 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'), ), - ), - subtitle: Text(messageDetails['subject']), - ), - ListTile( - title: Text( - 'Description', + SizedBox(height: 16), + Text( + 'History', style: TextStyle( + fontSize: 18.0, fontWeight: FontWeight.bold, ), ), - subtitle: Text(messageDetails['description']), - ), - ListTile( - title: Text( - 'Upload Date', + 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, ), - ), - subtitle: Text(messageDetails['upload_date']), - ), - ListTile( - title: Text( - 'Uploader', + ) + else + Text( + 'Sent By: ${sender}', style: TextStyle( fontWeight: FontWeight.bold, ), ), - subtitle: Text(messageDetails['uploader']), + 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_inbox/view_inbox.dart b/lib/screens/FileTracking/View_inbox/view_inbox.dart index 4dbce340..91bcb6df 100644 --- a/lib/screens/FileTracking/View_inbox/view_inbox.dart +++ b/lib/screens/FileTracking/View_inbox/view_inbox.dart @@ -153,7 +153,7 @@ Widget build(BuildContext context) { Text('File ID: $fileID'), TextButton( onPressed: () { - print(jsonData[fileIDsList.indexOf(fileID)]); + // print(jsonData[fileIDsList.indexOf(fileID)]); // Handle view button pressed Navigator.push( context, diff --git a/lib/screens/FileTracking/View_outbox/view_outbox.dart b/lib/screens/FileTracking/View_outbox/view_outbox.dart index 204ddf2d..c6e985cf 100644 --- a/lib/screens/FileTracking/View_outbox/view_outbox.dart +++ b/lib/screens/FileTracking/View_outbox/view_outbox.dart @@ -120,7 +120,8 @@ class _OutboxPageState extends State { Navigator.push( context, MaterialPageRoute( - builder: (context) => MessageDetailPage(messageDetails: jsonData[fileIDsList.indexOf(fileID)]), + builder: (context) => MessageDetailPage(messageDetails: jsonData[fileIDsList.indexOf(fileID)], + username: widget.username,), ), ); }, diff --git a/pubspec.yaml b/pubspec.yaml index 5c92b0ac..acafa3e8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,7 +34,7 @@ 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 From ffea4f8cdc1c8773181ff09b8ce532cf76b73034 Mon Sep 17 00:00:00 2001 From: Aishwarya Saxena <95125116+aish0749@users.noreply.github.com> Date: Sun, 29 Dec 2024 13:28:17 +0530 Subject: [PATCH 10/10] Create dart.yml --- .github/workflows/dart.yml | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/dart.yml 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