first commit
This commit is contained in:
280
lib/Screens/hrm/holiday/add_new_holiday.dart
Normal file
280
lib/Screens/hrm/holiday/add_new_holiday.dart
Normal file
@@ -0,0 +1,280 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart'; // Import Riverpod
|
||||
import 'package:iconly/iconly.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
import '../../../constant.dart';
|
||||
|
||||
// --- Riverpod Imports (Assuming these paths are correct) ---
|
||||
import 'package:mobile_pos/Screens/hrm/holiday/model/holiday_list_model.dart';
|
||||
import 'package:mobile_pos/Screens/hrm/holiday/repo/holiday_repo.dart';
|
||||
// -----------------------------------------------------------
|
||||
|
||||
// Accept optional HolidayData for editing (renamed from isEdit)
|
||||
class AddNewHoliday extends ConsumerStatefulWidget {
|
||||
final HolidayData? holidayData;
|
||||
// Changed constructor to use key and remove isEdit
|
||||
const AddNewHoliday({super.key, this.holidayData});
|
||||
|
||||
@override
|
||||
ConsumerState<AddNewHoliday> createState() => _AddNewHolidayState();
|
||||
}
|
||||
|
||||
class _AddNewHolidayState extends ConsumerState<AddNewHoliday> {
|
||||
final nameController = TextEditingController();
|
||||
final startDateController = TextEditingController();
|
||||
final endDateController = TextEditingController();
|
||||
final descriptionController = TextEditingController();
|
||||
final GlobalKey<FormState> _key = GlobalKey();
|
||||
|
||||
// Variables to hold parsed dates for comparison/API formatting
|
||||
DateTime? _selectedStartDate;
|
||||
DateTime? _selectedEndDate;
|
||||
|
||||
bool get isEditing => widget.holidayData != null;
|
||||
final DateFormat _displayFormat = DateFormat('dd/MM/yyyy');
|
||||
final DateFormat _apiFormat = DateFormat('yyyy-MM-dd'); // API typically needs YYYY-MM-DD
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (isEditing) {
|
||||
final holiday = widget.holidayData!;
|
||||
nameController.text = holiday.name ?? '';
|
||||
descriptionController.text = holiday.description ?? '';
|
||||
|
||||
try {
|
||||
if (holiday.startDate != null) {
|
||||
_selectedStartDate = DateTime.parse(holiday.startDate!);
|
||||
startDateController.text = _displayFormat.format(_selectedStartDate!);
|
||||
}
|
||||
if (holiday.endDate != null) {
|
||||
_selectedEndDate = DateTime.parse(holiday.endDate!);
|
||||
endDateController.text = _displayFormat.format(_selectedEndDate!);
|
||||
}
|
||||
} catch (e) {
|
||||
// Handle date parsing failure if API format is inconsistent
|
||||
debugPrint('Error parsing date for editing: $e');
|
||||
startDateController.text = holiday.startDate ?? '';
|
||||
endDateController.text = holiday.endDate ?? '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
nameController.dispose();
|
||||
startDateController.dispose();
|
||||
endDateController.dispose();
|
||||
descriptionController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _selectDate(BuildContext context, TextEditingController controller, bool isStart) async {
|
||||
DateTime initialDate = DateTime.now();
|
||||
if (isStart) {
|
||||
initialDate = _selectedStartDate ?? initialDate;
|
||||
} else {
|
||||
initialDate = _selectedEndDate ?? _selectedStartDate ?? initialDate;
|
||||
}
|
||||
|
||||
final DateTime? picked = await showDatePicker(
|
||||
initialDate: initialDate,
|
||||
firstDate: DateTime(2015, 8),
|
||||
lastDate: DateTime(2101),
|
||||
context: context,
|
||||
);
|
||||
|
||||
if (picked != null) {
|
||||
setState(() {
|
||||
controller.text = _displayFormat.format(picked);
|
||||
if (isStart) {
|
||||
_selectedStartDate = picked;
|
||||
// Auto-adjust end date if it is before the new start date
|
||||
if (_selectedEndDate != null && _selectedEndDate!.isBefore(picked)) {
|
||||
_selectedEndDate = picked;
|
||||
endDateController.text = _displayFormat.format(picked);
|
||||
}
|
||||
} else {
|
||||
_selectedEndDate = picked;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _submit() async {
|
||||
if (_key.currentState!.validate()) {
|
||||
// Validate that dates are not null and end date is not before start date
|
||||
if (_selectedStartDate == null || _selectedEndDate == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(lang.S.of(context).pleaseSelectValidStartAndEndDates)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_selectedEndDate!.isBefore(_selectedStartDate!)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(lang.S.of(context).endDateCannotBeBeforeStartDate)),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final repo = HolidayRepo();
|
||||
final String apiStartDate = _apiFormat.format(_selectedStartDate!);
|
||||
final String apiEndDate = _apiFormat.format(_selectedEndDate!);
|
||||
|
||||
if (isEditing) {
|
||||
// --- UPDATE HOLIDAY ---
|
||||
await repo.updateHolidays(
|
||||
ref: ref,
|
||||
context: context,
|
||||
id: widget.holidayData!.id!.toInt(),
|
||||
name: nameController.text,
|
||||
startDate: apiStartDate,
|
||||
endDate: apiEndDate,
|
||||
description: descriptionController.text,
|
||||
);
|
||||
} else {
|
||||
// --- CREATE HOLIDAY ---
|
||||
await repo.createHolidays(
|
||||
ref: ref,
|
||||
context: context,
|
||||
name: nameController.text,
|
||||
startDate: apiStartDate,
|
||||
endDate: apiEndDate,
|
||||
description: descriptionController.text,
|
||||
);
|
||||
}
|
||||
// Note: The repo functions already handle Navigator.pop(context) and SnackBar
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _lang = lang.S.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: kWhite,
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
isEditing ? _lang.editHoliday : _lang.addNewHoliday,
|
||||
),
|
||||
bottom: const PreferredSize(
|
||||
preferredSize: Size.fromHeight(1),
|
||||
child: Divider(
|
||||
height: 2,
|
||||
color: kBackgroundColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Form(
|
||||
key: _key,
|
||||
child: Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: nameController,
|
||||
decoration: InputDecoration(
|
||||
labelText: _lang.name,
|
||||
hintText: _lang.enterHolidayName,
|
||||
),
|
||||
validator: (value) => value!.isEmpty ? _lang.pleaseEnterHolidayName : null,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
keyboardType: TextInputType.name,
|
||||
readOnly: true,
|
||||
controller: startDateController,
|
||||
decoration: InputDecoration(
|
||||
labelText: _lang.startDate,
|
||||
hintText: _lang.pleaseEnterDate,
|
||||
suffixIcon: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
visualDensity: const VisualDensity(horizontal: -4, vertical: -4),
|
||||
onPressed: () => _selectDate(context, startDateController, true),
|
||||
icon: const Icon(IconlyLight.calendar, size: 22),
|
||||
),
|
||||
),
|
||||
validator: (value) => value!.isEmpty ? _lang.pleaseSelectStartDate : null,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
keyboardType: TextInputType.name,
|
||||
readOnly: true,
|
||||
controller: endDateController,
|
||||
decoration: InputDecoration(
|
||||
labelText: _lang.endDate,
|
||||
hintText: _lang.pleaseEnterDate,
|
||||
suffixIcon: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
visualDensity: const VisualDensity(horizontal: -4, vertical: -4),
|
||||
onPressed: () => _selectDate(context, endDateController, false),
|
||||
icon: const Icon(IconlyLight.calendar, size: 22),
|
||||
),
|
||||
),
|
||||
validator: (value) {
|
||||
if (value!.isEmpty) {
|
||||
return _lang.pleaseEnterEndDate;
|
||||
}
|
||||
if (_selectedStartDate != null &&
|
||||
_selectedEndDate != null &&
|
||||
_selectedEndDate!.isBefore(_selectedStartDate!)) {
|
||||
return _lang.endDateBeforeStartDate;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
TextFormField(
|
||||
controller: descriptionController,
|
||||
decoration: InputDecoration(
|
||||
labelText: _lang.description,
|
||||
hintText: '${_lang.enterDescription}...',
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: OutlinedButton(
|
||||
onPressed: () {
|
||||
// Reset functionality
|
||||
setState(() {
|
||||
_key.currentState?.reset();
|
||||
nameController.clear();
|
||||
descriptionController.clear();
|
||||
startDateController.clear();
|
||||
endDateController.clear();
|
||||
_selectedStartDate = null;
|
||||
_selectedEndDate = null;
|
||||
});
|
||||
},
|
||||
child: Text(_lang.resets),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: _submit, // Call the submission function
|
||||
child: Text(
|
||||
isEditing ? _lang.update : _lang.save,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
324
lib/Screens/hrm/holiday/holiday_list_screen.dart
Normal file
324
lib/Screens/hrm/holiday/holiday_list_screen.dart
Normal file
@@ -0,0 +1,324 @@
|
||||
// File: holiday_list.dart
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:hugeicons/hugeicons.dart';
|
||||
import 'package:intl/intl.dart'; // Import for DateFormat
|
||||
|
||||
// --- Local Imports (Assuming correct paths) ---
|
||||
import 'package:mobile_pos/Screens/hrm/holiday/add_new_holiday.dart';
|
||||
import 'package:mobile_pos/Screens/hrm/widgets/model_bottom_sheet.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import '../../../service/check_user_role_permission_provider.dart'; // PermissionService
|
||||
import '../../../widgets/empty_widget/_empty_widget.dart'; // PermitDenyWidget (Assuming this exists)
|
||||
import '../widgets/deleteing_alart_dialog.dart';
|
||||
import '../widgets/global_search_appbar.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
|
||||
// --- Data Layer Imports ---
|
||||
import 'package:mobile_pos/Screens/hrm/holiday/model/holiday_list_model.dart';
|
||||
import 'package:mobile_pos/Screens/hrm/holiday/repo/holiday_repo.dart';
|
||||
import 'package:mobile_pos/Screens/hrm/holiday/provider/holidays_list_provider.dart';
|
||||
|
||||
class HolidayList extends ConsumerStatefulWidget {
|
||||
const HolidayList({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<HolidayList> createState() => _HolidayListState();
|
||||
}
|
||||
|
||||
class _HolidayListState extends ConsumerState<HolidayList> {
|
||||
final TextEditingController _searchController = TextEditingController();
|
||||
List<HolidayData> _filteredHolidays = [];
|
||||
bool _isSearch = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_searchController.addListener(_onSearchChanged);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.removeListener(_onSearchChanged);
|
||||
_searchController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// --- Date Formatting Utility (FIX) ---
|
||||
String _formatDateForDisplay(String? date) {
|
||||
if (date == null || date.isEmpty) return 'N/A';
|
||||
try {
|
||||
// Parse YYYY-MM-DD from API
|
||||
final dateTime = DateFormat('yyyy-MM-dd').parse(date);
|
||||
// Format to dd MMM, yyyy (e.g., 02 Jun, 2025)
|
||||
return DateFormat('dd MMM, yyyy').format(dateTime);
|
||||
} catch (_) {
|
||||
return date;
|
||||
}
|
||||
}
|
||||
// --- End Date Formatting Utility ---
|
||||
|
||||
void _onSearchChanged() {
|
||||
setState(() {
|
||||
// Trigger rebuild to re-apply filter
|
||||
});
|
||||
}
|
||||
|
||||
void _filterHolidays(List<HolidayData> allHolidays) {
|
||||
final query = _searchController.text.toLowerCase().trim();
|
||||
if (query.isEmpty) {
|
||||
_filteredHolidays = allHolidays;
|
||||
} else {
|
||||
_filteredHolidays = allHolidays.where((holiday) {
|
||||
final nameMatch = (holiday.name ?? '').toLowerCase().contains(query);
|
||||
final branchMatch = (holiday.branch?.name ?? '').toLowerCase().contains(query);
|
||||
final startDateMatch = (holiday.startDate ?? '').toLowerCase().contains(query);
|
||||
final endDateMatch = (holiday.endDate ?? '').toLowerCase().contains(query);
|
||||
|
||||
return nameMatch || branchMatch || startDateMatch || endDateMatch;
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _lang = lang.S.of(context);
|
||||
final holidayListAsync = ref.watch(holidayListProvider);
|
||||
// Assuming PermissionService and Permit enum exist globally or are accessible
|
||||
final permissionService = PermissionService(ref);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: GlobalSearchAppBar(
|
||||
isSearch: _isSearch,
|
||||
onSearchToggle: () {
|
||||
setState(() {
|
||||
_isSearch = !_isSearch;
|
||||
if (!_isSearch) {
|
||||
_searchController.clear();
|
||||
}
|
||||
});
|
||||
},
|
||||
title: _lang.holidayList,
|
||||
controller: _searchController,
|
||||
onChanged: (query) {
|
||||
// Handled by _searchController.addListener
|
||||
},
|
||||
),
|
||||
body: holidayListAsync.when(
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error: (err, stack) => Center(
|
||||
child: Text('Failed to load holidays: $err'),
|
||||
),
|
||||
data: (model) {
|
||||
// Check read permission
|
||||
if (!permissionService.hasPermission(Permit.holidaysRead.value)) {
|
||||
return const Center(child: PermitDenyWidget());
|
||||
}
|
||||
final allHolidays = model.data ?? [];
|
||||
|
||||
// Apply filter
|
||||
_filterHolidays(allHolidays);
|
||||
|
||||
if (_filteredHolidays.isEmpty) {
|
||||
return Center(
|
||||
child: Text(
|
||||
_searchController.text.isEmpty
|
||||
? _lang.noHolidayFound
|
||||
: '${_lang.noHolidayFundMatching}"${_searchController.text}".',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: _filteredHolidays.length,
|
||||
separatorBuilder: (_, __) => const Divider(
|
||||
color: kBackgroundColor,
|
||||
height: 1.5,
|
||||
),
|
||||
itemBuilder: (_, index) => _buildHolidayItem(
|
||||
context: context,
|
||||
ref: ref,
|
||||
holiday: _filteredHolidays[index], // Use filtered list
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
bottomNavigationBar: permissionService.hasPermission(Permit.holidaysCreate.value)
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () => Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const AddNewHoliday(),
|
||||
),
|
||||
),
|
||||
icon: const Icon(Icons.add, color: Colors.white),
|
||||
label: Text(_lang.addHoliday),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
// --- Helper Methods ---
|
||||
|
||||
Widget _buildHolidayItem({
|
||||
required BuildContext context,
|
||||
required WidgetRef ref,
|
||||
required HolidayData holiday,
|
||||
}) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
// FIX: Formatting the dates for display
|
||||
final String startDateDisplay = _formatDateForDisplay(holiday.startDate);
|
||||
final String endDateDisplay = _formatDateForDisplay(holiday.endDate);
|
||||
final String description = holiday.description ?? 'N/A';
|
||||
|
||||
return InkWell(
|
||||
onTap: () => viewModalSheet(
|
||||
context: context,
|
||||
item: {
|
||||
lang.S.of(context).name: holiday.name ?? 'N/A',
|
||||
lang.S.of(context).startDate: startDateDisplay,
|
||||
lang.S.of(context).endDate: endDateDisplay,
|
||||
},
|
||||
description: description,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 13.5),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
holiday.name ?? 'n/a',
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
_buildActionButtons(context, ref, holiday),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_buildTimeColumn(
|
||||
time: startDateDisplay, // Use formatted date
|
||||
label: lang.S.of(context).startDate,
|
||||
theme: theme,
|
||||
),
|
||||
_buildTimeColumn(
|
||||
time: endDateDisplay, // Use formatted date
|
||||
label: lang.S.of(context).endDate,
|
||||
theme: theme,
|
||||
),
|
||||
const SizedBox(width: 50),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTimeColumn({
|
||||
required String time,
|
||||
required String label,
|
||||
required ThemeData theme,
|
||||
}) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
time,
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
label,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: kNeutral800,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButtons(BuildContext context, WidgetRef ref, HolidayData holiday) {
|
||||
final permissionService = PermissionService(ref);
|
||||
return Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (!permissionService.hasPermission(Permit.holidaysUpdate.value)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Colors.red,
|
||||
content: Text(lang.S.of(context).youDoNotHavePermissionToUpgradeHoliday),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AddNewHoliday(holidayData: holiday),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const HugeIcon(
|
||||
icon: HugeIcons.strokeRoundedPencilEdit02,
|
||||
color: kSuccessColor,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (!permissionService.hasPermission(Permit.holidaysDelete.value)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
backgroundColor: Colors.red,
|
||||
content: Text("You do not have permission to delete Holidays."),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (holiday.id != null) {
|
||||
_showDeleteConfirmationDialog(context, ref, holiday.id!);
|
||||
}
|
||||
},
|
||||
child: const HugeIcon(
|
||||
icon: HugeIcons.strokeRoundedDelete03,
|
||||
color: Colors.red,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _showDeleteConfirmationDialog(BuildContext context, WidgetRef ref, num id) async {
|
||||
bool result = await showDeleteConfirmationDialog(
|
||||
context: context,
|
||||
itemName: lang.S.of(context).holiday,
|
||||
);
|
||||
|
||||
if (result) {
|
||||
final repo = HolidayRepo();
|
||||
await repo.deleteHolidays(id: id, context: context, ref: ref);
|
||||
// The repo method handles refreshing the list
|
||||
}
|
||||
}
|
||||
}
|
||||
103
lib/Screens/hrm/holiday/model/holiday_list_model.dart
Normal file
103
lib/Screens/hrm/holiday/model/holiday_list_model.dart
Normal file
@@ -0,0 +1,103 @@
|
||||
class HolidayListModel {
|
||||
HolidayListModel({
|
||||
this.message,
|
||||
this.data,
|
||||
});
|
||||
|
||||
HolidayListModel.fromJson(dynamic json) {
|
||||
message = json['message'];
|
||||
if (json['data'] != null) {
|
||||
data = [];
|
||||
json['data'].forEach((v) {
|
||||
data?.add(HolidayData.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
String? message;
|
||||
List<HolidayData>? 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 HolidayData {
|
||||
HolidayData({
|
||||
this.id,
|
||||
this.businessId,
|
||||
this.branchId,
|
||||
this.name,
|
||||
this.startDate,
|
||||
this.endDate,
|
||||
this.description,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
this.branch,
|
||||
});
|
||||
|
||||
HolidayData.fromJson(dynamic json) {
|
||||
id = json['id'];
|
||||
businessId = json['business_id'];
|
||||
branchId = json['branch_id'];
|
||||
name = json['name'];
|
||||
startDate = json['start_date'];
|
||||
endDate = json['end_date'];
|
||||
description = json['description'];
|
||||
createdAt = json['created_at'];
|
||||
updatedAt = json['updated_at'];
|
||||
branch = json['branch'] != null ? Branch.fromJson(json['branch']) : null;
|
||||
}
|
||||
num? id;
|
||||
num? businessId;
|
||||
num? branchId;
|
||||
String? name;
|
||||
String? startDate;
|
||||
String? endDate;
|
||||
String? description;
|
||||
String? createdAt;
|
||||
String? updatedAt;
|
||||
Branch? branch;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['id'] = id;
|
||||
map['business_id'] = businessId;
|
||||
map['branch_id'] = branchId;
|
||||
map['name'] = name;
|
||||
map['start_date'] = startDate;
|
||||
map['end_date'] = endDate;
|
||||
map['description'] = description;
|
||||
map['created_at'] = createdAt;
|
||||
map['updated_at'] = updatedAt;
|
||||
if (branch != null) {
|
||||
map['branch'] = branch?.toJson();
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
class Branch {
|
||||
Branch({
|
||||
this.id,
|
||||
this.name,
|
||||
});
|
||||
|
||||
Branch.fromJson(dynamic json) {
|
||||
id = json['id'];
|
||||
name = json['name'];
|
||||
}
|
||||
num? id;
|
||||
String? name;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['id'] = id;
|
||||
map['name'] = name;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -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/holiday/model/holiday_list_model.dart';
|
||||
import 'package:mobile_pos/Screens/hrm/holiday/repo/holiday_repo.dart';
|
||||
|
||||
final repo = HolidayRepo();
|
||||
final holidayListProvider = FutureProvider<HolidayListModel>((ref) => repo.fetchAllHolidays());
|
||||
171
lib/Screens/hrm/holiday/repo/holiday_repo.dart
Normal file
171
lib/Screens/hrm/holiday/repo/holiday_repo.dart
Normal file
@@ -0,0 +1,171 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:mobile_pos/Screens/hrm/holiday/model/holiday_list_model.dart';
|
||||
import 'package:mobile_pos/Screens/hrm/holiday/provider/holidays_list_provider.dart';
|
||||
|
||||
import '../../../../Const/api_config.dart';
|
||||
import '../../../../http_client/custome_http_client.dart';
|
||||
import '../../../../http_client/customer_http_client_get.dart';
|
||||
|
||||
class HolidayRepo {
|
||||
///---------------- FETCH HOLIDAYS ----------------///
|
||||
Future<HolidayListModel> fetchAllHolidays() async {
|
||||
CustomHttpClientGet clientGet = CustomHttpClientGet(client: http.Client());
|
||||
final uri = Uri.parse('${APIConfig.url}/holidays');
|
||||
|
||||
final response = await clientGet.get(url: uri);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final parsedData = jsonDecode(response.body);
|
||||
return HolidayListModel.fromJson(parsedData);
|
||||
} else {
|
||||
throw Exception('Failed to fetch Holidays list');
|
||||
}
|
||||
}
|
||||
|
||||
///---------------- CREATE HOLIDAY ----------------///
|
||||
Future<void> createHolidays({
|
||||
required WidgetRef ref,
|
||||
required BuildContext context,
|
||||
required String name,
|
||||
required String startDate,
|
||||
required String endDate,
|
||||
required String description,
|
||||
}) async {
|
||||
final uri = Uri.parse('${APIConfig.url}/holidays'); // Modified endpoint
|
||||
|
||||
final requestBody = jsonEncode({
|
||||
'name': name,
|
||||
'start_date': startDate, // Field names match the model/API
|
||||
'end_date': endDate, // Field names match the model/API
|
||||
'description': description,
|
||||
});
|
||||
|
||||
try {
|
||||
EasyLoading.show(status: 'Creating Holiday...');
|
||||
CustomHttpClient customHttpClient = CustomHttpClient(client: http.Client(), context: context, ref: ref);
|
||||
|
||||
var responseData = await customHttpClient.post(
|
||||
url: uri,
|
||||
addContentTypeInHeader: true,
|
||||
body: requestBody,
|
||||
);
|
||||
|
||||
final parsedData = jsonDecode(responseData.body);
|
||||
EasyLoading.dismiss();
|
||||
|
||||
if (responseData.statusCode == 200) {
|
||||
ref.refresh(holidayListProvider); // Refresh the list after creation
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(parsedData['message'] ?? 'Holiday created successfully')),
|
||||
);
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Holiday creation failed: ${parsedData['message'] ?? 'Unknown error'}')),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
EasyLoading.dismiss();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('An error occurred: $error')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
///---------------- UPDATE HOLIDAY ----------------///
|
||||
Future<void> updateHolidays({
|
||||
// Renamed function
|
||||
required WidgetRef ref,
|
||||
required BuildContext context,
|
||||
required int id,
|
||||
required String name,
|
||||
required String startDate,
|
||||
required String endDate,
|
||||
required String description,
|
||||
}) async {
|
||||
final uri = Uri.parse('${APIConfig.url}/holidays/$id'); // Modified endpoint
|
||||
|
||||
final requestBody = jsonEncode({
|
||||
'_method': 'put', // Required for PUT/PATCH via POST on some APIs
|
||||
'name': name,
|
||||
'start_date': startDate,
|
||||
'end_date': endDate,
|
||||
'description': description,
|
||||
});
|
||||
|
||||
try {
|
||||
EasyLoading.show(status: 'Updating Holiday...');
|
||||
CustomHttpClient customHttpClient = CustomHttpClient(client: http.Client(), context: context, ref: ref);
|
||||
|
||||
var responseData = await customHttpClient.post(
|
||||
// Assuming the PUT is sent via POST with '_method': 'put'
|
||||
url: uri,
|
||||
addContentTypeInHeader: true,
|
||||
body: requestBody,
|
||||
);
|
||||
|
||||
final parsedData = jsonDecode(responseData.body);
|
||||
EasyLoading.dismiss();
|
||||
|
||||
if (responseData.statusCode == 200) {
|
||||
ref.refresh(holidayListProvider); // Refresh the list after update
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(parsedData['message'] ?? 'Holiday updated successfully')),
|
||||
);
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Holiday update failed: ${parsedData['message'] ?? 'Unknown error'}')),
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
EasyLoading.dismiss();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('An error occurred: $error')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
///---------------- DELETE HOLIDAY ----------------///
|
||||
Future<bool> deleteHolidays({
|
||||
// Renamed function
|
||||
required num id, // Changed to num to match model's id type
|
||||
required BuildContext context,
|
||||
required WidgetRef ref,
|
||||
}) async {
|
||||
try {
|
||||
EasyLoading.show(status: 'Deleting...');
|
||||
final url = Uri.parse('${APIConfig.url}/holidays/$id'); // Modified endpoint
|
||||
CustomHttpClient customHttpClient = CustomHttpClient(ref: ref, context: context, client: http.Client());
|
||||
final response = await customHttpClient.delete(url: url);
|
||||
|
||||
EasyLoading.dismiss();
|
||||
if (response.statusCode == 200) {
|
||||
ref.refresh(holidayListProvider); // Refresh the list after deletion
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Holiday deleted successfully')),
|
||||
);
|
||||
return true;
|
||||
} else {
|
||||
final parsedData = jsonDecode(response.body);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Deletion failed: ${parsedData['message'] ?? 'Unknown error'}')),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
EasyLoading.dismiss();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('An error occurred during deletion: $error')),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user