chore: Flutter mobile app, CI, and dev tooling

- mobile/: Flutter/Dart merchant mobile app skeleton
- .github/: GitHub Actions CI workflows
- .dockerignore: exclude host node_modules from build context
- .cursorrules: Cursor IDE project rules
- .claude/: Claude Code project settings and launch config

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-05-27 21:35:27 +03:30
parent 42d4cb896a
commit a85890f30a
52 changed files with 3919 additions and 0 deletions
@@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../core/utils/currency_utils.dart';
import '../cart/cart_state.dart';
final trackProvider = FutureProvider.autoDispose.family<Map<String, dynamic>?, String>((ref, orderId) {
return ref.watch(publicApiProvider).trackOrder(orderId);
});
class TrackScreen extends ConsumerWidget {
const TrackScreen({super.key, required this.orderId});
final String orderId;
@override
Widget build(BuildContext context, WidgetRef ref) {
final trackAsync = ref.watch(trackProvider(orderId));
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
appBar: AppBar(title: const Text('پیگیری سفارش')),
body: trackAsync.when(
data: (order) {
if (order == null) return const Center(child: Text('سفارش یافت نشد'));
final status = order['status'] as String? ?? '';
final items = order['items'] as List<dynamic>? ?? [];
return Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('وضعیت: $status', style: Theme.of(context).textTheme.titleLarge),
Text('مبلغ: ${formatToman((order['total'] as num).toInt())}'),
const SizedBox(height: 16),
const Text('اقلام:'),
...items.map((i) => Text('${i['menuItemName']} × ${i['quantity']}')),
],
),
);
},
loading: () => const Center(child: CircularProgressIndicator()),
error: (e, _) => Center(child: Text('خطا: $e')),
),
),
);
}
}