firebase setup and stock update api

This commit is contained in:
Vaibhav 2024-10-12 12:47:52 +05:30
parent 6792c73cde
commit f9b0eb40f3
25 changed files with 1094 additions and 457 deletions

View File

@ -1,5 +1,9 @@
plugins {
id "com.android.application"
// START: FlutterFire Configuration
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
// END: FlutterFire Configuration
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
@ -24,7 +28,7 @@ if (flutterVersionName == null) {
}
android {
namespace = "com.example.cheminova"
namespace = "com.example.cheminova_rd"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
@ -35,7 +39,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.cheminova"
applicationId = "com.example.cheminova_rd"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdk = flutter.minSdkVersion
@ -56,3 +60,10 @@ android {
flutter {
source = "../.."
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.10.0'
implementation 'androidx.activity:activity:1.8.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
}

View File

@ -0,0 +1,29 @@
{
"project_info": {
"project_number": "181536932687",
"project_id": "cheminova-rd",
"storage_bucket": "cheminova-rd.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:181536932687:android:8f3b0977f4b4dcc56f7b3a",
"android_client_info": {
"package_name": "com.example.cheminova_rd"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyAYZUuqmSoE9MrTvOaavYYjdRoQFD-XnFc"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View File

@ -1,49 +1,62 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.cheminova_rd">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<!--
Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin.
-->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT" />
<data android:mimeType="text/plain" />
</intent>
</queries>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:label="cheminova"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/ic_launcher"
android:label="cheminova">
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true"
android:hardwareAccelerated="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
<!--
Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
to determine the Window background behind the Flutter UI.
-->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<!--
Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java
-->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>

View File

@ -1,4 +1,4 @@
package com.example.cheminova
package com.example.cheminova_rd
import io.flutter.embedding.android.FlutterActivity

View File

@ -1,3 +1,8 @@
buildscript {
dependencies {
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.10'
}
}
allprojects {
repositories {
google()

View File

@ -19,6 +19,10 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
// START: FlutterFire Configuration
id "com.google.gms.google-services" version "4.3.15" apply false
id "com.google.firebase.crashlytics" version "2.8.1" apply false
// END: FlutterFire Configuration
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
}

View File

@ -17,7 +17,8 @@ class GetPlacedOrderController extends GetxController {
Get.put(OrderPlacedController());
final GetSingleOrderPlacedService _getSingleOrderPlacedService =
GetSingleOrderPlacedService();
var placedOrders = <PlacedOrderList>[].obs;
List<PlacedOrderList> placedOrders = <PlacedOrderList>[].obs;
List<PlacedOrderList> filterOrder = <PlacedOrderList>[].obs;
var products = <Product>[].obs;
var isLoading = false.obs;
@ -31,6 +32,8 @@ class GetPlacedOrderController extends GetxController {
RxInt currentPage = 1.obs;
RxBool hasMoreOrders = true.obs;
RxString productStatus = 'All'.obs;
Future<void> getOrders() async {
if (isLoading.value || !hasMoreOrders.value) return;
@ -44,7 +47,6 @@ class GetPlacedOrderController extends GetxController {
if (response.statusCode == 200) {
placedOrders.addAll(data.placedOrders ?? []);
debugPrint('allOrders.length: ${placedOrders.toJson()}');
currentPage++;
hasMoreOrders.value = placedOrders.length < data.totalOrders!.toInt();
} else {
@ -82,6 +84,18 @@ class GetPlacedOrderController extends GetxController {
}
}
void updateProductStatus(dynamic newValue) {
productStatus.value = newValue;
filterOrder.clear();
if (productStatus.value == 'All') {
filterOrder.addAll(placedOrders);
} else {
filterOrder.addAll(placedOrders
.where((order) => order.status.toString().toLowerCase() == productStatus.value.toLowerCase())
.toList());
}
}
// // Optional: Reset the pagination if needed
// void resetPagination() {
// _getOrderPlacedService.resetPagination();

View File

@ -0,0 +1 @@
import 'dart:convert'; import 'package:cheminova/services/api_service.dart'; import 'package:cheminova/utils/api_urls.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; import '../models/InventoryManagementResponse.dart'; class InventoryManagementController extends GetxController { final apiService = ApiService(); Future<InventoryManagementResponse> getProducts() async { final response = await apiService.get(ApiUrls.inventoryManangementOrdersStock); if (response.statusCode == 200) { return InventoryManagementResponse.fromJson(response.data); }else{ return InventoryManagementResponse(); } } }

View File

@ -0,0 +1,32 @@
import 'package:cheminova/services/api_service.dart';
import 'package:get/get.dart';
import '../models/NotificationListResponse.dart';
import '../utils/api_urls.dart';
class NotificationController extends GetxController {
final ApiService _apiClient = ApiService();
RxList<Notifications> notificationsList = <Notifications>[].obs;
RxBool isLoading = false.obs;
@override
void onInit() {
super.onInit();
getNotification();
}
Future<void> getNotification() async {
isLoading.value = true;
try {
final response = await _apiClient.get(ApiUrls.getNotificationUrl);
isLoading.value = false;
if (response.statusCode == 200) {
final data = NotificationListResponse.fromJson(response.data);
notificationsList.value = data.notifications ?? [];
}
} catch (e) {
isLoading.value = false;
Get.snackbar('Error', 'Failed to fetch notifications');
}
}
}

69
lib/firebase_options.dart Normal file
View File

@ -0,0 +1,69 @@
// File generated by FlutterFire CLI.
// ignore_for_file: type=lint
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, kIsWeb, TargetPlatform;
/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
/// options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for web - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for macos - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.windows:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for windows - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
case TargetPlatform.linux:
throw UnsupportedError(
'DefaultFirebaseOptions have not been configured for linux - '
'you can reconfigure this by running the FlutterFire CLI again.',
);
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSyAYZUuqmSoE9MrTvOaavYYjdRoQFD-XnFc',
appId: '1:181536932687:android:8f3b0977f4b4dcc56f7b3a',
messagingSenderId: '181536932687',
projectId: 'cheminova-rd',
storageBucket: 'cheminova-rd.appspot.com',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSyCerSkU_YMUkOhz5uM7t8pCOFi6mvkLpOw',
appId: '1:181536932687:ios:a4866cdefdcb5cf06f7b3a',
messagingSenderId: '181536932687',
projectId: 'cheminova-rd',
storageBucket: 'cheminova-rd.appspot.com',
iosBundleId: 'com.example.cheminova',
);
}

View File

@ -1,8 +1,94 @@
import 'dart:developer';
import 'dart:io';
import 'package:cheminova/screens/splash_screen.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:get/get.dart';
import 'firebase_options.dart';
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
log("Handling a background message: ${message.data["data"]}");
}
AndroidNotificationChannel channel = const AndroidNotificationChannel(
'High Importance Channel', 'High Importance Notifications',
importance: Importance.high);
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
void main()async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
FlutterError.onError = (errorDetails) {
FirebaseCrashlytics.instance.recordFlutterFatalError(errorDetails);
};
PlatformDispatcher.instance.onError = (error, stack) {
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true);
return true;
};
if (!kIsWeb) {
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
await FirebaseMessaging.instance.getInitialMessage().then((message) {});
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification?.android;
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(channel.id, channel.name,
icon: '@mipmap/ic_launcher', importance: Importance.max),
iOS: const DarwinNotificationDetails()));
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('@mipmap/ic_launcher');
const DarwinInitializationSettings darwinInitializationSettings =
DarwinInitializationSettings();
var initSettings = const InitializationSettings(
android: initializationSettingsAndroid,
iOS: darwinInitializationSettings);
flutterLocalNotificationsPlugin.initialize(initSettings);
}
});
if (Platform.isAndroid) {
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.requestNotificationsPermission();
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
}
if (Platform.isIOS) {
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(alert: true, badge: true, sound: true);
}
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true, badge: true, sound: true);
}
runApp(const MyApp());
}

View File

@ -0,0 +1,100 @@
class InventoryManagementResponse {
bool? success;
int? totalProducts;
List<Products>? products;
InventoryManagementResponse(
{this.success, this.totalProducts, this.products});
InventoryManagementResponse.fromJson(Map<String, dynamic> json) {
success = json['success'];
totalProducts = json['totalProducts'];
if (json['products'] != null) {
products = <Products>[];
json['products'].forEach((v) {
products!.add(new Products.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['success'] = this.success;
data['totalProducts'] = this.totalProducts;
if (this.products != null) {
data['products'] = this.products!.map((v) => v.toJson()).toList();
}
return data;
}
}
class Products {
String? sId;
String? sKU;
String? name;
int? price;
int? gST;
int? hSNCode;
String? description;
String? productStatus;
String? addedBy;
List<Null>? image;
String? createdAt;
String? updatedAt;
String? category;
String? brand;
int? stock;
Products(
{this.sId,
this.sKU,
this.name,
this.price,
this.gST,
this.hSNCode,
this.description,
this.productStatus,
this.addedBy,
this.image,
this.createdAt,
this.updatedAt,
this.category,
this.brand,
this.stock});
Products.fromJson(Map<String, dynamic> json) {
sId = json['_id'];
sKU = json['SKU'];
name = json['name'];
price = json['price'];
gST = json['GST'];
hSNCode = json['HSN_Code'];
description = json['description'];
productStatus = json['product_Status'];
addedBy = json['addedBy'];
createdAt = json['createdAt'];
updatedAt = json['updatedAt'];
category = json['category'];
brand = json['brand'];
stock = json['stock'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['_id'] = this.sId;
data['SKU'] = this.sKU;
data['name'] = this.name;
data['price'] = this.price;
data['GST'] = this.gST;
data['HSN_Code'] = this.hSNCode;
data['description'] = this.description;
data['product_Status'] = this.productStatus;
data['addedBy'] = this.addedBy;
data['createdAt'] = this.createdAt;
data['updatedAt'] = this.updatedAt;
data['category'] = this.category;
data['brand'] = this.brand;
data['stock'] = this.stock;
return data;
}
}

View File

@ -0,0 +1,67 @@
class NotificationListResponse {
String? returnMessage;
List<Notifications>? notifications;
NotificationListResponse({this.returnMessage, this.notifications});
NotificationListResponse.fromJson(Map<String, dynamic> json) {
returnMessage = json['return_message'];
if (json['notifications'] != null) {
notifications = <Notifications>[];
json['notifications'].forEach((v) {
notifications!.add(Notifications.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['return_message'] = returnMessage;
if (notifications != null) {
data['notifications'] =
notifications!.map((v) => v.toJson()).toList();
}
return data;
}
}
class Notifications {
String? sId;
String? title;
String? msg;
String? addedFor;
String? createdAt;
String? updatedAt;
int? iV;
Notifications(
{this.sId,
this.title,
this.msg,
this.addedFor,
this.createdAt,
this.updatedAt,
this.iV});
Notifications.fromJson(Map<String, dynamic> json) {
sId = json['_id'];
title = json['title'];
msg = json['msg'];
addedFor = json['added_for'];
createdAt = json['createdAt'];
updatedAt = json['updatedAt'];
iV = json['__v'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['_id'] = sId;
data['title'] = title;
data['msg'] = msg;
data['added_for'] = addedFor;
data['createdAt'] = createdAt;
data['updatedAt'] = updatedAt;
data['__v'] = iV;
return data;
}
}

View File

@ -0,0 +1,165 @@
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
class NotificationServices {
//initialising firebase message plugin
FirebaseMessaging messaging = FirebaseMessaging.instance;
//initialising firebase message plugin
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
//function to initialise flutter local notification plugin to show notifications for android when app is active
void initLocalNotifications(
BuildContext context, RemoteMessage message) async {
var androidInitializationSettings =
const AndroidInitializationSettings('@mipmap/ic_launcher');
var iosInitializationSettings = const DarwinInitializationSettings();
var initializationSetting = InitializationSettings(
android: androidInitializationSettings, iOS: iosInitializationSettings);
await _flutterLocalNotificationsPlugin.initialize(initializationSetting,
onDidReceiveNotificationResponse: (payload) {
// handle interaction when app is active for android
handleMessage(context, message);
});
}
void firebaseInit(BuildContext context) {
FirebaseMessaging.onMessage.listen((message) {
RemoteNotification? notification = message.notification;
AndroidNotification? android = message.notification!.android;
if (kDebugMode) {
print("notifications title:${notification!.title}");
print("notifications body:${notification.body}");
print('count:${android!.count}');
print('data:${message.data.toString()}');
}
if (Platform.isIOS) {
forgroundMessage();
}
if (Platform.isAndroid) {
initLocalNotifications(context, message);
showNotification(message);
}
});
}
void requestNotificationPermission() async {
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: true,
badge: true,
carPlay: true,
criticalAlert: true,
provisional: true,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
if (kDebugMode) {
print('user granted permission');
}
} else if (settings.authorizationStatus ==
AuthorizationStatus.provisional) {
if (kDebugMode) {
print('user granted provisional permission');
}
} else {
//appsetting.AppSettings.openNotificationSettings();
if (kDebugMode) {
print('user denied permission');
}
}
}
// function to show visible notification when app is active
Future<void> showNotification(RemoteMessage message) async {
AndroidNotificationChannel channel = AndroidNotificationChannel(
message.notification!.android!.channelId.toString(),
message.notification!.android!.channelId.toString(),
importance: Importance.max,
showBadge: true,
playSound: true,
sound: const RawResourceAndroidNotificationSound('jetsons_doorbell'));
AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails(
channel.id.toString(), channel.name.toString(),
channelDescription: 'your channel description',
importance: Importance.high,
priority: Priority.high,
playSound: true,
ticker: 'ticker',
sound: channel.sound
// sound: RawResourceAndroidNotificationSound('jetsons_doorbell')
// icon: largeIconPath
);
const DarwinNotificationDetails darwinNotificationDetails =
DarwinNotificationDetails(
presentAlert: true, presentBadge: true, presentSound: true);
NotificationDetails notificationDetails = NotificationDetails(
android: androidNotificationDetails, iOS: darwinNotificationDetails);
Future.delayed(Duration.zero, () {
_flutterLocalNotificationsPlugin.show(
0,
message.notification!.title.toString(),
message.notification!.body.toString(),
notificationDetails,
);
});
}
//function to get device token on which we will send the notifications
Future<String> getDeviceToken() async {
String? token = await messaging.getToken();
return token!;
}
void isTokenRefresh() async {
messaging.onTokenRefresh.listen((event) {
event.toString();
if (kDebugMode) {
print('refresh');
}
});
}
//handle tap on notification when app is in background or terminated
Future<void> setupInteractMessage(BuildContext context) async {
// when app is terminated
RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
handleMessage(context, initialMessage);
}
//when app ins background
FirebaseMessaging.onMessageOpenedApp.listen((event) {
handleMessage(context, event);
});
}
void handleMessage(BuildContext context, RemoteMessage message) {}
Future forgroundMessage() async {
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
}
}

View File

@ -1,12 +1,17 @@
import 'package:cheminova/screens/authentication/controller/auth_service.dart';
import 'package:cheminova/screens/home_screen.dart';
import 'package:cheminova/services/api_service.dart';
import 'package:cheminova/utils/show_snackbar.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../notification_services.dart';
import '../../../utils/api_urls.dart';
class AuthController extends GetxController {
final authService = AuthService();
final _apiClient = ApiService();
TextEditingController emailController = TextEditingController();
TextEditingController passwordController = TextEditingController();
@ -26,6 +31,9 @@ class AuthController extends GetxController {
isLoading.value = false;
update();
if (response != null) {
final fcmToken = await NotificationServices().getDeviceToken();
print('fcmToken: $fcmToken');
await _apiClient.post(ApiUrls.fcmUrl, data: {'fcmToken': fcmToken});
showSnackbar("Your Successfully logged In!");
Get.offAll(() => const HomeScreen());
}

View File

@ -3,10 +3,8 @@ import 'package:cheminova/screens/inventory/inventory_management_screen.dart';
import 'package:cheminova/screens/order/order_tracking_screen.dart';
import 'package:cheminova/screens/order_management/order_management_screen.dart';
import 'package:cheminova/screens/product/product_catalog_screen.dart';
import 'package:cheminova/screens/report/order_history_report_screen.dart';
import 'package:cheminova/screens/report/reporting_analytics_screen.dart';
import 'package:cheminova/screens/report/notification_screen.dart';
import 'package:cheminova/screens/retail/retail_distributer_on_boarding_screen.dart';
import 'package:cheminova/screens/shipping/shipping_management_screen.dart';
import 'package:cheminova/widgets/home_card.dart';
import 'package:cheminova/widgets/my_drawer.dart';
import 'package:flutter/material.dart';
@ -29,19 +27,23 @@ class _HomeScreenState extends State<HomeScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true, // The app bar extends behind the body content.
extendBodyBehindAppBar: true,
// The app bar extends behind the body content.
// AppBar configuration with transparent background and a custom menu icon
appBar: AppBar(
centerTitle: true,
backgroundColor: Colors.transparent, // AppBar background is transparent to blend with the background image.
elevation: 0, // No shadow under the AppBar.
backgroundColor: Colors.transparent,
// AppBar background is transparent to blend with the background image.
elevation: 0,
// No shadow under the AppBar.
// Leading widget is a custom menu icon that opens the drawer when tapped.
leading: Builder(
builder: (context) {
return GestureDetector(
onTap: () => Scaffold.of(context).openDrawer(), // Opens the drawer.
onTap: () => Scaffold.of(context).openDrawer(),
// Opens the drawer.
child: Padding(
padding: const EdgeInsets.all(16.0),
child: SvgPicture.asset(
@ -79,30 +81,34 @@ class _HomeScreenState extends State<HomeScreen> {
// SingleChildScrollView allows scrolling if the content is larger than the screen.
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, // Evenly spaces the rows of cards.
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// Evenly spaces the rows of cards.
children: [
// Row with two HomeCard widgets for Product Catalogue and Order Tracking.
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, // Cards are spaced evenly across the row.
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
// Cards are spaced evenly across the row.
children: [
// HomeCard widget with the title and navigation to the Product Catalog screen.
HomeCard(
title: 'Product Catalogue',
onTap: () =>
Get.to(() => const ProductCatalogScreen()), // Navigates to ProductCatalogScreen using GetX.
title: 'Products',
onTap: () => Get.to(() =>
const ProductCatalogScreen()), // Navigates to ProductCatalogScreen using GetX.
),
// HomeCard widget with the title and navigation to the Order Tracking screen.
HomeCard(
title: 'Order Tracking',
onTap: () => Get.to(
() => const OrderTrackingScreen(), // Navigates to OrderTrackingScreen using GetX.
() =>
const OrderTrackingScreen(), // Navigates to OrderTrackingScreen using GetX.
),
),
],
),
const SizedBox(height: 10), // Adds vertical spacing between rows.
const SizedBox(height: 30),
// Adds vertical spacing between rows.
// Row with two HomeCard widgets for Order Management and Shipping Management.
Row(
@ -111,55 +117,41 @@ class _HomeScreenState extends State<HomeScreen> {
HomeCard(
title: 'Order Management',
onTap: () => Get.to(
() => OrderManagementScreen(), // Navigates to OrderManagementScreen.
() =>
OrderManagementScreen(), // Navigates to OrderManagementScreen.
),
),
HomeCard(
title: 'Shipping Management',
onTap: () => Get.to(
() => const ShippingManagementScreen(), // Navigates to ShippingManagementScreen.
),
),
],
),
const SizedBox(height: 10),
// Row with two HomeCard widgets for Inventory Management and Reporting & Analytics.
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
HomeCard(
title: 'Inventory Management',
onTap: () => Get.to(
() => const InventoryManagementScreen(), // Navigates to InventoryManagementScreen.
),
),
HomeCard(
title: 'Reporting & Analytics',
onTap: () => Get.to(
() => const ReportingAnalyticsScreen(), // Navigates to ReportingAnalyticsScreen.
() =>
const InventoryManagementScreen(), // Navigates to InventoryManagementScreen.
),
),
// HomeCard(
// title: 'Shipping Management',
// onTap: () => Get.to(
// () =>
// const ShippingManagementScreen(), // Navigates to ShippingManagementScreen.
// ),
// ),
],
),
const SizedBox(height: 10),
const SizedBox(height: 30),
// Row with two HomeCard widgets for Order Data Export and Retail Distributors Onboarding.
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
HomeCard(
title: 'Order Data Export',
onTap: () => Get.to(
() => const OrderHistoryReportScreen(), // Navigates to OrderHistoryReportScreen.
),
),
title: 'Notifications',
onTap: () => Get.to(() => NotificationScreen())),
HomeCard(
title: 'Retail Distributors Onboarding',
title: 'Announcement',
onTap: () => Get.to(
() => const RetailDistributerOnBoardingScreen(), // Navigates to RetailDistributerOnBoardingScreen.
() =>
const RetailDistributerOnBoardingScreen(), // Navigates to RetailDistributerOnBoardingScreen.
),
),
],

View File

@ -1,10 +1,11 @@
import 'package:cheminova/models/product_model.dart';
import 'package:cheminova/widgets/inventory_product_card.dart';
import 'package:cheminova/widgets/my_drawer.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:cheminova/widgets/my_drawer.dart';
import '../../controller/inventory_management_controller.dart';
import '../../models/InventoryManagementResponse.dart';
class InventoryManagementScreen extends StatefulWidget {
const InventoryManagementScreen({super.key});
@ -15,22 +16,23 @@ class InventoryManagementScreen extends StatefulWidget {
}
class _InventoryManagementScreenState extends State<InventoryManagementScreen> {
final List<ProductModel> _productList = [
ProductModel(
id: '1',
name: 'Product 1',
price: 100,
quantity: 100,
description: 'Description 1',
category: ProductCategory.food,
image: 'assets/images/product.png',
)
];
late Future<InventoryManagementResponse> _productsFuture;
final InventoryManagementController _controller =
Get.put(InventoryManagementController());
final List<String> _filterList = [
"Category",
"Price Range",
"Availability",
];
@override
void initState() {
super.initState();
_productsFuture = _controller.getProducts();
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -45,9 +47,7 @@ class _InventoryManagementScreenState extends State<InventoryManagementScreen> {
onTap: () => Scaffold.of(context).openDrawer(),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: SvgPicture.asset(
'assets/svg/menu.svg',
),
child: SvgPicture.asset('assets/svg/menu.svg'),
),
);
},
@ -57,15 +57,11 @@ class _InventoryManagementScreenState extends State<InventoryManagementScreen> {
onTap: () => Get.back(),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SvgPicture.asset(
'assets/svg/back_arrow.svg',
),
child: SvgPicture.asset('assets/svg/back_arrow.svg'),
),
),
],
title: const Text(
"Inventory Management",
),
title: const Text("Inventory Management"),
),
drawer: const MyDrawer(),
body: Stack(
@ -78,9 +74,7 @@ class _InventoryManagementScreenState extends State<InventoryManagementScreen> {
SafeArea(
child: Column(
children: [
SizedBox(
height: Get.height * 0.02,
),
SizedBox(height: Get.height * 0.02),
Card(
margin: const EdgeInsets.symmetric(horizontal: 18),
shape: RoundedRectangleBorder(
@ -116,16 +110,32 @@ class _InventoryManagementScreenState extends State<InventoryManagementScreen> {
),
SizedBox(
height: Get.height * 0.6,
child: ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: 10,
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: InventoryProductCard(
product: _productList[0],
),
),
child: FutureBuilder<InventoryManagementResponse>(
future: _productsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return Center(
child: Text('Error: ${snapshot.error}'));
} else if (snapshot.hasData) {
final products = snapshot.data!.products ?? [];
return ListView.builder(
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: products.length,
itemBuilder: (context, index) {
final data = products[index];
return InventoryProductCard(product: data);
},
);
} else {
return const Center(
child: Text('No products found'));
}
},
),
)
],

View File

@ -1,3 +1,4 @@
import 'package:cheminova/models/InventoryManagementResponse.dart';
import 'package:cheminova/models/product_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
@ -5,7 +6,7 @@ import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart';
class InventoryProductDetailScreen extends StatefulWidget {
final ProductModel product;
final Products product;
const InventoryProductDetailScreen({
super.key,
@ -95,7 +96,7 @@ class _InventoryProductDetailScreenState
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset(
widget.product.image,
'assets/images/product.png',
fit: BoxFit.cover,
),
),
@ -159,7 +160,7 @@ class _InventoryProductDetailScreenState
padding:
const EdgeInsets.fromLTRB(8, 8, 8, 0),
child: Text(
"Current Stock: ${widget.product.quantity}",
"Current Stock: ${widget.product.stock}",
style: GoogleFonts.roboto(
fontSize: Get.width * 0.04,
fontWeight: FontWeight.w400,

View File

@ -1,3 +1,4 @@
import 'package:cheminova/models/InventoryManagementResponse.dart';
import 'package:cheminova/models/product_model.dart';
import 'package:cheminova/widgets/input_field.dart';
import 'package:flutter/material.dart';
@ -6,7 +7,7 @@ import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart';
class UpdateStockScreen extends StatefulWidget {
final ProductModel product;
final Products product;
const UpdateStockScreen({
super.key,
@ -48,7 +49,7 @@ class _UpdateStockScreenState extends State<UpdateStockScreen> {
),
],
title: const Text(
"Product Detail",
"Product update",
),
),
body: Stack(
@ -95,7 +96,7 @@ class _UpdateStockScreenState extends State<UpdateStockScreen> {
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset(
widget.product.image,
'assets/images/product.png',
fit: BoxFit.cover,
),
),
@ -123,7 +124,7 @@ class _UpdateStockScreenState extends State<UpdateStockScreen> {
child: Padding(
padding: const EdgeInsets.all(12),
child: Text(
"Current Stock: ${widget.product.quantity}",
"Current Stock: ${widget.product.stock}",
style: GoogleFonts.roboto(
fontSize: Get.width * 0.04,
fontWeight: FontWeight.w700,

View File

@ -7,14 +7,15 @@ import 'package:flutter_svg/svg.dart';
import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import '../../controller/cart_controller.dart';
import '../../controller/get_order_placed_controller.dart';
import '../../models/product_model1.dart';
class OrderManagementScreen extends StatefulWidget {
final Product? productModel;
PlacedOrderList? placeOrder;
OrderManagementScreen({super.key, this.productModel, this.placeOrder});
@override
@ -25,10 +26,11 @@ class _OrderManagementScreenState extends State<OrderManagementScreen> {
final _searchController = TextEditingController();
final List<String> _filterList = ["Order Status", "Date Range"];
final GetPlacedOrderController _getPlacedOrderController = Get.put(GetPlacedOrderController());
final GetPlacedOrderController _getPlacedOrderController =
Get.put(GetPlacedOrderController());
final CartController _cartController = Get.put(CartController());
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
GlobalKey<RefreshIndicatorState>();
GlobalKey<RefreshIndicatorState>();
@override
void initState() {
@ -38,7 +40,7 @@ class _OrderManagementScreenState extends State<OrderManagementScreen> {
Future<void> _onRefresh() async {
await getOrder1();
await Future.delayed(Duration(seconds: 1));
await Future.delayed(const Duration(seconds: 1));
}
Future<void> getOrder1() async {
@ -96,13 +98,14 @@ class _OrderManagementScreenState extends State<OrderManagementScreen> {
Image.asset('assets/images/image_1.png', fit: BoxFit.cover),
SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
child: Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: _onRefresh,
color: Colors.black,
backgroundColor: Colors.white,
key: _refreshIndicatorKey,
onRefresh: _onRefresh,
color: Colors.black,
backgroundColor: Colors.white,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
@ -125,20 +128,36 @@ class _OrderManagementScreenState extends State<OrderManagementScreen> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: Get.height * 0.05,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: _filterList.length,
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Chip(
label: Text(
_filterList[index],
style: GoogleFonts.roboto(fontSize: 14, fontWeight: FontWeight.w500),
),
),
Obx(
() => Container(
height: Get.height * 0.05,
padding: const EdgeInsets.all(8),
margin: const EdgeInsets.only(bottom: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
BorderRadius.circular(5)),
child: DropdownButton<String>(
value: _getPlacedOrderController
.productStatus.value,
onChanged: (dynamic newValue) {
if (newValue != null) {
_getPlacedOrderController
.updateProductStatus(newValue);
}
},
items: <String>[
'All',
'Delivered',
'Processing',
'Cancelled'
].map<DropdownMenuItem<String>>(
(String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
),
@ -147,10 +166,19 @@ class _OrderManagementScreenState extends State<OrderManagementScreen> {
child: Obx(() {
// Use a set to keep track of unique order IDs
final Set<String> uniqueOrderIds = {};
final List<PlacedOrderList> uniqueOrders = [];
final List<PlacedOrderList> uniqueOrders =
[];
for (var order in _getPlacedOrderController.placedOrders) {
if (uniqueOrderIds.add(order.sId??'')) {
final orders = _getPlacedOrderController
.filterOrder.isNotEmpty
? _getPlacedOrderController
.filterOrder
: _getPlacedOrderController
.placedOrders;
for (var order in orders) {
if (uniqueOrderIds
.add(order.sId ?? '')) {
uniqueOrders.add(order);
}
}
@ -164,48 +192,85 @@ class _OrderManagementScreenState extends State<OrderManagementScreen> {
// Combine product names into a single string
final productNames = order.orderItem!
.map((item) => capitalizeFirstLetter(item.name??''))
.map((item) =>
capitalizeFirstLetter(
item.name ?? ''))
.join(', ');
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
padding: const EdgeInsets.symmetric(
vertical: 8),
child: Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 8, 0),
padding: const EdgeInsets
.fromLTRB(16, 8, 8, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment
.start,
children: [
Text("Order ID: ", style: GoogleFonts.roboto(fontSize: 14, fontWeight: FontWeight.bold)),
Text("${order.uniqueId}")
Text("Order ID: ",
style: GoogleFonts.roboto(
fontSize: 14,
fontWeight:
FontWeight
.bold)),
Text(
"${order.uniqueId}")
],
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 8, 0),
padding: const EdgeInsets
.fromLTRB(16, 8, 8, 0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start, // Aligns the Column to the top of the Text
crossAxisAlignment:
CrossAxisAlignment
.start,
// Aligns the Column to the top of the Text
children: [
Text(
"Product Names: ",
style: GoogleFonts.roboto(
style: GoogleFonts
.roboto(
fontSize: 14,
fontWeight: FontWeight.bold,
fontWeight:
FontWeight.bold,
),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start, // Aligns text to the right within the Column
crossAxisAlignment:
CrossAxisAlignment
.start,
// Aligns text to the right within the Column
children: [
const SizedBox(height: 4), // Adds a small space between the label and the product names
for (int i = 0; i < productNames.split(",").length; i++)
const SizedBox(
height: 4),
// Adds a small space between the label and the product names
for (int i = 0;
i <
productNames
.split(
",")
.length;
i++)
Text(
'${i + 1}. ${productNames.split(",")[i].trim()}', // Adds index and trims whitespace
textAlign: TextAlign.left, // Aligns text to the right
style: GoogleFonts.roboto(
fontSize: 14,
'${i + 1}. ${productNames.split(",")[i].trim()}',
// Adds index and trims whitespace
textAlign:
TextAlign
.left,
// Aligns text to the right
style:
GoogleFonts
.roboto(
fontSize:
14,
),
),
],
@ -214,42 +279,76 @@ class _OrderManagementScreenState extends State<OrderManagementScreen> {
],
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 8, 0),
padding: const EdgeInsets
.fromLTRB(16, 8, 8, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment
.start,
children: [
Text("Order Date: ", style: GoogleFonts.roboto(fontSize: 14, fontWeight: FontWeight.bold)),
Text(formatDate("${order.createdAt}"))
Text("Order Date: ",
style: GoogleFonts.roboto(
fontSize: 14,
fontWeight:
FontWeight
.bold)),
Text(formatDate(
"${order.createdAt}"))
],
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 8, 8),
padding: const EdgeInsets
.fromLTRB(16, 8, 8, 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment
.start,
children: [
Text("Status: ", style: GoogleFonts.roboto(fontSize: 14, fontWeight: FontWeight.bold)),
Text(capitalizeFirstLetter("${order.status}"))
Text("Status: ",
style: GoogleFonts.roboto(
fontSize: 14,
fontWeight:
FontWeight
.bold)),
Text(capitalizeFirstLetter(
"${order.status}"))
],
),
),
SizedBox(
width: Get.width * 0.4,
child: Padding(
padding: const EdgeInsets.all(8.0),
padding:
const EdgeInsets.all(
8.0),
child: ElevatedButton(
onPressed: () => Get.to(() => OrderManagementDetailScreen(
placedOrderList: uniqueOrders[index])),
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: const Color(0xFF004791),
onPressed: () => Get.to(() =>
OrderManagementDetailScreen(
placedOrderList:
uniqueOrders[
index])),
style: ElevatedButton
.styleFrom(
foregroundColor:
Colors.white,
backgroundColor:
const Color(
0xFF004791),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
borderRadius:
BorderRadius
.circular(
10)),
),
child: Text("View Details", style: GoogleFonts.roboto(fontSize: 14, fontWeight: FontWeight.w400)),
child: Text(
"View Details",
style: GoogleFonts.roboto(
fontSize: 14,
fontWeight:
FontWeight
.w400)),
),
),
)
@ -274,31 +373,24 @@ class _OrderManagementScreenState extends State<OrderManagementScreen> {
],
),
),
Obx(() {
if (_getPlacedOrderController.isLoading.value) {
return Container(
color: Colors.black.withOpacity(0.5),
child: const Center(
child: CircularProgressIndicator(strokeWidth: 1),
),
);
}
return const SizedBox.shrink();
},)
Obx(
() {
if (_getPlacedOrderController.isLoading.value) {
return Container(
color: Colors.black.withOpacity(0.5),
child: const Center(
child: CircularProgressIndicator(strokeWidth: 1),
),
);
}
return const SizedBox.shrink();
},
)
],
);
}
}
// import 'package:cheminova/controller/get_place_order_service.dart';
// import 'package:cheminova/controller/place_order_controller.dart';
// import 'package:cheminova/models/place_order_list_model.dart';

View File

@ -0,0 +1,118 @@
import 'package:cheminova/widgets/my_drawer.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import '../../controller/notification_controller.dart';
import '../../models/NotificationListResponse.dart';
import '../../widgets/comman_background.dart';
import '../../widgets/common_appbar.dart';
import '../../widgets/common_elevated_button.dart';
class NotificationScreen extends StatelessWidget {
final NotificationController controller = Get.put(NotificationController());
NotificationScreen({super.key});
@override
Widget build(BuildContext context) {
return CommonBackground(
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: CommonAppBar(
title: const Text('Notification',
style: TextStyle(
fontSize: 20,
color: Colors.black,
fontWeight: FontWeight.w400,
fontFamily: 'Anek')),
backgroundColor: Colors.transparent,
elevation: 0,
actions: [
IconButton(
onPressed: () => Navigator.pop(context),
icon: SvgPicture.asset('assets/svg/back_arrow.svg'),
padding: const EdgeInsets.only(right: 20))
],
),
drawer: const MyDrawer(),
body: Obx(() => controller.isLoading.value
? const Center(child: CircularProgressIndicator())
: MyListView(notificationList: controller.notificationsList)),
),
);
}
}
Widget buildProductButton(String productName) {
return Padding(
padding: const EdgeInsets.only(bottom: 15),
child: CommonElevatedButton(
borderRadius: 30,
width: double.infinity,
height: kToolbarHeight - 10,
text: productName,
backgroundColor: const Color(0xff004791),
onPressed: () {
debugPrint('$productName pressed');
},
),
);
}
class MyListView extends StatelessWidget {
final List<Notifications> notificationList;
const MyListView({super.key, required this.notificationList});
@override
Widget build(BuildContext context) {
Map<String, List<Notifications>> groupedNotifications = {};
for (var notification in notificationList) {
String date = DateFormat("dd MMM yyyy").format(DateTime.parse(notification.createdAt ?? ''));
if (!groupedNotifications.containsKey(date)) {
groupedNotifications[date] = [];
}
groupedNotifications[date]!.add(notification);
}
return ListView.builder(
padding: const EdgeInsets.only(top: 15),
itemCount: groupedNotifications.length,
itemBuilder: (context, index) {
String date = groupedNotifications.keys.elementAt(index);
List<Notifications> notificationsForDate = groupedNotifications[date]!;
return Padding(
padding: const EdgeInsets.only(bottom: 10, left: 10, right: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Text(
date,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
),
...notificationsForDate.map((item) => Padding(
padding: const EdgeInsets.only(bottom: 10),
child: ExpansionTile(
collapsedBackgroundColor: Colors.white,
backgroundColor: Colors.white,
trailing: const SizedBox.shrink(),
title: Text(
item.title ?? '',
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500),
),
subtitle: Text(item.msg ?? ''),
),
)),
],
),
);
},
);
}
}

View File

@ -1,191 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart';
class OrderHistoryReportScreen extends StatefulWidget {
const OrderHistoryReportScreen({super.key});
@override
State<OrderHistoryReportScreen> createState() =>
_OrderHistoryReportScreenState();
}
class _OrderHistoryReportScreenState extends State<OrderHistoryReportScreen> {
final List<String> _filterList = [
"Order Status",
"Date Range",
];
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
appBar: AppBar(
centerTitle: true,
backgroundColor: Colors.transparent,
elevation: 0,
leading: GestureDetector(
onTap: () {},
child: Padding(
padding: const EdgeInsets.all(16.0),
child: SvgPicture.asset(
'assets/svg/menu.svg',
),
),
),
actions: [
GestureDetector(
onTap: () => Get.back(),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SvgPicture.asset(
'assets/svg/back_arrow.svg',
),
),
),
],
title: const Text(
"Order History Report",
),
),
body: Stack(
fit: StackFit.expand,
children: [
Image.asset(
'assets/images/image_1.png',
fit: BoxFit.cover,
),
SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(height: Get.height * 0.02),
Card(
margin: const EdgeInsets.symmetric(horizontal: 18),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(19),
side: const BorderSide(color: Color(0xFFFDFDFD)),
),
color: const Color(0xFFB4D1E5).withOpacity(0.9),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
height: Get.height * 0.05,
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: _filterList.length,
itemBuilder: (context, index) => Padding(
padding:
const EdgeInsets.symmetric(horizontal: 4),
child: Chip(
label: Text(
_filterList[index],
style: GoogleFonts.roboto(
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
),
),
),
),
SizedBox(height: Get.height * 0.02),
SizedBox(
width: Get.width,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: Get.height * 0.4,
width: Get.width * 0.7,
decoration: BoxDecoration(
border: Border.all(
width: 4,
color: Colors.white,
),
borderRadius: BorderRadius.circular(15),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.asset(
"assets/images/chart.png",
fit: BoxFit.cover,
),
),
),
),
),
SizedBox(height: Get.height * 0.02),
SizedBox(
width: Get.width,
child: Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding:
const EdgeInsets.fromLTRB(16, 8, 8, 0),
child: Text(
"Report Title: Sales Data Report",
style: GoogleFonts.roboto(
fontSize: 14,
fontWeight: FontWeight.w400,
),
),
),
Padding(
padding:
const EdgeInsets.fromLTRB(16, 8, 8, 8),
child: Text(
"Description: Overview of sales data",
style: GoogleFonts.roboto(
fontSize: 14,
fontWeight: FontWeight.w400,
),
),
),
SizedBox(
width: Get.width * 0.5,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor:
const Color(0xFF004791),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(10),
),
),
child: Text(
"Generate Report",
style: GoogleFonts.roboto(
fontSize: 14,
fontWeight: FontWeight.w400,
),
),
),
),
)
],
),
),
),
],
),
),
),
],
),
),
],
),
);
}
}

View File

@ -28,4 +28,7 @@ class ApiService {
data: {'message': 'An unexpected error occurred'});
}
}
}
Future<Response> post(String path, {dynamic data}) {
return _dio.post(path, data: data); // Send POST request
}}

View File

@ -5,14 +5,15 @@ class ApiUrls {
static const String profileUrl = '/api/rd-get-me';
static const String forgetPasswordUrl = '/api/v1/user/password/forgot';
static const String changePasswordUrl = '/api/v1/user/password/update';
static const String fcmUrl = '/api/v1/user/fcm-token';
static const String fcmUrl = '/api/rd-save-fcm-token';
static const String getCategoryUrl = '/api/category/getCategories';
static const String getProductUrl = '/api/category/getCategories';
static const String getProductManualUrl = '/api/productmanual/getall';
static const String getKycUrl = '/api/kyc/getAll';
static const String getNotificationUrl = '/api/get-notification-pd';
static const String getNotificationUrl = '/api/get-notification-rd';
static const String getPlacedOrderUrl ='/api/get-placed-order-pd';
static const String getSinglePlacedOrderUrl ='/api/get-single-placed-order-pd';
static const String placedOrderUrl ='${baseUrl}/api/order-place';
static const String inventoryManangementOrdersStock ='${baseUrl}/api/stock';
}

View File

@ -1,3 +1,4 @@
import 'package:cheminova/models/InventoryManagementResponse.dart';
import 'package:cheminova/models/product_model.dart';
import 'package:cheminova/screens/inventory/inventory_product_detail_screen.dart';
import 'package:cheminova/screens/inventory/update_stock_screen.dart';
@ -6,7 +7,8 @@ import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart';
class InventoryProductCard extends StatelessWidget {
final ProductModel product;
final Products product;
const InventoryProductCard({
super.key,
required this.product,
@ -26,7 +28,7 @@ class InventoryProductCard extends StatelessWidget {
width: Get.width * 0.30,
decoration: BoxDecoration(
image: DecorationImage(
image: Image.asset(product.image).image,
image: Image.asset('assets/images/product.png').image,
fit: BoxFit.cover,
),
),
@ -36,70 +38,74 @@ class InventoryProductCard extends StatelessWidget {
const SizedBox(
width: 10,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
style: GoogleFonts.roboto(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
Text(
"Stock: ${product.quantity}",
style: GoogleFonts.roboto(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
ElevatedButton(
onPressed: () => Get.to(
() => InventoryProductDetailScreen(
product: product,
),
),
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: const Color(0xFF004791),
padding:
const EdgeInsets.symmetric(horizontal: 18, vertical: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: Text(
"Details",
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name ?? '',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: GoogleFonts.roboto(
fontSize: 14,
fontWeight: FontWeight.w600,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
ElevatedButton(
onPressed: () => Get.to(
() => UpdateStockScreen(
product: product,
),
),
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: const Color(0xFF00784C),
padding:
const EdgeInsets.symmetric(horizontal: 18, vertical: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: Text(
"Update",
Text(
"Stock: ${product.stock ?? 0}",
style: GoogleFonts.roboto(
fontSize: 14,
fontWeight: FontWeight.w600,
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
],
ElevatedButton(
onPressed: () => Get.to(
() => InventoryProductDetailScreen(
product: product,
),
),
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: const Color(0xFF004791),
padding:
const EdgeInsets.symmetric(horizontal: 18, vertical: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: Text(
"Details",
style: GoogleFonts.roboto(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
ElevatedButton(
onPressed: () => Get.to(
() => UpdateStockScreen(
product: product,
),
),
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
backgroundColor: const Color(0xFF00784C),
padding:
const EdgeInsets.symmetric(horizontal: 18, vertical: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
child: Text(
"Update",
style: GoogleFonts.roboto(
fontSize: 14,
fontWeight: FontWeight.w600,
),
),
),
],
),
)
],
),