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