Merged PR 2357: dashboards in admin for appeals and lpa
dashboards in admin for appeals and lpa Related work items: #23527
This commit is contained in:
@@ -65,3 +65,23 @@ export const getNewDocumentsPaged = async (
|
|||||||
return logAndReturnResponse(error);
|
return logAndReturnResponse(error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getStatusByAppeal = async () => {
|
||||||
|
try {
|
||||||
|
return await getJson(
|
||||||
|
BASE_URL + "/api/admin/getStatusCountsByAppeal_api"
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
return logAndReturnResponse(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getStatusByAppealLPA = async () => {
|
||||||
|
try {
|
||||||
|
return await getJson(
|
||||||
|
BASE_URL + "/api/admin/getStatusCountsByAppealAndLPA_api"
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
return logAndReturnResponse(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
import {
|
import {
|
||||||
getNewAppeals,
|
getNewAppeals,
|
||||||
getNewAppealsPage,
|
getNewAppealsPage,
|
||||||
getNewDocumentsPaged
|
getNewDocumentsPaged,
|
||||||
|
getStatusByAppeal,
|
||||||
|
getStatusByAppealLPA
|
||||||
} from "./adminDirectService";
|
} from "./adminDirectService";
|
||||||
|
|
||||||
export { getNewAppeals, getNewAppealsPage, getNewDocumentsPaged };
|
export {
|
||||||
|
getNewAppeals,
|
||||||
|
getNewAppealsPage,
|
||||||
|
getNewDocumentsPaged,
|
||||||
|
getStatusByAppeal,
|
||||||
|
getStatusByAppealLPA
|
||||||
|
};
|
||||||
|
|||||||
@@ -0,0 +1,273 @@
|
|||||||
|
import { useMemo, useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function AppealStatusDashboard({ statusObj = [] }) {
|
||||||
|
const [openSections, setOpenSections] = useState({});
|
||||||
|
|
||||||
|
const sortedAppeals = useMemo(
|
||||||
|
() =>
|
||||||
|
[...statusObj].sort(
|
||||||
|
(a, b) => (b.caseCount || 0) - (a.caseCount || 0)
|
||||||
|
),
|
||||||
|
[statusObj]
|
||||||
|
);
|
||||||
|
|
||||||
|
const percentageOf = (value, total) =>
|
||||||
|
total ? `${(((value || 0) / total) * 100).toFixed(1)}%` : "0.0%";
|
||||||
|
|
||||||
|
const totalCases = sortedAppeals.reduce(
|
||||||
|
(sum, appeal) => sum + (appeal.caseCount || 0),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
const largestAppeal = sortedAppeals[0];
|
||||||
|
|
||||||
|
const allStatuses = sortedAppeals.flatMap((appeal) =>
|
||||||
|
(appeal.statuses || []).map((status) => ({
|
||||||
|
...status,
|
||||||
|
appealType: appeal.value
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
const statusTotals = allStatuses.reduce((acc, status) => {
|
||||||
|
if (!acc[status.statuscode]) {
|
||||||
|
acc[status.statuscode] = {
|
||||||
|
value: status.value,
|
||||||
|
statuscode: status.statuscode,
|
||||||
|
caseCount: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[status.statuscode].caseCount += status.caseCount || 0;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const mostCommonStatus = Object.values(statusTotals).sort(
|
||||||
|
(a, b) => b.caseCount - a.caseCount
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
const maxAppealCount = largestAppeal?.caseCount || 1;
|
||||||
|
|
||||||
|
const allExpanded =
|
||||||
|
sortedAppeals.length > 0 &&
|
||||||
|
sortedAppeals.every((_, index) => openSections[index]);
|
||||||
|
|
||||||
|
const toggleSection = (index) => {
|
||||||
|
setOpenSections((current) => ({
|
||||||
|
...current,
|
||||||
|
[index]: !current[index]
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const showAll = () => {
|
||||||
|
const allOpen = {};
|
||||||
|
sortedAppeals.forEach((_, index) => {
|
||||||
|
allOpen[index] = true;
|
||||||
|
});
|
||||||
|
setOpenSections(allOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideAll = () => {
|
||||||
|
setOpenSections({});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="govuk-grid-row">
|
||||||
|
<div className="govuk-grid-column-full">
|
||||||
|
<h1>Appeal case status dashboard</h1>
|
||||||
|
|
||||||
|
<div className="govuk-grid-row">
|
||||||
|
<DashboardCard title="Total cases" value={totalCases} />
|
||||||
|
<DashboardCard
|
||||||
|
title="Appeal types"
|
||||||
|
value={sortedAppeals.length}
|
||||||
|
/>
|
||||||
|
<DashboardCard
|
||||||
|
title="Largest appeal type"
|
||||||
|
value={largestAppeal?.caseCount || 0}
|
||||||
|
caption={largestAppeal?.value}
|
||||||
|
/>
|
||||||
|
<DashboardCard
|
||||||
|
title="Most common status"
|
||||||
|
value={mostCommonStatus?.caseCount || 0}
|
||||||
|
caption={mostCommonStatus?.value}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section className="govuk-!-margin-top-8">
|
||||||
|
<h2 className="govuk-heading-m">
|
||||||
|
Case counts and stage breakdown by appeal type
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p className="govuk-body-s">
|
||||||
|
Select an appeal type row to show the case stage
|
||||||
|
breakdown.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="govuk-accordion__controls">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="govuk-accordion__show-all"
|
||||||
|
onClick={allExpanded ? hideAll : showAll}
|
||||||
|
>
|
||||||
|
<span className="govuk-accordion__show-all-text">
|
||||||
|
{allExpanded
|
||||||
|
? "Hide all sections"
|
||||||
|
: "Show all sections"}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="govuk-accordion appeal-stage-chart"
|
||||||
|
id="appeals-with-status-accordion"
|
||||||
|
>
|
||||||
|
{sortedAppeals.map((appeal, index) => {
|
||||||
|
const isOpen = !!openSections[index];
|
||||||
|
|
||||||
|
const barWidth =
|
||||||
|
((appeal.caseCount || 0) / maxAppealCount) *
|
||||||
|
100;
|
||||||
|
|
||||||
|
const sortedStatuses = [...(appeal.statuses || [])]
|
||||||
|
.filter((status) => (status.caseCount || 0) > 0)
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
(b.caseCount || 0) - (a.caseCount || 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
isOpen
|
||||||
|
? "govuk-accordion__section govuk-accordion__section--expanded appeal-stage-chart__accordion-section"
|
||||||
|
: "govuk-accordion__section appeal-stage-chart__accordion-section"
|
||||||
|
}
|
||||||
|
key={appeal.appealType || index}
|
||||||
|
>
|
||||||
|
<div className="govuk-accordion__section-header">
|
||||||
|
<h2 className="govuk-accordion__section-heading">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="govuk-accordion__section-button appeal-stage-chart__button"
|
||||||
|
id={`appeal-status-heading-${index}`}
|
||||||
|
aria-expanded={isOpen}
|
||||||
|
aria-controls={`appeal-status-content-${index}`}
|
||||||
|
onClick={() =>
|
||||||
|
toggleSection(index)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span className="appeal-stage-chart__row">
|
||||||
|
<span className="appeal-stage-chart__label">
|
||||||
|
{appeal.value} (
|
||||||
|
{percentageOf(
|
||||||
|
appeal.caseCount,
|
||||||
|
totalCases
|
||||||
|
)}
|
||||||
|
)
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className="appeal-stage-chart__bar-wrapper">
|
||||||
|
<span
|
||||||
|
className="appeal-stage-chart__bar"
|
||||||
|
style={{
|
||||||
|
width: `${barWidth}%`
|
||||||
|
}}
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
{sortedStatuses.map(
|
||||||
|
(
|
||||||
|
status,
|
||||||
|
statusIndex
|
||||||
|
) => (
|
||||||
|
<span
|
||||||
|
key={`${status.statuscode}-${statusIndex}`}
|
||||||
|
className={`appeal-stage-chart__segment appeal-stage-chart__segment--${
|
||||||
|
statusIndex %
|
||||||
|
6
|
||||||
|
}`}
|
||||||
|
style={{
|
||||||
|
width: `${
|
||||||
|
((status.caseCount ||
|
||||||
|
0) /
|
||||||
|
(appeal.caseCount ||
|
||||||
|
1)) *
|
||||||
|
100
|
||||||
|
}%`
|
||||||
|
}}
|
||||||
|
title={`${status.value}: ${status.caseCount}`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span className="appeal-stage-chart__count">
|
||||||
|
{appeal.caseCount ||
|
||||||
|
0}{" "}
|
||||||
|
cases
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id={`appeal-status-content-${index}`}
|
||||||
|
className="govuk-accordion__section-content"
|
||||||
|
aria-labelledby={`appeal-status-heading-${index}`}
|
||||||
|
hidden={!isOpen}
|
||||||
|
>
|
||||||
|
<dl className="govuk-summary-list">
|
||||||
|
{sortedStatuses.map((status) => (
|
||||||
|
<div
|
||||||
|
className="govuk-summary-list__row"
|
||||||
|
key={status.statuscode}
|
||||||
|
>
|
||||||
|
<dt className="govuk-summary-list__key">
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
href={`/advancedsearchresults?apt=${appeal.appealType}&statuscode=${status.statuscode}`}
|
||||||
|
>
|
||||||
|
{status.value}
|
||||||
|
</Link>
|
||||||
|
</dt>
|
||||||
|
|
||||||
|
<dd className="govuk-summary-list__value">
|
||||||
|
{status.caseCount || 0}{" "}
|
||||||
|
cases (
|
||||||
|
{percentageOf(
|
||||||
|
status.caseCount,
|
||||||
|
appeal.caseCount
|
||||||
|
)}{" "}
|
||||||
|
of this appeal type)
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DashboardCard({ title, value, caption }) {
|
||||||
|
return (
|
||||||
|
<div className="govuk-grid-column-one-quarter">
|
||||||
|
<div className="dashboard-card">
|
||||||
|
<p className="govuk-body-s dashboard-card__title">{title}</p>
|
||||||
|
<p className="govuk-heading-l dashboard-card__value">{value}</p>
|
||||||
|
{caption && (
|
||||||
|
<p className="govuk-body-s dashboard-card__caption">
|
||||||
|
{caption}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,571 @@
|
|||||||
|
import { useMemo, useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function AppealLpaStatusDashboard({ statusObj = [] }) {
|
||||||
|
const [openAppeals, setOpenAppeals] = useState({});
|
||||||
|
const [openLpaSections, setOpenLpaSections] = useState({});
|
||||||
|
const [openAppealLpas, setOpenAppealLpas] = useState({});
|
||||||
|
const [openLpaAppeals, setOpenLpaAppeals] = useState({});
|
||||||
|
|
||||||
|
const percentageOf = (value, total) =>
|
||||||
|
total ? `${(((value || 0) / total) * 100).toFixed(1)}%` : "0.0%";
|
||||||
|
|
||||||
|
const totalCases = useMemo(
|
||||||
|
() =>
|
||||||
|
statusObj.reduce((total, item) => total + (item.caseCount || 0), 0),
|
||||||
|
[statusObj]
|
||||||
|
);
|
||||||
|
|
||||||
|
const groupedAppeals = useMemo(() => {
|
||||||
|
const grouped = statusObj.reduce((acc, item) => {
|
||||||
|
const appealKey = item.appealType;
|
||||||
|
|
||||||
|
if (!acc[appealKey]) {
|
||||||
|
acc[appealKey] = {
|
||||||
|
appealType: item.appealType,
|
||||||
|
appealTypeName: item.appealTypeName,
|
||||||
|
caseCount: 0,
|
||||||
|
lpas: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[appealKey].caseCount += item.caseCount || 0;
|
||||||
|
|
||||||
|
const lpaKey = item.lpaId || "unknown-lpa";
|
||||||
|
|
||||||
|
if (!acc[appealKey].lpas[lpaKey]) {
|
||||||
|
acc[appealKey].lpas[lpaKey] = {
|
||||||
|
lpaId: item.lpaId,
|
||||||
|
lpaName: item.lpaName || "Unknown LPA",
|
||||||
|
caseCount: 0,
|
||||||
|
statuses: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[appealKey].lpas[lpaKey].caseCount += item.caseCount || 0;
|
||||||
|
acc[appealKey].lpas[lpaKey].statuses.push({
|
||||||
|
statuscode: item.statuscode,
|
||||||
|
status: item.status,
|
||||||
|
caseCount: item.caseCount || 0
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return Object.values(grouped)
|
||||||
|
.map((appeal) => ({
|
||||||
|
...appeal,
|
||||||
|
lpas: Object.values(appeal.lpas)
|
||||||
|
.map((lpa) => ({
|
||||||
|
...lpa,
|
||||||
|
statuses: lpa.statuses.sort(
|
||||||
|
(a, b) => (b.caseCount || 0) - (a.caseCount || 0)
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.sort((a, b) => (b.caseCount || 0) - (a.caseCount || 0))
|
||||||
|
}))
|
||||||
|
.sort((a, b) => (b.caseCount || 0) - (a.caseCount || 0));
|
||||||
|
}, [statusObj]);
|
||||||
|
|
||||||
|
const groupedLpas = useMemo(() => {
|
||||||
|
const grouped = statusObj.reduce((acc, item) => {
|
||||||
|
const lpaKey = item.lpaId || "unknown-lpa";
|
||||||
|
|
||||||
|
if (!acc[lpaKey]) {
|
||||||
|
acc[lpaKey] = {
|
||||||
|
lpaId: item.lpaId,
|
||||||
|
lpaName: item.lpaName || "Unknown LPA",
|
||||||
|
caseCount: 0,
|
||||||
|
appeals: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[lpaKey].caseCount += item.caseCount || 0;
|
||||||
|
|
||||||
|
const appealKey = item.appealType;
|
||||||
|
|
||||||
|
if (!acc[lpaKey].appeals[appealKey]) {
|
||||||
|
acc[lpaKey].appeals[appealKey] = {
|
||||||
|
appealType: item.appealType,
|
||||||
|
appealTypeName: item.appealTypeName,
|
||||||
|
caseCount: 0,
|
||||||
|
statuses: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[lpaKey].appeals[appealKey].caseCount += item.caseCount || 0;
|
||||||
|
acc[lpaKey].appeals[appealKey].statuses.push({
|
||||||
|
statuscode: item.statuscode,
|
||||||
|
status: item.status,
|
||||||
|
caseCount: item.caseCount || 0
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return Object.values(grouped)
|
||||||
|
.map((lpa) => ({
|
||||||
|
...lpa,
|
||||||
|
appeals: Object.values(lpa.appeals)
|
||||||
|
.map((appeal) => ({
|
||||||
|
...appeal,
|
||||||
|
statuses: appeal.statuses.sort(
|
||||||
|
(a, b) => (b.caseCount || 0) - (a.caseCount || 0)
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.sort((a, b) => (b.caseCount || 0) - (a.caseCount || 0))
|
||||||
|
}))
|
||||||
|
.sort((a, b) => (b.caseCount || 0) - (a.caseCount || 0));
|
||||||
|
}, [statusObj]);
|
||||||
|
|
||||||
|
const toggleAppeal = (appealType) => {
|
||||||
|
setOpenAppeals((current) => ({
|
||||||
|
...current,
|
||||||
|
[appealType]: !current[appealType]
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleLpaSection = (lpaId) => {
|
||||||
|
setOpenLpaSections((current) => ({
|
||||||
|
...current,
|
||||||
|
[lpaId]: !current[lpaId]
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleAppealLpa = (key) => {
|
||||||
|
setOpenAppealLpas((current) => ({
|
||||||
|
...current,
|
||||||
|
[key]: !current[key]
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleLpaAppeal = (key) => {
|
||||||
|
setOpenLpaAppeals((current) => ({
|
||||||
|
...current,
|
||||||
|
[key]: !current[key]
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortedAppeals = useMemo(
|
||||||
|
() => [...statusObj].sort((a, b) => (b.lpaId || 0) - (a.lpaId || 0)),
|
||||||
|
[statusObj]
|
||||||
|
);
|
||||||
|
|
||||||
|
const allStatuses = sortedAppeals.flatMap((appeal) =>
|
||||||
|
(appeal.statuses || []).map((status) => ({
|
||||||
|
...status,
|
||||||
|
lpaId: appeal.lpaId,
|
||||||
|
lpaName: appeal.lpaName
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
const statusTotals = allStatuses.reduce((acc, status) => {
|
||||||
|
if (!acc[status.statuscode]) {
|
||||||
|
acc[status.statuscode] = {
|
||||||
|
value: status.value,
|
||||||
|
statuscode: status.statuscode,
|
||||||
|
caseCount: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[status.statuscode].caseCount += status.caseCount || 0;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
const largestAppeal = groupedLpas[0];
|
||||||
|
|
||||||
|
const mostCommonStatus = Object.values(groupedAppeals).sort(
|
||||||
|
(a, b) => b.caseCount - a.caseCount
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
const maxAppealCount = largestAppeal?.caseCount || 1;
|
||||||
|
const maxGroupedAppealCount = groupedAppeals[0]?.caseCount || 1;
|
||||||
|
|
||||||
|
const allAppealsExpanded =
|
||||||
|
groupedAppeals.length > 0 &&
|
||||||
|
groupedAppeals.every((appeal) => openAppeals[appeal.appealType]);
|
||||||
|
|
||||||
|
const allLpasExpanded =
|
||||||
|
groupedLpas.length > 0 &&
|
||||||
|
groupedLpas.every((lpa) => openLpaSections[lpa.lpaId]);
|
||||||
|
|
||||||
|
const showAllAppeals = () => {
|
||||||
|
const allOpen = {};
|
||||||
|
groupedAppeals.forEach((appeal) => {
|
||||||
|
allOpen[appeal.appealType] = true;
|
||||||
|
});
|
||||||
|
setOpenAppeals(allOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
const showAllLpas = () => {
|
||||||
|
const allOpen = {};
|
||||||
|
groupedLpas.forEach((lpa) => {
|
||||||
|
allOpen[lpa.lpaId] = true;
|
||||||
|
});
|
||||||
|
setOpenLpaSections(allOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideAllAppeals = () => {
|
||||||
|
setOpenAppeals({});
|
||||||
|
setOpenAppealLpas({});
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideAllLpas = () => {
|
||||||
|
setOpenLpaSections({});
|
||||||
|
setOpenLpaAppeals({});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="govuk-grid-row">
|
||||||
|
<div className="govuk-grid-column-full">
|
||||||
|
<h1 className="">LPA Appeal case status dashboard</h1>
|
||||||
|
|
||||||
|
<div className="govuk-grid-row">
|
||||||
|
<DashboardCard title="Total cases" value={totalCases} />
|
||||||
|
<DashboardCard title="LPAs" value={groupedLpas.length} />
|
||||||
|
<DashboardCard
|
||||||
|
title="Largest number of appeals"
|
||||||
|
value={largestAppeal?.caseCount || 0}
|
||||||
|
caption={largestAppeal?.lpaName}
|
||||||
|
/>
|
||||||
|
<DashboardCard
|
||||||
|
title="Most common appeal"
|
||||||
|
value={mostCommonStatus?.caseCount || 0}
|
||||||
|
caption={mostCommonStatus?.appealTypeName}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section className="govuk-!-margin-top-8">
|
||||||
|
<h2 className="govuk-heading-m">
|
||||||
|
Appeal types and case stages by LPA
|
||||||
|
</h2>
|
||||||
|
<div className="govuk-accordion__controls">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="govuk-accordion__show-all"
|
||||||
|
onClick={allLpasExpanded ? hideAllLpas : showAllLpas}
|
||||||
|
>
|
||||||
|
<span className="govuk-accordion__show-all-text">
|
||||||
|
{allLpasExpanded
|
||||||
|
? "Hide all sections"
|
||||||
|
: "Show all sections"}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="govuk-accordion"
|
||||||
|
id="lpa-appeal-status-accordion"
|
||||||
|
>
|
||||||
|
{groupedLpas.map((lpa) => {
|
||||||
|
const lpaOpen = !!openLpaSections[lpa.lpaId];
|
||||||
|
|
||||||
|
const width =
|
||||||
|
((lpa.caseCount || 0) / maxAppealCount) * 100;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
lpaOpen
|
||||||
|
? "govuk-accordion__section govuk-accordion__section--expanded"
|
||||||
|
: "govuk-accordion__section"
|
||||||
|
}
|
||||||
|
key={lpa.lpaId}
|
||||||
|
>
|
||||||
|
<div className="govuk-accordion__section-header">
|
||||||
|
<h3 className="govuk-accordion__section-heading">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="govuk-accordion__section-button"
|
||||||
|
aria-expanded={lpaOpen}
|
||||||
|
onClick={() =>
|
||||||
|
toggleLpaSection(lpa.lpaId)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="appeal-count-chart__row">
|
||||||
|
<div className="appeal-count-chart__label">
|
||||||
|
{lpa.lpaName} (
|
||||||
|
{percentageOf(
|
||||||
|
lpa.caseCount,
|
||||||
|
totalCases
|
||||||
|
)}
|
||||||
|
)
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="appeal-count-chart__bar-wrapper">
|
||||||
|
<span
|
||||||
|
className="appeal-count-chart__bar"
|
||||||
|
style={{
|
||||||
|
width: `${width}%`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="appeal-count-chart__count">
|
||||||
|
{lpa.caseCount}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="govuk-accordion__section-content"
|
||||||
|
hidden={!lpaOpen}
|
||||||
|
>
|
||||||
|
{lpa.appeals.map((appeal) => {
|
||||||
|
const key = `${lpa.lpaId}-${appeal.appealType}`;
|
||||||
|
const appealOpen =
|
||||||
|
!!openLpaAppeals[key];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="lpa-status-block"
|
||||||
|
key={key}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="lpa-status-block__button"
|
||||||
|
aria-expanded={appealOpen}
|
||||||
|
onClick={() =>
|
||||||
|
toggleLpaAppeal(key)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span className="govuk-body-m">
|
||||||
|
{appeal.appealTypeName}
|
||||||
|
</span>
|
||||||
|
<span className="govuk-body-m">
|
||||||
|
{appeal.caseCount} cases
|
||||||
|
(
|
||||||
|
{percentageOf(
|
||||||
|
appeal.caseCount,
|
||||||
|
lpa.caseCount
|
||||||
|
)}
|
||||||
|
)
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div hidden={!appealOpen}>
|
||||||
|
<dl className="govuk-summary-list govuk-!-margin-bottom-4">
|
||||||
|
{appeal.statuses.map(
|
||||||
|
(status) => (
|
||||||
|
<div
|
||||||
|
className="govuk-summary-list__row"
|
||||||
|
key={
|
||||||
|
status.statuscode
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<dt className="govuk-summary-list__key">
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
href={`/advancedsearchresults?apt=${appeal.appealType}&lpa=${lpa.lpaId}&statuscode=${status.statuscode}`}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
status.status
|
||||||
|
}
|
||||||
|
</Link>
|
||||||
|
</dt>
|
||||||
|
<dd className="govuk-summary-list__value">
|
||||||
|
{
|
||||||
|
status.caseCount
|
||||||
|
}{" "}
|
||||||
|
(
|
||||||
|
{percentageOf(
|
||||||
|
status.caseCount,
|
||||||
|
appeal.caseCount
|
||||||
|
)}
|
||||||
|
)
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="govuk-!-margin-top-8">
|
||||||
|
<h2 className="govuk-heading-m">
|
||||||
|
Case stage by LPA and Appeal Type
|
||||||
|
</h2>
|
||||||
|
<p className="govuk-body-s">
|
||||||
|
Select an appeal type row to show the LPA and then case
|
||||||
|
stage breakdown.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="govuk-accordion__controls">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="govuk-accordion__show-all"
|
||||||
|
onClick={
|
||||||
|
allAppealsExpanded ? hideAllAppeals : showAllAppeals
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span className="govuk-accordion__show-all-text">
|
||||||
|
{allAppealsExpanded
|
||||||
|
? "Hide all sections"
|
||||||
|
: "Show all sections"}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="govuk-accordion"
|
||||||
|
id="appeal-lpa-status-accordion"
|
||||||
|
>
|
||||||
|
{groupedAppeals.map((appeal) => {
|
||||||
|
const appealOpen = !!openAppeals[appeal.appealType];
|
||||||
|
|
||||||
|
const width =
|
||||||
|
((appeal.caseCount || 0) / maxGroupedAppealCount) *
|
||||||
|
100;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
appealOpen
|
||||||
|
? "govuk-accordion__section govuk-accordion__section--expanded"
|
||||||
|
: "govuk-accordion__section"
|
||||||
|
}
|
||||||
|
key={appeal.appealType}
|
||||||
|
>
|
||||||
|
<div className="govuk-accordion__section-header">
|
||||||
|
<h2 className="govuk-accordion__section-heading">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="govuk-accordion__section-button"
|
||||||
|
aria-expanded={appealOpen}
|
||||||
|
onClick={() =>
|
||||||
|
toggleAppeal(appeal.appealType)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="appeal-count-chart__row">
|
||||||
|
<div className="appeal-count-chart__label">
|
||||||
|
{appeal.appealTypeName}{" "}
|
||||||
|
{/* {appeal.caseCount} cases */}
|
||||||
|
(
|
||||||
|
{percentageOf(
|
||||||
|
appeal.caseCount,
|
||||||
|
totalCases
|
||||||
|
)}
|
||||||
|
)
|
||||||
|
</div>
|
||||||
|
<div className="appeal-count-chart__bar-wrapper">
|
||||||
|
<span
|
||||||
|
className="appeal-count-chart__bar"
|
||||||
|
style={{
|
||||||
|
width: `${width}%`
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="appeal-count-chart__count">
|
||||||
|
{appeal.caseCount}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="govuk-accordion__section-content"
|
||||||
|
hidden={!appealOpen}
|
||||||
|
>
|
||||||
|
{appeal.lpas.map((lpa) => {
|
||||||
|
const lpaKey = `${appeal.appealType}-${lpa.lpaId}`;
|
||||||
|
const lpaOpen =
|
||||||
|
!!openAppealLpas[lpaKey];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="lpa-status-block"
|
||||||
|
key={lpaKey}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="lpa-status-block__button"
|
||||||
|
aria-expanded={lpaOpen}
|
||||||
|
onClick={() =>
|
||||||
|
toggleAppealLpa(lpaKey)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span className="govuk-body-m">
|
||||||
|
{lpa.lpaName}
|
||||||
|
</span>
|
||||||
|
<span className="govuk-body-m">
|
||||||
|
{lpa.caseCount} cases (
|
||||||
|
{percentageOf(
|
||||||
|
lpa.caseCount,
|
||||||
|
appeal.caseCount
|
||||||
|
)}
|
||||||
|
)
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div hidden={!lpaOpen}>
|
||||||
|
<dl className="govuk-summary-list govuk-!-margin-bottom-4">
|
||||||
|
{lpa.statuses.map(
|
||||||
|
(status) => (
|
||||||
|
<div
|
||||||
|
className="govuk-summary-list__row"
|
||||||
|
key={
|
||||||
|
status.statuscode
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<dt className="govuk-summary-list__key">
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
href={`/advancedsearchresults?apt=${appeal.appealType}&lpa=${lpa.lpaId}&statuscode=${status.statuscode}`}
|
||||||
|
>
|
||||||
|
{
|
||||||
|
status.status
|
||||||
|
}
|
||||||
|
</Link>
|
||||||
|
</dt>
|
||||||
|
<dd className="govuk-summary-list__value">
|
||||||
|
{
|
||||||
|
status.caseCount
|
||||||
|
}{" "}
|
||||||
|
(
|
||||||
|
{percentageOf(
|
||||||
|
status.caseCount,
|
||||||
|
lpa.caseCount
|
||||||
|
)}
|
||||||
|
)
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DashboardCard({ title, value, caption }) {
|
||||||
|
return (
|
||||||
|
<div className="govuk-grid-column-one-quarter">
|
||||||
|
<div className="dashboard-card">
|
||||||
|
<p className="govuk-body-s dashboard-card__title">{title}</p>
|
||||||
|
<p className="govuk-heading-l dashboard-card__value">{value}</p>
|
||||||
|
{caption && (
|
||||||
|
<p className="govuk-body-s dashboard-card__caption">
|
||||||
|
{caption}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,294 @@
|
|||||||
|
// // components/admin/tabs/AccountsTab.js
|
||||||
|
// import Link from "next/link";
|
||||||
|
|
||||||
|
// export default function AppealsWithStatusTab({ statusObj }) {
|
||||||
|
// return (
|
||||||
|
// <div>
|
||||||
|
// <h1>User Accounts</h1>
|
||||||
|
|
||||||
|
// <dl className="govuk-summary-list govuk-!-margin-bottom-9 results">
|
||||||
|
// <div className="govuk-summary-list__row results-headings">
|
||||||
|
// <dd className="govuk-summary-list__key govuk-!-font-size-16">
|
||||||
|
// Name
|
||||||
|
// </dd>
|
||||||
|
// <dd className="govuk-summary-list__key govuk-!-font-size-16">
|
||||||
|
// Last accessed
|
||||||
|
// </dd>
|
||||||
|
// </div>
|
||||||
|
|
||||||
|
// {statusObj.map((appeal) => (
|
||||||
|
// <div key={appeal.id} className="govuk-summary-list__row">
|
||||||
|
// <dd className="govuk-summary-list__value govuk-!-font-size-16 ">
|
||||||
|
// <Link
|
||||||
|
// target="_blank"
|
||||||
|
// href={`/auth/signin2?email=${appeal.value}`}
|
||||||
|
// >
|
||||||
|
// {appeal.value}
|
||||||
|
// </Link>
|
||||||
|
// <br />
|
||||||
|
|
||||||
|
// <dl className="govuk-summary-list govuk-!-margin-bottom-9 govuk-!-margin-left-9 results">
|
||||||
|
// <div className="govuk-summary-list__row results-headings">
|
||||||
|
// <dd className="govuk-summary-list__key govuk-!-font-size-16">
|
||||||
|
// Name
|
||||||
|
// </dd>
|
||||||
|
// <dd className="govuk-summary-list__key govuk-!-font-size-16">
|
||||||
|
// Last accessed
|
||||||
|
// </dd>
|
||||||
|
// </div>
|
||||||
|
|
||||||
|
// {appeal.statuses.map((statuses) => (
|
||||||
|
// <div
|
||||||
|
// key={appeal.id}
|
||||||
|
// className="govuk-summary-list__row"
|
||||||
|
// >
|
||||||
|
// <dd className="govuk-summary-list__value govuk-!-font-size-16 ">
|
||||||
|
// {statuses.value}
|
||||||
|
// </dd>
|
||||||
|
// <dd className="govuk-summary-list__value govuk-!-font-size-16 ">
|
||||||
|
// Total: {statuses.caseCount}
|
||||||
|
// </dd>
|
||||||
|
// </div>
|
||||||
|
// ))}
|
||||||
|
// </dl>
|
||||||
|
// </dd>
|
||||||
|
// <dd className="govuk-summary-list__value govuk-!-font-size-16 ">
|
||||||
|
// Total: {appeal.caseCount}
|
||||||
|
// </dd>
|
||||||
|
// </div>
|
||||||
|
// ))}
|
||||||
|
// </dl>
|
||||||
|
// </div>
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function AppealsWithStatusTab({ statusObj = [] }) {
|
||||||
|
const [openSections, setOpenSections] = useState({});
|
||||||
|
|
||||||
|
const toggleSection = (index) => {
|
||||||
|
setOpenSections((current) => ({
|
||||||
|
...current,
|
||||||
|
[index]: !current[index]
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const showAll = () => {
|
||||||
|
const allOpen = {};
|
||||||
|
|
||||||
|
statusObj.forEach((_, index) => {
|
||||||
|
allOpen[index] = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
setOpenSections(allOpen);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideAll = () => {
|
||||||
|
setOpenSections({});
|
||||||
|
};
|
||||||
|
|
||||||
|
const allExpanded =
|
||||||
|
statusObj.length > 0 &&
|
||||||
|
statusObj.every((_, index) => openSections[index]);
|
||||||
|
|
||||||
|
const sortedAppeals = [...statusObj].sort(
|
||||||
|
(a, b) => (b.caseCount || 0) - (a.caseCount || 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
const maxCount = sortedAppeals[0]?.caseCount || 1;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1 className="govuk-heading-l">Case status by appeal type</h1>
|
||||||
|
|
||||||
|
<div className="govuk-!-margin-bottom-6">
|
||||||
|
<h2 className="govuk-heading-m">Case counts by appeal type</h2>
|
||||||
|
|
||||||
|
<dl className="appeal-count-chart">
|
||||||
|
{sortedAppeals.map((appeal) => {
|
||||||
|
const maxCount = sortedAppeals[0]?.caseCount || 1;
|
||||||
|
const width =
|
||||||
|
((appeal.caseCount || 0) / maxCount) * 100;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="appeal-count-chart__row"
|
||||||
|
key={appeal.appealType}
|
||||||
|
>
|
||||||
|
<dt className="appeal-count-chart__label">
|
||||||
|
{appeal.value}
|
||||||
|
</dt>
|
||||||
|
|
||||||
|
<dd className="appeal-count-chart__bar-wrapper">
|
||||||
|
<span
|
||||||
|
className="appeal-count-chart__bar"
|
||||||
|
style={{ width: `${width}%` }}
|
||||||
|
/>
|
||||||
|
<span className="appeal-count-chart__count">
|
||||||
|
{appeal.caseCount || 0}
|
||||||
|
</span>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="govuk-!-margin-bottom-6">
|
||||||
|
<h2 className="govuk-heading-m">Case stages by appeal type</h2>
|
||||||
|
|
||||||
|
<dl className="appeal-stage-chart">
|
||||||
|
{sortedAppeals.map((appeal) => (
|
||||||
|
<div
|
||||||
|
className="appeal-stage-chart__row"
|
||||||
|
key={appeal.appealType}
|
||||||
|
>
|
||||||
|
<dt className="appeal-stage-chart__label">
|
||||||
|
{appeal.value}
|
||||||
|
</dt>
|
||||||
|
|
||||||
|
<dd className="appeal-stage-chart__bar-wrapper">
|
||||||
|
<div
|
||||||
|
className="appeal-stage-chart__bar"
|
||||||
|
style={{
|
||||||
|
width: `${((appeal.caseCount || 0) / maxCount) * 100}%`
|
||||||
|
}}
|
||||||
|
aria-label={`${appeal.value}, ${appeal.caseCount || 0} cases`}
|
||||||
|
>
|
||||||
|
{(appeal.statuses || [])
|
||||||
|
.filter(
|
||||||
|
(status) => status.caseCount > 0
|
||||||
|
)
|
||||||
|
.sort(
|
||||||
|
(a, b) => b.caseCount - a.caseCount
|
||||||
|
)
|
||||||
|
.map((status, index) => (
|
||||||
|
<span
|
||||||
|
key={status.statuscode}
|
||||||
|
className={`appeal-stage-chart__segment appeal-stage-chart__segment--${index % 6}`}
|
||||||
|
style={{
|
||||||
|
width: `${((status.caseCount || 0) / (appeal.caseCount || 1)) * 100}%`
|
||||||
|
}}
|
||||||
|
title={`${status.value}: ${status.caseCount}`}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span className="appeal-stage-chart__count">
|
||||||
|
{appeal.caseCount || 0}
|
||||||
|
</span>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="govuk-accordion__controls">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="govuk-accordion__show-all"
|
||||||
|
onClick={allExpanded ? hideAll : showAll}
|
||||||
|
>
|
||||||
|
<span className="govuk-accordion__show-all-text">
|
||||||
|
{allExpanded
|
||||||
|
? "Hide all sections"
|
||||||
|
: "Show all sections"}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="govuk-accordion" id="appeals-with-status-accordion">
|
||||||
|
{[...statusObj]
|
||||||
|
.sort((a, b) => (b.caseCount || 0) - (a.caseCount || 0))
|
||||||
|
.map((appeal, index) => {
|
||||||
|
const isOpen = !!openSections[index];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
isOpen
|
||||||
|
? "govuk-accordion__section govuk-accordion__section--expanded"
|
||||||
|
: "govuk-accordion__section"
|
||||||
|
}
|
||||||
|
key={appeal.appealType || index}
|
||||||
|
>
|
||||||
|
<div className="govuk-accordion__section-header ">
|
||||||
|
<h2 className="govuk-accordion__section-heading">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="govuk-accordion__section-button "
|
||||||
|
id={`appeal-status-heading-${index}`}
|
||||||
|
aria-expanded={isOpen}
|
||||||
|
aria-controls={`appeal-status-content-${index}`}
|
||||||
|
onClick={() => toggleSection(index)}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent:
|
||||||
|
"space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
width: "100%",
|
||||||
|
paddingRight: "20px" // leaves room for accordion icon
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="govuk-heading-s ">
|
||||||
|
{appeal.value}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span
|
||||||
|
className="govuk-body-s"
|
||||||
|
style={{
|
||||||
|
fontWeight: "normal"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{appeal.caseCount || 0}{" "}
|
||||||
|
cases
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id={`appeal-status-content-${index}`}
|
||||||
|
className="govuk-accordion__section-content"
|
||||||
|
aria-labelledby={`appeal-status-heading-${index}`}
|
||||||
|
hidden={!isOpen}
|
||||||
|
>
|
||||||
|
<dl className="govuk-summary-list govuk-!-margin-left-9">
|
||||||
|
{(appeal.statuses || [])
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
(b.caseCount || 0) -
|
||||||
|
(a.caseCount || 0)
|
||||||
|
)
|
||||||
|
.map((status) => (
|
||||||
|
<div
|
||||||
|
className="govuk-summary-list__row"
|
||||||
|
key={status.statuscode}
|
||||||
|
>
|
||||||
|
<dt className="govuk-summary-list__key">
|
||||||
|
<Link
|
||||||
|
target="_blank"
|
||||||
|
href={`/advancedsearchresults?apt=${appeal.appealType}&statuscode=${status.statuscode}`}
|
||||||
|
>
|
||||||
|
{status.value}
|
||||||
|
</Link>
|
||||||
|
</dt>
|
||||||
|
<dd className="govuk-summary-list__value">
|
||||||
|
{status.caseCount || 0}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1104,9 +1104,7 @@ const DocumentsTab = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div className="govuk-grid-row">
|
<div className="govuk-grid-row">
|
||||||
<div className="govuk-grid-column-full">
|
<div className="govuk-grid-column-full">
|
||||||
<h2 className="govuk-heading-m ">
|
<h1 className="">{t("case:documents-heading-label")}</h1>
|
||||||
{t("case:documents-heading-label")}
|
|
||||||
</h2>
|
|
||||||
{/* <div className="">
|
{/* <div className="">
|
||||||
MDU :{" "}
|
MDU :{" "}
|
||||||
{countFieldValue(
|
{countFieldValue(
|
||||||
|
|||||||
@@ -283,5 +283,15 @@ export const statusValues = [
|
|||||||
"value": "Case Submitted",
|
"value": "Case Submitted",
|
||||||
"value_cy": "Achos wedi’i gyflwyno",
|
"value_cy": "Achos wedi’i gyflwyno",
|
||||||
"statuscode": 846040061
|
"statuscode": 846040061
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "Problem Solved",
|
||||||
|
"value_cy": "Problem wedi'i Datrys",
|
||||||
|
"statuscode": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "Information Provided",
|
||||||
|
"value_cy": "Gwybodaeth a Ddarperir",
|
||||||
|
"statuscode": 1000
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
+51
-4
@@ -11,9 +11,16 @@ import StorageTab from "../../components/admin/tabs/storage";
|
|||||||
import AccountsTab from "../../components/admin/tabs/accounts";
|
import AccountsTab from "../../components/admin/tabs/accounts";
|
||||||
import AppealsTab from "../../components/admin/tabs/appeals";
|
import AppealsTab from "../../components/admin/tabs/appeals";
|
||||||
import DocumentsTab from "../../components/admin/tabs/documents";
|
import DocumentsTab from "../../components/admin/tabs/documents";
|
||||||
|
import AppealsWithStatusTab from "../../components/admin/tabs/appealsWithStatus";
|
||||||
|
import AppealStatusDashboard from "../../components/admin/tabs/appealsDashboard";
|
||||||
|
import AppealLpaStatusDashboard from "../../components/admin/tabs/appealsDashboardLPA";
|
||||||
|
|
||||||
import { fetchAdminStorageData } from "../../components/admin/utils/serverside";
|
import { fetchAdminStorageData } from "../../components/admin/utils/serverside";
|
||||||
import { getNewAppeals } from "../../actions/services/adminService";
|
import {
|
||||||
|
getNewAppeals,
|
||||||
|
getStatusByAppeal,
|
||||||
|
getStatusByAppealLPA
|
||||||
|
} from "../../actions/services/adminService";
|
||||||
|
|
||||||
import { getSearchDetails } from "../../components/utils";
|
import { getSearchDetails } from "../../components/utils";
|
||||||
|
|
||||||
@@ -30,9 +37,11 @@ const StoragePage = (props) => {
|
|||||||
repCompleteCount,
|
repCompleteCount,
|
||||||
searchResultsObj,
|
searchResultsObj,
|
||||||
docsOffline,
|
docsOffline,
|
||||||
showFilteredDocs
|
showFilteredDocs,
|
||||||
|
statusObj,
|
||||||
|
statusLPAObj
|
||||||
} = props;
|
} = props;
|
||||||
const [whichTab, setWhichTab] = useState("documents");
|
const [whichTab, setWhichTab] = useState("appealDashboard");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -54,6 +63,8 @@ const StoragePage = (props) => {
|
|||||||
{/* Tabs */}
|
{/* Tabs */}
|
||||||
<ul className="govuk-tabs__list">
|
<ul className="govuk-tabs__list">
|
||||||
{[
|
{[
|
||||||
|
"appealDashboard",
|
||||||
|
"appealDashboardLPA",
|
||||||
"documents",
|
"documents",
|
||||||
"appeals",
|
"appeals",
|
||||||
"storage",
|
"storage",
|
||||||
@@ -85,6 +96,10 @@ const StoragePage = (props) => {
|
|||||||
"Storage Account"}
|
"Storage Account"}
|
||||||
{tab === "accounts" &&
|
{tab === "accounts" &&
|
||||||
"User Accounts"}
|
"User Accounts"}
|
||||||
|
{tab === "appealDashboard" &&
|
||||||
|
"Appeals Dashboard"}
|
||||||
|
{tab === "appealDashboardLPA" &&
|
||||||
|
"Appeals LPA Dashboard"}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
@@ -92,6 +107,26 @@ const StoragePage = (props) => {
|
|||||||
|
|
||||||
{/* Panels */}
|
{/* Panels */}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
whichTab === "appealDashboard"
|
||||||
|
? "govuk-tabs__panel"
|
||||||
|
: "govuk-tabs__panel govuk-tabs__panel--hidden"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<AppealStatusDashboard statusObj={statusObj} />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
whichTab === "appealDashboardLPA"
|
||||||
|
? "govuk-tabs__panel"
|
||||||
|
: "govuk-tabs__panel govuk-tabs__panel--hidden"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<AppealLpaStatusDashboard
|
||||||
|
statusObj={statusLPAObj}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
whichTab === "documents"
|
whichTab === "documents"
|
||||||
@@ -119,6 +154,15 @@ const StoragePage = (props) => {
|
|||||||
searchResultsObj={searchResultsObj}
|
searchResultsObj={searchResultsObj}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
whichTab === "appealsWithStatus"
|
||||||
|
? "govuk-tabs__panel"
|
||||||
|
: "govuk-tabs__panel govuk-tabs__panel--hidden"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<AppealsWithStatusTab statusObj={statusObj} />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
@@ -177,7 +221,8 @@ export const getServerSideProps = wrapper.getServerSideProps(
|
|||||||
await fetchAdminStorageData();
|
await fetchAdminStorageData();
|
||||||
|
|
||||||
const newAppeals = await getNewAppeals();
|
const newAppeals = await getNewAppeals();
|
||||||
|
const statusObj = await getStatusByAppeal();
|
||||||
|
const statusLPAObj = await getStatusByAppealLPA();
|
||||||
const searchDetailsObj = await getSearchDetails(newAppeals);
|
const searchDetailsObj = await getSearchDetails(newAppeals);
|
||||||
|
|
||||||
store.dispatch(setSearchResults(newAppeals));
|
store.dispatch(setSearchResults(newAppeals));
|
||||||
@@ -188,6 +233,8 @@ export const getServerSideProps = wrapper.getServerSideProps(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
|
statusObj,
|
||||||
|
statusLPAObj,
|
||||||
data,
|
data,
|
||||||
userData,
|
userData,
|
||||||
repCompleteCount,
|
repCompleteCount,
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { azureHeaders } from "../../../actions/core/headers";
|
||||||
|
import { relayGet } from "../middleware/relayForwarding";
|
||||||
|
import { RELAY_POLICY_BOUNDED_READ } from "../middleware/relayPolicyPresets";
|
||||||
|
|
||||||
|
import { statusValues } from "../../../components/utils/caseStagesObj";
|
||||||
|
|
||||||
|
export default async function ApiProxy(req, res) {
|
||||||
|
const queryUrl =
|
||||||
|
"incidents?$apply=groupby((statuscode,pinswg_appealcasetype),aggregate($count as caseCount))&$filter=pinswg_publishtoweb eq true";
|
||||||
|
|
||||||
|
return relayGet({
|
||||||
|
queryUrl,
|
||||||
|
res,
|
||||||
|
requestOptionsBuilder: (accessToken) => azureHeaders(accessToken),
|
||||||
|
relayPolicy: RELAY_POLICY_BOUNDED_READ,
|
||||||
|
transformData: (data) => {
|
||||||
|
const countsByStatusCode = (data.value || []).reduce(
|
||||||
|
(acc, item) => {
|
||||||
|
const statuscode = item.statuscode;
|
||||||
|
|
||||||
|
if (!acc[statuscode]) {
|
||||||
|
acc[statuscode] = {
|
||||||
|
caseCount: 0,
|
||||||
|
appealTypes: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[statuscode].caseCount += item.caseCount || 0;
|
||||||
|
|
||||||
|
acc[statuscode].appealTypes.push({
|
||||||
|
value:
|
||||||
|
item[
|
||||||
|
"pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue"
|
||||||
|
] || "",
|
||||||
|
appealType: item.pinswg_appealcasetype,
|
||||||
|
caseCount: item.caseCount || 0
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
return statusValues.map((status) => ({
|
||||||
|
...status,
|
||||||
|
caseCount: countsByStatusCode[status.statuscode] || 0,
|
||||||
|
appealTypes:
|
||||||
|
countsByStatusCode[status.statuscode]?.appealTypes || []
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
errorResponse: {
|
||||||
|
status: 400,
|
||||||
|
code: "STATUS_COUNTS_FETCH_FAILED",
|
||||||
|
message: "Failed to fetch status counts"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { azureHeaders } from "../../../actions/core/headers";
|
||||||
|
import { relayGet } from "../middleware/relayForwarding";
|
||||||
|
import { RELAY_POLICY_BOUNDED_READ } from "../middleware/relayPolicyPresets";
|
||||||
|
|
||||||
|
import { statusValues } from "../../../components/utils/caseStagesObj";
|
||||||
|
|
||||||
|
export default async function ApiProxy(req, res) {
|
||||||
|
// const queryUrl =
|
||||||
|
// "incidents?$apply=groupby((statuscode,pinswg_appealcasetype),aggregate($count as caseCount))&$filter=pinswg_publishtoweb eq true";
|
||||||
|
const queryUrl =
|
||||||
|
"incidents?$select=statuscode,pinswg_appealcasetype,_pinswg_associatedlpa_value&$filter=pinswg_publishtoweb eq true";
|
||||||
|
return relayGet({
|
||||||
|
queryUrl,
|
||||||
|
res,
|
||||||
|
requestOptionsBuilder: (accessToken) => azureHeaders(accessToken),
|
||||||
|
relayPolicy: RELAY_POLICY_BOUNDED_READ,
|
||||||
|
|
||||||
|
transformData: (data) => {
|
||||||
|
const grouped = (data.value || []).reduce((acc, item) => {
|
||||||
|
const key = [
|
||||||
|
item.statuscode,
|
||||||
|
item.pinswg_appealcasetype,
|
||||||
|
item._pinswg_associatedlpa_value || "no-lpa"
|
||||||
|
].join("|");
|
||||||
|
|
||||||
|
if (!acc[key]) {
|
||||||
|
acc[key] = {
|
||||||
|
statuscode: item.statuscode,
|
||||||
|
status: item[
|
||||||
|
"statuscode@OData.Community.Display.V1.FormattedValue"
|
||||||
|
],
|
||||||
|
appealType: item.pinswg_appealcasetype,
|
||||||
|
appealTypeName:
|
||||||
|
item[
|
||||||
|
"pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue"
|
||||||
|
],
|
||||||
|
lpaId: item._pinswg_associatedlpa_value || null,
|
||||||
|
lpaName:
|
||||||
|
item[
|
||||||
|
"_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue"
|
||||||
|
] || null,
|
||||||
|
caseCount: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[key].caseCount += 1;
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return Object.values(grouped);
|
||||||
|
},
|
||||||
|
errorResponse: {
|
||||||
|
status: 400,
|
||||||
|
code: "STATUS_COUNTS_FETCH_FAILED",
|
||||||
|
message: "Failed to fetch status counts"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { azureHeaders } from "../../../actions/core/headers";
|
||||||
|
import { relayGet } from "../middleware/relayForwarding";
|
||||||
|
import { RELAY_POLICY_BOUNDED_READ } from "../middleware/relayPolicyPresets";
|
||||||
|
|
||||||
|
import { statusValues } from "../../../components/utils/caseStagesObj";
|
||||||
|
|
||||||
|
export default async function ApiProxy(req, res) {
|
||||||
|
const queryUrl =
|
||||||
|
"incidents?$apply=groupby((statuscode,pinswg_appealcasetype),aggregate($count as caseCount))&$filter=pinswg_publishtoweb eq true";
|
||||||
|
|
||||||
|
return relayGet({
|
||||||
|
queryUrl,
|
||||||
|
res,
|
||||||
|
requestOptionsBuilder: (accessToken) => azureHeaders(accessToken),
|
||||||
|
relayPolicy: RELAY_POLICY_BOUNDED_READ,
|
||||||
|
transformData: (data) => {
|
||||||
|
const groupedByAppealType = (data.value || []).reduce(
|
||||||
|
(acc, item) => {
|
||||||
|
const appealType = item.pinswg_appealcasetype;
|
||||||
|
|
||||||
|
if (!acc[appealType]) {
|
||||||
|
acc[appealType] = {
|
||||||
|
value:
|
||||||
|
item[
|
||||||
|
"pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue"
|
||||||
|
] || "",
|
||||||
|
appealType,
|
||||||
|
caseCount: 0,
|
||||||
|
statuses: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
acc[appealType].caseCount += item.caseCount || 0;
|
||||||
|
|
||||||
|
acc[appealType].statuses.push({
|
||||||
|
value:
|
||||||
|
item[
|
||||||
|
"statuscode@OData.Community.Display.V1.FormattedValue"
|
||||||
|
] || "",
|
||||||
|
statuscode: item.statuscode,
|
||||||
|
caseCount: item.caseCount || 0
|
||||||
|
});
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
return Object.values(groupedByAppealType);
|
||||||
|
},
|
||||||
|
errorResponse: {
|
||||||
|
status: 400,
|
||||||
|
code: "STATUS_COUNTS_FETCH_FAILED",
|
||||||
|
message: "Failed to fetch status counts"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -3288,4 +3288,259 @@ ul.subFieldList {
|
|||||||
.govuk-task-list__name-and-hint {
|
.govuk-task-list__name-and-hint {
|
||||||
width: 85%;
|
width: 85%;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-count-chart {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-count-chart__row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(220px, 45%) 1fr;
|
||||||
|
gap: 15px;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-count-chart__label {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.2rem
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-count-chart__bar-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-count-chart__bar {
|
||||||
|
display: inline-block;
|
||||||
|
height: 16px;
|
||||||
|
background: #1d70b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-count-chart__count {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(260px, 42%) 1fr;
|
||||||
|
gap: 15px;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__label {
|
||||||
|
font-weight: 700;
|
||||||
|
font-weight: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__bar-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__bar {
|
||||||
|
display: flex;
|
||||||
|
height: 18px;
|
||||||
|
min-width: 2px;
|
||||||
|
background: #f3f2f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__segment {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__segment--0 {
|
||||||
|
background: #1d70b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__segment--1 {
|
||||||
|
background: #00703c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__segment--2 {
|
||||||
|
background: #f47738;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__segment--3 {
|
||||||
|
background: #b58840;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__segment--4 {
|
||||||
|
background: #4c2c92;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__segment--5 {
|
||||||
|
background: #d4351c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__count {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.dashboard-card {
|
||||||
|
border: 1px solid #b1b4b6;
|
||||||
|
padding: 15px;
|
||||||
|
min-height: 130px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-card__title {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-card__value {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-card__caption {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simple bar chart */
|
||||||
|
|
||||||
|
.appeal-count-chart,
|
||||||
|
.appeal-stage-chart {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-count-chart__row,
|
||||||
|
.appeal-stage-chart__row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(260px, 42%) 1fr;
|
||||||
|
gap: 15px;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-count-chart__label,
|
||||||
|
.appeal-stage-chart__label {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-count-chart__bar-wrapper,
|
||||||
|
.appeal-stage-chart__bar-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-count-chart__bar {
|
||||||
|
display: inline-block;
|
||||||
|
height: 18px;
|
||||||
|
background: #1d70b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-count-chart__count,
|
||||||
|
.appeal-stage-chart__count {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__bar {
|
||||||
|
display: flex;
|
||||||
|
height: 20px;
|
||||||
|
min-width: 2px;
|
||||||
|
background: #f3f2f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__segment {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__segment--0 {
|
||||||
|
background: #1d70b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__segment--1 {
|
||||||
|
background: #00703c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__segment--2 {
|
||||||
|
background: #f47738;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__segment--3 {
|
||||||
|
background: #b58840;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__segment--4 {
|
||||||
|
background: #4c2c92;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-stage-chart__segment--5 {
|
||||||
|
background: #d4351c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accordion heading total */
|
||||||
|
|
||||||
|
.appeal-accordion-heading {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-accordion-total {
|
||||||
|
font-weight: normal;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.govuk-accordion__show-all {
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
color: #1d70b8;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-accordion-heading,
|
||||||
|
.lpa-status-block__button {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.appeal-accordion-total {
|
||||||
|
font-weight: normal;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lpa-status-block {
|
||||||
|
border-bottom: 1px solid #b1b4b6;
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lpa-status-block__button {
|
||||||
|
background: none;
|
||||||
|
border: 0;
|
||||||
|
color: #1d70b8;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 0;
|
||||||
|
text-align: left;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lpa-status-block__button span:last-child {
|
||||||
|
color: #0b0c0c;
|
||||||
|
font-weight: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user