Improve Team-indeling dashboard UI and cache invalidation

- Replace 'TEAM' label with Type attribute (Business/Enabling/Staf) in team blocks
- Make Type labels larger (text-sm) and brighter colors
- Make SUBTEAM label less bright (indigo-300) and smaller (text-[10px])
- Add 'FTE' suffix to bandbreedte values in header and application blocks
- Add Platform and Connected Device labels to application blocks
- Show Platform FTE and Workloads FTE separately in Platform blocks
- Add spacing between Regiemodel letter and count value
- Add cache invalidation for Team Dashboard when applications are updated
- Enrich team references with Type attribute in getSubteamToTeamMapping
This commit is contained in:
2026-01-10 02:16:55 +01:00
parent ea1c84262c
commit ca21b9538d
54 changed files with 13444 additions and 1789 deletions

View File

@@ -29,7 +29,7 @@ export default function ApplicationList() {
setStatuses,
setApplicationFunction,
setGovernanceModel,
setApplicationCluster,
setApplicationSubteam,
setApplicationType,
setOrganisation,
setHostingType,
@@ -45,6 +45,7 @@ export default function ApplicationList() {
const [organisations, setOrganisations] = useState<ReferenceValue[]>([]);
const [hostingTypes, setHostingTypes] = useState<ReferenceValue[]>([]);
const [businessImportanceOptions, setBusinessImportanceOptions] = useState<ReferenceValue[]>([]);
const [applicationSubteams, setApplicationSubteams] = useState<ReferenceValue[]>([]);
const [showFilters, setShowFilters] = useState(true);
// Sync URL params with store on mount
@@ -98,6 +99,7 @@ export default function ApplicationList() {
setOrganisations(data.organisations);
setHostingTypes(data.hostingTypes);
setBusinessImportanceOptions(data.businessImportance || []);
setApplicationSubteams(data.applicationSubteams || []);
} catch (err) {
console.error('Failed to load reference data', err);
}
@@ -126,7 +128,7 @@ export default function ApplicationList() {
// Only navigate programmatically for regular clicks
if (!event.ctrlKey && !event.metaKey && !event.shiftKey && event.button === 0) {
event.preventDefault();
navigate(`/applications/${app.id}`);
navigate(`/application/${app.id}`);
}
};
@@ -257,26 +259,6 @@ export default function ApplicationList() {
</div>
</div>
<div>
<label className="label mb-2">Application Cluster</label>
<div className="space-y-1">
{(['all', 'filled', 'empty'] as const).map((value) => (
<label key={value} className="flex items-center space-x-2">
<input
type="radio"
name="applicationCluster"
checked={filters.applicationCluster === value}
onChange={() => setApplicationCluster(value)}
className="border-gray-300 text-blue-600 focus:ring-blue-500"
/>
<span className="text-sm text-gray-700">
{value === 'all' ? 'Alle' : value === 'filled' ? 'Ingevuld' : 'Leeg'}
</span>
</label>
))}
</div>
</div>
<div>
<label className="label mb-2">Application Type</label>
<div className="space-y-1">
@@ -347,6 +329,23 @@ export default function ApplicationList() {
))}
</select>
</div>
<div>
<label className="label mb-2">Subteam</label>
<select
value={filters.applicationSubteam || 'all'}
onChange={(e) => setApplicationSubteam(e.target.value as 'all' | 'empty' | string)}
className="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="all">Alle</option>
<option value="empty">Leeg</option>
{applicationSubteams.map((subteam) => (
<option key={subteam.objectId} value={subteam.name}>
{subteam.name}
</option>
))}
</select>
</div>
</div>
</div>
</div>
@@ -405,7 +404,7 @@ export default function ApplicationList() {
>
<td className="py-0">
<Link
to={`/applications/${app.id}`}
to={`/application/${app.id}`}
onClick={(e) => handleRowClick(app, index, e)}
className="block px-4 py-3 text-sm text-gray-500"
>
@@ -414,7 +413,7 @@ export default function ApplicationList() {
</td>
<td className="py-0">
<Link
to={`/applications/${app.id}`}
to={`/application/${app.id}`}
onClick={(e) => handleRowClick(app, index, e)}
className="block px-4 py-3"
>
@@ -426,7 +425,7 @@ export default function ApplicationList() {
</td>
<td className="py-0">
<Link
to={`/applications/${app.id}`}
to={`/application/${app.id}`}
onClick={(e) => handleRowClick(app, index, e)}
className="block px-4 py-3"
>
@@ -435,7 +434,7 @@ export default function ApplicationList() {
</td>
<td className="py-0">
<Link
to={`/applications/${app.id}`}
to={`/application/${app.id}`}
onClick={(e) => handleRowClick(app, index, e)}
className="block px-4 py-3"
>
@@ -460,7 +459,7 @@ export default function ApplicationList() {
</td>
<td className="py-0">
<Link
to={`/applications/${app.id}`}
to={`/application/${app.id}`}
onClick={(e) => handleRowClick(app, index, e)}
className="block px-4 py-3"
>
@@ -477,7 +476,7 @@ export default function ApplicationList() {
</td>
<td className="py-0">
<Link
to={`/applications/${app.id}`}
to={`/application/${app.id}`}
onClick={(e) => handleRowClick(app, index, e)}
className="block px-4 py-3 text-sm text-gray-900"
>
@@ -502,7 +501,7 @@ export default function ApplicationList() {
<div className="px-4 py-3 bg-gray-50 border-t border-gray-200 flex items-center justify-between">
{currentPage > 1 ? (
<Link
to={currentPage === 2 ? '/applications' : `/applications?page=${currentPage - 1}`}
to={currentPage === 2 ? '/application/overview' : `/application/overview?page=${currentPage - 1}`}
onClick={() => setCurrentPage(currentPage - 1)}
className="btn btn-secondary"
>
@@ -518,7 +517,7 @@ export default function ApplicationList() {
</span>
{currentPage < result.totalPages ? (
<Link
to={`/applications?page=${currentPage + 1}`}
to={`/application/overview?page=${currentPage + 1}`}
onClick={() => setCurrentPage(currentPage + 1)}
className="btn btn-secondary"
>