refactor: save current workspace state including FinlyticAnalyzer fixes, FinlyticApp trade route alignment, and DTO audit documentation

This commit is contained in:
2026-08-12 18:30:42 +02:00
parent a9553e9fbf
commit 3d8af3940b
163 changed files with 3421 additions and 1751 deletions
@@ -17,9 +17,12 @@ class CorporateCalendarScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => CalendarBloc(
repository: CalendarRepository(apiClient: apiClient),
)..add(FetchCalendarEvents()),
create: (context) {
final now = DateTime.now();
return CalendarBloc(
repository: CalendarRepository(apiClient: apiClient),
)..add(FetchCalendarEvents(year: now.year, month: now.month));
},
child: _CorporateCalendarScreenContent(apiClient: apiClient),
);
}
@@ -50,125 +53,130 @@ class _CorporateCalendarScreenContent extends StatelessWidget {
if (state is CalendarLoaded) {
final filtered = state.filteredEvents;
return SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MonthCalendarWidget(
currentMonth: state.currentMonth,
selectedDate: state.selectedDate,
events: state.allEvents.map((e) => e.toJson()).toList(),
onDateSelected: (date) {
context.read<CalendarBloc>().add(FilterDateSelected(date));
},
onMonthChanged: (newMonth) {
context.read<CalendarBloc>().add(MonthChanged(newMonth));
},
),
const SizedBox(height: 18),
Row(
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: categories.map((cat) {
final isSelected = state.selectedCategory == cat;
String label = 'Alle';
if (cat == 'Earnings') label = 'Quartalsergebnisse';
if (cat == 'ExDividend') label = 'Ex-Dividendentage';
if (cat == 'Payout') label = 'Zahlungstage';
return Padding(
padding: const EdgeInsets.only(right: 8),
child: ChoiceChip(
label: Text(label),
selected: isSelected,
selectedColor: AppTheme.primaryEmerald.withValues(alpha: 0.25),
backgroundColor: AppTheme.glassSurface,
labelStyle: TextStyle(
color: isSelected ? AppTheme.primaryEmerald : AppTheme.textSecondary,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
fontSize: 12,
),
side: BorderSide(color: isSelected ? AppTheme.primaryEmerald : AppTheme.glassBorder),
onSelected: (_) {
context.read<CalendarBloc>().add(FilterCategoryChanged(cat));
},
),
);
}).toList(),
),
),
),
if (state.selectedDate != null)
TextButton.icon(
onPressed: () {
context.read<CalendarBloc>().add(const FilterDateSelected(null));
return CustomScrollView(
slivers: [
SliverPadding(
padding: const EdgeInsets.all(20),
sliver: SliverToBoxAdapter(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
MonthCalendarWidget(
currentMonth: state.currentMonth,
selectedDate: state.selectedDate,
events: state.allEvents.map((e) => e.toJson()).toList(),
onDateSelected: (date) {
context.read<CalendarBloc>().add(FilterDateSelected(date));
},
onMonthChanged: (newMonth) {
context.read<CalendarBloc>().add(MonthChanged(newMonth));
},
icon: Icon(Icons.clear, size: 14, color: AppTheme.accentCyan),
label: Text('Alle Tage', style: TextStyle(color: AppTheme.accentCyan, fontSize: 12)),
),
],
),
const SizedBox(height: 14),
const SizedBox(height: 18),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
state.selectedDate != null
? 'Termine am ${state.selectedDate!.day.toString().padLeft(2, '0')}.${state.selectedDate!.month.toString().padLeft(2, '0')}.${state.selectedDate!.year} (${filtered.length})'
: 'Anstehende Termine (${filtered.length})',
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold, color: AppTheme.textPrimary),
),
Text('Kachelansicht', style: TextStyle(fontSize: 11, color: AppTheme.textMuted)),
],
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: categories.map((cat) {
final isSelected = state.selectedCategory == cat;
String label = 'Alle';
if (cat == 'Earnings') label = 'Quartalsergebnisse';
if (cat == 'ExDividend') label = 'Ex-Dividendentage';
if (cat == 'Payout') label = 'Zahlungstage';
filtered.isEmpty
? Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: AppTheme.glassSurface,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppTheme.glassBorder),
),
child: Center(
child: Text(
'Keine Unternehmenstermine für diesen Filter/Tag gefunden.',
style: TextStyle(color: AppTheme.textMuted, fontSize: 13),
return Padding(
padding: const EdgeInsets.only(right: 8),
child: ChoiceChip(
label: Text(label),
selected: isSelected,
selectedColor: AppTheme.primaryEmerald.withValues(alpha: 0.25),
backgroundColor: AppTheme.glassSurface,
labelStyle: TextStyle(
color: isSelected ? AppTheme.primaryEmerald : AppTheme.textSecondary,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
fontSize: 12,
),
side: BorderSide(color: isSelected ? AppTheme.primaryEmerald : AppTheme.glassBorder),
onSelected: (_) {
context.read<CalendarBloc>().add(FilterCategoryChanged(cat));
},
),
);
}).toList(),
),
),
),
if (state.selectedDate != null)
TextButton.icon(
onPressed: () {
context.read<CalendarBloc>().add(const FilterDateSelected(null));
},
icon: Icon(Icons.clear, size: 14, color: AppTheme.accentCyan),
label: Text('Alle Tage', style: TextStyle(color: AppTheme.accentCyan, fontSize: 12)),
),
],
),
const SizedBox(height: 14),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
state.selectedDate != null
? 'Termine am ${state.selectedDate!.day.toString().padLeft(2, '0')}.${state.selectedDate!.month.toString().padLeft(2, '0')}.${state.selectedDate!.year} (${filtered.length})'
: 'Anstehende Termine (${filtered.length})',
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold, color: AppTheme.textPrimary),
),
Text('Kachelansicht', style: TextStyle(fontSize: 11, color: AppTheme.textMuted)),
],
),
const SizedBox(height: 12),
if (filtered.isEmpty)
Container(
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: AppTheme.glassSurface,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppTheme.glassBorder),
),
child: Center(
child: Text(
'Keine Unternehmenstermine für diesen Filter/Tag gefunden.',
style: TextStyle(color: AppTheme.textMuted, fontSize: 13),
),
),
),
)
: LayoutBuilder(
builder: (context, constraints) {
final crossAxisCount = constraints.maxWidth > 750 ? 4 : (constraints.maxWidth > 480 ? 2 : 1);
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: filtered.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
childAspectRatio: 3,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
),
itemBuilder: (context, index) {
return CalendarEventTile(
event: filtered[index].toJson(),
apiClient: apiClient,
);
},
);
},
),
],
),
],
),
),
),
if (filtered.isNotEmpty)
SliverPadding(
padding: const EdgeInsets.fromLTRB(20, 0, 20, 20),
sliver: SliverGrid(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 300,
mainAxisExtent: 135,
mainAxisSpacing: 12,
crossAxisSpacing: 12,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
return CalendarEventTile(
event: filtered[index].toJson(),
apiClient: apiClient,
);
},
childCount: filtered.length,
),
),
),
],
);
}
return const SizedBox.shrink();