mirror of
https://github.com/koloideal/Casha.git
synced 2026-06-10 10:25:28 +03:00
update
This commit is contained in:
@@ -10,6 +10,8 @@ class AddTransactionState {
|
|||||||
final String note;
|
final String note;
|
||||||
final bool isSubmitting;
|
final bool isSubmitting;
|
||||||
final String? editingId;
|
final String? editingId;
|
||||||
|
final String overrideCurrency;
|
||||||
|
final String overrideCurrencyCode;
|
||||||
|
|
||||||
const AddTransactionState({
|
const AddTransactionState({
|
||||||
this.amount,
|
this.amount,
|
||||||
@@ -19,6 +21,8 @@ class AddTransactionState {
|
|||||||
this.note = '',
|
this.note = '',
|
||||||
this.isSubmitting = false,
|
this.isSubmitting = false,
|
||||||
this.editingId,
|
this.editingId,
|
||||||
|
this.overrideCurrency = '\$',
|
||||||
|
this.overrideCurrencyCode = 'USD',
|
||||||
});
|
});
|
||||||
|
|
||||||
factory AddTransactionState.fromTransaction(Transaction tx) {
|
factory AddTransactionState.fromTransaction(Transaction tx) {
|
||||||
@@ -29,6 +33,8 @@ class AddTransactionState {
|
|||||||
date: tx.date,
|
date: tx.date,
|
||||||
note: tx.note ?? '',
|
note: tx.note ?? '',
|
||||||
editingId: tx.id,
|
editingId: tx.id,
|
||||||
|
overrideCurrency: tx.currency,
|
||||||
|
overrideCurrencyCode: tx.currencyCode,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +50,8 @@ class AddTransactionState {
|
|||||||
String? note,
|
String? note,
|
||||||
bool? isSubmitting,
|
bool? isSubmitting,
|
||||||
String? editingId,
|
String? editingId,
|
||||||
|
String? overrideCurrency,
|
||||||
|
String? overrideCurrencyCode,
|
||||||
}) =>
|
}) =>
|
||||||
AddTransactionState(
|
AddTransactionState(
|
||||||
amount: amount ?? this.amount,
|
amount: amount ?? this.amount,
|
||||||
@@ -53,6 +61,8 @@ class AddTransactionState {
|
|||||||
note: note ?? this.note,
|
note: note ?? this.note,
|
||||||
isSubmitting: isSubmitting ?? this.isSubmitting,
|
isSubmitting: isSubmitting ?? this.isSubmitting,
|
||||||
editingId: editingId ?? this.editingId,
|
editingId: editingId ?? this.editingId,
|
||||||
|
overrideCurrency: overrideCurrency ?? this.overrideCurrency,
|
||||||
|
overrideCurrencyCode: overrideCurrencyCode ?? this.overrideCurrencyCode,
|
||||||
);
|
);
|
||||||
|
|
||||||
bool get isEditing => editingId != null;
|
bool get isEditing => editingId != null;
|
||||||
@@ -80,6 +90,10 @@ class AddTransactionNotifier extends StateNotifier<AddTransactionState> {
|
|||||||
|
|
||||||
void setSubmitting(bool v) => state = state.copyWith(isSubmitting: v);
|
void setSubmitting(bool v) => state = state.copyWith(isSubmitting: v);
|
||||||
|
|
||||||
|
void setCurrency(String symbol, String code) {
|
||||||
|
state = state.copyWith(overrideCurrency: symbol, overrideCurrencyCode: code);
|
||||||
|
}
|
||||||
|
|
||||||
void reset() => state = AddTransactionState.empty();
|
void reset() => state = AddTransactionState.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,12 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
|||||||
if (widget.initial != null) {
|
if (widget.initial != null) {
|
||||||
_amountController.text = widget.initial!.amount.toString();
|
_amountController.text = widget.initial!.amount.toString();
|
||||||
_noteController.text = widget.initial!.note ?? '';
|
_noteController.text = widget.initial!.note ?? '';
|
||||||
|
} else {
|
||||||
|
// Set default currency from global provider after first frame
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
final curr = ref.read(currencyProvider);
|
||||||
|
ref.read(addTransactionProvider(null).notifier).setCurrency(curr.symbol, curr.code);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +59,6 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
|||||||
Future<void> _submit() async {
|
Future<void> _submit() async {
|
||||||
if (!_formKey.currentState!.validate()) return;
|
if (!_formKey.currentState!.validate()) return;
|
||||||
final state = ref.read(addTransactionProvider(widget.initial));
|
final state = ref.read(addTransactionProvider(widget.initial));
|
||||||
final currencyInfo = ref.read(currencyProvider);
|
|
||||||
ref.read(addTransactionProvider(widget.initial).notifier).setSubmitting(true);
|
ref.read(addTransactionProvider(widget.initial).notifier).setSubmitting(true);
|
||||||
|
|
||||||
final note = _noteController.text.trim();
|
final note = _noteController.text.trim();
|
||||||
@@ -65,8 +70,8 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
|||||||
type: state.type,
|
type: state.type,
|
||||||
date: state.date,
|
date: state.date,
|
||||||
note: note.isEmpty ? null : note,
|
note: note.isEmpty ? null : note,
|
||||||
currency: currencyInfo.symbol,
|
currency: state.overrideCurrency,
|
||||||
currencyCode: currencyInfo.code,
|
currencyCode: state.overrideCurrencyCode,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (state.isEditing) {
|
if (state.isEditing) {
|
||||||
@@ -105,7 +110,7 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final state = ref.watch(addTransactionProvider(widget.initial));
|
final state = ref.watch(addTransactionProvider(widget.initial));
|
||||||
final categories = ref.watch(availableCategoriesProvider(widget.initial));
|
final categories = ref.watch(availableCategoriesProvider(widget.initial));
|
||||||
final currencyInfo = ref.watch(currencyProvider);
|
final overrideCurrency = state.overrideCurrency;
|
||||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -181,7 +186,7 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
child: Text(
|
child: Text(
|
||||||
currencyInfo.symbol,
|
overrideCurrency,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
@@ -226,6 +231,22 @@ class _AddTransactionScreenState extends ConsumerState<AddTransactionScreen> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
// Currency
|
||||||
|
Text(
|
||||||
|
'Currency',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
_CurrencyPicker(
|
||||||
|
selected: state.overrideCurrencyCode,
|
||||||
|
onChanged: (symbol, code) =>
|
||||||
|
ref.read(addTransactionProvider(widget.initial).notifier).setCurrency(symbol, code),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
// Category
|
// Category
|
||||||
_SectionLabel('Category'),
|
_SectionLabel('Category'),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
@@ -484,3 +505,65 @@ class _CategoryPicker extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _CurrencyPicker extends StatelessWidget {
|
||||||
|
final String selected;
|
||||||
|
final void Function(String symbol, String code) onChanged;
|
||||||
|
const _CurrencyPicker({
|
||||||
|
required this.selected,
|
||||||
|
required this.onChanged,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final currencies = [
|
||||||
|
('USD', '\$'),
|
||||||
|
('EUR', '€'),
|
||||||
|
('BYN', 'Br'),
|
||||||
|
('RUB', '₽'),
|
||||||
|
];
|
||||||
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: currencies.map((c) {
|
||||||
|
final isSelected = c.$1 == selected;
|
||||||
|
return Expanded(
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => onChanged(c.$2, c.$1),
|
||||||
|
child: Container(
|
||||||
|
margin: EdgeInsets.only(right: c.$1 == currencies.last.$1 ? 0 : 8),
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: isSelected ? const Color(0xFF7C6DED).withOpacity(0.15) : Theme.of(context).cardColor,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
border: Border.all(
|
||||||
|
color: isSelected ? const Color(0xFF7C6DED) : Colors.transparent,
|
||||||
|
width: 1.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
c.$2,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: isSelected ? const Color(0xFF7C6DED) : colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
c.$1,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
color: colorScheme.onSurface.withOpacity(0.5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ class _SearchBar extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
return TextField(
|
return TextField(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
@@ -212,6 +213,23 @@ class _SearchBar extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
|
filled: true,
|
||||||
|
fillColor: Theme.of(context).inputDecorationTheme.fillColor,
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: isDark ? Colors.transparent : const Color(0xFFCCCCDD),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: const BorderSide(color: Color(0xFF7C6DED), width: 1.5),
|
||||||
|
),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
borderSide: BorderSide.none,
|
||||||
|
),
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
),
|
),
|
||||||
onChanged: (v) => ref.read(searchQueryProvider.notifier).state = v,
|
onChanged: (v) => ref.read(searchQueryProvider.notifier).state = v,
|
||||||
|
|||||||
Reference in New Issue
Block a user