a85890f30a
- 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>
128 lines
4.6 KiB
Dart
128 lines
4.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:shamsi_date/shamsi_date.dart';
|
|
|
|
import '../cart/cart_state.dart';
|
|
|
|
class ReserveScreen extends ConsumerStatefulWidget {
|
|
const ReserveScreen({super.key, required this.slug});
|
|
|
|
final String slug;
|
|
|
|
@override
|
|
ConsumerState<ReserveScreen> createState() => _ReserveScreenState();
|
|
}
|
|
|
|
class _ReserveScreenState extends ConsumerState<ReserveScreen> {
|
|
final _nameController = TextEditingController();
|
|
final _phoneController = TextEditingController();
|
|
final _notesController = TextEditingController();
|
|
Jalali? _date;
|
|
TimeOfDay _time = const TimeOfDay(hour: 19, minute: 0);
|
|
int _partySize = 2;
|
|
bool _submitting = false;
|
|
String? _message;
|
|
|
|
@override
|
|
void dispose() {
|
|
_nameController.dispose();
|
|
_phoneController.dispose();
|
|
_notesController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
String _formatDate(Jalali d) =>
|
|
'${d.year.toString().padLeft(4, '0')}-${d.month.toString().padLeft(2, '0')}-${d.day.toString().padLeft(2, '0')}';
|
|
|
|
Future<void> _submit() async {
|
|
if (_date == null) {
|
|
setState(() => _message = 'تاریخ را انتخاب کنید');
|
|
return;
|
|
}
|
|
setState(() {
|
|
_submitting = true;
|
|
_message = null;
|
|
});
|
|
try {
|
|
await ref.read(publicApiProvider).createReservation(
|
|
widget.slug,
|
|
guestName: _nameController.text,
|
|
guestPhone: _phoneController.text,
|
|
date: _formatDate(_date!),
|
|
time: '${_time.hour.toString().padLeft(2, '0')}:${_time.minute.toString().padLeft(2, '0')}:00',
|
|
partySize: _partySize,
|
|
notes: _notesController.text.isEmpty ? null : _notesController.text,
|
|
);
|
|
setState(() => _message = 'رزرو ثبت شد — منتظر تأیید کافه باشید');
|
|
} catch (_) {
|
|
setState(() => _message = 'خطا در ثبت رزرو');
|
|
} finally {
|
|
setState(() => _submitting = false);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: Scaffold(
|
|
appBar: AppBar(title: const Text('رزرو میز')),
|
|
body: ListView(
|
|
padding: const EdgeInsets.all(16),
|
|
children: [
|
|
TextField(controller: _nameController, decoration: const InputDecoration(labelText: 'نام')),
|
|
TextField(
|
|
controller: _phoneController,
|
|
decoration: const InputDecoration(labelText: 'موبایل'),
|
|
keyboardType: TextInputType.phone,
|
|
),
|
|
ListTile(
|
|
title: Text(_date == null ? 'تاریخ' : _formatDate(_date!)),
|
|
trailing: const Icon(Icons.calendar_today),
|
|
onTap: () async {
|
|
final picked = await showDatePicker(
|
|
context: context,
|
|
initialDate: DateTime.now(),
|
|
firstDate: DateTime.now(),
|
|
lastDate: DateTime.now().add(const Duration(days: 90)),
|
|
);
|
|
if (picked != null) setState(() => _date = Jalali.fromDateTime(picked));
|
|
},
|
|
),
|
|
ListTile(
|
|
title: Text('ساعت ${_time.format(context)}'),
|
|
trailing: const Icon(Icons.access_time),
|
|
onTap: () async {
|
|
final picked = await showTimePicker(context: context, initialTime: _time);
|
|
if (picked != null) setState(() => _time = picked);
|
|
},
|
|
),
|
|
Row(
|
|
children: [
|
|
const Text('تعداد نفر:'),
|
|
IconButton(onPressed: () => setState(() => _partySize = (_partySize - 1).clamp(1, 20)), icon: const Icon(Icons.remove)),
|
|
Text('$_partySize'),
|
|
IconButton(onPressed: () => setState(() => _partySize = (_partySize + 1).clamp(1, 20)), icon: const Icon(Icons.add)),
|
|
],
|
|
),
|
|
TextField(
|
|
controller: _notesController,
|
|
decoration: const InputDecoration(labelText: 'یادداشت'),
|
|
maxLines: 2,
|
|
),
|
|
const SizedBox(height: 16),
|
|
FilledButton(
|
|
onPressed: _submitting ? null : _submit,
|
|
child: _submitting ? const CircularProgressIndicator() : const Text('ثبت رزرو'),
|
|
),
|
|
if (_message != null) ...[
|
|
const SizedBox(height: 12),
|
|
Text(_message!, style: TextStyle(color: Theme.of(context).colorScheme.primary)),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|