From 9b0f9acff9c8b9519220d1cc576a0548825ca590 Mon Sep 17 00:00:00 2001
From: robbond
Date: Fri, 9 Jan 2026 13:37:40 +0000
Subject: [PATCH] added searching by LPA application ref and criteria
highlighting
---
components/search/addresssearchresults.js | 135 ++++--
components/search/advancedsearch.js | 76 ++++
components/search/searchresults.js | 107 ++++-
locales/cy/home.json | 2 +-
locales/cy/myportal.json | 2 +-
locales/cy/search.json | 1 +
locales/en/home.json | 2 +-
locales/en/myportal.json | 2 +-
locales/en/search.json | 1 +
pages/api/endpoint/getadvancedsearch_api.js | 7 +
.../endpoint/getadvancedsearchpaged_api.js | 7 +
pages/api/endpoint/getbasicsearch_api.js | 2 +
.../endpoint/getbasicsearch_by_address_api.js | 1 +
.../endpoint/getbasicsearch_by_lparref_api.js | 388 ++++++++++++++++++
pages/api/endpoint/getbasicsearchpaged_api.js | 2 +
styles/sass/welshgov/_application.scss | 28 ++
16 files changed, 715 insertions(+), 48 deletions(-)
create mode 100644 pages/api/endpoint/getbasicsearch_by_lparref_api.js
diff --git a/components/search/addresssearchresults.js b/components/search/addresssearchresults.js
index 3e25a6ab..ce63b4d9 100644
--- a/components/search/addresssearchresults.js
+++ b/components/search/addresssearchresults.js
@@ -31,6 +31,32 @@ import {
} from "../../actions";
import RepsOnResults from "./repsonresults";
+const escapeRegExp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+
+const highlightText = (text, query) => {
+ if (!text || !query) return text;
+
+ const terms = Array.isArray(query)
+ ? query
+ : String(query).trim().split(/\s+/).filter(Boolean);
+
+ if (!terms.length) return text;
+
+ const regex = new RegExp(`(${terms.map(escapeRegExp).join("|")})`, "gi");
+
+ return String(text)
+ .split(regex)
+ .map((part, i) =>
+ regex.test(part) ? (
+
+ {part}
+
+ ) : (
+ part
+ )
+ );
+};
+
const SearchResults = (props) => {
const {
searchString,
@@ -55,9 +81,17 @@ const SearchResults = (props) => {
let [resultsArr, setResultsArr] = useState(searchResultsObj.value || []);
+ const hasCount =
+ searchResultsObj &&
+ typeof searchResultsObj["@odata.count"] === "number";
+
const [currentPage, setCurrentPage] = useState(1);
const [searchLoaded, setSearchLoaded] = useState(false);
- const [showSpinnerState, setShowSpinnerState] = useState(false);
+
+ const [showSpinnerState, setShowSpinnerState] = useState(
+ !(searchResultsObj && searchResultsObj["@odata.count"] != null)
+ );
+
const [selectedOption, setSelectedOption] = useState(10);
const [orderByState, setOrderbyState] = useState("createdon");
const [fieldSortState, setfieldSortState] = useState("asc");
@@ -193,6 +227,10 @@ const SearchResults = (props) => {
const indexOfLastRecord = currentPage * selectedOption;
const indexOfFirstRecord = indexOfLastRecord - selectedOption;
+ const highlightQuery = advancedSearch
+ ? searchString.map(([key, val]) => val).filter(Boolean)
+ : searchString;
+
const ResultsView = (resultsArr) => {
var resultsArrPaged = resultsArr.slice(
indexOfFirstRecord,
@@ -378,32 +416,42 @@ const SearchResults = (props) => {
detailsObj,
"pinswg_projectlocation"
)
- ? detailsObj.pinswg_projectlocation !=
- null
- ? detailsObj.pinswg_projectlocation.indexOf(
- ";"
- ) < 0
- ? detailsObj.pinswg_projectlocation
- : router.locale == "cy"
- ? detailsObj.pinswg_projectlocation.split(
- ";"
- )[1]
- : detailsObj.pinswg_projectlocation.split(
- ";"
- )[0]
- : ""
+ ? highlightText(
+ detailsObj.pinswg_projectlocation !=
+ null
+ ? detailsObj.pinswg_projectlocation.indexOf(
+ ";"
+ ) < 0
+ ? detailsObj.pinswg_projectlocation
+ : router.locale ==
+ "cy"
+ ? detailsObj.pinswg_projectlocation.split(
+ ";"
+ )[1]
+ : detailsObj.pinswg_projectlocation.split(
+ ";"
+ )[0]
+ : "",
+ highlightQuery
+ )
: ""}
{_.has(
detailsObj,
"pinswg_siteaddressline1"
)
- ? detailsObj.pinswg_siteaddressline1
+ ? highlightText(
+ detailsObj.pinswg_siteaddressline1,
+ highlightQuery
+ )
: ""}
{_.has(
detailsObj,
"pinswg_addressline1"
)
- ? detailsObj.pinswg_addressline1
+ ? highlightText(
+ detailsObj.pinswg_addressline1,
+ highlightQuery
+ )
: ""}
{/* {detailsObj.pinswg_siteaddressline1 !=
null &&
} */}
@@ -423,13 +471,19 @@ const SearchResults = (props) => {
detailsObj,
"pinswg_siteaddressline1"
)
- ? detailsObj.pinswg_siteaddressline2
+ ? highlightText(
+ detailsObj.pinswg_siteaddressline2,
+ highlightQuery
+ )
: ""}
{_.has(
detailsObj,
"pinswg_addressline1"
)
- ? detailsObj.pinswg_addressline2
+ ? highlightText(
+ detailsObj.pinswg_addressline2,
+ highlightQuery
+ )
: ""}
{_.has(
detailsObj,
@@ -447,10 +501,16 @@ const SearchResults = (props) => {
detailsObj,
"pinswg_siteaddresstown"
)
- ? detailsObj.pinswg_siteaddresstown
+ ? highlightText(
+ detailsObj.pinswg_siteaddresstown,
+ highlightQuery
+ )
: ""}
{_.has(detailsObj, "pinswg_addresstown")
- ? detailsObj.pinswg_addresstown
+ ? highlightText(
+ detailsObj.pinswg_addresstown,
+ highlightQuery
+ )
: ""}
{_.has(
detailsObj,
@@ -468,13 +528,19 @@ const SearchResults = (props) => {
detailsObj,
"pinswg_siteaddresscounty"
)
- ? detailsObj.pinswg_siteaddresscounty
+ ? highlightText(
+ detailsObj.pinswg_siteaddresscounty,
+ highlightQuery
+ )
: ""}
{_.has(
detailsObj,
"pinswg_addresscounty"
)
- ? detailsObj.pinswg_addresscounty
+ ? highlightText(
+ detailsObj.pinswg_addresscounty,
+ highlightQuery
+ )
: ""}
{_.has(
detailsObj,
@@ -492,10 +558,16 @@ const SearchResults = (props) => {
detailsObj,
"pinswg_siteaddresspostcode"
)
- ? detailsObj.pinswg_siteaddresspostcode
+ ? highlightText(
+ detailsObj.pinswg_siteaddresspostcode,
+ highlightQuery
+ )
: ""}
{_.has(detailsObj, "pinswg_postcode")
- ? detailsObj.pinswg_postcode
+ ? highlightText(
+ detailsObj.pinswg_postcode,
+ highlightQuery
+ )
: ""}
@@ -556,6 +628,19 @@ const SearchResults = (props) => {
"_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue"
] || "N/A"
: ""}
+ {item?.pinswg_lpareference && (
+ <>
+
+
+ Ref:
+ {" "}
+ {highlightText(
+ item.pinswg_lpareference,
+ highlightQuery
+ )}
+
+ >
+ )}
diff --git a/components/search/advancedsearch.js b/components/search/advancedsearch.js
index 9c6d62ef..2222e3ed 100644
--- a/components/search/advancedsearch.js
+++ b/components/search/advancedsearch.js
@@ -64,6 +64,54 @@ const RenderTextfield = ({
>
);
};
+const RenderLPATextfield = ({
+ id,
+ className,
+ rows,
+ datafieldname,
+ name,
+ label,
+ input,
+ hint1,
+ hint2,
+ hint3,
+ meta: { touched, error },
+ ...custom
+}) => {
+ let { t } = useTranslation();
+
+ return (
+ <>
+
+
+
+
+
+
+
+ {touched && error && (
+
+
+ Error:
+ {" "}
+ {error}
+
+ )}
+
+
+ >
+ );
+};
const RenderCaseStatusList = ({
name,
@@ -489,6 +537,9 @@ let AdvancedSearch = (props) => {
searchString +=
values.caseRef != null ? "q=" + values.caseRef.trim() + "&" : "";
+ searchString +=
+ values.lpaRef != null ? "lpaRef=" + values.lpaRef + "&" : "";
+
searchString +=
values.lpaTypes != null ? "lpa=" + values.lpaTypes + "&" : "";
@@ -562,6 +613,31 @@ let AdvancedSearch = (props) => {
)}
/>
+
+
string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+
+const highlightText = (text, query) => {
+ if (!text || !query) return text;
+
+ const terms = Array.isArray(query)
+ ? query
+ : String(query).trim().split(/\s+/).filter(Boolean);
+
+ if (!terms.length) return text;
+
+ const regex = new RegExp(`(${terms.map(escapeRegExp).join("|")})`, "gi");
+
+ return String(text)
+ .split(regex)
+ .map((part, i) =>
+ regex.test(part) ? (
+
+ {part}
+
+ ) : (
+ part
+ )
+ );
+};
+
const SearchResults = (props) => {
const {
searchString,
@@ -62,8 +88,17 @@ const SearchResults = (props) => {
var resultsArr = searchResultsObj.value || [];
+ const hasCount =
+ searchResultsObj &&
+ typeof searchResultsObj["@odata.count"] === "number";
+
const [searchLoaded, setSearchLoaded] = useState(false);
- const [showSpinnerState, setShowSpinnerState] = useState(false);
+ // const [showSpinnerState, setShowSpinnerState] = useState(false);
+
+ const [showSpinnerState, setShowSpinnerState] = useState(
+ !(searchResultsObj && searchResultsObj["@odata.count"] != null)
+ );
+
const [selectedOption, setSelectedOption] = useState(10);
const [orderByState, setOrderbyState] = useState("createdon");
const [fieldSortState, setfieldSortState] = useState("desc");
@@ -220,6 +255,10 @@ const SearchResults = (props) => {
);
}
+ const highlightQuery = advancedSearch
+ ? searchString.map(([key, val]) => val).filter(Boolean)
+ : searchString;
+
const ResultsView = (resultsArr) => {
return (
<>
@@ -402,7 +441,10 @@ const SearchResults = (props) => {
)}
:
- {item.title}
+ {highlightText(
+ item.title,
+ highlightQuery
+ )}
{item.hasOwnProperty(
@@ -575,6 +617,19 @@ const SearchResults = (props) => {
"_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue"
] || "N/A"
: ""}
+ {item?.pinswg_lpareference && (
+ <>
+
+
+ Ref:
+ {" "}
+ {highlightText(
+ item.pinswg_lpareference,
+ highlightQuery
+ )}
+
+ >
+ )}
@@ -996,29 +1051,43 @@ const SearchResults = (props) => {
: t("search:searchresults-title-label")}
- {!isLinkedCase && (
-
- {resultsArr.length > 200
- ? t("search:searchresults-max-count-label")
- : t("search:searchresults-count-label", {
- count: searchResultsObj["@odata.count"],
- })}
- .
- {advancedSearch != true ? (
- <>
+ {!isLinkedCase &&
+ (hasCount ? (
+
+ {resultsArr.length > 200
+ ? t("search:searchresults-max-count-label")
+ : t("search:searchresults-count-label", {
+ count: searchResultsObj[
+ "@odata.count"
+ ],
+ })}
+ .
+ {advancedSearch != true ? (
+ {" "}
{t(
"search:searchresults-case-count-label"
)}{" "}
- {" "}
"{searchString}"
- >
- ) : (
- ""
- )}
-
- )}
+ ) : (
+ ""
+ )}
+
+ ) : (
+
+ {t("common:spinner-fetching-data")}
+
+
+ ))}
{searchResultsObj["@odata.count"] > 5 && (
diff --git a/locales/cy/home.json b/locales/cy/home.json
index 1b42ce43..616862ef 100644
--- a/locales/cy/home.json
+++ b/locales/cy/home.json
@@ -25,7 +25,7 @@
"login-via-gov-gateway-label": "Mewngofnodwch drwy Borth y Llywodraeth",
"login-via-nextuth-label": "Mewngofnodwch gyda'ch cyfeiriad e-bost",
"casesearch-card-title": "Chwilio am achos",
- "casesearch-card-search-hint": "Defnyddiwch gyfeirnod yr achos neu deitl y prosiect Infrastructure i chwilio. Bydd cyfeirnod yr achos yn debyg i un o'r canlynol:",
+ "casesearch-card-search-hint": "Defnyddiwch gyfeirnod yr achos, y prosiect Seilwaith neu gyfeirnod cais yr Awdurdod Cynllunio Lleol i chwilio. Bydd cyfeirnod yr achos yn debyg i un o'r canlynol:",
"casesearch-card-search-example-label-1": "APP-A12345-A-21-1234567",
"casesearch-card-search-example-label-2": "CAS-12345-A1B2C3",
"casesearch-card-button": "Cyflwyno chwiliad",
diff --git a/locales/cy/myportal.json b/locales/cy/myportal.json
index 1d447e4b..e3b966d6 100644
--- a/locales/cy/myportal.json
+++ b/locales/cy/myportal.json
@@ -16,7 +16,7 @@
"makenewappeal-card-button-label": "Gwneud apêl newydd",
"mycases-card-title": "Fy achosion",
"searchcases-card-title": "Chwilio am achos",
- "searchcases-card-search-hint": "Defnyddiwch gyfeirnod yr achos neu deitl y prosiect Infrastructure i chwilio. Bydd cyfeirnod yr achos yn debyg i un o'r canlynol:",
+ "searchcases-card-search-hint": "Defnyddiwch gyfeirnod yr achos, y prosiect Seilwaith neu gyfeirnod cais yr Awdurdod Cynllunio Lleol i chwilio. Bydd cyfeirnod yr achos yn debyg i un o'r canlynol:",
"searchcases-card-search-example-label-1": "APP-A12345-A-21-1234567",
"searchcases-card-search-example-label-2": "CAS-12345-A1B2C3",
"searchcases-card-button": "Cyflwyno Chwiliad",
diff --git a/locales/cy/search.json b/locales/cy/search.json
index d2b13ce2..b7d1f27e 100644
--- a/locales/cy/search.json
+++ b/locales/cy/search.json
@@ -2,6 +2,7 @@
"page-title": "Chwilio am achos",
"search-title-label": "Chwilio am achos",
"casereference-label": "Cyfeirnod yr achos",
+ "lpareference-label": "Cyfeirnod yr ACLl",
"sitelocation-legend": "Lleoliad y safle",
"address-line-one-label": "Llinell gyntaf y cyfeiriad",
"town-label": "Tref neu ddinas (agosaf)",
diff --git a/locales/en/home.json b/locales/en/home.json
index 621b6e93..4f1dcf59 100644
--- a/locales/en/home.json
+++ b/locales/en/home.json
@@ -25,7 +25,7 @@
"login-via-gov-gateway-label": "Log in via Government Gateway",
"login-via-nextuth-label": "Sign in with your email address",
"casesearch-card-title": "Search for a case",
- "casesearch-card-search-hint": "Use the case reference or Infrastructure project title to search. Case reference will be similar to one of the following:",
+ "casesearch-card-search-hint": "Use the case reference, Infrastructure project or Local Planning Authority application reference to search. Case reference will be similar to one of the following:",
"casesearch-card-search-example-label-1": "APP-A12345-A-21-1234567",
"casesearch-card-search-example-label-2": "CAS-12345-A1B2C3",
"casesearch-card-button": "Submit search",
diff --git a/locales/en/myportal.json b/locales/en/myportal.json
index d2639346..098ca87b 100644
--- a/locales/en/myportal.json
+++ b/locales/en/myportal.json
@@ -16,7 +16,7 @@
"makenewappeal-card-button-label": "Make a new appeal",
"mycases-card-title": "My cases",
"searchcases-card-title": "Search for a case",
- "searchcases-card-search-hint": "Use the case reference or Infrastructure project title to search. Case reference will be similar to one of the following:",
+ "searchcases-card-search-hint": "Use the case reference, Infrastructure project or Local Planning Authority application reference to search. Case reference will be similar to one of the following:",
"searchcases-card-search-example-label-1": "APP-A12345-A-21-1234567",
"searchcases-card-search-example-label-2": "CAS-12345-A1B2C3",
"searchcases-card-button": "Submit Search",
diff --git a/locales/en/search.json b/locales/en/search.json
index 93d9fa3c..ced9e0b7 100644
--- a/locales/en/search.json
+++ b/locales/en/search.json
@@ -2,6 +2,7 @@
"page-title": "Search for a case",
"search-title-label": "Search for a case",
"casereference-label": "Case reference",
+ "lpareference-label": "LPA reference",
"sitelocation-legend": "Site location",
"address-line-one-label": "First line of address",
"town-label": "Town or city (nearest)",
diff --git a/pages/api/endpoint/getadvancedsearch_api.js b/pages/api/endpoint/getadvancedsearch_api.js
index d483e408..4d855f53 100644
--- a/pages/api/endpoint/getadvancedsearch_api.js
+++ b/pages/api/endpoint/getadvancedsearch_api.js
@@ -63,6 +63,13 @@ export default async function ApiProxy(req, res) {
"')) and"
: "";
+ queryString +=
+ _.has(searchString, "lpaRef") && searchString.lpaRef != null
+ ? "(contains(pinswg_lpareference, '" +
+ searchString.lpaRef.replace(/\'/g, "''") +
+ "')) and"
+ : "";
+
queryString +=
(_.has(searchString, "lpa") || _.has(searchString, "LPA")) &&
searchString.lpa != null
diff --git a/pages/api/endpoint/getadvancedsearchpaged_api.js b/pages/api/endpoint/getadvancedsearchpaged_api.js
index 9db7bcb3..48b72d9d 100644
--- a/pages/api/endpoint/getadvancedsearchpaged_api.js
+++ b/pages/api/endpoint/getadvancedsearchpaged_api.js
@@ -100,6 +100,13 @@ export default async function ApiProxy(req, res) {
"')) and"
: "";
+ queryString +=
+ _.has(searchString, "lpaRef") && searchString.lpaRef != null
+ ? "(contains(pinswg_lpareference, '" +
+ searchString.lpaRef.replace(/\'/g, "''") +
+ "')) and"
+ : "";
+
queryString +=
(_.has(searchString, "lpa") || _.has(searchString, "LPA")) &&
searchString.lpa != null
diff --git a/pages/api/endpoint/getbasicsearch_api.js b/pages/api/endpoint/getbasicsearch_api.js
index 763f0447..306098f3 100644
--- a/pages/api/endpoint/getbasicsearch_api.js
+++ b/pages/api/endpoint/getbasicsearch_api.js
@@ -49,6 +49,8 @@ export default async function ApiProxy(req, res) {
searchString +
"') or contains(ticketnumber, '" +
searchString +
+ "') or contains(pinswg_lpareference, '" +
+ searchString +
"')) and pinswg_appealcasetype ne null " +
(process.env.SHOWSIPS !== "true"
? "and pinswg_appealcasetype ne 846040002 "
diff --git a/pages/api/endpoint/getbasicsearch_by_address_api.js b/pages/api/endpoint/getbasicsearch_by_address_api.js
index eae961db..e4126d98 100644
--- a/pages/api/endpoint/getbasicsearch_by_address_api.js
+++ b/pages/api/endpoint/getbasicsearch_by_address_api.js
@@ -661,6 +661,7 @@ export default async function ApiProxy(req, res) {
Object.assign(key, {
"pinswg_publishtoweb": incidentObj.pinswg_publishtoweb,
"ticketnumber": incidentObj.ticketnumber,
+ "pinswg_lpareference": incidentObj.pinswg_lpareference,
});
return incidentObj;
diff --git a/pages/api/endpoint/getbasicsearch_by_lparref_api.js b/pages/api/endpoint/getbasicsearch_by_lparref_api.js
new file mode 100644
index 00000000..8d465f34
--- /dev/null
+++ b/pages/api/endpoint/getbasicsearch_by_lparref_api.js
@@ -0,0 +1,388 @@
+import axios from "axios";
+import CryptoJS from "crypto-js";
+import { azureHeaders, consoleLogger, getToken } from "../../../actions";
+
+const WORDKEY = process.env.HASHKEY;
+
+const WEBAPI_URL =
+ process.env.RELAY_ROOT ||
+ "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
+
+const hashAPIPath = (queryPath) => {
+ let hashlink = CryptoJS.HmacSHA256(
+ queryPath,
+ CryptoJS.enc.Hex.parse(WORDKEY)
+ );
+ hashlink = hashlink.toString(CryptoJS.enc.Hex);
+ return (queryPath.indexOf("?") > -1 ? "&hash=" : "?hash=") + hashlink;
+};
+
+// ✅ IMPORTANT: Only include the 23 types that actually contain pinswg_lpaapplicationreference
+// You can copy from your existing appealTypeArray but only keep those entities.
+
+var appealTypeArrayWithLpaRef = [
+ {
+ "LogicalCollectionName": "pinswg_advertses",
+ "LogicalName": "pinswg_adverts",
+ "PrimaryIdAttribute": "pinswg_advertsid",
+ "UrlName": "adverts",
+ "MetadataId": "0735866b-5482-eb11-aac0-00224800be9c",
+ "appealTypeID": "846040018",
+ "NavigationProperty": "pinswg_AdvertsIds",
+ "value": "Adverts",
+ },
+ {
+ "LogicalCollectionName": "pinswg_callinss77s",
+ "LogicalName": "pinswg_callinss77",
+ "PrimaryIdAttribute": "pinswg_callinss77id",
+ "UrlName": "callinss77",
+ "MetadataId": "dd309e51-4982-eb11-aac0-00224800be9c",
+ "appealTypeID": "846040010",
+ "NavigationProperty": "pinswg_CallInsS77Ids",
+ "value": "Called In Applications - S77",
+ },
+ // {
+ // "LogicalCollectionName": "pinswg_commonlands",
+ // "LogicalName": "pinswg_commonland",
+ // "PrimaryIdAttribute": "pinswg_commonlandid",
+ // "UrlName": "commonland",
+ // "MetadataId": "4161a618-5082-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040016",
+ // "NavigationProperty": "pinswg_CommonLandIds",
+ // "value": "Common Land",
+ // },
+ // {
+ // "LogicalCollectionName":
+ // "pinswg_communityinfrastructurelevys117118119s",
+ // "LogicalName": "pinswg_communityinfrastructurelevys117118119",
+ // "PrimaryIdAttribute": "pinswg_communityinfrastructurelevyid",
+ // "UrlName": "communityinfrastructurelevys117118119",
+ // "MetadataId": "0c309f3c-4882-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040009",
+ // "NavigationProperty": "pinswg_CommunityInfrastructureLevyIds",
+ // "value": "Community Infrastructure Levy - S117, 118, & 119",
+ // },
+ // {
+ // "LogicalCollectionName": "pinswg_compulsorypurchaseorderses",
+ // "LogicalName": "pinswg_compulsorypurchaseorders",
+ // "PrimaryIdAttribute": "pinswg_compulsorypurchaseordersid",
+ // "UrlName": "compulsorypurchaseorders",
+ // "MetadataId": "054da4c7-5582-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040019",
+ // "NavigationProperty": "pinswg_CompulsoryPurchaseOrdersIds",
+ // "value": "Compulsory Purchase Orders",
+ // },
+ // {
+ // "LogicalCollectionName": "pinswg_dnses",
+ // "LogicalName": "pinswg_dns",
+ // "PrimaryIdAttribute": "pinswg_dnsid",
+ // "UrlName": "dns",
+ // "MetadataId": "36f07225-4a82-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040011",
+ // "NavigationProperty": "pinswg_DNSIds",
+ // "value": "Developments of National Significance",
+ // },
+ // {
+ // "LogicalCollectionName": "pinswg_electricityacts",
+ // "LogicalName": "pinswg_electricityact",
+ // "PrimaryIdAttribute": "pinswg_electricityactid",
+ // "UrlName": "electricityact",
+ // "MetadataId": "6aedaab1-4a82-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040012",
+ // "NavigationProperty": "pinswg_ElectricityActIds",
+ // "value": "Electricity Act",
+ // },
+ // {
+ // "LogicalCollectionName":
+ // "pinswg_enforcementlistedbuildingconservationaps",
+ // "LogicalName":
+ // "pinswg_enforcementlistedbuildingandconservationappeals39",
+ // "PrimaryIdAttribute": "pinswg_enforcementlistedbuildingconseid",
+ // "UrlName": "enforcementlistedbuildingconservationap",
+ // "MetadataId": "c697be96-9481-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040006",
+ // "NavigationProperty": "pinswg_EnforcementListedBuildingConseIds",
+ // "value": "Enforcement Listed Building and Conservation Appeal - S39",
+ // },
+ // {
+ // "LogicalCollectionName": "pinswg_enforcementnoticeappeals174s",
+ // "LogicalName": "pinswg_enforcementnoticeappeals174",
+ // "PrimaryIdAttribute": "pinswg_enforcementnoticeappeals174id",
+ // "UrlName": "enforcementnoticeappeals174",
+ // "MetadataId": "930a3cc4-9181-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040005",
+ // "NavigationProperty": "pinswg_EnforcementNoticeAppealS174Ids",
+ // "value": "Enforcement Notice Appeal - S174",
+ // },
+ // {
+ // "LogicalCollectionName": "pinswg_environmentalpermittings",
+ // "LogicalName": "pinswg_environmentalpermitting",
+ // "PrimaryIdAttribute": "pinswg_enforcementpermittingid",
+ // "UrlName": "environmentalpermitting",
+ // "MetadataId": "62d4aa9a-5682-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040020",
+ // "NavigationProperty": "pinswg_EnforcementPermittingIds",
+ // "value": "Environmental Permitting",
+ // },
+ // {
+ // "LogicalCollectionName": "pinswg_harbourrevisionorders",
+ // "LogicalName": "pinswg_harbourrevisionorder",
+ // "PrimaryIdAttribute": "pinswg_harbourrevisionorderid",
+ // "UrlName": "harbourrevisionorder",
+ // "MetadataId": "dc8dcd0d-4f82-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040014",
+ // "NavigationProperty": "pinswg_HarbourRevisionOrderIds",
+ // "value": "Harbour Revision Order",
+ // },
+ // {
+ // "LogicalCollectionName":
+ // "pinswg_hedgeshedgerowstreepreservationreplacems",
+ // "LogicalName": "pinswg_hedgeshedgerowstreepreservationreplacem",
+ // "PrimaryIdAttribute": "pinswg_hedgeshedgerowstreepreservatioid",
+ // "UrlName": "hedgeshedgerowstreepreservationreplacem",
+ // "MetadataId": "60f38fed-5382-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040017",
+ // "NavigationProperty": "pinswg_HedgesHedgerowsTreePreservatioIds",
+ // "value":
+ // "High Hedges, Tree Preservation orders, Hedgerows, Tree Replacement Notice.",
+ // },
+ {
+ "LogicalCollectionName": "pinswg_householderappealhases",
+ "LogicalName": "pinswg_householderappealhas",
+ "PrimaryIdAttribute": "pinswg_householderappealhasid",
+ "UrlName": "householderappealhas",
+ "MetadataId": "a6c728b0-9081-eb11-aac0-00224800be9c",
+ "appealTypeID": "846040004",
+ "NavigationProperty": "pinswg_HouseholderAppealHASIds",
+ "value": "Householder and Minor Commercial Appeals - HAS/CAS",
+ },
+ {
+ "LogicalCollectionName": "pinswg_lawfuldevelopmentcertificates",
+ "LogicalName": "pinswg_lawfuldevelopmentcertificateappeals194195",
+ "PrimaryIdAttribute": "pinswg_lawfuldevelopmentcertificatappid",
+ "UrlName": "lawfuldevelopmentcertificateappeals1941",
+ "MetadataId": "184e4861-9681-eb11-aac0-00224800be9c",
+ "appealTypeID": "846040008",
+ "NavigationProperty": "pinswg_LawfulDevelopmentCertificatAppIds",
+ "value": "Lawful Development Certificate Appeal - S194 + 195",
+ },
+ // {
+ // "LogicalCollectionName": "pinswg_ldps",
+ // "LogicalName": "pinswg_ldp",
+ // "PrimaryIdAttribute": "pinswg_ldpid",
+ // "UrlName": "ldp",
+ // "MetadataId": "af424d4d-5882-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040023",
+ // "NavigationProperty": "pinswg_LDPIds",
+ // },
+ // {
+ // "LogicalCollectionName":
+ // "pinswg_enforcementlistedbuildingconservationaps",
+ // "LogicalName": "pinswg_enforcementlistedbuildingandconservationap",
+ // "PrimaryIdAttribute": "pinswg_enforcementlistedbuildingconseid",
+ // "UrlName": "enforcementlistedbuildingconservationap",
+ // "MetadataId": "c697be96-9481-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040006",
+ // "NavigationProperty": "pinswg_EnforcementListedBuildingConseIds",
+ // },
+ // {
+ // "LogicalCollectionName": "pinswg_maintenanceoflands217s",
+ // "LogicalName": "pinswg_maintenanceoflands217",
+ // "PrimaryIdAttribute": "pinswg_maintenanceoflands217id",
+ // "UrlName": "maintenanceoflands217",
+ // "MetadataId": "bb302784-9581-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040007",
+ // "NavigationProperty": "pinswg_MaintenanceofLandS217Ids",
+ // "value": "Maintenance of Land - S217",
+ // },
+ // {
+ // "LogicalCollectionName": "pinswg_miscellaneouscaseworks",
+ // "LogicalName": "pinswg_misccasework",
+ // "PrimaryIdAttribute": "pinswg_miscellaneouscasewordid",
+ // "UrlName": "miscellaneouscasework",
+ // "MetadataId": "f519aa8e-5782-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040022",
+ // "NavigationProperty": "pinswg_MiscellaneousCasewordIds",
+ // },
+ // {
+ // "LogicalCollectionName": "pinswg_nonvalidations",
+ // "LogicalName": "pinswg_nonvalidation",
+ // "PrimaryIdAttribute": "pinswg_nonvalidationid",
+ // "UrlName": "nonvalidation",
+ // "MetadataId": "37f31a93-5882-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040024",
+ // "NavigationProperty": "pinswg_NonValidationIds",
+ // "value": "Non-Validation",
+ // },
+ {
+ "LogicalCollectionName": "pinswg_planningappeals78s",
+ "LogicalName": "pinswg_planningappeals78",
+ "PrimaryIdAttribute": "pinswg_planningappeals78id",
+ "UrlName": "planningappeals78",
+ "MetadataId": "8d7f813a-4183-eb11-aac0-00224800be9c",
+ "appealTypeID": "846040000",
+ "NavigationProperty": "pinswg_PlanningAppealS78Ids",
+ "value": "Planning Appeal - S78",
+ },
+ {
+ "LogicalCollectionName": "pinswg_planningconditionss73s79s",
+ "LogicalName": "pinswg_planningconditionss73s79",
+ "PrimaryIdAttribute": "pinswg_planningconditionss73s79id",
+ "UrlName": "planningconditionss73s79",
+ "MetadataId": "093bf9ff-8d81-eb11-aac0-00224800be9c",
+ "appealTypeID": "846040001",
+ "NavigationProperty": "pinswg_PlanningConditionsS73S79Ids",
+ "value": "Planning Conditions - S73 + S79",
+ },
+ // {
+ // "LogicalCollectionName": "pinswg_planningobligationappeals106s",
+ // "LogicalName": "pinswg_planningobligationappeals106",
+ // "PrimaryIdAttribute": "pinswg_planningobligationappeals106id",
+ // "UrlName": "planningobligationappeals106",
+ // "MetadataId": "cb8beeae-8f81-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040003",
+ // "NavigationProperty": "pinswg_PlanningObligationAppealS106Ids",
+ // "value": "Planning Obligation Appeal - S106",
+ // },
+ // {
+ // "LogicalCollectionName": "pinswg_sipses",
+ // "LogicalName": "pinswg_sips",
+ // "PrimaryIdAttribute": "pinswg_sipscase",
+ // "UrlName": "sips",
+ // "MetadataId": "8d31911b-b8ea-ef11-ab08-00224800be9c",
+ // "appealTypeID": "846040002",
+ // "tmpNav": "pinswg_sipsid",
+ // "NavigationProperty": "pinswg_sipscase",
+ // "value": "SIP",
+ // },
+ // {
+ // "LogicalCollectionName": "pinswg_rows",
+ // "LogicalName": "pinswg_row",
+ // "PrimaryIdAttribute": "pinswg_rowid",
+ // "UrlName": "row",
+ // "MetadataId": "4ccc6e93-4f82-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040015",
+ // "NavigationProperty": "pinswg_ROWIds",
+ // "value": "Rights of Way",
+ // },
+ // {
+ // "LogicalCollectionName": "pinswg_transportandworkacts",
+ // "LogicalName": "pinswg_transportandworksact",
+ // "PrimaryIdAttribute": "pinswg_transportandworkactid",
+ // "UrlName": "transportandworksact",
+ // "MetadataId": "bc3edc4b-7d82-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040013",
+ // "NavigationProperty": "pinswg_TransportandWorkActIds",
+ // "value": "Transport and Works Act",
+ // },
+ // {
+ // "LogicalCollectionName": "pinswg_wayleaves",
+ // "LogicalName": "pinswg_wayleave",
+ // "PrimaryIdAttribute": "pinswg_wayleaveid",
+ // "UrlName": "wayleave",
+ // "MetadataId": "a0652117-5782-eb11-aac0-00224800be9c",
+ // "appealTypeID": "846040021",
+ // "NavigationProperty": "pinswg_WayleaveIds",
+ // "value": "Wayleave",
+ // },
+];
+
+export default async function ApiProxy(req, res) {
+ try {
+ const token = await getToken();
+ let lpaRef = (req.query.lpaRef || "").trim();
+
+ if (!lpaRef) {
+ return res
+ .status(400)
+ .json({ error: "Missing required query param: lpaRef" });
+ }
+
+ // ✅ Escape single quotes for OData
+ // OData expects '' inside string literals
+ lpaRef = lpaRef.replace(/\'/g, "''");
+
+ // ✅ Case-insensitive search:
+ // contains(tolower(pinswg_lpaapplicationreference), 'lowered search')
+ const lowered = lpaRef.toLowerCase();
+
+ console.log(
+ `\n=== LPA REF SEARCH ===\nSearching for "${lpaRef}" (case-insensitive contains)\nAcross ${appealTypeArrayWithLpaRef.length} entity types\n======================\n`
+ );
+
+ const resultsPerType = await Promise.all(
+ appealTypeArrayWithLpaRef.map(async (item) => {
+ // You can expand/select more fields if needed, but keep light for speed
+ const selectFields = [
+ "modifiedon",
+ "createdon",
+ "statuscode",
+ "pinswg_lpaapplicationreference",
+ "pinswg_name",
+ "_" + item.NavigationProperty.toLowerCase() + "_value", // for incidentid mapping (same pattern you used)
+ ].join(",");
+
+ const filter = `contains(pinswg_lpaapplicationreference,'${lowered}')`;
+
+ const queryUrl =
+ `${item.LogicalCollectionName}` +
+ `?$select=${selectFields}` +
+ `&$filter=${filter}` +
+ `&$count=true` +
+ `&$orderby=modifiedon desc`;
+
+ console.log(
+ "\n------------------------------------------\n",
+ "Entity:",
+ item.LogicalCollectionName,
+ "\nQuery:",
+ queryUrl,
+ "\nFull:",
+ WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
+ "\n------------------------------------------\n"
+ );
+
+ const { data } = await axios.get(
+ WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
+ azureHeaders(token.access_token)
+ );
+
+ // Normalize response rows
+ const normalized = (data?.value || []).map((r) => ({
+ ...r,
+ appealTypeLabel: item.value,
+ entity: item.LogicalCollectionName,
+ // match your previous convention:
+ incidentid:
+ r[
+ "_" +
+ item.NavigationProperty.toLowerCase() +
+ "_value"
+ ] || null,
+ }));
+
+ console.log(
+ `Returned ${normalized.length} records from ${item.LogicalCollectionName}`
+ );
+
+ return normalized;
+ })
+ );
+
+ // Flatten and remove null incidentid if needed
+ let flattened = resultsPerType.flat();
+
+ // Optional: if incidentid is required
+ flattened = flattened.filter((r) => r.incidentid !== null);
+
+ const output = {
+ "@odata.count": flattened.length,
+ value: flattened,
+ };
+
+ return res.status(200).json(output);
+ } catch (error) {
+ consoleLogger(error);
+ return res.status(400).json(error);
+ }
+}
diff --git a/pages/api/endpoint/getbasicsearchpaged_api.js b/pages/api/endpoint/getbasicsearchpaged_api.js
index 95cfc671..3488889d 100644
--- a/pages/api/endpoint/getbasicsearchpaged_api.js
+++ b/pages/api/endpoint/getbasicsearchpaged_api.js
@@ -83,6 +83,8 @@ export default async function ApiProxy(req, res) {
searchString +
"') or contains(ticketnumber, '" +
searchString +
+ "') or contains(pinswg_lpareference, '" +
+ searchString +
"')) and pinswg_appealcasetype ne null " +
(process.env.SHOWSIPS !== "true"
? "and pinswg_appealcasetype ne 846040002 "
diff --git a/styles/sass/welshgov/_application.scss b/styles/sass/welshgov/_application.scss
index 6a5b1152..14f802ae 100644
--- a/styles/sass/welshgov/_application.scss
+++ b/styles/sass/welshgov/_application.scss
@@ -3136,4 +3136,32 @@ ul.subFieldList {
.govuk-fieldset {
margin-bottom: 20px;
}
+}
+
+.ref-label {
+ white-space: nowrap;
+}
+
+
+.ref-block {
+ display: block;
+ /* forces new line */
+ margin-top: 4px;
+ /* space from authority */
+ white-space: normal;
+ /* allow wrapping */
+ overflow-wrap: anywhere;
+ /* break long refs nicely */
+ word-break: break-word;
+ /* fallback */
+}
+
+.search-highlight {
+ background: #fff2a8;
+ padding: 0 2px;
+ border-radius: 2px;
+ /* ✅ crucial fixes for wrapping */
+ -webkit-box-decoration-break: clone;
+ box-decoration-break: clone;
+ line-height: 1.4;
}
\ No newline at end of file