first commit

This commit is contained in:
2026-02-07 15:57:09 +07:00
commit 157096f164
1153 changed files with 415766 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
class ShiftListModel {
ShiftListModel({
this.message,
this.data,
});
ShiftListModel.fromJson(dynamic json) {
message = json['message'];
if (json['data'] != null) {
data = [];
json['data'].forEach((v) {
data?.add(ShiftData.fromJson(v));
});
}
}
String? message;
List<ShiftData>? data;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['message'] = message;
if (data != null) {
map['data'] = data?.map((v) => v.toJson()).toList();
}
return map;
}
}
class ShiftData {
ShiftData({
this.id,
this.name,
this.businessId,
this.startTime,
this.endTime,
this.startBreakTime,
this.endBreakTime,
this.breakTime,
this.breakStatus,
this.status,
this.createdAt,
this.updatedAt,
});
ShiftData.fromJson(dynamic json) {
id = json['id'];
name = json['name'];
businessId = json['business_id'];
startTime = json['start_time'];
endTime = json['end_time'];
startBreakTime = json['start_break_time'];
endBreakTime = json['end_break_time'];
breakTime = json['break_time'];
breakStatus = json['break_status'];
status = json['status'];
createdAt = json['created_at'];
updatedAt = json['updated_at'];
}
num? id;
String? name;
num? businessId;
String? startTime;
String? endTime;
String? startBreakTime;
String? endBreakTime;
String? breakTime;
String? breakStatus;
num? status;
String? createdAt;
String? updatedAt;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = id;
map['name'] = name;
map['business_id'] = businessId;
map['start_time'] = startTime;
map['end_time'] = endTime;
map['start_break_time'] = startBreakTime;
map['end_break_time'] = endBreakTime;
map['break_time'] = breakTime;
map['break_status'] = breakStatus;
map['status'] = status;
map['created_at'] = createdAt;
map['updated_at'] = updatedAt;
return map;
}
}

View File

@@ -0,0 +1,316 @@
// File: add_new_shift.dart (Shift Name Changed back to Dropdown)
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mobile_pos/Screens/hrm/shift/Model/shift_list_model.dart';
import 'package:mobile_pos/Screens/hrm/shift/repo/shift_repo.dart';
import 'package:mobile_pos/generated/l10n.dart' as l;
import 'package:icons_plus/icons_plus.dart';
import '../../../constant.dart';
import '../widgets/set_time.dart';
import 'package:intl/intl.dart';
class AddNewShift extends ConsumerStatefulWidget {
const AddNewShift({super.key, this.isEdit = false, this.shift});
final bool isEdit;
final ShiftData? shift;
@override
ConsumerState<AddNewShift> createState() => _AddNewShiftState();
}
class _AddNewShiftState extends ConsumerState<AddNewShift> {
final GlobalKey<FormState> _key = GlobalKey();
// *** CHANGED: Shift Name is now managed by selectedShift String? ***
String? selectedShift;
// shiftNameController is now unnecessary for dropdown, but kept for cleanup clarity
final TextEditingController shiftNameController = TextEditingController();
String? selectedBreakStatus;
String? _selectedStatus;
final startTimeController = TextEditingController();
final endTimeController = TextEditingController();
final startBreakTimeController = TextEditingController();
final endBreakTimeController = TextEditingController();
final List<String> _statusOptions = ['Active', 'Inactive'];
final List<String> _shiftNameOptions = ['Morning', "Day", "Evening", 'Night']; // Fixed list for dropdown
String formatTime(String time24) {
try {
DateTime parsedTime = DateFormat("HH:mm:ss").parse(time24);
return DateFormat("h:mm a").format(parsedTime);
} catch (e) {
return time24;
}
}
@override
void initState() {
super.initState();
if (widget.isEdit && widget.shift != null) {
final data = widget.shift!;
// *** FIX: Use Shift Name from data for selectedShift state ***
selectedShift = data.name;
selectedBreakStatus = data.breakStatus == 'yes' ? "Yes" : "No";
_selectedStatus = data.status == 1 ? 'Active' : 'Inactive';
startTimeController.text = formatTime(data.startTime ?? '');
endTimeController.text = formatTime(data.endTime ?? '');
startBreakTimeController.text = formatTime(data.startBreakTime ?? '');
endBreakTimeController.text = formatTime(data.endBreakTime ?? '');
} else {
_selectedStatus = 'Active';
selectedBreakStatus = 'No';
}
}
@override
void dispose() {
// shiftNameController.dispose(); // No longer needed if using dropdown
startTimeController.dispose();
endTimeController.dispose();
startBreakTimeController.dispose();
endBreakTimeController.dispose();
super.dispose();
}
Future<void> _saveOrUpdateShift(BuildContext context) async {
if (!_key.currentState!.validate()) return;
// Check if the required state variables are set by the dropdowns
if (selectedShift == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Please select Shift Name')),
);
return;
}
final repo = ShiftRepo();
final int apiStatus = _selectedStatus == 'Active' ? 1 : 0;
// Ensure times are in HH:mm format before sending (setTime uses HH:mm a,
// but the repo needs to handle conversion to HH:mm:ss if necessary)
if (widget.isEdit) {
await repo.updateShift(
ref: ref,
context: context,
id: widget.shift!.id!.round(),
shiftName: selectedShift!,
breakStatus: selectedBreakStatus!,
startTime: startTimeController.text,
endTime: endTimeController.text,
breakStartTime: startBreakTimeController.text.isEmpty ? null : startBreakTimeController.text,
breakEndTime: endBreakTimeController.text.isEmpty ? null : endBreakTimeController.text,
status: apiStatus.toString(),
);
} else {
await repo.createShift(
ref: ref,
context: context,
shiftName: selectedShift!,
breakStatus: selectedBreakStatus ?? "No",
startTime: startTimeController.text,
endTime: endTimeController.text,
breakStartTime: startBreakTimeController.text.isEmpty ? null : startBreakTimeController.text,
breakEndTime: endBreakTimeController.text.isEmpty ? null : endBreakTimeController.text,
status: apiStatus.toString(),
);
}
}
void _resetForm() {
_key.currentState?.reset();
setState(() {
selectedShift = null; // Reset dropdown selection
selectedBreakStatus = 'No';
_selectedStatus = 'Active';
// Reset time controllers
startTimeController.clear();
endTimeController.clear();
startBreakTimeController.clear();
endBreakTimeController.clear();
});
}
@override
Widget build(BuildContext context) {
final _lang = l.S.of(context);
return Scaffold(
backgroundColor: kWhite,
appBar: AppBar(
centerTitle: true,
title: Text(
widget.isEdit ? _lang.editShift : _lang.addNewShift,
),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(1),
child: Divider(height: 2, color: kBackgroundColor),
),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Form(
key: _key,
child: Column(
children: [
// 1. Shift Name (FIXED: Changed back to Dropdown)
DropdownButtonFormField<String>(
value: selectedShift,
icon: const Icon(Icons.keyboard_arrow_down, color: kNeutral800),
decoration: InputDecoration(labelText: _lang.shiftName, hintText: _lang.selectOne),
items: _shiftNameOptions
.map((String value) => DropdownMenuItem(value: value, child: Text(value)))
.toList(),
onChanged: (String? newValue) => setState(() => selectedShift = newValue),
validator: (value) => value == null ? _lang.pleaseSelectAShift : null,
),
const SizedBox(height: 20),
// 2. Break Status Dropdown
DropdownButtonFormField<String>(
value: selectedBreakStatus,
icon: const Icon(Icons.keyboard_arrow_down, color: kNeutral800),
decoration: InputDecoration(
labelText: _lang.breakStatus,
hintText: _lang.selectOne,
),
items: const ['Yes', 'No']
.map((String value) => DropdownMenuItem(value: value, child: Text(value)))
.toList(),
onChanged: (String? newValue) => setState(() => selectedBreakStatus = newValue),
validator: (value) => value == null ? _lang.pleaseSelectBreakStatus : null,
),
const SizedBox(height: 20),
// 3. Status Dropdown
DropdownButtonFormField<String>(
value: _selectedStatus,
icon: const Icon(Icons.keyboard_arrow_down, color: kNeutral800),
decoration: InputDecoration(
labelText: _lang.status,
hintText: _lang.selectOne,
),
items:
_statusOptions.map((String value) => DropdownMenuItem(value: value, child: Text(value))).toList(),
onChanged: (String? newValue) => setState(() => _selectedStatus = newValue),
validator: (value) => value == null ? _lang.pleaseSelectAStatus : null,
),
const SizedBox(height: 20),
// 4. Start Time & End Time
Row(
children: [
Expanded(
child: TextFormField(
onTap: () => setTime(startTimeController, context),
readOnly: true,
controller: startTimeController,
validator: (value) => value.isNullOrEmpty() ? _lang.startTimeIsRequired : null,
decoration: InputDecoration(
labelText: _lang.startTime,
hintText: _lang.enterStartTime,
suffixIcon: Icon(
AntDesign.clock_circle_outline,
size: 18,
color: kNeutral800,
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: TextFormField(
readOnly: true,
controller: endTimeController,
onTap: () => setTime(endTimeController, context),
validator: (value) => value.isNullOrEmpty() ? _lang.endTimeIsRequired : null,
decoration: InputDecoration(
labelText: _lang.endTime,
hintText: _lang.enterEndTime,
suffixIcon: Icon(
AntDesign.clock_circle_outline,
size: 18,
color: kNeutral800,
),
),
),
),
],
),
const SizedBox(height: 20),
// 5. Break Time (Conditional)
if (selectedBreakStatus == 'Yes')
Row(
children: [
Expanded(
child: TextFormField(
onTap: () => setTime(startBreakTimeController, context),
readOnly: true,
controller: startBreakTimeController,
decoration: InputDecoration(
labelText: _lang.startBreakTime,
hintText: _lang.enterBreakTime,
suffixIcon: Icon(
AntDesign.clock_circle_outline,
size: 18,
color: kNeutral800,
),
),
),
),
const SizedBox(width: 16),
Expanded(
child: TextFormField(
readOnly: true,
controller: endBreakTimeController,
onTap: () => setTime(endBreakTimeController, context),
decoration: InputDecoration(
labelText: _lang.endBreakTime,
hintText: _lang.enterBreakTime,
suffixIcon: Icon(
AntDesign.clock_circle_outline,
size: 18,
color: kNeutral800,
),
),
),
),
],
),
const SizedBox(height: 20),
// 6. Action Buttons
Row(
children: [
Expanded(
child: OutlinedButton(onPressed: _resetForm, child: Text(_lang.resets)),
),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton(
onPressed: () => _saveOrUpdateShift(context),
child: Text(widget.isEdit ? _lang.update : _lang.save)),
),
],
),
],
),
),
),
);
}
}
// Extension to help with simple validation checks
extension on String? {
bool isNullOrEmpty() => this == null || this!.isEmpty;
}

View File

@@ -0,0 +1,8 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:mobile_pos/Screens/hrm/department/model/department_list_model.dart';
import 'package:mobile_pos/Screens/hrm/department/repo/department_repo.dart';
import 'package:mobile_pos/Screens/hrm/shift/Model/shift_list_model.dart';
import 'package:mobile_pos/Screens/hrm/shift/repo/shift_repo.dart';
ShiftRepo repo = ShiftRepo();
final shiftListProvider = FutureProvider<ShiftListModel>((ref) => repo.fetchAllShifts());

View File

@@ -0,0 +1,185 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:http/http.dart' as http;
import 'package:mobile_pos/Screens/hrm/shift/Model/shift_list_model.dart';
import 'package:mobile_pos/Screens/hrm/shift/provider/shift_list_provider.dart';
import '../../../../Const/api_config.dart';
import '../../../../http_client/custome_http_client.dart';
import '../../../../http_client/customer_http_client_get.dart';
import 'package:intl/intl.dart';
class ShiftRepo {
String convertTo24HourFormat(String time12h) {
// Example input: "8:00 PM"
final dateTime = DateFormat('h:mm a').parse(time12h);
return DateFormat('HH:mm').format(dateTime); // Output: "20:00"
}
/// Fetch all shifts
Future<ShiftListModel> fetchAllShifts() async {
CustomHttpClientGet clientGet = CustomHttpClientGet(client: http.Client());
final uri = Uri.parse('${APIConfig.url}/shifts');
final response = await clientGet.get(url: uri);
if (response.statusCode == 200) {
final parsedData = jsonDecode(response.body);
return ShiftListModel.fromJson(parsedData);
} else {
throw Exception('Failed to fetch Shift list');
}
}
/// Create new shift (form-data format)
Future<void> createShift({
required WidgetRef ref,
required BuildContext context,
required String shiftName,
required String breakStatus,
required String startTime,
required String endTime,
required String status,
String? breakStartTime,
String? breakEndTime,
}) async {
final uri = Uri.parse('${APIConfig.url}/shifts');
// Build form-data map (Postman style)
final Map<String, String> body = {
'name': shiftName,
'start_time': convertTo24HourFormat(startTime),
'status': status,
'end_time': convertTo24HourFormat(endTime),
'break_status': breakStatus.toLowerCase(), // yes/no
if (breakStartTime != null && breakStartTime.isNotEmpty) 'start_break_time': breakStatus.toLowerCase() == 'no' ? '' : convertTo24HourFormat(breakStartTime),
if (breakEndTime != null && breakEndTime.isNotEmpty) 'end_break_time': breakStatus.toLowerCase() == 'no' ? '' : convertTo24HourFormat(breakEndTime),
};
try {
EasyLoading.show(status: 'Saving...');
CustomHttpClient client = CustomHttpClient(client: http.Client(), context: context, ref: ref);
print("POST Data For: $body");
final response = await client.post(
url: uri,
body: body, // form-data
addContentTypeInHeader: false, // important for form-data
);
EasyLoading.dismiss();
final parsed = jsonDecode(response.body);
if (response.statusCode == 200 || response.statusCode == 201) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Shift created successfully!')),
);
ref.refresh(shiftListProvider);
Navigator.pop(context);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to create shift: ${parsed['message']}')),
);
}
} catch (e) {
EasyLoading.dismiss();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
}
}
/// Update existing shift (form-data format)
Future<void> updateShift({
required WidgetRef ref,
required BuildContext context,
required int id,
required String shiftName,
required String breakStatus,
required String startTime,
required String endTime,
required String status,
String? breakStartTime,
String? breakEndTime,
}) async {
final uri = Uri.parse('${APIConfig.url}/shifts/$id');
final Map<String, String> body = {
"_method": 'put',
'name': shiftName,
'status': status,
'start_time': convertTo24HourFormat(startTime),
'end_time': convertTo24HourFormat(endTime),
'break_status': breakStatus.toLowerCase(),
if (breakStartTime != null && breakStartTime.isNotEmpty) 'start_break_time': breakStatus.toLowerCase() == 'no' ? '' : convertTo24HourFormat(breakStartTime),
if (breakEndTime != null && breakEndTime.isNotEmpty) 'end_break_time': breakStatus.toLowerCase() == 'no' ? '' : convertTo24HourFormat(breakEndTime),
};
try {
EasyLoading.show(status: 'Updating...');
CustomHttpClient client = CustomHttpClient(client: http.Client(), context: context, ref: ref);
final response = await client.post(
url: uri,
body: body,
addContentTypeInHeader: false, // still form-data
);
EasyLoading.dismiss();
final parsed = jsonDecode(response.body);
if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Shift updated successfully!')),
);
ref.refresh(shiftListProvider);
Navigator.pop(context);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to update shift: ${parsed['message']}')),
);
}
} catch (e) {
EasyLoading.dismiss();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
}
}
/// Delete shift
Future<bool> deleteShift({
required WidgetRef ref,
required BuildContext context,
required int id,
}) async {
final uri = Uri.parse('${APIConfig.url}/shifts/$id');
try {
EasyLoading.show(status: 'Deleting...');
CustomHttpClient client = CustomHttpClient(client: http.Client(), context: context, ref: ref);
final response = await client.delete(url: uri);
EasyLoading.dismiss();
if (response.statusCode == 200) {
return true;
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to delete shift: ${response.body}')),
);
}
} catch (e) {
EasyLoading.dismiss();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $e')),
);
}
return false;
}
}

View File

@@ -0,0 +1,287 @@
// ignore_for_file: use_build_context_synchronously
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hugeicons/hugeicons.dart';
import 'package:intl/intl.dart';
import 'package:mobile_pos/Screens/hrm/shift/add_new_shift.dart';
import 'package:mobile_pos/Screens/hrm/shift/provider/shift_list_provider.dart';
import 'package:mobile_pos/Screens/hrm/shift/repo/shift_repo.dart';
import 'package:mobile_pos/Screens/hrm/shift/Model/shift_list_model.dart';
import 'package:mobile_pos/Screens/hrm/widgets/global_search_appbar.dart';
import 'package:mobile_pos/Screens/hrm/widgets/model_bottom_sheet.dart';
import 'package:mobile_pos/Screens/hrm/widgets/deleteing_alart_dialog.dart';
import 'package:mobile_pos/constant.dart';
import '../../../generated/l10n.dart' as lang;
import '../../../service/check_user_role_permission_provider.dart';
import '../../../widgets/empty_widget/_empty_widget.dart';
class ShiftScreen extends ConsumerStatefulWidget {
const ShiftScreen({super.key});
@override
ConsumerState<ShiftScreen> createState() => _ShiftScreenState();
}
class _ShiftScreenState extends ConsumerState<ShiftScreen> {
bool _isSearch = false;
final _searchController = TextEditingController();
String _searchQuery = '';
@override
void dispose() {
_searchController.dispose();
super.dispose();
}
Future<void> _refreshList() async {
ref.refresh(shiftListProvider);
}
/// ✅ Convert "HH:mm" to "hh:mm a" format safely
String _formatToAmPm(String? time) {
if (time == null || time.isEmpty || !time.contains(':')) return 'n/a';
try {
final date = DateFormat("HH:mm").parse(time);
return DateFormat("hh:mm a").format(date);
} catch (_) {
return time; // fallback
}
}
@override
Widget build(BuildContext context) {
final asyncShifts = ref.watch(shiftListProvider);
final permissionService = PermissionService(ref);
final _lang = lang.S.of(context);
return Scaffold(
backgroundColor: Colors.white,
appBar: GlobalSearchAppBar(
isSearch: _isSearch,
onSearchToggle: () => setState(() {
_isSearch = !_isSearch;
_searchQuery = '';
_searchController.clear();
}),
title: _lang.shift,
controller: _searchController,
onChanged: (query) {
setState(() {
_searchQuery = query.toLowerCase().trim();
});
},
),
body: RefreshIndicator(
onRefresh: _refreshList,
child: asyncShifts.when(
data: (shiftList) {
if (!permissionService.hasPermission(Permit.shiftsRead.value)) {
return const Center(child: PermitDenyWidget());
}
final allShifts = shiftList.data ?? [];
// ✅ Apply search filter
final shifts = allShifts.where((shift) {
final name = (shift.name ?? '').toLowerCase();
return name.contains(_searchQuery);
}).toList();
if (shifts.isEmpty) {
return Center(child: Text(_lang.noShiftFound));
}
return ListView.separated(
padding: EdgeInsets.zero,
itemCount: shifts.length,
separatorBuilder: (_, __) => const Divider(
color: kBackgroundColor,
height: 2,
),
itemBuilder: (_, index) {
final shift = shifts[index];
return _buildShiftItem(context, shift);
},
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (e, _) => Center(child: Text('Error: $e')),
),
),
bottomNavigationBar: permissionService.hasPermission(Permit.shiftsCreate.value)
? Padding(
padding: const EdgeInsets.all(16),
child: ElevatedButton.icon(
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const AddNewShift(isEdit: false),
),
),
icon: const Icon(Icons.add, color: Colors.white),
label: Text(_lang.addShift),
),
)
: null,
);
}
Widget _buildShiftItem(BuildContext context, ShiftData shift) {
final theme = Theme.of(context);
return InkWell(
onTap: () => viewModalSheet(
context: context,
item: {
lang.S.of(context).shift: shift.name ?? 'n/a',
lang.S.of(context).startTime: _formatToAmPm(shift.startTime),
lang.S.of(context).endTime: _formatToAmPm(shift.endTime),
lang.S.of(context).breakTime: (shift.startBreakTime == null ||
shift.startBreakTime!.isEmpty ||
shift.endBreakTime == null ||
shift.endBreakTime!.isEmpty)
? 'N/A'
: "${_formatToAmPm(shift.startBreakTime)} - ${_formatToAmPm(shift.endBreakTime)}",
lang.S.of(context).breakDuration: shift.breakTime?.isEmpty ?? true ? 'N/A' : shift.breakTime!,
lang.S.of(context).status: shift.status == 1 ? lang.S.of(context).active : lang.S.of(context).inactive,
},
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 13.5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
shift.name ?? 'n/a',
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 10),
if (shift.breakStatus == 'yes')
Text.rich(
TextSpan(
text: '${lang.S.of(context).breakTime}: ',
style: const TextStyle(color: kNeutral800),
children: [
TextSpan(
text: '${_formatToAmPm(shift.startBreakTime)} - ${_formatToAmPm(shift.endBreakTime)}',
style: const TextStyle(color: kTitleColor),
),
],
),
style: theme.textTheme.bodyMedium,
),
const SizedBox(height: 20.5),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_buildTimeColumn(_formatToAmPm(shift.startTime), lang.S.of(context).startTime, theme),
_buildTimeColumn(_formatToAmPm(shift.endTime), lang.S.of(context).endTime, theme),
_buildActionButtons(context, shift),
],
),
],
),
),
);
}
Widget _buildTimeColumn(String time, String label, ThemeData theme) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
time,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 8),
Text(
label,
style: theme.textTheme.bodyMedium?.copyWith(color: kNeutral800),
),
],
);
}
Widget _buildActionButtons(BuildContext context, shift) {
final _lang = lang.S.of(context);
final permissionService = PermissionService(ref);
return Column(
children: [
GestureDetector(
onTap: () {
if (!permissionService.hasPermission(Permit.shiftsUpdate.value)) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.red,
content: Text(_lang.youDoNotToHavePermissionToUpdateShift),
),
);
return;
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddNewShift(
isEdit: true,
shift: shift,
),
),
);
},
child: const HugeIcon(
icon: HugeIcons.strokeRoundedPencilEdit02,
color: kSuccessColor,
size: 20,
),
),
const SizedBox(height: 8),
GestureDetector(
onTap: () async {
if (!permissionService.hasPermission(Permit.shiftsDelete.value)) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.red,
content: Text(lang.S.of(context).youDoNotToHavePermissionToDeleteShift),
),
);
return;
}
final _lang = lang.S.of(context);
final confirm = await showDeleteConfirmationDialog(
itemName: _lang.shift,
context: context,
);
if (confirm) {
EasyLoading.show(status: _lang.deleting);
final repo = ShiftRepo();
try {
final result = await repo.deleteShift(id: shift.id, ref: ref, context: context);
if (result) {
ref.refresh(shiftListProvider);
EasyLoading.showSuccess(_lang.deletedSuccessFully);
} else {
EasyLoading.showError('Failed to delete the Shift');
}
} catch (e) {
EasyLoading.showError('Error: $e');
} finally {
EasyLoading.dismiss();
}
}
},
child: const HugeIcon(
icon: HugeIcons.strokeRoundedDelete03,
color: Colors.red,
size: 20,
),
),
],
);
}
}