mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-09 14:47:23 +00:00
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|
||||||
|
|
||||||
class DialogDropdown extends StatefulWidget {
|
class DialogDropdown extends StatefulWidget {
|
||||||
final List<String> items;
|
final List<String> items;
|
||||||
@ -8,14 +7,14 @@ class DialogDropdown extends StatefulWidget {
|
|||||||
final String? selectedValue;
|
final String? selectedValue;
|
||||||
|
|
||||||
const DialogDropdown({
|
const DialogDropdown({
|
||||||
super.key,
|
Key? key,
|
||||||
required this.items,
|
required this.items,
|
||||||
required this.onSelected,
|
required this.onSelected,
|
||||||
this.selectedValue,
|
this.selectedValue,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<DialogDropdown> createState() => _DialogDropdownState();
|
_DialogDropdownState createState() => _DialogDropdownState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DialogDropdownState extends State<DialogDropdown> {
|
class _DialogDropdownState extends State<DialogDropdown> {
|
||||||
@ -47,14 +46,16 @@ class _DialogDropdownState extends State<DialogDropdown> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
OverlayEntry _createOverlayEntry() {
|
OverlayEntry _createOverlayEntry() {
|
||||||
final renderBox = context.findRenderObject()! as RenderBox;
|
final renderBox = context.findRenderObject() as RenderBox;
|
||||||
final size = renderBox.size;
|
final size = renderBox.size;
|
||||||
final offset = renderBox.localToGlobal(Offset.zero);
|
final offset = renderBox.localToGlobal(Offset.zero);
|
||||||
|
|
||||||
return OverlayEntry(
|
return OverlayEntry(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: _closeDropdown,
|
onTap: () {
|
||||||
|
_closeDropdown();
|
||||||
|
},
|
||||||
behavior: HitTestBehavior.translucent,
|
behavior: HitTestBehavior.translucent,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
@ -86,9 +87,12 @@ class _DialogDropdownState extends State<DialogDropdown> {
|
|||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
item,
|
item,
|
||||||
style: context.textTheme.bodyMedium?.copyWith(
|
style: Theme.of(context)
|
||||||
color: ColorsManager.textPrimaryColor,
|
.textTheme
|
||||||
),
|
.bodyMedium
|
||||||
|
?.copyWith(
|
||||||
|
color: ColorsManager.textPrimaryColor,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
widget.onSelected(item);
|
widget.onSelected(item);
|
||||||
|
@ -10,25 +10,24 @@ class EditChip extends StatelessWidget {
|
|||||||
final double borderRadius;
|
final double borderRadius;
|
||||||
|
|
||||||
const EditChip({
|
const EditChip({
|
||||||
super.key,
|
Key? key,
|
||||||
this.label = 'Edit',
|
this.label = 'Edit',
|
||||||
required this.onTap,
|
required this.onTap,
|
||||||
this.labelColor = ColorsManager.spaceColor,
|
this.labelColor = ColorsManager.spaceColor,
|
||||||
this.backgroundColor = ColorsManager.whiteColors,
|
this.backgroundColor = ColorsManager.whiteColors,
|
||||||
this.borderColor = ColorsManager.spaceColor,
|
this.borderColor = ColorsManager.spaceColor,
|
||||||
this.borderRadius = 16.0,
|
this.borderRadius = 16.0,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Chip(
|
child: Chip(
|
||||||
label: Text(label,
|
label: Text(
|
||||||
style: Theme.of(context)
|
label,
|
||||||
.textTheme
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(color: labelColor)
|
||||||
.bodySmall!
|
),
|
||||||
.copyWith(color: labelColor)),
|
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(borderRadius),
|
borderRadius: BorderRadius.circular(borderRadius),
|
||||||
|
@ -9,12 +9,12 @@ class TagDialogTextfieldDropdown extends StatefulWidget {
|
|||||||
final String product;
|
final String product;
|
||||||
|
|
||||||
const TagDialogTextfieldDropdown({
|
const TagDialogTextfieldDropdown({
|
||||||
super.key,
|
Key? key,
|
||||||
required this.items,
|
required this.items,
|
||||||
required this.onSelected,
|
required this.onSelected,
|
||||||
this.initialValue,
|
this.initialValue,
|
||||||
required this.product,
|
required this.product,
|
||||||
});
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_DialogTextfieldDropdownState createState() =>
|
_DialogTextfieldDropdownState createState() =>
|
||||||
@ -79,7 +79,7 @@ class _DialogTextfieldDropdownState extends State<TagDialogTextfieldDropdown> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
OverlayEntry _createOverlayEntry() {
|
OverlayEntry _createOverlayEntry() {
|
||||||
final renderBox = context.findRenderObject()! as RenderBox;
|
final renderBox = context.findRenderObject() as RenderBox;
|
||||||
final size = renderBox.size;
|
final size = renderBox.size;
|
||||||
final offset = renderBox.localToGlobal(Offset.zero);
|
final offset = renderBox.localToGlobal(Offset.zero);
|
||||||
|
|
||||||
|
@ -10,8 +10,7 @@ class CustomExpansionTile extends StatefulWidget {
|
|||||||
final ValueChanged<bool>? onExpansionChanged; // Notify when expansion changes
|
final ValueChanged<bool>? onExpansionChanged; // Notify when expansion changes
|
||||||
final VoidCallback? onItemSelected; // Callback for selecting the item
|
final VoidCallback? onItemSelected; // Callback for selecting the item
|
||||||
|
|
||||||
const CustomExpansionTile({
|
CustomExpansionTile({
|
||||||
super.key,
|
|
||||||
required this.title,
|
required this.title,
|
||||||
this.children,
|
this.children,
|
||||||
this.initiallyExpanded = false,
|
this.initiallyExpanded = false,
|
||||||
|
@ -7,7 +7,7 @@ class CustomSearchBar extends StatefulWidget {
|
|||||||
final TextEditingController? controller;
|
final TextEditingController? controller;
|
||||||
final String hintText;
|
final String hintText;
|
||||||
final String? searchQuery;
|
final String? searchQuery;
|
||||||
final void Function(String)? onSearchChanged;
|
final Function(String)? onSearchChanged; // Callback for search input changes
|
||||||
|
|
||||||
const CustomSearchBar({
|
const CustomSearchBar({
|
||||||
super.key,
|
super.key,
|
||||||
@ -37,7 +37,7 @@ class _CustomSearchBarState extends State<CustomSearchBar> {
|
|||||||
color: ColorsManager.whiteColors,
|
color: ColorsManager.whiteColors,
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withValues(alpha: 0.2),
|
color: Colors.black.withOpacity(0.2),
|
||||||
spreadRadius: 0,
|
spreadRadius: 0,
|
||||||
blurRadius: 8,
|
blurRadius: 8,
|
||||||
offset: const Offset(0, 4),
|
offset: const Offset(0, 4),
|
||||||
@ -57,7 +57,7 @@ class _CustomSearchBarState extends State<CustomSearchBar> {
|
|||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
),
|
),
|
||||||
onChanged: widget.onSearchChanged,
|
onChanged: widget.onSearchChanged, // Call the callback on text change
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: ColorsManager.textFieldGreyColor,
|
fillColor: ColorsManager.textFieldGreyColor,
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
// File generated by FlutterFire CLI.
|
// File generated by FlutterFire CLI.
|
||||||
// ignore_for_file: type=lint
|
// ignore_for_file: type=lint
|
||||||
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
||||||
import 'package:flutter/foundation.dart'
|
import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb, TargetPlatform;
|
||||||
show defaultTargetPlatform, kIsWeb, TargetPlatform;
|
|
||||||
|
|
||||||
/// Default [FirebaseOptions] for use with your Firebase apps.
|
/// Default [FirebaseOptions] for use with your Firebase apps.
|
||||||
///
|
///
|
||||||
|
@ -40,7 +40,7 @@ class MyApp extends StatelessWidget {
|
|||||||
initialLocation: RoutesConst.auth,
|
initialLocation: RoutesConst.auth,
|
||||||
routes: AppRoutes.getRoutes(),
|
routes: AppRoutes.getRoutes(),
|
||||||
redirect: (context, state) async {
|
redirect: (context, state) async {
|
||||||
final checkToken = await AuthBloc.getTokenAndValidate();
|
String checkToken = await AuthBloc.getTokenAndValidate();
|
||||||
final loggedIn = checkToken == 'Success';
|
final loggedIn = checkToken == 'Success';
|
||||||
final goingToLogin = state.uri.toString() == RoutesConst.auth;
|
final goingToLogin = state.uri.toString() == RoutesConst.auth;
|
||||||
|
|
||||||
|
@ -21,8 +21,7 @@ import 'package:syncrow_web/utils/theme/theme.dart';
|
|||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
try {
|
try {
|
||||||
const environment =
|
const environment = String.fromEnvironment('FLAVOR', defaultValue: 'development');
|
||||||
String.fromEnvironment('FLAVOR', defaultValue: 'development');
|
|
||||||
await dotenv.load(fileName: '.env.$environment');
|
await dotenv.load(fileName: '.env.$environment');
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await Firebase.initializeApp(
|
await Firebase.initializeApp(
|
||||||
@ -40,7 +39,7 @@ class MyApp extends StatelessWidget {
|
|||||||
initialLocation: RoutesConst.auth,
|
initialLocation: RoutesConst.auth,
|
||||||
routes: AppRoutes.getRoutes(),
|
routes: AppRoutes.getRoutes(),
|
||||||
redirect: (context, state) async {
|
redirect: (context, state) async {
|
||||||
final checkToken = await AuthBloc.getTokenAndValidate();
|
String checkToken = await AuthBloc.getTokenAndValidate();
|
||||||
final loggedIn = checkToken == 'Success';
|
final loggedIn = checkToken == 'Success';
|
||||||
final goingToLogin = state.uri.toString() == RoutesConst.auth;
|
final goingToLogin = state.uri.toString() == RoutesConst.auth;
|
||||||
|
|
||||||
@ -58,8 +57,7 @@ class MyApp extends StatelessWidget {
|
|||||||
BlocProvider<CreateRoutineBloc>(
|
BlocProvider<CreateRoutineBloc>(
|
||||||
create: (context) => CreateRoutineBloc(),
|
create: (context) => CreateRoutineBloc(),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
||||||
create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
|
||||||
BlocProvider<VisitorPasswordBloc>(
|
BlocProvider<VisitorPasswordBloc>(
|
||||||
create: (context) => VisitorPasswordBloc(),
|
create: (context) => VisitorPasswordBloc(),
|
||||||
),
|
),
|
||||||
|
@ -21,8 +21,7 @@ import 'package:syncrow_web/utils/theme/theme.dart';
|
|||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
try {
|
try {
|
||||||
const environment =
|
const environment = String.fromEnvironment('FLAVOR', defaultValue: 'staging');
|
||||||
String.fromEnvironment('FLAVOR', defaultValue: 'staging');
|
|
||||||
await dotenv.load(fileName: '.env.$environment');
|
await dotenv.load(fileName: '.env.$environment');
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await Firebase.initializeApp(
|
await Firebase.initializeApp(
|
||||||
@ -40,7 +39,7 @@ class MyApp extends StatelessWidget {
|
|||||||
initialLocation: RoutesConst.auth,
|
initialLocation: RoutesConst.auth,
|
||||||
routes: AppRoutes.getRoutes(),
|
routes: AppRoutes.getRoutes(),
|
||||||
redirect: (context, state) async {
|
redirect: (context, state) async {
|
||||||
final checkToken = await AuthBloc.getTokenAndValidate();
|
String checkToken = await AuthBloc.getTokenAndValidate();
|
||||||
final loggedIn = checkToken == 'Success';
|
final loggedIn = checkToken == 'Success';
|
||||||
final goingToLogin = state.uri.toString() == RoutesConst.auth;
|
final goingToLogin = state.uri.toString() == RoutesConst.auth;
|
||||||
|
|
||||||
@ -58,8 +57,7 @@ class MyApp extends StatelessWidget {
|
|||||||
BlocProvider<CreateRoutineBloc>(
|
BlocProvider<CreateRoutineBloc>(
|
||||||
create: (context) => CreateRoutineBloc(),
|
create: (context) => CreateRoutineBloc(),
|
||||||
),
|
),
|
||||||
BlocProvider(
|
BlocProvider(create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
||||||
create: (context) => HomeBloc()..add(const FetchUserInfo())),
|
|
||||||
BlocProvider<VisitorPasswordBloc>(
|
BlocProvider<VisitorPasswordBloc>(
|
||||||
create: (context) => VisitorPasswordBloc(),
|
create: (context) => VisitorPasswordBloc(),
|
||||||
),
|
),
|
||||||
|
@ -11,7 +11,7 @@ import 'package:syncrow_web/utils/constants/app_enum.dart';
|
|||||||
import 'package:syncrow_web/utils/snack_bar.dart';
|
import 'package:syncrow_web/utils/snack_bar.dart';
|
||||||
|
|
||||||
class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
||||||
AccessBloc() : super(AccessInitial()) {
|
AccessBloc() : super((AccessInitial())) {
|
||||||
on<FetchTableData>(_onFetchTableData);
|
on<FetchTableData>(_onFetchTableData);
|
||||||
on<SelectTime>(selectTime);
|
on<SelectTime>(selectTime);
|
||||||
on<FilterDataEvent>(_filterData);
|
on<FilterDataEvent>(_filterData);
|
||||||
@ -43,12 +43,12 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateTabsCount() {
|
void updateTabsCount() {
|
||||||
final toBeEffectiveCount = data
|
int toBeEffectiveCount = data
|
||||||
.where((item) => item.passwordStatus.value == 'To be effective')
|
.where((item) => item.passwordStatus.value == 'To be effective')
|
||||||
.length;
|
.length;
|
||||||
final effectiveCount =
|
int effectiveCount =
|
||||||
data.where((item) => item.passwordStatus.value == 'Effective').length;
|
data.where((item) => item.passwordStatus.value == 'Effective').length;
|
||||||
final expiredCount =
|
int expiredCount =
|
||||||
data.where((item) => item.passwordStatus.value == 'Expired').length;
|
data.where((item) => item.passwordStatus.value == 'Expired').length;
|
||||||
tabs[1] = 'To Be Effective ($toBeEffectiveCount)';
|
tabs[1] = 'To Be Effective ($toBeEffectiveCount)';
|
||||||
tabs[2] = 'Effective ($effectiveCount)';
|
tabs[2] = 'Effective ($effectiveCount)';
|
||||||
@ -81,7 +81,7 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
Emitter<AccessState> emit,
|
Emitter<AccessState> emit,
|
||||||
) async {
|
) async {
|
||||||
emit(AccessLoaded());
|
emit(AccessLoaded());
|
||||||
final picked = await showDatePicker(
|
final DateTime? picked = await showDatePicker(
|
||||||
context: event.context,
|
context: event.context,
|
||||||
initialDate: DateTime.now(),
|
initialDate: DateTime.now(),
|
||||||
firstDate: DateTime.now().add(const Duration(days: -5095)),
|
firstDate: DateTime.now().add(const Duration(days: -5095)),
|
||||||
@ -89,7 +89,7 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
builder: (BuildContext context, Widget? child) {
|
builder: (BuildContext context, Widget? child) {
|
||||||
return Theme(
|
return Theme(
|
||||||
data: ThemeData.light().copyWith(
|
data: ThemeData.light().copyWith(
|
||||||
colorScheme: const ColorScheme.light(
|
colorScheme: ColorScheme.light(
|
||||||
primary: ColorsManager.blackColor,
|
primary: ColorsManager.blackColor,
|
||||||
onPrimary: Colors.white,
|
onPrimary: Colors.white,
|
||||||
onSurface: ColorsManager.grayColor,
|
onSurface: ColorsManager.grayColor,
|
||||||
@ -105,20 +105,20 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (picked != null) {
|
if (picked != null) {
|
||||||
final timePicked = await showHourPicker(
|
final TimeOfDay? timePicked = await showHourPicker(
|
||||||
context: event.context,
|
context: event.context,
|
||||||
initialTime: TimeOfDay.now(),
|
initialTime: TimeOfDay.now(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (timePicked != null) {
|
if (timePicked != null) {
|
||||||
final selectedDateTime = DateTime(
|
final DateTime selectedDateTime = DateTime(
|
||||||
picked.year,
|
picked.year,
|
||||||
picked.month,
|
picked.month,
|
||||||
picked.day,
|
picked.day,
|
||||||
timePicked.hour,
|
timePicked.hour,
|
||||||
timePicked.minute,
|
timePicked.minute,
|
||||||
);
|
);
|
||||||
final selectedTimestamp =
|
final int selectedTimestamp =
|
||||||
selectedDateTime.millisecondsSinceEpoch ~/ 1000;
|
selectedDateTime.millisecondsSinceEpoch ~/ 1000;
|
||||||
if (event.isStart) {
|
if (event.isStart) {
|
||||||
if (expirationTimeTimeStamp != null &&
|
if (expirationTimeTimeStamp != null &&
|
||||||
@ -152,35 +152,39 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
final searchText = event.passwordName?.toLowerCase() ?? '';
|
final searchText = event.passwordName?.toLowerCase() ?? '';
|
||||||
final searchEmailText = event.emailAuthorizer?.toLowerCase() ?? '';
|
final searchEmailText = event.emailAuthorizer?.toLowerCase() ?? '';
|
||||||
filteredData = data.where((item) {
|
filteredData = data.where((item) {
|
||||||
var matchesCriteria = true;
|
bool matchesCriteria = true;
|
||||||
// Convert timestamp to DateTime and extract date component
|
// Convert timestamp to DateTime and extract date component
|
||||||
final effectiveDate = DateTime.fromMillisecondsSinceEpoch(
|
DateTime effectiveDate = DateTime.fromMillisecondsSinceEpoch(
|
||||||
int.parse(item.effectiveTime.toString()) * 1000)
|
int.parse(item.effectiveTime.toString()) * 1000)
|
||||||
.toUtc()
|
.toUtc()
|
||||||
.toLocal();
|
.toLocal();
|
||||||
final invalidDate = DateTime.fromMillisecondsSinceEpoch(
|
DateTime invalidDate = DateTime.fromMillisecondsSinceEpoch(
|
||||||
int.parse(item.invalidTime.toString()) * 1000)
|
int.parse(item.invalidTime.toString()) * 1000)
|
||||||
.toUtc()
|
.toUtc()
|
||||||
.toLocal();
|
.toLocal();
|
||||||
final effectiveDateAndTime = DateTime(
|
DateTime effectiveDateAndTime = DateTime(
|
||||||
effectiveDate.year,
|
effectiveDate.year,
|
||||||
effectiveDate.month,
|
effectiveDate.month,
|
||||||
effectiveDate.day,
|
effectiveDate.day,
|
||||||
effectiveDate.hour,
|
effectiveDate.hour,
|
||||||
effectiveDate.minute);
|
effectiveDate.minute);
|
||||||
final invalidDateAndTime = DateTime(invalidDate.year, invalidDate.month,
|
DateTime invalidDateAndTime = DateTime(
|
||||||
invalidDate.day, invalidDate.hour, invalidDate.minute);
|
invalidDate.year,
|
||||||
|
invalidDate.month,
|
||||||
|
invalidDate.day,
|
||||||
|
invalidDate.hour,
|
||||||
|
invalidDate.minute);
|
||||||
|
|
||||||
// Filter by password name, making the search case-insensitive
|
// Filter by password name, making the search case-insensitive
|
||||||
if (searchText.isNotEmpty) {
|
if (searchText.isNotEmpty) {
|
||||||
final matchesName =
|
final bool matchesName =
|
||||||
item.passwordName.toString().toLowerCase().contains(searchText);
|
item.passwordName.toString().toLowerCase().contains(searchText);
|
||||||
if (!matchesName) {
|
if (!matchesName) {
|
||||||
matchesCriteria = false;
|
matchesCriteria = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (searchEmailText.isNotEmpty) {
|
if (searchEmailText.isNotEmpty) {
|
||||||
final matchesName = item.authorizerEmail
|
final bool matchesName = item.authorizerEmail
|
||||||
.toString()
|
.toString()
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.contains(searchEmailText);
|
.contains(searchEmailText);
|
||||||
@ -190,7 +194,7 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
}
|
}
|
||||||
// Filter by start date only
|
// Filter by start date only
|
||||||
if (event.startTime != null && event.endTime == null) {
|
if (event.startTime != null && event.endTime == null) {
|
||||||
var startDateTime =
|
DateTime startDateTime =
|
||||||
DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000)
|
DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000)
|
||||||
.toUtc()
|
.toUtc()
|
||||||
.toLocal();
|
.toLocal();
|
||||||
@ -202,7 +206,7 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
}
|
}
|
||||||
// Filter by end date only
|
// Filter by end date only
|
||||||
if (event.endTime != null && event.startTime == null) {
|
if (event.endTime != null && event.startTime == null) {
|
||||||
var startDateTime =
|
DateTime startDateTime =
|
||||||
DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000)
|
DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000)
|
||||||
.toUtc()
|
.toUtc()
|
||||||
.toLocal();
|
.toLocal();
|
||||||
@ -215,11 +219,11 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
|
|
||||||
// Filter by both start date and end date
|
// Filter by both start date and end date
|
||||||
if (event.startTime != null && event.endTime != null) {
|
if (event.startTime != null && event.endTime != null) {
|
||||||
var startDateTime =
|
DateTime startDateTime =
|
||||||
DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000)
|
DateTime.fromMillisecondsSinceEpoch(event.startTime! * 1000)
|
||||||
.toUtc()
|
.toUtc()
|
||||||
.toLocal();
|
.toLocal();
|
||||||
var endDateTime =
|
DateTime endDateTime =
|
||||||
DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000)
|
DateTime.fromMillisecondsSinceEpoch(event.endTime! * 1000)
|
||||||
.toUtc()
|
.toUtc()
|
||||||
.toLocal();
|
.toLocal();
|
||||||
@ -254,7 +258,7 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> resetSearch(ResetSearch event, Emitter<AccessState> emit) async {
|
resetSearch(ResetSearch event, Emitter<AccessState> emit) async {
|
||||||
emit(AccessLoaded());
|
emit(AccessLoaded());
|
||||||
startTime = 'Start Time';
|
startTime = 'Start Time';
|
||||||
endTime = 'End Time';
|
endTime = 'End Time';
|
||||||
@ -268,7 +272,7 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String timestampToDate(dynamic timestamp) {
|
String timestampToDate(dynamic timestamp) {
|
||||||
final dateTime =
|
DateTime dateTime =
|
||||||
DateTime.fromMillisecondsSinceEpoch(int.parse(timestamp) * 1000);
|
DateTime.fromMillisecondsSinceEpoch(int.parse(timestamp) * 1000);
|
||||||
return "${dateTime.year}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.day.toString().padLeft(2, '0')} "
|
return "${dateTime.year}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.day.toString().padLeft(2, '0')} "
|
||||||
" ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}";
|
" ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}";
|
||||||
@ -285,17 +289,17 @@ class AccessBloc extends Bloc<AccessEvent, AccessState> {
|
|||||||
break;
|
break;
|
||||||
case 1: // To Be Effective
|
case 1: // To Be Effective
|
||||||
filteredData = data
|
filteredData = data
|
||||||
.where((item) => item.passwordStatus.value == 'To Be Effective')
|
.where((item) => item.passwordStatus.value == "To Be Effective")
|
||||||
.toList();
|
.toList();
|
||||||
break;
|
break;
|
||||||
case 2: // Effective
|
case 2: // Effective
|
||||||
filteredData = data
|
filteredData = data
|
||||||
.where((item) => item.passwordStatus.value == 'Effective')
|
.where((item) => item.passwordStatus.value == "Effective")
|
||||||
.toList();
|
.toList();
|
||||||
break;
|
break;
|
||||||
case 3: // Expired
|
case 3: // Expired
|
||||||
filteredData = data
|
filteredData = data
|
||||||
.where((item) => item.passwordStatus.value == 'Expired')
|
.where((item) => item.passwordStatus.value == "Expired")
|
||||||
.toList();
|
.toList();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -15,7 +15,7 @@ class AccessLoaded extends AccessState {}
|
|||||||
class FailedState extends AccessState {
|
class FailedState extends AccessState {
|
||||||
final String message;
|
final String message;
|
||||||
|
|
||||||
const FailedState(this.message);
|
FailedState(this.message);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [message];
|
List<Object> get props => [message];
|
||||||
|
@ -36,7 +36,7 @@ class PasswordModel {
|
|||||||
effectiveTime: json['effectiveTime'],
|
effectiveTime: json['effectiveTime'],
|
||||||
passwordCreated: json['passwordCreated'],
|
passwordCreated: json['passwordCreated'],
|
||||||
createdTime: json['createdTime'],
|
createdTime: json['createdTime'],
|
||||||
passwordName: json['passwordName'] ?? 'No Name',
|
passwordName: json['passwordName']??'No Name',
|
||||||
passwordStatus: AccessStatusExtension.fromString(json['passwordStatus']),
|
passwordStatus: AccessStatusExtension.fromString(json['passwordStatus']),
|
||||||
passwordType: AccessTypeExtension.fromString(json['passwordType']),
|
passwordType: AccessTypeExtension.fromString(json['passwordType']),
|
||||||
deviceUuid: json['deviceUuid'],
|
deviceUuid: json['deviceUuid'],
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class DashedBorderPainter extends CustomPainter {
|
class DashedBorderPainter extends CustomPainter {
|
||||||
@ -18,28 +20,27 @@ class DashedBorderPainter extends CustomPainter {
|
|||||||
..strokeWidth = 0.5
|
..strokeWidth = 0.5
|
||||||
..style = PaintingStyle.stroke;
|
..style = PaintingStyle.stroke;
|
||||||
|
|
||||||
final topPath = Path()
|
final Path topPath = Path()
|
||||||
..moveTo(0, 0)
|
..moveTo(0, 0)
|
||||||
..lineTo(size.width, 0);
|
..lineTo(size.width, 0);
|
||||||
|
|
||||||
final bottomPath = Path()
|
final Path bottomPath = Path()
|
||||||
..moveTo(0, size.height)
|
..moveTo(0, size.height)
|
||||||
..lineTo(size.width, size.height);
|
..lineTo(size.width, size.height);
|
||||||
|
|
||||||
final dashedTopPath = _createDashedPath(topPath, dashWidth, dashSpace);
|
final dashedTopPath = _createDashedPath(topPath, dashWidth, dashSpace);
|
||||||
final dashedBottomPath =
|
final dashedBottomPath = _createDashedPath(bottomPath, dashWidth, dashSpace);
|
||||||
_createDashedPath(bottomPath, dashWidth, dashSpace);
|
|
||||||
|
|
||||||
canvas.drawPath(dashedTopPath, paint);
|
canvas.drawPath(dashedTopPath, paint);
|
||||||
canvas.drawPath(dashedBottomPath, paint);
|
canvas.drawPath(dashedBottomPath, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
Path _createDashedPath(Path source, double dashWidth, double dashSpace) {
|
Path _createDashedPath(Path source, double dashWidth, double dashSpace) {
|
||||||
final dashedPath = Path();
|
final Path dashedPath = Path();
|
||||||
for (final pathMetric in source.computeMetrics()) {
|
for (PathMetric pathMetric in source.computeMetrics()) {
|
||||||
var distance = 0.0;
|
double distance = 0.0;
|
||||||
while (distance < pathMetric.length) {
|
while (distance < pathMetric.length) {
|
||||||
final nextDistance = distance + dashWidth;
|
final double nextDistance = distance + dashWidth;
|
||||||
dashedPath.addPath(
|
dashedPath.addPath(
|
||||||
pathMetric.extractPath(distance, nextDistance),
|
pathMetric.extractPath(distance, nextDistance),
|
||||||
Offset.zero,
|
Offset.zero,
|
||||||
|
@ -16,4 +16,4 @@ extension GetMonthNameFromNumber on num {
|
|||||||
_ => 'N/A'
|
_ => 'N/A'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,8 +15,7 @@ class AirQualityDataModel extends Equatable {
|
|||||||
return AirQualityDataModel(
|
return AirQualityDataModel(
|
||||||
date: DateTime.parse(json['date'] as String),
|
date: DateTime.parse(json['date'] as String),
|
||||||
data: (json['data'] as List<dynamic>)
|
data: (json['data'] as List<dynamic>)
|
||||||
.map((e) =>
|
.map((e) => AirQualityPercentageData.fromJson(e as Map<String, dynamic>))
|
||||||
AirQualityPercentageData.fromJson(e as Map<String, dynamic>))
|
|
||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -47,7 +46,7 @@ class AirQualityPercentageData extends Equatable {
|
|||||||
|
|
||||||
factory AirQualityPercentageData.fromJson(Map<String, dynamic> json) {
|
factory AirQualityPercentageData.fromJson(Map<String, dynamic> json) {
|
||||||
return AirQualityPercentageData(
|
return AirQualityPercentageData(
|
||||||
type: json['type'] as String? ?? '',
|
type: json['type'] as String? ?? '',
|
||||||
name: json['name'] as String? ?? '',
|
name: json['name'] as String? ?? '',
|
||||||
percentage: (json['percentage'] as num?)?.toDouble() ?? 0,
|
percentage: (json['percentage'] as num?)?.toDouble() ?? 0,
|
||||||
);
|
);
|
||||||
|
@ -36,14 +36,11 @@ class AnalyticsDevice {
|
|||||||
deviceTuyaUuid: json['deviceTuyaUuid'] as String?,
|
deviceTuyaUuid: json['deviceTuyaUuid'] as String?,
|
||||||
isActive: json['isActive'] as bool?,
|
isActive: json['isActive'] as bool?,
|
||||||
productDevice: json['productDevice'] != null
|
productDevice: json['productDevice'] != null
|
||||||
? ProductDevice.fromJson(
|
? ProductDevice.fromJson(json['productDevice'] as Map<String, dynamic>)
|
||||||
json['productDevice'] as Map<String, dynamic>)
|
|
||||||
: null,
|
: null,
|
||||||
spaceUuid: json['spaceUuid'] as String?,
|
spaceUuid: json['spaceUuid'] as String?,
|
||||||
latitude:
|
latitude: json['lat'] != null ? double.parse(json['lat'] as String) : null,
|
||||||
json['lat'] != null ? double.parse(json['lat'] as String) : null,
|
longitude: json['lon'] != null ? double.parse(json['lon'] as String) : null,
|
||||||
longitude:
|
|
||||||
json['lon'] != null ? double.parse(json['lon'] as String) : null,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,7 @@ class Occupacy extends Equatable {
|
|||||||
|
|
||||||
factory Occupacy.fromJson(Map<String, dynamic> json) {
|
factory Occupacy.fromJson(Map<String, dynamic> json) {
|
||||||
return Occupacy(
|
return Occupacy(
|
||||||
date:
|
date: DateTime.parse(json['event_date'] as String? ?? '${DateTime.now()}'),
|
||||||
DateTime.parse(json['event_date'] as String? ?? '${DateTime.now()}'),
|
|
||||||
occupancy: (json['occupancy_percentage'] ?? 0).toString(),
|
occupancy: (json['occupancy_percentage'] ?? 0).toString(),
|
||||||
spaceUuid: json['space_uuid'] as String? ?? '',
|
spaceUuid: json['space_uuid'] as String? ?? '',
|
||||||
occupiedSeconds: json['occupied_seconds'] as int? ?? 0,
|
occupiedSeconds: json['occupied_seconds'] as int? ?? 0,
|
||||||
|
@ -19,8 +19,7 @@ class OccupancyHeatMapModel extends Equatable {
|
|||||||
eventDate: DateTime.parse(
|
eventDate: DateTime.parse(
|
||||||
json['event_date'] as String? ?? '${DateTime.now()}',
|
json['event_date'] as String? ?? '${DateTime.now()}',
|
||||||
),
|
),
|
||||||
countTotalPresenceDetected:
|
countTotalPresenceDetected: json['count_total_presence_detected'] as int? ?? 0,
|
||||||
json['count_total_presence_detected'] as int? ?? 0,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,8 +33,7 @@ class AirQualityDistributionBloc
|
|||||||
state.copyWith(
|
state.copyWith(
|
||||||
status: AirQualityDistributionStatus.success,
|
status: AirQualityDistributionStatus.success,
|
||||||
chartData: result,
|
chartData: result,
|
||||||
filteredChartData:
|
filteredChartData: _arrangeChartDataByType(result, state.selectedAqiType),
|
||||||
_arrangeChartDataByType(result, state.selectedAqiType),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -62,8 +61,7 @@ class AirQualityDistributionBloc
|
|||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
selectedAqiType: event.aqiType,
|
selectedAqiType: event.aqiType,
|
||||||
filteredChartData:
|
filteredChartData: _arrangeChartDataByType(state.chartData, event.aqiType),
|
||||||
_arrangeChartDataByType(state.chartData, event.aqiType),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,7 @@ import 'package:syncrow_web/pages/analytics/services/device_location/device_loca
|
|||||||
part 'device_location_event.dart';
|
part 'device_location_event.dart';
|
||||||
part 'device_location_state.dart';
|
part 'device_location_state.dart';
|
||||||
|
|
||||||
class DeviceLocationBloc
|
class DeviceLocationBloc extends Bloc<DeviceLocationEvent, DeviceLocationState> {
|
||||||
extends Bloc<DeviceLocationEvent, DeviceLocationState> {
|
|
||||||
DeviceLocationBloc(
|
DeviceLocationBloc(
|
||||||
this._deviceLocationService,
|
this._deviceLocationService,
|
||||||
) : super(const DeviceLocationState()) {
|
) : super(const DeviceLocationState()) {
|
||||||
|
@ -53,8 +53,7 @@ class RangeOfAqiBloc extends Bloc<RangeOfAqiEvent, RangeOfAqiState> {
|
|||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
selectedAqiType: event.aqiType,
|
selectedAqiType: event.aqiType,
|
||||||
filteredRangeOfAqi:
|
filteredRangeOfAqi: _arrangeChartDataByType(state.rangeOfAqi, event.aqiType),
|
||||||
_arrangeChartDataByType(state.rangeOfAqi, event.aqiType),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -105,8 +105,7 @@ abstract final class RangeOfAqiChartsHelper {
|
|||||||
tooltipRoundedRadius: 16,
|
tooltipRoundedRadius: 16,
|
||||||
showOnTopOfTheChartBoxArea: false,
|
showOnTopOfTheChartBoxArea: false,
|
||||||
tooltipPadding: const EdgeInsets.all(8),
|
tooltipPadding: const EdgeInsets.all(8),
|
||||||
getTooltipItems: (touchedSpots) =>
|
getTooltipItems: (touchedSpots) => RangeOfAqiChartsHelper.getTooltipItems(
|
||||||
RangeOfAqiChartsHelper.getTooltipItems(
|
|
||||||
touchedSpots,
|
touchedSpots,
|
||||||
chartData,
|
chartData,
|
||||||
),
|
),
|
||||||
|
@ -81,8 +81,7 @@ class AqiDeviceInfo extends StatelessWidget {
|
|||||||
aqiLevel: status
|
aqiLevel: status
|
||||||
.firstWhere(
|
.firstWhere(
|
||||||
(e) => e.code == 'air_quality_index',
|
(e) => e.code == 'air_quality_index',
|
||||||
orElse: () =>
|
orElse: () => Status(code: 'air_quality_index', value: ''),
|
||||||
Status(code: 'air_quality_index', value: ''),
|
|
||||||
)
|
)
|
||||||
.value
|
.value
|
||||||
.toString(),
|
.toString(),
|
||||||
|
@ -36,25 +36,23 @@ class AqiDistributionChart extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<BarChartGroupData> _buildBarGroups(
|
List<BarChartGroupData> _buildBarGroups(List<AirQualityDataModel> sortedData) {
|
||||||
List<AirQualityDataModel> sortedData) {
|
|
||||||
return List.generate(sortedData.length, (index) {
|
return List.generate(sortedData.length, (index) {
|
||||||
final data = sortedData[index];
|
final data = sortedData[index];
|
||||||
final stackItems = <BarChartRodData>[];
|
final stackItems = <BarChartRodData>[];
|
||||||
double currentY = 0;
|
double currentY = 0;
|
||||||
var isFirstElement = true;
|
bool isFirstElement = true;
|
||||||
|
|
||||||
// Sort data by type to ensure consistent order
|
// Sort data by type to ensure consistent order
|
||||||
final sortedPercentageData =
|
final sortedPercentageData = List<AirQualityPercentageData>.from(data.data)
|
||||||
List<AirQualityPercentageData>.from(data.data)
|
..sort((a, b) => a.type.compareTo(b.type));
|
||||||
..sort((a, b) => a.type.compareTo(b.type));
|
|
||||||
|
|
||||||
for (final percentageData in sortedPercentageData) {
|
for (final percentageData in sortedPercentageData) {
|
||||||
stackItems.add(
|
stackItems.add(
|
||||||
BarChartRodData(
|
BarChartRodData(
|
||||||
fromY: currentY,
|
fromY: currentY,
|
||||||
toY: currentY + percentageData.percentage,
|
toY: currentY + percentageData.percentage ,
|
||||||
color: AirQualityDataModel.metricColors[percentageData.name],
|
color: AirQualityDataModel.metricColors[percentageData.name]!,
|
||||||
borderRadius: isFirstElement
|
borderRadius: isFirstElement
|
||||||
? const BorderRadius.only(
|
? const BorderRadius.only(
|
||||||
topLeft: Radius.circular(22),
|
topLeft: Radius.circular(22),
|
||||||
@ -86,9 +84,9 @@ class AqiDistributionChart extends StatelessWidget {
|
|||||||
tooltipRoundedRadius: 16,
|
tooltipRoundedRadius: 16,
|
||||||
tooltipPadding: const EdgeInsets.all(8),
|
tooltipPadding: const EdgeInsets.all(8),
|
||||||
getTooltipItem: (group, groupIndex, rod, rodIndex) {
|
getTooltipItem: (group, groupIndex, rod, rodIndex) {
|
||||||
final data = chartData[group.x];
|
final data = chartData[group.x.toInt()];
|
||||||
|
|
||||||
final children = <TextSpan>[];
|
final List<TextSpan> children = [];
|
||||||
|
|
||||||
final textStyle = context.textTheme.bodySmall?.copyWith(
|
final textStyle = context.textTheme.bodySmall?.copyWith(
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
@ -96,9 +94,8 @@ class AqiDistributionChart extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Sort data by type to ensure consistent order
|
// Sort data by type to ensure consistent order
|
||||||
final sortedPercentageData =
|
final sortedPercentageData = List<AirQualityPercentageData>.from(data.data)
|
||||||
List<AirQualityPercentageData>.from(data.data)
|
..sort((a, b) => a.type.compareTo(b.type));
|
||||||
..sort((a, b) => a.type.compareTo(b.type));
|
|
||||||
|
|
||||||
for (final percentageData in sortedPercentageData) {
|
for (final percentageData in sortedPercentageData) {
|
||||||
children.add(TextSpan(
|
children.add(TextSpan(
|
||||||
|
@ -49,7 +49,7 @@ class AqiSubValueWidget extends StatelessWidget {
|
|||||||
|
|
||||||
int _getActiveSegmentByRange(double value, (double min, double max) range) {
|
int _getActiveSegmentByRange(double value, (double min, double max) range) {
|
||||||
final ranges = _getRangesForValue(range);
|
final ranges = _getRangesForValue(range);
|
||||||
for (var i = 0; i < ranges.length; i++) {
|
for (int i = 0; i < ranges.length; i++) {
|
||||||
if (value <= ranges[i].max) return i;
|
if (value <= ranges[i].max) return i;
|
||||||
}
|
}
|
||||||
return ranges.length - 1;
|
return ranges.length - 1;
|
||||||
|
@ -29,8 +29,7 @@ class AqiTypeDropdown extends StatefulWidget {
|
|||||||
class _AqiTypeDropdownState extends State<AqiTypeDropdown> {
|
class _AqiTypeDropdownState extends State<AqiTypeDropdown> {
|
||||||
AqiType? _selectedItem = AqiType.aqi;
|
AqiType? _selectedItem = AqiType.aqi;
|
||||||
|
|
||||||
void _updateSelectedItem(AqiType? item) =>
|
void _updateSelectedItem(AqiType? item) => setState(() => _selectedItem = item);
|
||||||
setState(() => _selectedItem = item);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -63,7 +63,7 @@ class RangeOfAqiChart extends StatelessWidget {
|
|||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.bottomCenter,
|
begin: Alignment.bottomCenter,
|
||||||
end: Alignment.topCenter,
|
end: Alignment.topCenter,
|
||||||
stops: const [0.0, 0.2, 0.4, 0.6, 0.8, 1.0],
|
stops: [0.0, 0.2, 0.4, 0.6, 0.8, 1.0],
|
||||||
colors: RangeOfAqiChartsHelper.gradientData.map((e) {
|
colors: RangeOfAqiChartsHelper.gradientData.map((e) {
|
||||||
final (color, _) = e;
|
final (color, _) = e;
|
||||||
return color.withValues(alpha: 0.6);
|
return color.withValues(alpha: 0.6);
|
||||||
@ -99,8 +99,7 @@ class RangeOfAqiChart extends StatelessWidget {
|
|||||||
}) {
|
}) {
|
||||||
const invisibleDot = FlDotData(show: false);
|
const invisibleDot = FlDotData(show: false);
|
||||||
return LineChartBarData(
|
return LineChartBarData(
|
||||||
spots:
|
spots: List.generate(values.length, (i) => FlSpot(i.toDouble(), values[i])),
|
||||||
List.generate(values.length, (i) => FlSpot(i.toDouble(), values[i])),
|
|
||||||
isCurved: true,
|
isCurved: true,
|
||||||
color: color,
|
color: color,
|
||||||
barWidth: 4,
|
barWidth: 4,
|
||||||
|
@ -32,8 +32,7 @@ class RangeOfAqiChartBox extends StatelessWidget {
|
|||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Expanded(
|
Expanded(child: RangeOfAqiChart(chartData: state.filteredRangeOfAqi)),
|
||||||
child: RangeOfAqiChart(chartData: state.filteredRangeOfAqi)),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -8,8 +8,7 @@ sealed class AnalyticsDevicesEvent extends Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final class LoadAnalyticsDevicesEvent extends AnalyticsDevicesEvent {
|
final class LoadAnalyticsDevicesEvent extends AnalyticsDevicesEvent {
|
||||||
const LoadAnalyticsDevicesEvent(
|
const LoadAnalyticsDevicesEvent({required this.param, required this.onSuccess});
|
||||||
{required this.param, required this.onSuccess});
|
|
||||||
|
|
||||||
final GetAnalyticsDevicesParam param;
|
final GetAnalyticsDevicesParam param;
|
||||||
final void Function(AnalyticsDevice device) onSuccess;
|
final void Function(AnalyticsDevice device) onSuccess;
|
||||||
|
@ -7,7 +7,7 @@ sealed class AnalyticsTabEvent extends Equatable {
|
|||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
class UpdateAnalyticsTabEvent extends AnalyticsTabEvent {
|
class UpdateAnalyticsTabEvent extends AnalyticsTabEvent {
|
||||||
const UpdateAnalyticsTabEvent(this.analyticsTab);
|
const UpdateAnalyticsTabEvent(this.analyticsTab);
|
||||||
|
|
||||||
final AnalyticsPageTab analyticsTab;
|
final AnalyticsPageTab analyticsTab;
|
||||||
|
@ -8,8 +8,7 @@ import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
|
|
||||||
final class AirQualityDataLoadingStrategy
|
final class AirQualityDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
||||||
implements AnalyticsDataLoadingStrategy {
|
|
||||||
@override
|
@override
|
||||||
void onCommunitySelected(
|
void onCommunitySelected(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
@ -26,8 +25,7 @@ final class AirQualityDataLoadingStrategy
|
|||||||
SpaceModel space,
|
SpaceModel space,
|
||||||
) {
|
) {
|
||||||
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
||||||
final isSpaceSelected =
|
final isSpaceSelected = spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
|
||||||
spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
|
|
||||||
|
|
||||||
final hasSelectedSpaces = spaceTreeBloc.state.selectedSpaces.isNotEmpty;
|
final hasSelectedSpaces = spaceTreeBloc.state.selectedSpaces.isNotEmpty;
|
||||||
if (hasSelectedSpaces) clearData(context);
|
if (hasSelectedSpaces) clearData(context);
|
||||||
@ -36,7 +34,7 @@ final class AirQualityDataLoadingStrategy
|
|||||||
|
|
||||||
spaceTreeBloc
|
spaceTreeBloc
|
||||||
..add(const SpaceTreeClearSelectionEvent())
|
..add(const SpaceTreeClearSelectionEvent())
|
||||||
..add(OnSpaceSelected(community, space.uuid ?? '', const []));
|
..add(OnSpaceSelected(community, space.uuid ?? '', []));
|
||||||
|
|
||||||
FetchAirQualityDataHelper.loadAirQualityData(
|
FetchAirQualityDataHelper.loadAirQualityData(
|
||||||
context,
|
context,
|
||||||
|
@ -8,8 +8,7 @@ abstract final class AnalyticsDataLoadingStrategyFactory {
|
|||||||
const AnalyticsDataLoadingStrategyFactory._();
|
const AnalyticsDataLoadingStrategyFactory._();
|
||||||
static AnalyticsDataLoadingStrategy getStrategy(AnalyticsPageTab tab) {
|
static AnalyticsDataLoadingStrategy getStrategy(AnalyticsPageTab tab) {
|
||||||
return switch (tab) {
|
return switch (tab) {
|
||||||
AnalyticsPageTab.energyManagement =>
|
AnalyticsPageTab.energyManagement => EnergyManagementDataLoadingStrategy(),
|
||||||
EnergyManagementDataLoadingStrategy(),
|
|
||||||
AnalyticsPageTab.occupancy => OccupancyDataLoadingStrategy(),
|
AnalyticsPageTab.occupancy => OccupancyDataLoadingStrategy(),
|
||||||
AnalyticsPageTab.airQuality => AirQualityDataLoadingStrategy(),
|
AnalyticsPageTab.airQuality => AirQualityDataLoadingStrategy(),
|
||||||
};
|
};
|
||||||
|
@ -7,8 +7,7 @@ import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart';
|
|||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||||
|
|
||||||
class EnergyManagementDataLoadingStrategy
|
class EnergyManagementDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
||||||
implements AnalyticsDataLoadingStrategy {
|
|
||||||
@override
|
@override
|
||||||
void onCommunitySelected(
|
void onCommunitySelected(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
@ -32,8 +31,7 @@ class EnergyManagementDataLoadingStrategy
|
|||||||
SpaceModel space,
|
SpaceModel space,
|
||||||
) {
|
) {
|
||||||
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
||||||
final isSpaceSelected =
|
final isSpaceSelected = spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
|
||||||
spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
|
|
||||||
final hasSelectedSpaces = spaceTreeBloc.state.selectedSpaces.isNotEmpty;
|
final hasSelectedSpaces = spaceTreeBloc.state.selectedSpaces.isNotEmpty;
|
||||||
|
|
||||||
if (isSpaceSelected) {
|
if (isSpaceSelected) {
|
||||||
|
@ -24,8 +24,7 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
|||||||
SpaceModel space,
|
SpaceModel space,
|
||||||
) {
|
) {
|
||||||
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
final spaceTreeBloc = context.read<SpaceTreeBloc>();
|
||||||
final isSpaceSelected =
|
final isSpaceSelected = spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
|
||||||
spaceTreeBloc.state.selectedSpaces.contains(space.uuid);
|
|
||||||
|
|
||||||
final hasSelectedSpaces = spaceTreeBloc.state.selectedSpaces.isNotEmpty;
|
final hasSelectedSpaces = spaceTreeBloc.state.selectedSpaces.isNotEmpty;
|
||||||
if (hasSelectedSpaces) clearData(context);
|
if (hasSelectedSpaces) clearData(context);
|
||||||
@ -34,7 +33,7 @@ class OccupancyDataLoadingStrategy implements AnalyticsDataLoadingStrategy {
|
|||||||
|
|
||||||
spaceTreeBloc
|
spaceTreeBloc
|
||||||
..add(const SpaceTreeClearSelectionEvent())
|
..add(const SpaceTreeClearSelectionEvent())
|
||||||
..add(OnSpaceSelected(community, space.uuid ?? '', const []));
|
..add(OnSpaceSelected(community, space.uuid ?? '', []));
|
||||||
|
|
||||||
FetchOccupancyDataHelper.loadOccupancyData(
|
FetchOccupancyDataHelper.loadOccupancyData(
|
||||||
context,
|
context,
|
||||||
|
@ -10,8 +10,7 @@ class AnalyticsCommunitiesSidebar extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final selectedTab = context.watch<AnalyticsTabBloc>().state;
|
final selectedTab = context.watch<AnalyticsTabBloc>().state;
|
||||||
final strategy =
|
final strategy = AnalyticsDataLoadingStrategyFactory.getStrategy(selectedTab);
|
||||||
AnalyticsDataLoadingStrategyFactory.getStrategy(selectedTab);
|
|
||||||
|
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: AnalyticsSpaceTreeView(
|
child: AnalyticsSpaceTreeView(
|
||||||
|
@ -20,7 +20,7 @@ class AnalyticsDateFilterButton extends StatefulWidget {
|
|||||||
final void Function(DateTime)? onDateSelected;
|
final void Function(DateTime)? onDateSelected;
|
||||||
final DatePickerType datePickerType;
|
final DatePickerType datePickerType;
|
||||||
|
|
||||||
static final Color _color = ColorsManager.blackColor.withValues(alpha: 0.8);
|
static final _color = ColorsManager.blackColor.withValues(alpha: 0.8);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AnalyticsDateFilterButton> createState() =>
|
State<AnalyticsDateFilterButton> createState() =>
|
||||||
|
@ -21,8 +21,8 @@ class AnalyticsPageTabButton extends StatelessWidget {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
AnalyticsDataLoadingStrategyFactory.getStrategy(tab).clearData(context);
|
AnalyticsDataLoadingStrategyFactory.getStrategy(tab).clearData(context);
|
||||||
context.read<AnalyticsTabBloc>().add(
|
context.read<AnalyticsTabBloc>().add(
|
||||||
UpdateAnalyticsTabEvent(tab),
|
UpdateAnalyticsTabEvent(tab),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
tab.title,
|
tab.title,
|
||||||
@ -33,9 +33,8 @@ class AnalyticsPageTabButton extends StatelessWidget {
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w400,
|
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w400,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: isSelected
|
color:
|
||||||
? ColorsManager.slidingBlueColor
|
isSelected ? ColorsManager.slidingBlueColor : ColorsManager.textGray,
|
||||||
: ColorsManager.textGray,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -21,18 +21,18 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
|||||||
int? _selectedMonth;
|
int? _selectedMonth;
|
||||||
|
|
||||||
static const _monthNames = [
|
static const _monthNames = [
|
||||||
'January',
|
"January",
|
||||||
'February',
|
"February",
|
||||||
'March',
|
"March",
|
||||||
'April',
|
"April",
|
||||||
'May',
|
"May",
|
||||||
'June',
|
"June",
|
||||||
'July',
|
"July",
|
||||||
'August',
|
"August",
|
||||||
'September',
|
"September",
|
||||||
'October',
|
"October",
|
||||||
'November',
|
"November",
|
||||||
'December',
|
"December",
|
||||||
];
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -189,19 +189,14 @@ class _MonthPickerWidgetState extends State<MonthPickerWidget> {
|
|||||||
final isFutureMonth = isCurrentYear && index > currentDate.month - 1;
|
final isFutureMonth = isCurrentYear && index > currentDate.month - 1;
|
||||||
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: isFutureMonth
|
onTap: isFutureMonth ? null : () => setState(() => _selectedMonth = index),
|
||||||
? null
|
|
||||||
: () => setState(() => _selectedMonth = index),
|
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFEDF2F7),
|
color: const Color(0xFFEDF2F7),
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topLeft:
|
topLeft: index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||||
index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
bottomLeft: index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||||
bottomLeft:
|
topRight: index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
||||||
index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
|
||||||
topRight:
|
|
||||||
index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
|
||||||
bottomRight:
|
bottomRight:
|
||||||
index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
||||||
),
|
),
|
||||||
|
@ -53,8 +53,7 @@ class _AnalyticsSpaceTreeViewState extends State<AnalyticsSpaceTreeView> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(
|
return BlocBuilder<SpaceTreeBloc, SpaceTreeState>(builder: (context, state) {
|
||||||
builder: (context, state) {
|
|
||||||
final communities = state.searchQuery.isNotEmpty
|
final communities = state.searchQuery.isNotEmpty
|
||||||
? state.filteredCommunity
|
? state.filteredCommunity
|
||||||
: state.communityList;
|
: state.communityList;
|
||||||
@ -77,10 +76,9 @@ class _AnalyticsSpaceTreeViewState extends State<AnalyticsSpaceTreeView> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
CustomSearchBar(
|
CustomSearchBar(
|
||||||
onSearchChanged: (query) =>
|
onSearchChanged: (query) => context.read<SpaceTreeBloc>().add(
|
||||||
context.read<SpaceTreeBloc>().add(
|
SearchQueryEvent(query),
|
||||||
SearchQueryEvent(query),
|
),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -115,8 +113,7 @@ class _AnalyticsSpaceTreeViewState extends State<AnalyticsSpaceTreeView> {
|
|||||||
isExpanded: state.expandedCommunities.contains(
|
isExpanded: state.expandedCommunities.contains(
|
||||||
communities[index].uuid,
|
communities[index].uuid,
|
||||||
),
|
),
|
||||||
onItemSelected: () =>
|
onItemSelected: () => widget.onSelectCommunity?.call(
|
||||||
widget.onSelectCommunity?.call(
|
|
||||||
communities[index],
|
communities[index],
|
||||||
communities[index].spaces,
|
communities[index].spaces,
|
||||||
),
|
),
|
||||||
@ -124,8 +121,8 @@ class _AnalyticsSpaceTreeViewState extends State<AnalyticsSpaceTreeView> {
|
|||||||
(space) {
|
(space) {
|
||||||
return CustomExpansionTileSpaceTree(
|
return CustomExpansionTileSpaceTree(
|
||||||
title: space.name,
|
title: space.name,
|
||||||
isExpanded: state.expandedSpaces
|
isExpanded:
|
||||||
.contains(space.uuid),
|
state.expandedSpaces.contains(space.uuid),
|
||||||
onItemSelected: () =>
|
onItemSelected: () =>
|
||||||
widget.onSelectSpace?.call(
|
widget.onSelectSpace?.call(
|
||||||
communities[index],
|
communities[index],
|
||||||
@ -156,8 +153,7 @@ class _AnalyticsSpaceTreeViewState extends State<AnalyticsSpaceTreeView> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (state.paginationIsLoading)
|
if (state.paginationIsLoading) const CircularProgressIndicator(),
|
||||||
const CircularProgressIndicator(),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -19,9 +19,9 @@ class YearPickerWidget extends StatefulWidget {
|
|||||||
class _YearPickerWidgetState extends State<YearPickerWidget> {
|
class _YearPickerWidgetState extends State<YearPickerWidget> {
|
||||||
late int _currentYear;
|
late int _currentYear;
|
||||||
|
|
||||||
static final List<int> years = List.generate(
|
static final years = List.generate(
|
||||||
DateTime.now().year - (DateTime.now().year - 5) + 1,
|
DateTime.now().year - (DateTime.now().year - 5) + 1,
|
||||||
(index) => 2020 + index,
|
(index) => (2020 + index),
|
||||||
).where((year) => year <= DateTime.now().year).toList();
|
).where((year) => year <= DateTime.now().year).toList();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -123,12 +123,9 @@ class _YearPickerWidgetState extends State<YearPickerWidget> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFEDF2F7),
|
color: const Color(0xFFEDF2F7),
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topLeft:
|
topLeft: index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||||
index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
bottomLeft: index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
||||||
bottomLeft:
|
topRight: index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
||||||
index % 3 == 0 ? const Radius.circular(16) : Radius.zero,
|
|
||||||
topRight:
|
|
||||||
index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
|
||||||
bottomRight:
|
bottomRight:
|
||||||
index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
index % 3 == 2 ? const Radius.circular(16) : Radius.zero,
|
||||||
),
|
),
|
||||||
|
@ -8,15 +8,13 @@ import 'package:syncrow_web/services/api/api_exception.dart';
|
|||||||
part 'energy_consumption_by_phases_event.dart';
|
part 'energy_consumption_by_phases_event.dart';
|
||||||
part 'energy_consumption_by_phases_state.dart';
|
part 'energy_consumption_by_phases_state.dart';
|
||||||
|
|
||||||
class EnergyConsumptionByPhasesBloc extends Bloc<EnergyConsumptionByPhasesEvent,
|
class EnergyConsumptionByPhasesBloc
|
||||||
EnergyConsumptionByPhasesState> {
|
extends Bloc<EnergyConsumptionByPhasesEvent, EnergyConsumptionByPhasesState> {
|
||||||
EnergyConsumptionByPhasesBloc(
|
EnergyConsumptionByPhasesBloc(
|
||||||
this._energyConsumptionByPhasesService,
|
this._energyConsumptionByPhasesService,
|
||||||
) : super(const EnergyConsumptionByPhasesState()) {
|
) : super(const EnergyConsumptionByPhasesState()) {
|
||||||
on<LoadEnergyConsumptionByPhasesEvent>(
|
on<LoadEnergyConsumptionByPhasesEvent>(_onLoadEnergyConsumptionByPhasesEvent);
|
||||||
_onLoadEnergyConsumptionByPhasesEvent);
|
on<ClearEnergyConsumptionByPhasesEvent>(_onClearEnergyConsumptionByPhasesEvent);
|
||||||
on<ClearEnergyConsumptionByPhasesEvent>(
|
|
||||||
_onClearEnergyConsumptionByPhasesEvent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final EnergyConsumptionByPhasesService _energyConsumptionByPhasesService;
|
final EnergyConsumptionByPhasesService _energyConsumptionByPhasesService;
|
||||||
@ -27,8 +25,7 @@ class EnergyConsumptionByPhasesBloc extends Bloc<EnergyConsumptionByPhasesEvent,
|
|||||||
) async {
|
) async {
|
||||||
emit(state.copyWith(status: EnergyConsumptionByPhasesStatus.loading));
|
emit(state.copyWith(status: EnergyConsumptionByPhasesStatus.loading));
|
||||||
try {
|
try {
|
||||||
final chartData =
|
final chartData = await _energyConsumptionByPhasesService.load(event.param);
|
||||||
await _energyConsumptionByPhasesService.load(event.param);
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
status: EnergyConsumptionByPhasesStatus.loaded,
|
status: EnergyConsumptionByPhasesStatus.loaded,
|
||||||
@ -52,7 +49,7 @@ class EnergyConsumptionByPhasesBloc extends Bloc<EnergyConsumptionByPhasesEvent,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onClearEnergyConsumptionByPhasesEvent(
|
void _onClearEnergyConsumptionByPhasesEvent(
|
||||||
ClearEnergyConsumptionByPhasesEvent event,
|
ClearEnergyConsumptionByPhasesEvent event,
|
||||||
Emitter<EnergyConsumptionByPhasesState> emit,
|
Emitter<EnergyConsumptionByPhasesState> emit,
|
||||||
) async {
|
) async {
|
||||||
|
@ -7,8 +7,7 @@ sealed class EnergyConsumptionByPhasesEvent extends Equatable {
|
|||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
class LoadEnergyConsumptionByPhasesEvent
|
class LoadEnergyConsumptionByPhasesEvent extends EnergyConsumptionByPhasesEvent {
|
||||||
extends EnergyConsumptionByPhasesEvent {
|
|
||||||
const LoadEnergyConsumptionByPhasesEvent({
|
const LoadEnergyConsumptionByPhasesEvent({
|
||||||
required this.param,
|
required this.param,
|
||||||
});
|
});
|
||||||
@ -19,7 +18,6 @@ class LoadEnergyConsumptionByPhasesEvent
|
|||||||
List<Object> get props => [param];
|
List<Object> get props => [param];
|
||||||
}
|
}
|
||||||
|
|
||||||
final class ClearEnergyConsumptionByPhasesEvent
|
final class ClearEnergyConsumptionByPhasesEvent extends EnergyConsumptionByPhasesEvent {
|
||||||
extends EnergyConsumptionByPhasesEvent {
|
|
||||||
const ClearEnergyConsumptionByPhasesEvent();
|
const ClearEnergyConsumptionByPhasesEvent();
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,12 @@ import 'package:syncrow_web/services/api/api_exception.dart';
|
|||||||
part 'energy_consumption_per_device_event.dart';
|
part 'energy_consumption_per_device_event.dart';
|
||||||
part 'energy_consumption_per_device_state.dart';
|
part 'energy_consumption_per_device_state.dart';
|
||||||
|
|
||||||
class EnergyConsumptionPerDeviceBloc extends Bloc<
|
class EnergyConsumptionPerDeviceBloc
|
||||||
EnergyConsumptionPerDeviceEvent, EnergyConsumptionPerDeviceState> {
|
extends Bloc<EnergyConsumptionPerDeviceEvent, EnergyConsumptionPerDeviceState> {
|
||||||
EnergyConsumptionPerDeviceBloc(
|
EnergyConsumptionPerDeviceBloc(
|
||||||
this._energyConsumptionPerDeviceService,
|
this._energyConsumptionPerDeviceService,
|
||||||
) : super(const EnergyConsumptionPerDeviceState()) {
|
) : super(const EnergyConsumptionPerDeviceState()) {
|
||||||
on<LoadEnergyConsumptionPerDeviceEvent>(
|
on<LoadEnergyConsumptionPerDeviceEvent>(_onLoadEnergyConsumptionPerDeviceEvent);
|
||||||
_onLoadEnergyConsumptionPerDeviceEvent);
|
|
||||||
on<ClearEnergyConsumptionPerDeviceEvent>(
|
on<ClearEnergyConsumptionPerDeviceEvent>(
|
||||||
_onClearEnergyConsumptionPerDeviceEvent);
|
_onClearEnergyConsumptionPerDeviceEvent);
|
||||||
}
|
}
|
||||||
@ -27,8 +26,7 @@ class EnergyConsumptionPerDeviceBloc extends Bloc<
|
|||||||
) async {
|
) async {
|
||||||
emit(state.copyWith(status: EnergyConsumptionPerDeviceStatus.loading));
|
emit(state.copyWith(status: EnergyConsumptionPerDeviceStatus.loading));
|
||||||
try {
|
try {
|
||||||
final chartData =
|
final chartData = await _energyConsumptionPerDeviceService.load(event.param);
|
||||||
await _energyConsumptionPerDeviceService.load(event.param);
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
status: EnergyConsumptionPerDeviceStatus.loaded,
|
status: EnergyConsumptionPerDeviceStatus.loaded,
|
||||||
@ -52,7 +50,7 @@ class EnergyConsumptionPerDeviceBloc extends Bloc<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onClearEnergyConsumptionPerDeviceEvent(
|
void _onClearEnergyConsumptionPerDeviceEvent(
|
||||||
ClearEnergyConsumptionPerDeviceEvent event,
|
ClearEnergyConsumptionPerDeviceEvent event,
|
||||||
Emitter<EnergyConsumptionPerDeviceState> emit,
|
Emitter<EnergyConsumptionPerDeviceState> emit,
|
||||||
) async {
|
) async {
|
||||||
|
@ -8,8 +8,7 @@ import 'package:syncrow_web/services/api/api_exception.dart';
|
|||||||
part 'power_clamp_info_event.dart';
|
part 'power_clamp_info_event.dart';
|
||||||
part 'power_clamp_info_state.dart';
|
part 'power_clamp_info_state.dart';
|
||||||
|
|
||||||
class PowerClampInfoBloc
|
class PowerClampInfoBloc extends Bloc<PowerClampInfoEvent, PowerClampInfoState> {
|
||||||
extends Bloc<PowerClampInfoEvent, PowerClampInfoState> {
|
|
||||||
PowerClampInfoBloc(
|
PowerClampInfoBloc(
|
||||||
this._powerClampInfoService,
|
this._powerClampInfoService,
|
||||||
) : super(const PowerClampInfoState()) {
|
) : super(const PowerClampInfoState()) {
|
||||||
@ -26,8 +25,7 @@ class PowerClampInfoBloc
|
|||||||
) async {
|
) async {
|
||||||
emit(state.copyWith(status: PowerClampInfoStatus.loading));
|
emit(state.copyWith(status: PowerClampInfoStatus.loading));
|
||||||
try {
|
try {
|
||||||
final powerClampModel =
|
final powerClampModel = await _powerClampInfoService.getInfo(event.deviceId);
|
||||||
await _powerClampInfoService.getInfo(event.deviceId);
|
|
||||||
emit(
|
emit(
|
||||||
state.copyWith(
|
state.copyWith(
|
||||||
status: PowerClampInfoStatus.loaded,
|
status: PowerClampInfoStatus.loaded,
|
||||||
@ -51,7 +49,7 @@ class PowerClampInfoBloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onUpdatePowerClampStatusEvent(
|
void _onUpdatePowerClampStatusEvent(
|
||||||
UpdatePowerClampStatusEvent event,
|
UpdatePowerClampStatusEvent event,
|
||||||
Emitter<PowerClampInfoState> emit,
|
Emitter<PowerClampInfoState> emit,
|
||||||
) async {
|
) async {
|
||||||
|
@ -16,6 +16,7 @@ final class LoadPowerClampInfoEvent extends PowerClampInfoEvent {
|
|||||||
List<Object> get props => [deviceId];
|
List<Object> get props => [deviceId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final class UpdatePowerClampStatusEvent extends PowerClampInfoEvent {
|
final class UpdatePowerClampStatusEvent extends PowerClampInfoEvent {
|
||||||
const UpdatePowerClampStatusEvent(this.statusList);
|
const UpdatePowerClampStatusEvent(this.statusList);
|
||||||
|
|
||||||
@ -27,4 +28,4 @@ final class UpdatePowerClampStatusEvent extends PowerClampInfoEvent {
|
|||||||
|
|
||||||
final class ClearPowerClampInfoEvent extends PowerClampInfoEvent {
|
final class ClearPowerClampInfoEvent extends PowerClampInfoEvent {
|
||||||
const ClearPowerClampInfoEvent();
|
const ClearPowerClampInfoEvent();
|
||||||
}
|
}
|
@ -24,4 +24,4 @@ class _RealtimeDeviceChangesUpdated extends RealtimeDeviceChangesEvent {
|
|||||||
final List<Status> deviceStatusList;
|
final List<Status> deviceStatusList;
|
||||||
|
|
||||||
const _RealtimeDeviceChangesUpdated(this.deviceStatusList);
|
const _RealtimeDeviceChangesUpdated(this.deviceStatusList);
|
||||||
}
|
}
|
@ -49,7 +49,7 @@ class TotalEnergyConsumptionBloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onClearTotalEnergyConsumptionEvent(
|
void _onClearTotalEnergyConsumptionEvent(
|
||||||
ClearTotalEnergyConsumptionEvent event,
|
ClearTotalEnergyConsumptionEvent event,
|
||||||
Emitter<TotalEnergyConsumptionState> emit,
|
Emitter<TotalEnergyConsumptionState> emit,
|
||||||
) async {
|
) async {
|
||||||
|
@ -7,8 +7,7 @@ sealed class TotalEnergyConsumptionEvent extends Equatable {
|
|||||||
List<Object?> get props => [];
|
List<Object?> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
final class TotalEnergyConsumptionLoadEvent
|
final class TotalEnergyConsumptionLoadEvent extends TotalEnergyConsumptionEvent {
|
||||||
extends TotalEnergyConsumptionEvent {
|
|
||||||
const TotalEnergyConsumptionLoadEvent({required this.param});
|
const TotalEnergyConsumptionLoadEvent({required this.param});
|
||||||
|
|
||||||
final GetTotalEnergyConsumptionParam param;
|
final GetTotalEnergyConsumptionParam param;
|
||||||
@ -17,7 +16,6 @@ final class TotalEnergyConsumptionLoadEvent
|
|||||||
List<Object?> get props => [param];
|
List<Object?> get props => [param];
|
||||||
}
|
}
|
||||||
|
|
||||||
final class ClearTotalEnergyConsumptionEvent
|
final class ClearTotalEnergyConsumptionEvent extends TotalEnergyConsumptionEvent {
|
||||||
extends TotalEnergyConsumptionEvent {
|
|
||||||
const ClearTotalEnergyConsumptionEvent();
|
const ClearTotalEnergyConsumptionEvent();
|
||||||
}
|
}
|
||||||
|
@ -69,8 +69,7 @@ abstract final class EnergyManagementChartsHelper {
|
|||||||
return labels.where((element) => element.isNotEmpty).join(', ');
|
return labels.where((element) => element.isNotEmpty).join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<LineTooltipItem?> getTooltipItems(
|
static List<LineTooltipItem?> getTooltipItems(List<LineBarSpot> touchedSpots) {
|
||||||
List<LineBarSpot> touchedSpots) {
|
|
||||||
return touchedSpots.map((spot) {
|
return touchedSpots.map((spot) {
|
||||||
return LineTooltipItem(
|
return LineTooltipItem(
|
||||||
getToolTipLabel(spot.x, spot.y),
|
getToolTipLabel(spot.x, spot.y),
|
||||||
@ -86,8 +85,7 @@ abstract final class EnergyManagementChartsHelper {
|
|||||||
static LineTouchTooltipData lineTouchTooltipData() {
|
static LineTouchTooltipData lineTouchTooltipData() {
|
||||||
return LineTouchTooltipData(
|
return LineTouchTooltipData(
|
||||||
getTooltipColor: (touchTooltipItem) => ColorsManager.whiteColors,
|
getTooltipColor: (touchTooltipItem) => ColorsManager.whiteColors,
|
||||||
tooltipBorder:
|
tooltipBorder: const BorderSide(color: ColorsManager.semiTransparentBlack),
|
||||||
const BorderSide(color: ColorsManager.semiTransparentBlack),
|
|
||||||
tooltipRoundedRadius: 16,
|
tooltipRoundedRadius: 16,
|
||||||
showOnTopOfTheChartBoxArea: false,
|
showOnTopOfTheChartBoxArea: false,
|
||||||
tooltipPadding: const EdgeInsets.all(8),
|
tooltipPadding: const EdgeInsets.all(8),
|
||||||
|
@ -122,8 +122,7 @@ abstract final class FetchEnergyManagementDataHelper {
|
|||||||
final selectedDevice = getSelectedDevice(context);
|
final selectedDevice = getSelectedDevice(context);
|
||||||
|
|
||||||
context.read<RealtimeDeviceChangesBloc>().add(
|
context.read<RealtimeDeviceChangesBloc>().add(
|
||||||
RealtimeDeviceChangesStarted(
|
RealtimeDeviceChangesStarted(deviceUuid ?? selectedDevice?.uuid ?? ''),
|
||||||
deviceUuid ?? selectedDevice?.uuid ?? ''),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,8 +51,7 @@ class AnalyticsEnergyManagementView extends StatelessWidget {
|
|||||||
spacing: 20,
|
spacing: 20,
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: TotalEnergyConsumptionChartBox()),
|
Expanded(child: TotalEnergyConsumptionChartBox()),
|
||||||
Expanded(
|
Expanded(child: EnergyConsumptionPerDeviceChartBox()),
|
||||||
child: EnergyConsumptionPerDeviceChartBox()),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -52,8 +52,7 @@ class AnalyticsDeviceDropdown extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDevicesDropdown(
|
Widget _buildDevicesDropdown(BuildContext context, AnalyticsDevicesState state) {
|
||||||
BuildContext context, AnalyticsDevicesState state) {
|
|
||||||
final spaceUuid = state.selectedDevice?.spaceUuid;
|
final spaceUuid = state.selectedDevice?.spaceUuid;
|
||||||
return DropdownButton<AnalyticsDevice?>(
|
return DropdownButton<AnalyticsDevice?>(
|
||||||
value: state.selectedDevice,
|
value: state.selectedDevice,
|
||||||
|
@ -18,6 +18,7 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BarChart(
|
return BarChart(
|
||||||
BarChartData(
|
BarChartData(
|
||||||
|
|
||||||
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
||||||
checkToShowHorizontalLine: (value) => true,
|
checkToShowHorizontalLine: (value) => true,
|
||||||
horizontalInterval: 250,
|
horizontalInterval: 250,
|
||||||
@ -99,11 +100,11 @@ class EnergyConsumptionByPhasesChart extends StatelessWidget {
|
|||||||
}) {
|
}) {
|
||||||
final data = energyData;
|
final data = energyData;
|
||||||
|
|
||||||
final date = DateFormat('dd/MM/yyyy').format(data[group.x].date);
|
final date = DateFormat('dd/MM/yyyy').format(data[group.x.toInt()].date);
|
||||||
final phaseA = data[group.x].energyConsumedA;
|
final phaseA = data[group.x.toInt()].energyConsumedA;
|
||||||
final phaseB = data[group.x].energyConsumedB;
|
final phaseB = data[group.x.toInt()].energyConsumedB;
|
||||||
final phaseC = data[group.x].energyConsumedC;
|
final phaseC = data[group.x.toInt()].energyConsumedC;
|
||||||
final total = data[group.x].energyConsumedKw;
|
final total = data[group.x.toInt()].energyConsumedKw;
|
||||||
|
|
||||||
return BarTooltipItem(
|
return BarTooltipItem(
|
||||||
'$date\n',
|
'$date\n',
|
||||||
|
@ -22,8 +22,7 @@ class EnergyConsumptionByPhasesChartBox extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
AnalyticsErrorWidget(state.errorMessage),
|
AnalyticsErrorWidget(state.errorMessage),
|
||||||
EnergyConsumptionByPhasesTitle(
|
EnergyConsumptionByPhasesTitle(
|
||||||
isLoading:
|
isLoading: state.status == EnergyConsumptionByPhasesStatus.loading,
|
||||||
state.status == EnergyConsumptionByPhasesStatus.loading,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -17,6 +17,7 @@ class EnergyConsumptionPerDeviceChart extends StatelessWidget {
|
|||||||
context,
|
context,
|
||||||
leftTitlesInterval: 250,
|
leftTitlesInterval: 250,
|
||||||
),
|
),
|
||||||
|
|
||||||
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
gridData: EnergyManagementChartsHelper.gridData().copyWith(
|
||||||
checkToShowHorizontalLine: (value) => true,
|
checkToShowHorizontalLine: (value) => true,
|
||||||
horizontalInterval: 250,
|
horizontalInterval: 250,
|
||||||
|
@ -46,8 +46,7 @@ class EnergyConsumptionPerDeviceChartBox extends StatelessWidget {
|
|||||||
flex: 2,
|
flex: 2,
|
||||||
child: EnergyConsumptionPerDeviceDevicesList(
|
child: EnergyConsumptionPerDeviceDevicesList(
|
||||||
chartData: state.chartData,
|
chartData: state.chartData,
|
||||||
devices:
|
devices: context.watch<AnalyticsDevicesBloc>().state.devices,
|
||||||
context.watch<AnalyticsDevicesBloc>().state.devices,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -56,8 +55,7 @@ class EnergyConsumptionPerDeviceChartBox extends StatelessWidget {
|
|||||||
const Divider(height: 0),
|
const Divider(height: 0),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Expanded(
|
Expanded(
|
||||||
child:
|
child: EnergyConsumptionPerDeviceChart(chartData: state.chartData),
|
||||||
EnergyConsumptionPerDeviceChart(chartData: state.chartData),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -43,14 +43,11 @@ class PowerClampEnergyDataWidget extends StatelessWidget {
|
|||||||
title: 'Smart Power Clamp',
|
title: 'Smart Power Clamp',
|
||||||
showSpaceUuidInDevicesDropdown: true,
|
showSpaceUuidInDevicesDropdown: true,
|
||||||
onChanged: (device) {
|
onChanged: (device) {
|
||||||
FetchEnergyManagementDataHelper
|
FetchEnergyManagementDataHelper.loadEnergyConsumptionByPhases(
|
||||||
.loadEnergyConsumptionByPhases(
|
|
||||||
context,
|
context,
|
||||||
powerClampUuid: device.uuid,
|
powerClampUuid: device.uuid,
|
||||||
selectedDate: context
|
selectedDate:
|
||||||
.read<AnalyticsDatePickerBloc>()
|
context.read<AnalyticsDatePickerBloc>().state.monthlyDate,
|
||||||
.state
|
|
||||||
.monthlyDate,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -94,8 +91,7 @@ class PowerClampEnergyDataWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 14),
|
const SizedBox(height: 14),
|
||||||
const Expanded(
|
const Expanded(flex: 3, child: EnergyConsumptionByPhasesChartBox()),
|
||||||
flex: 3, child: EnergyConsumptionByPhasesChartBox()),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -140,9 +140,9 @@ class PowerClampPhasesDataWidget extends StatelessWidget {
|
|||||||
|
|
||||||
String _formatCurrentValue(String? value) {
|
String _formatCurrentValue(String? value) {
|
||||||
if (value == null) return '--';
|
if (value == null) return '--';
|
||||||
var str = value;
|
String str = value;
|
||||||
if (str.isEmpty || str == '--') return '--';
|
if (str.isEmpty || str == '--') return '--';
|
||||||
str = str.replaceAll(RegExp('[^0-9]'), '');
|
str = str.replaceAll(RegExp(r'[^0-9]'), '');
|
||||||
if (str.isEmpty) return '--';
|
if (str.isEmpty) return '--';
|
||||||
if (str.length == 1) return '${str[0]}.0';
|
if (str.length == 1) return '${str[0]}.0';
|
||||||
return '${str[0]}.${str.substring(1)}';
|
return '${str[0]}.${str.substring(1)}';
|
||||||
@ -150,9 +150,9 @@ class PowerClampPhasesDataWidget extends StatelessWidget {
|
|||||||
|
|
||||||
String _formatPowerFactor(String? value) {
|
String _formatPowerFactor(String? value) {
|
||||||
if (value == null) return '--';
|
if (value == null) return '--';
|
||||||
var str = value;
|
String str = value;
|
||||||
if (str.isEmpty || str == '--') return '--';
|
if (str.isEmpty || str == '--') return '--';
|
||||||
str = str.replaceAll(RegExp('[^0-9]'), '');
|
str = str.replaceAll(RegExp(r'[^0-9]'), '');
|
||||||
if (str.isEmpty) return '--';
|
if (str.isEmpty) return '--';
|
||||||
final intValue = int.tryParse(str);
|
final intValue = int.tryParse(str);
|
||||||
if (intValue == null) return '--';
|
if (intValue == null) return '--';
|
||||||
@ -162,9 +162,9 @@ class PowerClampPhasesDataWidget extends StatelessWidget {
|
|||||||
|
|
||||||
String _formatVoltage(String? value) {
|
String _formatVoltage(String? value) {
|
||||||
if (value == null) return '--';
|
if (value == null) return '--';
|
||||||
var str = value;
|
String str = value;
|
||||||
if (str.isEmpty || str == '--') return '--';
|
if (str.isEmpty || str == '--') return '--';
|
||||||
str = str.replaceAll(RegExp('[^0-9]'), '');
|
str = str.replaceAll(RegExp(r'[^0-9]'), '');
|
||||||
if (str.isEmpty) return '--';
|
if (str.isEmpty) return '--';
|
||||||
if (str.length == 1) return '0.${str[0]}';
|
if (str.length == 1) return '0.${str[0]}';
|
||||||
return '${str.substring(0, str.length - 1)}.${str.substring(str.length - 1)}';
|
return '${str.substring(0, str.length - 1)}.${str.substring(str.length - 1)}';
|
||||||
|
@ -29,6 +29,7 @@ class TotalEnergyConsumptionChart extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
duration: Duration.zero,
|
duration: Duration.zero,
|
||||||
curve: Curves.easeIn,
|
curve: Curves.easeIn,
|
||||||
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -25,8 +25,7 @@ class TotalEnergyConsumptionChartBox extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
ChartsLoadingWidget(
|
ChartsLoadingWidget(
|
||||||
isLoading:
|
isLoading: state.status == TotalEnergyConsumptionStatus.loading,
|
||||||
state.status == TotalEnergyConsumptionStatus.loading,
|
|
||||||
),
|
),
|
||||||
const Expanded(
|
const Expanded(
|
||||||
flex: 3,
|
flex: 3,
|
||||||
|
@ -23,11 +23,9 @@ class OccupancyBloc extends Bloc<OccupancyEvent, OccupancyState> {
|
|||||||
emit(state.copyWith(status: OccupancyStatus.loading));
|
emit(state.copyWith(status: OccupancyStatus.loading));
|
||||||
try {
|
try {
|
||||||
final chartData = await _occupacyService.load(event.param);
|
final chartData = await _occupacyService.load(event.param);
|
||||||
emit(
|
emit(state.copyWith(chartData: chartData, status: OccupancyStatus.loaded));
|
||||||
state.copyWith(chartData: chartData, status: OccupancyStatus.loaded));
|
|
||||||
} on APIException catch (e) {
|
} on APIException catch (e) {
|
||||||
emit(state.copyWith(
|
emit(state.copyWith(status: OccupancyStatus.failure, errorMessage: e.message));
|
||||||
status: OccupancyStatus.failure, errorMessage: e.message));
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(state.copyWith(status: OccupancyStatus.failure, errorMessage: '$e'));
|
emit(state.copyWith(status: OccupancyStatus.failure, errorMessage: '$e'));
|
||||||
}
|
}
|
||||||
|
@ -25,18 +25,15 @@ abstract final class FetchOccupancyDataHelper {
|
|||||||
|
|
||||||
final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
|
final datePickerState = context.read<AnalyticsDatePickerBloc>().state;
|
||||||
|
|
||||||
loadAnalyticsDevices(context,
|
loadAnalyticsDevices(context, communityUuid: communityId, spaceUuid: spaceId);
|
||||||
communityUuid: communityId, spaceUuid: spaceId);
|
final selectedDevice = context.read<AnalyticsDevicesBloc>().state.selectedDevice;
|
||||||
final selectedDevice =
|
|
||||||
context.read<AnalyticsDevicesBloc>().state.selectedDevice;
|
|
||||||
|
|
||||||
loadOccupancyChartData(
|
loadOccupancyChartData(
|
||||||
context,
|
context,
|
||||||
spaceUuid: spaceId,
|
spaceUuid: spaceId,
|
||||||
date: datePickerState.monthlyDate,
|
date: datePickerState.monthlyDate,
|
||||||
);
|
);
|
||||||
loadHeatMapData(context,
|
loadHeatMapData(context, spaceUuid: spaceId, year: datePickerState.yearlyDate);
|
||||||
spaceUuid: spaceId, year: datePickerState.yearlyDate);
|
|
||||||
|
|
||||||
if (selectedDevice case final AnalyticsDevice device) {
|
if (selectedDevice case final AnalyticsDevice device) {
|
||||||
context.read<RealtimeDeviceChangesBloc>()
|
context.read<RealtimeDeviceChangesBloc>()
|
||||||
@ -67,8 +64,7 @@ abstract final class FetchOccupancyDataHelper {
|
|||||||
context.read<OccupancyBloc>().add(
|
context.read<OccupancyBloc>().add(
|
||||||
LoadOccupancyEvent(
|
LoadOccupancyEvent(
|
||||||
GetOccupancyParam(
|
GetOccupancyParam(
|
||||||
monthDate:
|
monthDate: '${date.year}-${date.month.toString().padLeft(2, '0')}',
|
||||||
'${date.year}-${date.month.toString().padLeft(2, '0')}',
|
|
||||||
spaceUuid: spaceUuid,
|
spaceUuid: spaceUuid,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -20,12 +20,9 @@ class AnalyticsOccupancyView extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
spacing: 32,
|
spacing: 32,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(height: height * 0.46, child: const OccupancyEndSideBar()),
|
||||||
height: height * 0.46, child: const OccupancyEndSideBar()),
|
SizedBox(height: height * 0.5, child: const OccupancyChartBox()),
|
||||||
SizedBox(
|
SizedBox(height: height * 0.5, child: const OccupancyHeatMapBox()),
|
||||||
height: height * 0.5, child: const OccupancyChartBox()),
|
|
||||||
SizedBox(
|
|
||||||
height: height * 0.5, child: const OccupancyHeatMapBox()),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -41,8 +41,7 @@ class OccupancyChart extends StatelessWidget {
|
|||||||
barRods: [
|
barRods: [
|
||||||
BarChartRodData(
|
BarChartRodData(
|
||||||
toY: 100.0,
|
toY: 100.0,
|
||||||
fromY:
|
fromY: occupancyValue == 0 ? occupancyValue : occupancyValue + 2.5,
|
||||||
occupancyValue == 0 ? occupancyValue : occupancyValue + 2.5,
|
|
||||||
color: ColorsManager.graysColor,
|
color: ColorsManager.graysColor,
|
||||||
width: _chartWidth,
|
width: _chartWidth,
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
@ -89,8 +88,8 @@ class OccupancyChart extends StatelessWidget {
|
|||||||
}) {
|
}) {
|
||||||
final data = chartData;
|
final data = chartData;
|
||||||
|
|
||||||
final occupancyValue = double.parse(data[group.x].occupancy);
|
final occupancyValue = double.parse(data[group.x.toInt()].occupancy);
|
||||||
final percentage = '${occupancyValue.toStringAsFixed(0)}%';
|
final percentage = '${(occupancyValue).toStringAsFixed(0)}%';
|
||||||
|
|
||||||
return BarTooltipItem(
|
return BarTooltipItem(
|
||||||
percentage,
|
percentage,
|
||||||
@ -117,7 +116,7 @@ class OccupancyChart extends StatelessWidget {
|
|||||||
alignment: AlignmentDirectional.centerStart,
|
alignment: AlignmentDirectional.centerStart,
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
child: Text(
|
child: Text(
|
||||||
'${value.toStringAsFixed(0)}%',
|
'${(value).toStringAsFixed(0)}%',
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: ColorsManager.greyColor,
|
color: ColorsManager.greyColor,
|
||||||
|
@ -44,15 +44,13 @@ class OccupancyChartBox extends StatelessWidget {
|
|||||||
child: AnalyticsDateFilterButton(
|
child: AnalyticsDateFilterButton(
|
||||||
onDateSelected: (DateTime value) {
|
onDateSelected: (DateTime value) {
|
||||||
context.read<AnalyticsDatePickerBloc>().add(
|
context.read<AnalyticsDatePickerBloc>().add(
|
||||||
UpdateAnalyticsDatePickerEvent(
|
UpdateAnalyticsDatePickerEvent(montlyDate: value),
|
||||||
montlyDate: value),
|
|
||||||
);
|
);
|
||||||
if (spaceTreeState.selectedSpaces.isNotEmpty) {
|
if (spaceTreeState.selectedSpaces.isNotEmpty) {
|
||||||
FetchOccupancyDataHelper.loadOccupancyChartData(
|
FetchOccupancyDataHelper.loadOccupancyChartData(
|
||||||
context,
|
context,
|
||||||
spaceUuid:
|
spaceUuid:
|
||||||
spaceTreeState.selectedSpaces.firstOrNull ??
|
spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||||
'',
|
|
||||||
date: value,
|
date: value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -44,15 +44,13 @@ class OccupancyHeatMapBox extends StatelessWidget {
|
|||||||
child: AnalyticsDateFilterButton(
|
child: AnalyticsDateFilterButton(
|
||||||
onDateSelected: (DateTime value) {
|
onDateSelected: (DateTime value) {
|
||||||
context.read<AnalyticsDatePickerBloc>().add(
|
context.read<AnalyticsDatePickerBloc>().add(
|
||||||
UpdateAnalyticsDatePickerEvent(
|
UpdateAnalyticsDatePickerEvent(yearlyDate: value),
|
||||||
yearlyDate: value),
|
|
||||||
);
|
);
|
||||||
if (spaceTreeState.selectedSpaces.isNotEmpty) {
|
if (spaceTreeState.selectedSpaces.isNotEmpty) {
|
||||||
FetchOccupancyDataHelper.loadHeatMapData(
|
FetchOccupancyDataHelper.loadHeatMapData(
|
||||||
context,
|
context,
|
||||||
spaceUuid:
|
spaceUuid:
|
||||||
spaceTreeState.selectedSpaces.firstOrNull ??
|
spaceTreeState.selectedSpaces.firstOrNull ?? '',
|
||||||
'',
|
|
||||||
year: value,
|
year: value,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -28,11 +28,11 @@ class OccupancyPainter extends CustomPainter {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(Canvas canvas, Size size) {
|
void paint(Canvas canvas, Size size) {
|
||||||
final fillPaint = Paint();
|
final Paint fillPaint = Paint();
|
||||||
final borderPaint = Paint()
|
final Paint borderPaint = Paint()
|
||||||
..color = ColorsManager.grayBorder.withValues(alpha: 0.4)
|
..color = ColorsManager.grayBorder.withValues(alpha: 0.4)
|
||||||
..style = PaintingStyle.stroke;
|
..style = PaintingStyle.stroke;
|
||||||
final hoveredBorderPaint = Paint()
|
final Paint hoveredBorderPaint = Paint()
|
||||||
..color = Colors.black
|
..color = Colors.black
|
||||||
..style = PaintingStyle.stroke
|
..style = PaintingStyle.stroke
|
||||||
..strokeWidth = 1.5;
|
..strokeWidth = 1.5;
|
||||||
@ -66,24 +66,24 @@ class OccupancyPainter extends CustomPainter {
|
|||||||
);
|
);
|
||||||
|
|
||||||
canvas.drawLine(Offset(x, y), Offset(x, y + cellSize), borderPaint);
|
canvas.drawLine(Offset(x, y), Offset(x, y + cellSize), borderPaint);
|
||||||
canvas.drawLine(Offset(x + cellSize, y),
|
canvas.drawLine(Offset(x + cellSize, y), Offset(x + cellSize, y + cellSize),
|
||||||
Offset(x + cellSize, y + cellSize), borderPaint);
|
borderPaint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _drawDashedLine(Canvas canvas, Offset start, Offset end, Paint paint) {
|
void _drawDashedLine(Canvas canvas, Offset start, Offset end, Paint paint) {
|
||||||
const dashWidth = 2.0;
|
const double dashWidth = 2.0;
|
||||||
const dashSpace = 4.0;
|
const double dashSpace = 4.0;
|
||||||
final totalLength = (end - start).distance;
|
final double totalLength = (end - start).distance;
|
||||||
final direction = (end - start) / (end - start).distance;
|
final Offset direction = (end - start) / (end - start).distance;
|
||||||
|
|
||||||
var currentLength = 0.0;
|
double currentLength = 0.0;
|
||||||
while (currentLength < totalLength) {
|
while (currentLength < totalLength) {
|
||||||
final dashStart = start + direction * currentLength;
|
final Offset dashStart = start + direction * currentLength;
|
||||||
final nextLength = currentLength + dashWidth;
|
final double nextLength = currentLength + dashWidth;
|
||||||
final dashEnd = start +
|
final Offset dashEnd =
|
||||||
direction * (nextLength < totalLength ? nextLength : totalLength);
|
start + direction * (nextLength < totalLength ? nextLength : totalLength);
|
||||||
canvas.drawLine(dashStart, dashEnd, paint);
|
canvas.drawLine(dashStart, dashEnd, paint);
|
||||||
currentLength = nextLength + dashSpace;
|
currentLength = nextLength + dashSpace;
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,7 @@ import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_type
|
|||||||
import 'package:syncrow_web/pages/analytics/params/get_air_quality_distribution_param.dart';
|
import 'package:syncrow_web/pages/analytics/params/get_air_quality_distribution_param.dart';
|
||||||
import 'package:syncrow_web/pages/analytics/services/air_quality_distribution/air_quality_distribution_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/air_quality_distribution/air_quality_distribution_service.dart';
|
||||||
|
|
||||||
class FakeAirQualityDistributionService
|
class FakeAirQualityDistributionService implements AirQualityDistributionService {
|
||||||
implements AirQualityDistributionService {
|
|
||||||
final _random = Random();
|
final _random = Random();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -44,6 +43,7 @@ class FakeAirQualityDistributionService
|
|||||||
name: 'poor',
|
name: 'poor',
|
||||||
percentage: nonNullValues[2],
|
percentage: nonNullValues[2],
|
||||||
type: AqiType.hcho.code,
|
type: AqiType.hcho.code,
|
||||||
|
|
||||||
),
|
),
|
||||||
AirQualityPercentageData(
|
AirQualityPercentageData(
|
||||||
name: 'unhealthy',
|
name: 'unhealthy',
|
||||||
@ -71,7 +71,7 @@ class FakeAirQualityDistributionService
|
|||||||
List<bool> nullMask,
|
List<bool> nullMask,
|
||||||
) {
|
) {
|
||||||
double nonNullSum = 0;
|
double nonNullSum = 0;
|
||||||
for (var i = 0; i < originalValues.length; i++) {
|
for (int i = 0; i < originalValues.length; i++) {
|
||||||
if (!nullMask[i]) {
|
if (!nullMask[i]) {
|
||||||
nonNullSum += originalValues[i];
|
nonNullSum += originalValues[i];
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,7 @@ import 'package:syncrow_web/pages/analytics/params/get_air_quality_distribution_
|
|||||||
import 'package:syncrow_web/pages/analytics/services/air_quality_distribution/air_quality_distribution_service.dart';
|
import 'package:syncrow_web/pages/analytics/services/air_quality_distribution/air_quality_distribution_service.dart';
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
|
|
||||||
class RemoteAirQualityDistributionService
|
class RemoteAirQualityDistributionService implements AirQualityDistributionService {
|
||||||
implements AirQualityDistributionService {
|
|
||||||
RemoteAirQualityDistributionService(this._httpService);
|
RemoteAirQualityDistributionService(this._httpService);
|
||||||
|
|
||||||
final HTTPService _httpService;
|
final HTTPService _httpService;
|
||||||
|
@ -16,8 +16,7 @@ class AnalyticsDevicesServiceDelegate implements AnalyticsDevicesService {
|
|||||||
GetAnalyticsDevicesParam param,
|
GetAnalyticsDevicesParam param,
|
||||||
) {
|
) {
|
||||||
return switch (param.requestType) {
|
return switch (param.requestType) {
|
||||||
AnalyticsDeviceRequestType.occupancy =>
|
AnalyticsDeviceRequestType.occupancy => _occupancyService.getDevices(param),
|
||||||
_occupancyService.getDevices(param),
|
|
||||||
AnalyticsDeviceRequestType.energyManagement =>
|
AnalyticsDeviceRequestType.energyManagement =>
|
||||||
_energyManagementService.getDevices(param),
|
_energyManagementService.getDevices(param),
|
||||||
};
|
};
|
||||||
|
@ -14,8 +14,7 @@ final class RemoteEnergyManagementAnalyticsDevicesService
|
|||||||
static const _defaultErrorMessage = 'Failed to load analytics devices';
|
static const _defaultErrorMessage = 'Failed to load analytics devices';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<AnalyticsDevice>> getDevices(
|
Future<List<AnalyticsDevice>> getDevices(GetAnalyticsDevicesParam param) async {
|
||||||
GetAnalyticsDevicesParam param) async {
|
|
||||||
try {
|
try {
|
||||||
final response = await _httpService.get(
|
final response = await _httpService.get(
|
||||||
path: '/devices-space-community/recursive-child',
|
path: '/devices-space-community/recursive-child',
|
||||||
@ -38,8 +37,7 @@ final class RemoteEnergyManagementAnalyticsDevicesService
|
|||||||
final message = e.response?.data as Map<String, dynamic>?;
|
final message = e.response?.data as Map<String, dynamic>?;
|
||||||
final error = message?['error'] as Map<String, dynamic>?;
|
final error = message?['error'] as Map<String, dynamic>?;
|
||||||
final errorMessage = error?['error'] as String? ?? '';
|
final errorMessage = error?['error'] as String? ?? '';
|
||||||
final formattedErrorMessage =
|
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||||
[_defaultErrorMessage, errorMessage].join(': ');
|
|
||||||
throw APIException(formattedErrorMessage);
|
throw APIException(formattedErrorMessage);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw APIException('$_defaultErrorMessage: $e');
|
throw APIException('$_defaultErrorMessage: $e');
|
||||||
|
@ -6,8 +6,7 @@ import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
|||||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
|
|
||||||
class RemoteOccupancyAnalyticsDevicesService
|
class RemoteOccupancyAnalyticsDevicesService implements AnalyticsDevicesService {
|
||||||
implements AnalyticsDevicesService {
|
|
||||||
const RemoteOccupancyAnalyticsDevicesService(this._httpService);
|
const RemoteOccupancyAnalyticsDevicesService(this._httpService);
|
||||||
|
|
||||||
final HTTPService _httpService;
|
final HTTPService _httpService;
|
||||||
@ -15,8 +14,7 @@ class RemoteOccupancyAnalyticsDevicesService
|
|||||||
static const _defaultErrorMessage = 'Failed to load analytics devices';
|
static const _defaultErrorMessage = 'Failed to load analytics devices';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<AnalyticsDevice>> getDevices(
|
Future<List<AnalyticsDevice>> getDevices(GetAnalyticsDevicesParam param) async {
|
||||||
GetAnalyticsDevicesParam param) async {
|
|
||||||
try {
|
try {
|
||||||
final requests = await Future.wait<List<AnalyticsDevice>>(
|
final requests = await Future.wait<List<AnalyticsDevice>>(
|
||||||
param.deviceTypes.map((e) {
|
param.deviceTypes.map((e) {
|
||||||
@ -36,18 +34,15 @@ class RemoteOccupancyAnalyticsDevicesService
|
|||||||
final message = e.response?.data as Map<String, dynamic>?;
|
final message = e.response?.data as Map<String, dynamic>?;
|
||||||
final error = message?['error'] as Map<String, dynamic>?;
|
final error = message?['error'] as Map<String, dynamic>?;
|
||||||
final errorMessage = error?['error'] as String? ?? '';
|
final errorMessage = error?['error'] as String? ?? '';
|
||||||
final formattedErrorMessage =
|
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||||
[_defaultErrorMessage, errorMessage].join(': ');
|
|
||||||
throw APIException(formattedErrorMessage);
|
throw APIException(formattedErrorMessage);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final formattedErrorMessage =
|
final formattedErrorMessage = [_defaultErrorMessage, e.toString()].join(': ');
|
||||||
[_defaultErrorMessage, e.toString()].join(': ');
|
|
||||||
throw APIException(formattedErrorMessage);
|
throw APIException(formattedErrorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<AnalyticsDevice>> _makeRequest(
|
Future<List<AnalyticsDevice>> _makeRequest(GetAnalyticsDevicesParam param) async {
|
||||||
GetAnalyticsDevicesParam param) async {
|
|
||||||
try {
|
try {
|
||||||
final projectUuid = await ProjectManager.getProjectUUID();
|
final projectUuid = await ProjectManager.getProjectUUID();
|
||||||
|
|
||||||
@ -74,8 +69,7 @@ class RemoteOccupancyAnalyticsDevicesService
|
|||||||
final message = e.response?.data as Map<String, dynamic>?;
|
final message = e.response?.data as Map<String, dynamic>?;
|
||||||
final error = message?['error'] as Map<String, dynamic>?;
|
final error = message?['error'] as Map<String, dynamic>?;
|
||||||
final errorMessage = error?['error'] as String? ?? '';
|
final errorMessage = error?['error'] as String? ?? '';
|
||||||
final formattedErrorMessage =
|
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||||
[_defaultErrorMessage, errorMessage].join(': ');
|
|
||||||
throw APIException(formattedErrorMessage);
|
throw APIException(formattedErrorMessage);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw APIException('$_defaultErrorMessage: $e');
|
throw APIException('$_defaultErrorMessage: $e');
|
||||||
|
@ -34,7 +34,7 @@ class DeviceLocationDetailsServiceDecorator implements DeviceLocationService {
|
|||||||
|
|
||||||
return deviceLocationInfo;
|
return deviceLocationInfo;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw Exception('Failed to load device location info: $e');
|
throw Exception('Failed to load device location info: ${e.toString()}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,7 @@ final class RemoteEnergyConsumptionByPhasesService
|
|||||||
|
|
||||||
final HTTPService _httpService;
|
final HTTPService _httpService;
|
||||||
|
|
||||||
static const _defaultErrorMessage =
|
static const _defaultErrorMessage = 'Failed to load energy consumption per phase';
|
||||||
'Failed to load energy consumption per phase';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<PhasesEnergyConsumption>> load(
|
Future<List<PhasesEnergyConsumption>> load(
|
||||||
@ -37,8 +36,7 @@ final class RemoteEnergyConsumptionByPhasesService
|
|||||||
final message = e.response?.data as Map<String, dynamic>?;
|
final message = e.response?.data as Map<String, dynamic>?;
|
||||||
final error = message?['error'] as Map<String, dynamic>?;
|
final error = message?['error'] as Map<String, dynamic>?;
|
||||||
final errorMessage = error?['error'] as String? ?? '';
|
final errorMessage = error?['error'] as String? ?? '';
|
||||||
final formattedErrorMessage =
|
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||||
[_defaultErrorMessage, errorMessage].join(': ');
|
|
||||||
throw APIException(formattedErrorMessage);
|
throw APIException(formattedErrorMessage);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||||
|
@ -13,8 +13,7 @@ class RemoteEnergyConsumptionPerDeviceService
|
|||||||
|
|
||||||
final HTTPService _httpService;
|
final HTTPService _httpService;
|
||||||
|
|
||||||
static const _defaultErrorMessage =
|
static const _defaultErrorMessage = 'Failed to load energy consumption per device';
|
||||||
'Failed to load energy consumption per device';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<DeviceEnergyDataModel>> load(
|
Future<List<DeviceEnergyDataModel>> load(
|
||||||
@ -32,8 +31,7 @@ class RemoteEnergyConsumptionPerDeviceService
|
|||||||
final message = e.response?.data as Map<String, dynamic>?;
|
final message = e.response?.data as Map<String, dynamic>?;
|
||||||
final error = message?['error'] as Map<String, dynamic>?;
|
final error = message?['error'] as Map<String, dynamic>?;
|
||||||
final errorMessage = error?['error'] as String? ?? '';
|
final errorMessage = error?['error'] as String? ?? '';
|
||||||
final formattedErrorMessage =
|
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||||
[_defaultErrorMessage, errorMessage].join(': ');
|
|
||||||
throw APIException(formattedErrorMessage);
|
throw APIException(formattedErrorMessage);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||||
@ -61,8 +59,7 @@ abstract final class _EnergyConsumptionPerDeviceMapper {
|
|||||||
final energyJson = data as Map<String, dynamic>;
|
final energyJson = data as Map<String, dynamic>;
|
||||||
return EnergyDataModel(
|
return EnergyDataModel(
|
||||||
date: DateTime.parse(energyJson['date'] as String),
|
date: DateTime.parse(energyJson['date'] as String),
|
||||||
value:
|
value: double.parse(energyJson['total_energy_consumed_kw'] as String),
|
||||||
double.parse(energyJson['total_energy_consumed_kw'] as String),
|
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
);
|
);
|
||||||
|
@ -33,8 +33,7 @@ final class RemoteOccupancyService implements OccupacyService {
|
|||||||
final message = e.response?.data as Map<String, dynamic>?;
|
final message = e.response?.data as Map<String, dynamic>?;
|
||||||
final error = message?['error'] as Map<String, dynamic>?;
|
final error = message?['error'] as Map<String, dynamic>?;
|
||||||
final errorMessage = error?['error'] as String? ?? '';
|
final errorMessage = error?['error'] as String? ?? '';
|
||||||
final formattedErrorMessage =
|
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||||
[_defaultErrorMessage, errorMessage].join(': ');
|
|
||||||
throw APIException(formattedErrorMessage);
|
throw APIException(formattedErrorMessage);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||||
|
@ -13,8 +13,7 @@ final class RemoteOccupancyHeatMapService implements OccupancyHeatMapService {
|
|||||||
static const _defaultErrorMessage = 'Failed to load occupancy heat map';
|
static const _defaultErrorMessage = 'Failed to load occupancy heat map';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<OccupancyHeatMapModel>> load(
|
Future<List<OccupancyHeatMapModel>> load(GetOccupancyHeatMapParam param) async {
|
||||||
GetOccupancyHeatMapParam param) async {
|
|
||||||
try {
|
try {
|
||||||
final response = await _httpService.get(
|
final response = await _httpService.get(
|
||||||
path: '/occupancy/heat-map/space/${param.spaceUuid}',
|
path: '/occupancy/heat-map/space/${param.spaceUuid}',
|
||||||
@ -25,8 +24,7 @@ final class RemoteOccupancyHeatMapService implements OccupancyHeatMapService {
|
|||||||
final dailyData = json['data'] as List<dynamic>? ?? <dynamic>[];
|
final dailyData = json['data'] as List<dynamic>? ?? <dynamic>[];
|
||||||
|
|
||||||
final result = dailyData.map(
|
final result = dailyData.map(
|
||||||
(json) =>
|
(json) => OccupancyHeatMapModel.fromJson(json as Map<String, dynamic>),
|
||||||
OccupancyHeatMapModel.fromJson(json as Map<String, dynamic>),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return result.toList();
|
return result.toList();
|
||||||
@ -38,8 +36,7 @@ final class RemoteOccupancyHeatMapService implements OccupancyHeatMapService {
|
|||||||
final message = e.response?.data as Map<String, dynamic>?;
|
final message = e.response?.data as Map<String, dynamic>?;
|
||||||
final error = message?['error'] as Map<String, dynamic>?;
|
final error = message?['error'] as Map<String, dynamic>?;
|
||||||
final errorMessage = error?['error'] as String? ?? '';
|
final errorMessage = error?['error'] as String? ?? '';
|
||||||
final formattedErrorMessage =
|
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||||
[_defaultErrorMessage, errorMessage].join(': ');
|
|
||||||
throw APIException(formattedErrorMessage);
|
throw APIException(formattedErrorMessage);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||||
|
@ -28,8 +28,7 @@ final class RemotePowerClampInfoService implements PowerClampInfoService {
|
|||||||
final message = e.response?.data as Map<String, dynamic>?;
|
final message = e.response?.data as Map<String, dynamic>?;
|
||||||
final error = message?['error'] as Map<String, dynamic>?;
|
final error = message?['error'] as Map<String, dynamic>?;
|
||||||
final errorMessage = error?['error'] as String? ?? '';
|
final errorMessage = error?['error'] as String? ?? '';
|
||||||
final formattedErrorMessage =
|
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||||
[_defaultErrorMessage, errorMessage].join(': ');
|
|
||||||
throw APIException(formattedErrorMessage);
|
throw APIException(formattedErrorMessage);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||||
|
@ -6,7 +6,7 @@ import 'package:syncrow_web/pages/analytics/services/range_of_aqi/range_of_aqi_s
|
|||||||
class FakeRangeOfAqiService implements RangeOfAqiService {
|
class FakeRangeOfAqiService implements RangeOfAqiService {
|
||||||
@override
|
@override
|
||||||
Future<List<RangeOfAqi>> load(GetRangeOfAqiParam param) async {
|
Future<List<RangeOfAqi>> load(GetRangeOfAqiParam param) async {
|
||||||
return Future.delayed(const Duration(milliseconds: 800), () {
|
return await Future.delayed(const Duration(milliseconds: 800), () {
|
||||||
final random = DateTime.now().millisecondsSinceEpoch;
|
final random = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
|
||||||
return List.generate(30, (index) {
|
return List.generate(30, (index) {
|
||||||
@ -19,20 +19,14 @@ class FakeRangeOfAqiService implements RangeOfAqiService {
|
|||||||
final avg = (min + avgDelta).clamp(0.0, 301.0);
|
final avg = (min + avgDelta).clamp(0.0, 301.0);
|
||||||
final max = (avg + maxDelta).clamp(0.0, 301.0);
|
final max = (avg + maxDelta).clamp(0.0, 301.0);
|
||||||
|
|
||||||
return RangeOfAqi(
|
return RangeOfAqi(
|
||||||
data: [
|
data: [
|
||||||
RangeOfAqiValue(
|
RangeOfAqiValue(type: AqiType.aqi.code, min: min, average: avg, max: max),
|
||||||
type: AqiType.aqi.code, min: min, average: avg, max: max),
|
RangeOfAqiValue(type: AqiType.pm25.code, min: min, average: avg, max: max),
|
||||||
RangeOfAqiValue(
|
RangeOfAqiValue(type: AqiType.pm10.code, min: min, average: avg, max: max),
|
||||||
type: AqiType.pm25.code, min: min, average: avg, max: max),
|
RangeOfAqiValue(type: AqiType.hcho.code, min: min, average: avg, max: max),
|
||||||
RangeOfAqiValue(
|
RangeOfAqiValue(type: AqiType.tvoc.code, min: min, average: avg, max: max),
|
||||||
type: AqiType.pm10.code, min: min, average: avg, max: max),
|
RangeOfAqiValue(type: AqiType.co2.code, min: min, average: avg, max: max),
|
||||||
RangeOfAqiValue(
|
|
||||||
type: AqiType.hcho.code, min: min, average: avg, max: max),
|
|
||||||
RangeOfAqiValue(
|
|
||||||
type: AqiType.tvoc.code, min: min, average: avg, max: max),
|
|
||||||
RangeOfAqiValue(
|
|
||||||
type: AqiType.co2.code, min: min, average: avg, max: max),
|
|
||||||
],
|
],
|
||||||
date: date,
|
date: date,
|
||||||
);
|
);
|
||||||
|
@ -3,4 +3,4 @@ import 'package:syncrow_web/pages/analytics/params/get_range_of_aqi_param.dart';
|
|||||||
|
|
||||||
abstract interface class RangeOfAqiService {
|
abstract interface class RangeOfAqiService {
|
||||||
Future<List<RangeOfAqi>> load(GetRangeOfAqiParam param);
|
Future<List<RangeOfAqi>> load(GetRangeOfAqiParam param);
|
||||||
}
|
}
|
@ -2,4 +2,4 @@ import 'package:syncrow_web/pages/device_managment/all_devices/models/device_sta
|
|||||||
|
|
||||||
abstract interface class RealtimeDeviceService {
|
abstract interface class RealtimeDeviceService {
|
||||||
Stream<List<Status>> subscribe(String deviceId);
|
Stream<List<Status>> subscribe(String deviceId);
|
||||||
}
|
}
|
@ -5,8 +5,7 @@ import 'package:syncrow_web/pages/analytics/services/total_energy_consumption/to
|
|||||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||||
import 'package:syncrow_web/services/api/http_service.dart';
|
import 'package:syncrow_web/services/api/http_service.dart';
|
||||||
|
|
||||||
class RemoteTotalEnergyConsumptionService
|
class RemoteTotalEnergyConsumptionService implements TotalEnergyConsumptionService {
|
||||||
implements TotalEnergyConsumptionService {
|
|
||||||
const RemoteTotalEnergyConsumptionService(this._httpService);
|
const RemoteTotalEnergyConsumptionService(this._httpService);
|
||||||
|
|
||||||
final HTTPService _httpService;
|
final HTTPService _httpService;
|
||||||
@ -30,8 +29,7 @@ class RemoteTotalEnergyConsumptionService
|
|||||||
final message = e.response?.data as Map<String, dynamic>?;
|
final message = e.response?.data as Map<String, dynamic>?;
|
||||||
final error = message?['error'] as Map<String, dynamic>?;
|
final error = message?['error'] as Map<String, dynamic>?;
|
||||||
final errorMessage = error?['error'] as String? ?? '';
|
final errorMessage = error?['error'] as String? ?? '';
|
||||||
final formattedErrorMessage =
|
final formattedErrorMessage = [_defaultErrorMessage, errorMessage].join(': ');
|
||||||
[_defaultErrorMessage, errorMessage].join(': ');
|
|
||||||
throw APIException(formattedErrorMessage);
|
throw APIException(formattedErrorMessage);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||||
|
@ -75,8 +75,7 @@ class AnalyticsSidebarHeader extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
SelectableText(
|
SelectableText(
|
||||||
context.watch<AnalyticsDevicesBloc>().state.selectedDevice?.uuid ??
|
context.watch<AnalyticsDevicesBloc>().state.selectedDevice?.uuid ?? 'N/A',
|
||||||
'N/A',
|
|
||||||
style: context.textTheme.bodySmall?.copyWith(
|
style: context.textTheme.bodySmall?.copyWith(
|
||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
|
@ -36,8 +36,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
|
|
||||||
////////////////////////////// forget password //////////////////////////////////
|
////////////////////////////// forget password //////////////////////////////////
|
||||||
final TextEditingController forgetEmailController = TextEditingController();
|
final TextEditingController forgetEmailController = TextEditingController();
|
||||||
final TextEditingController forgetPasswordController =
|
final TextEditingController forgetPasswordController = TextEditingController();
|
||||||
TextEditingController();
|
|
||||||
final TextEditingController forgetOtp = TextEditingController();
|
final TextEditingController forgetOtp = TextEditingController();
|
||||||
final forgetFormKey = GlobalKey<FormState>();
|
final forgetFormKey = GlobalKey<FormState>();
|
||||||
final forgetEmailKey = GlobalKey<FormState>();
|
final forgetEmailKey = GlobalKey<FormState>();
|
||||||
@ -54,8 +53,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_remainingTime = 1;
|
_remainingTime = 1;
|
||||||
add(UpdateTimerEvent(
|
add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
|
||||||
remainingTime: _remainingTime, isButtonEnabled: false));
|
|
||||||
try {
|
try {
|
||||||
forgetEmailValidate = '';
|
forgetEmailValidate = '';
|
||||||
_remainingTime = (await AuthenticationAPI.sendOtp(
|
_remainingTime = (await AuthenticationAPI.sendOtp(
|
||||||
@ -64,7 +62,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
if (e.response!.statusCode == 400) {
|
if (e.response!.statusCode == 400) {
|
||||||
final errorData = e.response!.data;
|
final errorData = e.response!.data;
|
||||||
final String errorMessage = errorData['message'];
|
String errorMessage = errorData['message'];
|
||||||
if (errorMessage == 'User not found') {
|
if (errorMessage == 'User not found') {
|
||||||
validate = 'Invalid Credential';
|
validate = 'Invalid Credential';
|
||||||
emit(AuthInitialState());
|
emit(AuthInitialState());
|
||||||
@ -92,8 +90,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
|
add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
|
||||||
} else {
|
} else {
|
||||||
add(UpdateTimerEvent(
|
add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
|
||||||
remainingTime: _remainingTime, isButtonEnabled: false));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -103,11 +100,11 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changePassword(
|
Future<void> changePassword(
|
||||||
ChangePasswordEvent event, Emitter<AuthState> emit) async {
|
ChangePasswordEvent event, Emitter<AuthState> emit) async {
|
||||||
emit(LoadingForgetState());
|
emit(LoadingForgetState());
|
||||||
try {
|
try {
|
||||||
final response = await AuthenticationAPI.verifyOtp(
|
var response = await AuthenticationAPI.verifyOtp(
|
||||||
email: forgetEmailController.text, otpCode: forgetOtp.text);
|
email: forgetEmailController.text, otpCode: forgetOtp.text);
|
||||||
if (response == true) {
|
if (response == true) {
|
||||||
await AuthenticationAPI.forgetPassword(
|
await AuthenticationAPI.forgetPassword(
|
||||||
@ -125,6 +122,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String? validateCode(String? value) {
|
String? validateCode(String? value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Code is required';
|
return 'Code is required';
|
||||||
@ -133,9 +131,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) {
|
void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) {
|
||||||
emit(TimerState(
|
emit(TimerState(isButtonEnabled: event.isButtonEnabled, remainingTime: event.remainingTime));
|
||||||
isButtonEnabled: event.isButtonEnabled,
|
|
||||||
remainingTime: event.remainingTime));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////// login /////////////////////////////////////
|
///////////////////////////////////// login /////////////////////////////////////
|
||||||
@ -155,7 +151,8 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
static UserModel? user;
|
static UserModel? user;
|
||||||
bool showValidationMessage = false;
|
bool showValidationMessage = false;
|
||||||
|
|
||||||
Future<void> _login(LoginButtonPressed event, Emitter<AuthState> emit) async {
|
|
||||||
|
void _login(LoginButtonPressed event, Emitter<AuthState> emit) async {
|
||||||
emit(AuthLoading());
|
emit(AuthLoading());
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
try {
|
try {
|
||||||
@ -182,7 +179,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (token.accessTokenIsNotEmpty) {
|
if (token.accessTokenIsNotEmpty) {
|
||||||
const storage = FlutterSecureStorage();
|
FlutterSecureStorage storage = const FlutterSecureStorage();
|
||||||
await storage.write(
|
await storage.write(
|
||||||
key: Token.loginAccessTokenKey, value: token.accessToken);
|
key: Token.loginAccessTokenKey, value: token.accessToken);
|
||||||
const FlutterSecureStorage().write(
|
const FlutterSecureStorage().write(
|
||||||
@ -200,7 +197,8 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkBoxToggle(
|
|
||||||
|
checkBoxToggle(
|
||||||
CheckBoxEvent event,
|
CheckBoxEvent event,
|
||||||
Emitter<AuthState> emit,
|
Emitter<AuthState> emit,
|
||||||
) {
|
) {
|
||||||
@ -279,12 +277,12 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Please enter your password';
|
return 'Please enter your password';
|
||||||
}
|
}
|
||||||
final validationErrors = <String>[];
|
List<String> validationErrors = [];
|
||||||
|
|
||||||
if (!RegExp('^(?=.*[a-z])').hasMatch(value)) {
|
if (!RegExp(r'^(?=.*[a-z])').hasMatch(value)) {
|
||||||
validationErrors.add(' - one lowercase letter');
|
validationErrors.add(' - one lowercase letter');
|
||||||
}
|
}
|
||||||
if (!RegExp('^(?=.*[A-Z])').hasMatch(value)) {
|
if (!RegExp(r'^(?=.*[A-Z])').hasMatch(value)) {
|
||||||
validationErrors.add(' - one uppercase letter');
|
validationErrors.add(' - one uppercase letter');
|
||||||
}
|
}
|
||||||
if (!RegExp(r'^(?=.*\d)').hasMatch(value)) {
|
if (!RegExp(r'^(?=.*\d)').hasMatch(value)) {
|
||||||
@ -306,7 +304,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
|
|
||||||
String? fullNameValidator(String? value) {
|
String? fullNameValidator(String? value) {
|
||||||
if (value == null) return 'Full name is required';
|
if (value == null) return 'Full name is required';
|
||||||
final withoutExtraSpaces = value.replaceAll(RegExp(r'\s+'), ' ').trim();
|
final withoutExtraSpaces = value.replaceAll(RegExp(r"\s+"), ' ').trim();
|
||||||
if (withoutExtraSpaces.length < 2 || withoutExtraSpaces.length > 30) {
|
if (withoutExtraSpaces.length < 2 || withoutExtraSpaces.length > 30) {
|
||||||
return 'Full name must be between 2 and 30 characters long';
|
return 'Full name must be between 2 and 30 characters long';
|
||||||
}
|
}
|
||||||
@ -341,14 +339,12 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
static Future<String> getTokenAndValidate() async {
|
static Future<String> getTokenAndValidate() async {
|
||||||
try {
|
try {
|
||||||
const storage = FlutterSecureStorage();
|
const storage = FlutterSecureStorage();
|
||||||
final firstLaunch = await SharedPreferencesHelper.readBoolFromSP(
|
final firstLaunch =
|
||||||
StringsManager.firstLaunch) ??
|
await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true;
|
||||||
true;
|
|
||||||
if (firstLaunch) {
|
if (firstLaunch) {
|
||||||
storage.deleteAll();
|
storage.deleteAll();
|
||||||
}
|
}
|
||||||
await SharedPreferencesHelper.saveBoolToSP(
|
await SharedPreferencesHelper.saveBoolToSP(StringsManager.firstLaunch, false);
|
||||||
StringsManager.firstLaunch, false);
|
|
||||||
final value = await storage.read(key: Token.loginAccessTokenKey) ?? '';
|
final value = await storage.read(key: Token.loginAccessTokenKey) ?? '';
|
||||||
if (value.isEmpty) {
|
if (value.isEmpty) {
|
||||||
return 'Token not found';
|
return 'Token not found';
|
||||||
@ -370,8 +366,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _fetchRegion(
|
void _fetchRegion(RegionInitialEvent event, Emitter<AuthState> emit) async {
|
||||||
RegionInitialEvent event, Emitter<AuthState> emit) async {
|
|
||||||
try {
|
try {
|
||||||
emit(AuthLoading());
|
emit(AuthLoading());
|
||||||
regionList = await AuthenticationAPI.fetchRegion();
|
regionList = await AuthenticationAPI.fetchRegion();
|
||||||
@ -394,17 +389,15 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String formattedTime(int time) {
|
String formattedTime(int time) {
|
||||||
final days = (time / 86400).floor(); // 86400 seconds in a day
|
final int days = (time / 86400).floor(); // 86400 seconds in a day
|
||||||
final hours = ((time % 86400) / 3600).floor();
|
final int hours = ((time % 86400) / 3600).floor();
|
||||||
final minutes = (((time % 86400) % 3600) / 60).floor();
|
final int minutes = (((time % 86400) % 3600) / 60).floor();
|
||||||
final seconds = ((time % 86400) % 3600) % 60;
|
final int seconds = (((time % 86400) % 3600) % 60).floor();
|
||||||
|
|
||||||
final formattedTime = [
|
final String formattedTime = [
|
||||||
if (days > 0) '${days}d', // Append 'd' for days
|
if (days > 0) '${days}d', // Append 'd' for days
|
||||||
if (days > 0 || hours > 0)
|
if (days > 0 || hours > 0)
|
||||||
hours
|
hours.toString().padLeft(2, '0'), // Show hours if there are days or hours
|
||||||
.toString()
|
|
||||||
.padLeft(2, '0'), // Show hours if there are days or hours
|
|
||||||
minutes.toString().padLeft(2, '0'),
|
minutes.toString().padLeft(2, '0'),
|
||||||
seconds.toString().padLeft(2, '0'),
|
seconds.toString().padLeft(2, '0'),
|
||||||
].join(':');
|
].join(':');
|
||||||
@ -424,7 +417,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
return checkValidate;
|
return checkValidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeValidate(
|
changeValidate(
|
||||||
ChangeValidateEvent event,
|
ChangeValidateEvent event,
|
||||||
Emitter<AuthState> emit,
|
Emitter<AuthState> emit,
|
||||||
) {
|
) {
|
||||||
@ -433,7 +426,7 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
emit(LoginInitial());
|
emit(LoginInitial());
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeForgetValidate(
|
changeForgetValidate(
|
||||||
ChangeValidateEvent event,
|
ChangeValidateEvent event,
|
||||||
Emitter<AuthState> emit,
|
Emitter<AuthState> emit,
|
||||||
) {
|
) {
|
||||||
|
@ -46,8 +46,7 @@ class StopTimerEvent extends AuthEvent {}
|
|||||||
class UpdateTimerEvent extends AuthEvent {
|
class UpdateTimerEvent extends AuthEvent {
|
||||||
final int remainingTime;
|
final int remainingTime;
|
||||||
final bool isButtonEnabled;
|
final bool isButtonEnabled;
|
||||||
const UpdateTimerEvent(
|
const UpdateTimerEvent({required this.remainingTime, required this.isButtonEnabled});
|
||||||
{required this.remainingTime, required this.isButtonEnabled});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChangePasswordEvent extends AuthEvent {}
|
class ChangePasswordEvent extends AuthEvent {}
|
||||||
|
@ -56,8 +56,7 @@ class TimerState extends AuthState {
|
|||||||
final bool isButtonEnabled;
|
final bool isButtonEnabled;
|
||||||
final int remainingTime;
|
final int remainingTime;
|
||||||
|
|
||||||
const TimerState(
|
const TimerState({required this.isButtonEnabled, required this.remainingTime});
|
||||||
{required this.isButtonEnabled, required this.remainingTime});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [isButtonEnabled, remainingTime];
|
List<Object> get props => [isButtonEnabled, remainingTime];
|
||||||
@ -81,7 +80,7 @@ class TimerUpdated extends AuthState {
|
|||||||
final String formattedTime;
|
final String formattedTime;
|
||||||
final bool isButtonEnabled;
|
final bool isButtonEnabled;
|
||||||
|
|
||||||
const TimerUpdated({
|
TimerUpdated({
|
||||||
required this.formattedTime,
|
required this.formattedTime,
|
||||||
required this.isButtonEnabled,
|
required this.isButtonEnabled,
|
||||||
});
|
});
|
||||||
|
@ -21,9 +21,9 @@ class LoginWithEmailModel {
|
|||||||
return {
|
return {
|
||||||
'email': email,
|
'email': email,
|
||||||
'password': password,
|
'password': password,
|
||||||
'platform': 'web'
|
"platform": "web"
|
||||||
// 'regionUuid': regionUuid,
|
// 'regionUuid': regionUuid,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//tst@tst.com
|
//tst@tst.com
|
@ -11,14 +11,6 @@ class Token {
|
|||||||
final int iat;
|
final int iat;
|
||||||
final int exp;
|
final int exp;
|
||||||
|
|
||||||
Token(
|
|
||||||
this.accessToken,
|
|
||||||
this.refreshToken,
|
|
||||||
this.sessionId,
|
|
||||||
this.iat,
|
|
||||||
this.exp,
|
|
||||||
);
|
|
||||||
|
|
||||||
Token.emptyConstructor()
|
Token.emptyConstructor()
|
||||||
: accessToken = '',
|
: accessToken = '',
|
||||||
refreshToken = '',
|
refreshToken = '',
|
||||||
@ -32,6 +24,14 @@ class Token {
|
|||||||
|
|
||||||
bool get isNotEmpty => accessToken.isNotEmpty && refreshToken.isNotEmpty;
|
bool get isNotEmpty => accessToken.isNotEmpty && refreshToken.isNotEmpty;
|
||||||
|
|
||||||
|
Token(
|
||||||
|
this.accessToken,
|
||||||
|
this.refreshToken,
|
||||||
|
this.sessionId,
|
||||||
|
this.iat,
|
||||||
|
this.exp,
|
||||||
|
);
|
||||||
|
|
||||||
Token.refreshToken(this.refreshToken)
|
Token.refreshToken(this.refreshToken)
|
||||||
: accessToken = '',
|
: accessToken = '',
|
||||||
sessionId = '',
|
sessionId = '',
|
||||||
@ -40,7 +40,7 @@ class Token {
|
|||||||
|
|
||||||
factory Token.fromJson(Map<String, dynamic> json) {
|
factory Token.fromJson(Map<String, dynamic> json) {
|
||||||
//save token to secure storage
|
//save token to secure storage
|
||||||
const storage = FlutterSecureStorage();
|
var storage = const FlutterSecureStorage();
|
||||||
storage.write(
|
storage.write(
|
||||||
key: loginAccessTokenKey, value: json[loginAccessTokenKey] ?? '');
|
key: loginAccessTokenKey, value: json[loginAccessTokenKey] ?? '');
|
||||||
storage.write(
|
storage.write(
|
||||||
|
@ -55,21 +55,22 @@ class UserModel {
|
|||||||
|
|
||||||
//from token
|
//from token
|
||||||
factory UserModel.fromToken(Token token) {
|
factory UserModel.fromToken(Token token) {
|
||||||
final tempJson = Token.decodeToken(token.accessToken);
|
Map<String, dynamic> tempJson = Token.decodeToken(token.accessToken);
|
||||||
|
|
||||||
return UserModel(
|
return UserModel(
|
||||||
hasAcceptedWebAgreement: null,
|
hasAcceptedWebAgreement: null,
|
||||||
role: null,
|
role: null,
|
||||||
webAgreementAcceptedAt: null,
|
webAgreementAcceptedAt: null,
|
||||||
uuid: tempJson['uuid'].toString(),
|
uuid: tempJson['uuid'].toString(),
|
||||||
email: tempJson['email'],
|
email: tempJson['email'],
|
||||||
firstName: null,
|
firstName: null,
|
||||||
lastName: null,
|
lastName: null,
|
||||||
photoUrl: null,
|
photoUrl: null,
|
||||||
phoneNumber: null,
|
phoneNumber: null,
|
||||||
isEmailVerified: null,
|
isEmailVerified: null,
|
||||||
isAgreementAccepted: null,
|
isAgreementAccepted: null,
|
||||||
project: null);
|
project: null
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
@ -36,18 +36,20 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
builder: _buildForm,
|
builder: (context, state) {
|
||||||
|
return _buildForm(context, state);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildForm(BuildContext context, AuthState state) {
|
Widget _buildForm(BuildContext context, AuthState state) {
|
||||||
late ScrollController scrollController;
|
late ScrollController _scrollController;
|
||||||
scrollController = ScrollController();
|
_scrollController = ScrollController();
|
||||||
void scrollToCenter() {
|
void _scrollToCenter() {
|
||||||
final middlePosition = scrollController.position.maxScrollExtent / 2;
|
final double middlePosition = _scrollController.position.maxScrollExtent / 2;
|
||||||
scrollController.animateTo(
|
_scrollController.animateTo(
|
||||||
middlePosition,
|
middlePosition,
|
||||||
duration: const Duration(seconds: 1),
|
duration: const Duration(seconds: 1),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
@ -55,25 +57,24 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
scrollToCenter();
|
_scrollToCenter();
|
||||||
});
|
});
|
||||||
final forgetBloc = BlocProvider.of<AuthBloc>(context);
|
final forgetBloc = BlocProvider.of<AuthBloc>(context);
|
||||||
final size = MediaQuery.of(context).size;
|
Size size = MediaQuery.of(context).size;
|
||||||
return FirstLayer(
|
return FirstLayer(
|
||||||
second: Center(
|
second: Center(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
if (state is AuthLoading)
|
if (state is AuthLoading) const Center(child: CircularProgressIndicator()),
|
||||||
const Center(child: CircularProgressIndicator()),
|
|
||||||
ListView(
|
ListView(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
controller: scrollController,
|
controller: _scrollController,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.all(size.width * 0.02),
|
padding: EdgeInsets.all(size.width * 0.02),
|
||||||
margin: EdgeInsets.all(size.width * 0.09),
|
margin: EdgeInsets.all(size.width * 0.09),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black.withValues(alpha: 0.3),
|
color: Colors.black.withOpacity(0.3),
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -93,22 +94,17 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
flex: 3,
|
flex: 3,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withValues(alpha: 0.1),
|
color: Colors.white.withOpacity(0.1),
|
||||||
borderRadius:
|
borderRadius: const BorderRadius.all(Radius.circular(30)),
|
||||||
const BorderRadius.all(Radius.circular(30)),
|
border: Border.all(color: ColorsManager.graysColor.withOpacity(0.2)),
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.graysColor
|
|
||||||
.withValues(alpha: 0.2)),
|
|
||||||
),
|
),
|
||||||
child: Form(
|
child: Form(
|
||||||
key: forgetBloc.forgetFormKey,
|
key: forgetBloc.forgetFormKey,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(
|
||||||
horizontal: size.width * 0.02,
|
horizontal: size.width * 0.02, vertical: size.width * 0.003),
|
||||||
vertical: size.width * 0.003),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
MainAxisAlignment.spaceEvenly,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
@ -125,9 +121,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodySmall!
|
.bodySmall!
|
||||||
.copyWith(
|
.copyWith(fontSize: 14, fontWeight: FontWeight.w400),
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w400),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
// Column(
|
// Column(
|
||||||
@ -146,90 +140,69 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
Form(
|
Form(
|
||||||
key: forgetBloc.forgetEmailKey,
|
key: forgetBloc.forgetEmailKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment:
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
CrossAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Account',
|
"Account",
|
||||||
style: Theme.of(context)
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
.textTheme
|
fontSize: 14, fontWeight: FontWeight.w400),
|
||||||
.bodySmall!
|
|
||||||
.copyWith(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight:
|
|
||||||
FontWeight.w400),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: forgetBloc
|
controller: forgetBloc.forgetEmailController,
|
||||||
.forgetEmailController,
|
validator: forgetBloc.validateEmail,
|
||||||
validator:
|
decoration: textBoxDecoration()!.copyWith(
|
||||||
forgetBloc.validateEmail,
|
|
||||||
decoration:
|
|
||||||
textBoxDecoration()!.copyWith(
|
|
||||||
hintText: 'Enter your email',
|
hintText: 'Enter your email',
|
||||||
hintStyle: Theme.of(context)
|
hintStyle: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodySmall!
|
.bodySmall!
|
||||||
.copyWith(
|
.copyWith(
|
||||||
color: ColorsManager
|
color: ColorsManager.grayColor,
|
||||||
.grayColor,
|
fontWeight: FontWeight.w400),
|
||||||
fontWeight:
|
|
||||||
FontWeight.w400),
|
|
||||||
),
|
),
|
||||||
style: const TextStyle(
|
style: const TextStyle(color: Colors.black),
|
||||||
color: Colors.black),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
const SizedBox(height: 20.0),
|
const SizedBox(height: 20.0),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment:
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'One Time Password',
|
"One Time Password",
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodySmall!
|
.bodySmall!
|
||||||
.copyWith(
|
.copyWith(fontSize: 14, fontWeight: FontWeight.w400),
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w400),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
validator: forgetBloc.validateCode,
|
validator: forgetBloc.validateCode,
|
||||||
keyboardType:
|
keyboardType: TextInputType.visiblePassword,
|
||||||
TextInputType.visiblePassword,
|
|
||||||
controller: forgetBloc.forgetOtp,
|
controller: forgetBloc.forgetOtp,
|
||||||
decoration:
|
decoration: textBoxDecoration()!.copyWith(
|
||||||
textBoxDecoration()!.copyWith(
|
|
||||||
hintText: 'Enter Code',
|
hintText: 'Enter Code',
|
||||||
hintStyle: Theme.of(context)
|
hintStyle: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodySmall!
|
.bodySmall!
|
||||||
.copyWith(
|
.copyWith(
|
||||||
color:
|
color: ColorsManager.grayColor,
|
||||||
ColorsManager.grayColor,
|
fontWeight: FontWeight.w400),
|
||||||
fontWeight:
|
|
||||||
FontWeight.w400),
|
|
||||||
suffixIcon: SizedBox(
|
suffixIcon: SizedBox(
|
||||||
width: 100,
|
width: 100,
|
||||||
child: Center(
|
child: Center(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: state is TimerState &&
|
onTap: state is TimerState &&
|
||||||
!state
|
!state.isButtonEnabled &&
|
||||||
.isButtonEnabled &&
|
state.remainingTime != 1
|
||||||
state.remainingTime !=
|
|
||||||
1
|
|
||||||
? null
|
? null
|
||||||
: () {
|
:
|
||||||
|
() {
|
||||||
if (forgetBloc
|
if (forgetBloc
|
||||||
.forgetEmailKey
|
.forgetEmailKey
|
||||||
.currentState!
|
.currentState!
|
||||||
@ -238,31 +211,27 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
StartTimerEvent());
|
StartTimerEvent());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
child: Text(
|
child: Text(
|
||||||
'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}',
|
'Get Code ${state is TimerState && !state.isButtonEnabled && state.remainingTime != 1 ? "(${forgetBloc.formattedTime(state.remainingTime)}) " : ""}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: state
|
color: state is TimerState &&
|
||||||
is TimerState &&
|
!state.isButtonEnabled
|
||||||
!state
|
|
||||||
.isButtonEnabled
|
|
||||||
? Colors.grey
|
? Colors.grey
|
||||||
: ColorsManager
|
: ColorsManager.btnColor,
|
||||||
.btnColor,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
style: const TextStyle(
|
style: const TextStyle(color: Colors.black),
|
||||||
color: Colors.black),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (forgetBloc.forgetValidate !=
|
if (forgetBloc.forgetValidate !=
|
||||||
'') // Check if there is a validation message
|
'') // Check if there is a validation message
|
||||||
Padding(
|
Padding(
|
||||||
padding:
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
const EdgeInsets.only(top: 8.0),
|
|
||||||
child: Text(
|
child: Text(
|
||||||
forgetBloc.forgetValidate,
|
forgetBloc.forgetValidate,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
@ -275,44 +244,34 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 20.0),
|
const SizedBox(height: 20.0),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment:
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
CrossAxisAlignment.start,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Password',
|
"Password",
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodySmall!
|
.bodySmall!
|
||||||
.copyWith(
|
.copyWith(fontSize: 14, fontWeight: FontWeight.w400),
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w400),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
obscureText: forgetBloc.obscureText,
|
obscureText: forgetBloc.obscureText,
|
||||||
keyboardType:
|
keyboardType: TextInputType.visiblePassword,
|
||||||
TextInputType.visiblePassword,
|
validator: forgetBloc.passwordValidator,
|
||||||
validator:
|
controller: forgetBloc.forgetPasswordController,
|
||||||
forgetBloc.passwordValidator,
|
decoration: textBoxDecoration()!.copyWith(
|
||||||
controller: forgetBloc
|
|
||||||
.forgetPasswordController,
|
|
||||||
decoration:
|
|
||||||
textBoxDecoration()!.copyWith(
|
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
forgetBloc.add(
|
forgetBloc.add(PasswordVisibleEvent(
|
||||||
PasswordVisibleEvent(
|
newValue: forgetBloc.obscureText));
|
||||||
newValue: forgetBloc
|
|
||||||
.obscureText));
|
|
||||||
},
|
},
|
||||||
icon: SizedBox(
|
icon: SizedBox(
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
forgetBloc.obscureText
|
forgetBloc.obscureText
|
||||||
? Assets.visiblePassword
|
? Assets.visiblePassword
|
||||||
: Assets
|
: Assets.invisiblePassword,
|
||||||
.invisiblePassword,
|
|
||||||
height: 15,
|
height: 15,
|
||||||
width: 15,
|
width: 15,
|
||||||
),
|
),
|
||||||
@ -323,13 +282,10 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
.textTheme
|
.textTheme
|
||||||
.bodySmall!
|
.bodySmall!
|
||||||
.copyWith(
|
.copyWith(
|
||||||
color:
|
color: ColorsManager.grayColor,
|
||||||
ColorsManager.grayColor,
|
fontWeight: FontWeight.w400),
|
||||||
fontWeight:
|
|
||||||
FontWeight.w400),
|
|
||||||
),
|
),
|
||||||
style: const TextStyle(
|
style: const TextStyle(color: Colors.black),
|
||||||
color: Colors.black),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -339,31 +295,23 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 20.0),
|
const SizedBox(height: 20.0),
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment:
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: size.width * 0.2,
|
width: size.width * 0.2,
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
backgroundColor:
|
backgroundColor: ColorsManager.btnColor,
|
||||||
ColorsManager.btnColor,
|
|
||||||
child: const Text('Submit'),
|
child: const Text('Submit'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (forgetBloc
|
if (forgetBloc.forgetFormKey.currentState!.validate() ||
|
||||||
.forgetFormKey.currentState!
|
forgetBloc.forgetEmailKey.currentState!
|
||||||
.validate() ||
|
|
||||||
forgetBloc.forgetEmailKey
|
|
||||||
.currentState!
|
|
||||||
.validate()) {
|
.validate()) {
|
||||||
if (forgetBloc.forgetEmailKey
|
if (forgetBloc.forgetEmailKey.currentState!
|
||||||
.currentState!
|
|
||||||
.validate() &&
|
.validate() &&
|
||||||
forgetBloc.forgetFormKey
|
forgetBloc.forgetFormKey.currentState!
|
||||||
.currentState!
|
|
||||||
.validate()) {
|
.validate()) {
|
||||||
forgetBloc
|
forgetBloc.add(ChangePasswordEvent());
|
||||||
.add(ChangePasswordEvent());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -377,8 +325,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
forgetBloc.validate,
|
forgetBloc.validate,
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700, color: ColorsManager.red),
|
||||||
color: ColorsManager.red),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -390,9 +337,8 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
child: Wrap(
|
child: Wrap(
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
const Text(
|
||||||
'Do you have an account? ',
|
"Do you have an account? ",
|
||||||
style:
|
style: TextStyle(color: Colors.white),
|
||||||
TextStyle(color: Colors.white),
|
|
||||||
),
|
),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -400,7 +346,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
child: const Text(
|
child: const Text(
|
||||||
'Sign in',
|
"Sign in",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -426,15 +372,14 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDropdownField(
|
Widget _buildDropdownField(BuildContext context, AuthBloc loginBloc, Size size) {
|
||||||
BuildContext context, AuthBloc loginBloc, Size size) {
|
final TextEditingController textEditingController = TextEditingController();
|
||||||
final textEditingController = TextEditingController();
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Country/Region',
|
"Country/Region",
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
@ -453,13 +398,10 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
builder: (FormFieldState<String> field) {
|
builder: (FormFieldState<String> field) {
|
||||||
return InputDecorator(
|
return InputDecorator(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
contentPadding:
|
contentPadding: const EdgeInsets.symmetric(horizontal: 2, vertical: 10),
|
||||||
const EdgeInsets.symmetric(horizontal: 2, vertical: 10),
|
|
||||||
errorText: field.errorText,
|
errorText: field.errorText,
|
||||||
filled:
|
filled: true, // Ensure the dropdown is filled with the background color
|
||||||
true, // Ensure the dropdown is filled with the background color
|
fillColor: ColorsManager.boxColor, // Match the dropdown container color
|
||||||
fillColor: ColorsManager
|
|
||||||
.boxColor, // Match the dropdown container color
|
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
@ -470,16 +412,14 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color:
|
color: field.hasError ? Colors.red : ColorsManager.grayColor,
|
||||||
field.hasError ? Colors.red : ColorsManager.grayColor,
|
|
||||||
width: 1.5,
|
width: 1.5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color:
|
color: field.hasError ? Colors.red : ColorsManager.grayColor,
|
||||||
field.hasError ? Colors.red : ColorsManager.grayColor,
|
|
||||||
width: 1.5,
|
width: 1.5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -506,11 +446,10 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
return DropdownMenuItem<String>(
|
return DropdownMenuItem<String>(
|
||||||
value: region.id,
|
value: region.id,
|
||||||
child: Text(
|
child: Text(
|
||||||
style:
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
Theme.of(context).textTheme.bodyMedium!.copyWith(
|
fontSize: 14,
|
||||||
fontSize: 14,
|
fontWeight: FontWeight.w400,
|
||||||
fontWeight: FontWeight.w400,
|
),
|
||||||
),
|
|
||||||
region.name,
|
region.name,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
@ -523,8 +462,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
onChanged: (String? value) {
|
onChanged: (String? value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
loginBloc.add(SelectRegionEvent(val: value));
|
loginBloc.add(SelectRegionEvent(val: value));
|
||||||
field.didChange(
|
field.didChange(value); // Notify the form field of the change
|
||||||
value); // Notify the form field of the change
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
buttonStyleData: const ButtonStyleData(
|
buttonStyleData: const ButtonStyleData(
|
||||||
@ -548,8 +486,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
searchInnerWidgetHeight: 50,
|
searchInnerWidgetHeight: 50,
|
||||||
searchInnerWidget: Container(
|
searchInnerWidget: Container(
|
||||||
height: 50,
|
height: 50,
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
horizontal: 8, vertical: 4),
|
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
style: const TextStyle(color: Colors.black),
|
style: const TextStyle(color: Colors.black),
|
||||||
controller: textEditingController,
|
controller: textEditingController,
|
||||||
@ -563,8 +500,7 @@ class ForgetPasswordWebPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
searchMatchFn: (item, searchValue) {
|
searchMatchFn: (item, searchValue) {
|
||||||
final regionName =
|
final regionName = (item.child as Text).data?.toLowerCase() ?? '';
|
||||||
(item.child as Text).data?.toLowerCase() ?? '';
|
|
||||||
final search = searchValue.toLowerCase().trim();
|
final search = searchValue.toLowerCase().trim();
|
||||||
return regionName.contains(search);
|
return regionName.contains(search);
|
||||||
},
|
},
|
||||||
|
@ -79,7 +79,7 @@ class LoginMobilePage extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.all(50),
|
margin: const EdgeInsets.all(50),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black.withValues(alpha: 0.3),
|
color: Colors.black.withOpacity(0.3),
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(20))),
|
borderRadius: const BorderRadius.all(Radius.circular(20))),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@ -93,15 +93,12 @@ class LoginMobilePage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.all(30),
|
margin: EdgeInsets.all(30),
|
||||||
padding: const EdgeInsets.all(20),
|
padding: EdgeInsets.all(20),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withValues(alpha: 0.1),
|
color: Colors.white.withOpacity(0.1),
|
||||||
borderRadius:
|
borderRadius: const BorderRadius.all(Radius.circular(30)),
|
||||||
const BorderRadius.all(Radius.circular(30)),
|
border: Border.all(color: ColorsManager.graysColor.withOpacity(0.2))),
|
||||||
border: Border.all(
|
|
||||||
color: ColorsManager.graysColor
|
|
||||||
.withValues(alpha: 0.2))),
|
|
||||||
child: Form(
|
child: Form(
|
||||||
key: loginBloc.loginFormKey,
|
key: loginBloc.loginFormKey,
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -112,9 +109,7 @@ class LoginMobilePage extends StatelessWidget {
|
|||||||
const Text(
|
const Text(
|
||||||
'Login',
|
'Login',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold),
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
// Column(
|
// Column(
|
||||||
@ -160,15 +155,15 @@ class LoginMobilePage extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Email',
|
"Email",
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
validator: loginBloc.validateEmail,
|
validator: loginBloc.validateEmail,
|
||||||
controller: loginBloc.loginEmailController,
|
controller: loginBloc.loginEmailController,
|
||||||
decoration: textBoxDecoration()!
|
decoration:
|
||||||
.copyWith(hintText: 'Enter your email'),
|
textBoxDecoration()!.copyWith(hintText: 'Enter your email'),
|
||||||
style: const TextStyle(color: Colors.black),
|
style: const TextStyle(color: Colors.black),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -180,7 +175,7 @@ class LoginMobilePage extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Password',
|
"Password",
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
@ -188,8 +183,7 @@ class LoginMobilePage extends StatelessWidget {
|
|||||||
validator: loginBloc.validatePassword,
|
validator: loginBloc.validatePassword,
|
||||||
obscureText: loginBloc.obscureText,
|
obscureText: loginBloc.obscureText,
|
||||||
keyboardType: TextInputType.visiblePassword,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
controller:
|
controller: loginBloc.loginPasswordController,
|
||||||
loginBloc.loginPasswordController,
|
|
||||||
decoration: textBoxDecoration()!.copyWith(
|
decoration: textBoxDecoration()!.copyWith(
|
||||||
hintText: 'At least 8 characters',
|
hintText: 'At least 8 characters',
|
||||||
),
|
),
|
||||||
@ -207,19 +201,16 @@ class LoginMobilePage extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context)
|
Navigator.of(context).push(MaterialPageRoute(
|
||||||
.push(MaterialPageRoute(
|
builder: (context) => const ForgetPasswordPage(),
|
||||||
builder: (context) =>
|
|
||||||
const ForgetPasswordPage(),
|
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'Forgot Password?',
|
"Forgot Password?",
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodySmall!
|
.bodySmall!
|
||||||
.copyWith(
|
.copyWith(color: ColorsManager.blackColor),
|
||||||
color: ColorsManager.blackColor),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -230,15 +221,13 @@ class LoginMobilePage extends StatelessWidget {
|
|||||||
Transform.scale(
|
Transform.scale(
|
||||||
scale: 1.2, // Adjust the scale as needed
|
scale: 1.2, // Adjust the scale as needed
|
||||||
child: Checkbox(
|
child: Checkbox(
|
||||||
fillColor: WidgetStateProperty.all<Color>(
|
fillColor: MaterialStateProperty.all<Color>(Colors.white),
|
||||||
Colors.white),
|
|
||||||
activeColor: Colors.white,
|
activeColor: Colors.white,
|
||||||
value: loginBloc.isChecked,
|
value: loginBloc.isChecked,
|
||||||
checkColor: Colors.black,
|
checkColor: Colors.black,
|
||||||
shape: const CircleBorder(),
|
shape: const CircleBorder(),
|
||||||
onChanged: (bool? newValue) {
|
onChanged: (bool? newValue) {
|
||||||
loginBloc.add(
|
loginBloc.add(CheckBoxEvent(newValue: newValue));
|
||||||
CheckBoxEvent(newValue: newValue));
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -247,37 +236,30 @@ class LoginMobilePage extends StatelessWidget {
|
|||||||
child: RichText(
|
child: RichText(
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
text: 'Agree to ',
|
text: 'Agree to ',
|
||||||
style:
|
style: const TextStyle(color: Colors.white),
|
||||||
const TextStyle(color: Colors.white),
|
|
||||||
children: [
|
children: [
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: '(Terms of Service)',
|
text: '(Terms of Service)',
|
||||||
style: const TextStyle(
|
style: const TextStyle(color: Colors.black),
|
||||||
color: Colors.black),
|
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () {
|
..onTap = () {
|
||||||
loginBloc.launchURL(
|
loginBloc.launchURL('https://example.com/terms');
|
||||||
'https://example.com/terms');
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: ' (Legal Statement)',
|
text: ' (Legal Statement)',
|
||||||
style: const TextStyle(
|
style: const TextStyle(color: Colors.black),
|
||||||
color: Colors.black),
|
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () {
|
..onTap = () {
|
||||||
loginBloc.launchURL(
|
loginBloc.launchURL('https://example.com/legal');
|
||||||
'https://example.com/legal');
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: ' (Privacy Statement)',
|
text: ' (Privacy Statement)',
|
||||||
style: const TextStyle(
|
style: const TextStyle(color: Colors.black),
|
||||||
color: Colors.black),
|
|
||||||
recognizer: TapGestureRecognizer()
|
recognizer: TapGestureRecognizer()
|
||||||
..onTap = () {
|
..onTap = () {
|
||||||
loginBloc.launchURL(
|
loginBloc.launchURL('https://example.com/privacy');
|
||||||
'https://example.com/privacy');
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -294,14 +276,11 @@ class LoginMobilePage extends StatelessWidget {
|
|||||||
: ColorsManager.grayColor,
|
: ColorsManager.grayColor,
|
||||||
child: const Text('Sign in'),
|
child: const Text('Sign in'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (loginBloc.loginFormKey.currentState!
|
if (loginBloc.loginFormKey.currentState!.validate()) {
|
||||||
.validate()) {
|
|
||||||
loginBloc.add(
|
loginBloc.add(
|
||||||
LoginButtonPressed(
|
LoginButtonPressed(
|
||||||
username:
|
username: loginBloc.loginEmailController.text,
|
||||||
loginBloc.loginEmailController.text,
|
password: loginBloc.loginPasswordController.text,
|
||||||
password: loginBloc
|
|
||||||
.loginPasswordController.text,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -316,11 +295,10 @@ class LoginMobilePage extends StatelessWidget {
|
|||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
"Don't you have an account? ",
|
"Don't you have an account? ",
|
||||||
style: TextStyle(
|
style: TextStyle(color: Colors.white, fontSize: 13),
|
||||||
color: Colors.white, fontSize: 13),
|
|
||||||
)),
|
)),
|
||||||
Text(
|
Text(
|
||||||
'Sign up',
|
"Sign up",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -24,8 +24,7 @@ class LoginWebPage extends StatefulWidget {
|
|||||||
State<LoginWebPage> createState() => _LoginWebPageState();
|
State<LoginWebPage> createState() => _LoginWebPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LoginWebPageState extends State<LoginWebPage>
|
class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout {
|
||||||
with HelperResponsiveLayout {
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -43,7 +42,9 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
builder: _buildLoginForm,
|
builder: (context, state) {
|
||||||
|
return _buildLoginForm(context, state);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -53,12 +54,12 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
final loginBloc = BlocProvider.of<AuthBloc>(context);
|
final loginBloc = BlocProvider.of<AuthBloc>(context);
|
||||||
final isSmallScreen = isSmallScreenSize(context);
|
final isSmallScreen = isSmallScreenSize(context);
|
||||||
final isMediumScreen = isMediumScreenSize(context);
|
final isMediumScreen = isMediumScreenSize(context);
|
||||||
final size = MediaQuery.of(context).size;
|
Size size = MediaQuery.of(context).size;
|
||||||
late ScrollController scrollController;
|
late ScrollController scrollController;
|
||||||
scrollController = ScrollController();
|
scrollController = ScrollController();
|
||||||
|
|
||||||
void scrollToCenter() {
|
void scrollToCenter() {
|
||||||
final middlePosition = scrollController.position.maxScrollExtent / 2;
|
final double middlePosition = scrollController.position.maxScrollExtent / 2;
|
||||||
scrollController.animateTo(
|
scrollController.animateTo(
|
||||||
middlePosition,
|
middlePosition,
|
||||||
duration: const Duration(seconds: 1),
|
duration: const Duration(seconds: 1),
|
||||||
@ -83,7 +84,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
padding: EdgeInsets.all(size.width * 0.02),
|
padding: EdgeInsets.all(size.width * 0.02),
|
||||||
margin: EdgeInsets.all(size.width * 0.05),
|
margin: EdgeInsets.all(size.width * 0.05),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black.withValues(alpha: 0.3),
|
color: Colors.black.withOpacity(0.3),
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
@ -120,8 +121,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
const Spacer(),
|
const Spacer(),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: _buildLoginFormFields(
|
child: _buildLoginFormFields(context, loginBloc, size),
|
||||||
context, loginBloc, size),
|
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
],
|
],
|
||||||
@ -132,26 +132,23 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (state is AuthLoading)
|
if (state is AuthLoading) const Center(child: CircularProgressIndicator())
|
||||||
const Center(child: CircularProgressIndicator())
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLoginFormFields(
|
Widget _buildLoginFormFields(BuildContext context, AuthBloc loginBloc, Size size) {
|
||||||
BuildContext context, AuthBloc loginBloc, Size size) {
|
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withValues(alpha: 0.1),
|
color: Colors.white.withOpacity(0.1),
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(30)),
|
borderRadius: const BorderRadius.all(Radius.circular(30)),
|
||||||
border:
|
border: Border.all(color: ColorsManager.graysColor.withOpacity(0.2)),
|
||||||
Border.all(color: ColorsManager.graysColor.withValues(alpha: 0.2)),
|
|
||||||
),
|
),
|
||||||
child: Form(
|
child: Form(
|
||||||
key: loginBloc.loginFormKey,
|
key: loginBloc.loginFormKey,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.symmetric(
|
padding:
|
||||||
horizontal: size.width * 0.02, vertical: size.width * 0.003),
|
EdgeInsets.symmetric(horizontal: size.width * 0.02, vertical: size.width * 0.003),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -179,15 +176,14 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildDropdownField(
|
Widget _buildDropdownField(BuildContext context, AuthBloc loginBloc, Size size) {
|
||||||
BuildContext context, AuthBloc loginBloc, Size size) {
|
final TextEditingController textEditingController = TextEditingController();
|
||||||
final textEditingController = TextEditingController();
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Country/Region',
|
"Country/Region",
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
@ -250,8 +246,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
searchInnerWidgetHeight: 50,
|
searchInnerWidgetHeight: 50,
|
||||||
searchInnerWidget: Container(
|
searchInnerWidget: Container(
|
||||||
height: 50,
|
height: 50,
|
||||||
padding:
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
style: const TextStyle(color: Colors.black),
|
style: const TextStyle(color: Colors.black),
|
||||||
controller: textEditingController,
|
controller: textEditingController,
|
||||||
@ -266,8 +261,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
),
|
),
|
||||||
searchMatchFn: (item, searchValue) {
|
searchMatchFn: (item, searchValue) {
|
||||||
// Use the item's child text (region name) for searching.
|
// Use the item's child text (region name) for searching.
|
||||||
final regionName =
|
final regionName = (item.child as Text).data?.toLowerCase() ?? '';
|
||||||
(item.child as Text).data?.toLowerCase() ?? '';
|
|
||||||
final search = searchValue.toLowerCase().trim();
|
final search = searchValue.toLowerCase().trim();
|
||||||
// Debugging print statement to ensure values are captured correctly.
|
// Debugging print statement to ensure values are captured correctly.
|
||||||
// Return true if the region name contains the search term.
|
// Return true if the region name contains the search term.
|
||||||
@ -292,7 +286,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Email',
|
"Email",
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodySmall!
|
.bodySmall!
|
||||||
@ -309,9 +303,10 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
decoration: textBoxDecoration()!.copyWith(
|
decoration: textBoxDecoration()!.copyWith(
|
||||||
errorStyle: const TextStyle(height: 0),
|
errorStyle: const TextStyle(height: 0),
|
||||||
hintText: 'Enter your email address',
|
hintText: 'Enter your email address',
|
||||||
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
|
hintStyle: Theme.of(context)
|
||||||
color: ColorsManager.grayColor,
|
.textTheme
|
||||||
fontWeight: FontWeight.w400)),
|
.bodySmall!
|
||||||
|
.copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400)),
|
||||||
style: const TextStyle(color: Colors.black),
|
style: const TextStyle(color: Colors.black),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -325,7 +320,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Password',
|
"Password",
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodySmall!
|
.bodySmall!
|
||||||
@ -343,7 +338,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
controller: loginBloc.loginPasswordController,
|
controller: loginBloc.loginPasswordController,
|
||||||
onFieldSubmitted: (value) {
|
onFieldSubmitted: (value) {
|
||||||
if (loginBloc.loginFormKey.currentState!.validate()) {
|
if (loginBloc.loginFormKey.currentState!.validate()) {
|
||||||
loginBloc.add(LoginButtonPressed(
|
loginBloc.add(LoginButtonPressed(
|
||||||
username: loginBloc.loginEmailController.text,
|
username: loginBloc.loginEmailController.text,
|
||||||
password: value,
|
password: value,
|
||||||
));
|
));
|
||||||
@ -353,18 +348,17 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
},
|
},
|
||||||
decoration: textBoxDecoration()!.copyWith(
|
decoration: textBoxDecoration()!.copyWith(
|
||||||
hintText: 'At least 8 characters',
|
hintText: 'At least 8 characters',
|
||||||
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
|
hintStyle: Theme.of(context)
|
||||||
color: ColorsManager.grayColor, fontWeight: FontWeight.w400),
|
.textTheme
|
||||||
|
.bodySmall!
|
||||||
|
.copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400),
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
loginBloc.add(
|
loginBloc.add(PasswordVisibleEvent(newValue: loginBloc.obscureText));
|
||||||
PasswordVisibleEvent(newValue: loginBloc.obscureText));
|
|
||||||
},
|
},
|
||||||
icon: SizedBox(
|
icon: SizedBox(
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
loginBloc.obscureText
|
loginBloc.obscureText ? Assets.visiblePassword : Assets.invisiblePassword,
|
||||||
? Assets.visiblePassword
|
|
||||||
: Assets.invisiblePassword,
|
|
||||||
height: 15,
|
height: 15,
|
||||||
width: 15,
|
width: 15,
|
||||||
),
|
),
|
||||||
@ -391,11 +385,11 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
));
|
));
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
'Forgot Password?',
|
"Forgot Password?",
|
||||||
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
style: Theme.of(context)
|
||||||
color: Colors.black,
|
.textTheme
|
||||||
fontSize: 14,
|
.bodySmall!
|
||||||
fontWeight: FontWeight.w400),
|
.copyWith(color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -459,8 +453,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSignInButton(
|
Widget _buildSignInButton(BuildContext context, AuthBloc loginBloc, Size size) {
|
||||||
BuildContext context, AuthBloc loginBloc, Size size) {
|
|
||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -474,7 +467,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: loginBloc.checkValidate
|
color: loginBloc.checkValidate
|
||||||
? ColorsManager.whiteColors
|
? ColorsManager.whiteColors
|
||||||
: ColorsManager.whiteColors.withValues(alpha: 0.2),
|
: ColorsManager.whiteColors.withOpacity(0.2),
|
||||||
)),
|
)),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (loginBloc.loginFormKey.currentState!.validate()) {
|
if (loginBloc.loginFormKey.currentState!.validate()) {
|
||||||
@ -501,8 +494,7 @@ class _LoginWebPageState extends State<LoginWebPage>
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
child: Text(
|
child: Text(
|
||||||
loginBloc.validate,
|
loginBloc.validate,
|
||||||
style: const TextStyle(
|
style: const TextStyle(fontWeight: FontWeight.w700, color: ColorsManager.red),
|
||||||
fontWeight: FontWeight.w700, color: ColorsManager.red),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -108,64 +108,66 @@ class _DynamicTableState extends State<AccessDeviceTable> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
if (widget.withCheckBox) _buildSelectAllCheckbox(),
|
if (widget.withCheckBox) _buildSelectAllCheckbox(),
|
||||||
...widget.headers.map(_buildTableHeaderCell),
|
...widget.headers
|
||||||
|
.map((header) => _buildTableHeaderCell(header)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.isEmpty)
|
widget.isEmpty
|
||||||
Expanded(
|
? Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
SvgPicture.asset(Assets.emptyTable),
|
Column(
|
||||||
const SizedBox(
|
children: [
|
||||||
height: 15,
|
SvgPicture.asset(Assets.emptyTable),
|
||||||
|
const SizedBox(
|
||||||
|
height: 15,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
// no password
|
||||||
|
widget.tableName == 'AccessManagement'
|
||||||
|
? 'No Password '
|
||||||
|
: 'No Devices',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall!
|
||||||
|
.copyWith(
|
||||||
|
color: ColorsManager.grayColor),
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
Text(
|
|
||||||
// no password
|
|
||||||
widget.tableName == 'AccessManagement'
|
|
||||||
? 'No Password '
|
|
||||||
: 'No Devices',
|
|
||||||
style: Theme.of(context)
|
|
||||||
.textTheme
|
|
||||||
.bodySmall!
|
|
||||||
.copyWith(color: ColorsManager.grayColor),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
)
|
||||||
),
|
: Expanded(
|
||||||
)
|
child: Container(
|
||||||
else
|
color: Colors.white,
|
||||||
Expanded(
|
child: ListView.builder(
|
||||||
child: ColoredBox(
|
shrinkWrap: true,
|
||||||
color: Colors.white,
|
itemCount: widget.data.length,
|
||||||
child: ListView.builder(
|
itemBuilder: (context, index) {
|
||||||
shrinkWrap: true,
|
final row = widget.data[index];
|
||||||
itemCount: widget.data.length,
|
return Row(
|
||||||
itemBuilder: (context, index) {
|
children: [
|
||||||
final row = widget.data[index];
|
if (widget.withCheckBox)
|
||||||
return Row(
|
_buildRowCheckbox(
|
||||||
children: [
|
index, widget.size.height * 0.10),
|
||||||
if (widget.withCheckBox)
|
...row.map((cell) => _buildTableCell(
|
||||||
_buildRowCheckbox(
|
cell.toString(),
|
||||||
index, widget.size.height * 0.10),
|
widget.size.height * 0.10)),
|
||||||
...row.map((cell) => _buildTableCell(
|
],
|
||||||
cell.toString(), widget.size.height * 0.10)),
|
);
|
||||||
],
|
},
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -243,7 +245,7 @@ class _DynamicTableState extends State<AccessDeviceTable> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTableCell(String content, double size) {
|
Widget _buildTableCell(String content, double size) {
|
||||||
final isBatteryLevel = content.endsWith('%');
|
bool isBatteryLevel = content.endsWith('%');
|
||||||
double? batteryLevel;
|
double? batteryLevel;
|
||||||
|
|
||||||
if (isBatteryLevel) {
|
if (isBatteryLevel) {
|
||||||
|
@ -22,21 +22,17 @@ class CancelButton extends StatelessWidget {
|
|||||||
return ElevatedButton(
|
return ElevatedButton(
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
backgroundColor:
|
backgroundColor: WidgetStateProperty.all(ColorsManager.boxColor), // White background
|
||||||
WidgetStateProperty.all(ColorsManager.boxColor), // White background
|
foregroundColor: WidgetStateProperty.all(Colors.black), // Black text color
|
||||||
foregroundColor:
|
|
||||||
WidgetStateProperty.all(Colors.black), // Black text color
|
|
||||||
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
shape: WidgetStateProperty.all<RoundedRectangleBorder>(
|
||||||
RoundedRectangleBorder(
|
RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(borderRadius ?? 10),
|
borderRadius: BorderRadius.circular(borderRadius ?? 10),
|
||||||
side:
|
side: const BorderSide(color: ColorsManager.boxColor), // Black border
|
||||||
const BorderSide(color: ColorsManager.boxColor), // Black border
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
fixedSize: WidgetStateProperty.all(
|
fixedSize: WidgetStateProperty.all(Size(width ?? 50, height ?? 40)), // Set button height
|
||||||
Size(width ?? 50, height ?? 40)), // Set button height
|
|
||||||
),
|
),
|
||||||
child: Text(label), // Dynamic label
|
child: Text(label), // Dynamic label
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -64,7 +64,7 @@ class DefaultButton extends StatelessWidget {
|
|||||||
(Set<WidgetState> states) {
|
(Set<WidgetState> states) {
|
||||||
return enabled
|
return enabled
|
||||||
? backgroundColor ?? ColorsManager.primaryColor
|
? backgroundColor ?? ColorsManager.primaryColor
|
||||||
: Colors.black.withValues(alpha: 0.2);
|
: Colors.black.withOpacity(0.2);
|
||||||
}),
|
}),
|
||||||
shape: WidgetStateProperty.all(
|
shape: WidgetStateProperty.all(
|
||||||
RoundedRectangleBorder(
|
RoundedRectangleBorder(
|
||||||
|
@ -34,7 +34,7 @@ class CurtainToggle extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ClipOval(
|
ClipOval(
|
||||||
child: ColoredBox(
|
child: Container(
|
||||||
color: ColorsManager.whiteColors,
|
color: ColorsManager.whiteColors,
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
Assets.curtainIcon,
|
Assets.curtainIcon,
|
||||||
@ -52,7 +52,7 @@ class CurtainToggle extends StatelessWidget {
|
|||||||
width: 35,
|
width: 35,
|
||||||
child: CupertinoSwitch(
|
child: CupertinoSwitch(
|
||||||
value: value,
|
value: value,
|
||||||
activeTrackColor: ColorsManager.dialogBlueTitle,
|
activeColor: ColorsManager.dialogBlueTitle,
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -12,7 +12,7 @@ Future<void> showCustomDialog({
|
|||||||
double? iconWidth,
|
double? iconWidth,
|
||||||
VoidCallback? onOkPressed,
|
VoidCallback? onOkPressed,
|
||||||
bool barrierDismissible = false,
|
bool barrierDismissible = false,
|
||||||
List<Widget>? actions,
|
List<Widget>? actions,
|
||||||
}) {
|
}) {
|
||||||
return showDialog(
|
return showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@ -40,20 +40,14 @@ Future<void> showCustomDialog({
|
|||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.headlineLarge!
|
.headlineLarge!
|
||||||
.copyWith(
|
.copyWith(fontSize: 20, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
color: Colors.black),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
message,
|
message,
|
||||||
style: Theme.of(context)
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(color: Colors.black),
|
||||||
.textTheme
|
|
||||||
.bodyMedium!
|
|
||||||
.copyWith(color: Colors.black),
|
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -20,8 +20,8 @@ class DynamicTable extends StatefulWidget {
|
|||||||
final void Function(int, bool, dynamic)? onRowSelected;
|
final void Function(int, bool, dynamic)? onRowSelected;
|
||||||
final List<String>? initialSelectedIds;
|
final List<String>? initialSelectedIds;
|
||||||
final int uuidIndex;
|
final int uuidIndex;
|
||||||
final void Function(dynamic selectedRows)? onSelectionChanged;
|
final Function(dynamic selectedRows)? onSelectionChanged;
|
||||||
final void Function(int rowIndex)? onSettingsPressed;
|
final Function(int rowIndex)? onSettingsPressed;
|
||||||
const DynamicTable({
|
const DynamicTable({
|
||||||
super.key,
|
super.key,
|
||||||
required this.headers,
|
required this.headers,
|
||||||
@ -79,10 +79,10 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
// Check if the old and new lists are the same
|
// Check if the old and new lists are the same
|
||||||
if (oldList.length != newList.length) return false;
|
if (oldList.length != newList.length) return false;
|
||||||
|
|
||||||
for (var i = 0; i < oldList.length; i++) {
|
for (int i = 0; i < oldList.length; i++) {
|
||||||
if (oldList[i].length != newList[i].length) return false;
|
if (oldList[i].length != newList[i].length) return false;
|
||||||
|
|
||||||
for (var j = 0; j < oldList[i].length; j++) {
|
for (int j = 0; j < oldList[i].length; j++) {
|
||||||
if (oldList[i][j] != newList[i][j]) return false;
|
if (oldList[i][j] != newList[i][j]) return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,7 +162,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
controller: _horizontalBodyScrollController,
|
controller: _horizontalBodyScrollController,
|
||||||
child: ColoredBox(
|
child: Container(
|
||||||
color: ColorsManager.whiteColors,
|
color: ColorsManager.whiteColors,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: widget.size.width,
|
width: widget.size.width,
|
||||||
@ -184,7 +184,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
rowIndex: rowIndex,
|
rowIndex: rowIndex,
|
||||||
columnIndex: entry.key,
|
columnIndex: entry.key,
|
||||||
);
|
);
|
||||||
}),
|
}).toList(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@ -304,13 +304,13 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
required int rowIndex,
|
required int rowIndex,
|
||||||
required int columnIndex,
|
required int columnIndex,
|
||||||
}) {
|
}) {
|
||||||
final isBatteryLevel = content.endsWith('%');
|
bool isBatteryLevel = content.endsWith('%');
|
||||||
double? batteryLevel;
|
double? batteryLevel;
|
||||||
|
|
||||||
if (isBatteryLevel) {
|
if (isBatteryLevel) {
|
||||||
batteryLevel = double.tryParse(content.replaceAll('%', '').trim());
|
batteryLevel = double.tryParse(content.replaceAll('%', '').trim());
|
||||||
}
|
}
|
||||||
final isSettingsColumn = widget.headers[columnIndex] == 'Settings';
|
bool isSettingsColumn = widget.headers[columnIndex] == 'Settings';
|
||||||
|
|
||||||
if (isSettingsColumn) {
|
if (isSettingsColumn) {
|
||||||
return buildSettingsIcon(
|
return buildSettingsIcon(
|
||||||
@ -404,7 +404,7 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
borderRadius: BorderRadius.circular(height / 2),
|
borderRadius: BorderRadius.circular(height / 2),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withValues(alpha: 0.17),
|
color: Colors.black.withOpacity(0.17),
|
||||||
blurRadius: 14,
|
blurRadius: 14,
|
||||||
offset: const Offset(0, 4),
|
offset: const Offset(0, 4),
|
||||||
),
|
),
|
||||||
@ -416,10 +416,11 @@ class _DynamicTableState extends State<DynamicTable> {
|
|||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
Assets.settings,
|
Assets.settings, // ضع المسار الصحيح هنا
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 22,
|
height: 22,
|
||||||
color: ColorsManager.primaryColor,
|
color: ColorsManager
|
||||||
|
.primaryColor, // نفس لون الأيقونة في الصورة
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -36,7 +36,7 @@ class FilterWidget extends StatelessWidget {
|
|||||||
color: ColorsManager.boxColor,
|
color: ColorsManager.boxColor,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.blueColor.withValues(alpha: 0.8)
|
? ColorsManager.blueColor.withOpacity(0.8)
|
||||||
: Colors.transparent,
|
: Colors.transparent,
|
||||||
width: 2.0,
|
width: 2.0,
|
||||||
),
|
),
|
||||||
@ -48,7 +48,7 @@ class FilterWidget extends StatelessWidget {
|
|||||||
tabs[index],
|
tabs[index],
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? ColorsManager.blueColor.withValues(alpha: 0.8)
|
? ColorsManager.blueColor.withOpacity(0.8)
|
||||||
: Colors.black,
|
: Colors.black,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class HourPickerDialog extends StatefulWidget {
|
class HourPickerDialog extends StatefulWidget {
|
||||||
@ -16,7 +18,7 @@ class _HourPickerDialogState extends State<HourPickerDialog> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// Initialize the selectedHour with the initial time passed to the dialog
|
// Initialize the selectedHour with the initial time passed to the dialog
|
||||||
selectedHour = '${widget.initialTime.hour.toString().padLeft(2, '0')}:00';
|
selectedHour = widget.initialTime.hour.toString().padLeft(2, '0') + ':00';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -26,7 +28,7 @@ class _HourPickerDialogState extends State<HourPickerDialog> {
|
|||||||
content: DropdownButton<String>(
|
content: DropdownButton<String>(
|
||||||
value: selectedHour, // Show the currently selected hour
|
value: selectedHour, // Show the currently selected hour
|
||||||
items: List.generate(24, (index) {
|
items: List.generate(24, (index) {
|
||||||
final hour = index.toString().padLeft(2, '0');
|
String hour = index.toString().padLeft(2, '0');
|
||||||
return DropdownMenuItem<String>(
|
return DropdownMenuItem<String>(
|
||||||
value: '$hour:00',
|
value: '$hour:00',
|
||||||
child: Text('$hour:00'),
|
child: Text('$hour:00'),
|
||||||
@ -35,16 +37,14 @@ class _HourPickerDialogState extends State<HourPickerDialog> {
|
|||||||
onChanged: (String? newValue) {
|
onChanged: (String? newValue) {
|
||||||
if (newValue != null) {
|
if (newValue != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
selectedHour =
|
selectedHour = newValue; // Update the selected hour without closing the dialog
|
||||||
newValue; // Update the selected hour without closing the dialog
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context)
|
onPressed: () => Navigator.of(context).pop(null), // Close the dialog without selection
|
||||||
.pop(null), // Close the dialog without selection
|
|
||||||
child: const Text('Cancel'),
|
child: const Text('Cancel'),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user