Compare commits
No commits in common. "ae9fc45177f483894628a0e4ca6de7c2f86a1a2f" and "94f44736869b3c72c36ea535bb93ce7c68e5d2b7" have entirely different histories.
ae9fc45177
...
94f4473686
7 changed files with 28 additions and 177 deletions
|
|
@ -90,12 +90,6 @@ class Fixtures {
|
||||||
'T (Tarif) T1 — DESTATIS Verdiensterhebung kaufm. Tätigkeit ~30 €/h; '
|
'T (Tarif) T1 — DESTATIS Verdiensterhebung kaufm. Tätigkeit ~30 €/h; '
|
||||||
'h (Zeitaufwand) T3 — eigene Schätzung ~30 min, NKR-Validierung ausstehend. '
|
'h (Zeitaufwand) T3 — eigene Schätzung ~30 min, NKR-Validierung ausstehend. '
|
||||||
'Regel der niedrigsten Stufe: Gesamt → T3 (in der Demo konservativ als T4 markiert).',
|
'Regel der niedrigsten Stufe: Gesamt → T3 (in der Demo konservativ als T4 markiert).',
|
||||||
componentSummary: const [
|
|
||||||
'P · T1 — DESTATIS Gewerbeanzeigen',
|
|
||||||
'F · T1 — anlassbezogen, je Vorgang',
|
|
||||||
'T · T1 — DESTATIS Verdiensterhebung',
|
|
||||||
'h · T3 — eigene Schätzung ~30 min',
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
static final _kassenSichV = Evaluation(
|
static final _kassenSichV = Evaluation(
|
||||||
|
|
@ -183,12 +177,6 @@ class Fixtures {
|
||||||
'Laufende Folgekosten (TSE-Ausfallmeldungen, Hardware-Erneuerung): T2 '
|
'Laufende Folgekosten (TSE-Ausfallmeldungen, Hardware-Erneuerung): T2 '
|
||||||
'aus DIHK-Bürokratie-Index + DEHOGA-Gastronomie-Stichprobe. '
|
'aus DIHK-Bürokratie-Index + DEHOGA-Gastronomie-Stichprobe. '
|
||||||
'Hoher Frust-Score wegen Pluralität TSE-Hersteller × Finanzamt-Vollzug.',
|
'Hoher Frust-Score wegen Pluralität TSE-Hersteller × Finanzamt-Vollzug.',
|
||||||
componentSummary: const [
|
|
||||||
'P · T1 — Kassen-pflichtige Betriebe',
|
|
||||||
'F · T1 — kontinuierlich + Ausfälle',
|
|
||||||
'T · T2 — DIHK Bürokratie-Index',
|
|
||||||
'h · T2 — DEHOGA-Stichprobe',
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
static final _dsgvoArt30 = Evaluation(
|
static final _dsgvoArt30 = Evaluation(
|
||||||
|
|
@ -284,13 +272,6 @@ class Fixtures {
|
||||||
'pro Unternehmen liegt im Streit zwischen BfDI-Position (gering, weil '
|
'pro Unternehmen liegt im Streit zwischen BfDI-Position (gering, weil '
|
||||||
'<250-MA-Ausnahme) und DIHK-Position (hoch, weil 5-Abs.-Ausnahme '
|
'<250-MA-Ausnahme) und DIHK-Position (hoch, weil 5-Abs.-Ausnahme '
|
||||||
'praktisch nicht greift). Tier T2 für Magnitude.',
|
'praktisch nicht greift). Tier T2 für Magnitude.',
|
||||||
componentSummary: const [
|
|
||||||
'P · T1 — DESTATIS Unternehmensregister',
|
|
||||||
'F · T1 — jährlich überprüfen',
|
|
||||||
'T · T2 — DIHK-Position',
|
|
||||||
'h · T2 — Streit BfDI ↔ DIHK',
|
|
||||||
'EU-determiniert · Reform-Hebel Brüssel',
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
static final _berlinBauO60 = Evaluation(
|
static final _berlinBauO60 = Evaluation(
|
||||||
|
|
@ -370,13 +351,6 @@ class Fixtures {
|
||||||
'→ Genehmigung sind im Land getrennt geregelt). Höchster Frust-Score '
|
'→ Genehmigung sind im Land getrennt geregelt). Höchster Frust-Score '
|
||||||
'aus Verweistiefe + Behörden-Pluralität (Bezirksamt × SenStadt × '
|
'aus Verweistiefe + Behörden-Pluralität (Bezirksamt × SenStadt × '
|
||||||
'Beteiligungsverfahren).',
|
'Beteiligungsverfahren).',
|
||||||
componentSummary: const [
|
|
||||||
'P · T2 — Handwerkskammer Berlin',
|
|
||||||
'F · T1 — pro Bauvorhaben',
|
|
||||||
'T · T2 — Bauwirtschaft Berlin',
|
|
||||||
'h · T3 — Verfahren mehrstufig',
|
|
||||||
'Frust · T2 — Bezirk × Senat × Beteiligung',
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
static final _miLoG17 = Evaluation(
|
static final _miLoG17 = Evaluation(
|
||||||
|
|
@ -454,12 +428,5 @@ class Fixtures {
|
||||||
'dokumentierte den initial-Erfüllungsaufwand; aktuelle Zahlen '
|
'dokumentierte den initial-Erfüllungsaufwand; aktuelle Zahlen '
|
||||||
'aus ZDH Bürokratiekosten-Handwerk. Bürokratieentlastungs-'
|
'aus ZDH Bürokratiekosten-Handwerk. Bürokratieentlastungs-'
|
||||||
'gesetz IV adressierte den § 17 — Reform-Bewegung ist im Lauf.',
|
'gesetz IV adressierte den § 17 — Reform-Bewegung ist im Lauf.',
|
||||||
componentSummary: const [
|
|
||||||
'P · T1 — DESTATIS MiLoG-pflichtig',
|
|
||||||
'F · T1 — arbeitstäglich',
|
|
||||||
'T · T1 — DESTATIS Tariflohn',
|
|
||||||
'h · T1 — NKR-Stellungnahme 2014',
|
|
||||||
'Reform · BEG IV in Lauf',
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,6 @@ class Evaluation {
|
||||||
required this.sources,
|
required this.sources,
|
||||||
required this.auditEventId,
|
required this.auditEventId,
|
||||||
this.notes,
|
this.notes,
|
||||||
this.componentSummary = const [],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final Norm norm;
|
final Norm norm;
|
||||||
|
|
@ -173,10 +172,4 @@ class Evaluation {
|
||||||
final String auditEventId;
|
final String auditEventId;
|
||||||
|
|
||||||
final String? notes;
|
final String? notes;
|
||||||
|
|
||||||
/// Compact lines for at-a-glance reading on list views.
|
|
||||||
/// One line per SKM-Komponente (P / F / T / h) plus optional
|
|
||||||
/// extras — each carries its tier-tag inline. Empty list ⇒
|
|
||||||
/// fall back to the notes field for the list view.
|
|
||||||
final List<String> componentSummary;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
|
||||||
import '../data/repository.dart';
|
import '../data/repository.dart';
|
||||||
import '../theme/reclaim_theme.dart';
|
import '../theme/reclaim_theme.dart';
|
||||||
import '../theme/reclaim_tokens.dart';
|
import '../theme/reclaim_tokens.dart';
|
||||||
import '../widgets/brand_wordmark.dart';
|
|
||||||
import '../widgets/delta_mark.dart';
|
import '../widgets/delta_mark.dart';
|
||||||
import 'shell_page.dart';
|
import 'shell_page.dart';
|
||||||
|
|
||||||
|
|
@ -21,7 +20,7 @@ class LandingPage extends StatelessWidget {
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxWidth: 820),
|
constraints: const BoxConstraints(maxWidth: 720),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: ReclaimSpace.xxl,
|
horizontal: ReclaimSpace.xxl,
|
||||||
|
|
@ -31,18 +30,9 @@ class LandingPage extends StatelessWidget {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Row(
|
const DeltaMark(size: 96),
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
const SizedBox(height: ReclaimSpace.xxl),
|
||||||
children: [
|
Text('Recl∆Im', style: t.displaySmall),
|
||||||
DeltaMark(size: 96),
|
|
||||||
SizedBox(width: ReclaimSpace.xl),
|
|
||||||
BrandWordmark(
|
|
||||||
size: 56,
|
|
||||||
letterSpacing: -1.0,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: ReclaimSpace.xl),
|
|
||||||
const SizedBox(height: ReclaimSpace.sm),
|
const SizedBox(height: ReclaimSpace.sm),
|
||||||
Text(
|
Text(
|
||||||
'Pflicht-basierte Wirkungsanalyse von Rechtsnormen — '
|
'Pflicht-basierte Wirkungsanalyse von Rechtsnormen — '
|
||||||
|
|
|
||||||
|
|
@ -107,42 +107,31 @@ class _NormRow extends StatelessWidget {
|
||||||
color: ReclaimColors.mute,
|
color: ReclaimColors.mute,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: ReclaimSpace.sm),
|
|
||||||
TierBadge(tier: evaluation.tierLowest),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: ReclaimSpace.lg),
|
const SizedBox(width: ReclaimSpace.lg),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
child: _MetricColumn(
|
||||||
child: Column(
|
label: 'Schaden / Jahr',
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
value: _eur(evaluation.skmEurPerYear),
|
||||||
children: [
|
|
||||||
_MetricColumn(
|
|
||||||
label: 'Schaden / Jahr',
|
|
||||||
value: _eur(evaluation.skmEurPerYear),
|
|
||||||
),
|
|
||||||
const SizedBox(height: ReclaimSpace.sm),
|
|
||||||
_MetricColumn(
|
|
||||||
label: 'Nutzen',
|
|
||||||
value:
|
|
||||||
'${evaluation.benefitScore.toStringAsFixed(1)} / 5',
|
|
||||||
),
|
|
||||||
const SizedBox(height: ReclaimSpace.sm),
|
|
||||||
_MetricColumn(
|
|
||||||
label: 'Betroffene',
|
|
||||||
value: _kmu(evaluation.affectedCount),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: ReclaimSpace.lg),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 3,
|
child: _MetricColumn(
|
||||||
child: _ComponentSummary(
|
label: 'Nutzen',
|
||||||
summary: evaluation.componentSummary,
|
value:
|
||||||
|
'${evaluation.benefitScore.toStringAsFixed(1)} / 5',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Expanded(
|
||||||
|
child: _MetricColumn(
|
||||||
|
label: 'Betroffene',
|
||||||
|
value: _kmu(evaluation.affectedCount),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: ReclaimSpace.md),
|
||||||
|
TierBadge(tier: evaluation.tierLowest),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -163,44 +152,6 @@ class _NormRow extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ComponentSummary extends StatelessWidget {
|
|
||||||
const _ComponentSummary({required this.summary});
|
|
||||||
|
|
||||||
final List<String> summary;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final t = Theme.of(context).textTheme;
|
|
||||||
if (summary.isEmpty) {
|
|
||||||
return Text(
|
|
||||||
'—',
|
|
||||||
style: t.labelSmall?.copyWith(color: ReclaimColors.mute),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'Komponenten',
|
|
||||||
style: t.labelSmall?.copyWith(color: ReclaimColors.mute),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
for (final line in summary)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 2),
|
|
||||||
child: Text(
|
|
||||||
line,
|
|
||||||
style: t.bodyLarge?.copyWith(
|
|
||||||
height: 1.35,
|
|
||||||
fontSize: 12.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MetricColumn extends StatelessWidget {
|
class _MetricColumn extends StatelessWidget {
|
||||||
const _MetricColumn({required this.label, required this.value});
|
const _MetricColumn({required this.label, required this.value});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../data/repository.dart';
|
import '../data/repository.dart';
|
||||||
import '../theme/reclaim_tokens.dart';
|
import '../theme/reclaim_tokens.dart';
|
||||||
import '../widgets/brand_wordmark.dart';
|
|
||||||
import '../widgets/delta_mark.dart';
|
import '../widgets/delta_mark.dart';
|
||||||
import 'about_page.dart';
|
import 'about_page.dart';
|
||||||
import 'heatmap_page.dart';
|
import 'heatmap_page.dart';
|
||||||
|
|
@ -52,7 +51,14 @@ class _ShellPageState extends State<ShellPage> {
|
||||||
children: [
|
children: [
|
||||||
const DeltaMark(size: 36),
|
const DeltaMark(size: 36),
|
||||||
const SizedBox(height: ReclaimSpace.sm),
|
const SizedBox(height: ReclaimSpace.sm),
|
||||||
const BrandWordmark(size: 18),
|
Text(
|
||||||
|
'Recl∆Im',
|
||||||
|
style: t.labelLarge?.copyWith(
|
||||||
|
letterSpacing: 1.5,
|
||||||
|
color: ReclaimColors.signal,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
'F∆I',
|
'F∆I',
|
||||||
style: t.labelSmall?.copyWith(
|
style: t.labelSmall?.copyWith(
|
||||||
|
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import '../theme/reclaim_tokens.dart';
|
|
||||||
|
|
||||||
/// "Recl∆Im" wordmark with the ∆I glyphs petrol-coloured — the
|
|
||||||
/// brand rule of thumb in the fai_web universe: wherever the name
|
|
||||||
/// stands as a brand, the ∆I tinted petrol carries the F∆I-family
|
|
||||||
/// identity. As plain text it would all share the surrounding ink
|
|
||||||
/// colour.
|
|
||||||
///
|
|
||||||
/// Pass [size] for the base font size; the ∆I scales relative.
|
|
||||||
class BrandWordmark extends StatelessWidget {
|
|
||||||
const BrandWordmark({
|
|
||||||
super.key,
|
|
||||||
this.size = 24,
|
|
||||||
this.fontWeight = FontWeight.w600,
|
|
||||||
this.color,
|
|
||||||
this.letterSpacing = -0.5,
|
|
||||||
});
|
|
||||||
|
|
||||||
final double size;
|
|
||||||
final FontWeight fontWeight;
|
|
||||||
|
|
||||||
/// Color of the non-∆I letters. Defaults to the surface's
|
|
||||||
/// onSurface so the wordmark reads against whichever ink/paper
|
|
||||||
/// backdrop it sits on.
|
|
||||||
final Color? color;
|
|
||||||
final double letterSpacing;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final base = color ?? Theme.of(context).colorScheme.onSurface;
|
|
||||||
final style = TextStyle(
|
|
||||||
fontSize: size,
|
|
||||||
fontWeight: fontWeight,
|
|
||||||
fontFamily: ReclaimTypography.display,
|
|
||||||
color: base,
|
|
||||||
letterSpacing: letterSpacing,
|
|
||||||
height: 1.0,
|
|
||||||
);
|
|
||||||
return RichText(
|
|
||||||
text: TextSpan(
|
|
||||||
style: style,
|
|
||||||
children: [
|
|
||||||
const TextSpan(text: 'Recl'),
|
|
||||||
TextSpan(
|
|
||||||
text: '∆I',
|
|
||||||
style: style.copyWith(color: ReclaimColors.signal),
|
|
||||||
),
|
|
||||||
const TextSpan(text: 'm'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,7 +5,6 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
import 'package:reclaim_app/main.dart';
|
import 'package:reclaim_app/main.dart';
|
||||||
import 'package:reclaim_app/widgets/brand_wordmark.dart';
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// Generous surface so NavigationRail + Heatmap fit without
|
// Generous surface so NavigationRail + Heatmap fit without
|
||||||
|
|
@ -21,7 +20,7 @@ void main() {
|
||||||
await tester.binding.setSurfaceSize(surfaceSize);
|
await tester.binding.setSurfaceSize(surfaceSize);
|
||||||
await tester.pumpWidget(const ReclaimApp());
|
await tester.pumpWidget(const ReclaimApp());
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
expect(find.byType(BrandWordmark), findsWidgets);
|
expect(find.text('Recl∆Im'), findsOneWidget);
|
||||||
expect(
|
expect(
|
||||||
find.widgetWithText(FilledButton, 'App öffnen (Demo-Modus)'),
|
find.widgetWithText(FilledButton, 'App öffnen (Demo-Modus)'),
|
||||||
findsOneWidget,
|
findsOneWidget,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue