first commit
This commit is contained in:
178
lib/Screens/product_model/add_products_models.dart
Normal file
178
lib/Screens/product_model/add_products_models.dart
Normal file
@@ -0,0 +1,178 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:mobile_pos/Screens/product_model/provider/models_provider.dart';
|
||||
import 'package:mobile_pos/Screens/product_model/repo/product_models_repo.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
import '../../constant.dart';
|
||||
import '../../http_client/custome_http_client.dart';
|
||||
import '../../service/check_user_role_permission_provider.dart';
|
||||
import 'model/product_models_model.dart';
|
||||
|
||||
class AddProductModel extends ConsumerStatefulWidget {
|
||||
const AddProductModel({super.key, this.editData});
|
||||
|
||||
final Data? editData;
|
||||
|
||||
bool get isEditMode => editData != null;
|
||||
|
||||
@override
|
||||
ConsumerState<AddProductModel> createState() => _AddProductModelState();
|
||||
}
|
||||
|
||||
class _AddProductModelState extends ConsumerState<AddProductModel> {
|
||||
late final TextEditingController nameController;
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
bool isActive = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
nameController = TextEditingController();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (widget.isEditMode) {
|
||||
setState(() {
|
||||
nameController.text = widget.editData?.name ?? '';
|
||||
isActive = widget.editData?.status == 1;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
nameController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final permissionService = PermissionService(ref);
|
||||
final _lang = lang.S.of(context);
|
||||
return Scaffold(
|
||||
backgroundColor: kWhite,
|
||||
appBar: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(widget.isEditMode ? _lang.editModel : _lang.addNewModel),
|
||||
),
|
||||
body: Form(
|
||||
key: formKey,
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
children: [
|
||||
// Model Name Input
|
||||
TextFormField(
|
||||
controller: nameController,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return _lang.pleaseEnterValidName;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.always,
|
||||
labelText: _lang.modelName,
|
||||
hintText: _lang.enterModelName,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Status Toggle
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(_lang.status),
|
||||
SizedBox(
|
||||
height: 32,
|
||||
width: 44,
|
||||
child: FittedBox(
|
||||
child: Switch.adaptive(
|
||||
value: isActive,
|
||||
onChanged: (value) => setState(() => isActive = value),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Submit Button
|
||||
ElevatedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
disabledBackgroundColor: theme.colorScheme.primary.withAlpha(40),
|
||||
),
|
||||
onPressed: () async {
|
||||
if (widget.editData == null) {
|
||||
if (!permissionService.hasPermission(Permit.productModelsCreate.value)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Colors.red,
|
||||
content: Text(_lang.youDoNotHavePermissionToCreateModel),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!permissionService.hasPermission(Permit.productModelsUpdate.value)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Colors.red,
|
||||
content: Text(_lang.youDoNotHavePermissionToUpdateModel),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (formKey.currentState?.validate() ?? false) {
|
||||
final repo = ProductModelsRepo();
|
||||
final data = CreateModelsModel(
|
||||
name: nameController.text,
|
||||
status: isActive ? '1' : '0',
|
||||
modelId: widget.editData?.id.toString(),
|
||||
);
|
||||
|
||||
bool success =
|
||||
widget.isEditMode ? await repo.updateModels(data: data) : await repo.createModels(data: data);
|
||||
|
||||
if (success) {
|
||||
EasyLoading.showSuccess(
|
||||
widget.isEditMode ? _lang.modelUpdateSuccessfully : _lang.modelCreatedSuccessfully,
|
||||
);
|
||||
ref.refresh(fetchModelListProvider);
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
_lang.save,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.primaryContainer,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CreateModelsModel {
|
||||
CreateModelsModel({
|
||||
this.modelId,
|
||||
this.name,
|
||||
this.status,
|
||||
});
|
||||
String? modelId;
|
||||
String? name;
|
||||
String? status;
|
||||
}
|
||||
64
lib/Screens/product_model/model/product_models_model.dart
Normal file
64
lib/Screens/product_model/model/product_models_model.dart
Normal file
@@ -0,0 +1,64 @@
|
||||
class ProductModelsModel {
|
||||
ProductModelsModel({
|
||||
this.message,
|
||||
this.data,
|
||||
});
|
||||
|
||||
ProductModelsModel.fromJson(dynamic json) {
|
||||
message = json['message'];
|
||||
if (json['data'] != null) {
|
||||
data = [];
|
||||
json['data'].forEach((v) {
|
||||
data?.add(Data.fromJson(v));
|
||||
});
|
||||
}
|
||||
}
|
||||
String? message;
|
||||
List<Data>? 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 Data {
|
||||
Data({
|
||||
this.id,
|
||||
this.name,
|
||||
this.businessId,
|
||||
this.status,
|
||||
this.createdAt,
|
||||
this.updatedAt,
|
||||
});
|
||||
|
||||
Data.fromJson(dynamic json) {
|
||||
id = json['id'];
|
||||
name = json['name'];
|
||||
businessId = json['business_id'];
|
||||
status = json['status'];
|
||||
createdAt = json['created_at'];
|
||||
updatedAt = json['updated_at'];
|
||||
}
|
||||
num? id;
|
||||
String? name;
|
||||
num? businessId;
|
||||
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['status'] = status;
|
||||
map['created_at'] = createdAt;
|
||||
map['updated_at'] = updatedAt;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
243
lib/Screens/product_model/product_model_list.dart
Normal file
243
lib/Screens/product_model/product_model_list.dart
Normal file
@@ -0,0 +1,243 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:iconly/iconly.dart';
|
||||
import 'package:mobile_pos/Screens/product_model/provider/models_provider.dart';
|
||||
import 'package:mobile_pos/Screens/product_model/repo/product_models_repo.dart';
|
||||
import 'package:mobile_pos/constant.dart';
|
||||
import 'package:mobile_pos/core/theme/_app_colors.dart';
|
||||
import 'package:mobile_pos/generated/l10n.dart' as lang;
|
||||
import 'package:mobile_pos/widgets/empty_widget/_empty_widget.dart';
|
||||
|
||||
import '../../GlobalComponents/glonal_popup.dart';
|
||||
import '../../http_client/custome_http_client.dart';
|
||||
import '../../service/check_user_role_permission_provider.dart';
|
||||
import 'add_products_models.dart';
|
||||
|
||||
class ProductModelList extends StatefulWidget {
|
||||
const ProductModelList({super.key, required this.fromProductList});
|
||||
|
||||
final bool fromProductList;
|
||||
|
||||
@override
|
||||
ProductModelListState createState() => ProductModelListState();
|
||||
}
|
||||
|
||||
class ProductModelListState extends State<ProductModelList> {
|
||||
String search = '';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _lang = lang.S.of(context);
|
||||
return GlobalPopup(
|
||||
child: Scaffold(
|
||||
backgroundColor: kWhite,
|
||||
appBar: AppBar(
|
||||
title: Text(_lang.models),
|
||||
iconTheme: const IconThemeData(color: Colors.black),
|
||||
centerTitle: true,
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 0.0,
|
||||
),
|
||||
body: Consumer(builder: (context, ref, __) {
|
||||
final modelData = ref.watch(fetchModelListProvider);
|
||||
final permissionService = PermissionService(ref);
|
||||
if (!permissionService.hasPermission(Permit.productModelsRead.value)) {
|
||||
return Center(child: PermitDenyWidget());
|
||||
}
|
||||
return SingleChildScrollView(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: lang.S.of(context).search,
|
||||
prefixIcon: Icon(
|
||||
Icons.search,
|
||||
color: kGreyTextColor.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
search = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10.0),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => AddProductModel()),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
height: 48.0,
|
||||
width: 48,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(5.0),
|
||||
border: Border.all(color: kMainColor),
|
||||
),
|
||||
child: const Icon(Icons.add, color: kMainColor),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
modelData.when(
|
||||
data: (snapshot) {
|
||||
final allModels = snapshot.data ?? [];
|
||||
final filteredModels = allModels.where((model) {
|
||||
final name = (model.name ?? '').toLowerCase();
|
||||
return name.contains(search.toLowerCase());
|
||||
}).toList();
|
||||
|
||||
if (filteredModels.isEmpty) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 50),
|
||||
child: EmptyWidgetUpdated(
|
||||
message: TextSpan(text: lang.S.of(context).noDataFound),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: filteredModels.length,
|
||||
physics: AlwaysScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemBuilder: (context, i) {
|
||||
final model = filteredModels[i];
|
||||
return ListCardWidget(
|
||||
onSelect: widget.fromProductList
|
||||
? () {}
|
||||
: () {
|
||||
Navigator.pop(context, model);
|
||||
},
|
||||
title: model.name?.toString() ?? 'n/a',
|
||||
onDelete: () async {
|
||||
if (!permissionService.hasPermission(Permit.productModelsDelete.value)) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Colors.red,
|
||||
content: Text(_lang.youDoNotHavePermissionDeleteModel),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
ProductModelsRepo repo = ProductModelsRepo();
|
||||
bool success = await repo.deleteModel(id: model.id?.toString() ?? '');
|
||||
if (success) {
|
||||
ref.refresh(fetchModelListProvider);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(_lang.deletedSuccessFully)),
|
||||
);
|
||||
}
|
||||
},
|
||||
onEdit: () async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AddProductModel(editData: model),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
error: (_, __) => const SizedBox.shrink(),
|
||||
loading: () => const Center(
|
||||
child: SizedBox(height: 40, width: 40, child: CircularProgressIndicator()),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ListCardWidget extends StatelessWidget {
|
||||
const ListCardWidget({
|
||||
super.key,
|
||||
this.onEdit,
|
||||
this.onDelete,
|
||||
required this.title,
|
||||
this.onSelect,
|
||||
});
|
||||
|
||||
final void Function()? onEdit;
|
||||
final void Function()? onDelete;
|
||||
final void Function()? onSelect;
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return InkWell(
|
||||
onTap: onSelect,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(color: Color(0xffD8D8D8)),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.bodyLarge,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
IconButton.filledTonal(
|
||||
onPressed: onEdit,
|
||||
style: IconButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
backgroundColor: Colors.white.withValues(alpha: 0.25),
|
||||
),
|
||||
visualDensity: const VisualDensity(horizontal: -2, vertical: -2),
|
||||
iconSize: 20,
|
||||
icon: const Icon(
|
||||
IconlyLight.edit,
|
||||
color: DAppColors.kSecondary,
|
||||
),
|
||||
),
|
||||
IconButton.filledTonal(
|
||||
onPressed: onDelete,
|
||||
style: IconButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
backgroundColor: Colors.white.withValues(alpha: 0.25),
|
||||
),
|
||||
visualDensity: const VisualDensity(horizontal: -2, vertical: -2),
|
||||
iconSize: 20,
|
||||
icon: const Icon(
|
||||
IconlyLight.delete,
|
||||
color: Colors.redAccent,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// const Spacer(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
11
lib/Screens/product_model/provider/models_provider.dart
Normal file
11
lib/Screens/product_model/provider/models_provider.dart
Normal file
@@ -0,0 +1,11 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../model/product_models_model.dart';
|
||||
import '../repo/product_models_repo.dart';
|
||||
|
||||
ProductModelsRepo repo = ProductModelsRepo();
|
||||
|
||||
// fetch models list
|
||||
final fetchModelListProvider = FutureProvider<ProductModelsModel>((ref) {
|
||||
return repo.fetchModelsList();
|
||||
});
|
||||
144
lib/Screens/product_model/repo/product_models_repo.dart
Normal file
144
lib/Screens/product_model/repo/product_models_repo.dart
Normal file
@@ -0,0 +1,144 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_easyloading/flutter_easyloading.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:nb_utils/nb_utils.dart';
|
||||
|
||||
import '../../../Const/api_config.dart';
|
||||
import '../../../Repository/constant_functions.dart';
|
||||
import '../../../http_client/customer_http_client_get.dart';
|
||||
import '../model/product_models_model.dart';
|
||||
import '../add_products_models.dart';
|
||||
|
||||
class ProductModelsRepo {
|
||||
// Create Model
|
||||
Future<bool> createModels({required CreateModelsModel data}) async {
|
||||
EasyLoading.show(status: 'Creating Models...');
|
||||
final url = Uri.parse('${APIConfig.url}/product-models');
|
||||
|
||||
// Create a multipart request
|
||||
var request = http.MultipartRequest('POST', url);
|
||||
request.headers.addAll({
|
||||
'Accept': 'application/json',
|
||||
'Authorization': await getAuthToken(),
|
||||
});
|
||||
request.fields['name'] = data.name.toString();
|
||||
request.fields['status'] = data.status.toString();
|
||||
try {
|
||||
var response = await request.send();
|
||||
|
||||
var responseData = await http.Response.fromStream(response);
|
||||
EasyLoading.dismiss();
|
||||
print('Model create ${response.statusCode}');
|
||||
print('Model create ${data.status}');
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
return true;
|
||||
} else {
|
||||
var data = jsonDecode(responseData.body);
|
||||
EasyLoading.showError(data['message'] ?? 'Failed to create Model');
|
||||
print('Error: ${data['message']}');
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
EasyLoading.dismiss();
|
||||
EasyLoading.showError('Error: ${e.toString()}');
|
||||
print('Error: ${e.toString()}');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// models List
|
||||
Future<ProductModelsModel> fetchModelsList() async {
|
||||
CustomHttpClientGet clientGet = CustomHttpClientGet(client: http.Client());
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
String token = prefs.getString('token') ?? '';
|
||||
final url = Uri.parse('${APIConfig.url}/product-models');
|
||||
try {
|
||||
var response = await clientGet.get(url: url);
|
||||
EasyLoading.dismiss();
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var jsonData = jsonDecode(response.body);
|
||||
return ProductModelsModel.fromJson(jsonData);
|
||||
} else {
|
||||
var data = jsonDecode(response.body);
|
||||
EasyLoading.showError(data['message'] ?? 'Failed to fetch models');
|
||||
throw Exception(data['message'] ?? 'Failed to fetch models');
|
||||
}
|
||||
} catch (e) {
|
||||
// Hide loading indicator and show error
|
||||
EasyLoading.dismiss();
|
||||
EasyLoading.showError('Error: ${e.toString()}');
|
||||
throw Exception('Error: ${e.toString()}');
|
||||
}
|
||||
}
|
||||
|
||||
// Update Model
|
||||
Future<bool> updateModels({required CreateModelsModel data}) async {
|
||||
EasyLoading.show(status: 'Updating Model...');
|
||||
final url = Uri.parse('${APIConfig.url}/product-models/${data.modelId}');
|
||||
|
||||
// Create a multipart request
|
||||
var request = http.MultipartRequest('POST', url);
|
||||
request.headers.addAll({
|
||||
'Accept': 'application/json',
|
||||
'Authorization': await getAuthToken(),
|
||||
});
|
||||
request.fields['name'] = data.name.toString();
|
||||
request.fields['status'] = data.status.toString();
|
||||
request.fields['_method'] = 'put';
|
||||
try {
|
||||
var response = await request.send();
|
||||
|
||||
var responseData = await http.Response.fromStream(response);
|
||||
EasyLoading.dismiss();
|
||||
print(response.statusCode);
|
||||
if (response.statusCode == 200) {
|
||||
return true;
|
||||
} else {
|
||||
var data = jsonDecode(responseData.body);
|
||||
EasyLoading.showError(data['message'] ?? 'Failed to update');
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
EasyLoading.dismiss();
|
||||
EasyLoading.showError('Error: ${e.toString()}');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// delete warehouse
|
||||
Future<bool> deleteModel({required String id}) async {
|
||||
EasyLoading.show(status: 'Processing');
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
String token = prefs.getString('token') ?? '';
|
||||
final url = Uri.parse('${APIConfig.url}/product-models/$id');
|
||||
final headers = {
|
||||
'Accept': 'application/json',
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
try {
|
||||
var response = await http.delete(
|
||||
url,
|
||||
headers: headers,
|
||||
);
|
||||
EasyLoading.dismiss();
|
||||
print(response.statusCode);
|
||||
if (response.statusCode == 200) {
|
||||
return true;
|
||||
} else {
|
||||
var data = jsonDecode(response.body);
|
||||
EasyLoading.showError(data['message'] ?? 'Failed to delete');
|
||||
print(data['message']);
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
EasyLoading.dismiss();
|
||||
EasyLoading.showError('Error: ${e.toString()}');
|
||||
print(e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user