Compare commits

...

16 Commits

Author SHA1 Message Date
9bfc0cea10 add some horizontal padding 2025-07-16 09:19:55 +03:00
75b9f4a4e6 changed the title from Routine to Workflow Automation (#354)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1851](https://syncrow.atlassian.net/browse/SP-1851)

## Description

change the Title to Work Flow 

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1851]:
https://syncrow.atlassian.net/browse/SP-1851?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-07-15 11:54:29 +03:00
fe4063ef8f changed the title from Routine to Workflow Automation 2025-07-15 11:20:13 +03:00
029b5d32e0 Adjust table scroll behavior and modify height for improved layout (#353)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Description

<!--- Describe your changes in detail -->
Adjust table scroll behavior and modify height for improved layout

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore
2025-07-15 10:43:22 +03:00
428c81efff Adjust table scroll behavior and modify height for improved layout 2025-07-15 10:05:07 +03:00
288c252f46 SP-1696-fe-edit-user-dialog-enhancements (#347)
<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1696](https://syncrow.atlassian.net/browse/SP-1696)

## Description

add company Name and replace it with job title

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [x]  New feature (non-breaking change which adds functionality)
- [ ] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1696]:
https://syncrow.atlassian.net/browse/SP-1696?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-07-15 09:08:15 +03:00
7399dee687 [FE] Redundant API calls on Routines page when selecting a community from the tree (#345)
…rom the tree

<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->

## Jira Ticket
[SP-1800](https://syncrow.atlassian.net/browse/SP-1800)

## Description

fix Redundant API calls when choosing devices and use Que Par to send
spaces instead of send api for every space

## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore 


[SP-1800]:
https://syncrow.atlassian.net/browse/SP-1800?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2025-07-15 08:32:08 +03:00
08e2ed4b4c Merge branch 'dev' into revert-SP-1589 2025-07-15 08:31:45 +03:00
59e04708cd Merge branch 'main' into revert-SP-1589 2025-07-15 08:30:33 +03:00
338d4f5737 fix typo 2025-07-15 08:19:43 +03:00
5532935a3a Enhance UI components: update color management, adjust button styles,… (#350)
… and improve text formatting for better readability

<!--
  Thanks for contributing!

Provide a description of your changes below and a general summary in the
title

Please look at the following checklist to ensure that your PR can be
accepted quickly:
-->



## Description

<!--- Describe your changes in detail -->
Enhance UI components: update color 
## Type of Change

<!--- Put an `x` in all the boxes that apply: -->

- [ ]  New feature (non-breaking change which adds functionality)
- [x] 🛠️ Bug fix (non-breaking change which fixes an issue)
- [ ]  Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] 🧹 Code refactor
- [ ]  Build configuration change
- [ ] 📝 Documentation
- [ ] 🗑️ Chore
2025-07-14 16:15:42 +03:00
249cbfc242 Merge branch 'dev' into Build-Schedule-List-View-Support-State-Persistence 2025-07-14 15:20:08 +03:00
8167926620 Enhance UI components: update color management, adjust button styles, and improve text formatting for better readability 2025-07-14 15:14:56 +03:00
2681c837f5 add company name and replace it with job title 2025-07-11 12:10:53 +03:00
b6664ec1ba fix Redundant API calls on Routines page when selecting a community from the tree 2025-07-11 10:53:04 +03:00
dcf1df9b4a sp1613 delete condition word 2025-05-21 07:25:34 -05:00
23 changed files with 221 additions and 180 deletions

View File

@ -132,6 +132,8 @@ class _DynamicTableState extends State<DynamicTable> {
child: SingleChildScrollView(
controller: _horizontalScrollController,
scrollDirection: Axis.horizontal,
physics:
widget.isEmpty ? const NeverScrollableScrollPhysics() : null,
child: SizedBox(
width: _totalTableWidth,
child: Column(
@ -164,7 +166,6 @@ class _DynamicTableState extends State<DynamicTable> {
],
),
),
Expanded(
child: widget.isEmpty
? _buildEmptyState()
@ -265,7 +266,7 @@ class _DynamicTableState extends State<DynamicTable> {
),
],
),
SizedBox(height: widget.size.height * 0.5),
SizedBox(height: widget.size.height * 0.2),
],
),
);

View File

@ -46,15 +46,15 @@ class DeviceManagementBloc
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (spaceBloc.state.selectedCommunities.isEmpty) {
devices = await DevicesManagementApi().fetchDevices('', '', projectUuid);
devices = await DevicesManagementApi().fetchDevices(
projectUuid,
);
} else {
for (final community in spaceBloc.state.selectedCommunities) {
for (var community in spaceBloc.state.selectedCommunities) {
final spacesList =
spaceBloc.state.selectedCommunityAndSpaces[community] ?? [];
for (final space in spacesList) {
devices.addAll(await DevicesManagementApi()
.fetchDevices(community, space, projectUuid));
}
devices.addAll(await DevicesManagementApi()
.fetchDevices(projectUuid, spacesId: spacesList));
}
}

View File

@ -24,12 +24,12 @@ class DeviceManagementPage extends StatefulWidget with HelperResponsiveLayout {
}
class _DeviceManagementPageState extends State<DeviceManagementPage> {
@override
@override
void initState() {
context.read<SpaceTreeBloc>().add(InitialEvent());
super.initState();
}
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
@ -90,7 +90,7 @@ class _DeviceManagementPageState extends State<DeviceManagementPage> {
const TriggerSwitchTabsEvent(isRoutineTab: true));
},
child: Text(
'Routines',
'Workflow Automation',
style: context.textTheme.titleMedium?.copyWith(
color: state.routineTab
? ColorsManager.whiteColors

View File

@ -29,7 +29,9 @@ class CountdownModeButtons extends StatelessWidget {
children: [
Expanded(
child: DefaultButton(
elevation: 2.5,
height: 40,
borderRadius: 8,
onPressed: () => Navigator.pop(context),
backgroundColor: ColorsManager.boxColor,
child: Text('Cancel', style: context.textTheme.bodyMedium),
@ -39,6 +41,8 @@ class CountdownModeButtons extends StatelessWidget {
Expanded(
child: isActive
? DefaultButton(
elevation: 2.5,
borderRadius: 8,
height: 40,
onPressed: () {
context.read<ScheduleBloc>().add(
@ -49,10 +53,12 @@ class CountdownModeButtons extends StatelessWidget {
),
);
},
backgroundColor: Colors.red,
backgroundColor: ColorsManager.red100,
child: const Text('Stop'),
)
: DefaultButton(
elevation: 2.5,
borderRadius: 8,
height: 40,
onPressed: () {
context.read<ScheduleBloc>().add(
@ -63,7 +69,7 @@ class CountdownModeButtons extends StatelessWidget {
countDownCode: countDownCode),
);
},
backgroundColor: ColorsManager.primaryColor,
backgroundColor: ColorsManager.primaryColorWithOpacity,
child: const Text('Save'),
),
),

View File

@ -226,6 +226,7 @@ class _CountdownInchingViewState extends State<CountdownInchingView> {
index.toString().padLeft(2, '0'),
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.w400,
color: isActive ? ColorsManager.grayColor : Colors.black,
),
),
@ -240,7 +241,8 @@ class _CountdownInchingViewState extends State<CountdownInchingView> {
label,
style: const TextStyle(
color: ColorsManager.grayColor,
fontSize: 18,
fontSize: 24,
fontWeight: FontWeight.w400,
),
),
],

View File

@ -31,12 +31,11 @@ class BuildScheduleView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => ScheduleBloc(
deviceId: deviceUuid,
)
create: (_) => ScheduleBloc(deviceId: deviceUuid,)
..add(ScheduleGetEvent(category: category))
..add(ScheduleFetchStatusEvent(
deviceId: deviceUuid, countdownCode: countdownCode ?? '')),
deviceId: deviceUuid,
countdownCode: countdownCode ?? '')),
child: Dialog(
backgroundColor: Colors.white,
insetPadding: const EdgeInsets.all(20),
@ -77,7 +76,8 @@ class BuildScheduleView extends StatelessWidget {
category: category,
time: '',
function: Status(
code: code.toString(), value: null),
code: code.toString(),
value: true),
days: [],
),
isEdit: false,

View File

@ -13,9 +13,9 @@ class ScheduleHeader extends StatelessWidget {
Text(
'Scheduling',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 22,
color: ColorsManager.dialogBlueTitle,
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontWeight.w700,
fontSize: 30,
),
),
Container(

View File

@ -27,7 +27,7 @@ class ScheduleManagementUI extends StatelessWidget {
width: 170,
height: 40,
child: DefaultButton(
borderColor: ColorsManager.boxColor,
borderColor: ColorsManager.grayColor.withOpacity(0.5),
padding: 2,
backgroundColor: ColorsManager.graysColor,
borderRadius: 15,

View File

@ -19,6 +19,8 @@ class ScheduleModeButtons extends StatelessWidget {
children: [
Expanded(
child: DefaultButton(
elevation: 2.5,
borderRadius: 8,
height: 40,
onPressed: () {
Navigator.pop(context);
@ -33,9 +35,11 @@ class ScheduleModeButtons extends StatelessWidget {
const SizedBox(width: 20),
Expanded(
child: DefaultButton(
elevation: 2.5,
borderRadius: 8,
height: 40,
onPressed: onSave,
backgroundColor: ColorsManager.primaryColor,
backgroundColor: ColorsManager.primaryColorWithOpacity,
child: const Text('Save'),
),
),

View File

@ -35,12 +35,12 @@ class ScheduleModeSelector extends StatelessWidget {
),
const SizedBox(height: 4),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
_buildRadioTile(
context, 'Countdown', ScheduleModes.countdown, currentMode),
_buildRadioTile(
context, 'Schedule', ScheduleModes.schedule, currentMode),
const Spacer(flex: 1),
// _buildRadioTile(
// context, 'Circulate', ScheduleModes.circulate, currentMode),
// _buildRadioTile(
@ -65,6 +65,7 @@ class ScheduleModeSelector extends StatelessWidget {
style: context.textTheme.bodySmall!.copyWith(
fontSize: 13,
color: ColorsManager.blackColor,
fontWeight: FontWeight.w400,
),
),
leading: Radio<ScheduleModes>(

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/device_managment/schedule_device/schedule_widgets/schedule_mode_buttons.dart';
import 'package:syncrow_web/pages/device_managment/water_heater/models/schedule_entry.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class ScheduleDialogHelper {
static const List<String> allDays = [
@ -56,8 +58,9 @@ class ScheduleDialogHelper {
Text(
isEdit ? 'Edit Schedule' : 'Add Schedule',
style: Theme.of(context).textTheme.titleLarge!.copyWith(
color: Colors.blue,
fontWeight: FontWeight.bold,
color: ColorsManager.primaryColorWithOpacity,
fontWeight: FontWeight.w700,
fontSize: 30,
),
),
const SizedBox(),
@ -69,9 +72,9 @@ class ScheduleDialogHelper {
height: 40,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[200],
backgroundColor: ColorsManager.boxColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
borderRadius: BorderRadius.circular(8),
),
),
onPressed: () async {
@ -110,39 +113,27 @@ class ScheduleDialogHelper {
],
),
actions: [
SizedBox(
width: 100,
child: OutlinedButton(
onPressed: () {
Navigator.pop(ctx, null);
},
child: const Text('Cancel'),
),
ScheduleModeButtons(
onSave: () {
dynamic temp;
if (deviceType == 'CUR_2') {
temp = functionOn! ? 'open' : 'close';
} else {
temp = functionOn;
}
final entry = ScheduleEntry(
category: schedule?.category ?? 'switch_1',
time: _formatTimeOfDayToISO(selectedTime),
function: Status(
code: code ?? 'switch_1',
value: temp,
),
days: _convertSelectedDaysToStrings(selectedDays),
scheduleId: schedule.scheduleId,
);
Navigator.pop(ctx, entry);
},
),
SizedBox(
width: 100,
child: ElevatedButton(
onPressed: () {
dynamic temp;
if (deviceType == 'CUR_2') {
temp = functionOn! ? 'open' : 'close';
} else {
temp = functionOn;
}
final entry = ScheduleEntry(
category: schedule?.category ?? 'switch_1',
time: _formatTimeOfDayToISO(selectedTime),
function: Status(
code: code ?? 'switch_1',
value: temp,
),
days: _convertSelectedDaysToStrings(selectedDays),
scheduleId: schedule.scheduleId,
);
Navigator.pop(ctx, entry);
},
child: const Text('Save'),
)),
],
);
},

View File

@ -153,6 +153,7 @@ class EditUserModel {
final String? jobTitle; // can be empty
final String roleType; // e.g. "ADMIN"
final List<UserSpaceModel> spaces;
final String? companyName;
EditUserModel({
required this.uuid,
@ -167,6 +168,7 @@ class EditUserModel {
required this.jobTitle,
required this.roleType,
required this.spaces,
this.companyName,
});
/// Create a [UserData] from JSON data
@ -182,6 +184,7 @@ class EditUserModel {
invitedBy: json['invitedBy'] as String,
phoneNumber: json['phoneNumber'] ?? '',
jobTitle: json['jobTitle'] ?? '',
companyName: json['companyName'] as String?,
roleType: json['roleType'] as String,
spaces: (json['spaces'] as List<dynamic>)
.map((e) => UserSpaceModel.fromJson(e as Map<String, dynamic>))

View File

@ -12,7 +12,7 @@ class RolesUserModel {
final dynamic jobTitle;
final dynamic createdDate;
final dynamic createdTime;
final String? companyName;
RolesUserModel({
required this.uuid,
required this.createdAt,
@ -27,6 +27,7 @@ class RolesUserModel {
this.jobTitle,
required this.createdDate,
required this.createdTime,
this.companyName,
});
factory RolesUserModel.fromJson(Map<String, dynamic> json) {
@ -47,6 +48,7 @@ class RolesUserModel {
: json['jobTitle'],
createdDate: json['createdDate'],
createdTime: json['createdTime'],
companyName: json['companyName'] as String?,
);
}
}

View File

@ -52,7 +52,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
final TextEditingController lastNameController = TextEditingController();
final TextEditingController emailController = TextEditingController();
final TextEditingController phoneController = TextEditingController();
final TextEditingController jobTitleController = TextEditingController();
final TextEditingController companyNameController = TextEditingController();
final TextEditingController roleSearchController = TextEditingController();
bool? isCompleteBasics;
@ -352,7 +352,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
bool res = await UserPermissionApi().sendInviteUser(
email: emailController.text,
firstName: firstNameController.text,
jobTitle: jobTitleController.text,
companyName: companyNameController.text,
lastName: lastNameController.text,
phoneNumber: phoneController.text,
roleUuid: roleSelected,
@ -405,7 +405,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
bool res = await UserPermissionApi().editInviteUser(
userId: event.userId,
firstName: firstNameController.text,
jobTitle: jobTitleController.text,
companyName: companyNameController.text,
lastName: lastNameController.text,
phoneNumber: phoneController.text,
roleUuid: roleSelected,
@ -455,7 +455,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
Future<void> checkEmail(
CheckEmailEvent event, Emitter<UsersState> emit) async {
emit(UsersLoadingState());
String? res = await UserPermissionApi().checkEmail(
String? res = await UserPermissionApi().checkEmail(
emailController.text,
);
checkEmailValid = res!;
@ -529,7 +529,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
lastNameController.text = res.lastName;
emailController.text = res.email;
phoneController.text = res.phoneNumber ?? '';
jobTitleController.text = res.jobTitle ?? '';
companyNameController.text = res.companyName ?? '';
res.roleType;
res.spaces.map((space) {
selectedIds.add(space.uuid);
@ -645,7 +645,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
lastNameController.dispose();
emailController.dispose();
phoneController.dispose();
jobTitleController.dispose();
companyNameController.dispose();
roleSearchController.dispose();
return super.close();
}

View File

@ -317,7 +317,7 @@ class BasicsView extends StatelessWidget {
child: Row(
children: [
Text(
'Job Title',
'Company Name',
style: context.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w400,
fontSize: 13,
@ -328,11 +328,11 @@ class BasicsView extends StatelessWidget {
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
controller: _blocRole.jobTitleController,
controller: _blocRole.companyNameController,
style:
const TextStyle(color: ColorsManager.blackColor),
decoration: inputTextFormDeco(
hintText: "Job Title (Optional)")
hintText: 'Company Name (Optional)')
.copyWith(
hintStyle: context.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w400,

View File

@ -411,7 +411,7 @@ class UsersPage extends StatelessWidget {
titles: const [
"Full Name",
"Email Address",
"Job Title",
"Company Name",
"Role",
"Creation Date",
"Creation Time",
@ -424,7 +424,7 @@ class UsersPage extends StatelessWidget {
return [
Text('${user.firstName} ${user.lastName}'),
Text(user.email),
Text(user.jobTitle),
Center(child: Text(user.companyName ?? '-')),
Text(user.roleType ?? ''),
Text(user.createdDate ?? ''),
Text(user.createdTime ?? ''),

View File

@ -170,45 +170,45 @@ class RoutineBloc extends Bloc<RoutineEvent, RoutineState> {
}
}
Future<void> _onLoadScenes(
LoadScenes event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
List<ScenesModel> scenes = [];
try {
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
var spaceBloc = context.read<SpaceTreeBloc>();
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
scenes.addAll(
await SceneApi.getScenes(spaceId, communityId, projectUuid));
Future<void> _onLoadScenes(
LoadScenes event, Emitter<RoutineState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
List<ScenesModel> scenes = [];
try {
BuildContext context = NavigationService.navigatorKey.currentContext!;
var createRoutineBloc = context.read<CreateRoutineBloc>();
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
if (createRoutineBloc.selectedSpaceId == '' &&
createRoutineBloc.selectedCommunityId == '') {
var spaceBloc = context.read<SpaceTreeBloc>();
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
scenes.addAll(
await SceneApi.getScenes(spaceId, communityId, projectUuid));
}
}
} else {
scenes.addAll(await SceneApi.getScenes(
createRoutineBloc.selectedSpaceId,
createRoutineBloc.selectedCommunityId,
projectUuid));
}
} else {
scenes.addAll(await SceneApi.getScenes(
createRoutineBloc.selectedSpaceId,
createRoutineBloc.selectedCommunityId,
projectUuid));
}
emit(state.copyWith(
scenes: scenes,
isLoading: false,
));
} catch (e) {
emit(state.copyWith(
emit(state.copyWith(
scenes: scenes,
isLoading: false,
loadScenesErrorMessage: 'Failed to load scenes',
errorMessage: '',
loadAutomationErrorMessage: '',
scenes: scenes));
));
} catch (e) {
emit(state.copyWith(
isLoading: false,
loadScenesErrorMessage: 'Failed to load scenes',
errorMessage: '',
loadAutomationErrorMessage: '',
scenes: scenes));
}
}
}
Future<void> _onLoadAutomation(
LoadAutomation event, Emitter<RoutineState> emit) async {
@ -936,16 +936,15 @@ Future<void> _onLoadScenes(
for (var communityId in spaceBloc.state.selectedCommunities) {
List<String> spacesList =
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
for (var spaceId in spacesList) {
devices.addAll(await DevicesManagementApi()
.fetchDevices(communityId, spaceId, projectUuid));
}
devices.addAll(await DevicesManagementApi()
.fetchDevices(projectUuid, spacesId: spacesList));
}
} else {
devices.addAll(await DevicesManagementApi().fetchDevices(
createRoutineBloc.selectedCommunityId,
createRoutineBloc.selectedSpaceId,
projectUuid));
projectUuid,
spacesId: [createRoutineBloc.selectedSpaceId],
));
}
emit(state.copyWith(isLoading: false, devices: devices));

View File

@ -96,9 +96,7 @@ class _WallPresenceSensorState extends State<FlushPresenceSensor> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
DialogHeader(widget.dialogType == 'THEN'
? 'Presence Sensor Functions'
: 'Presence Sensor Condition'),
const DialogHeader('Presence Sensor'),
Expanded(child: _buildMainContent(context, state)),
_buildDialogFooter(context, state),
],

View File

@ -39,7 +39,8 @@ class VisitorPasswordDialog extends StatelessWidget {
title: 'Sent Successfully',
widgeta: Column(
children: [
if (visitorBloc.passwordStatus!.failedOperations.isNotEmpty)
if (visitorBloc
.passwordStatus!.failedOperations.isNotEmpty)
Column(
children: [
const Text('Failed Devices'),
@ -54,18 +55,23 @@ class VisitorPasswordDialog extends StatelessWidget {
itemBuilder: (context, index) {
return Container(
margin: const EdgeInsets.all(5),
padding: const EdgeInsets.symmetric(
horizontal: 10),
decoration: containerDecoration,
height: 45,
child: Center(
child: Text(visitorBloc.passwordStatus!
.failedOperations[index].deviceName)),
child: Text(visitorBloc
.passwordStatus!
.failedOperations[index]
.deviceName)),
);
},
),
),
],
),
if (visitorBloc.passwordStatus!.successOperations.isNotEmpty)
if (visitorBloc
.passwordStatus!.successOperations.isNotEmpty)
Column(
children: [
const Text('Success Devices'),
@ -80,11 +86,15 @@ class VisitorPasswordDialog extends StatelessWidget {
itemBuilder: (context, index) {
return Container(
margin: const EdgeInsets.all(5),
padding: const EdgeInsets.symmetric(
horizontal: 10),
decoration: containerDecoration,
height: 45,
child: Center(
child: Text(visitorBloc.passwordStatus!
.successOperations[index].deviceName)),
child: Text(visitorBloc
.passwordStatus!
.successOperations[index]
.deviceName)),
);
},
),
@ -114,7 +124,9 @@ class VisitorPasswordDialog extends StatelessWidget {
title: Text(
'Create visitor password',
style: Theme.of(context).textTheme.headlineLarge!.copyWith(
fontWeight: FontWeight.w400, fontSize: 24, color: Colors.black),
fontWeight: FontWeight.w400,
fontSize: 24,
color: Colors.black),
),
content: state is LoadingInitialState
? const Center(child: CircularProgressIndicator())
@ -300,12 +312,14 @@ class VisitorPasswordDialog extends StatelessWidget {
visitorBloc.accessTypeSelected ==
'Offline Password') {
visitorBloc.add(SelectTimeEvent(
context: context, isEffective: false));
} else {
visitorBloc.add(SelectTimeVisitorPassword(
context: context,
isStart: false,
isRepeat: false));
isEffective: false));
} else {
visitorBloc.add(
SelectTimeVisitorPassword(
context: context,
isStart: false,
isRepeat: false));
}
},
startTime: () {
@ -314,21 +328,23 @@ class VisitorPasswordDialog extends StatelessWidget {
visitorBloc.accessTypeSelected ==
'Offline Password') {
visitorBloc.add(SelectTimeEvent(
context: context, isEffective: true));
} else {
visitorBloc.add(SelectTimeVisitorPassword(
context: context,
isStart: true,
isRepeat: false));
isEffective: true));
} else {
visitorBloc.add(
SelectTimeVisitorPassword(
context: context,
isStart: true,
isRepeat: false));
}
},
firstString:
(visitorBloc.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected ==
'Offline Password')
? visitorBloc.effectiveTime
: visitorBloc.startTimeAccess,
firstString: (visitorBloc
.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected ==
'Offline Password')
? visitorBloc.effectiveTime
: visitorBloc.startTimeAccess,
secondString: (visitorBloc
.usageFrequencySelected ==
'Periodic' &&
@ -395,7 +411,8 @@ class VisitorPasswordDialog extends StatelessWidget {
child: CupertinoSwitch(
value: visitorBloc.repeat,
onChanged: (value) {
visitorBloc.add(ToggleRepeatEvent());
visitorBloc
.add(ToggleRepeatEvent());
},
applyTheme: true,
),
@ -426,7 +443,8 @@ class VisitorPasswordDialog extends StatelessWidget {
},
).then((listDevice) {
if (listDevice != null) {
visitorBloc.selectedDevices = listDevice;
visitorBloc.selectedDevices =
listDevice;
}
});
},
@ -438,7 +456,8 @@ class VisitorPasswordDialog extends StatelessWidget {
.bodySmall!
.copyWith(
fontWeight: FontWeight.w400,
color: ColorsManager.whiteColors,
color:
ColorsManager.whiteColors,
fontSize: 12),
),
),
@ -477,19 +496,23 @@ class VisitorPasswordDialog extends StatelessWidget {
onPressed: () {
if (visitorBloc.forgetFormKey.currentState!.validate()) {
if (visitorBloc.selectedDevices.isNotEmpty) {
if (visitorBloc.usageFrequencySelected == 'One-Time' &&
visitorBloc.accessTypeSelected == 'Offline Password') {
if (visitorBloc.usageFrequencySelected ==
'One-Time' &&
visitorBloc.accessTypeSelected ==
'Offline Password') {
setPasswordFunction(context, size, visitorBloc);
} else if (visitorBloc.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected == 'Offline Password') {
visitorBloc.accessTypeSelected ==
'Offline Password') {
if (visitorBloc.expirationTime != 'End Time' &&
visitorBloc.effectiveTime != 'Start Time') {
setPasswordFunction(context, size, visitorBloc);
} else {
visitorBloc.stateDialog(
context: context,
message: 'Please select Access Period to continue',
message:
'Please select Access Period to continue',
title: 'Access Period');
}
} else if (visitorBloc.endTimeAccess != 'End Time' &&
@ -500,7 +523,8 @@ class VisitorPasswordDialog extends StatelessWidget {
if (visitorBloc.expirationTime != 'End Time' &&
visitorBloc.effectiveTime != 'Start Time' &&
visitorBloc.selectedDays.isNotEmpty) {
setPasswordFunction(context, size, visitorBloc);
setPasswordFunction(
context, size, visitorBloc);
} else {
visitorBloc.stateDialog(
context: context,
@ -514,13 +538,15 @@ class VisitorPasswordDialog extends StatelessWidget {
} else {
visitorBloc.stateDialog(
context: context,
message: 'Please select Access Period to continue',
message:
'Please select Access Period to continue',
title: 'Access Period');
}
} else {
visitorBloc.stateDialog(
context: context,
message: 'Please select Access Period to continue',
message:
'Please select Access Period to continue',
title: 'Access Period');
}
} else {
@ -567,7 +593,8 @@ class VisitorPasswordDialog extends StatelessWidget {
content: SizedBox(
height: size.height * 0.25,
child: const Center(
child: CircularProgressIndicator(), // Display a loading spinner
child:
CircularProgressIndicator(), // Display a loading spinner
),
),
);
@ -640,7 +667,8 @@ class VisitorPasswordDialog extends StatelessWidget {
onPressed: () {
Navigator.pop(context);
if (visitorBloc.usageFrequencySelected == 'One-Time' &&
visitorBloc.accessTypeSelected == 'Online Password') {
visitorBloc.accessTypeSelected ==
'Online Password') {
visitorBloc.add(OnlineOneTimePasswordEvent(
context: context,
passwordName: visitorBloc.userNameController.text,
@ -648,7 +676,8 @@ class VisitorPasswordDialog extends StatelessWidget {
));
} else if (visitorBloc.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected == 'Online Password') {
visitorBloc.accessTypeSelected ==
'Online Password') {
visitorBloc.add(OnlineMultipleTimePasswordEvent(
passwordName: visitorBloc.userNameController.text,
email: visitorBloc.emailController.text,
@ -659,7 +688,8 @@ class VisitorPasswordDialog extends StatelessWidget {
));
} else if (visitorBloc.usageFrequencySelected ==
'One-Time' &&
visitorBloc.accessTypeSelected == 'Offline Password') {
visitorBloc.accessTypeSelected ==
'Offline Password') {
visitorBloc.add(OfflineOneTimePasswordEvent(
context: context,
passwordName: visitorBloc.userNameController.text,
@ -667,7 +697,8 @@ class VisitorPasswordDialog extends StatelessWidget {
));
} else if (visitorBloc.usageFrequencySelected ==
'Periodic' &&
visitorBloc.accessTypeSelected == 'Offline Password') {
visitorBloc.accessTypeSelected ==
'Offline Password') {
visitorBloc.add(OfflineMultipleTimePasswordEvent(
passwordName: visitorBloc.userNameController.text,
email: visitorBloc.emailController.text,

View File

@ -12,20 +12,16 @@ import 'package:syncrow_web/services/api/http_service.dart';
import 'package:syncrow_web/utils/constants/api_const.dart';
class DevicesManagementApi {
Future<List<AllDevicesModel>> fetchDevices(
String communityId, String spaceId, String projectId) async {
Future<List<AllDevicesModel>> fetchDevices(String projectId,
{List<String>? spacesId}) async {
try {
final response = await HTTPService().get(
path: communityId.isNotEmpty && spaceId.isNotEmpty
? ApiEndpoints.getSpaceDevices
.replaceAll('{spaceUuid}', spaceId)
.replaceAll('{communityUuid}', communityId)
.replaceAll('{projectId}', projectId)
: ApiEndpoints.getAllDevices.replaceAll('{projectId}', projectId),
path: ApiEndpoints.getSpaceDevices.replaceAll('{projectId}', projectId),
queryParameters: {if (spacesId != null) 'spaces': spacesId},
showServerMessage: true,
expectedResponseModel: (json) {
List<dynamic> jsonData = json['data'];
List<AllDevicesModel> devicesList = jsonData.map((jsonItem) {
final List<dynamic> jsonData = json['data'] as List<dynamic>;
final List<AllDevicesModel> devicesList = jsonData.map((jsonItem) {
return AllDevicesModel.fromJson(jsonItem);
}).toList();
return devicesList;
@ -416,5 +412,4 @@ class DevicesManagementApi {
);
return response;
}
}

View File

@ -34,8 +34,9 @@ class UserPermissionApi {
path: ApiEndpoints.roleTypes,
showServerMessage: true,
expectedResponseModel: (json) {
final List<RoleTypeModel> fetchedRoles =
(json['data'] as List).map((item) => RoleTypeModel.fromJson(item)).toList();
final List<RoleTypeModel> fetchedRoles = (json['data'] as List)
.map((item) => RoleTypeModel.fromJson(item))
.toList();
return fetchedRoles;
},
);
@ -47,7 +48,9 @@ class UserPermissionApi {
path: ApiEndpoints.permission.replaceAll("roleUuid", roleUuid),
showServerMessage: true,
expectedResponseModel: (json) {
return (json as List).map((data) => PermissionOption.fromJson(data)).toList();
return (json as List)
.map((data) => PermissionOption.fromJson(data))
.toList();
},
);
return response ?? [];
@ -57,7 +60,7 @@ class UserPermissionApi {
String? firstName,
String? lastName,
String? email,
String? jobTitle,
String? companyName,
String? phoneNumber,
String? roleUuid,
List<String>? spaceUuids,
@ -68,7 +71,7 @@ class UserPermissionApi {
"firstName": firstName,
"lastName": lastName,
"email": email,
"jobTitle": jobTitle != '' ? jobTitle : null,
"companyName": companyName != '' ? companyName : null,
"phoneNumber": phoneNumber != '' ? phoneNumber : null,
"roleUuid": roleUuid,
"projectUuid": projectUuid,
@ -140,7 +143,7 @@ class UserPermissionApi {
String? firstName,
String? userId,
String? lastName,
String? jobTitle,
String? companyName,
String? phoneNumber,
String? roleUuid,
List<String>? spaceUuids,
@ -150,8 +153,8 @@ class UserPermissionApi {
final body = <String, dynamic>{
"firstName": firstName,
"lastName": lastName,
"jobTitle": jobTitle != '' ? jobTitle : " ",
"phoneNumber": phoneNumber != '' ? phoneNumber : " ",
"companyName": companyName != '' ? companyName : ' ',
"phoneNumber": phoneNumber != '' ? phoneNumber : ' ',
"roleUuid": roleUuid,
"projectUuid": projectUuid,
"spaceUuids": spaceUuids,
@ -190,12 +193,17 @@ class UserPermissionApi {
}
}
Future<bool> changeUserStatusById(userUuid, status, String projectUuid) async {
Future<bool> changeUserStatusById(
userUuid, status, String projectUuid) async {
try {
Map<String, dynamic> bodya = {"disable": status, "projectUuid": projectUuid};
Map<String, dynamic> bodya = {
"disable": status,
"projectUuid": projectUuid
};
final response = await _httpService.put(
path: ApiEndpoints.changeUserStatus.replaceAll("{invitedUserUuid}", userUuid),
path: ApiEndpoints.changeUserStatus
.replaceAll("{invitedUserUuid}", userUuid),
body: bodya,
expectedResponseModel: (json) {
return json['success'];

View File

@ -84,5 +84,6 @@ abstract class ColorsManager {
static const Color minBlueDot = Color(0xFF023DFE);
static const Color grey25 = Color(0xFFF9F9F9);
static const Color grey50 = Color(0xFF718096);
static const Color red100 = Color(0xFFFE0202);
static const Color grey800 = Color(0xffF8F8F8);
}

View File

@ -17,8 +17,7 @@ abstract class ApiEndpoints {
////// Devices Management ////////////////
static const String getAllDevices = '/projects/{projectId}/devices';
static const String getSpaceDevices =
'/projects/{projectId}/communities/{communityUuid}/spaces/{spaceUuid}/devices';
static const String getSpaceDevices = '/projects/{projectId}/devices';
static const String getDeviceStatus = '/devices/{uuid}/functions/status';
static const String getBatchStatus = '/devices/batch';