Compare commits
5 Commits
b7a525056a
...
refactor-n
| Author | SHA1 | Date | |
|---|---|---|---|
| 123c006a1e | |||
| 415811f592 | |||
| 31066a4d8f | |||
| b700c2de8d | |||
| fda5b8fe2e |
@@ -21,17 +21,17 @@ jobs:
|
||||
- name: Build Flutter Windows
|
||||
run: flutter build windows --release
|
||||
|
||||
- name: Compila Installer Inno Setup
|
||||
- name: Build Windows Installer
|
||||
run: |
|
||||
$TagVersion = "${{ gitea.ref_name }}".Substring(1)
|
||||
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" /DMyAppVersion=$TagVersion "win_installer.iss"
|
||||
$TagVersion = "${{ github.ref_name }}".Substring(1)
|
||||
& "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" "/DMyAppVersion=$TagVersion" "win_installer.iss"
|
||||
|
||||
# Nel dubbio usiamo l'action per caricare l'asset
|
||||
- name: Upload Windows Asset
|
||||
uses: https://gitea.com/actions/release-action@main
|
||||
with:
|
||||
files: "build/windows/installer/FLUX_Setup_x64.exe"
|
||||
api_key: ${{ secrets.GITEA_TOKEN }}
|
||||
files: "build/windows/installer/FluxInstaller.exe"
|
||||
api_key: ${{ secrets.MYRELEASE_TOKEN }}
|
||||
|
||||
- name: Pulisci Workspace Windows
|
||||
if: always()
|
||||
@@ -54,14 +54,14 @@ jobs:
|
||||
EOF
|
||||
|
||||
- name: Build Flutter APK
|
||||
run: flutter build apk --release --split-per-abi
|
||||
run: flutter build apk --release
|
||||
|
||||
# Carichiamo l'APK universale o quelli splittati nelle release di Gitea
|
||||
- name: Upload Android Asset
|
||||
uses: https://gitea.com/actions/release-action@main
|
||||
with:
|
||||
files: "build/app/outputs/flutter-apk/app-release.apk"
|
||||
api_key: ${{ secrets.GITEA_TOKEN }}
|
||||
api_key: ${{ secrets.MYRELEASE_TOKEN }}
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# JOB 3: WEB & CLOUDFLARE DEPLOY (Gira sul tuo MacBook)
|
||||
|
||||
@@ -1,97 +1,370 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flux/core/routes/routes.dart';
|
||||
import 'package:flux/core/utils/extensions.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
// ==========================================
|
||||
// 1. IL GUSCIO (QUELLO CHE PASSI AL ROUTER)
|
||||
// ==========================================
|
||||
class AppShell extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const AppShell({super.key, required this.child});
|
||||
|
||||
// Calcoliamo l'indice attivo in base all'URL corrente!
|
||||
int _calculateSelectedIndex(BuildContext context) {
|
||||
final String location = GoRouterState.of(context).uri.path;
|
||||
if (location.startsWith('/master-data')) return 1;
|
||||
if (location.startsWith('/settings')) return 2;
|
||||
return 0; // Default: Dashboard
|
||||
}
|
||||
|
||||
void _onItemTapped(int index, BuildContext context) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
context.go('/');
|
||||
break;
|
||||
case 1:
|
||||
context.go('/master-data');
|
||||
break;
|
||||
case 2:
|
||||
context.go('/settings');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final currentIndex = _calculateSelectedIndex(context);
|
||||
// Breakpoint: se lo schermo è più largo di 600px, usiamo la Rail laterale
|
||||
final isDesktop = MediaQuery.sizeOf(context).width >= 600;
|
||||
// Breakpoint a 900px: sotto è Mobile/Tablet (Drawer), sopra è Desktop (Sidebar)
|
||||
final isDesktop = MediaQuery.sizeOf(context).width >= 900;
|
||||
final currentPath = GoRouterState.of(context).uri.path;
|
||||
|
||||
return Scaffold(
|
||||
// Su mobile usiamo un'AppBar minimale per avere il bottone "Hamburger" nativo
|
||||
appBar: isDesktop
|
||||
? null
|
||||
: AppBar(
|
||||
title: const Text(
|
||||
"FLUX",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
centerTitle: true,
|
||||
elevation: 0,
|
||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
),
|
||||
drawer: isDesktop
|
||||
? null
|
||||
: Drawer(
|
||||
// Su mobile inietta il menu qui!
|
||||
child: AppMenu(currentPath: currentPath, isDrawer: true),
|
||||
),
|
||||
body: isDesktop
|
||||
? Row(
|
||||
children: [
|
||||
NavigationRail(
|
||||
selectedIndex: currentIndex,
|
||||
onDestinationSelected: (index) =>
|
||||
_onItemTapped(index, context),
|
||||
labelType: NavigationRailLabelType.all,
|
||||
destinations: [
|
||||
NavigationRailDestination(
|
||||
icon: Icon(Icons.dashboard_outlined),
|
||||
selectedIcon: Icon(Icons.dashboard),
|
||||
label: Text(context.l10n.commonDashboard),
|
||||
),
|
||||
NavigationRailDestination(
|
||||
icon: Icon(Icons.folder_special_outlined),
|
||||
selectedIcon: Icon(Icons.folder_special),
|
||||
label: Text(context.l10n.commonMasterData),
|
||||
),
|
||||
NavigationRailDestination(
|
||||
icon: Icon(Icons.settings_outlined),
|
||||
selectedIcon: Icon(Icons.settings),
|
||||
label: Text(context.l10n.commonSettings),
|
||||
),
|
||||
],
|
||||
),
|
||||
// Su desktop inietta il menu a sinistra!
|
||||
AppMenu(currentPath: currentPath, isDrawer: false),
|
||||
const VerticalDivider(thickness: 1, width: 1),
|
||||
// Il contenuto della pagina
|
||||
Expanded(child: child),
|
||||
],
|
||||
)
|
||||
: child, // Su mobile il contenuto prende tutto lo schermo...
|
||||
// ... e mettiamo la barra in basso!
|
||||
bottomNavigationBar: isDesktop
|
||||
? null
|
||||
: NavigationBar(
|
||||
selectedIndex: currentIndex,
|
||||
onDestinationSelected: (index) => _onItemTapped(index, context),
|
||||
destinations: [
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.dashboard_outlined),
|
||||
selectedIcon: Icon(Icons.dashboard),
|
||||
label: context.l10n.commonDashboard,
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.folder_special_outlined),
|
||||
selectedIcon: Icon(Icons.folder_special),
|
||||
label: context.l10n.commonMasterData,
|
||||
),
|
||||
NavigationDestination(
|
||||
icon: Icon(Icons.settings_outlined),
|
||||
selectedIcon: Icon(Icons.settings),
|
||||
label: context.l10n.commonSettings,
|
||||
),
|
||||
],
|
||||
),
|
||||
: child, // Su mobile il child prende tutto lo schermo sotto l'AppBar
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppMenu extends StatefulWidget {
|
||||
final String currentPath; // Lo usiamo ancora per capire cosa accendere
|
||||
final bool isDrawer;
|
||||
|
||||
const AppMenu({super.key, required this.currentPath, required this.isDrawer});
|
||||
|
||||
@override
|
||||
State<AppMenu> createState() => _AppMenuState();
|
||||
}
|
||||
|
||||
class _AppMenuState extends State<AppMenu> {
|
||||
bool _isCollapsed = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final bool effectivelyCollapsed = _isCollapsed && !widget.isDrawer;
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeInOut,
|
||||
width: effectivelyCollapsed ? 72 : 260,
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
// --- HEADER ---
|
||||
Container(
|
||||
height: 80,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
alignment: effectivelyCollapsed
|
||||
? Alignment.center
|
||||
: Alignment.centerLeft,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.bolt, color: theme.colorScheme.primary, size: 32),
|
||||
if (!effectivelyCollapsed) ...[
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
"FLUX",
|
||||
style: theme.textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// --- VOCI DI MENU ---
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
child: SizedBox(
|
||||
width: effectivelyCollapsed ? 72 : 260,
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||
children: [
|
||||
_buildRouteItem(
|
||||
title: context.l10n.commonDashboard,
|
||||
icon: Icons.dashboard_outlined,
|
||||
routeName: Routes.home, // <--- Usiamo la tua costante!
|
||||
pathToCheck:
|
||||
'/', // Il path da controllare per colorarlo
|
||||
isCollapsed: effectivelyCollapsed,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// --- IL MENU GERARCHICO (ANAGRAFICHE) ---
|
||||
_buildHierarchicalItem(
|
||||
title: context.l10n.commonMasterData,
|
||||
icon: Icons.folder_special_outlined,
|
||||
basePathToCheck:
|
||||
'/master-data', // Se il path inizia così, espandi
|
||||
isCollapsed: effectivelyCollapsed,
|
||||
subItems: [
|
||||
_SubMenuItem(
|
||||
"Clienti",
|
||||
Routes.customers,
|
||||
'/master-data/customers',
|
||||
),
|
||||
_SubMenuItem(
|
||||
"Fornitori",
|
||||
Routes.providers,
|
||||
'/master-data/providers',
|
||||
),
|
||||
_SubMenuItem(
|
||||
"Prodotti",
|
||||
Routes.products,
|
||||
'/master-data/products',
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(height: 8),
|
||||
_buildRouteItem(
|
||||
title: context.l10n.commonSettings,
|
||||
icon: Icons.settings_outlined,
|
||||
routeName: Routes.settings,
|
||||
pathToCheck: '/settings',
|
||||
isCollapsed: effectivelyCollapsed,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// --- PULSANTE TOGGLE (Solo Desktop) ---
|
||||
if (!widget.isDrawer)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: IconButton(
|
||||
tooltip: _isCollapsed ? 'Espandi Menu' : 'Riduci Menu',
|
||||
icon: Icon(
|
||||
_isCollapsed
|
||||
? Icons.keyboard_double_arrow_right
|
||||
: Icons.keyboard_double_arrow_left,
|
||||
color: theme.iconTheme.color?.withValues(alpha: 0.5),
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_isCollapsed = !_isCollapsed;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// WIDGET HELPER AGGIORNATI
|
||||
// ==========================================
|
||||
|
||||
Widget _buildRouteItem({
|
||||
required String title,
|
||||
required IconData icon,
|
||||
required String routeName,
|
||||
required String pathToCheck,
|
||||
required bool isCollapsed,
|
||||
}) {
|
||||
final isSelected = widget.currentPath == pathToCheck;
|
||||
final theme = Theme.of(context);
|
||||
|
||||
if (isCollapsed) {
|
||||
return Tooltip(
|
||||
message: title,
|
||||
preferBelow: false,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (widget.isDrawer) Navigator.pop(context);
|
||||
context.goNamed(routeName); // <--- goNamed!
|
||||
},
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
height: 48,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? theme.colorScheme.primaryContainer.withValues(alpha: 0.4)
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(
|
||||
icon,
|
||||
color: isSelected ? theme.colorScheme.primary : null,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListTile(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
selectedTileColor: theme.colorScheme.primaryContainer.withValues(
|
||||
alpha: 0.4,
|
||||
),
|
||||
selected: isSelected,
|
||||
leading: Icon(icon, color: isSelected ? theme.colorScheme.primary : null),
|
||||
title: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
onTap: () {
|
||||
if (widget.isDrawer) Navigator.pop(context);
|
||||
context.goNamed(routeName); // <--- goNamed!
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHierarchicalItem({
|
||||
required String title,
|
||||
required IconData icon,
|
||||
required String basePathToCheck,
|
||||
required bool isCollapsed,
|
||||
required List<_SubMenuItem> subItems,
|
||||
}) {
|
||||
final isSelected = widget.currentPath.startsWith(basePathToCheck);
|
||||
final theme = Theme.of(context);
|
||||
|
||||
if (isCollapsed) {
|
||||
return PopupMenuButton<String>(
|
||||
tooltip: title,
|
||||
offset: const Offset(60, 0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
onSelected: (routeName) {
|
||||
// Il routeName arriva dal value del menu
|
||||
if (widget.isDrawer) Navigator.pop(context);
|
||||
context.goNamed(routeName); // <--- goNamed!
|
||||
},
|
||||
itemBuilder: (context) => subItems
|
||||
.map(
|
||||
(item) => PopupMenuItem(
|
||||
value: item
|
||||
.routeName, // Passiamo il nome della rotta (Routes.customers)
|
||||
child: Text(
|
||||
item.title,
|
||||
style: TextStyle(
|
||||
fontWeight: widget.currentPath == item.pathToCheck
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
color: widget.currentPath == item.pathToCheck
|
||||
? theme.colorScheme.primary
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
child: Container(
|
||||
height: 48,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected
|
||||
? theme.colorScheme.primaryContainer.withValues(alpha: 0.4)
|
||||
: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Icon(
|
||||
icon,
|
||||
color: isSelected ? theme.colorScheme.primary : null,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Theme(
|
||||
data: theme.copyWith(dividerColor: Colors.transparent),
|
||||
child: ExpansionTile(
|
||||
initiallyExpanded: isSelected,
|
||||
maintainState: true,
|
||||
tilePadding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
leading: Icon(
|
||||
icon,
|
||||
color: isSelected ? theme.colorScheme.primary : null,
|
||||
),
|
||||
title: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
children: subItems.map((item) {
|
||||
final isSubSelected = widget.currentPath == item.pathToCheck;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 32.0, bottom: 4.0),
|
||||
child: ListTile(
|
||||
dense: true,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
selectedTileColor: theme.colorScheme.primaryContainer.withValues(
|
||||
alpha: 0.2,
|
||||
),
|
||||
selected: isSubSelected,
|
||||
title: Text(
|
||||
item.title,
|
||||
style: TextStyle(
|
||||
fontWeight: isSubSelected
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
color: isSubSelected
|
||||
? theme.colorScheme.primary
|
||||
: theme.textTheme.bodyMedium?.color,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
onTap: () {
|
||||
if (widget.isDrawer) Navigator.pop(context);
|
||||
context.goNamed(item.routeName); // <--- goNamed!
|
||||
},
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Struttura dati per le voci dei sottomenu aggiornata
|
||||
class _SubMenuItem {
|
||||
final String title;
|
||||
final String routeName; // Es: Routes.customers
|
||||
final String pathToCheck; // Es: '/master-data/customers'
|
||||
_SubMenuItem(this.title, this.routeName, this.pathToCheck);
|
||||
}
|
||||
|
||||
@@ -126,78 +126,89 @@ class AppRouter {
|
||||
ShellRoute(
|
||||
builder: (context, state, child) => AppShell(child: child),
|
||||
routes: [
|
||||
// ==========================================
|
||||
// 1. DASHBOARD
|
||||
// ==========================================
|
||||
GoRoute(
|
||||
path: '/',
|
||||
name: Routes.home,
|
||||
builder: (context, state) => const HomeScreen(),
|
||||
),
|
||||
|
||||
// ==========================================
|
||||
// 2. HUB ANAGRAFICHE E SOTTO-ROTTE
|
||||
// ==========================================
|
||||
GoRoute(
|
||||
path: '/master-data',
|
||||
name: Routes.masterData,
|
||||
builder: (context, state) => const MasterDataHubScreen(),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'products', // Diventa /master-data/products
|
||||
path:
|
||||
'customers', // Niente slash iniziale per le sottorotte! -> /master-data/customers
|
||||
name: Routes.customers,
|
||||
builder: (context, state) => const CustomersListScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'providers', // -> /master-data/providers
|
||||
name: Routes.providers,
|
||||
builder: (context, state) => const ProviderListScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'products', // -> /master-data/products
|
||||
name: Routes.products,
|
||||
builder: (context, state) {
|
||||
context.read<ProductsCubit>().refreshCubit();
|
||||
|
||||
return const ProductsScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
path: 'company-settings',
|
||||
path: 'staff', // -> /master-data/staff
|
||||
name: Routes.staff,
|
||||
builder: (context, state) => const StaffScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path:
|
||||
'stores', // Sistemata l'inversione path/name -> /master-data/stores
|
||||
name: Routes.stores,
|
||||
builder: (context, state) => const StoresScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'company-settings', // -> /master-data/company-settings
|
||||
name: Routes.companySettings,
|
||||
builder: (context, state) => BlocProvider(
|
||||
create: (context) => CompanySettingsCubit(),
|
||||
child: const CompanySettingsScreen(),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'staff',
|
||||
name: Routes.staff, // Diventa /master-data/staff
|
||||
builder: (context, state) => const StaffScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: Routes.stores,
|
||||
name: 'stores', // Diventa /master-data/stores
|
||||
builder: (context, state) => const StoresScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/providers',
|
||||
name: Routes.providers,
|
||||
builder: (context, state) => const ProviderListScreen(),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// ==========================================
|
||||
// 3. IMPOSTAZIONI
|
||||
// ==========================================
|
||||
GoRoute(
|
||||
path: '/settings',
|
||||
name: Routes.settings,
|
||||
builder: (context, state) => const SettingsScreen(),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'themeSettings',
|
||||
path: 'themeSettings', // -> /settings/themeSettings
|
||||
name: Routes.themeSettings,
|
||||
builder: (context, state) => const ThemeSettingsView(),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// ==========================================
|
||||
// 4. SCHERMATE PRINCIPALI EXTRA NELLA SHELL
|
||||
// (Accessibili ad es. dalla dashboard, mantengono la sidebar)
|
||||
// ==========================================
|
||||
GoRoute(
|
||||
path: '/operations',
|
||||
name: Routes.operations,
|
||||
builder: (context, state) => const OperationListScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/customers',
|
||||
name: Routes.customers,
|
||||
builder: (context, state) =>
|
||||
const CustomersListScreen(), // O come si chiama il tuo widget della lista!
|
||||
),
|
||||
GoRoute(
|
||||
path: '/tickets',
|
||||
name: Routes.tickets,
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:flux/core/blocs/session/session_cubit.dart';
|
||||
import 'package:flux/core/routes/routes.dart';
|
||||
import 'package:flux/core/theme/theme.dart';
|
||||
import 'package:flux/features/settings/blocs/settings_cubit.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class SettingsScreen extends StatelessWidget {
|
||||
@@ -17,20 +18,53 @@ class SettingsScreen extends StatelessWidget {
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
_settingsSection('Account', [
|
||||
_settingsSection('Azienda', [
|
||||
_settingsTile(
|
||||
icon: Icons.person,
|
||||
title: 'Profilo Utente',
|
||||
subtitle: 'Configura i tuoi dati',
|
||||
title: 'Impostazioni Azienda',
|
||||
icon: Icons.business,
|
||||
subtitle: 'Configura i dati aziendali',
|
||||
context: context,
|
||||
onTap: () {},
|
||||
onTap: () => context.pushNamed(Routes.companySettings),
|
||||
),
|
||||
const Divider(height: 30),
|
||||
_settingsTile(
|
||||
title: 'Impostazione Negozi',
|
||||
icon: Icons.store,
|
||||
subtitle: 'Crea o configura i negozi',
|
||||
context: context,
|
||||
onTap: () => context.pushNamed(Routes.stores),
|
||||
),
|
||||
const Divider(height: 30),
|
||||
_settingsTile(
|
||||
title: 'Impostazione Staff / Utenti',
|
||||
icon: Icons.group,
|
||||
subtitle:
|
||||
'Configura i membri dei negozi o invita nuovi utenti in azienda',
|
||||
context: context,
|
||||
onTap: () => context.pushNamed(Routes.staff),
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 20),
|
||||
_settingsSection('Applicazione', [
|
||||
BlocBuilder<SettingsCubit, SettingsState>(
|
||||
builder: (context, state) => CheckboxListTile(
|
||||
value: state.isSingleUserMode,
|
||||
title: const Text(
|
||||
'Modalità utente singolo (dispositivo personale)',
|
||||
|
||||
title: Row(
|
||||
children: [
|
||||
const Icon(Icons.person, color: FluxColors.primaryBlue),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
'Modalità utente singolo (dispositivo personale)',
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Padding(
|
||||
padding: const EdgeInsets.only(left: 36),
|
||||
child: Text(
|
||||
'Utente ${GetIt.I.get<SessionCubit>().state.currentStaffMember?.name ?? 'Nessuno'} selezionato automaticamente',
|
||||
),
|
||||
),
|
||||
onChanged: (value) {
|
||||
context.read<SessionCubit>().setIsSingleUserMode(value!);
|
||||
@@ -39,16 +73,6 @@ class SettingsScreen extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const Divider(height: 30),
|
||||
_settingsTile(
|
||||
title: 'Impostazioni Azienda',
|
||||
icon: Icons.business,
|
||||
subtitle: 'Configura i dati aziendali',
|
||||
context: context,
|
||||
onTap: () => context.pushNamed(Routes.companySettings),
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
_settingsSection('Applicazione', [
|
||||
_settingsTile(
|
||||
icon: Icons.dark_mode,
|
||||
title: 'Tema (FLUX Dark)',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: flux
|
||||
description: "Gestione attività negozio di telefonia"
|
||||
publish_to: 'none'
|
||||
version: 1.0.10+10
|
||||
version: 1.0.12+12
|
||||
|
||||
environment:
|
||||
sdk: ^3.11.3
|
||||
|
||||
@@ -9,7 +9,7 @@ DefaultDirName={autopf}\Flux
|
||||
|
||||
; Cartella di output dell'installer e nome del file generato
|
||||
OutputDir=.\build\windows\installer
|
||||
OutputBaseFilename=FLUX_Setup_v{#MyAppVersion}
|
||||
OutputBaseFilename=FluxInstaller
|
||||
|
||||
; Compressione e impostazioni grafiche
|
||||
Compression=lzma2/max
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Project-level configuration.
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(flux LANGUAGES CXX)
|
||||
add_compile_definitions(_SILENCE_EXPERIMENTAL_COROUTINE_DEPRECATION_WARNINGS)
|
||||
|
||||
# The name of the executable created for the application. Change this to change
|
||||
# the on-disk name of your application.
|
||||
|
||||
Reference in New Issue
Block a user