live today from home
This commit is contained in:
@@ -1,16 +1,16 @@
|
|||||||
# Basic Usage
|
# Basic Usage
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
ExampleConnector.instance.listClientFeedbacks(listClientFeedbacksVariables).execute();
|
ExampleConnector.instance.listActivityLogs(listActivityLogsVariables).execute();
|
||||||
ExampleConnector.instance.getClientFeedbackById(getClientFeedbackByIdVariables).execute();
|
ExampleConnector.instance.getActivityLogById(getActivityLogByIdVariables).execute();
|
||||||
ExampleConnector.instance.listClientFeedbacksByBusinessId(listClientFeedbacksByBusinessIdVariables).execute();
|
ExampleConnector.instance.listActivityLogsByUserId(listActivityLogsByUserIdVariables).execute();
|
||||||
ExampleConnector.instance.listClientFeedbacksByVendorId(listClientFeedbacksByVendorIdVariables).execute();
|
ExampleConnector.instance.listUnreadActivityLogsByUserId(listUnreadActivityLogsByUserIdVariables).execute();
|
||||||
ExampleConnector.instance.listClientFeedbacksByBusinessAndVendor(listClientFeedbacksByBusinessAndVendorVariables).execute();
|
ExampleConnector.instance.filterActivityLogs(filterActivityLogsVariables).execute();
|
||||||
ExampleConnector.instance.filterClientFeedbacks(filterClientFeedbacksVariables).execute();
|
ExampleConnector.instance.listConversations(listConversationsVariables).execute();
|
||||||
ExampleConnector.instance.listClientFeedbackRatingsByVendorId(listClientFeedbackRatingsByVendorIdVariables).execute();
|
ExampleConnector.instance.getConversationById(getConversationByIdVariables).execute();
|
||||||
ExampleConnector.instance.createStaffAvailabilityStats(createStaffAvailabilityStatsVariables).execute();
|
ExampleConnector.instance.listConversationsByType(listConversationsByTypeVariables).execute();
|
||||||
ExampleConnector.instance.updateStaffAvailabilityStats(updateStaffAvailabilityStatsVariables).execute();
|
ExampleConnector.instance.listConversationsByStatus(listConversationsByStatusVariables).execute();
|
||||||
ExampleConnector.instance.deleteStaffAvailabilityStats(deleteStaffAvailabilityStatsVariables).execute();
|
ExampleConnector.instance.filterConversations(filterConversationsVariables).execute();
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -23,8 +23,8 @@ Optional fields can be discovered based on classes that have `Optional` object t
|
|||||||
This is an example of a mutation with an optional field:
|
This is an example of a mutation with an optional field:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
await ExampleConnector.instance.listWorkforceByStaffId({ ... })
|
await ExampleConnector.instance.createStaffRole({ ... })
|
||||||
.offset(...)
|
.roleType(...)
|
||||||
.execute();
|
.execute();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -114,10 +114,14 @@ class ListStaffsApplicationsByBusinessForDayApplications {
|
|||||||
@immutable
|
@immutable
|
||||||
class ListStaffsApplicationsByBusinessForDayApplicationsShiftRole {
|
class ListStaffsApplicationsByBusinessForDayApplicationsShiftRole {
|
||||||
final ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift shift;
|
final ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift shift;
|
||||||
|
final int count;
|
||||||
|
final int? assigned;
|
||||||
final ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleRole role;
|
final ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleRole role;
|
||||||
ListStaffsApplicationsByBusinessForDayApplicationsShiftRole.fromJson(dynamic json):
|
ListStaffsApplicationsByBusinessForDayApplicationsShiftRole.fromJson(dynamic json):
|
||||||
|
|
||||||
shift = ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift.fromJson(json['shift']),
|
shift = ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift.fromJson(json['shift']),
|
||||||
|
count = nativeFromJson<int>(json['count']),
|
||||||
|
assigned = json['assigned'] == null ? null : nativeFromJson<int>(json['assigned']),
|
||||||
role = ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleRole.fromJson(json['role']);
|
role = ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleRole.fromJson(json['role']);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
@@ -130,22 +134,30 @@ class ListStaffsApplicationsByBusinessForDayApplicationsShiftRole {
|
|||||||
|
|
||||||
final ListStaffsApplicationsByBusinessForDayApplicationsShiftRole otherTyped = other as ListStaffsApplicationsByBusinessForDayApplicationsShiftRole;
|
final ListStaffsApplicationsByBusinessForDayApplicationsShiftRole otherTyped = other as ListStaffsApplicationsByBusinessForDayApplicationsShiftRole;
|
||||||
return shift == otherTyped.shift &&
|
return shift == otherTyped.shift &&
|
||||||
|
count == otherTyped.count &&
|
||||||
|
assigned == otherTyped.assigned &&
|
||||||
role == otherTyped.role;
|
role == otherTyped.role;
|
||||||
|
|
||||||
}
|
}
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hashAll([shift.hashCode, role.hashCode]);
|
int get hashCode => Object.hashAll([shift.hashCode, count.hashCode, assigned.hashCode, role.hashCode]);
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
Map<String, dynamic> json = {};
|
Map<String, dynamic> json = {};
|
||||||
json['shift'] = shift.toJson();
|
json['shift'] = shift.toJson();
|
||||||
|
json['count'] = nativeToJson<int>(count);
|
||||||
|
if (assigned != null) {
|
||||||
|
json['assigned'] = nativeToJson<int?>(assigned);
|
||||||
|
}
|
||||||
json['role'] = role.toJson();
|
json['role'] = role.toJson();
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
ListStaffsApplicationsByBusinessForDayApplicationsShiftRole({
|
ListStaffsApplicationsByBusinessForDayApplicationsShiftRole({
|
||||||
required this.shift,
|
required this.shift,
|
||||||
|
required this.count,
|
||||||
|
this.assigned,
|
||||||
required this.role,
|
required this.role,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -153,9 +165,11 @@ class ListStaffsApplicationsByBusinessForDayApplicationsShiftRole {
|
|||||||
@immutable
|
@immutable
|
||||||
class ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift {
|
class ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift {
|
||||||
final String? location;
|
final String? location;
|
||||||
|
final double? cost;
|
||||||
ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift.fromJson(dynamic json):
|
ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift.fromJson(dynamic json):
|
||||||
|
|
||||||
location = json['location'] == null ? null : nativeFromJson<String>(json['location']);
|
location = json['location'] == null ? null : nativeFromJson<String>(json['location']),
|
||||||
|
cost = json['cost'] == null ? null : nativeFromJson<double>(json['cost']);
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if(identical(this, other)) {
|
if(identical(this, other)) {
|
||||||
@@ -166,11 +180,12 @@ class ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift otherTyped = other as ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift;
|
final ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift otherTyped = other as ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift;
|
||||||
return location == otherTyped.location;
|
return location == otherTyped.location &&
|
||||||
|
cost == otherTyped.cost;
|
||||||
|
|
||||||
}
|
}
|
||||||
@override
|
@override
|
||||||
int get hashCode => location.hashCode;
|
int get hashCode => Object.hashAll([location.hashCode, cost.hashCode]);
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
@@ -178,11 +193,15 @@ class ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift {
|
|||||||
if (location != null) {
|
if (location != null) {
|
||||||
json['location'] = nativeToJson<String?>(location);
|
json['location'] = nativeToJson<String?>(location);
|
||||||
}
|
}
|
||||||
|
if (cost != null) {
|
||||||
|
json['cost'] = nativeToJson<double?>(cost);
|
||||||
|
}
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift({
|
ListStaffsApplicationsByBusinessForDayApplicationsShiftRoleShift({
|
||||||
this.location,
|
this.location,
|
||||||
|
this.cost,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,9 @@ class DashboardWidgetBuilder extends StatelessWidget {
|
|||||||
: 0,
|
: 0,
|
||||||
);
|
);
|
||||||
case 'liveActivity':
|
case 'liveActivity':
|
||||||
return LiveActivityWidget(onViewAllPressed: () {});
|
return LiveActivityWidget(
|
||||||
|
onViewAllPressed: () => Modular.to.navigate('/client-main/coverage/'),
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,107 @@
|
|||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
|
||||||
|
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||||
import 'coverage_dashboard.dart';
|
import 'coverage_dashboard.dart';
|
||||||
|
|
||||||
/// A widget that displays live activity information.
|
/// A widget that displays live activity information.
|
||||||
class LiveActivityWidget extends StatelessWidget {
|
class LiveActivityWidget extends StatefulWidget {
|
||||||
/// Callback when "View all" is pressed.
|
/// Callback when "View all" is pressed.
|
||||||
final VoidCallback onViewAllPressed;
|
final VoidCallback onViewAllPressed;
|
||||||
|
|
||||||
/// Creates a [LiveActivityWidget].
|
/// Creates a [LiveActivityWidget].
|
||||||
const LiveActivityWidget({super.key, required this.onViewAllPressed});
|
const LiveActivityWidget({super.key, required this.onViewAllPressed});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<LiveActivityWidget> createState() => _LiveActivityWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LiveActivityWidgetState extends State<LiveActivityWidget> {
|
||||||
|
late final Future<_LiveActivityData> _liveActivityFuture =
|
||||||
|
_loadLiveActivity();
|
||||||
|
|
||||||
|
Future<_LiveActivityData> _loadLiveActivity() async {
|
||||||
|
final String? businessId =
|
||||||
|
dc.ClientSessionStore.instance.session?.business?.id;
|
||||||
|
if (businessId == null || businessId.isEmpty) {
|
||||||
|
return _LiveActivityData.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
final DateTime now = DateTime.now();
|
||||||
|
final DateTime start = DateTime(now.year, now.month, now.day);
|
||||||
|
final DateTime end = DateTime(now.year, now.month, now.day, 23, 59, 59, 999);
|
||||||
|
final fdc.QueryResult<dc.ListStaffsApplicationsByBusinessForDayData,
|
||||||
|
dc.ListStaffsApplicationsByBusinessForDayVariables> result =
|
||||||
|
await dc.ExampleConnector.instance
|
||||||
|
.listStaffsApplicationsByBusinessForDay(
|
||||||
|
businessId: businessId,
|
||||||
|
dayStart: _toTimestamp(start),
|
||||||
|
dayEnd: _toTimestamp(end),
|
||||||
|
)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
if (result.data.applications.isEmpty) {
|
||||||
|
return _LiveActivityData.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, _LiveShiftAggregate> aggregates =
|
||||||
|
<String, _LiveShiftAggregate>{};
|
||||||
|
for (final dc.ListStaffsApplicationsByBusinessForDayApplications app
|
||||||
|
in result.data.applications) {
|
||||||
|
final String key = '${app.shiftId}:${app.roleId}';
|
||||||
|
final _LiveShiftAggregate aggregate = aggregates[key] ??
|
||||||
|
_LiveShiftAggregate(
|
||||||
|
workersNeeded: app.shiftRole.count,
|
||||||
|
assigned: app.shiftRole.assigned ?? 0,
|
||||||
|
cost: app.shiftRole.shift.cost ?? 0,
|
||||||
|
);
|
||||||
|
aggregates[key] = aggregate;
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalNeeded = 0;
|
||||||
|
int totalAssigned = 0;
|
||||||
|
double totalCost = 0;
|
||||||
|
for (final _LiveShiftAggregate aggregate in aggregates.values) {
|
||||||
|
totalNeeded += aggregate.workersNeeded;
|
||||||
|
totalAssigned += aggregate.assigned;
|
||||||
|
totalCost += aggregate.cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lateCount = 0;
|
||||||
|
int checkedInCount = 0;
|
||||||
|
for (final dc.ListStaffsApplicationsByBusinessForDayApplications app
|
||||||
|
in result.data.applications) {
|
||||||
|
if (app.checkInTime != null) {
|
||||||
|
checkedInCount += 1;
|
||||||
|
}
|
||||||
|
if (app.status is dc.Known<dc.ApplicationStatus> &&
|
||||||
|
(app.status as dc.Known<dc.ApplicationStatus>).value ==
|
||||||
|
dc.ApplicationStatus.LATE) {
|
||||||
|
lateCount += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _LiveActivityData(
|
||||||
|
totalNeeded: totalNeeded,
|
||||||
|
totalAssigned: totalAssigned,
|
||||||
|
totalCost: totalCost,
|
||||||
|
checkedInCount: checkedInCount,
|
||||||
|
lateCount: lateCount,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fdc.Timestamp _toTimestamp(DateTime dateTime) {
|
||||||
|
final int seconds = dateTime.millisecondsSinceEpoch ~/ 1000;
|
||||||
|
final int nanoseconds =
|
||||||
|
(dateTime.millisecondsSinceEpoch % 1000) * 1000000;
|
||||||
|
return fdc.Timestamp(nanoseconds, seconds);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final TranslationsClientHomeEn i18n = t.client_home;
|
final TranslationsClientHomeEn i18n = t.client_home;
|
||||||
|
|
||||||
// Mock data
|
|
||||||
final List<Map<String, Object>> shifts = <Map<String, Object>>[
|
|
||||||
<String, Object>{
|
|
||||||
'workersNeeded': 5,
|
|
||||||
'filled': 4,
|
|
||||||
'hourlyRate': 20.0,
|
|
||||||
'status': 'OPEN',
|
|
||||||
'date': DateTime.now().toIso8601String().split('T')[0],
|
|
||||||
},
|
|
||||||
<String, Object>{
|
|
||||||
'workersNeeded': 5,
|
|
||||||
'filled': 5,
|
|
||||||
'hourlyRate': 22.0,
|
|
||||||
'status': 'FILLED',
|
|
||||||
'date': DateTime.now().toIso8601String().split('T')[0],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
final List<Map<String, String>> applications = <Map<String, String>>[
|
|
||||||
<String, String>{'status': 'CONFIRMED', 'checkInTime': '09:00'},
|
|
||||||
<String, String>{'status': 'CONFIRMED', 'checkInTime': '09:05'},
|
|
||||||
<String, String>{'status': 'CONFIRMED'},
|
|
||||||
<String, String>{'status': 'LATE'},
|
|
||||||
];
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
@@ -51,7 +114,7 @@ class LiveActivityWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: onViewAllPressed,
|
onTap: widget.onViewAllPressed,
|
||||||
child: Text(
|
child: Text(
|
||||||
i18n.dashboard.view_all,
|
i18n.dashboard.view_all,
|
||||||
style: UiTypography.footnote1m.copyWith(
|
style: UiTypography.footnote1m.copyWith(
|
||||||
@@ -62,8 +125,82 @@ class LiveActivityWidget extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space2),
|
const SizedBox(height: UiConstants.space2),
|
||||||
CoverageDashboard(shifts: shifts, applications: applications),
|
FutureBuilder<_LiveActivityData>(
|
||||||
|
future: _liveActivityFuture,
|
||||||
|
builder: (BuildContext context,
|
||||||
|
AsyncSnapshot<_LiveActivityData> snapshot) {
|
||||||
|
final _LiveActivityData data =
|
||||||
|
snapshot.data ?? _LiveActivityData.empty();
|
||||||
|
final List<Map<String, Object>> shifts =
|
||||||
|
<Map<String, Object>>[
|
||||||
|
<String, Object>{
|
||||||
|
'workersNeeded': data.totalNeeded,
|
||||||
|
'filled': data.totalAssigned,
|
||||||
|
'hourlyRate': data.totalAssigned == 0
|
||||||
|
? 0.0
|
||||||
|
: data.totalCost / data.totalAssigned,
|
||||||
|
'status': 'OPEN',
|
||||||
|
'date': DateTime.now().toIso8601String().split('T')[0],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
final List<Map<String, Object?>> applications =
|
||||||
|
<Map<String, Object?>>[];
|
||||||
|
for (int i = 0; i < data.checkedInCount; i += 1) {
|
||||||
|
applications.add(
|
||||||
|
<String, Object?>{
|
||||||
|
'status': 'CONFIRMED',
|
||||||
|
'checkInTime': '09:00',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < data.lateCount; i += 1) {
|
||||||
|
applications.add(<String, Object?>{'status': 'LATE'});
|
||||||
|
}
|
||||||
|
return CoverageDashboard(
|
||||||
|
shifts: shifts,
|
||||||
|
applications: applications,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _LiveActivityData {
|
||||||
|
const _LiveActivityData({
|
||||||
|
required this.totalNeeded,
|
||||||
|
required this.totalAssigned,
|
||||||
|
required this.totalCost,
|
||||||
|
required this.checkedInCount,
|
||||||
|
required this.lateCount,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int totalNeeded;
|
||||||
|
final int totalAssigned;
|
||||||
|
final double totalCost;
|
||||||
|
final int checkedInCount;
|
||||||
|
final int lateCount;
|
||||||
|
|
||||||
|
factory _LiveActivityData.empty() {
|
||||||
|
return const _LiveActivityData(
|
||||||
|
totalNeeded: 0,
|
||||||
|
totalAssigned: 0,
|
||||||
|
totalCost: 0,
|
||||||
|
checkedInCount: 0,
|
||||||
|
lateCount: 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LiveShiftAggregate {
|
||||||
|
_LiveShiftAggregate({
|
||||||
|
required this.workersNeeded,
|
||||||
|
required this.assigned,
|
||||||
|
required this.cost,
|
||||||
|
});
|
||||||
|
|
||||||
|
final int workersNeeded;
|
||||||
|
final int assigned;
|
||||||
|
final double cost;
|
||||||
|
}
|
||||||
|
|||||||
@@ -357,7 +357,7 @@ query listAcceptedApplicationsByBusinessForDay(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#coverage list
|
#coverage list and today live
|
||||||
query listStaffsApplicationsByBusinessForDay(
|
query listStaffsApplicationsByBusinessForDay(
|
||||||
$businessId: UUID!
|
$businessId: UUID!
|
||||||
$dayStart: Timestamp!
|
$dayStart: Timestamp!
|
||||||
@@ -385,10 +385,14 @@ query listStaffsApplicationsByBusinessForDay(
|
|||||||
checkOutTime
|
checkOutTime
|
||||||
appliedAt
|
appliedAt
|
||||||
status
|
status
|
||||||
|
|
||||||
shiftRole{
|
shiftRole{
|
||||||
shift{
|
shift{
|
||||||
location
|
location
|
||||||
|
cost
|
||||||
}
|
}
|
||||||
|
count
|
||||||
|
assigned
|
||||||
|
|
||||||
role{
|
role{
|
||||||
name
|
name
|
||||||
|
|||||||
Reference in New Issue
Block a user