Preview:
import 'dart:async';
import 'dart:developer';
import 'dart:io';
import 'dart:math';
import 'package:collection/collection.dart';
import 'package:excel/excel.dart' as exc;
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:rrispat_app/src/features/store/domain/models/store_user_model.dart';
import 'package:rrispat_app/src/shared/providers/providers.dart';
import '../../../../../l10n/app_localizations.dart';
import '../../../../shared/widgets/loader_widget.dart';
import '../../../../shared/widgets/rr_filled_button.dart';
import '../../../auth/controllers/auth_controller.dart';
import '../../../auth/domains/models/auth_model.dart';
import '../../../auth/domains/providers/auth_model_provider.dart';
import '../../controller/store_item_controller.dart';
import '../../controller/store_user_controller.dart';
import '../../domain/models/store_item_model.dart';
import '../../domain/store_enums.dart';

class StoreAddStock extends ConsumerStatefulWidget {
  const StoreAddStock({Key? key}) : super(key: key);

  @override
  ConsumerState<StoreAddStock> createState() => _StoreAddStockState();
}

class _StoreAddStockState extends ConsumerState<StoreAddStock> {
  final GlobalKey<FormState> _pieces = GlobalKey<FormState>();

  bool _showLoading = true;

  final TextEditingController _itemNameController = TextEditingController();
  final TextEditingController _poNumberController = TextEditingController();

  final TextEditingController pieces = TextEditingController();

  StoreUserModel? storeUser;
  List<StoreItemModel> storeItemListfromBe = [];
  List<AuthModel> authModels = [];
  double screenWidth = 0;

  PaginationParams _paginationParams = PaginationParams(
    page: 1,
    pageSize: 10,
    totalPages: 0,
    itemName: '',
    poNumber: '',
  );

  int _totalItems = 0;

  @override
  void initState() {
    super.initState();

    Timer(const Duration(seconds: 90), () {
      if (mounted) {
        setState(() {
          _showLoading = false;
        });
      }
    });
  }

  Future<void> uploadExcelDataFromWebStoreStockAdd(BuildContext context) async {
    FilePickerResult? file = await FilePicker.platform.pickFiles(
      type: FileType.custom,
      allowedExtensions: ['xlsx'],
      allowMultiple: false,
    );
    if (file != null) {
      List<StoreItemModel> tempList = [];
      Uint8List fileBytes = file.files.first.bytes!;
      var excel = exc.Excel.decodeBytes(fileBytes);
      for (var table in excel.tables.keys) {
        for (var row in excel.tables[table]!.rows) {
          if (row[0]?.value.toString() == "Company Name") continue;
          try {
            StoreItemModel storeItemModel = StoreItemModel(
              id: '',
              itemName: (row[4]?.value).toString(),
              vendorName: (row[1]?.value).toString(),
              pieces: double.parse((row[6]?.value).toString()),
              issuedPcs: double.parse(0.toString()),
              createdAt: DateTime.now(),
              createdBy: ref.read(authModelProvider)!.uid,
              itemCode: (row[3]?.value).toString(),
              poNumber: (row[8]?.value).toString(),
              unit: (row[5]?.value).toString(),
              issuedCompleted: double.parse(0.toString()),
              sapItemCode: '',
              plantName: (row[10]?.value).toString(),
              poDate: (row[11]?.value).toString(),
              status: StoreItemStatus.approved,
              type: StoreItemCreatedType.normal,
              remark: '',
            );
            tempList.add(storeItemModel);
          } catch (e) {
            print("$e store item error getFile");
          }
        }
      }
      for (var element in tempList) {
        ref
            .read(storeItemControllerProvider)
            .createStoreItemStock(ref, element);
      }
    }
  }

  Widget buildFilters(BuildContext context) {
    final double screenWidth = MediaQuery.of(context).size.width;
    if (screenWidth < 600) {
      return Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          TextFormField(
            controller: _itemNameController,
            decoration: InputDecoration(
              labelText: "Filter by Item Name",
              border:
                  OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
            ),
            onChanged: (value) {
              setState(() {
                _paginationParams = PaginationParams(
                  page: 1,
                  pageSize: _paginationParams.pageSize,
                  totalPages: _paginationParams.totalPages,
                  itemName: value,
                  poNumber: _poNumberController.text,
                );
              });
            },
          ),
          const SizedBox(height: 10),
          TextFormField(
            controller: _poNumberController,
            decoration: InputDecoration(
              labelText: "Filter by PO Number",
              border:
                  OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
            ),
            onChanged: (value) {
              setState(() {
                _paginationParams = PaginationParams(
                  page: 1,
                  pageSize: _paginationParams.pageSize,
                  totalPages: _paginationParams.totalPages,
                  itemName: _itemNameController.text,
                  poNumber: value,
                );
              });
            },
          ),
          const SizedBox(height: 10),
        ],
      );
    } else {
      return Row(
        children: [
          Expanded(
            child: TextFormField(
              controller: _itemNameController,
              decoration: InputDecoration(
                labelText: "Filter by Item Name",
                border:
                    OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
              ),
              onChanged: (value) {
                setState(() {
                  _paginationParams = PaginationParams(
                    page: 1,
                    pageSize: _paginationParams.pageSize,
                    totalPages: _paginationParams.totalPages,
                    itemName: value,
                    poNumber: _poNumberController.text,
                  );
                });
              },
            ),
          ),
          const SizedBox(width: 10),
          Expanded(
            child: TextFormField(
              controller: _poNumberController,
              decoration: InputDecoration(
                labelText: "Filter by PO Number",
                border:
                    OutlineInputBorder(borderRadius: BorderRadius.circular(10)),
              ),
              onChanged: (value) {
                setState(() {
                  _paginationParams = PaginationParams(
                    page: 1,
                    pageSize: _paginationParams.pageSize,
                    totalPages: _paginationParams.totalPages,
                    itemName: _itemNameController.text,
                    poNumber: value,
                  );
                });
              },
            ),
          ),
        ],
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    screenWidth = MediaQuery.of(context).size.width;

    ref
        .watch(authUserListStreamProvider)
        .whenData((value) => authModels = value);
    ref.watch(storeUserStreamProvider).whenData((value) => storeUser = value);

    return Scaffold(
      appBar: AppBar(
        title: Text('PO - Stock Add'),
        centerTitle: true,
      ),
      body: Padding(
        padding: const EdgeInsets.all(10),
        child: Column(
          children: [
            if (storeUser != null &&
                storeUser!.userType.contains(StoreUserType.poListUpload))
              Center(
                child: RrFilledButton(
                  onPressed: () {
                    uploadExcelDataFromWebStoreStockAdd(context);
                  },
                  label: ('Store Item Excel File Upload'),
                ),
              ),
            const SizedBox(height: 20),
            buildFilters(context),
            const SizedBox(height: 10),
            Expanded(
              child: ref
                  .watch(storeItemStockListWithPaginationStreamProvider(
                      _paginationParams))
                  .when(
                    data: (paginatedData) {
                      _totalItems = paginatedData.totalItems;
                      _paginationParams = PaginationParams(
                        page: _paginationParams.page,
                        pageSize: _paginationParams.pageSize,
                        totalPages: paginatedData.totalPages,
                        itemName: _itemNameController.text,
                        poNumber: _poNumberController.text,
                      );
                      if (paginatedData.storeItemStocks.isEmpty) {
                        return _showLoading
                            ? const Padding(
                                padding: EdgeInsets.all(16.0),
                                child:
                                    Center(child: CircularProgressIndicator()),
                              )
                            : const Padding(
                                padding: EdgeInsets.all(16.0),
                                child:
                                    Center(child: Text("No Stock Item Found")),
                              );
                      } else {
                        return ListView.builder(
                          itemCount: paginatedData.storeItemStocks.length,
                          itemBuilder: (context, index) {
                            return addStockCard(
                                context, paginatedData.storeItemStocks[index]);
                          },
                        );
                      }
                    },
                    error: (error, stack) =>
                        Center(child: Text(error.toString())),
                    loading: () =>
                        const Center(child: CircularProgressIndicator()),
                  ),
            ),
            _buildPageNumbers(
                computedTotalPages: _paginationParams.totalPages ?? 1),
          ],
        ),
      ),
    );
  }

  Widget _buildPageNumbers({required int computedTotalPages}) {
    int totalVisiblePages = 5;
    List<Widget> pageButtons = [];
    int startPage = max(1, _paginationParams.page - totalVisiblePages ~/ 2);
    int endPage = min(computedTotalPages, startPage + totalVisiblePages - 1);
    if (endPage - startPage < totalVisiblePages - 1) {
      startPage = max(1, endPage - totalVisiblePages + 1);
    }
    if (startPage > 1) {
      pageButtons.add(_pageButton(1, 'First'));
    }
    for (int i = startPage; i <= endPage; i++) {
      pageButtons.add(_pageButton(i, '$i'));
    }
    if (endPage < computedTotalPages) {
      pageButtons.add(_pageButton(computedTotalPages, 'Last'));
    }
    return Wrap(
      alignment: WrapAlignment.center,
      children: pageButtons,
    );
  }

  Widget _pageButton(int pageNumber, String text) {
    return Padding(
      padding: const EdgeInsets.all(2.0),
      child: InkWell(
        onTap: () => _changePage(pageNumber),
        child: Padding(
          padding: const EdgeInsets.all(10.0),
          child: Text(
            text,
            style: TextStyle(
              color: pageNumber == _paginationParams.page
                  ? Theme.of(context).colorScheme.primary
                  : Colors.grey,
            ),
          ),
        ),
      ),
    );
  }

  void _changePage(int page) {
    if (page != _paginationParams.page) {
      setState(() {
        _paginationParams = PaginationParams(
          page: page,
          pageSize: _paginationParams.pageSize,
          totalPages: _paginationParams.totalPages,
          itemName: _itemNameController.text,
          poNumber: _poNumberController.text,
        );
      });
    }
  }

  Widget addStockCard(BuildContext context, StoreItemModel storeItemModel) {
    bool isClosed = storeItemModel.issuedPcs != 0;
    AuthModel closedByPerson = authModels.firstWhere(
      (element) => element.uid == storeItemModel.createdBy,
    );
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 2, horizontal: 5),
      child: Card(
        color: Theme.of(context).colorScheme.background,
        child: ListTile(
          leading: Container(
            width: 40,
            height: 40,
            decoration: BoxDecoration(
              color: Theme.of(context).colorScheme.tertiary,
              shape: BoxShape.circle,
            ),
            alignment: Alignment.center,
            child: Text(
              storeItemModel.pieces.toStringAsFixed(2),
              style: Theme.of(context)
                  .textTheme
                  .titleMedium
                  ?.copyWith(color: Colors.white, fontSize: 9),
              textAlign: TextAlign.center,
            ),
          ),
          title: Text(
            storeItemModel.itemName.toUpperCase(),
            style: Theme.of(context).textTheme.titleLarge,
          ),
          subtitle: Wrap(
            spacing: 20,
            children: [
              Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    "Pieces: ",
                    style: TextStyle(
                      color: Theme.of(context).colorScheme.primary,
                    ),
                  ),
                  Text(storeItemModel.pieces.toStringAsFixed(2)),
                  Text(" ${storeItemModel.unit.toString()}"),
                ],
              ),
              Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    "${AppLocalizations.of(context).translate('Vendor Name')}: ",
                    style: TextStyle(
                      color: Theme.of(context).colorScheme.primary,
                    ),
                  ),
                  Text(storeItemModel.vendorName),
                ],
              ),
              Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    "SAP Item Code: ",
                    style: TextStyle(
                      color: Theme.of(context).colorScheme.primary,
                    ),
                  ),
                  Text(storeItemModel.itemCode),
                ],
              ),
              Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    "Plant Name: ",
                    style: TextStyle(
                      color: Theme.of(context).colorScheme.primary,
                    ),
                  ),
                  Text(storeItemModel.plantName),
                ],
              ),
              Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    "PO Created Date: ",
                    style: TextStyle(
                      color: Theme.of(context).colorScheme.primary,
                    ),
                  ),
                  Text(storeItemModel.poDate),
                ],
              ),
              Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    "PO Number: ",
                    style: TextStyle(
                      color: Theme.of(context).colorScheme.primary,
                    ),
                  ),
                  Text(storeItemModel.poNumber),
                ],
              ),
              Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    "${AppLocalizations.of(context).translate('Created At')}: ",
                    style: TextStyle(
                      color: Theme.of(context).colorScheme.primary,
                    ),
                  ),
                  Text(
                    ref
                        .read(dateFormatterProvider)
                        .format(storeItemModel.createdAt),
                  ),
                ],
              ),
              screenWidth <= 500
                  ? (isClosed)
                      ? Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Text(
                              "Closed By : ${closedByPerson.fullName}",
                              style: TextStyle(
                                color: Theme.of(context).colorScheme.tertiary,
                              ),
                            ),
                          ],
                        )
                      : Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            buildIconButton(
                              Icons.done_all_rounded,
                              AppLocalizations.of(context).translate('Approve'),
                              () {
                                addStockModel(storeItemModel);
                              },
                            ),
                            const SizedBox(width: 20),
                            buildIconButton(
                              Icons.close_rounded,
                              'Close',
                              () {
                                partiallyCloseStockModel(storeItemModel);
                              },
                            ),
                          ],
                        )
                  : const SizedBox.shrink(),
            ],
          ),
          trailing: screenWidth > 500
              ? (isClosed)
                  ? Wrap(
                      children: [
                        Text(
                          "Closed By : ${closedByPerson.fullName}",
                          style: TextStyle(
                            color: Theme.of(context).colorScheme.tertiary,
                          ),
                        ),
                      ],
                    )
                  : Wrap(
                      children: [
                        buildIconButton(
                          Icons.done_all_rounded,
                          AppLocalizations.of(context).translate('Approve'),
                          () {
                            addStockModel(storeItemModel);
                          },
                        ),
                        const SizedBox(width: 20),
                        buildIconButton(
                          Icons.close_rounded,
                          'Close',
                          () {
                            partiallyCloseStockModel(storeItemModel);
                          },
                        ),
                      ],
                    )
              : null,
        ),
      ),
    );
  }

  Widget buildIconButton(
      IconData icon, String tooltip, void Function() onPressed) {
    return Tooltip(
      message: tooltip,
      child: IconButton(
        onPressed: onPressed,
        icon: Icon(
          icon,
          color: Theme.of(context).colorScheme.tertiary,
        ),
      ),
    );
  }

  void partiallyCloseStockModel(StoreItemModel storeItemModel) {
    try {
      showDialog(
        barrierDismissible: false,
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Center(
              child: Text(
                "Verify",
                style: Theme.of(context).textTheme.bodyLarge,
              ),
            ),
            content: const SizedBox(
              height: 80,
              child: Center(
                child: Column(
                  children: [
                    Text('Are you sure you want to close this stock item?'),
                    SizedBox(height: 5),
                  ],
                ),
              ),
            ),
            actions: <Widget>[
              TextButton(
                child: const Text('Cancel'),
                onPressed: () {
                  context.pop(true);
                },
              ),
              ElevatedButton(
                child: const Text('Close'),
                onPressed: () async {
                  String closedBy = ref.read(authModelProvider)!.uid;
                  ref.read(storeItemControllerProvider).updateStoreItemStock(
                      ref, storeItemModel, storeItemModel.pieces, closedBy);
                  context.pop(true);
                  ref.invalidate(
                      storeItemStockListWithPaginationStreamProvider);
                },
              )
            ],
          );
        },
      );
    } catch (e) {
      print(e.toString());
    }
  }

  void addStockModel(StoreItemModel storeItemModel) {
    try {
      showDialog(
        barrierDismissible: false,
        context: context,
        builder: (context) {
          return AlertDialog(
            title: Center(
              child: Text(
                "Verify",
                style: Theme.of(context).textTheme.bodyLarge,
              ),
            ),
            content: SizedBox(
              height: 180,
              child: Center(
                child: Column(
                  children: [
                    Text('Item Name : ${storeItemModel.itemName}'),
                    const SizedBox(height: 5),
                    Text(
                        'Total pcs. in Po list : ${storeItemModel.pieces} ${storeItemModel.unit}'),
                    const SizedBox(height: 10),
                    Form(
                      key: _pieces,
                      child: TextFormField(
                        validator: (text) {
                          if (text == null ||
                              text.isEmpty ||
                              double.parse(text) < 1) {
                            return 'Please enter Pieces to add';
                          }
                          if (double.parse(text) >
                              (storeItemModel.pieces +
                                  (storeItemModel.pieces * 0.1))) {
                            return 'Pcs should not be greater than available stock';
                          }
                          return null;
                        },
                        controller: pieces,
                        keyboardType: TextInputType.number,
                        textCapitalization: TextCapitalization.words,
                        decoration: InputDecoration(
                          labelText: "Pieces",
                          hintText: "Enter Pieces",
                          border: OutlineInputBorder(
                            borderRadius: BorderRadius.circular(10),
                          ),
                        ),
                      ),
                    ),
                    const SizedBox(height: 5),
                  ],
                ),
              ),
            ),
            actions: <Widget>[
              TextButton(
                child: const Text('Cancel'),
                onPressed: () {
                  context.pop(true);
                },
              ),
              ElevatedButton(
                child: const Text('OK'),
                onPressed: () async {
                  if (_pieces.currentState!.validate()) {
                    double pcs = double.parse(pieces.text);
                    String updateBy = ref.read(authModelProvider)!.uid;
                    ref
                        .read(storeItemControllerProvider)
                        .updateStoreItem(ref, storeItemModel, pcs, updateBy);
                    context.pop(true);
                    pieces.clear();
                    _itemNameController.clear();
                    _poNumberController.clear();
                    ref.invalidate(
                        storeItemStockListWithPaginationStreamProvider);
                    _pieces.currentState?.reset();
                  }
                },
              )
            ],
          );
        },
      );
    } catch (e) {
      print(e.toString());
    }
  }
}
downloadDownload PNG downloadDownload JPEG downloadDownload SVG

Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!

Click to optimize width for Twitter