diff --git a/actions/services/adminDirectService.js b/actions/services/adminDirectService.js index 93deb2ef..45efdf03 100644 --- a/actions/services/adminDirectService.js +++ b/actions/services/adminDirectService.js @@ -65,3 +65,23 @@ export const getNewDocumentsPaged = async ( 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); + } +}; diff --git a/actions/services/adminService.js b/actions/services/adminService.js index 1cf0c6c0..6a76ff40 100644 --- a/actions/services/adminService.js +++ b/actions/services/adminService.js @@ -1,7 +1,15 @@ import { getNewAppeals, getNewAppealsPage, - getNewDocumentsPaged + getNewDocumentsPaged, + getStatusByAppeal, + getStatusByAppealLPA } from "./adminDirectService"; -export { getNewAppeals, getNewAppealsPage, getNewDocumentsPaged }; +export { + getNewAppeals, + getNewAppealsPage, + getNewDocumentsPaged, + getStatusByAppeal, + getStatusByAppealLPA +}; diff --git a/components/admin/tabs/appealsDashboard.js b/components/admin/tabs/appealsDashboard.js new file mode 100644 index 00000000..e3d70591 --- /dev/null +++ b/components/admin/tabs/appealsDashboard.js @@ -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 ( +
+
+

Appeal case status dashboard

+ +
+ + + + +
+ +
+

+ Case counts and stage breakdown by appeal type +

+ +

+ Select an appeal type row to show the case stage + breakdown. +

+ +
+ +
+ +
+ {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 ( +
+
+

+ +

+
+ + +
+ ); + })} +
+
+
+
+ ); +} + +function DashboardCard({ title, value, caption }) { + return ( +
+
+

{title}

+

{value}

+ {caption && ( +

+ {caption} +

+ )} +
+
+ ); +} diff --git a/components/admin/tabs/appealsDashboardLPA.js b/components/admin/tabs/appealsDashboardLPA.js new file mode 100644 index 00000000..edda11bd --- /dev/null +++ b/components/admin/tabs/appealsDashboardLPA.js @@ -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 ( +
+
+

LPA Appeal case status dashboard

+ +
+ + + + +
+
+ +
+

+ Appeal types and case stages by LPA +

+
+ +
+
+ {groupedLpas.map((lpa) => { + const lpaOpen = !!openLpaSections[lpa.lpaId]; + + const width = + ((lpa.caseCount || 0) / maxAppealCount) * 100; + + return ( +
+
+

+ +

+
+ + +
+ ); + })} +
+
+ +
+

+ Case stage by LPA and Appeal Type +

+

+ Select an appeal type row to show the LPA and then case + stage breakdown. +

+ +
+ +
+
+ {groupedAppeals.map((appeal) => { + const appealOpen = !!openAppeals[appeal.appealType]; + + const width = + ((appeal.caseCount || 0) / maxGroupedAppealCount) * + 100; + + return ( +
+
+

+ +

+
+ + +
+ ); + })} +
+
+
+ ); +} + +function DashboardCard({ title, value, caption }) { + return ( +
+
+

{title}

+

{value}

+ {caption && ( +

+ {caption} +

+ )} +
+
+ ); +} diff --git a/components/admin/tabs/appealsWithStatus.js b/components/admin/tabs/appealsWithStatus.js new file mode 100644 index 00000000..3219728e --- /dev/null +++ b/components/admin/tabs/appealsWithStatus.js @@ -0,0 +1,294 @@ +// // components/admin/tabs/AccountsTab.js +// import Link from "next/link"; + +// export default function AppealsWithStatusTab({ statusObj }) { +// return ( +//
+//

User Accounts

+ +//
+//
+//
+// Name +//
+//
+// Last accessed +//
+//
+ +// {statusObj.map((appeal) => ( +//
+//
+// +// {appeal.value} +// +//
+ +//
+//
+//
+// Name +//
+//
+// Last accessed +//
+//
+ +// {appeal.statuses.map((statuses) => ( +//
+//
+// {statuses.value} +//
+//
+// Total: {statuses.caseCount} +//
+//
+// ))} +//
+//
+//
+// Total: {appeal.caseCount} +//
+//
+// ))} +//
+//
+// ); +// } + +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 ( +
+

Case status by appeal type

+ +
+

Case counts by appeal type

+ +
+ {sortedAppeals.map((appeal) => { + const maxCount = sortedAppeals[0]?.caseCount || 1; + const width = + ((appeal.caseCount || 0) / maxCount) * 100; + + return ( +
+
+ {appeal.value} +
+ +
+ + + {appeal.caseCount || 0} + +
+
+ ); + })} +
+
+ +
+

Case stages by appeal type

+ +
+ {sortedAppeals.map((appeal) => ( +
+
+ {appeal.value} +
+ +
+
+ {(appeal.statuses || []) + .filter( + (status) => status.caseCount > 0 + ) + .sort( + (a, b) => b.caseCount - a.caseCount + ) + .map((status, index) => ( + + ))} +
+ + + {appeal.caseCount || 0} + +
+
+ ))} +
+
+ +
+ +
+ +
+ {[...statusObj] + .sort((a, b) => (b.caseCount || 0) - (a.caseCount || 0)) + .map((appeal, index) => { + const isOpen = !!openSections[index]; + + return ( +
+
+

+ +

+
+ + +
+ ); + })} +
+
+ ); +} diff --git a/components/admin/tabs/documents.js b/components/admin/tabs/documents.js index aa573d4c..007bec94 100644 --- a/components/admin/tabs/documents.js +++ b/components/admin/tabs/documents.js @@ -1104,9 +1104,7 @@ const DocumentsTab = (props) => { return (
-

- {t("case:documents-heading-label")} -

+

{t("case:documents-heading-label")}

{/*
MDU :{" "} {countFieldValue( diff --git a/components/utils/caseStagesObj.js b/components/utils/caseStagesObj.js index 42fe1ca6..e875eb81 100644 --- a/components/utils/caseStagesObj.js +++ b/components/utils/caseStagesObj.js @@ -283,5 +283,15 @@ export const statusValues = [ "value": "Case Submitted", "value_cy": "Achos wedi’i gyflwyno", "statuscode": 846040061 + }, + { + "value": "Problem Solved", + "value_cy": "Problem wedi'i Datrys", + "statuscode": 5 + }, + { + "value": "Information Provided", + "value_cy": "Gwybodaeth a Ddarperir", + "statuscode": 1000 } ]; diff --git a/pages/admin/index.js b/pages/admin/index.js index 90896292..4f06b04b 100644 --- a/pages/admin/index.js +++ b/pages/admin/index.js @@ -11,9 +11,16 @@ import StorageTab from "../../components/admin/tabs/storage"; import AccountsTab from "../../components/admin/tabs/accounts"; import AppealsTab from "../../components/admin/tabs/appeals"; 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 { getNewAppeals } from "../../actions/services/adminService"; +import { + getNewAppeals, + getStatusByAppeal, + getStatusByAppealLPA +} from "../../actions/services/adminService"; import { getSearchDetails } from "../../components/utils"; @@ -30,9 +37,11 @@ const StoragePage = (props) => { repCompleteCount, searchResultsObj, docsOffline, - showFilteredDocs + showFilteredDocs, + statusObj, + statusLPAObj } = props; - const [whichTab, setWhichTab] = useState("documents"); + const [whichTab, setWhichTab] = useState("appealDashboard"); return (
@@ -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