Frontend first draft

This commit is contained in:
Arseni
2025-11-13 15:06:15 +03:00
parent e47f343afb
commit ddb54ddfdc
504 changed files with 25498 additions and 1 deletions

View File

@@ -0,0 +1,46 @@
import 'package:flutter/material.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
enum PayoutDestination {
dashboard(Icons.dashboard_outlined, 'dashboard'),
sendPayout(Icons.send_outlined, 'sendPayout'),
recipients(Icons.people_outline, 'recipients'),
reports(Icons.insert_chart, 'reports'),
settings(Icons.settings_outlined, 'settings'),
methods(Icons.credit_card, 'methods'),
payment(Icons.payment, 'payout'),
addrecipient(Icons.app_registration, 'add recipient'),
editwallet(Icons.wallet, 'edit wallet');
const PayoutDestination(this.icon, this.labelKey);
final IconData icon;
final String labelKey;
String localizedLabel(BuildContext context) {
final loc = AppLocalizations.of(context)!;
switch (this) {
case PayoutDestination.dashboard:
return loc.payoutNavDashboard;
case PayoutDestination.sendPayout:
return loc.payoutNavSendPayout;
case PayoutDestination.recipients:
return loc.payoutNavRecipients;
case PayoutDestination.reports:
return loc.payoutNavReports;
case PayoutDestination.settings:
return loc.payoutNavSettings;
case PayoutDestination.methods:
return loc.payoutNavMethods;
case PayoutDestination.payment:
return loc.payout;
case PayoutDestination.addrecipient:
return loc.addRecipient;
case PayoutDestination.editwallet:
return 'Edit Wallet';
}
}
}

View File

@@ -0,0 +1,111 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:pweb/pages/address_book/form/page.dart';
import 'package:pweb/pages/address_book/page/page.dart';
import 'package:pweb/pages/payment_methods/page.dart';
import 'package:pweb/pages/payment_page/page.dart';
import 'package:pweb/pages/payment_page/wallet/edit/page.dart';
import 'package:pweb/pages/report/page.dart';
import 'package:pweb/pages/settings/profile/page.dart';
import 'package:pweb/providers/page_selector.dart';
import 'package:pweb/widgets/appbar/app_bar.dart';
import 'package:pweb/pages/dashboard/dashboard.dart';
import 'package:pweb/widgets/sidebar/destinations.dart';
import 'package:pweb/widgets/sidebar/sidebar.dart';
class PageSelector extends StatelessWidget {
const PageSelector({super.key});
@override
Widget build(BuildContext context) {
final provider = context.watch<PageSelectorProvider>();
Widget content;
switch (provider.selected) {
case PayoutDestination.dashboard:
content = DashboardPage(
onRecipientSelected: (recipient) =>
provider.selectRecipient(recipient),
onGoToPaymentWithoutRecipient: provider.startPaymentWithoutRecipient,
);
break;
case PayoutDestination.recipients:
content = RecipientAddressBookPage(
onRecipientSelected: (recipient) =>
provider.selectRecipient(recipient, fromList: true),
onAddRecipient: provider.goToAddRecipient,
onEditRecipient: provider.editRecipient,
);
break;
case PayoutDestination.addrecipient:
final recipient = provider.recipientProvider?.selectedRecipient;
content = AdressBookRecipientForm(
recipient: recipient,
onSaved: (_) => provider.selectPage(PayoutDestination.recipients),
);
break;
case PayoutDestination.payment:
content = PaymentPage(
type: provider.type,
onBack: (_) => provider.goBackFromPayment(),
);
break;
case PayoutDestination.settings:
content = ProfileSettingsPage();
break;
case PayoutDestination.reports:
content = OperationHistoryPage();
break;
case PayoutDestination.methods:
content = PaymentConfigPage(
onWalletTap: provider.selectWallet,
);
break;
case PayoutDestination.editwallet:
final wallet = provider.walletsProvider?.selectedWallet;
content = wallet != null
? WalletEditPage(
wallet: wallet,
onBack: () => provider.goBackFromPayment(),
)
: const Center(child: Text('No wallet selected')); //TODO Localize
break;
default:
content = Text(provider.selected.name);
}
return Scaffold(
appBar: PayoutAppBar(
title: Text(provider.selected.localizedLabel(context)),
onAddFundsPressed: () {},
onLogout: () => debugPrint('Logout clicked'),
),
body: Padding(
padding: const EdgeInsets.only(left: 200, top: 40, right: 200),
child: Row(
spacing: 40,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
PayoutSidebar(
selected: provider.selected,
onSelected: provider.selectPage,
onLogout: () => debugPrint('Logout clicked'),
),
Expanded(child: content),
],
),
),
);
}
}

View File

@@ -0,0 +1,79 @@
import 'package:flutter/material.dart';
import 'package:pweb/services/amplitude.dart';
import 'package:pweb/widgets/sidebar/destinations.dart';
class SideMenuColumn extends StatelessWidget {
final ThemeData theme;
final String? avatarUrl;
final String? userName;
final List<PayoutDestination> items;
final PayoutDestination selected;
final void Function(PayoutDestination) onSelected;
const SideMenuColumn({
super.key,
required this.theme,
required this.avatarUrl,
required this.userName,
required this.items,
required this.selected,
required this.onSelected,
});
@override
Widget build(BuildContext context) {
return ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 320),
child: Material(
elevation: 4,
borderRadius: BorderRadius.circular(16),
color: theme.colorScheme.onSecondary,
child: ListView(
shrinkWrap: true,
padding: const EdgeInsets.symmetric(vertical: 8),
children: items.map((item) {
final isSelected = item == selected;
final backgroundColor = isSelected
? theme.colorScheme.primaryContainer
: Colors.transparent;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: Container(
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(12),
),
child: InkWell(
onTap: () {
onSelected(item);
AmplitudeService.pageOpened(item, uiSource: 'sidebar');
},
borderRadius: BorderRadius.circular(12),
hoverColor: theme.colorScheme.primaryContainer,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 12),
child: Row(
children: [
Icon(item.icon, color: theme.iconTheme.color, size: 28),
const SizedBox(width: 16),
Text(
item.localizedLabel(context),
style: theme.textTheme.bodyMedium?.copyWith(
fontSize: 16,
),
),
],
),
),
),
),
);
}).toList(),
),
),
);
}
}

View File

@@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:pweb/widgets/sidebar/destinations.dart';
import 'package:pweb/widgets/sidebar/side_menu.dart';
import 'package:pweb/widgets/sidebar/user.dart';
class PayoutSidebar extends StatelessWidget {
const PayoutSidebar({
super.key,
required this.selected,
required this.onSelected,
this.onLogout,
this.userName,
this.avatarUrl,
});
final PayoutDestination selected;
final ValueChanged<PayoutDestination> onSelected;
final VoidCallback? onLogout;
final String? userName;
final String? avatarUrl;
@override
Widget build(BuildContext context) {
final items = [
PayoutDestination.dashboard,
PayoutDestination.recipients,
PayoutDestination.methods,
PayoutDestination.reports,
];
final theme = Theme.of(context);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
UserProfileCard(
theme: theme,
avatarUrl: avatarUrl,
userName: userName,
selected: selected,
onSelected: onSelected
),
const SizedBox(height: 8),
SideMenuColumn(
theme: theme,
avatarUrl: avatarUrl,
userName: userName,
items: items,
selected: selected,
onSelected: onSelected,
),
],
);
}
}

View File

@@ -0,0 +1,70 @@
import 'package:flutter/material.dart';
import 'package:pweb/widgets/sidebar/destinations.dart';
class UserProfileCard extends StatelessWidget {
final ThemeData theme;
final String? avatarUrl;
final String? userName;
final PayoutDestination selected;
final void Function(PayoutDestination) onSelected;
const UserProfileCard({
super.key,
required this.theme,
required this.avatarUrl,
required this.userName,
required this.selected,
required this.onSelected,
});
@override
Widget build(BuildContext context) {
bool isSelected = selected == PayoutDestination.settings;
final backgroundColor = isSelected
? theme.colorScheme.primaryContainer
: Colors.transparent;
return Material(
elevation: 4,
borderRadius: BorderRadius.circular(14),
color: theme.colorScheme.onSecondary,
child: InkWell(
borderRadius: BorderRadius.circular(14),
onTap: () => onSelected(PayoutDestination.settings),
child: Container(
height: 80,
width: 320,
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular(14),
),
padding: const EdgeInsets.only(top: 15.0, left: 30, right: 20, bottom: 15),
child: Row(
spacing: 5,
children: [
CircleAvatar(
radius: 20,
foregroundImage: avatarUrl != null ? NetworkImage(avatarUrl!) : null,
child: avatarUrl == null ? const Icon(Icons.person, size: 28) : null,
),
const SizedBox(width: 8),
Flexible(
child: Text(
userName ?? 'User Name',
style: theme.textTheme.bodyLarge?.copyWith(
fontSize: 20,
fontWeight: FontWeight.w500,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
),
);
}
}