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,14 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:meezi_pos/features/auth/login_screen.dart';
import 'package:meezi_pos/features/pos/pos_screen.dart';
final appRouterProvider = Provider<GoRouter>((ref) {
return GoRouter(
initialLocation: '/login',
routes: [
GoRoute(path: '/login', builder: (_, __) => const LoginScreen()),
GoRoute(path: '/pos', builder: (_, __) => const PosScreen()),
],
);
});
@@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
/// Phase 1: online-only OTP login (wire Dio to /api/auth/*).
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _phone = TextEditingController(text: '09121234567');
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text('میزی — صندوق', style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
const SizedBox(height: 24),
TextField(
controller: _phone,
keyboardType: TextInputType.phone,
decoration: const InputDecoration(labelText: 'موبایل', border: OutlineInputBorder()),
),
const SizedBox(height: 16),
FilledButton(
onPressed: () => context.go('/pos'),
child: const Text('ورود (دمو)'),
),
],
),
),
),
);
}
}
@@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
/// Phase 1: online POS shell. Phase 2: Drift cart + sync queue + bluetooth_print.
class PosScreen extends StatelessWidget {
const PosScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('صندوق')),
body: const Center(
child: Text('POS — منو و سبد در فاز بعدی (Drift + API)'),
),
);
}
}
+25
View File
@@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:meezi_pos/core/router/app_router.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const ProviderScope(child: MeeziPosApp()));
}
class MeeziPosApp extends ConsumerWidget {
const MeeziPosApp({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final router = ref.watch(appRouterProvider);
return MaterialApp.router(
title: 'Meezi POS',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF0F6E56)),
useMaterial3: true,
),
routerConfig: router,
);
}
}