Done Category, Product Catalogue, Customer, Vendor, Purchase Invoice Module

This commit is contained in:
2026-08-28 11:30:18 +05:30
parent 0d13833679
commit 189f49ed07
118 changed files with 12624 additions and 4004 deletions

View File

@@ -0,0 +1,211 @@
import os
file_path = "/Users/maddy/Projects/Kifi/kifi-app/lib/features/inventory/presentation/product_detail_screen.dart"
content = """import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:kifi_app/features/inventory/providers/products_provider.dart';
import 'package:kifi_app/core/network/dio_client.dart';
import 'package:kifi_app/features/inventory/domain/product.dart';
import 'package:kifi_app/features/inventory/presentation/add_product_screen.dart';
import 'package:kifi_app/features/inventory/presentation/stock_ledger_tab.dart';
import 'package:kifi_app/features/inventory/presentation/widgets/adjust_stock_sheet.dart';
class ProductDetailScreen extends ConsumerWidget {
final Product product;
const ProductDetailScreen({super.key, required this.product});
@override
Widget build(BuildContext context, WidgetRef ref) {
// Watch products to get latest updates (like stock changes)
final productsState = ref.watch(productsProvider);
final currentProduct = productsState.value?.firstWhere((p) => p.id == product.id, orElse: () => product) ?? product;
return DefaultTabController(
length: 2,
child: Scaffold(
backgroundColor: Colors.grey[50],
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.white,
foregroundColor: Colors.black,
title: Text(currentProduct.name, style: const TextStyle(fontWeight: FontWeight.bold)),
actions: [
IconButton(
icon: const Icon(Icons.edit, color: Colors.black),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddProductScreen(product: currentProduct),
),
);
},
),
],
bottom: const TabBar(
labelColor: Colors.blue,
unselectedLabelColor: Colors.grey,
indicatorColor: Colors.blue,
tabs: [
Tab(text: "Overview"),
Tab(text: "Stock Ledger"),
],
),
),
body: TabBarView(
children: [
_buildOverviewTab(context, currentProduct, ref),
StockLedgerTab(productId: currentProduct.id!),
],
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => AdjustStockSheet(productId: currentProduct.id!),
);
},
backgroundColor: Colors.blue,
icon: const Icon(Icons.inventory),
label: const Text("Adjust Stock"),
),
),
);
}
Widget _buildOverviewTab(BuildContext context, Product p, WidgetRef ref) {
return SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Image Header
if (p.imageIds.isNotEmpty)
Container(
height: 200,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.grey[200],
),
clipBehavior: Clip.antiAlias,
child: FutureBuilder<String?>(
future: DioClient().storage.read(key: 'jwt_token'),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
final token = snapshot.data;
if (token == null) {
return const Icon(Icons.image, size: 50, color: Colors.grey);
}
return Image.network(
'${DioClient().dio.options.baseUrl}/inventory/products/${p.id}/images/${p.imageIds.first}/content',
fit: BoxFit.cover,
headers: {'Authorization': 'Bearer $token'},
errorBuilder: (context, error, stackTrace) => const Icon(Icons.image, size: 50, color: Colors.grey),
);
},
),
),
const SizedBox(height: 24),
// Stock Summary Card
Card(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
shape: BoxShape.circle,
),
child: const Icon(Icons.inventory_2, color: Colors.blue),
),
const SizedBox(width: 16),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text("Current Stock", style: TextStyle(color: Colors.grey[600], fontSize: 14)),
Text(
"${p.currentStock ?? 0}",
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 24),
),
],
),
],
),
),
),
const SizedBox(height: 16),
// Details Card
Card(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text("Metal Details", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
const Divider(),
_buildDetailRow("SKU / HUID", p.sku ?? "-"),
_buildDetailRow("Gross Weight", "${p.weight ?? 0} g"),
_buildDetailRow("Purity Factor", "${p.purityFactor ?? 1.0}"),
_buildDetailRow("Wastage", "${p.wastagePercentage ?? 0}%"),
_buildDetailRow("Net Weight", "${(p.weight ?? 0) * (1 - (p.wastagePercentage ?? 0) / 100)} g"),
],
),
),
),
const SizedBox(height: 16),
// Pricing Card
Card(
elevation: 0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text("Pricing Details", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16)),
const Divider(),
_buildDetailRow("Making Charges", "₹${p.makingCharges?.toStringAsFixed(2) ?? '0.00'} (${p.makingChargesType})"),
_buildDetailRow("HSN Code", p.hsnCode ?? "-"),
_buildDetailRow("GST Rate", "${p.gstRate?.toStringAsFixed(1) ?? '0'}%"),
],
),
),
),
],
),
);
}
Widget _buildDetailRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: TextStyle(color: Colors.grey[600])),
Text(value, style: const TextStyle(fontWeight: FontWeight.w500)),
],
),
);
}
}
"""
with open(file_path, "w") as f:
f.write(content)