feat(app): responsive asset detail layout, full width chart, reactive hero header, shimmer loaders and enriched fundamentals

This commit is contained in:
2026-08-14 23:57:03 +02:00
parent f94e3b8164
commit 1d244b338a
22 changed files with 1950 additions and 1074 deletions
@@ -106,11 +106,16 @@ class StrategySignalModel extends Equatable {
});
factory StrategySignalModel.fromJson(Map<String, dynamic> json) {
final rawDir = (json['direction'] ?? json['signalType'] ?? json['type'])?.toString().toUpperCase() ?? 'BUY';
final sigDir = (rawDir == 'BUY' || rawDir == 'SELL') ? rawDir : 'BUY';
final sigTitle = (json['title'] ?? json['type'] ?? json['description'])?.toString() ?? 'Signal';
final dateStr = (json['timestamp'] ?? json['date'] ?? json['time'])?.toString();
return StrategySignalModel(
title: json['title']?.toString() ?? '',
date: DateTime.tryParse(json['date']?.toString() ?? '') ?? DateTime.now(),
title: sigTitle,
date: dateStr != null ? (DateTime.tryParse(dateStr) ?? DateTime.now()) : DateTime.now(),
price: (json['price'] as num?)?.toDouble() ?? 0.0,
type: json['type']?.toString() ?? 'BUY',
type: sigDir,
);
}
@@ -123,33 +128,76 @@ class PatternPoint extends Equatable {
final double price;
const PatternPoint(this.time, this.price);
factory PatternPoint.fromJson(Map<String, dynamic> json) => PatternPoint(DateTime.tryParse(json['time'] ?? '') ?? DateTime.now(), (json['price'] as num).toDouble());
factory PatternPoint.fromJson(Map<String, dynamic> json) => PatternPoint(DateTime.tryParse(json['time']?.toString() ?? '') ?? DateTime.now(), (json['price'] as num?)?.toDouble() ?? 0.0);
@override
List<Object?> get props => [time, price];
}
class ChartPatternModel extends Equatable {
final String type;
final List<PatternPoint> upperLine;
final List<PatternPoint> lowerLine;
class BreakoutSignalModel extends Equatable {
final String direction; // "UP", "DOWN"
final double targetPrice;
final double potentialPercent;
const ChartPatternModel({required this.type, required this.upperLine, required this.lowerLine});
const BreakoutSignalModel({
required this.direction,
required this.targetPrice,
required this.potentialPercent,
});
factory ChartPatternModel.fromJson(Map<String, dynamic> json) {
return ChartPatternModel(
type: json['type']?.toString() ?? 'Pattern',
upperLine: (json['upperLine'] as List<dynamic>? ?? []).map((e) => PatternPoint.fromJson(e)).toList(),
lowerLine: (json['lowerLine'] as List<dynamic>? ?? []).map((e) => PatternPoint.fromJson(e)).toList(),
factory BreakoutSignalModel.fromJson(Map<String, dynamic> json) {
return BreakoutSignalModel(
direction: (json['direction'] ?? json['Direction'])?.toString() ?? 'UP',
targetPrice: (json['targetPrice'] ?? json['TargetPrice'] as num?)?.toDouble() ?? 0.0,
potentialPercent: (json['potentialPercent'] ?? json['PotentialPercent'] as num?)?.toDouble() ?? 0.0,
);
}
@override
List<Object?> get props => [type, upperLine, lowerLine];
List<Object?> get props => [direction, targetPrice, potentialPercent];
}
class ChartPatternModel extends Equatable {
final String type;
final String description;
final double confidencePercent;
final BreakoutSignalModel? breakoutSignal;
final List<PatternPoint> upperLine;
final List<PatternPoint> lowerLine;
const ChartPatternModel({
required this.type,
this.description = '',
this.confidencePercent = 0.0,
this.breakoutSignal,
required this.upperLine,
required this.lowerLine,
});
factory ChartPatternModel.fromJson(Map<String, dynamic> json) {
BreakoutSignalModel? breakout;
final bJson = json['breakoutSignal'] ?? json['BreakoutSignal'];
if (bJson != null && bJson is Map<String, dynamic>) {
breakout = BreakoutSignalModel.fromJson(bJson);
}
return ChartPatternModel(
type: json['type']?.toString() ?? json['Type']?.toString() ?? 'Pattern',
description: json['description']?.toString() ?? json['Description']?.toString() ?? '',
confidencePercent: (json['confidencePercent'] ?? json['ConfidencePercent'] as num?)?.toDouble() ?? 0.0,
breakoutSignal: breakout,
upperLine: (json['upperLine'] as List<dynamic>? ?? []).map((e) => PatternPoint.fromJson(e as Map<String, dynamic>)).toList(),
lowerLine: (json['lowerLine'] as List<dynamic>? ?? []).map((e) => PatternPoint.fromJson(e as Map<String, dynamic>)).toList(),
);
}
@override
List<Object?> get props => [type, description, confidencePercent, breakoutSignal, upperLine, lowerLine];
}
class TechnicalAnalysisModel extends Equatable {
final String symbol;
final String currency;
final String trend;
final String rsi;
final String macd;
@@ -167,6 +215,7 @@ class TechnicalAnalysisModel extends Equatable {
const TechnicalAnalysisModel({
required this.symbol,
this.currency = 'EUR',
required this.trend,
required this.rsi,
required this.macd,
@@ -211,6 +260,7 @@ class TechnicalAnalysisModel extends Equatable {
return TechnicalAnalysisModel(
symbol: json['symbol']?.toString() ?? json['isin']?.toString() ?? json['ticker']?.toString() ?? '',
currency: json['currency']?.toString() ?? 'EUR',
trend: parsedTrend,
rsi: lastInd?.rsi14?.toStringAsFixed(1) ?? 'N/A',
macd: lastInd?.macdHistogram?.toStringAsFixed(2) ?? lastInd?.macdLine?.toStringAsFixed(2) ?? 'N/A',
@@ -231,6 +281,7 @@ class TechnicalAnalysisModel extends Equatable {
Map<String, dynamic> toJson() {
return {
'symbol': symbol,
'currency': currency,
'trend': trend,
'rsi': rsi,
'macd': macd,
@@ -246,7 +297,7 @@ class TechnicalAnalysisModel extends Equatable {
@override
List<Object?> get props => [
symbol, trend, rsi, macd, overallSignal, sma50, sma200, vix,
symbol, currency, trend, rsi, macd, overallSignal, sma50, sma200, vix,
sp500Trend, dxy, stopLossAtr, candles, indicators, patterns, signals
];
}