Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@babel/plugin-transform-private-property-in-object": "^7.24.7",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@foerderfunke/matching-engine": "^1.1.7",
"@foerderfunke/matching-engine": "^1.1.8",
"@mui/icons-material": "^5.15.19",
"@mui/material": "^5.15.19",
"@mui/x-charts": "^7.6.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"en": "Cost-of-living assistance"
},
"description": {
"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.",
"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."
"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.",
"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."
}
},
"ff:kindergeld": {
Expand Down
137 changes: 114 additions & 23 deletions src/core/utils/buildEligibilityReports.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@ export const buildEligibilityReports = (validationReport, metadata, hydrationDat
};
}

const reports = validationReport['ff:hasEvaluatedRequirementProfile'] || [];
const reports = validationReport['ff:hasEvaluatedRequirementProfile'] || [];
const hasDefinition = metadata['ff:hasDefinition'] || [];
const defDict = hasDefinition.reduce((acc, cat) => {
const id = cat['@id'];
const label = cat['rdfs:label']?.['@value'] || '';
acc[id] = label;
return acc;
}, {});

const allReports = reports.map(report => {
const rpUri = report['ff:hasRpUri']?.['@id'] || null;
const rpUri = report['ff:hasRpUri']?.['@id'] || null;
const result = report['ff:hasEligibilityStatus']?.['@id'] || null;

// hydration data
Expand All @@ -27,38 +35,121 @@ export const buildEligibilityReports = (validationReport, metadata, hydrationDat
// metadata
const rpMetadata = metadata['ff:hasRP']?.find(rp => rp['@id'] === rpUri)
const validationStage = rpMetadata?.['ff:validationStage']?.['@id'] || 'Unknown Validation Stage';

// administrative level
const rawLevels = rpMetadata?.['ff:administrativeLevel'];
const administrativeLevels = rawLevels
? (Array.isArray(rawLevels) ? rawLevels : [rawLevels])
.map(level => ({
id: level['@id'],
label: defDict[level['@id']] || ''
}))
: [];

// associated law
const rawLaw = rpMetadata?.['ff:legalBasis'];
const associatedLaws = rawLaw
? (Array.isArray(rawLaw) ? rawLaw : [rawLaw])
.map(law => ({
id: law['@id'],
label: defDict[law['@id']] || ''
}))
: [];

return { uri: rpUri, result, id, title, category, description, status, requiredDocuments, additionalSupport, legalBasis, furtherInformation, validationStage };
});
// providing agency
const rawAgencies = rpMetadata?.['ff:providingAgency'];
const providingAgencies = rawAgencies
? (Array.isArray(rawAgencies) ? rawAgencies : [rawAgencies])
.map(agency => ({
id: agency['@id'],
label: defDict[agency['@id']] || ''
}))
: [];

const eligibilityStatus = allReports.reduce((acc, report) => {

// benefit categories
const rawCategories = rpMetadata?.['ff:category'];
const benefitCategories = rawCategories
? (Array.isArray(rawCategories) ? rawCategories : [rawCategories])
.map(cat => ({
id: cat['@id'],
label: defDict[cat['@id']] || ''
}))
: [];

return {
uri: rpUri,
result,
id,
title,
category,
description,
status,
requiredDocuments,
additionalSupport,
legalBasis,
furtherInformation,
validationStage,
associatedLaws,
providingAgencies,
administrativeLevels,
benefitCategories
};
});

const filterOptions = {
associatedLaws: Array.from(
new Map(
allReports
.flatMap(r => r.associatedLaws || [])
.filter(Boolean)
.map(cat => [cat.id, cat])
).values()
),

providingAgencies: Array.from(
new Map(
allReports
.flatMap(r => r.providingAgencies || [])
.filter(Boolean)
.map(cat => [cat.id, cat])
).values()
),

administrativeLevels: Array.from(
new Map(
allReports
.flatMap(r => r.administrativeLevels || [])
.filter(Boolean)
.map(cat => [cat.id, cat])
).values()
),

benefitCategories: Array.from(
new Map(
allReports
.flatMap(r => r.benefitCategories || [])
.filter(Boolean)
.map(cat => [cat.id, cat])
).values()
),
};

const eligibilityData = allReports.reduce((acc, report) => {
const { category, result } = report;

if (!acc[category]) {
acc[category] = {};
}

if (result === "ff:eligible") {
if (!acc[category][result]) {
acc[category][result] = { preliminary: [], final: [] };
}

if (report.validationStage === "ff:complete-validation") {
acc[category][result].final.push(report);
}

if (report.validationStage === "ff:preliminary-validation") {
acc[category][result].preliminary.push(report);
}
} else {
if (!acc[category][result]) {
acc[category][result] = [];
}
acc[category][result].push(report);
if (!acc[category][result]) {
acc[category][result] = [];
}

acc[category][result].push(report);

return acc;
}, {});

return eligibilityStatus;
return { eligibilityData, filterOptions };
};
22 changes: 22 additions & 0 deletions src/ui/language/translations.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,17 @@ const translations = {
probableNotEligible: "Probable ineligibility",
notEnoughData: "Not enough data for assessment",
beta: "Some benefits are still in beta, meaning they are not fully tested and may contain errors."
},
filter: {
title: "Filter",
benefitCategories: "Benefit category",
administrativeLevels: "Administrative level",
providingAgencies: "Providing agency",
associatedLaws: "Associated law",
},
item: {
eligible: "You are likely eligible",
notEligible: "You are likely not eligible",
}
},
profile: {
Expand Down Expand Up @@ -498,6 +509,17 @@ const translations = {
probableNotEligible: "Wahrscheinlich besteht kein Anspruch",
notEnoughData: "Nicht genügend Angaben für eine Einschätzung",
beta: "Eininge Leistungen sind noch in der Beta-Phase, d.h. sie sind noch nicht vollständig getestet und können Fehler enthalten."
},
filter: {
title: "Filter",
benefitCategories: "Leistungskategorie",
administrativeLevels: "Verwaltungsebene",
providingAgencies: "Zuständige Behörde",
associatedLaws: "Zugehöriges Gesetz",
},
item: {
eligible: "Sie haben womöglich Anspruch",
notEligible: "Sie haben voraussichtlich keinen Anspruch",
}
},
profile: {
Expand Down
67 changes: 38 additions & 29 deletions src/ui/screens/eligibilty-overview/EligibilityOverviewScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,54 @@ import AppScreenWrapperContainer from "@/ui/shared-components/app-screen-wrapper
import { VBox } from '@/ui/shared-components/LayoutBoxes';
import EligibilityOverviewHeader from "./components/EligibilityOverviewHeader";
import EligibilityOverviewSection from "./components/EligibilityOverviewSection";
import EligibilityOverviewFilter from './components/EligibilityOverviewFilter';

const EligibilityOverviewScreen = ({
t,
iconPaths,
eligibilityData,
filterOptions,
filters,
onChangeFilters,
}) => {

return (
<Layout isApp={true} logo={true}>
<AppScreenWrapperContainer back={true}>
<VBox sx={{ gap: { xs: 4, md: 8 } }} >
<EligibilityOverviewHeader iconPaths={iconPaths} />
{
eligibilityData ? (
<>
{
eligibilityData["social_benefit"] && (
<EligibilityOverviewSection
iconPaths={iconPaths}
color={'yellow.main'}
category={t('app.topicSelection.socialBenefitsTitle')}
eligibilitySection={eligibilityData["social_benefit"]}
/>
)
}
{
eligibilityData["business"] && (
<EligibilityOverviewSection
iconPaths={iconPaths}
color={'custom.colorDeepTealTransparent'}
category={t('app.topicSelection.businessTitle')}
eligibilitySection={eligibilityData["business"]}
/>
)
}
</>
) :
<VBox sx={{ alignItems: "center" }}><CircularProgress /></VBox>
}
<EligibilityOverviewHeader />
<VBox sx={{ gap: { xs: 2, md: 4 } }}>
<EligibilityOverviewFilter
t={t}
filterOptions={filterOptions}
filters={filters}
onChangeFilters={onChangeFilters}
/>
{
eligibilityData ? (
<>
{
eligibilityData["social_benefit"] && (
<EligibilityOverviewSection
t={t}
category={t('app.topicSelection.socialBenefitsTitle')}
eligibilitySection={eligibilityData["social_benefit"]}
/>
)
}
{
eligibilityData["business"] && (
<EligibilityOverviewSection
t={t}
category={t('app.topicSelection.businessTitle')}
eligibilitySection={eligibilityData["business"]}
/>
)
}
</>
) :
<VBox sx={{ alignItems: "center" }}><CircularProgress /></VBox>
}
</VBox>
</VBox>
</AppScreenWrapperContainer>
</Layout >
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,36 @@
import React, { useMemo } from 'react';
import React, { useState } from 'react';
import useFetchData from "@/ui/shared-hooks/utility/useFetchResource";
import useTranslation from "@/ui/language/useTranslation";
import EligibilityOverviewScreen from './EligibilityOverviewScreen';
import useEligibilityData from "./hooks/useEligibilityData";
import { useLanguageStore } from '@/ui/storage/useLanguageStore';
import { useMetadataStore } from '@/ui/storage/zustand';
import useProduceValidationReport from './hooks/useProduceValidationReport';
import useFilterEligibilityData from './hooks/useFilterEligibilityData';

const EligibilityOverviewScreenContainer = () => {
const { t } = useTranslation();
const language = useLanguageStore((state) => state.language);
const [filters, setFilters] = useState({
providingAgencies: [],
associatedLaws: [],
benefitCategories: [],
administrativeLevels: []
});

const hydrationData = useFetchData('assets/data/requirement-profiles/requirement-profiles-hydration.json');
const {validationReport} = useProduceValidationReport();
const { validationReport } = useProduceValidationReport();
const metadata = useMetadataStore((state) => state.metadata);
const eligibilityData = useEligibilityData(validationReport, metadata, hydrationData, language);

const iconPaths = useMemo(() => ({
eligible: `${process.env.PUBLIC_URL}/assets/images/application/icon-image-eligible.svg`,
preliminaryEligible: `${process.env.PUBLIC_URL}/assets/images/application/icon-image-preliminary-eligible.svg`,
ineligible: `${process.env.PUBLIC_URL}/assets/images/application/icon-image-ineligible.svg`,
missing: `${process.env.PUBLIC_URL}/assets/images/application/icon-image-missing.svg`,
}), []);
const { eligibilityData, filterOptions } = useEligibilityData(validationReport, metadata, hydrationData, language);
const filteredEligibilityData = useFilterEligibilityData(eligibilityData, filters);

return (
<EligibilityOverviewScreen
t={t}
iconPaths={iconPaths}
eligibilityData={eligibilityData}
eligibilityData={filteredEligibilityData}
filterOptions={filterOptions}
filters={filters}
onChangeFilters={setFilters}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { Typography } from "@mui/material";
import { HBox } from "@/ui/shared-components/LayoutBoxes";
import theme from "@/theme";

const EligibilityOverviewBanner = ({ t, eligible }) => {
console.log("EligibilityOverviewBanner", eligible);

const eligibilityBannerType = (() => {
switch (eligible) {
case 'eligible':
return {
text: 'app.browseAll.item.eligible',
backgroundColor: 'secondary.light'
}
case 'preliminary-eligible':
return {
text: 'app.browseAll.item.preliminaryEligible',
backgroundColor: 'yellow.main'
}
case 'non-eligible':
return {
text: 'app.browseAll.item.notEligible',
backgroundColor: 'error.light'
}
default:
return {
text: 'app.browseAll.item.missingData',
backgroundColor: theme.palette.black.main
}
}
})();

return (
<HBox sx={{
backgroundColor: eligibilityBannerType.backgroundColor,
padding: "8px 12px",
borderRadius: theme.shape.borderRadius,
alignItems: 'center',
}}>
<Typography>{t(eligibilityBannerType.text)}</Typography>
</HBox>
);
}
export default EligibilityOverviewBanner;
Loading