Skip to content

Commit 7445ec6

Browse files
authored
Beta (#6)
* try new catalog view * small change to catalog view * add tags to eligibility overview * start building filtered view * continue building filter * install functionable filter setup - cosmetics and mobile view missing * inject translated stringts * add eligibility banner * re-arrange eligibility item * re-arrange filter * fix feedback button
1 parent 0b2512b commit 7445ec6

20 files changed

Lines changed: 698 additions & 261 deletions

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"@babel/plugin-transform-private-property-in-object": "^7.24.7",
99
"@emotion/react": "^11.11.4",
1010
"@emotion/styled": "^11.11.5",
11-
"@foerderfunke/matching-engine": "^1.1.7",
11+
"@foerderfunke/matching-engine": "^1.1.8",
1212
"@mui/icons-material": "^5.15.19",
1313
"@mui/material": "^5.15.19",
1414
"@mui/x-charts": "^7.6.2",

public/assets/data/requirement-profiles/requirement-profiles-hydration.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
"en": "Cost-of-living assistance"
99
},
1010
"description": {
11-
"de": "Die „Hilfe zum Lebensunterhalt“ richtet sich an Menschen, die eine Zeit lang nicht oder nur wenig arbeiten können. Sie dient dazu grundlegende Bedürfnisse wie Essen, Kleidung und Miete zu bezahlen. Hilfe zum Lebensunterhalt erhalten Personen, für die andere staatliche Leistungen, wie etwa Rente oder Bürgergeld (früher ALG II), nicht infrage kommen.",
12-
"en": "If you are currently unable to work due to health reasons, you may be entitled to cost of living assistance. The prerequisite is that you are temporarily unable to earn your living yourself or with the help of other people, for example a partner or your family."
11+
"de": "Die „Hilfe zum Lebensunterhalt“ unterstützt Menschen, die vorübergehend nicht oder nur wenig arbeiten können und keinen Anspruch auf andere Sozialleistungen haben, bei der Deckung von Grundbedürfnissen wie Essen, Kleidung und Miete.",
12+
"en": "The 'Hilfe zum Lebensunterhalt' (cost-of-living assistance) supports people who are temporarily unable to work or can only work a little and do not have access to other social benefits. It helps cover basic needs such as food, clothing, and rent."
1313
}
1414
},
1515
"ff:kindergeld": {

src/core/utils/buildEligibilityReports.js

Lines changed: 114 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,17 @@ export const buildEligibilityReports = (validationReport, metadata, hydrationDat
77
};
88
}
99

10-
const reports = validationReport['ff:hasEvaluatedRequirementProfile'] || [];
10+
const reports = validationReport['ff:hasEvaluatedRequirementProfile'] || [];
11+
const hasDefinition = metadata['ff:hasDefinition'] || [];
12+
const defDict = hasDefinition.reduce((acc, cat) => {
13+
const id = cat['@id'];
14+
const label = cat['rdfs:label']?.['@value'] || '';
15+
acc[id] = label;
16+
return acc;
17+
}, {});
18+
1119
const allReports = reports.map(report => {
12-
const rpUri = report['ff:hasRpUri']?.['@id'] || null;
20+
const rpUri = report['ff:hasRpUri']?.['@id'] || null;
1321
const result = report['ff:hasEligibilityStatus']?.['@id'] || null;
1422

1523
// hydration data
@@ -27,38 +35,121 @@ export const buildEligibilityReports = (validationReport, metadata, hydrationDat
2735
// metadata
2836
const rpMetadata = metadata['ff:hasRP']?.find(rp => rp['@id'] === rpUri)
2937
const validationStage = rpMetadata?.['ff:validationStage']?.['@id'] || 'Unknown Validation Stage';
38+
39+
// administrative level
40+
const rawLevels = rpMetadata?.['ff:administrativeLevel'];
41+
const administrativeLevels = rawLevels
42+
? (Array.isArray(rawLevels) ? rawLevels : [rawLevels])
43+
.map(level => ({
44+
id: level['@id'],
45+
label: defDict[level['@id']] || ''
46+
}))
47+
: [];
48+
49+
// associated law
50+
const rawLaw = rpMetadata?.['ff:legalBasis'];
51+
const associatedLaws = rawLaw
52+
? (Array.isArray(rawLaw) ? rawLaw : [rawLaw])
53+
.map(law => ({
54+
id: law['@id'],
55+
label: defDict[law['@id']] || ''
56+
}))
57+
: [];
3058

31-
return { uri: rpUri, result, id, title, category, description, status, requiredDocuments, additionalSupport, legalBasis, furtherInformation, validationStage };
32-
});
59+
// providing agency
60+
const rawAgencies = rpMetadata?.['ff:providingAgency'];
61+
const providingAgencies = rawAgencies
62+
? (Array.isArray(rawAgencies) ? rawAgencies : [rawAgencies])
63+
.map(agency => ({
64+
id: agency['@id'],
65+
label: defDict[agency['@id']] || ''
66+
}))
67+
: [];
3368

34-
const eligibilityStatus = allReports.reduce((acc, report) => {
69+
70+
// benefit categories
71+
const rawCategories = rpMetadata?.['ff:category'];
72+
const benefitCategories = rawCategories
73+
? (Array.isArray(rawCategories) ? rawCategories : [rawCategories])
74+
.map(cat => ({
75+
id: cat['@id'],
76+
label: defDict[cat['@id']] || ''
77+
}))
78+
: [];
79+
80+
return {
81+
uri: rpUri,
82+
result,
83+
id,
84+
title,
85+
category,
86+
description,
87+
status,
88+
requiredDocuments,
89+
additionalSupport,
90+
legalBasis,
91+
furtherInformation,
92+
validationStage,
93+
associatedLaws,
94+
providingAgencies,
95+
administrativeLevels,
96+
benefitCategories
97+
};
98+
});
99+
100+
const filterOptions = {
101+
associatedLaws: Array.from(
102+
new Map(
103+
allReports
104+
.flatMap(r => r.associatedLaws || [])
105+
.filter(Boolean)
106+
.map(cat => [cat.id, cat])
107+
).values()
108+
),
109+
110+
providingAgencies: Array.from(
111+
new Map(
112+
allReports
113+
.flatMap(r => r.providingAgencies || [])
114+
.filter(Boolean)
115+
.map(cat => [cat.id, cat])
116+
).values()
117+
),
118+
119+
administrativeLevels: Array.from(
120+
new Map(
121+
allReports
122+
.flatMap(r => r.administrativeLevels || [])
123+
.filter(Boolean)
124+
.map(cat => [cat.id, cat])
125+
).values()
126+
),
127+
128+
benefitCategories: Array.from(
129+
new Map(
130+
allReports
131+
.flatMap(r => r.benefitCategories || [])
132+
.filter(Boolean)
133+
.map(cat => [cat.id, cat])
134+
).values()
135+
),
136+
};
137+
138+
const eligibilityData = allReports.reduce((acc, report) => {
35139
const { category, result } = report;
36140

37141
if (!acc[category]) {
38142
acc[category] = {};
39143
}
40144

41-
if (result === "ff:eligible") {
42-
if (!acc[category][result]) {
43-
acc[category][result] = { preliminary: [], final: [] };
44-
}
45-
46-
if (report.validationStage === "ff:complete-validation") {
47-
acc[category][result].final.push(report);
48-
}
49-
50-
if (report.validationStage === "ff:preliminary-validation") {
51-
acc[category][result].preliminary.push(report);
52-
}
53-
} else {
54-
if (!acc[category][result]) {
55-
acc[category][result] = [];
56-
}
57-
acc[category][result].push(report);
145+
if (!acc[category][result]) {
146+
acc[category][result] = [];
58147
}
59148

149+
acc[category][result].push(report);
150+
60151
return acc;
61152
}, {});
62153

63-
return eligibilityStatus;
154+
return { eligibilityData, filterOptions };
64155
};

src/ui/language/translations.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,17 @@ const translations = {
213213
probableNotEligible: "Probable ineligibility",
214214
notEnoughData: "Not enough data for assessment",
215215
beta: "Some benefits are still in beta, meaning they are not fully tested and may contain errors."
216+
},
217+
filter: {
218+
title: "Filter",
219+
benefitCategories: "Benefit category",
220+
administrativeLevels: "Administrative level",
221+
providingAgencies: "Providing agency",
222+
associatedLaws: "Associated law",
223+
},
224+
item: {
225+
eligible: "You are likely eligible",
226+
notEligible: "You are likely not eligible",
216227
}
217228
},
218229
profile: {
@@ -500,6 +511,17 @@ const translations = {
500511
probableNotEligible: "Wahrscheinlich besteht kein Anspruch",
501512
notEnoughData: "Nicht genügend Angaben für eine Einschätzung",
502513
beta: "Eininge Leistungen sind noch in der Beta-Phase, d.h. sie sind noch nicht vollständig getestet und können Fehler enthalten."
514+
},
515+
filter: {
516+
title: "Filter",
517+
benefitCategories: "Leistungskategorie",
518+
administrativeLevels: "Verwaltungsebene",
519+
providingAgencies: "Zuständige Behörde",
520+
associatedLaws: "Zugehöriges Gesetz",
521+
},
522+
item: {
523+
eligible: "Sie haben womöglich Anspruch",
524+
notEligible: "Sie haben voraussichtlich keinen Anspruch",
503525
}
504526
},
505527
profile: {

src/ui/screens/eligibilty-overview/EligibilityOverviewScreen.js

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,45 +5,54 @@ import AppScreenWrapperContainer from "@/ui/shared-components/app-screen-wrapper
55
import { VBox } from '@/ui/shared-components/LayoutBoxes';
66
import EligibilityOverviewHeader from "./components/EligibilityOverviewHeader";
77
import EligibilityOverviewSection from "./components/EligibilityOverviewSection";
8+
import EligibilityOverviewFilter from './components/EligibilityOverviewFilter';
89

910
const EligibilityOverviewScreen = ({
1011
t,
11-
iconPaths,
1212
eligibilityData,
13+
filterOptions,
14+
filters,
15+
onChangeFilters,
1316
}) => {
1417

1518
return (
1619
<Layout isApp={true} logo={true}>
1720
<AppScreenWrapperContainer back={true}>
1821
<VBox sx={{ gap: { xs: 4, md: 8 } }} >
19-
<EligibilityOverviewHeader iconPaths={iconPaths} />
20-
{
21-
eligibilityData ? (
22-
<>
23-
{
24-
eligibilityData["social_benefit"] && (
25-
<EligibilityOverviewSection
26-
iconPaths={iconPaths}
27-
color={'yellow.main'}
28-
category={t('app.topicSelection.socialBenefitsTitle')}
29-
eligibilitySection={eligibilityData["social_benefit"]}
30-
/>
31-
)
32-
}
33-
{
34-
eligibilityData["business"] && (
35-
<EligibilityOverviewSection
36-
iconPaths={iconPaths}
37-
color={'custom.colorDeepTealTransparent'}
38-
category={t('app.topicSelection.businessTitle')}
39-
eligibilitySection={eligibilityData["business"]}
40-
/>
41-
)
42-
}
43-
</>
44-
) :
45-
<VBox sx={{ alignItems: "center" }}><CircularProgress /></VBox>
46-
}
22+
<EligibilityOverviewHeader />
23+
<VBox sx={{ gap: { xs: 2, md: 4 } }}>
24+
<EligibilityOverviewFilter
25+
t={t}
26+
filterOptions={filterOptions}
27+
filters={filters}
28+
onChangeFilters={onChangeFilters}
29+
/>
30+
{
31+
eligibilityData ? (
32+
<>
33+
{
34+
eligibilityData["social_benefit"] && (
35+
<EligibilityOverviewSection
36+
t={t}
37+
category={t('app.topicSelection.socialBenefitsTitle')}
38+
eligibilitySection={eligibilityData["social_benefit"]}
39+
/>
40+
)
41+
}
42+
{
43+
eligibilityData["business"] && (
44+
<EligibilityOverviewSection
45+
t={t}
46+
category={t('app.topicSelection.businessTitle')}
47+
eligibilitySection={eligibilityData["business"]}
48+
/>
49+
)
50+
}
51+
</>
52+
) :
53+
<VBox sx={{ alignItems: "center" }}><CircularProgress /></VBox>
54+
}
55+
</VBox>
4756
</VBox>
4857
</AppScreenWrapperContainer>
4958
</Layout >

src/ui/screens/eligibilty-overview/EligibilityOverviewScreenContainer.js

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,36 @@
1-
import React, { useMemo } from 'react';
1+
import React, { useState } from 'react';
22
import useFetchData from "@/ui/shared-hooks/utility/useFetchResource";
33
import useTranslation from "@/ui/language/useTranslation";
44
import EligibilityOverviewScreen from './EligibilityOverviewScreen';
55
import useEligibilityData from "./hooks/useEligibilityData";
66
import { useLanguageStore } from '@/ui/storage/useLanguageStore';
77
import { useMetadataStore } from '@/ui/storage/zustand';
88
import useProduceValidationReport from './hooks/useProduceValidationReport';
9+
import useFilterEligibilityData from './hooks/useFilterEligibilityData';
910

1011
const EligibilityOverviewScreenContainer = () => {
1112
const { t } = useTranslation();
1213
const language = useLanguageStore((state) => state.language);
14+
const [filters, setFilters] = useState({
15+
providingAgencies: [],
16+
associatedLaws: [],
17+
benefitCategories: [],
18+
administrativeLevels: []
19+
});
1320

1421
const hydrationData = useFetchData('assets/data/requirement-profiles/requirement-profiles-hydration.json');
15-
const {validationReport} = useProduceValidationReport();
22+
const { validationReport } = useProduceValidationReport();
1623
const metadata = useMetadataStore((state) => state.metadata);
17-
const eligibilityData = useEligibilityData(validationReport, metadata, hydrationData, language);
18-
19-
const iconPaths = useMemo(() => ({
20-
eligible: `${process.env.PUBLIC_URL}/assets/images/application/icon-image-eligible.svg`,
21-
preliminaryEligible: `${process.env.PUBLIC_URL}/assets/images/application/icon-image-preliminary-eligible.svg`,
22-
ineligible: `${process.env.PUBLIC_URL}/assets/images/application/icon-image-ineligible.svg`,
23-
missing: `${process.env.PUBLIC_URL}/assets/images/application/icon-image-missing.svg`,
24-
}), []);
24+
const { eligibilityData, filterOptions } = useEligibilityData(validationReport, metadata, hydrationData, language);
25+
const filteredEligibilityData = useFilterEligibilityData(eligibilityData, filters);
2526

2627
return (
2728
<EligibilityOverviewScreen
2829
t={t}
29-
iconPaths={iconPaths}
30-
eligibilityData={eligibilityData}
30+
eligibilityData={filteredEligibilityData}
31+
filterOptions={filterOptions}
32+
filters={filters}
33+
onChangeFilters={setFilters}
3134
/>
3235
);
3336
};
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Typography } from "@mui/material";
2+
import { HBox } from "@/ui/shared-components/LayoutBoxes";
3+
import theme from "@/theme";
4+
5+
const EligibilityOverviewBanner = ({ t, eligible }) => {
6+
console.log("EligibilityOverviewBanner", eligible);
7+
8+
const eligibilityBannerType = (() => {
9+
switch (eligible) {
10+
case 'eligible':
11+
return {
12+
text: 'app.browseAll.item.eligible',
13+
backgroundColor: 'secondary.light'
14+
}
15+
case 'preliminary-eligible':
16+
return {
17+
text: 'app.browseAll.item.preliminaryEligible',
18+
backgroundColor: 'yellow.main'
19+
}
20+
case 'non-eligible':
21+
return {
22+
text: 'app.browseAll.item.notEligible',
23+
backgroundColor: 'error.light'
24+
}
25+
default:
26+
return {
27+
text: 'app.browseAll.item.missingData',
28+
backgroundColor: theme.palette.black.main
29+
}
30+
}
31+
})();
32+
33+
return (
34+
<HBox sx={{
35+
backgroundColor: eligibilityBannerType.backgroundColor,
36+
padding: "8px 12px",
37+
borderRadius: theme.shape.borderRadius,
38+
alignItems: 'center',
39+
}}>
40+
<Typography>{t(eligibilityBannerType.text)}</Typography>
41+
</HBox>
42+
);
43+
}
44+
export default EligibilityOverviewBanner;

0 commit comments

Comments
 (0)