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.
265 lines
11 KiB
Dart
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,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|