mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-08-25 14:29:41 +00:00
[FE] Manage Bookable Spaces Tab (#355)
<!-- 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-1693](https://syncrow.atlassian.net/browse/SP-1693) [SP-1694](https://syncrow.atlassian.net/browse/SP-1694) ## Description all about unbookable spaces is Ready ## 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-1693]: https://syncrow.atlassian.net/browse/SP-1693?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ [SP-1694]: https://syncrow.atlassian.net/browse/SP-1694?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
This commit is contained in:
@ -18,7 +18,7 @@ class RemoteBookableSpacesService implements BookableSystemService {
|
||||
}) async {
|
||||
try {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.getBookableSpaces,
|
||||
path: ApiEndpoints.bookableSpaces,
|
||||
queryParameters: {
|
||||
'page': param.page,
|
||||
'size': param.size,
|
||||
|
@ -4,6 +4,7 @@ import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class SvgTextButton extends StatelessWidget {
|
||||
final String svgAsset;
|
||||
final EdgeInsets? padding;
|
||||
final String label;
|
||||
final VoidCallback onPressed;
|
||||
final Color backgroundColor;
|
||||
@ -12,16 +13,20 @@ class SvgTextButton extends StatelessWidget {
|
||||
final double borderRadius;
|
||||
final List<BoxShadow> boxShadow;
|
||||
final double svgSize;
|
||||
|
||||
final double? fontSize;
|
||||
final FontWeight? fontWeight;
|
||||
const SvgTextButton({
|
||||
super.key,
|
||||
required this.svgAsset,
|
||||
this.fontSize,
|
||||
this.fontWeight,
|
||||
required this.label,
|
||||
required this.onPressed,
|
||||
this.backgroundColor = ColorsManager.circleRolesBackground,
|
||||
this.svgColor = const Color(0xFF496EFF),
|
||||
this.labelColor = Colors.black,
|
||||
this.borderRadius = 10.0,
|
||||
this.padding,
|
||||
this.boxShadow = const [
|
||||
BoxShadow(
|
||||
color: ColorsManager.lightGrayColor,
|
||||
@ -40,7 +45,8 @@ class SvgTextButton extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(borderRadius),
|
||||
onTap: onPressed,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
padding: padding ??
|
||||
const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: backgroundColor,
|
||||
borderRadius: BorderRadius.circular(borderRadius),
|
||||
|
@ -0,0 +1,57 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart';
|
||||
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class BookingPage extends StatelessWidget {
|
||||
final PageController pageController;
|
||||
const BookingPage({
|
||||
super.key,
|
||||
required this.pageController,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
color: Colors.blueGrey[100],
|
||||
child: const Center(
|
||||
child: Text(
|
||||
'Side bar',
|
||||
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
)),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: SizedBox(
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
SvgTextButton(
|
||||
svgAsset: Assets.homeIcon,
|
||||
label: 'Manage Bookable Spaces',
|
||||
onPressed: () {
|
||||
pageController.jumpToPage(2);
|
||||
}),
|
||||
const SizedBox(width: 20),
|
||||
SvgTextButton(
|
||||
svgAsset: Assets.groupIcon,
|
||||
label: 'Manage Users',
|
||||
onPressed: () {})
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart';
|
||||
|
||||
class NonBookableSpacesDebouncerDecoratorService
|
||||
implements NonBookableSpacesService {
|
||||
final NonBookableSpacesService _delegate;
|
||||
Timer? _debounce;
|
||||
|
||||
NonBookableSpacesDebouncerDecoratorService(this._delegate);
|
||||
|
||||
@override
|
||||
Future<PaginatedDataModel<BookableSpacemodel>> load(
|
||||
NonBookableSpacesParams params) {
|
||||
final completer = Completer<PaginatedDataModel<BookableSpacemodel>>();
|
||||
|
||||
_debounce?.cancel();
|
||||
_debounce = Timer(const Duration(milliseconds: 500), () async {
|
||||
try {
|
||||
final result = await _delegate.load(params);
|
||||
completer.complete(result);
|
||||
} catch (e) {
|
||||
completer.completeError(e);
|
||||
}
|
||||
});
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart';
|
||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||
|
||||
class RemoteBookableSpacesService implements BookableSpacesService {
|
||||
final HTTPService _httpService;
|
||||
RemoteBookableSpacesService(this._httpService);
|
||||
static const _defaultErrorMessage = 'Failed to load Bookable Spaces';
|
||||
@override
|
||||
Future<PaginatedDataModel<BookableSpacemodel>> load(
|
||||
BookableSpacesParam param) async {
|
||||
try {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.bookableSpaces,
|
||||
queryParameters: {
|
||||
'configured': true,
|
||||
'page': param.currentPage,
|
||||
},
|
||||
expectedResponseModel: (json) {
|
||||
final result = json as Map<String, dynamic>;
|
||||
return PaginatedDataModel.fromJson(
|
||||
result,
|
||||
BookableSpacemodel.fromJsonList,
|
||||
);
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} on DioException catch (e) {
|
||||
final message = e.response?.data as Map<String, dynamic>?;
|
||||
final error = message?['error'] as Map<String, dynamic>?;
|
||||
final errorMessage = error?['error'] as String? ?? '';
|
||||
final formattedErrorMessage = [
|
||||
_defaultErrorMessage,
|
||||
errorMessage,
|
||||
].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
} catch (e) {
|
||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart';
|
||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||
|
||||
class RemoteNonBookableSpaces implements NonBookableSpacesService {
|
||||
final HTTPService _httpService;
|
||||
|
||||
RemoteNonBookableSpaces(this._httpService);
|
||||
|
||||
static const _defaultErrorMessage = 'Failed to load Spaces';
|
||||
|
||||
@override
|
||||
Future<PaginatedDataModel<BookableSpacemodel>> load(
|
||||
NonBookableSpacesParams params) async {
|
||||
try {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.bookableSpaces,
|
||||
queryParameters: {
|
||||
'configured': false,
|
||||
'page': params.currentPage,
|
||||
'search': params.searchedWords,
|
||||
},
|
||||
expectedResponseModel: (json) {
|
||||
final result = json as Map<String, dynamic>;
|
||||
return PaginatedDataModel.fromJson(
|
||||
result,
|
||||
BookableSpacemodel.fromJsonList,
|
||||
);
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} on DioException catch (e) {
|
||||
final message = e.response?.data as Map<String, dynamic>?;
|
||||
final error = message?['error'] as Map<String, dynamic>?;
|
||||
final errorMessage = error?['error'] as String? ?? '';
|
||||
final formattedErrorMessage = [
|
||||
_defaultErrorMessage,
|
||||
errorMessage,
|
||||
].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
} catch (e) {
|
||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_service.dart';
|
||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||
|
||||
class RemoteSendBookableSpaces implements SendBookableSpacesService {
|
||||
final HTTPService _httpService;
|
||||
RemoteSendBookableSpaces(this._httpService);
|
||||
static const _defaultErrorMessage = 'Failed to load Spaces';
|
||||
@override
|
||||
Future<void> sendBookableSpacesToApi(
|
||||
SendBookableSpacesToApiParams params) async {
|
||||
try {
|
||||
await _httpService.post(
|
||||
path: ApiEndpoints.bookableSpaces,
|
||||
body: params.toJson(),
|
||||
expectedResponseModel: (p0) {},
|
||||
);
|
||||
} on DioException catch (e) {
|
||||
final message = e.response?.data as Map<String, dynamic>?;
|
||||
final error = message?['error'] as Map<String, dynamic>?;
|
||||
final errorMessage = error?['error'] as String? ?? '';
|
||||
final formattedErrorMessage = [
|
||||
_defaultErrorMessage,
|
||||
errorMessage,
|
||||
].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
} catch (e) {
|
||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/update_bookable_space_service.dart';
|
||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||
|
||||
class RemoteUpdateBookableSpaceService implements UpdateBookableSpaceService {
|
||||
final HTTPService _httpService;
|
||||
RemoteUpdateBookableSpaceService(this._httpService);
|
||||
static const _defaultErrorMessage = 'Failed to load Bookable Spaces';
|
||||
@override
|
||||
Future<BookableSpaceConfig> update(
|
||||
UpdateBookableSpaceParam updateParam) async {
|
||||
try {
|
||||
final response = await _httpService.put(
|
||||
path: '${ApiEndpoints.bookableSpaces}/${updateParam.spaceUuid}',
|
||||
body: updateParam.toJson(),
|
||||
expectedResponseModel: (json) {
|
||||
return BookableSpaceConfig.fromJson(
|
||||
json['data'] as Map<String, dynamic>);
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} on DioException catch (e) {
|
||||
final message = e.response?.data as Map<String, dynamic>?;
|
||||
final error = message?['error'] as Map<String, dynamic>?;
|
||||
final errorMessage = error?['error'] as String? ?? '';
|
||||
final formattedErrorMessage = [
|
||||
_defaultErrorMessage,
|
||||
errorMessage,
|
||||
].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
} catch (e) {
|
||||
final formattedErrorMessage = [_defaultErrorMessage, '$e'].join(': ');
|
||||
throw APIException(formattedErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BookableSpaceConfig {
|
||||
final String configUuid;
|
||||
final List<String> bookableDays;
|
||||
final TimeOfDay? bookingStartTime;
|
||||
final TimeOfDay? bookingEndTime;
|
||||
final int cost;
|
||||
final bool availability;
|
||||
BookableSpaceConfig({
|
||||
required this.configUuid,
|
||||
required this.availability,
|
||||
required this.bookableDays,
|
||||
this.bookingEndTime,
|
||||
this.bookingStartTime,
|
||||
required this.cost,
|
||||
});
|
||||
factory BookableSpaceConfig.zero() => BookableSpaceConfig(
|
||||
configUuid: '',
|
||||
bookableDays: [],
|
||||
availability: false,
|
||||
cost: -1,
|
||||
);
|
||||
factory BookableSpaceConfig.fromJson(Map<String, dynamic> json) =>
|
||||
BookableSpaceConfig(
|
||||
configUuid: json['uuid'] as String,
|
||||
bookableDays: (json['daysAvailable'] as List).cast<String>(),
|
||||
availability: (json['active'] as bool?) ?? false,
|
||||
bookingStartTime: parseTimeOfDay(json['startTime'] as String),
|
||||
bookingEndTime: parseTimeOfDay(json['endTime'] as String),
|
||||
cost: json['points'] as int,
|
||||
);
|
||||
|
||||
static TimeOfDay parseTimeOfDay(String timeString) {
|
||||
final parts = timeString.split(':');
|
||||
final hour = int.parse(parts[0]);
|
||||
final minute = int.parse(parts[1]);
|
||||
return TimeOfDay(hour: hour, minute: minute);
|
||||
}
|
||||
|
||||
bool get isValid =>
|
||||
bookableDays.isNotEmpty &&
|
||||
cost >= 0 &&
|
||||
bookingStartTime != null &&
|
||||
bookingEndTime != null;
|
||||
|
||||
BookableSpaceConfig copyWith({
|
||||
List<String>? bookableDays,
|
||||
TimeOfDay? bookingStartTime,
|
||||
TimeOfDay? bookingEndTime,
|
||||
int? cost,
|
||||
bool? availability,
|
||||
}) {
|
||||
return BookableSpaceConfig(
|
||||
configUuid: configUuid,
|
||||
availability: availability ?? this.availability,
|
||||
bookableDays: bookableDays ?? this.bookableDays,
|
||||
cost: cost ?? this.cost,
|
||||
bookingEndTime: bookingEndTime ?? this.bookingEndTime,
|
||||
bookingStartTime: bookingStartTime ?? this.bookingStartTime,
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart';
|
||||
|
||||
class BookableSpacemodel {
|
||||
final String spaceUuid;
|
||||
final String spaceName;
|
||||
final BookableSpaceConfig? spaceConfig;
|
||||
final String spaceVirtualAddress;
|
||||
|
||||
BookableSpacemodel({
|
||||
required this.spaceUuid,
|
||||
required this.spaceName,
|
||||
this.spaceConfig,
|
||||
required this.spaceVirtualAddress,
|
||||
});
|
||||
factory BookableSpacemodel.zero() => BookableSpacemodel(
|
||||
spaceUuid: '',
|
||||
spaceName: '',
|
||||
spaceVirtualAddress: '',
|
||||
);
|
||||
factory BookableSpacemodel.fromJson(Map<String, dynamic> json) =>
|
||||
BookableSpacemodel(
|
||||
spaceUuid: json['uuid'] as String,
|
||||
spaceName: json['spaceName'] as String,
|
||||
spaceConfig: json['bookableConfig'] == null
|
||||
? BookableSpaceConfig.zero()
|
||||
: BookableSpaceConfig.fromJson(
|
||||
json['bookableConfig'] as Map<String, dynamic>),
|
||||
spaceVirtualAddress: json['virtualLocation'] as String,
|
||||
);
|
||||
|
||||
static List<BookableSpacemodel> fromJsonList(List<dynamic> jsonList) =>
|
||||
jsonList
|
||||
.map(
|
||||
(e) => BookableSpacemodel.fromJson(e as Map<String, dynamic>),
|
||||
)
|
||||
.toList();
|
||||
|
||||
bool get isValid =>
|
||||
spaceUuid.isNotEmpty &&
|
||||
spaceName.isNotEmpty &&
|
||||
spaceVirtualAddress.isNotEmpty &&
|
||||
spaceConfig != null &&
|
||||
spaceConfig!.isValid;
|
||||
|
||||
BookableSpacemodel copyWith({
|
||||
String? spaceUuid,
|
||||
String? spaceName,
|
||||
BookableSpaceConfig? spaceConfig,
|
||||
String? spaceVirtualAddress,
|
||||
}) {
|
||||
return BookableSpacemodel(
|
||||
spaceUuid: spaceUuid ?? this.spaceUuid,
|
||||
spaceName: spaceName ?? this.spaceName,
|
||||
spaceConfig: spaceConfig ?? this.spaceConfig,
|
||||
spaceVirtualAddress: spaceVirtualAddress ?? this.spaceVirtualAddress,
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
class BookableSpacesParam {
|
||||
final int currentPage;
|
||||
BookableSpacesParam({
|
||||
required this.currentPage,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'page': currentPage,
|
||||
};
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
class NonBookableSpacesParams {
|
||||
final int currentPage;
|
||||
final String? searchedWords;
|
||||
NonBookableSpacesParams({
|
||||
required this.currentPage,
|
||||
this.searchedWords,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'page': currentPage,
|
||||
};
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/utils/string_utils.dart';
|
||||
|
||||
class SendBookableSpacesToApiParams {
|
||||
final List<String> spaceUuids;
|
||||
final List<String> daysAvailable;
|
||||
final String startTime;
|
||||
final String endTime;
|
||||
final int points;
|
||||
SendBookableSpacesToApiParams({
|
||||
required this.spaceUuids,
|
||||
required this.daysAvailable,
|
||||
required this.startTime,
|
||||
required this.endTime,
|
||||
required this.points,
|
||||
});
|
||||
|
||||
static SendBookableSpacesToApiParams fromBookableSpacesModel(
|
||||
List<BookableSpacemodel> bookableSpaces) {
|
||||
return SendBookableSpacesToApiParams(
|
||||
spaceUuids: bookableSpaces.map((space) => space.spaceUuid).toList(),
|
||||
daysAvailable: bookableSpaces
|
||||
.expand((space) => space.spaceConfig!.bookableDays)
|
||||
.toSet()
|
||||
.toList(),
|
||||
startTime: formatTimeOfDayTo24HourString(
|
||||
bookableSpaces.first.spaceConfig!.bookingStartTime!),
|
||||
endTime: formatTimeOfDayTo24HourString(
|
||||
bookableSpaces.first.spaceConfig!.bookingEndTime!),
|
||||
points: bookableSpaces.first.spaceConfig!.cost,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'spaceUuids': spaceUuids,
|
||||
'daysAvailable': daysAvailable,
|
||||
'startTime': startTime,
|
||||
'endTime': endTime,
|
||||
'points': points
|
||||
};
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/utils/string_utils.dart';
|
||||
|
||||
class UpdateBookableSpaceParam {
|
||||
final String spaceUuid;
|
||||
final List<String>? bookableDays;
|
||||
final String? bookingStartTime;
|
||||
final String? bookingEndTime;
|
||||
final int? cost;
|
||||
final bool? availability;
|
||||
UpdateBookableSpaceParam({
|
||||
required this.spaceUuid,
|
||||
this.bookingStartTime,
|
||||
this.bookingEndTime,
|
||||
this.bookableDays,
|
||||
this.availability,
|
||||
this.cost,
|
||||
});
|
||||
factory UpdateBookableSpaceParam.fromBookableModel(
|
||||
BookableSpacemodel bookableSpace) {
|
||||
return UpdateBookableSpaceParam(
|
||||
spaceUuid: bookableSpace.spaceUuid,
|
||||
availability: bookableSpace.spaceConfig!.availability,
|
||||
bookableDays: bookableSpace.spaceConfig!.bookableDays,
|
||||
cost: bookableSpace.spaceConfig!.cost,
|
||||
bookingStartTime: formatTimeOfDayTo24HourString(
|
||||
bookableSpace.spaceConfig!.bookingStartTime!),
|
||||
bookingEndTime: formatTimeOfDayTo24HourString(
|
||||
bookableSpace.spaceConfig!.bookingEndTime!),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
if (bookableDays != null) 'daysAvailable': bookableDays,
|
||||
if (bookingStartTime != null) 'startTime': bookingStartTime,
|
||||
if (bookingEndTime != null) 'endTime': bookingEndTime,
|
||||
if (cost != null) 'points': cost,
|
||||
if (availability != null) 'active': availability,
|
||||
};
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart';
|
||||
|
||||
abstract class BookableSpacesService {
|
||||
Future<PaginatedDataModel<BookableSpacemodel>> load(
|
||||
BookableSpacesParam param);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart';
|
||||
|
||||
abstract class NonBookableSpacesService {
|
||||
Future<PaginatedDataModel<BookableSpacemodel>> load(
|
||||
NonBookableSpacesParams params);
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart';
|
||||
|
||||
abstract class SendBookableSpacesService{
|
||||
Future<void> sendBookableSpacesToApi(SendBookableSpacesToApiParams params);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart';
|
||||
|
||||
abstract class UpdateBookableSpaceService {
|
||||
Future<BookableSpaceConfig> update(UpdateBookableSpaceParam updateParam);
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/bookable_spaces_service.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart';
|
||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||
|
||||
part 'bookable_spaces_event.dart';
|
||||
part 'bookable_spaces_state.dart';
|
||||
|
||||
class BookableSpacesBloc
|
||||
extends Bloc<BookableSpacesEvent, BookableSpacesState> {
|
||||
final BookableSpacesService bookableSpacesService;
|
||||
BookableSpacesBloc(this.bookableSpacesService)
|
||||
: super(BookableSpacesInitial()) {
|
||||
on<LoadBookableSpacesEvent>(_onLoadBookableSpaces);
|
||||
on<InsertUpdatedSpaceEvent>(_onInsertUpdatedSpaceEven);
|
||||
}
|
||||
|
||||
Future<void> _onLoadBookableSpaces(
|
||||
LoadBookableSpacesEvent event, Emitter<BookableSpacesState> emit) async {
|
||||
emit(BookableSpacesLoading());
|
||||
try {
|
||||
final bookableSpaces = await bookableSpacesService.load(event.param);
|
||||
emit(BookableSpacesLoaded(bookableSpacesList: bookableSpaces));
|
||||
} on APIException catch (e) {
|
||||
emit(BookableSpacesError(error: e.message));
|
||||
} catch (e) {
|
||||
emit(
|
||||
BookableSpacesError(error: e.toString()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _onInsertUpdatedSpaceEven(
|
||||
InsertUpdatedSpaceEvent event, Emitter<BookableSpacesState> emit) {
|
||||
emit(InsertingUpdatedSpaceState());
|
||||
|
||||
if (event.bookableSpace.spaceConfig!.configUuid ==
|
||||
event.updatedBookableSpaceConfig.configUuid) {
|
||||
final index = event.bookableSpaces.data.indexWhere(
|
||||
(element) => element.spaceUuid == event.bookableSpace.spaceUuid,
|
||||
);
|
||||
|
||||
if (index != -1) {
|
||||
final original = event.bookableSpaces.data[index];
|
||||
|
||||
final updatedConfig = original.spaceConfig!.copyWith(
|
||||
availability: event.updatedBookableSpaceConfig.availability,
|
||||
bookableDays: event.updatedBookableSpaceConfig.bookableDays,
|
||||
bookingEndTime: event.updatedBookableSpaceConfig.bookingEndTime,
|
||||
bookingStartTime: event.updatedBookableSpaceConfig.bookingStartTime,
|
||||
cost: event.updatedBookableSpaceConfig.cost,
|
||||
);
|
||||
|
||||
final updatedSpace = original.copyWith(spaceConfig: updatedConfig);
|
||||
|
||||
event.bookableSpaces.data[index] = updatedSpace;
|
||||
}
|
||||
}
|
||||
|
||||
emit(BookableSpacesLoaded(bookableSpacesList: event.bookableSpaces));
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
part of 'bookable_spaces_bloc.dart';
|
||||
|
||||
sealed class BookableSpacesEvent extends Equatable {
|
||||
const BookableSpacesEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class LoadBookableSpacesEvent extends BookableSpacesEvent {
|
||||
final BookableSpacesParam param;
|
||||
const LoadBookableSpacesEvent(this.param);
|
||||
}
|
||||
|
||||
class InsertUpdatedSpaceEvent extends BookableSpacesEvent {
|
||||
final PaginatedDataModel<BookableSpacemodel> bookableSpaces;
|
||||
final BookableSpacemodel bookableSpace;
|
||||
final BookableSpaceConfig updatedBookableSpaceConfig;
|
||||
const InsertUpdatedSpaceEvent({
|
||||
required this.bookableSpaces,
|
||||
required this.bookableSpace,
|
||||
required this.updatedBookableSpaceConfig,
|
||||
});
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
part of 'bookable_spaces_bloc.dart';
|
||||
|
||||
sealed class BookableSpacesState extends Equatable {
|
||||
const BookableSpacesState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class BookableSpacesInitial extends BookableSpacesState {}
|
||||
|
||||
final class BookableSpacesLoading extends BookableSpacesState {}
|
||||
|
||||
final class BookableSpacesLoaded extends BookableSpacesState {
|
||||
final PaginatedDataModel<BookableSpacemodel> bookableSpacesList;
|
||||
const BookableSpacesLoaded({
|
||||
required this.bookableSpacesList,
|
||||
});
|
||||
}
|
||||
|
||||
final class BookableSpacesError extends BookableSpacesState {
|
||||
final String error;
|
||||
const BookableSpacesError({
|
||||
required this.error,
|
||||
});
|
||||
}
|
||||
|
||||
class InsertingUpdatedSpaceState extends BookableSpacesState {}
|
@ -0,0 +1,65 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart';
|
||||
|
||||
part 'non_bookaable_spaces_event.dart';
|
||||
part 'non_bookaable_spaces_state.dart';
|
||||
|
||||
class NonBookableSpacesBloc
|
||||
extends Bloc<NonBookableSpacesEvent, NonBookableSpacesState> {
|
||||
NonBookableSpacesService nonBookableSpacesService;
|
||||
|
||||
NonBookableSpacesBloc(this.nonBookableSpacesService)
|
||||
: super(NonBookableSpacesInitial()) {
|
||||
on<CallInitStateEvent>(_onCallInitStateEvent);
|
||||
on<LoadUnBookableSpacesEvent>(_onLoadUnBookableSpacesEvent);
|
||||
}
|
||||
|
||||
void _onCallInitStateEvent(
|
||||
CallInitStateEvent event, Emitter<NonBookableSpacesState> emit) {
|
||||
emit(NonBookableSpacesInitial());
|
||||
}
|
||||
|
||||
Future<void> _onLoadUnBookableSpacesEvent(LoadUnBookableSpacesEvent event,
|
||||
Emitter<NonBookableSpacesState> emit) async {
|
||||
if (state is NonBookableSpacesLoaded) {
|
||||
final currState = state as NonBookableSpacesLoaded;
|
||||
try {
|
||||
emit(NonBookableSpacesLoading(
|
||||
lastNonBookableSpaces: currState.nonBookableSpaces));
|
||||
|
||||
final nonBookableSpacesList = await nonBookableSpacesService.load(
|
||||
event.nonBookableSpacesParams,
|
||||
);
|
||||
nonBookableSpacesList.data.addAll(currState.nonBookableSpaces.data);
|
||||
|
||||
emit(
|
||||
NonBookableSpacesLoaded(
|
||||
nonBookableSpaces: nonBookableSpacesList,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
NonBookableSpacesError(e.toString()),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
emit(const NonBookableSpacesLoading());
|
||||
final nonBookableSpacesList = await nonBookableSpacesService.load(
|
||||
event.nonBookableSpacesParams,
|
||||
);
|
||||
emit(
|
||||
NonBookableSpacesLoaded(nonBookableSpaces: nonBookableSpacesList),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
NonBookableSpacesError(e.toString()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
part of 'non_bookaable_spaces_bloc.dart';
|
||||
|
||||
sealed class NonBookableSpacesEvent extends Equatable {
|
||||
const NonBookableSpacesEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class CallInitStateEvent extends NonBookableSpacesEvent {}
|
||||
|
||||
class LoadUnBookableSpacesEvent extends NonBookableSpacesEvent {
|
||||
final NonBookableSpacesParams nonBookableSpacesParams;
|
||||
const LoadUnBookableSpacesEvent({
|
||||
required this.nonBookableSpacesParams,
|
||||
});
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
part of 'non_bookaable_spaces_bloc.dart';
|
||||
|
||||
sealed class NonBookableSpacesState extends Equatable {
|
||||
const NonBookableSpacesState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class NonBookableSpacesInitial extends NonBookableSpacesState {}
|
||||
|
||||
class NonBookableSpacesLoading extends NonBookableSpacesState {
|
||||
final PaginatedDataModel<BookableSpacemodel>? lastNonBookableSpaces;
|
||||
const NonBookableSpacesLoading({
|
||||
this.lastNonBookableSpaces,
|
||||
});
|
||||
}
|
||||
|
||||
class NonBookableSpacesLoaded extends NonBookableSpacesState {
|
||||
final PaginatedDataModel<BookableSpacemodel> nonBookableSpaces;
|
||||
final List<BookableSpacemodel> selectedBookableSpaces;
|
||||
const NonBookableSpacesLoaded({
|
||||
required this.nonBookableSpaces,
|
||||
this.selectedBookableSpaces = const [],
|
||||
});
|
||||
}
|
||||
|
||||
class NonBookableSpacesError extends NonBookableSpacesState {
|
||||
final String error;
|
||||
const NonBookableSpacesError(this.error);
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/send_bookable_spaces_to_api_params.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/send_bookable_spaces_service.dart';
|
||||
|
||||
part 'send_bookable_spaces_event.dart';
|
||||
part 'send_bookable_spaces_state.dart';
|
||||
|
||||
class SendBookableSpacesBloc
|
||||
extends Bloc<SendBookableSpacesEvent, SendBookableSpacesState> {
|
||||
SendBookableSpacesService sendBookableSpacesService;
|
||||
SendBookableSpacesBloc(
|
||||
this.sendBookableSpacesService,
|
||||
) : super(SendBookableSpacesInitial()) {
|
||||
on<SendBookableSpacesToApi>(_onSendBookableSpacesToApi);
|
||||
}
|
||||
Future<void> _onSendBookableSpacesToApi(SendBookableSpacesToApi event,
|
||||
Emitter<SendBookableSpacesState> emit) async {
|
||||
emit(SendBookableSpacesLoading());
|
||||
try {
|
||||
await sendBookableSpacesService.sendBookableSpacesToApi(
|
||||
SendBookableSpacesToApiParams.fromBookableSpacesModel(
|
||||
event.selectedBookableSpaces),
|
||||
);
|
||||
emit(SendBookableSpacesSuccess());
|
||||
} catch (e) {
|
||||
emit(
|
||||
SendBookableSpacesError(e.toString()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
part of 'send_bookable_spaces_bloc.dart';
|
||||
|
||||
sealed class SendBookableSpacesEvent extends Equatable {
|
||||
const SendBookableSpacesEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class SendBookableSpacesToApi extends SendBookableSpacesEvent {
|
||||
final List<BookableSpacemodel> selectedBookableSpaces;
|
||||
const SendBookableSpacesToApi({required this.selectedBookableSpaces});
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
part of 'send_bookable_spaces_bloc.dart';
|
||||
|
||||
sealed class SendBookableSpacesState extends Equatable {
|
||||
const SendBookableSpacesState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class SendBookableSpacesInitial extends SendBookableSpacesState {}
|
||||
|
||||
class SendBookableSpacesLoading extends SendBookableSpacesState {}
|
||||
|
||||
class SendBookableSpacesSuccess extends SendBookableSpacesState {}
|
||||
|
||||
class SendBookableSpacesError extends SendBookableSpacesState {
|
||||
final String error;
|
||||
const SendBookableSpacesError(this.error);
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/non_bookable_spaces_service.dart';
|
||||
|
||||
part 'setup_bookable_spaces_event.dart';
|
||||
part 'setup_bookable_spaces_state.dart';
|
||||
|
||||
class SetupBookableSpacesBloc
|
||||
extends Bloc<SetupBookableSpacesEvent, SetupBookableSpacesState> {
|
||||
NonBookableSpacesService nonBookableSpacesService;
|
||||
|
||||
SetupBookableSpacesBloc(this.nonBookableSpacesService)
|
||||
: super(const SetupBookableSpacesInitial(bookableSpaces: [])) {
|
||||
on<AddToBookableSpaceEvent>(_onAddToBookableSpaceEvent);
|
||||
on<RemoveFromBookableSpaceEvent>(_onRemoveFromBookableSpaceEvent);
|
||||
on<AddBookableDaysEvent>(_onAddBookableDays);
|
||||
on<ChangeStartTimeEvent>(_onChangeStartTimeEvent);
|
||||
on<ChangeEndTimeEvent>(_onChangeEndTimeEvent);
|
||||
on<ChangeCostEvent>(_onChangeCostEvent);
|
||||
|
||||
on<CheckConfigurValidityEvent>(_onCheckConfigurValidityEvent);
|
||||
on<EditModeSelected>(_onEditModeSelected);
|
||||
}
|
||||
List<BookableSpacemodel> get currentBookableSpaces {
|
||||
return switch (state) {
|
||||
AddNonBookableSpaceIntoBookableState(:final bookableSpaces) =>
|
||||
bookableSpaces,
|
||||
RemoveBookableSpaceIntoNonBookableState(:final bookableSpaces) =>
|
||||
bookableSpaces,
|
||||
SetupBookableSpacesInitial(:final bookableSpaces) => bookableSpaces,
|
||||
InProgressState(:final bookableSpaces) => bookableSpaces,
|
||||
ValidSaveButtonState(:final bookableSpaces) => bookableSpaces,
|
||||
UnValidSaveButtonState(:final bookableSpaces) => bookableSpaces,
|
||||
};
|
||||
}
|
||||
|
||||
void _onAddToBookableSpaceEvent(
|
||||
AddToBookableSpaceEvent event,
|
||||
Emitter<SetupBookableSpacesState> emit,
|
||||
) {
|
||||
emit(InProgressState(bookableSpaces: state.bookableSpaces));
|
||||
final updatedSpaces = List<BookableSpacemodel>.from(state.bookableSpaces);
|
||||
|
||||
updatedSpaces.add(event.nonBookableSpace);
|
||||
|
||||
emit(AddNonBookableSpaceIntoBookableState(bookableSpaces: updatedSpaces));
|
||||
}
|
||||
|
||||
void _onRemoveFromBookableSpaceEvent(RemoveFromBookableSpaceEvent event,
|
||||
Emitter<SetupBookableSpacesState> emit) {
|
||||
emit(InProgressState(bookableSpaces: state.bookableSpaces));
|
||||
state.bookableSpaces.remove(event.bookableSpace);
|
||||
emit(RemoveBookableSpaceIntoNonBookableState(
|
||||
bookableSpaces: state.bookableSpaces));
|
||||
}
|
||||
|
||||
void _onAddBookableDays(
|
||||
AddBookableDaysEvent event, Emitter<SetupBookableSpacesState> emit) {
|
||||
final updatedSpaces = state.bookableSpaces.map((space) {
|
||||
final updatedConfig = space.spaceConfig?.copyWith(
|
||||
bookableDays: event.bookableDays,
|
||||
);
|
||||
|
||||
return space.copyWith(spaceConfig: updatedConfig);
|
||||
}).toList();
|
||||
|
||||
emit(SetupBookableSpacesInitial(bookableSpaces: updatedSpaces));
|
||||
}
|
||||
|
||||
void _onChangeStartTimeEvent(
|
||||
ChangeStartTimeEvent event, Emitter<SetupBookableSpacesState> emit) {
|
||||
final updatedSpaces = state.bookableSpaces.map((space) {
|
||||
final updatedConfig = space.spaceConfig?.copyWith(
|
||||
bookingStartTime: event.startTime,
|
||||
);
|
||||
|
||||
return space.copyWith(spaceConfig: updatedConfig);
|
||||
}).toList();
|
||||
|
||||
emit(SetupBookableSpacesInitial(bookableSpaces: updatedSpaces));
|
||||
}
|
||||
|
||||
void _onChangeEndTimeEvent(
|
||||
ChangeEndTimeEvent event, Emitter<SetupBookableSpacesState> emit) {
|
||||
final updatedSpaces = state.bookableSpaces.map((space) {
|
||||
final updatedConfig = space.spaceConfig?.copyWith(
|
||||
bookingEndTime: event.endTime,
|
||||
);
|
||||
|
||||
return space.copyWith(spaceConfig: updatedConfig);
|
||||
}).toList();
|
||||
|
||||
emit(SetupBookableSpacesInitial(bookableSpaces: updatedSpaces));
|
||||
}
|
||||
|
||||
void _onChangeCostEvent(
|
||||
ChangeCostEvent event, Emitter<SetupBookableSpacesState> emit) {
|
||||
final updatedSpaces = state.bookableSpaces.map((space) {
|
||||
final updatedConfig = space.spaceConfig?.copyWith(
|
||||
cost: event.cost,
|
||||
);
|
||||
|
||||
return space.copyWith(spaceConfig: updatedConfig);
|
||||
}).toList();
|
||||
|
||||
emit(SetupBookableSpacesInitial(bookableSpaces: updatedSpaces));
|
||||
}
|
||||
|
||||
void _onCheckConfigurValidityEvent(CheckConfigurValidityEvent event,
|
||||
Emitter<SetupBookableSpacesState> emit) {
|
||||
if (state.bookableSpaces.first.spaceConfig!.isValid) {
|
||||
emit(ValidSaveButtonState(
|
||||
bookableSpaces: state.bookableSpaces,
|
||||
));
|
||||
} else {
|
||||
emit(UnValidSaveButtonState(
|
||||
bookableSpaces: state.bookableSpaces,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
void _onEditModeSelected(
|
||||
EditModeSelected event,
|
||||
Emitter<SetupBookableSpacesState> emit,
|
||||
) {
|
||||
final updatedList = [event.editingBookableSpace];
|
||||
|
||||
emit(SetupBookableSpacesInitial(bookableSpaces: updatedList));
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
part of 'setup_bookable_spaces_bloc.dart';
|
||||
|
||||
sealed class SetupBookableSpacesEvent extends Equatable {
|
||||
const SetupBookableSpacesEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class AddToBookableSpaceEvent extends SetupBookableSpacesEvent {
|
||||
final BookableSpacemodel nonBookableSpace;
|
||||
const AddToBookableSpaceEvent({
|
||||
required this.nonBookableSpace,
|
||||
});
|
||||
}
|
||||
|
||||
class RemoveFromBookableSpaceEvent extends SetupBookableSpacesEvent {
|
||||
final BookableSpacemodel bookableSpace;
|
||||
const RemoveFromBookableSpaceEvent({
|
||||
required this.bookableSpace,
|
||||
});
|
||||
}
|
||||
|
||||
class AddBookableDaysEvent extends SetupBookableSpacesEvent {
|
||||
final List<String> bookableDays;
|
||||
const AddBookableDaysEvent({
|
||||
required this.bookableDays,
|
||||
});
|
||||
}
|
||||
|
||||
class ChangeCostEvent extends SetupBookableSpacesEvent {
|
||||
final int cost;
|
||||
const ChangeCostEvent({
|
||||
required this.cost,
|
||||
});
|
||||
}
|
||||
|
||||
class ChangeStartTimeEvent extends SetupBookableSpacesEvent {
|
||||
final TimeOfDay startTime;
|
||||
const ChangeStartTimeEvent({
|
||||
required this.startTime,
|
||||
});
|
||||
}
|
||||
|
||||
class ChangeEndTimeEvent extends SetupBookableSpacesEvent {
|
||||
final TimeOfDay endTime;
|
||||
const ChangeEndTimeEvent({
|
||||
required this.endTime,
|
||||
});
|
||||
}
|
||||
|
||||
class CheckConfigurValidityEvent extends SetupBookableSpacesEvent {}
|
||||
|
||||
class EditModeSelected extends SetupBookableSpacesEvent {
|
||||
final BookableSpacemodel editingBookableSpace;
|
||||
const EditModeSelected({
|
||||
required this.editingBookableSpace,
|
||||
});
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
part of 'setup_bookable_spaces_bloc.dart';
|
||||
|
||||
sealed class SetupBookableSpacesState extends Equatable {
|
||||
final List<BookableSpacemodel> bookableSpaces;
|
||||
const SetupBookableSpacesState({required this.bookableSpaces});
|
||||
TimeOfDay? get startTime =>
|
||||
bookableSpaces.first.spaceConfig!.bookingStartTime;
|
||||
TimeOfDay? get endTime => bookableSpaces.first.spaceConfig!.bookingEndTime;
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class SetupBookableSpacesInitial extends SetupBookableSpacesState {
|
||||
const SetupBookableSpacesInitial({required super.bookableSpaces});
|
||||
}
|
||||
|
||||
class AddNonBookableSpaceIntoBookableState extends SetupBookableSpacesState {
|
||||
const AddNonBookableSpaceIntoBookableState({required super.bookableSpaces});
|
||||
}
|
||||
|
||||
class InProgressState extends SetupBookableSpacesState {
|
||||
const InProgressState({required super.bookableSpaces});
|
||||
}
|
||||
|
||||
class RemoveBookableSpaceIntoNonBookableState extends SetupBookableSpacesState {
|
||||
const RemoveBookableSpaceIntoNonBookableState({
|
||||
required super.bookableSpaces,
|
||||
});
|
||||
}
|
||||
|
||||
class ValidSaveButtonState extends SetupBookableSpacesState {
|
||||
const ValidSaveButtonState({required super.bookableSpaces});
|
||||
}
|
||||
|
||||
class UnValidSaveButtonState extends SetupBookableSpacesState {
|
||||
const UnValidSaveButtonState({required super.bookableSpaces});
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
part 'steps_state.dart';
|
||||
|
||||
class StepsCubit extends Cubit<StepsState> {
|
||||
StepsCubit() : super(StepOneState());
|
||||
|
||||
void initDialogValue() {
|
||||
emit(StepOneState());
|
||||
}
|
||||
|
||||
void editValueInit() {
|
||||
emit(StepTwoState());
|
||||
}
|
||||
|
||||
void goToNextStep() {
|
||||
if (state is StepOneState) {
|
||||
emit(StepTwoState());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
part of 'steps_cubit.dart';
|
||||
|
||||
sealed class StepsState extends Equatable {
|
||||
const StepsState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class StepOneState extends StepsState {}
|
||||
|
||||
final class StepTwoState extends StepsState {}
|
@ -0,0 +1,16 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
part 'toggle_points_switch_state.dart';
|
||||
|
||||
class TogglePointsSwitchCubit extends Cubit<TogglePointsSwitchState> {
|
||||
TogglePointsSwitchCubit() : super(UnActivatePointsSwitch());
|
||||
|
||||
void activateSwitch() {
|
||||
emit(ActivatePointsSwitch());
|
||||
}
|
||||
|
||||
void unActivateSwitch() {
|
||||
emit(UnActivatePointsSwitch());
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
part of 'toggle_points_switch_cubit.dart';
|
||||
|
||||
sealed class TogglePointsSwitchState extends Equatable {
|
||||
const TogglePointsSwitchState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
|
||||
class ActivatePointsSwitch extends TogglePointsSwitchState {}
|
||||
|
||||
class UnActivatePointsSwitch extends TogglePointsSwitchState {}
|
@ -0,0 +1,36 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_config.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/service/update_bookable_space_service.dart';
|
||||
import 'package:syncrow_web/services/api/api_exception.dart';
|
||||
|
||||
part 'update_bookable_spaces_event.dart';
|
||||
part 'update_bookable_spaces_state.dart';
|
||||
|
||||
class UpdateBookableSpacesBloc
|
||||
extends Bloc<UpdateBookableSpaceEvent, UpdateBookableSpacesState> {
|
||||
final UpdateBookableSpaceService updateBookableSpaceService;
|
||||
UpdateBookableSpacesBloc(this.updateBookableSpaceService)
|
||||
: super(UpdateBookableSpacesInitial()) {
|
||||
on<UpdateBookableSpace>(_onUpdateBookableSpace);
|
||||
}
|
||||
|
||||
Future<void> _onUpdateBookableSpace(UpdateBookableSpace event,
|
||||
Emitter<UpdateBookableSpacesState> emit) async {
|
||||
emit(UpdateBookableSpaceLoading(event.updatedParam.spaceUuid));
|
||||
try {
|
||||
final updatedSpace =
|
||||
await updateBookableSpaceService.update(event.updatedParam);
|
||||
|
||||
emit(UpdateBookableSpaceSuccess(bookableSpaceConfig: updatedSpace));
|
||||
event.onSuccess?.call();
|
||||
} on APIException catch (e) {
|
||||
emit(UpdateBookableSpaceFailure(error: e.message));
|
||||
} catch (e) {
|
||||
emit(
|
||||
UpdateBookableSpaceFailure(error: e.toString()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
part of 'update_bookable_spaces_bloc.dart';
|
||||
|
||||
sealed class UpdateBookableSpaceEvent extends Equatable {
|
||||
const UpdateBookableSpaceEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class UpdateBookableSpace extends UpdateBookableSpaceEvent {
|
||||
final void Function()? onSuccess;
|
||||
final UpdateBookableSpaceParam updatedParam;
|
||||
const UpdateBookableSpace({
|
||||
required this.updatedParam,
|
||||
this.onSuccess,
|
||||
});
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
part of 'update_bookable_spaces_bloc.dart';
|
||||
|
||||
sealed class UpdateBookableSpacesState extends Equatable {
|
||||
const UpdateBookableSpacesState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class UpdateBookableSpacesInitial extends UpdateBookableSpacesState {}
|
||||
|
||||
final class UpdateBookableSpaceLoading extends UpdateBookableSpacesState {
|
||||
final String updatingSpaceUuid;
|
||||
|
||||
const UpdateBookableSpaceLoading(this.updatingSpaceUuid);
|
||||
}
|
||||
|
||||
final class UpdateBookableSpaceSuccess extends UpdateBookableSpacesState {
|
||||
final BookableSpaceConfig bookableSpaceConfig;
|
||||
const UpdateBookableSpaceSuccess({
|
||||
required this.bookableSpaceConfig,
|
||||
});
|
||||
}
|
||||
|
||||
final class UpdateBookableSpaceFailure extends UpdateBookableSpacesState {
|
||||
final String error;
|
||||
const UpdateBookableSpaceFailure({
|
||||
required this.error,
|
||||
});
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_bookable_spaces_service.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_update_bookable_space_service.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/bottom_pagination_part_widget.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/table_part_widget.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/main_manage_bookable_widgets/top_part_widget.dart';
|
||||
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
|
||||
class ManageBookableSpacesPage extends StatefulWidget {
|
||||
final PageController pageController;
|
||||
const ManageBookableSpacesPage({
|
||||
super.key,
|
||||
required this.pageController,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ManageBookableSpacesPage> createState() =>
|
||||
_ManageBookableSpacesPageState();
|
||||
}
|
||||
|
||||
class _ManageBookableSpacesPageState extends State<ManageBookableSpacesPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
create: (context) => BookableSpacesBloc(
|
||||
RemoteBookableSpacesService(HTTPService()),
|
||||
)..add(
|
||||
LoadBookableSpacesEvent(
|
||||
BookableSpacesParam(currentPage: 1),
|
||||
),
|
||||
),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) => UpdateBookableSpacesBloc(
|
||||
RemoteUpdateBookableSpaceService(HTTPService()),
|
||||
),
|
||||
)
|
||||
],
|
||||
child: ManageBookableSpacesWidget(
|
||||
pageController: widget.pageController,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ManageBookableSpacesWidget extends StatelessWidget {
|
||||
final PageController pageController;
|
||||
|
||||
const ManageBookableSpacesWidget({
|
||||
super.key,
|
||||
required this.pageController,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
horizontal: 35,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 10,
|
||||
child: RowOfButtonsTitleWidget(pageController: pageController)),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
const Expanded(
|
||||
flex: 85,
|
||||
child: TableOfBookableSpacesWidget(),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
const Expanded(
|
||||
flex: 5,
|
||||
child: PaginationButtonsWidget(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/non_bookable_spaces_decorator.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_non_bookable_spaces.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_send_bookable_spaces.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/details_steps_widget.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/next_first_step_button.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/save_second_step_button.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/stepper_part_widget.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class SetupBookableSpacesDialog extends StatefulWidget {
|
||||
final BookableSpacemodel? editingBookableSpace;
|
||||
SetupBookableSpacesDialog({
|
||||
super.key,
|
||||
this.editingBookableSpace,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SetupBookableSpacesDialog> createState() =>
|
||||
_SetupBookableSpacesDialogState();
|
||||
}
|
||||
|
||||
class _SetupBookableSpacesDialogState extends State<SetupBookableSpacesDialog> {
|
||||
final TextEditingController pointsController = TextEditingController();
|
||||
@override
|
||||
void dispose() {
|
||||
pointsController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<StepsCubit>(
|
||||
create: widget.editingBookableSpace == null
|
||||
? (context) => StepsCubit()..initDialogValue()
|
||||
: (context) => StepsCubit()..editValueInit(),
|
||||
),
|
||||
BlocProvider<NonBookableSpacesBloc>(
|
||||
create: (context) => NonBookableSpacesBloc(
|
||||
NonBookableSpacesDebouncerDecoratorService(
|
||||
RemoteNonBookableSpaces(HTTPService()),
|
||||
),
|
||||
)..add(
|
||||
LoadUnBookableSpacesEvent(
|
||||
nonBookableSpacesParams:
|
||||
NonBookableSpacesParams(currentPage: 1),
|
||||
),
|
||||
),
|
||||
),
|
||||
BlocProvider<SetupBookableSpacesBloc>(
|
||||
create: widget.editingBookableSpace == null
|
||||
? (context) => SetupBookableSpacesBloc(
|
||||
RemoteNonBookableSpaces(HTTPService()))
|
||||
: (context) => SetupBookableSpacesBloc(
|
||||
RemoteNonBookableSpaces(HTTPService()))
|
||||
..add(
|
||||
EditModeSelected(
|
||||
editingBookableSpace: widget.editingBookableSpace!),
|
||||
),
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) => SendBookableSpacesBloc(
|
||||
RemoteSendBookableSpaces(HTTPService()),
|
||||
),
|
||||
)
|
||||
],
|
||||
child: AlertDialog(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Center(
|
||||
child: Text(
|
||||
'Set Up a Bookable Spaces',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorsManager.dialogBlueTitle,
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
),
|
||||
content: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Divider(),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Expanded(
|
||||
flex: 3,
|
||||
child: StepperPartWidget(),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 588,
|
||||
child: VerticalDivider(
|
||||
thickness: 0.5,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 7,
|
||||
child: DetailsStepsWidget(
|
||||
pointsController: pointsController,
|
||||
editingBookableSpace: widget.editingBookableSpace,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Builder(builder: (context) {
|
||||
final stepsState = context.watch<StepsCubit>().state;
|
||||
final bookableSpaces =
|
||||
context.watch<SetupBookableSpacesBloc>().state.bookableSpaces;
|
||||
return stepsState is StepOneState
|
||||
? const NextFirstStepButton()
|
||||
: SaveSecondStepButton(
|
||||
pointsController: pointsController,
|
||||
isEditingMode: widget.editingBookableSpace != null,
|
||||
bookableSpaces: bookableSpaces,
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class BookableSpaceSwitchActivationWidget extends StatelessWidget {
|
||||
final PaginatedDataModel<BookableSpacemodel> bookableSpaces;
|
||||
final BookableSpacemodel space;
|
||||
const BookableSpaceSwitchActivationWidget({
|
||||
super.key,
|
||||
required this.bookableSpaces,
|
||||
required this.space,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Transform.scale(
|
||||
scale: 0.7,
|
||||
child:
|
||||
BlocConsumer<UpdateBookableSpacesBloc, UpdateBookableSpacesState>(
|
||||
listener: (context, updateState) {
|
||||
if (updateState is UpdateBookableSpaceSuccess) {
|
||||
context.read<BookableSpacesBloc>().add(
|
||||
InsertUpdatedSpaceEvent(
|
||||
bookableSpaces: bookableSpaces,
|
||||
bookableSpace: space,
|
||||
updatedBookableSpaceConfig:
|
||||
updateState.bookableSpaceConfig,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
builder: (context, updateState) {
|
||||
final isLoading = updateState is UpdateBookableSpaceLoading &&
|
||||
updateState.updatingSpaceUuid == space.spaceUuid;
|
||||
if (isLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
return Switch(
|
||||
trackOutlineColor: WidgetStateProperty.resolveWith<Color>(
|
||||
(Set<WidgetState> states) {
|
||||
return ColorsManager.whiteColors;
|
||||
}),
|
||||
value: space.spaceConfig!.availability,
|
||||
activeTrackColor: ColorsManager.dialogBlueTitle,
|
||||
inactiveTrackColor: ColorsManager.grayBorder,
|
||||
thumbColor: WidgetStateProperty.resolveWith<Color>(
|
||||
(Set<WidgetState> states) {
|
||||
return ColorsManager.whiteColors;
|
||||
}),
|
||||
onChanged: (value) {
|
||||
context.read<UpdateBookableSpacesBloc>().add(
|
||||
UpdateBookableSpace(
|
||||
updatedParam: UpdateBookableSpaceParam(
|
||||
spaceUuid: space.spaceUuid,
|
||||
availability: value,
|
||||
)),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/time_picker_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/string_utils.dart';
|
||||
|
||||
class BookingPeriodWidget extends StatelessWidget {
|
||||
final BookableSpacemodel? editingBookableSpace;
|
||||
const BookingPeriodWidget({
|
||||
super.key,
|
||||
this.editingBookableSpace,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final state = context.watch<SetupBookableSpacesBloc>().state;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'* ',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(color: Colors.red),
|
||||
),
|
||||
const Text('Booking Period'),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Container(
|
||||
width: 230,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
color: ColorsManager.circleRolesBackground,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
offset: Offset.zero,
|
||||
blurRadius: 4,
|
||||
spreadRadius: 0,
|
||||
color: ColorsManager.timePickerColor.withValues(alpha: 0.15),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
TimePickerWidget(
|
||||
title: editingBookableSpace?.spaceConfig?.bookingStartTime
|
||||
?.format(context) ??
|
||||
'Start Time',
|
||||
onTimePicked: (pickedStartTime) {
|
||||
if (pickedStartTime == null) return;
|
||||
|
||||
if (state.endTime != null &&
|
||||
isEndTimeAfterStartTime(
|
||||
pickedStartTime, state.endTime!)) {
|
||||
_showInvalidSnackBar(
|
||||
context, "You can't choose Start Time after End Time");
|
||||
return;
|
||||
}
|
||||
|
||||
context.read<SetupBookableSpacesBloc>().add(
|
||||
ChangeStartTimeEvent(startTime: pickedStartTime),
|
||||
);
|
||||
context.read<SetupBookableSpacesBloc>().add(
|
||||
CheckConfigurValidityEvent(),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
const Icon(
|
||||
Icons.arrow_right_alt,
|
||||
color: ColorsManager.grayColor,
|
||||
size: 13,
|
||||
),
|
||||
TimePickerWidget(
|
||||
title: editingBookableSpace?.spaceConfig?.bookingEndTime
|
||||
?.format(context) ??
|
||||
'End Time',
|
||||
onTimePicked: (pickedEndTime) {
|
||||
if (pickedEndTime == null) return;
|
||||
|
||||
if (state.startTime != null &&
|
||||
isEndTimeAfterStartTime(
|
||||
state.startTime!, pickedEndTime)) {
|
||||
_showInvalidSnackBar(
|
||||
context, "You can't choose End Time before Start Time");
|
||||
return;
|
||||
}
|
||||
|
||||
context.read<SetupBookableSpacesBloc>().add(
|
||||
ChangeEndTimeEvent(endTime: pickedEndTime),
|
||||
);
|
||||
context.read<SetupBookableSpacesBloc>().add(
|
||||
CheckConfigurValidityEvent(),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 15),
|
||||
Container(
|
||||
width: 30,
|
||||
height: 32,
|
||||
alignment: Alignment.center,
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(10),
|
||||
bottomLeft: Radius.circular(10),
|
||||
),
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.clockIcon,
|
||||
height: 18,
|
||||
color: ColorsManager.blackColor.withValues(alpha: 0.4),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _showInvalidSnackBar(BuildContext context, String message) {
|
||||
ScaffoldMessenger.of(context).clearSnackBars();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
duration: const Duration(seconds: 2),
|
||||
backgroundColor: ColorsManager.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class ButtonsDividerBottomDialogWidget extends StatelessWidget {
|
||||
final String title;
|
||||
final void Function()? onNextPressed;
|
||||
final void Function() onCancelPressed;
|
||||
const ButtonsDividerBottomDialogWidget({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.onNextPressed,
|
||||
required this.onCancelPressed,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
const Divider(
|
||||
thickness: 0.5,
|
||||
height: 1,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(26),
|
||||
),
|
||||
onTap: onCancelPressed,
|
||||
child: Container(
|
||||
height: 40,
|
||||
alignment: Alignment.center,
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
right: BorderSide(
|
||||
color: ColorsManager.grayBorder,
|
||||
),
|
||||
),
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(26),
|
||||
),
|
||||
),
|
||||
child: const Text(
|
||||
'Cancel',
|
||||
style: TextStyle(color: ColorsManager.blackColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child:
|
||||
BlocConsumer<NonBookableSpacesBloc, NonBookableSpacesState>(
|
||||
listener: (context, nonBookableState) {
|
||||
if (nonBookableState is NonBookableSpacesInitial) {
|
||||
context.pop();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text(
|
||||
'Operation Done Successfully',
|
||||
style: TextStyle(color: ColorsManager.activeGreen),
|
||||
),
|
||||
duration: Duration(seconds: 2),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
context.read<BookableSpacesBloc>().add(
|
||||
LoadBookableSpacesEvent(
|
||||
BookableSpacesParam(currentPage: 1),
|
||||
),
|
||||
);
|
||||
} else if (nonBookableState is NonBookableSpacesError) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
nonBookableState.error,
|
||||
style: const TextStyle(color: ColorsManager.red),
|
||||
),
|
||||
duration: const Duration(seconds: 2),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
builder: (context, nonBookableState) {
|
||||
return TextButton(
|
||||
onPressed: onNextPressed,
|
||||
child: Text(
|
||||
title,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_checkbox_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class CheckBoxSpaceWidget extends StatelessWidget {
|
||||
final BookableSpacemodel nonBookableSpace;
|
||||
|
||||
const CheckBoxSpaceWidget({
|
||||
super.key,
|
||||
required this.nonBookableSpace,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
BlocBuilder<SetupBookableSpacesBloc, SetupBookableSpacesState>(
|
||||
builder: (context, state) {
|
||||
final isChecked = switch (state) {
|
||||
AddNonBookableSpaceIntoBookableState(
|
||||
bookableSpaces: final spaces
|
||||
) =>
|
||||
spaces.any((s) => s.spaceUuid == nonBookableSpace.spaceUuid),
|
||||
RemoveBookableSpaceIntoNonBookableState(
|
||||
bookableSpaces: final spaces
|
||||
) =>
|
||||
spaces.any((s) => s.spaceUuid == nonBookableSpace.spaceUuid),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
return CustomCheckboxWidget(
|
||||
value: isChecked,
|
||||
onChanged: (value) {
|
||||
final bloc = context.read<SetupBookableSpacesBloc>();
|
||||
|
||||
if (value ?? false) {
|
||||
bloc.add(AddToBookableSpaceEvent(
|
||||
nonBookableSpace: nonBookableSpace,
|
||||
));
|
||||
} else {
|
||||
bloc.add(RemoveFromBookableSpaceEvent(
|
||||
bookableSpace: nonBookableSpace,
|
||||
));
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 15),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
nonBookableSpace.spaceName,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 12,
|
||||
color: ColorsManager.titleGray,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
nonBookableSpace.spaceVirtualAddress,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 10,
|
||||
color: ColorsManager.titleGray,
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class ColumnTitleWidget extends StatelessWidget {
|
||||
final bool isFirst;
|
||||
final bool isLast;
|
||||
final String title;
|
||||
const ColumnTitleWidget({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.isFirst,
|
||||
required this.isLast,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.graysColor,
|
||||
borderRadius: isFirst
|
||||
? const BorderRadius.only(
|
||||
topLeft: Radius.circular(12),
|
||||
)
|
||||
: isLast
|
||||
? const BorderRadius.only(
|
||||
topRight: Radius.circular(12),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.grayColor,
|
||||
fontSize: 12,
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class CustomCheckboxWidget extends StatelessWidget {
|
||||
final bool value;
|
||||
final ValueChanged<bool?> onChanged;
|
||||
final double? outHeight;
|
||||
final double? outWidth;
|
||||
final double? iconSize;
|
||||
const CustomCheckboxWidget({
|
||||
super.key,
|
||||
required this.value,
|
||||
required this.onChanged,
|
||||
this.outWidth,
|
||||
this.outHeight,
|
||||
this.iconSize,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () => onChanged(!value),
|
||||
child: Container(
|
||||
width: outWidth ?? 17,
|
||||
height: outHeight ?? 17,
|
||||
decoration: BoxDecoration(
|
||||
color: value ? Colors.white : ColorsManager.checkBoxFillColor,
|
||||
border: value
|
||||
? Border.all(color: ColorsManager.secondaryColor, width: 1)
|
||||
: Border.all(color: ColorsManager.checkBoxBorderGray, width: 1),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: value
|
||||
? Center(
|
||||
child: Container(
|
||||
width: outWidth != null ? outWidth! - 4 : 13,
|
||||
height: outHeight != null ? outHeight! - 4 : 13,
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.secondaryColor,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.check,
|
||||
size: 12,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
import 'package:data_table_2/data_table_2.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/column_title_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class CustomDataTable<T> extends StatelessWidget {
|
||||
final List<String> columnsTitles;
|
||||
final List<DataCell> Function(T item) cellsWidgets;
|
||||
final List<T> items;
|
||||
|
||||
const CustomDataTable({
|
||||
super.key,
|
||||
required this.items,
|
||||
required this.cellsWidgets,
|
||||
required this.columnsTitles,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DataTable2(
|
||||
dividerThickness: 0.5,
|
||||
columnSpacing: 2,
|
||||
horizontalMargin: 0,
|
||||
empty: SvgPicture.asset(Assets.emptyDataTable),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.circleRolesBackground,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: ColorsManager.textGray,
|
||||
blurRadius: 12,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
columns: columnsTitles.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final title = entry.value;
|
||||
|
||||
return DataColumn(
|
||||
label: ColumnTitleWidget(
|
||||
title: title,
|
||||
isFirst: index == 0,
|
||||
isLast: index == columnsTitles.length - 1,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
rows: items.map((item) {
|
||||
return DataRow(cells: cellsWidgets(item));
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/space_step_part_widget.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/step_two_details_widget.dart';
|
||||
|
||||
class DetailsStepsWidget extends StatelessWidget {
|
||||
final TextEditingController pointsController;
|
||||
final BookableSpacemodel? editingBookableSpace;
|
||||
const DetailsStepsWidget({
|
||||
super.key,
|
||||
required this.pointsController,
|
||||
this.editingBookableSpace,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 20),
|
||||
child: BlocBuilder<StepsCubit, StepsState>(builder: (context, state) {
|
||||
return switch (state) {
|
||||
StepOneState() => const SpacesStepDetailsWidget(),
|
||||
StepTwoState() => StepTwoDetailsWidget(
|
||||
pointsController: pointsController,
|
||||
editingBookableSpace: editingBookableSpace,
|
||||
),
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/data/remote_update_bookable_space_service.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class EditBookableSpaceButtonWidget extends StatelessWidget {
|
||||
final BookableSpacemodel? space;
|
||||
const EditBookableSpaceButtonWidget({
|
||||
super.key,
|
||||
required this.space,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
offset: Offset.zero,
|
||||
blurRadius: 3,
|
||||
spreadRadius: 0,
|
||||
color: ColorsManager.timePickerColor.withValues(
|
||||
alpha: 0.3,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
final bookableBloc = context.read<BookableSpacesBloc>();
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider.value(
|
||||
value: bookableBloc,
|
||||
),
|
||||
BlocProvider(
|
||||
create: (context) => UpdateBookableSpacesBloc(
|
||||
RemoteUpdateBookableSpaceService(HTTPService()),
|
||||
),
|
||||
),
|
||||
],
|
||||
child: SetupBookableSpacesDialog(
|
||||
editingBookableSpace: space,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
minimumSize: const Size(45, 30),
|
||||
elevation: 0,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.settings,
|
||||
height: 13,
|
||||
color: ColorsManager.blue1,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class PaginationButtonsWidget extends StatelessWidget {
|
||||
const PaginationButtonsWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<BookableSpacesBloc, BookableSpacesState>(
|
||||
builder: (context, state) {
|
||||
if (state is BookableSpacesLoaded) {
|
||||
final totalPages = state.bookableSpacesList.totalPages;
|
||||
final currentPage = state.bookableSpacesList.page;
|
||||
|
||||
List<Widget> paginationItems = [];
|
||||
|
||||
if (currentPage > 2) {
|
||||
paginationItems.add(
|
||||
_buildArrowButton(
|
||||
label: '«',
|
||||
onTap: () {
|
||||
context.read<BookableSpacesBloc>().add(
|
||||
LoadBookableSpacesEvent(
|
||||
BookableSpacesParam(currentPage: currentPage - 2),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (currentPage > 1) {
|
||||
paginationItems.add(
|
||||
_buildArrowButton(
|
||||
label: '<',
|
||||
onTap: () {
|
||||
context.read<BookableSpacesBloc>().add(
|
||||
LoadBookableSpacesEvent(
|
||||
BookableSpacesParam(currentPage: currentPage - 1),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
for (int i = 1; i <= totalPages; i++) {
|
||||
paginationItems.add(
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (i != currentPage) {
|
||||
context.read<BookableSpacesBloc>().add(
|
||||
LoadBookableSpacesEvent(
|
||||
BookableSpacesParam(currentPage: i),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
width: 30,
|
||||
height: 30,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: i == currentPage
|
||||
? ColorsManager.dialogBlueTitle
|
||||
: ColorsManager.whiteColors,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: ColorsManager.lightGrayBorderColor,
|
||||
)),
|
||||
child: Text(
|
||||
'$i',
|
||||
style: TextStyle(
|
||||
color: i == currentPage ? Colors.white : Colors.black,
|
||||
fontWeight: i == currentPage
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (currentPage < totalPages) {
|
||||
paginationItems.add(
|
||||
_buildArrowButton(
|
||||
label: '>',
|
||||
onTap: () {
|
||||
context.read<BookableSpacesBloc>().add(
|
||||
LoadBookableSpacesEvent(
|
||||
BookableSpacesParam(currentPage: currentPage + 1),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (currentPage + 1 < totalPages) {
|
||||
paginationItems.add(
|
||||
_buildArrowButton(
|
||||
label: '»',
|
||||
onTap: () {
|
||||
context.read<BookableSpacesBloc>().add(
|
||||
LoadBookableSpacesEvent(
|
||||
BookableSpacesParam(currentPage: currentPage + 2),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: paginationItems,
|
||||
);
|
||||
} else {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildArrowButton(
|
||||
{required String label, required VoidCallback onTap}) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
width: 30,
|
||||
height: 30,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.whiteColors,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: ColorsManager.lightGrayBorderColor,
|
||||
)),
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/bookable_spaces_params.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/bookable_space_switch_activation_widget.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_data_table.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/edit_bookable_space_button_widget.dart';
|
||||
|
||||
class TableOfBookableSpacesWidget extends StatelessWidget {
|
||||
const TableOfBookableSpacesWidget({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<BookableSpacesBloc, BookableSpacesState>(
|
||||
builder: (context, state) {
|
||||
if (state is BookableSpacesLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is BookableSpacesError) {
|
||||
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||
Text(state.error),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () => context
|
||||
.read<BookableSpacesBloc>()
|
||||
.add(LoadBookableSpacesEvent(
|
||||
BookableSpacesParam(currentPage: 1),
|
||||
)),
|
||||
child: const Text('Try Again'))
|
||||
]);
|
||||
} else if (state is BookableSpacesLoaded) {
|
||||
return CustomDataTable<BookableSpacemodel>(
|
||||
items: state.bookableSpacesList.data,
|
||||
cellsWidgets: (space) => [
|
||||
DataCell(
|
||||
DataCellWidget(
|
||||
title: space.spaceName,
|
||||
),
|
||||
),
|
||||
DataCell(DataCellWidget(
|
||||
title: space.spaceVirtualAddress,
|
||||
)),
|
||||
DataCell(Container(
|
||||
padding: const EdgeInsetsGeometry.only(left: 10),
|
||||
width: 200,
|
||||
child: Wrap(
|
||||
spacing: 4,
|
||||
children: space.spaceConfig!.bookableDays
|
||||
.map(
|
||||
(day) => DataCellWidget(title: day),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
)),
|
||||
DataCell(
|
||||
DataCellWidget(
|
||||
title: space.spaceConfig!.bookingStartTime!.format(context),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
DataCellWidget(
|
||||
title: space.spaceConfig!.bookingEndTime!.format(context),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
DataCellWidget(
|
||||
title: '${space.spaceConfig!.cost} Points',
|
||||
),
|
||||
),
|
||||
DataCell(BookableSpaceSwitchActivationWidget(
|
||||
bookableSpaces: state.bookableSpacesList,
|
||||
space: space,
|
||||
)),
|
||||
DataCell(EditBookableSpaceButtonWidget(
|
||||
space: space,
|
||||
)),
|
||||
],
|
||||
columnsTitles: const [
|
||||
'Space',
|
||||
'Space Virtual Address',
|
||||
'Bookable Days',
|
||||
'Booking Start Time',
|
||||
'Booking End Time',
|
||||
'Cost',
|
||||
'Availability',
|
||||
'Settings',
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DataCellWidget extends StatelessWidget {
|
||||
final String title;
|
||||
const DataCellWidget({
|
||||
super.key,
|
||||
required this.title,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsetsGeometry.only(left: 10),
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 11),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/widgets/icon_text_button.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/bookable_spaces_bloc/bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/setup_bookable_spaces_dialog.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class RowOfButtonsTitleWidget extends StatelessWidget {
|
||||
const RowOfButtonsTitleWidget({
|
||||
super.key,
|
||||
required this.pageController,
|
||||
});
|
||||
|
||||
final PageController pageController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsetsGeometry.symmetric(vertical: 5),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
offset: Offset.zero,
|
||||
blurRadius: 3,
|
||||
spreadRadius: 0,
|
||||
color: ColorsManager.timePickerColor.withValues(
|
||||
alpha: 0.3,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
minimumSize: const Size(50, 40),
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.backButtonIcon,
|
||||
height: 15,
|
||||
),
|
||||
onPressed: () {
|
||||
pageController.jumpToPage(1);
|
||||
}),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Text(
|
||||
'Manage Bookable Spaces',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
color: ColorsManager.vividBlue.withValues(
|
||||
alpha: 0.7,
|
||||
),
|
||||
fontWeight: FontWeight.w700),
|
||||
)
|
||||
],
|
||||
),
|
||||
SvgTextButton(
|
||||
padding: const EdgeInsets.all(10),
|
||||
svgSize: 15,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
svgAsset: Assets.addButtonIcon,
|
||||
label: 'Set Up a Bookable Spaces',
|
||||
onPressed: () {
|
||||
final bloc = context.read<BookableSpacesBloc>();
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => BlocProvider.value(
|
||||
value: bloc,
|
||||
child: SetupBookableSpacesDialog(),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart';
|
||||
|
||||
class NextFirstStepButton extends StatelessWidget {
|
||||
const NextFirstStepButton({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SetupBookableSpacesBloc, SetupBookableSpacesState>(
|
||||
builder: (context, state) {
|
||||
return switch (state) {
|
||||
SetupBookableSpacesInitial() => ButtonsDividerBottomDialogWidget(
|
||||
title: 'Next',
|
||||
onNextPressed: null,
|
||||
onCancelPressed: () => context.pop(),
|
||||
),
|
||||
AddNonBookableSpaceIntoBookableState(:final bookableSpaces) ||
|
||||
RemoveBookableSpaceIntoNonBookableState(:final bookableSpaces) =>
|
||||
ButtonsDividerBottomDialogWidget(
|
||||
title: 'Next',
|
||||
onNextPressed: bookableSpaces.isEmpty
|
||||
? null
|
||||
: () {
|
||||
context.read<StepsCubit>().goToNextStep();
|
||||
context.read<SetupBookableSpacesBloc>().add(
|
||||
CheckConfigurValidityEvent(),
|
||||
);
|
||||
},
|
||||
onCancelPressed: () => context.pop(),
|
||||
),
|
||||
_ => const SizedBox(),
|
||||
};
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,121 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class PointsPartWidget extends StatefulWidget {
|
||||
final BookableSpacemodel? editingBookableSpace;
|
||||
final TextEditingController pointsController;
|
||||
|
||||
const PointsPartWidget({
|
||||
super.key,
|
||||
required this.pointsController,
|
||||
this.editingBookableSpace,
|
||||
});
|
||||
|
||||
@override
|
||||
State<PointsPartWidget> createState() => _PointsPartWidgetState();
|
||||
}
|
||||
|
||||
class _PointsPartWidgetState extends State<PointsPartWidget> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.editingBookableSpace != null) {
|
||||
widget.pointsController.text =
|
||||
widget.editingBookableSpace!.spaceConfig!.cost.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<TogglePointsSwitchCubit, TogglePointsSwitchState>(
|
||||
builder: (context, switchState) {
|
||||
final isSwitchOn = switchState is ActivatePointsSwitch;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
if (isSwitchOn)
|
||||
Text(
|
||||
'* ',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(color: Colors.red),
|
||||
)
|
||||
else
|
||||
const SizedBox(width: 11),
|
||||
const Text('Points/hrs'),
|
||||
],
|
||||
),
|
||||
Transform.scale(
|
||||
scale: 0.7,
|
||||
child: Switch(
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
trackOutlineColor:
|
||||
WidgetStateProperty.all(ColorsManager.whiteColors),
|
||||
activeTrackColor: ColorsManager.dialogBlueTitle,
|
||||
inactiveTrackColor: ColorsManager.lightGrayBorderColor,
|
||||
thumbColor:
|
||||
WidgetStateProperty.all(ColorsManager.whiteColors),
|
||||
value: isSwitchOn,
|
||||
onChanged: (value) {
|
||||
final toggleCubit =
|
||||
context.read<TogglePointsSwitchCubit>();
|
||||
final bloc = context.read<SetupBookableSpacesBloc>();
|
||||
|
||||
final updatedCost = value ? -1 : 0;
|
||||
|
||||
if (value) {
|
||||
toggleCubit.activateSwitch();
|
||||
} else {
|
||||
toggleCubit.unActivateSwitch();
|
||||
widget.pointsController.clear();
|
||||
}
|
||||
bloc.add(ChangeCostEvent(cost: updatedCost));
|
||||
bloc.add(CheckConfigurValidityEvent());
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
if (isSwitchOn)
|
||||
SearchUnbookableSpacesWidget(
|
||||
title: 'Ex: 0',
|
||||
topPadding: 0,
|
||||
blur: 1,
|
||||
raduis: 10,
|
||||
height: 34,
|
||||
controller: widget.pointsController,
|
||||
suffix: const SizedBox(),
|
||||
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
|
||||
onChanged: (_) {
|
||||
final updatedCost =
|
||||
int.tryParse(widget.pointsController.text) ?? 0;
|
||||
context
|
||||
.read<SetupBookableSpacesBloc>()
|
||||
.add(ChangeCostEvent(cost: updatedCost));
|
||||
context.read<SetupBookableSpacesBloc>().add(
|
||||
CheckConfigurValidityEvent(),
|
||||
);
|
||||
},
|
||||
)
|
||||
else
|
||||
const SizedBox(),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/update_bookable_space_param.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/send_bookable_spaces_bloc/send_bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/update_bookable_spaces/update_bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/buttons_divider_bottom_dialog_widget.dart';
|
||||
|
||||
class SaveSecondStepButton extends StatelessWidget {
|
||||
final TextEditingController pointsController;
|
||||
final bool isEditingMode;
|
||||
final List<BookableSpacemodel> bookableSpaces;
|
||||
|
||||
const SaveSecondStepButton({
|
||||
super.key,
|
||||
required this.pointsController,
|
||||
required this.isEditingMode,
|
||||
required this.bookableSpaces,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocListener<SendBookableSpacesBloc, SendBookableSpacesState>(
|
||||
listener: (context, state) {
|
||||
if (state is SendBookableSpacesSuccess) {
|
||||
context.read<NonBookableSpacesBloc>().add(CallInitStateEvent());
|
||||
} else if (state is SendBookableSpacesError) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(state.error)),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: BlocBuilder<SetupBookableSpacesBloc, SetupBookableSpacesState>(
|
||||
builder: (context, state) {
|
||||
return ButtonsDividerBottomDialogWidget(
|
||||
title: 'Save',
|
||||
onNextPressed: state is UnValidSaveButtonState
|
||||
? null
|
||||
: () {
|
||||
if (bookableSpaces.any((e) => e.isValid)) {
|
||||
if (isEditingMode) {
|
||||
callEditLogic(context);
|
||||
} else {
|
||||
context.read<SendBookableSpacesBloc>().add(
|
||||
SendBookableSpacesToApi(
|
||||
selectedBookableSpaces: bookableSpaces,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
onCancelPressed: () => context.pop(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void callEditLogic(BuildContext context) {
|
||||
print(bookableSpaces.first.spaceConfig!.cost);
|
||||
if (bookableSpaces.isNotEmpty) {
|
||||
context.read<UpdateBookableSpacesBloc>().add(
|
||||
UpdateBookableSpace(
|
||||
onSuccess: () => context
|
||||
.read<NonBookableSpacesBloc>()
|
||||
.add(CallInitStateEvent()),
|
||||
updatedParam: UpdateBookableSpaceParam.fromBookableModel(
|
||||
bookableSpaces.first,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class SearchUnbookableSpacesWidget extends StatelessWidget {
|
||||
final String title;
|
||||
final Widget? suffix;
|
||||
final double? height;
|
||||
final double? width;
|
||||
final double? blur;
|
||||
final double? raduis;
|
||||
final double? topPadding;
|
||||
final TextEditingController? controller;
|
||||
final List<TextInputFormatter>? inputFormatters;
|
||||
final void Function(String)? onChanged;
|
||||
const SearchUnbookableSpacesWidget({
|
||||
required this.title,
|
||||
this.controller,
|
||||
this.blur,
|
||||
this.onChanged,
|
||||
this.suffix,
|
||||
this.height,
|
||||
this.width,
|
||||
this.topPadding,
|
||||
this.raduis,
|
||||
this.inputFormatters,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: width ?? 480,
|
||||
height: height ?? 40,
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.whiteColors,
|
||||
borderRadius: BorderRadius.circular(raduis ?? 15),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color:
|
||||
ColorsManager.shadowOfSearchTextfield.withValues(alpha: 0.15),
|
||||
offset: Offset.zero,
|
||||
blurRadius: blur ?? 5,
|
||||
spreadRadius: 0,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
inputFormatters: inputFormatters,
|
||||
onChanged: onChanged,
|
||||
decoration: InputDecoration(
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
vertical: topPadding ?? 5,
|
||||
horizontal: 15,
|
||||
),
|
||||
hintText: title,
|
||||
hintStyle: const TextStyle(color: ColorsManager.hintTextGrey),
|
||||
border: InputBorder.none,
|
||||
suffixIcon: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: suffix ??
|
||||
SvgPicture.asset(
|
||||
Assets.searchIcon,
|
||||
height: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: ColorsManager.hintTextGrey,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/params/non_bookable_spaces_params.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/non_bookable_spaces_bloc/non_bookaable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/search_unbookable_spaces_widget.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/unbookable_list_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class SpacesStepDetailsWidget extends StatefulWidget {
|
||||
const SpacesStepDetailsWidget({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SpacesStepDetailsWidget> createState() =>
|
||||
_SpacesStepDetailsWidgetState();
|
||||
}
|
||||
|
||||
class _SpacesStepDetailsWidgetState extends State<SpacesStepDetailsWidget> {
|
||||
ScrollController scrollController = ScrollController();
|
||||
int currentPage = 1;
|
||||
String? currentSearchTerm;
|
||||
bool isLoadingMore = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
scrollController.addListener(() {
|
||||
if (scrollController.position.pixels >=
|
||||
scrollController.position.maxScrollExtent - 100) {
|
||||
final state = context.read<NonBookableSpacesBloc>().state;
|
||||
if (state is NonBookableSpacesLoaded &&
|
||||
state.nonBookableSpaces.hasNext &&
|
||||
!isLoadingMore) {
|
||||
isLoadingMore = true;
|
||||
currentPage++;
|
||||
context.read<NonBookableSpacesBloc>().add(
|
||||
LoadUnBookableSpacesEvent(
|
||||
nonBookableSpacesParams: NonBookableSpacesParams(
|
||||
currentPage: currentPage,
|
||||
searchedWords: currentSearchTerm,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'Select Space',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Container(
|
||||
width: 450,
|
||||
height: 480,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: ColorsManager.shadowOfDetailsContainer,
|
||||
offset: Offset.zero,
|
||||
blurRadius: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 520,
|
||||
height: 70,
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 15, horizontal: 20),
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.circleRolesBackground,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(20),
|
||||
),
|
||||
),
|
||||
child: SearchUnbookableSpacesWidget(
|
||||
title: 'Search',
|
||||
onChanged: (p0) {
|
||||
currentSearchTerm = p0;
|
||||
currentPage = 1;
|
||||
context.read<NonBookableSpacesBloc>().add(
|
||||
LoadUnBookableSpacesEvent(
|
||||
nonBookableSpacesParams: NonBookableSpacesParams(
|
||||
currentPage: currentPage,
|
||||
searchedWords: currentSearchTerm,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child:
|
||||
BlocConsumer<NonBookableSpacesBloc, NonBookableSpacesState>(
|
||||
listener: (context, state) {
|
||||
if (state is NonBookableSpacesLoaded) {
|
||||
isLoadingMore = false;
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
return switch (state) {
|
||||
NonBookableSpacesError(error: final error) => Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(error),
|
||||
const SizedBox(height: 5),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
context.read<NonBookableSpacesBloc>().add(
|
||||
LoadUnBookableSpacesEvent(
|
||||
nonBookableSpacesParams:
|
||||
NonBookableSpacesParams(
|
||||
currentPage: currentPage,
|
||||
searchedWords: currentSearchTerm,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const Text('Try Again'),
|
||||
),
|
||||
],
|
||||
),
|
||||
NonBookableSpacesLoading(lastNonBookableSpaces: null) =>
|
||||
const Center(child: CircularProgressIndicator()),
|
||||
NonBookableSpacesLoading(
|
||||
lastNonBookableSpaces: final spaces
|
||||
) =>
|
||||
UnbookableListWidget(
|
||||
scrollController: scrollController,
|
||||
nonBookableSpaces: spaces!,
|
||||
),
|
||||
NonBookableSpacesLoaded(
|
||||
nonBookableSpaces: final spaces
|
||||
) =>
|
||||
UnbookableListWidget(
|
||||
scrollController: scrollController,
|
||||
nonBookableSpaces: spaces,
|
||||
),
|
||||
_ => const SizedBox(),
|
||||
};
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/toggle_cubit/toggle_points_switch_cubit.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/booking_period_widget.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/points_part_widget.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/week_checkbox_title_widget.dart';
|
||||
|
||||
class StepTwoDetailsWidget extends StatelessWidget {
|
||||
final TextEditingController pointsController;
|
||||
final BookableSpacemodel? editingBookableSpace;
|
||||
const StepTwoDetailsWidget({
|
||||
super.key,
|
||||
required this.pointsController,
|
||||
this.editingBookableSpace,
|
||||
});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 450,
|
||||
height: 480,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
WeekDaysCheckboxRow(
|
||||
editingBookableSpace: editingBookableSpace,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
BookingPeriodWidget(
|
||||
editingBookableSpace: editingBookableSpace,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
BlocProvider(
|
||||
create: editingBookableSpace == null
|
||||
? (context) => TogglePointsSwitchCubit()..activateSwitch()
|
||||
: editingBookableSpace!.spaceConfig!.cost == 0
|
||||
? (context) => TogglePointsSwitchCubit()..unActivateSwitch()
|
||||
: (context) => TogglePointsSwitchCubit()..activateSwitch(),
|
||||
child: PointsPartWidget(
|
||||
pointsController: pointsController,
|
||||
editingBookableSpace: editingBookableSpace),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/steps_cubit/cubit/steps_cubit.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class StepperPartWidget extends StatelessWidget {
|
||||
const StepperPartWidget({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.all(20),
|
||||
padding: const EdgeInsetsGeometry.only(left: 20),
|
||||
child: BlocBuilder<StepsCubit, StepsState>(
|
||||
builder: (context, state) {
|
||||
if (state is StepOneState) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
const CircleTitleStepperWidget(
|
||||
title: 'Space',
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.only(left: 3),
|
||||
alignment: Alignment.centerLeft,
|
||||
height: 40,
|
||||
child: const VerticalDivider(
|
||||
width: 8,
|
||||
)),
|
||||
const CircleTitleStepperWidget(
|
||||
title: 'Settings',
|
||||
titleColor: ColorsManager.softGray,
|
||||
circleColor: ColorsManager.whiteColors,
|
||||
borderColor: ColorsManager.textGray,
|
||||
)
|
||||
],
|
||||
);
|
||||
} else if (state is StepTwoState) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
const CircleTitleStepperWidget(
|
||||
title: 'Space',
|
||||
titleColor: ColorsManager.softGray,
|
||||
cicleIcon: Icon(
|
||||
Icons.check,
|
||||
color: ColorsManager.whiteColors,
|
||||
size: 12,
|
||||
),
|
||||
circleColor: ColorsManager.trueIconGreen,
|
||||
radius: 15,
|
||||
borderColor: ColorsManager.trueIconGreen,
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.only(left: 3),
|
||||
alignment: Alignment.centerLeft,
|
||||
height: 40,
|
||||
child: const VerticalDivider(
|
||||
width: 8,
|
||||
)),
|
||||
const CircleTitleStepperWidget(
|
||||
title: 'Settings',
|
||||
)
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CircleTitleStepperWidget extends StatelessWidget {
|
||||
final double? radius;
|
||||
final Widget? cicleIcon;
|
||||
final Color? circleColor;
|
||||
final Color? borderColor;
|
||||
final Color? titleColor;
|
||||
final String title;
|
||||
const CircleTitleStepperWidget({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.circleColor,
|
||||
this.borderColor,
|
||||
this.cicleIcon,
|
||||
this.titleColor,
|
||||
this.radius,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
Container(
|
||||
width: radius ?? 15,
|
||||
height: radius ?? 15,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: circleColor ?? ColorsManager.blue1,
|
||||
border: Border.all(color: borderColor ?? ColorsManager.blue1)),
|
||||
child: cicleIcon,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: titleColor ?? ColorsManager.blackColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart';
|
||||
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class TimePickerWidget extends StatefulWidget {
|
||||
final String title;
|
||||
TimePickerWidget({
|
||||
super.key,
|
||||
required this.onTimePicked,
|
||||
required this.title,
|
||||
});
|
||||
late final SetupBookableSpacesBloc setupBookableSpacesBloc;
|
||||
final void Function(TimeOfDay? timePicked) onTimePicked;
|
||||
@override
|
||||
State<TimePickerWidget> createState() => _TimePickerWidgetState();
|
||||
}
|
||||
|
||||
class _TimePickerWidgetState extends State<TimePickerWidget> {
|
||||
TimeOfDay? timePicked;
|
||||
@override
|
||||
void initState() {
|
||||
widget.setupBookableSpacesBloc = context.read<SetupBookableSpacesBloc>();
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
onTap: () async {
|
||||
final tempTime = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: TimeOfDay.now(),
|
||||
builder: (context, child) {
|
||||
return Theme(
|
||||
data: ThemeData.light().copyWith(
|
||||
colorScheme: const ColorScheme.light(
|
||||
primary: ColorsManager.primaryColor,
|
||||
onSurface: Colors.black,
|
||||
),
|
||||
),
|
||||
child: child!,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (tempTime == null) return;
|
||||
|
||||
widget.onTimePicked(tempTime);
|
||||
timePicked = tempTime;
|
||||
|
||||
widget.setupBookableSpacesBloc.add(CheckConfigurValidityEvent());
|
||||
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(
|
||||
height: 32,
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(10),
|
||||
bottomRight: Radius.circular(10),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Text(
|
||||
timePicked == null ? widget.title : timePicked!.format(context),
|
||||
style: TextStyle(
|
||||
color: ColorsManager.blackColor.withValues(alpha: 0.4),
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/check_box_space_widget.dart';
|
||||
import 'package:syncrow_web/pages/space_management_v2/main_module/shared/models/paginated_data_model.dart';
|
||||
|
||||
class UnbookableListWidget extends StatelessWidget {
|
||||
final PaginatedDataModel<BookableSpacemodel> nonBookableSpaces;
|
||||
const UnbookableListWidget({
|
||||
super.key,
|
||||
required this.scrollController,
|
||||
required this.nonBookableSpaces,
|
||||
});
|
||||
|
||||
final ScrollController scrollController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 490,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
bottom: Radius.circular(20),
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.only(top: 10, left: 20, bottom: 5),
|
||||
child: ListView.separated(
|
||||
separatorBuilder: (context, index) => const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
controller: scrollController,
|
||||
itemCount: nonBookableSpaces.data.length,
|
||||
itemBuilder: (context, index) {
|
||||
if (index < nonBookableSpaces.data.length) {
|
||||
return CheckBoxSpaceWidget(
|
||||
nonBookableSpace: nonBookableSpaces.data[index],
|
||||
|
||||
);
|
||||
} else {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 10),
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/domain/models/bookable_space_model.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/blocs/setup_bookable_spaces_bloc/setup_bookable_spaces_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/widgets/custom_checkbox_widget.dart';
|
||||
|
||||
class WeekDaysCheckboxRow extends StatefulWidget {
|
||||
final BookableSpacemodel? editingBookableSpace;
|
||||
const WeekDaysCheckboxRow({
|
||||
super.key,
|
||||
this.editingBookableSpace,
|
||||
});
|
||||
|
||||
@override
|
||||
State<WeekDaysCheckboxRow> createState() => _WeekDaysCheckboxRowState();
|
||||
}
|
||||
|
||||
class _WeekDaysCheckboxRowState extends State<WeekDaysCheckboxRow> {
|
||||
final Map<String, bool> _daysChecked = {
|
||||
'Mon': false,
|
||||
'Tue': false,
|
||||
'Wed': false,
|
||||
'Thu': false,
|
||||
'Fri': false,
|
||||
'Sat': false,
|
||||
'Sun': false,
|
||||
};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
final existingDays =
|
||||
widget.editingBookableSpace?.spaceConfig?.bookableDays ?? [];
|
||||
|
||||
for (var day in _daysChecked.keys) {
|
||||
if (existingDays.contains(day)) {
|
||||
_daysChecked[day] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'* ',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(color: Colors.red),
|
||||
),
|
||||
const Text('Days'),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _daysChecked.entries.map((entry) {
|
||||
return Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
CustomCheckboxWidget(
|
||||
outHeight: 16,
|
||||
outWidth: 16,
|
||||
value: entry.value,
|
||||
onChanged: (newValue) {
|
||||
setState(() {
|
||||
_daysChecked[entry.key] = newValue ?? false;
|
||||
});
|
||||
|
||||
final selectedDays = _daysChecked.entries
|
||||
.where((e) => e.value)
|
||||
.map((e) => e.key)
|
||||
.toList();
|
||||
|
||||
context.read<SetupBookableSpacesBloc>().add(
|
||||
AddBookableDaysEvent(bookableDays: selectedDays),
|
||||
);
|
||||
context.read<SetupBookableSpacesBloc>().add(
|
||||
CheckConfigurValidityEvent(),
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
entry.key,
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/bloc/access_bloc.dart';
|
||||
import 'package:syncrow_web/pages/access_management/bloc/access_event.dart';
|
||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/booking_page.dart';
|
||||
import 'package:syncrow_web/pages/access_management/booking_system/view/booking_page.dart';
|
||||
import 'package:syncrow_web/pages/access_management/manage_bookable_spaces/presentation/screens/manage_bookable_spaces_screen.dart';
|
||||
import 'package:syncrow_web/pages/access_management/booking_system/presentation/view/booking_page.dart' hide BookingPage;
|
||||
import 'package:syncrow_web/pages/access_management/view/access_overview_content.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
@ -71,9 +73,14 @@ class _AccessManagementPageState extends State<AccessManagementPage>
|
||||
scaffoldBody: PageView(
|
||||
controller: _pageController,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
children: const [
|
||||
AccessOverviewContent(),
|
||||
BookingPage(),
|
||||
children: [
|
||||
const AccessOverviewContent(),
|
||||
BookingPage(
|
||||
pageController: _pageController,
|
||||
),
|
||||
ManageBookableSpacesPage(
|
||||
pageController: _pageController,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -40,4 +40,22 @@ class PaginatedDataModel<T> extends Equatable {
|
||||
totalItems,
|
||||
totalPages,
|
||||
];
|
||||
|
||||
PaginatedDataModel<T> copyWith({
|
||||
List<T>? data,
|
||||
int? page,
|
||||
int? size,
|
||||
bool? hasNext,
|
||||
int? totalItems,
|
||||
int? totalPages,
|
||||
}) {
|
||||
return PaginatedDataModel<T>(
|
||||
data: data ?? this.data,
|
||||
page: page ?? this.page,
|
||||
size: size ?? this.size,
|
||||
hasNext: hasNext ?? this.hasNext,
|
||||
totalItems: totalItems ?? this.totalItems,
|
||||
totalPages: totalPages ?? this.totalPages,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ class VisitorPasswordBloc
|
||||
effectiveTimeTimeStamp = selectedTimestamp;
|
||||
startTimeAccess = selectedDateTime.toString().split('.').first;
|
||||
} else {
|
||||
// END TIME VALIDATION
|
||||
|
||||
if (effectiveTimeTimeStamp != null &&
|
||||
selectedTimestamp < effectiveTimeTimeStamp!) {
|
||||
await showDialog<void>(
|
||||
|
Reference in New Issue
Block a user