diff --git a/campus/bffs/attendance/api/attendance_client.bal b/campus/bffs/attendance/api/attendance_client.bal index 2c087302..152d90dd 100644 --- a/campus/bffs/attendance/api/attendance_client.bal +++ b/campus/bffs/attendance/api/attendance_client.bal @@ -302,10 +302,16 @@ log:printInfo("Formatted Response: " + formattedJson); json graphqlResponse = check self.graphqlClient->executeWithType(query, variables); return check performDataBinding(graphqlResponse, GetTotalAttendanceCountByParentOrgResponse); } - remote isolated function getDailyAttendanceSummaryReport(string from_date, string to_date, int parent_organization_id) returns GetDailyAttendanceSummaryReportResponse|graphql:ClientError { - string query = string `query getDailyAttendanceSummaryReport($parent_organization_id:Int!,$from_date:String!,$to_date:String!) {daily_attendance_summary_report(parent_organization_id:$parent_organization_id,from_date:$from_date,to_date:$to_date) {sign_in_date present_count late_count total_count present_attendance_percentage late_attendance_percentage}}`; - map variables = {"from_date": from_date, "to_date": to_date, "parent_organization_id": parent_organization_id}; + remote isolated function getDailyAttendanceSummaryReport(string from_date, string to_date, int organization_id, int avinya_type_id) returns GetDailyAttendanceSummaryReportResponse|graphql:ClientError { + string query = string `query getDailyAttendanceSummaryReport($organization_id:Int!,$avinya_type_id:Int!,$from_date:String!,$to_date:String!) {daily_attendance_summary_report(organization_id:$organization_id,avinya_type_id:$avinya_type_id,from_date:$from_date,to_date:$to_date) {sign_in_date present_count late_count total_count present_attendance_percentage late_attendance_percentage}}`; + map variables = {"from_date": from_date, "to_date": to_date, "organization_id": organization_id, "avinya_type_id": avinya_type_id}; json graphqlResponse = check self.graphqlClient->executeWithType(query, variables); return check performDataBinding(graphqlResponse, GetDailyAttendanceSummaryReportResponse); } + remote isolated function getOrganizationsByAvinyaType(int avinya_type) returns GetOrganizationsByAvinyaTypeResponse|graphql:ClientError { + string query = string `query getOrganizationsByAvinyaType($avinya_type:Int!) {organizations_by_avinya_type(avinya_type:$avinya_type) {id name {name_en} description organization_metadata {key_name value}}}`; + map variables = {"avinya_type": avinya_type}; + json graphqlResponse = check self.graphqlClient->executeWithType(query, variables); + return check performDataBinding(graphqlResponse, GetOrganizationsByAvinyaTypeResponse); + } } diff --git a/campus/bffs/attendance/api/service.bal b/campus/bffs/attendance/api/service.bal index 789e7e1a..49407e64 100644 --- a/campus/bffs/attendance/api/service.bal +++ b/campus/bffs/attendance/api/service.bal @@ -833,8 +833,8 @@ service / on new http:Listener(9091) { } } - resource function get daily_attendance_summary_report/[int parent_organization_id]/[string from_date]/[string to_date]() returns ActivityParticipantAttendanceSummary[]|error { - GetDailyAttendanceSummaryReportResponse|graphql:ClientError getDailyAttendanceSummaryReportResponse = globalDataClient->getDailyAttendanceSummaryReport(from_date,to_date,parent_organization_id); + resource function get daily_attendance_summary_report/[int organization_id]/[int avinya_type_id]/[string from_date]/[string to_date]() returns ActivityParticipantAttendanceSummary[]|error { + GetDailyAttendanceSummaryReportResponse|graphql:ClientError getDailyAttendanceSummaryReportResponse = globalDataClient->getDailyAttendanceSummaryReport(from_date,to_date,organization_id,avinya_type_id); if(getDailyAttendanceSummaryReportResponse is GetDailyAttendanceSummaryReportResponse) { ActivityParticipantAttendanceSummary[] activityParticipantAttendances = []; foreach var attendance_record in getDailyAttendanceSummaryReportResponse.daily_attendance_summary_report { @@ -856,4 +856,28 @@ service / on new http:Listener(9091) { ":: Detail: " + getDailyAttendanceSummaryReportResponse.detail().toString()); } } + + resource function get organizations_by_avinya_type/[int avinya_type_id]() returns Organization[]|error { + GetOrganizationsByAvinyaTypeResponse|graphql:ClientError getOrganizationsByAvinyaTypeResponse = globalDataClient->getOrganizationsByAvinyaType(avinya_type_id); + if(getOrganizationsByAvinyaTypeResponse is GetOrganizationsByAvinyaTypeResponse) { + Organization[] organizations = []; + foreach var organization_record in getOrganizationsByAvinyaTypeResponse.organizations_by_avinya_type { + Organization|error organization = organization_record.cloneWithType(Organization); + if(organization is Organization) { + organizations.push(organization); + } else { + log:printError("Error while processing Application record received", organization); + return error("Error while processing Application record received: " + organization.message() + + ":: Detail: " + organization.detail().toString()); + } + } + + return organizations; + + } else { + log:printError("Error while creating application", getOrganizationsByAvinyaTypeResponse); + return error("Error while creating application: " + getOrganizationsByAvinyaTypeResponse.message() + + ":: Detail: " + getOrganizationsByAvinyaTypeResponse.detail().toString()); + } + } } diff --git a/campus/bffs/attendance/api/types.bal b/campus/bffs/attendance/api/types.bal index d2cc9821..b29295c3 100644 --- a/campus/bffs/attendance/api/types.bal +++ b/campus/bffs/attendance/api/types.bal @@ -789,4 +789,19 @@ public type GetDailyAttendanceSummaryReportResponse record {| anydata? present_attendance_percentage; anydata? late_attendance_percentage; |}[] daily_attendance_summary_report; +|}; + +public type GetOrganizationsByAvinyaTypeResponse record {| + map __extensions?; + record {| + int? id; + record {| + string name_en; + |} name; + string? description; + record {| + string? key_name; + string? value; + |}[]? organization_metadata; + |}[] organizations_by_avinya_type; |}; \ No newline at end of file diff --git a/campus/bffs/attendance/graphql_client/activity.graphql b/campus/bffs/attendance/graphql_client/activity.graphql index 81bedeee..fef90a07 100644 --- a/campus/bffs/attendance/graphql_client/activity.graphql +++ b/campus/bffs/attendance/graphql_client/activity.graphql @@ -389,8 +389,8 @@ query getTotalAttendanceCountByParentOrg($parent_organization_id: Int!, $from_da } } -query getDailyAttendanceSummaryReport($parent_organization_id: Int!, $from_date: String!, $to_date: String!) { - daily_attendance_summary_report(parent_organization_id: $parent_organization_id, from_date: $from_date, to_date: $to_date) { +query getDailyAttendanceSummaryReport($organization_id: Int!,$avinya_type_id: Int!, $from_date: String!, $to_date: String!) { + daily_attendance_summary_report(organization_id: $organization_id, avinya_type_id: $avinya_type_id,from_date: $from_date, to_date: $to_date) { sign_in_date present_count late_count @@ -398,4 +398,18 @@ query getDailyAttendanceSummaryReport($parent_organization_id: Int!, $from_date: present_attendance_percentage late_attendance_percentage } +} + +query getOrganizationsByAvinyaType($avinya_type: Int!) { + organizations_by_avinya_type(avinya_type:$avinya_type) { + id + name{ + name_en + } + description + organization_metadata{ + key_name + value + } + } } \ No newline at end of file diff --git a/campus/bffs/attendance/graphql_client/graphql_client_cg_src/client.bal b/campus/bffs/attendance/graphql_client/graphql_client_cg_src/client.bal index 871734de..593a8912 100644 --- a/campus/bffs/attendance/graphql_client/graphql_client_cg_src/client.bal +++ b/campus/bffs/attendance/graphql_client/graphql_client_cg_src/client.bal @@ -207,10 +207,16 @@ public isolated client class GraphqlClient { json graphqlResponse = check self.graphqlClient->executeWithType(query, variables); return check performDataBinding(graphqlResponse, GetTotalAttendanceCountByParentOrgResponse); } - remote isolated function getDailyAttendanceSummaryReport(string from_date, string to_date, int parent_organization_id) returns GetDailyAttendanceSummaryReportResponse|graphql:ClientError { - string query = string `query getDailyAttendanceSummaryReport($parent_organization_id:Int!,$from_date:String!,$to_date:String!) {daily_attendance_summary_report(parent_organization_id:$parent_organization_id,from_date:$from_date,to_date:$to_date) {sign_in_date present_count late_count total_count present_attendance_percentage late_attendance_percentage}}`; - map variables = {"from_date": from_date, "to_date": to_date, "parent_organization_id": parent_organization_id}; + remote isolated function getDailyAttendanceSummaryReport(string from_date, string to_date, int organization_id, int avinya_type_id) returns GetDailyAttendanceSummaryReportResponse|graphql:ClientError { + string query = string `query getDailyAttendanceSummaryReport($organization_id:Int!,$avinya_type_id:Int!,$from_date:String!,$to_date:String!) {daily_attendance_summary_report(organization_id:$organization_id,avinya_type_id:$avinya_type_id,from_date:$from_date,to_date:$to_date) {sign_in_date present_count late_count total_count present_attendance_percentage late_attendance_percentage}}`; + map variables = {"from_date": from_date, "to_date": to_date, "organization_id": organization_id, "avinya_type_id": avinya_type_id}; json graphqlResponse = check self.graphqlClient->executeWithType(query, variables); return check performDataBinding(graphqlResponse, GetDailyAttendanceSummaryReportResponse); } + remote isolated function getOrganizationsByAvinyaType(int avinya_type) returns GetOrganizationsByAvinyaTypeResponse|graphql:ClientError { + string query = string `query getOrganizationsByAvinyaType($avinya_type:Int!) {organizations_by_avinya_type(avinya_type:$avinya_type) {id name {name_en} description organization_metadata {key_name value}}}`; + map variables = {"avinya_type": avinya_type}; + json graphqlResponse = check self.graphqlClient->executeWithType(query, variables); + return check performDataBinding(graphqlResponse, GetOrganizationsByAvinyaTypeResponse); + } } diff --git a/campus/bffs/attendance/graphql_client/graphql_client_cg_src/types.bal b/campus/bffs/attendance/graphql_client/graphql_client_cg_src/types.bal index 53fbb09f..06b457f5 100644 --- a/campus/bffs/attendance/graphql_client/graphql_client_cg_src/types.bal +++ b/campus/bffs/attendance/graphql_client/graphql_client_cg_src/types.bal @@ -876,3 +876,18 @@ public type GetDailyAttendanceSummaryReportResponse record {| anydata? late_attendance_percentage; |}[]? daily_attendance_summary_report; |}; + +public type GetOrganizationsByAvinyaTypeResponse record {| + map __extensions?; + record {| + int? id; + record {| + string name_en; + |} name; + string? description; + record {| + string? key_name; + string? value; + |}[]? organization_metadata; + |}[]? organizations_by_avinya_type; +|}; diff --git a/campus/bffs/attendance/graphql_client/schema.graphql b/campus/bffs/attendance/graphql_client/schema.graphql index 060d8ca7..5f2bc8ad 100644 --- a/campus/bffs/attendance/graphql_client/schema.graphql +++ b/campus/bffs/attendance/graphql_client/schema.graphql @@ -659,6 +659,14 @@ type OrganizationData { parent_organizations: [OrganizationData!] people: [PersonData!] vacancies: [VacancyData!] + organization_metadata: [OrganizationMetaData!] +} + +type OrganizationMetaData { + id: Int + organization_id: Int + key_name: String + value: String } type OrganizationStructureData { @@ -869,7 +877,7 @@ type Query { attendance_missed_by_security(organization_id: Int, parent_organization_id: Int, from_date: String = "", to_date: String = ""): [ActivityParticipantAttendanceMissedBySecurityData!] daily_students_attendance_by_parent_org(parent_organization_id: Int): [DailyActivityParticipantAttendanceByParentOrgData!] total_attendance_count_by_date(organization_id: Int, parent_organization_id: Int, from_date: String = "", to_date: String = ""): [TotalActivityParticipantAttendanceCountByDateData!] - daily_attendance_summary_report(parent_organization_id: Int, from_date: String = "", to_date: String = ""): [DailyActivityParticipantAttendanceSummaryReportData!] + daily_attendance_summary_report(organization_id: Int, avinya_type_id: Int, from_date: String = "", to_date: String = ""): [DailyActivityParticipantAttendanceSummaryReportData!] } input ResourceAllocation { diff --git a/campus/bffs/attendance/graphql_client/schema.json b/campus/bffs/attendance/graphql_client/schema.json index 715a7121..a5cb8175 100644 --- a/campus/bffs/attendance/graphql_client/schema.json +++ b/campus/bffs/attendance/graphql_client/schema.json @@ -2728,7 +2728,16 @@ "name": "daily_attendance_summary_report", "args": [ { - "name": "parent_organization_id", + "name": "organization_id", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "avinya_type_id", "type": { "kind": "SCALAR", "name": "Int", @@ -5448,6 +5457,25 @@ }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "organization_metadata", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "OrganizationMetaData", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, @@ -5798,6 +5826,60 @@ ], "possibleTypes": null }, + { + "kind": "OBJECT", + "name": "OrganizationMetaData", + "fields": [ + { + "name": "id", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "organization_id", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "key_name", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "value", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "OBJECT", "name": "InventoryData", diff --git a/campus/frontend/lib/avinya/attendance/lib/data/activity_attendance.dart b/campus/frontend/lib/avinya/attendance/lib/data/activity_attendance.dart index 9845a190..5bc53c0a 100644 --- a/campus/frontend/lib/avinya/attendance/lib/data/activity_attendance.dart +++ b/campus/frontend/lib/avinya/attendance/lib/data/activity_attendance.dart @@ -1,6 +1,4 @@ import 'dart:ui'; - -import 'package:gallery/avinya/attendance/lib/data.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; @@ -546,12 +544,13 @@ Future> getTotalAttendanceCountByParentOrg( } Future> getDailyAttendanceSummaryReport( - int parent_organization_id, + int organization_id, + int avinya_type_id, String from_date, String to_date) async { final response = await http.get( Uri.parse( - '${AppConfig.campusAttendanceBffApiUrl}/daily_attendance_summary_report/$parent_organization_id/$from_date/$to_date'), + '${AppConfig.campusAttendanceBffApiUrl}/daily_attendance_summary_report/$organization_id/$avinya_type_id/$from_date/$to_date'), headers: { 'Content-Type': 'application/json; charset=UTF-8', 'accept': 'application/json', @@ -566,6 +565,6 @@ Future> getDailyAttendanceSummaryReport( return activityAttendances; } else { throw Exception( - 'Failed to get Daily Activity Participant Attendances Count'); + 'Failed to get Daily Attendances Summary Data'); } } diff --git a/campus/frontend/lib/avinya/attendance/lib/data/organization.dart b/campus/frontend/lib/avinya/attendance/lib/data/organization.dart new file mode 100644 index 00000000..f32ad128 --- /dev/null +++ b/campus/frontend/lib/avinya/attendance/lib/data/organization.dart @@ -0,0 +1,122 @@ +import 'package:gallery/avinya/attendance/lib/data.dart'; +import 'package:gallery/avinya/attendance/lib/data/organization_meta_data.dart'; +import 'package:http/http.dart' as http; +import 'dart:convert'; + +import 'package:gallery/config/app_config.dart'; + +class Name { + String? name_en; + String? name_si; + String? name_ta; + + Name({ + this.name_en, + this.name_si, + this.name_ta, + }); + + factory Name.fromJson(Map json) { + return Name( + name_en: json['name_en'], + name_si: json['name_si'], + name_ta: json['name_ta'], + ); + } + + Map toJson() => { + if (name_en != null) 'name_en': name_en, + if (name_si != null) 'name_si': name_si, + if (name_ta != null) 'name_ta': name_ta, + // if (address != null) 'address': address!.toJson(), + // if (employees != null) 'employees': List.from(employees!.map((x) => x.toJson())), + }; +} + +class Organization { + int? id; + Name? name; + String? description; + var child_organizations = []; + var parent_organizations = []; + var people = []; + var organization_metadata = []; + + Organization({ + this.id, + this.name, + this.description, + this.child_organizations = const [], + this.parent_organizations = const [], + this.people = const [], + this.organization_metadata = const[], + }); + + factory Organization.fromJson(Map json) { + return Organization( + id: json['id'], + name: json['name'] != null ? Name.fromJson(json['name']) : null, + description: json['description'], + child_organizations: json['child_organizations'] != null + ? List.from( + json['child_organizations'].map((x) => Organization.fromJson(x))) + : [], + parent_organizations: json['parent_organizations'] != null + ? List.from( + json['parent_organizations'].map((x) => Organization.fromJson(x))) + : [], + people: json['people'] != null + ? List.from(json['people'].map((x) => Person.fromJson(x))) + : [], + organization_metadata: json['organization_metadata'] != null + ? List.from( + json['organization_metadata'].map((x) => OrganizationMetaData.fromJson(x))) + : [], + ); + } + + Map toJson() => { + if (id != null) 'id': id, + // if (name != null) 'name': name, + // if (name != null) 'name': Name!.toJson(), + if (name != null) 'name': name!.toJson(), + if (description != null) 'description': description, + 'child_organizations': + List.from(child_organizations.map((x) => x.toJson())), + 'parent_organizations': + List.from(parent_organizations.map((x) => x.toJson())), + 'people': List.from(people.map((x) => x.toJson())), + 'organization_metadata':List.from(organization_metadata.map((x) => x.toJson())) + // if (employees != null) 'employees': List.from(employees!.map((x) => x.toJson())), + }; + + +} + +Future> fetchOrganizationsByAvinyaType(int avinya_type) async { + + final response = await http.get( + Uri.parse( + '${AppConfig.campusAttendanceBffApiUrl}/organizations_by_avinya_type/$avinya_type'), + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + 'accept': 'application/json', + 'Authorization': 'Bearer ${AppConfig.campusBffApiKey}', + }, + ); + + if (response.statusCode > 199 && response.statusCode < 300) { + + var resultsJson = json.decode(response.body).cast>(); + + List organization = + await resultsJson + .map((json) => Organization.fromJson(json)) + .toList(); + return organization; + } else { + throw Exception('Failed to load organizations'); + } +} + + diff --git a/campus/frontend/lib/avinya/attendance/lib/data/organization_meta_data.dart b/campus/frontend/lib/avinya/attendance/lib/data/organization_meta_data.dart new file mode 100644 index 00000000..71b2d281 --- /dev/null +++ b/campus/frontend/lib/avinya/attendance/lib/data/organization_meta_data.dart @@ -0,0 +1,29 @@ +class OrganizationMetaData { + int? id; + int? organization_id; + String? key_name; + String? value; + + OrganizationMetaData({ + this.id, + this.organization_id, + this.key_name, + this.value, + }); + + factory OrganizationMetaData.fromJson(Map json) { + return OrganizationMetaData( + id: json['id'], + organization_id: json['organization_id'], + key_name: json['key_name'], + value: json['value'], + ); + } + + Map toJson() => { + if (id != null) 'id': id, + if (organization_id != null) 'organization_id': organization_id, + if (key_name != null) 'key_name': key_name, + if (value != null) 'value': value, + }; +} diff --git a/campus/frontend/lib/avinya/attendance/lib/screens/daily_attendance_summary_report.dart b/campus/frontend/lib/avinya/attendance/lib/screens/daily_attendance_summary_report.dart index 32b72c2b..61efb8a2 100644 --- a/campus/frontend/lib/avinya/attendance/lib/screens/daily_attendance_summary_report.dart +++ b/campus/frontend/lib/avinya/attendance/lib/screens/daily_attendance_summary_report.dart @@ -32,129 +32,11 @@ class _DailyAttendanceSummaryReportScreenState extends State selected) { - setState(() { - selected[index] = value; - }); - } - - void updateDateRange(_rangeStart, _rangeEnd) async { - - int? parentOrgId = - campusAppsPortalInstance.getUserPerson().organization!.id; - if (parentOrgId != null) { - setState(() { - _isFetching = true; // Set _isFetching to true before starting the fetch - }); - - _fetchedExcelReportData = await getDailyAttendanceSummaryReport( - parentOrgId, - DateFormat('yyyy-MM-dd') - .format(DateFormat('MMM d, yyyy').parse(this.formattedStartDate)), - DateFormat('yyyy-MM-dd') - .format(DateFormat('MMM d, yyyy').parse(this.formattedEndDate))); - - - try { - setState(() { - final startDate = _rangeStart ?? _selectedDay; - final endDate = _rangeEnd ?? _selectedDay; - final formatter = DateFormat('MMM d, yyyy'); - final formattedStartDate = formatter.format(startDate!); - final formattedEndDate = formatter.format(endDate!); - this.formattedStartDate = formattedStartDate; - this.formattedEndDate = formattedEndDate; - this._fetchedExcelReportData = _fetchedExcelReportData; - refreshState(); - }); - } catch (error) { - // Handle any errors that occur during the fetch - setState(() { - _isFetching = false; // Set _isFetching to false in case of error - }); - // Perform error handling, e.g., show an error message - } - } - } - - void refreshState() async { - setState(() { - _isFetching = true; // Set _isFetching to true before starting the fetch - }); - int? parentOrgId = - campusAppsPortalInstance.getUserPerson().organization!.id; - - _fetchedExcelReportData = await getDailyAttendanceSummaryReport( - parentOrgId!, - DateFormat('yyyy-MM-dd') - .format(DateFormat('MMM d, yyyy').parse(this.formattedStartDate)), - DateFormat('yyyy-MM-dd') - .format(DateFormat('MMM d, yyyy').parse(this.formattedEndDate))); - - - setState(() { - this._isFetching = false; - _data = MyData(_fetchedDailyAttendanceSummaryData,updateSelected); - }); - } - - List _buildDataColumns() { - - List ColumnNames = []; - - ColumnNames.add(DataColumn( - label: Text('Date', - style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)))); - ColumnNames.add(DataColumn( - label: Text('Daily Count', - style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)))); - ColumnNames.add(DataColumn( - label: Text('Daily Attendance Percentage', - style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)))); - ColumnNames.add(DataColumn( - label: Text('Late Count', - style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)))); - ColumnNames.add(DataColumn( - label: Text('Late Attendance Percentage', - style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)))); - ColumnNames.add(DataColumn( - label: Text('Total Count', - style: TextStyle(fontSize: 14.0, fontWeight: FontWeight.bold)))); - - return ColumnNames; } - @override void dispose() { super.dispose(); @@ -171,25 +53,11 @@ class _DailyAttendanceSummaryReportScreenState extends State { String startDate = ""; String endDate = ""; List cardData = []; + late Future> _fetchBatchData; + Organization? _selectedOrganizationValue; @override void initState() { @@ -49,6 +53,7 @@ class _AttendanceDashboardScreenState extends State { formattedEndDate = DateFormat('MMM d, yyyy').format(today); String formattedToday = DateFormat('yyyy-MM-dd').format(today); refreshState(null, formattedToday, formattedToday); + _fetchBatchData = _loadBatchData(); } @override @@ -56,6 +61,10 @@ class _AttendanceDashboardScreenState extends State { super.dispose(); } + Future> _loadBatchData() async{ + return await fetchOrganizationsByAvinyaType(86); + } + DateRange? selectedDateRange; void updateDateRange(_rangeStart, _rangeEnd) async { int? parentOrgId = @@ -327,6 +336,61 @@ class _AttendanceDashboardScreenState extends State { ), child: Text(formattedStartDate + " - " + formattedEndDate), ), + SizedBox( + width: 30, + ), + Text('Select a Batch :'), + SizedBox( + width: 10, + ), + FutureBuilder>( + future: _fetchBatchData, + builder: (context,snapshot) { + + if(snapshot.connectionState == ConnectionState.waiting){ + return Container( + margin: EdgeInsets.only(top: 10), + child: SpinKitCircle( + color: (Colors.deepPurpleAccent), + size: 70, + ), + ); + }else if(snapshot.hasError){ + return const Center( + child: Text('Something went wrong...'), + ); + + }else if(!snapshot.hasData){ + return const Center( + child: Text('No batch found'), + ); + } + final batchData = snapshot.data!; + return DropdownButton( + value: _selectedOrganizationValue, + items: batchData.map((Organization batch){ + return DropdownMenuItem( + value: batch, + child: Text(batch.name!.name_en??'') + ); + }).toList(), + onChanged:(Organization? newValue) async{ + if(newValue == null){ + return; + } + + if(newValue.organization_metadata.isEmpty){ + return; + } + + setState(() { + _selectedOrganizationValue = newValue; + }); + + } + ); + }, + ), Expanded( child: Container( margin: EdgeInsets.only(left: 8.0), diff --git a/campus/frontend/lib/avinya/attendance/lib/screens/scaffold.dart b/campus/frontend/lib/avinya/attendance/lib/screens/scaffold.dart index 022e7544..4d5015a9 100644 --- a/campus/frontend/lib/avinya/attendance/lib/screens/scaffold.dart +++ b/campus/frontend/lib/avinya/attendance/lib/screens/scaffold.dart @@ -118,7 +118,7 @@ class _SMSScaffoldState extends State { padding: EdgeInsets.only(left: 15.0, right: 15.0, bottom: 5.0), child: ListTile( hoverColor: Colors.white.withOpacity(0.3), - leading: Icon(Icons.report, + leading: Icon(Icons.summarize_sharp, color: Colors.white, size: 20.0), title: Container( margin: EdgeInsets.only(left: 12.0), diff --git a/campus/frontend/lib/avinya/attendance/lib/widgets/attendance_summary_excel_report_export.dart b/campus/frontend/lib/avinya/attendance/lib/widgets/attendance_summary_excel_report_export.dart index ab87645e..3897c340 100644 --- a/campus/frontend/lib/avinya/attendance/lib/widgets/attendance_summary_excel_report_export.dart +++ b/campus/frontend/lib/avinya/attendance/lib/widgets/attendance_summary_excel_report_export.dart @@ -159,13 +159,19 @@ class _AttendanceSummaryExcelReportExportState extends State{ List _fetchedDailyAttendanceSummaryData = []; List _fetchedExcelReportData = []; - bool _isFetching = true; + late Future> _fetchBatchData; + bool _isFetching = false; + Organization? _selectedValue; + AvinyaTypeId _selectedAvinyaTypeId = AvinyaTypeId.Empower; + List filteredAvinyaTypeIdValues = [AvinyaTypeId.Empower,AvinyaTypeId.IT,AvinyaTypeId.CS]; List columnNames = []; - late String formattedStartDate; - late String formattedEndDate; late DataTableSource _data; //calendar specific variables - DateTime? _selectedDay; - void selectDateRange(DateTime today) async { - // Update the variables to select the week - final formatter = DateFormat('MMM d, yyyy'); - formattedStartDate = formatter.format(today); - formattedEndDate = formatter.format(today); - setState(() { - _isFetching = false; - }); - } @override void initState() { super.initState(); - var today = DateTime.now(); - selectDateRange(today); + _fetchBatchData = _loadBatchData(); + } + + Future> _loadBatchData() async{ + return await fetchOrganizationsByAvinyaType(86); } void updateExcelState() { AttendanceSummaryExcelReportExport( - fetchedDailyAttendanceSummaryData: _fetchedExcelReportData, + fetchedDailyAttendanceSummaryData: _fetchedDailyAttendanceSummaryData, columnNames: columnNames, updateExcelState: updateExcelState, isFetching: _isFetching, - formattedStartDate: formattedStartDate, - formattedEndDate: formattedEndDate, + formattedStartDate: _selectedValue!.organization_metadata[0].value!, + formattedEndDate: _selectedValue!.organization_metadata[1].value!, ); } @@ -71,7 +73,6 @@ class _AttendanceSummaryReportState extends State{ void didChangeDependencies() { super.didChangeDependencies(); _data = MyData(_fetchedDailyAttendanceSummaryData,updateSelected); - DateRangePicker(updateDateRange, formattedStartDate); } void updateSelected(int index, bool value, List selected) { @@ -80,67 +81,6 @@ class _AttendanceSummaryReportState extends State{ }); } - void updateDateRange(_rangeStart, _rangeEnd) async { - widget.updateDateRangeForExcel(_rangeStart, _rangeEnd); - int? parentOrgId = - campusAppsPortalInstance.getUserPerson().organization!.id; - if (parentOrgId != null) { - setState(() { - _isFetching = true; // Set _isFetching to true before starting the fetch - }); - - _fetchedExcelReportData = await getDailyAttendanceSummaryReport( - parentOrgId, - DateFormat('yyyy-MM-dd') - .format(DateFormat('MMM d, yyyy').parse(this.formattedStartDate)), - DateFormat('yyyy-MM-dd') - .format(DateFormat('MMM d, yyyy').parse(this.formattedEndDate))); - - - - try { - setState(() { - final startDate = _rangeStart ?? _selectedDay; - final endDate = _rangeEnd ?? _selectedDay; - final formatter = DateFormat('MMM d, yyyy'); - final formattedStartDate = formatter.format(startDate!); - final formattedEndDate = formatter.format(endDate!); - this.formattedStartDate = formattedStartDate; - this.formattedEndDate = formattedEndDate; - this._fetchedExcelReportData = _fetchedExcelReportData; - _isFetching = false; - refreshState(); - }); - } catch (error) { - // Handle any errors that occur during the fetch - setState(() { - _isFetching = false; // Set _isFetching to false in case of error - }); - // Perform error handling, e.g., show an error message - } - } - } - - void refreshState() async { - setState(() { - _isFetching = true; // Set _isFetching to true before starting the fetch - }); - int? parentOrgId = - campusAppsPortalInstance.getUserPerson().organization!.id; - - _fetchedDailyAttendanceSummaryData = await getDailyAttendanceSummaryReport( - parentOrgId!, - DateFormat('yyyy-MM-dd') - .format(DateFormat('MMM d, yyyy').parse(this.formattedStartDate)), - DateFormat('yyyy-MM-dd') - .format(DateFormat('MMM d, yyyy').parse(this.formattedEndDate))); - - setState(() { - this._isFetching = false; - _data = MyData(_fetchedDailyAttendanceSummaryData,updateSelected); - }); - } - List _buildDataColumns() { List ColumnNames = []; @@ -177,90 +117,179 @@ class _AttendanceSummaryReportState extends State{ @override Widget build(BuildContext context) { - - AttendanceSummaryExcelReportExport( - fetchedDailyAttendanceSummaryData: _fetchedExcelReportData, - columnNames: columnNames, - updateExcelState: updateExcelState, - isFetching: _isFetching, - formattedStartDate: formattedStartDate, - formattedEndDate: formattedEndDate, - ); - return SingleChildScrollView( child: Column( children: [ Wrap( children: [ - Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - // create a text widget with some padding - - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - margin: - EdgeInsets.only(left: 20, top: 20, bottom: 10), - child: Row(children: [ - ElevatedButton( - style: ButtonStyle( - textStyle: MaterialStateProperty.all( - TextStyle(fontSize: 20), - ), - elevation: MaterialStateProperty.all(20), - backgroundColor: MaterialStateProperty.all( - Colors.greenAccent), - foregroundColor: - MaterialStateProperty.all(Colors.black), - ), - onPressed: _isFetching - ? null - : () { - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => DateRangePicker( - updateDateRange, - formattedStartDate)), - ); - }, - child: Container( - height: 50, // Adjust the height as needed - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (_isFetching) - Padding( - padding: EdgeInsets.only(right: 10), - child: SpinKitFadingCircle( - color: Colors - .black, // Customize the color of the indicator - size: - 20, // Customize the size of the indicator - ), - ), - if (!_isFetching) - Icon(Icons.calendar_today, - color: Colors.black), - SizedBox(width: 10), - Text( - '${this.formattedStartDate} - ${this.formattedEndDate}', - style: TextStyle( - color: Colors.black, - fontSize: 17, - fontWeight: FontWeight.w400, - ), - ), - ], + Padding( + padding: EdgeInsets.only(top: 20,left: 20), + child: Row( + children: [ + Text('Select a Batch :'), + SizedBox( + width: 10, + ), + FutureBuilder>( + future: _fetchBatchData, + builder: (context,snapshot) { + + if(snapshot.connectionState == ConnectionState.waiting){ + return Container( + margin: EdgeInsets.only(top: 10), + child: SpinKitCircle( + color: (Colors.deepPurpleAccent), + size: 70, + ), + ); + }else if(snapshot.hasError){ + return const Center( + child: Text('Something went wrong...'), + ); + + }else if(!snapshot.hasData){ + return const Center( + child: Text('No batch found'), + ); + } + final batchData = snapshot.data!; + return DropdownButton( + value: _selectedValue, + items: batchData.map((Organization batch){ + return DropdownMenuItem( + value: batch, + child: Text(batch.name!.name_en??'') + ); + }).toList(), + onChanged:(Organization? newValue) async{ + if(newValue == null){ + return; + } + + if(newValue.organization_metadata.isEmpty){ + return; + } + + if (DateTime.parse(newValue.organization_metadata[1].value.toString()).isBefore(DateTime.parse('2024-03-01'))) { + filteredAvinyaTypeIdValues = [AvinyaTypeId.Empower]; + }else{ + filteredAvinyaTypeIdValues = [AvinyaTypeId.Empower,AvinyaTypeId.IT,AvinyaTypeId.CS]; + } + + setState(() { + this._isFetching = true; + filteredAvinyaTypeIdValues; + }); + + if(filteredAvinyaTypeIdValues.contains(_selectedAvinyaTypeId)){ + + _fetchedDailyAttendanceSummaryData + = await getDailyAttendanceSummaryReport( + newValue.id!, + avinyaTypeId[_selectedAvinyaTypeId]!, + newValue.organization_metadata[0].value!, + newValue.organization_metadata[1].value!, + ); + }else{ + _selectedAvinyaTypeId = filteredAvinyaTypeIdValues.first; + + _fetchedDailyAttendanceSummaryData + = await getDailyAttendanceSummaryReport( + newValue.id!, + avinyaTypeId[_selectedAvinyaTypeId]!, + newValue.organization_metadata[0].value!, + newValue.organization_metadata[1].value!, + ); + + } + + + setState(() { + _selectedValue = newValue; + this._isFetching = false; + _selectedAvinyaTypeId; + _data = MyData(_fetchedDailyAttendanceSummaryData,updateSelected); + }); + + } + ); + + }, + ), + SizedBox( + width: 30, + ), + Text('Select a Programme :'), + SizedBox( + width: 10, + ), + DropdownButton( + value: _selectedAvinyaTypeId, + items: filteredAvinyaTypeIdValues + .map( + (typeId) => DropdownMenuItem( + value: typeId, + child: Text( + typeId.name.toUpperCase() ), ), - ), - ]), - ), - ]), - ], + ) + .toList(), + onChanged: (AvinyaTypeId? value) async{ + if(value == null){ + return; + } + + + if(_selectedValue == null || _selectedValue!.organization_metadata.length == 0){ + return; + } + + setState(() { + this._isFetching = true; + }); + + _fetchedDailyAttendanceSummaryData + = await getDailyAttendanceSummaryReport( + _selectedValue!.id!, + avinyaTypeId[value]!, + _selectedValue!.organization_metadata[0].value!, + _selectedValue!.organization_metadata[1].value!, + ); + + setState(() { + _selectedAvinyaTypeId = value; + this._isFetching = false; + _data = MyData(_fetchedDailyAttendanceSummaryData,updateSelected); + }); + } + ), + SizedBox( + width: 10, + ), + Expanded( + child: Container( + alignment: Alignment.bottomRight, + margin: EdgeInsets.only( + right: 20.0 + ), + width: 25.0, + height: 30.0, + child: _selectedValue != null ? AttendanceSummaryExcelReportExport( + fetchedDailyAttendanceSummaryData: _fetchedDailyAttendanceSummaryData, + columnNames: columnNames, + updateExcelState: updateExcelState, + isFetching: _isFetching, + formattedStartDate: _selectedValue!.organization_metadata[0].value!, + formattedEndDate: _selectedValue!.organization_metadata[1].value!, + ) : SizedBox(), + ), + ) + ], + ), + ), + SizedBox( + height: 10, ), Wrap(children: [ if (_isFetching) diff --git a/campus/frontend/lib/data/person.dart b/campus/frontend/lib/data/person.dart index 83111a42..52bd3652 100644 --- a/campus/frontend/lib/data/person.dart +++ b/campus/frontend/lib/data/person.dart @@ -2,6 +2,7 @@ import 'dart:developer'; import 'package:flutter/src/material/data_table.dart'; import 'package:gallery/avinya/attendance/lib/data.dart'; +import 'package:gallery/avinya/attendance/lib/data/organization_meta_data.dart'; import 'package:gallery/data/address.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; @@ -43,6 +44,7 @@ class Organization { var child_organizations = []; var parent_organizations = []; var people = []; + var organization_metadata = []; Organization({ this.id, @@ -51,6 +53,7 @@ class Organization { this.child_organizations = const [], this.parent_organizations = const [], this.people = const [], + this.organization_metadata = const[], }); factory Organization.fromJson(Map json) { @@ -69,6 +72,10 @@ class Organization { people: json['people'] != null ? List.from(json['people'].map((x) => Person.fromJson(x))) : [], + organization_metadata: json['organization_metadata'] != null + ? List.from( + json['organization_metadata'].map((x) => OrganizationMetaData.fromJson(x))) + : [], ); } @@ -83,6 +90,7 @@ class Organization { 'parent_organizations': List.from(parent_organizations.map((x) => x.toJson())), 'people': List.from(people.map((x) => x.toJson())), + 'organization_metadata':List.from(organization_metadata.map((x) => x.toJson())) // if (employees != null) 'employees': List.from(employees!.map((x) => x.toJson())), }; } @@ -134,6 +142,31 @@ Future> fetchOrganizationForAll(int id) async { throw Exception('Failed to load Person'); } } +Future> fetchOrganizationsByAvinyaType(int avinya_type) async { + + final response = await http.get( + Uri.parse( + '${AppConfig.campusAttendanceBffApiUrl}/organizations_by_avinya_type/$avinya_type'), + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + 'accept': 'application/json', + 'Authorization': 'Bearer ${AppConfig.campusBffApiKey}', + }, + ); + + if (response.statusCode > 199 && response.statusCode < 300) { + + var resultsJson = json.decode(response.body).cast>(); + + List organization = + await resultsJson + .map((json) => Organization.fromJson(json)) + .toList(); + return organization; + } else { + throw Exception('Failed to load organizations'); + } +} class Person { int? id;