@@ -54,6 +63,8 @@ const StoragePage = (props) => {
{/* Tabs */}
{[
+ "appealDashboard",
+ "appealDashboardLPA",
"documents",
"appeals",
"storage",
@@ -85,6 +96,10 @@ const StoragePage = (props) => {
"Storage Account"}
{tab === "accounts" &&
"User Accounts"}
+ {tab === "appealDashboard" &&
+ "Appeals Dashboard"}
+ {tab === "appealDashboardLPA" &&
+ "Appeals LPA Dashboard"}
))}
@@ -92,6 +107,26 @@ const StoragePage = (props) => {
{/* Panels */}
+
+
{
searchResultsObj={searchResultsObj}
/>
+
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"
+ }
+ });
+}
diff --git a/pages/api/admin/getStatusCountsByAppealAndLPA_api.js b/pages/api/admin/getStatusCountsByAppealAndLPA_api.js
new file mode 100644
index 00000000..97a36855
--- /dev/null
+++ b/pages/api/admin/getStatusCountsByAppealAndLPA_api.js
@@ -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"
+ }
+ });
+}
diff --git a/pages/api/admin/getStatusCountsByAppeal_api.js b/pages/api/admin/getStatusCountsByAppeal_api.js
new file mode 100644
index 00000000..12eb4a98
--- /dev/null
+++ b/pages/api/admin/getStatusCountsByAppeal_api.js
@@ -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"
+ }
+ });
+}
diff --git a/styles/sass/welshgov/_application.scss b/styles/sass/welshgov/_application.scss
index dd4cca2a..88d54f22 100644
--- a/styles/sass/welshgov/_application.scss
+++ b/styles/sass/welshgov/_application.scss
@@ -3288,4 +3288,259 @@ ul.subFieldList {
.govuk-task-list__name-and-hint {
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;
}
\ No newline at end of file