Files
ERP-APP-INVENTORY/lib/views/my_scans.dart
BACHIR SOULDI 5a483da01d Add full app source
Adds the Flutter inventory app source, cleaned up for a shared repo:
generic package name, no hardcoded ERP endpoints (moved to gitignored
local config), no dead auth code, no debug logging of session data.
2026-07-16 14:37:28 +01:00

265 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:inventory_app/constants.dart';
import 'package:inventory_app/models/inventory_line.dart';
import 'package:inventory_app/service/axelor_client.dart';
import 'package:inventory_app/widgets/glass_widgets.dart';
class MyScans extends StatefulWidget {
const MyScans({Key? key}) : super(key: key);
@override
State<MyScans> createState() => _MyScansState();
}
class _MyScansState extends State<MyScans> {
Future<List<InventoryLine>?>? _futureInventoryLines;
@override
void initState() {
super.initState();
_loadInventoryLines();
}
Future<void> _loadInventoryLines() async {
final client = await AxelorClient.create();
setState(() {
_futureInventoryLines = client.getMyInventoryLines();
});
}
@override
Widget build(BuildContext context) {
final MaterialColor primary = Colors.blue;
final isDark = Theme.of(context).brightness == Brightness.dark;
return Scaffold(
body: Stack(
children: [
const FancyBackground(),
SafeArea(
child: Column(
children: [
BackAppBar(title: '📦 Mes Scans', icon: Icons.inventory_2_rounded),
Expanded(
child: FutureBuilder<List<InventoryLine>?>(
future: _futureInventoryLines,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(
child: Text(
'❌ Erreur: ${snapshot.error}',
style: const TextStyle(color: Colors.redAccent),
),
);
}
final lines = snapshot.data ?? [];
if (lines.isEmpty) {
return const Center(
child: Text(
'Aucun scan trouvé.',
style: TextStyle(
fontSize: 16,
color: Colors.grey,
fontWeight: FontWeight.w500,
),
),
);
}
return RefreshIndicator(
onRefresh: _loadInventoryLines,
child: ListView.builder(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.fromLTRB(16, 8, 16, 100),
itemCount: lines.length,
itemBuilder: (context, index) {
final line = lines[index];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: GlassCard(
padding: const EdgeInsets.all(14),
child: ListTile(
leading: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: primary.withValues(alpha: 0.1),
shape: BoxShape.circle,
),
child: const Icon(
Icons.inventory_2_rounded,
color: Colors.blue,
),
),
title: Text(
"${line.product!.code!}-${line.productName}",
style: kTextFormFieldStyle(
fontWeight: FontWeight.bold,
color:
isDark
? Colors.white
: primary.shade700,
),
),
subtitle: Padding(
padding: const EdgeInsets.only(top: 6),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
"Stock ID: ${line.id}",
style: kTextFormFieldStyle(
fontSize: 13.0,
color:
isDark
? Colors.white
: primary.shade700,
),
),
Text(
"Comptage: ${line.countingTypeSelect}",
style: kTextFormFieldStyle(
fontSize: 13.0,
color:
isDark
? Colors.white
: primary.shade700,
),
),
Text(
"Quantité Réelle: ${line.realQty}",
style: kTextFormFieldStyle(
fontSize: 13.0,
color:
isDark
? Colors.white
: primary.shade700,
),
),
if (line.observation != null &&
line.observation!.isNotEmpty)
Text(
"Observation: ${line.observation}",
style: kTextFormFieldStyle(
fontSize: 13.0,
color:
isDark
? Colors.white
: primary.shade700,
),
),
],
),
),
onTap: () {
HapticFeedback.selectionClick();
_showLineDetails(context, line);
},
),
),
);
},
),
);
},
),
),
],
),
),
],
),
);
}
void _showLineDetails(BuildContext context, InventoryLine line) {
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
barrierColor: Colors.black.withValues(alpha: 0.3),
builder: (_) {
return GlassBottomSheet(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SheetHandle(Colors.blue),
Text(
line.productName,
style: kTextFormFieldStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
color: Colors.blue.shade700,
),
),
SizedBox(height: 8),
_lineDetail("ID", line.id?.toString() ?? "-", context),
_lineDetail("Inventaire", "${line.inventoryId}", context),
_lineDetail(
"Type de comptage",
"${line.countingTypeSelect}",
context,
),
if (line.firstCountingDate != null)
_lineDetail("1er comptage", line.firstCountingDate!, context),
if (line.secondCountingDate != null)
_lineDetail("2ème comptage", line.secondCountingDate!, context),
if (line.thirdCountingDate != null)
_lineDetail("3ème comptage", line.thirdCountingDate!, context),
SizedBox(height: 14),
Align(
alignment: Alignment.centerRight,
child: ElevatedButton.icon(
icon: Icon(Icons.close_rounded, color: Colors.white),
label: Text("Fermer"),
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
],
),
);
},
);
}
Widget _lineDetail(String label, String value, BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark;
final MaterialColor primary = Colors.blue;
return Padding(
padding: EdgeInsets.symmetric(vertical: 2),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: kTextFormFieldStyle(
fontWeight: FontWeight.w600,
color: isDark ? Colors.white : primary.shade700,
),
),
Text(
value,
style: kTextFormFieldStyle(
fontWeight: FontWeight.bold,
color: isDark ? Colors.white : primary.shade700,
),
),
],
),
);
}
}