QueenFruits/Mobile Operasional/lib/features/supplier/presentation/screens/edit_supplier_screen.dart

566 lines
23 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:niogu_app/core/components/top_back_bar_app.dart';
import 'package:niogu_app/core/constants/app_color.dart';
import 'package:niogu_app/core/constants/app_font_size.dart';
import 'package:niogu_app/core/router/app_route.dart';
import 'package:niogu_app/core/utils/log_message.dart';
import 'package:niogu_app/core/widgets/custom_snackbar.dart';
import 'package:niogu_app/core/widgets/custom_text_form_field.dart';
import 'package:niogu_app/features/supplier/domain/entities/supplier.dart';
import 'package:niogu_app/features/supplier/presentation/providers/supplier_provider.dart';
import 'package:niogu_app/features/supplier/presentation/widgets/edit_supplier_shimmer.dart';
import 'package:niogu_app/features/supplier/presentation/widgets/supplier_activity.dart';
import 'package:sizer/sizer.dart';
class EditSupplierScreen extends ConsumerStatefulWidget {
final String supplierId;
const EditSupplierScreen({super.key, required this.supplierId});
@override
ConsumerState<EditSupplierScreen> createState() => _EditSupplierScreenState();
}
class _EditSupplierScreenState extends ConsumerState<EditSupplierScreen> {
final GlobalKey<FormState> _formKey = GlobalKey();
final TextEditingController _nameController = TextEditingController();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _phoneController = TextEditingController();
List<SupplierActivities> _activities = [];
bool _isLoading = true;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
Future.delayed(const Duration(milliseconds: 800), () {
_fetchSupplier();
});
});
}
Future<void> _fetchSupplier() async {
try {
final supplierRepository = ref.read(supplierRepositoryProvider);
final supplier = await supplierRepository.fetchSupplierById(
widget.supplierId,
);
_nameController.text = supplier.name;
_emailController.text = supplier.email;
_phoneController.text = supplier.phoneNumber;
_activities = supplier.activities;
setState(() {
_isLoading = false;
});
} catch (e, st) {
if (!mounted) return;
setState(() {
_isLoading = false;
});
LogMessage.log.e(e.toString(), error: e, stackTrace: st);
CustomSnackbar.showError(context, "Ups, terjadi kesalahan");
context.pop();
}
}
Future<void> _updateSupplier() async {
if (!_formKey.currentState!.validate()) return;
try {
await ref
.read(supplierControllerProvider.notifier)
.saveSupplier(
UpsertSupplier(
id: widget.supplierId,
name: _nameController.text.trim(),
email: _emailController.text.trim(),
phoneNumber: _phoneController.text.trim(),
),
);
if (!mounted) return;
CustomSnackbar.showSuccess(context, "Pemasok berhasil diubah");
context.pop();
} catch (e, st) {
LogMessage.log.e(e.toString(), error: e, stackTrace: st);
CustomSnackbar.showError(context, "Ups, terjadi kesalahan");
context.pop();
}
}
Future<void> _deleteSupplier() async {
try {
ref
.read(supplierControllerProvider.notifier)
.deleteSupplier(widget.supplierId);
if (!mounted) return;
CustomSnackbar.showSuccess(context, "Pemasok berhasil dihapus");
Navigator.pop(context);
context.pop();
} catch (e, st) {
LogMessage.log.e(e.toString(), error: e, stackTrace: st);
CustomSnackbar.showError(context, "Ups, terjadi kesalahan");
Navigator.pop(context);
context.pop();
}
}
@override
void dispose() {
// TODO: implement dispose
_nameController.dispose();
_emailController.dispose();
_phoneController.dispose();
super.dispose();
}
void _showDeleteConfirmation(BuildContext context) {
final bool isTablet = 100.w >= 600;
final supplierControllerState = ref.watch(supplierControllerProvider);
showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4.5.w),
),
elevation: 0,
backgroundColor: Colors.transparent,
child: Container(
padding: EdgeInsets.all(6.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(4.5.w),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: EdgeInsets.all(4.w),
decoration: BoxDecoration(
color: Colors.red[50],
shape: BoxShape.circle,
),
child: Icon(
Icons.warning_amber_rounded,
color: Colors.red,
size: 10.w,
),
),
SizedBox(height: 2.h),
Text(
"Hapus Pemasok?",
style: TextStyle(
fontSize: AppFontSize.medium.sp,
fontWeight: FontWeight.bold,
color: Colors.black87,
),
),
SizedBox(height: 1.h),
Text(
"Tindakan ini tidak dapat dibatalkan. Semua data profil pemasok akan dihapus dari sistem.",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: isTablet
? (AppFontSize.medium - 1.25).sp
: (AppFontSize.small - 1.25).sp,
color: Colors.grey[600],
height: 1.5,
),
),
SizedBox(height: 3.h),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey.shade300,
padding: EdgeInsets.symmetric(vertical: 1.5.h),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(2.5.w),
),
elevation: 0,
),
child: Text(
"Batal",
style: TextStyle(
color: Colors.grey[800],
fontWeight: FontWeight.bold,
fontSize: AppFontSize.medium.sp,
),
),
),
),
SizedBox(width: 3.w),
Expanded(
child: ElevatedButton(
onPressed: supplierControllerState.isLoading
? null
: _deleteSupplier,
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 1.5.h),
backgroundColor: Colors.red,
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(2.5.w),
),
disabledBackgroundColor: Colors.grey.shade300,
),
child: Text(
"Ya, Hapus",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: AppFontSize.medium.sp,
),
),
),
),
],
),
],
),
),
);
},
);
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
final bool isTablet = 100.w >= 600;
final int totalActivity = _activities.length;
final int displayActivityCount = totalActivity > 3 ? 3 : totalActivity;
final supplierControllerState = ref.watch(supplierControllerProvider);
return SafeArea(
top: false,
bottom: true,
right: false,
left: false,
child: Scaffold(
backgroundColor: Colors.white,
appBar: TopBackBarApp(
title: "Detail Pemasok",
onTap: () => context.pop(),
),
body: _isLoading
? const EditSupplierShimmer()
: RefreshIndicator(
onRefresh: () async {
await Future.delayed(const Duration(milliseconds: 800));
await _fetchSupplier();
},
color: AppColor.primaryColor,
backgroundColor: Colors.white,
strokeWidth: 2.5,
triggerMode: RefreshIndicatorTriggerMode.onEdge,
child: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
children: [
Center(
child: Container(
width: 25.w,
height: 25.w,
decoration: BoxDecoration(
color: Colors.grey[100],
shape: BoxShape.circle,
border: Border.all(
color: Colors.grey.shade200,
width: 2,
),
),
child: Icon(
Icons.person_rounded,
size: 12.w,
color: Colors.grey[400],
),
),
),
Container(
color: Colors.white,
padding: EdgeInsets.fromLTRB(5.w, 0, 5.w, 3.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
CustomTextFormField(
label: "Nama Pemasok",
controller: _nameController,
hint: "Masukkan nama",
validator: (value) {
if (value == null || value.isEmpty) {
return "Nama pemasok belum diisi";
}
return null;
},
prefixIcon: Icons.person_outline,
),
SizedBox(height: 2.h),
CustomTextFormField(
label: "Email",
controller: _emailController,
hint: "Masukkan email (opsional)",
keyboardType: TextInputType.emailAddress,
prefixIcon: Icons.email_outlined,
),
SizedBox(height: 2.h),
CustomTextFormField(
label: "No. Handphone / WA",
controller: _phoneController,
hint:
"Masukkan no handphone / wa (opsional)",
keyboardType: TextInputType.phone,
prefixIcon: Icons.phone_android_outlined,
),
],
),
),
_buildSectionHeader("Aktivitas Dengan Pemasok"),
_activities.isEmpty
? Container(
width: double.infinity,
padding: EdgeInsets.symmetric(
vertical: 4.h,
),
margin: EdgeInsets.symmetric(
horizontal: 5.w,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(
2.5.w,
),
border: Border.all(
color: Colors.grey.shade100,
),
),
child: Column(
children: [
Icon(
Icons.history_rounded,
color: Colors.grey[300],
size: 10.w,
),
SizedBox(height: 1.5.h),
Text(
"Belum ada riwayat aktivitas dengan pemasok",
style: TextStyle(
fontSize:
(AppFontSize.medium - 1.25).sp,
color: Colors.grey[500],
),
),
],
),
)
: Column(
children: [
ListView.builder(
shrinkWrap: true,
physics:
const NeverScrollableScrollPhysics(),
padding: EdgeInsets.symmetric(
horizontal: 5.w,
),
itemCount: displayActivityCount,
itemBuilder: (context, index) {
final activity = _activities[index];
return SupplierActivity(
activity: activity,
);
},
),
if (totalActivity > 3)
Padding(
padding: EdgeInsets.symmetric(
horizontal: 5.w,
),
child: InkWell(
onTap: () {
context.pushNamed(
AppRoute.supplierActivityScreen,
extra: {
"activities": _activities,
},
);
},
borderRadius: BorderRadius.circular(
2.5.w,
),
child: Container(
padding: EdgeInsets.symmetric(
vertical: 1.5.h,
),
decoration: BoxDecoration(
border: Border.all(
color: Colors.grey[300]!,
),
borderRadius:
BorderRadius.circular(
2.5.w,
),
),
child: Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Text(
"Lihat ${totalActivity - 3} Aktivitas Lainnya",
style: TextStyle(
color: Colors.grey[600],
fontWeight:
FontWeight.bold,
fontSize: isTablet
? (AppFontSize.medium -
1.25)
.sp
: (AppFontSize.small -
1.25)
.sp,
),
),
SizedBox(width: 2.w),
Icon(
Icons
.keyboard_arrow_down_rounded,
color: Colors.grey[600],
size: 5.w,
),
],
),
),
),
),
],
),
SizedBox(height: 5.h),
Padding(
padding: EdgeInsets.symmetric(horizontal: 5.w),
child: TextButton.icon(
onPressed: () =>
_showDeleteConfirmation(context),
style: TextButton.styleFrom(
foregroundColor: Colors.red[700],
padding: EdgeInsets.symmetric(
vertical: 1.5.h,
horizontal: 4.w,
),
),
icon: Icon(
Icons.delete_outline_rounded,
size: 5.w,
),
label: Text(
"Hapus Pelanggan Ini",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: isTablet
? (AppFontSize.medium - 1.25).sp
: (AppFontSize.small - 1.25).sp,
),
),
),
),
SizedBox(height: 4.h),
],
),
),
),
),
bottomNavigationBar: _isLoading
? null
: Container(
padding: EdgeInsets.all(5.w),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, -5),
),
],
),
child: ElevatedButton(
onPressed: supplierControllerState.isLoading
? null
: _updateSupplier,
style: ElevatedButton.styleFrom(
backgroundColor: AppColor.primaryColor,
minimumSize: Size(double.infinity, 6.5.h),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(2.5.w),
),
disabledBackgroundColor: Colors.grey.shade300,
),
child: Text(
"Perbarui Informasi Pemasok",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: AppFontSize.medium.sp,
color: Colors.white,
),
),
),
),
),
);
},
);
}
Widget _buildSectionHeader(String title) {
return Padding(
padding: EdgeInsets.fromLTRB(5.w, 3.h, 5.w, 1.5.h),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: (AppFontSize.medium - 1.25).sp,
),
),
],
),
);
}
}