diff --git a/.env.local b/.env.local index 7d13acc3..548fde8a 100644 --- a/.env.local +++ b/.env.local @@ -19,6 +19,7 @@ RELAY_ROOT = https://ar-ns-lz-pedw-dev-uks-01.servicebus.windows.net/ar-hc-lz-pe RELAYURI = "ar-ns-lz-pedw-dev-uks-01.servicebus.windows.net" RELAYPATH = "ar-hc-lz-pedw-dev-uks-01" CRMURL = "devcrm2016.llcdt.gov.wales/DVPINS" +CRMURL_VERSION = "v8.2" # //New Test CLIENT_ID = d40b9373-96c2-4b04-8bd8-c2a2fe6444c1 @@ -27,6 +28,7 @@ RELAY_ROOT = https://ar-ns-lz-pedw-test-uks-01.servicebus.windows.net/ar-hc-lz-p RELAYURI = "ar-ns-lz-pedw-test-uks-01.servicebus.windows.net" RELAYPATH = "ar-hc-lz-pedw-test-uks-01" CRMURL = "tscrm2016.llcdt.gov.wales/UATPINS" +CRMURL_VERSION = "v8.2" # # //New PreProd # CLIENT_ID = ab6c4678-b31a-4eb3-b429-e039120e488a @@ -38,6 +40,7 @@ CRMURL = "tscrm2016.llcdt.gov.wales/UATPINS" # # RELAYURI = "ar-ns-lz-pedw-ppe-ukw-01.servicebus.windows.net" # # RELAYPATH = "ar-hc-lz-pedw-ppe-ukw-01" # CRMURL = "ppcrm2016.llcdt.gov.wales/PPPINSWales" +# CRMURL_VERSION = "v8.2" # //Prod Oauth WGO @@ -49,15 +52,55 @@ CRMURL = "tscrm2016.llcdt.gov.wales/UATPINS" # CRMURL = "crm2016.llc.gov.wales/PEDW" # //New Prod -CLIENT_ID = 3e4141a3-1fbd-454e-98f0-7f3a4f14a3cc -CLIENT_SECRET = tFk8Q~E.R-HFjROTu1sRKqwb_ONzy05uPYoF6awR -RELAY_ROOT = https://ar-ns-lz-pedw-prod-uks-01.servicebus.windows.net/ar-hc-lz-pedw-prod-uks-01/ -RELAYURI = "ar-ns-lz-pedw-prod-uks-01.servicebus.windows.net" -RELAYPATH = "ar-hc-lz-pedw-prod-uks-01" -# RELAY_ROOT = https://ar-ns-lz-pedw-ppe-ukw-01.servicebus.windows.net/ar-hc-lz-pedw-prod-ukw-01/ -# RELAYURI = "ar-ns-lz-pedw-prod-ukw-01.servicebus.windows.net" -# RELAYPATH = "ar-hc-lz-pedw-prod-ukw-01" -CRMURL = "crm2016.llcdt.gov.wales/PEDW" +# CLIENT_ID = 3e4141a3-1fbd-454e-98f0-7f3a4f14a3cc +# CLIENT_SECRET = tFk8Q~E.R-HFjROTu1sRKqwb_ONzy05uPYoF6awR +# RELAY_ROOT = https://ar-ns-lz-pedw-prod-uks-01.servicebus.windows.net/ar-hc-lz-pedw-prod-uks-01/ +# RELAYURI = "ar-ns-lz-pedw-prod-uks-01.servicebus.windows.net" +# RELAYPATH = "ar-hc-lz-pedw-prod-uks-01" +# # RELAY_ROOT = https://ar-ns-lz-pedw-ppe-ukw-01.servicebus.windows.net/ar-hc-lz-pedw-prod-ukw-01/ +# # RELAYURI = "ar-ns-lz-pedw-prod-ukw-01.servicebus.windows.net" +# # RELAYPATH = "ar-hc-lz-pedw-prod-ukw-01" +# CRMURL = "crm2016.llc.gov.wales/PEDW" +# CRMURL_VERSION = "v8.2" + + + +# D365 cnr WGO +# CLIENT_ID = 3125ac03-91ac-46a7-a166-0086f0ed90b3 +# CLIENT_SECRET = bFq8Q~9VX5Ra3lKnGGIXsB.G6-ZGMx7LqyqXjdr4 +# RELAY_ROOT = https://ar-ns-lz-pedw-d365cnr-uks-01.servicebus.windows.net/ar-hc-lz-pedw-d365cnr-uks-01/ +# RELAYURI = "ar-ns-lz-pedw-d365cnr-uks-01.servicebus.windows.net" +# RELAYPATH = "ar-hc-lz-pedw-d365cnr-uks-01" +# CRMURL = "dv-pedw.crm11.dynamics.com" +# CRMURL_VERSION = "v9.2" + +# D365 Dev WGO +# CLIENT_ID = 9b834097-03d7-409b-b038-ecfec31a394a +# CLIENT_SECRET = .Jf8Q~tKMObCJjhh_hy3J5AIctFrBalU4FS~tdxk +# RELAY_ROOT = https://ar-ns-lz-pedw-d365dev-uks-01.servicebus.windows.net/ar-hc-lz-pedw-d365dev-uks-01/ +# RELAYURI = "ar-ns-lz-pedw-d365dev-uks-01.servicebus.windows.net" +# RELAYPATH = "ar-hc-lz-pedw-d365dev-uks-01" +# CRMURL = "dv-pedw.crm11.dynamics.com" +# CRMURL_VERSION = "v9.2" + +# D365 Test WGO +# CLIENT_ID = d40b9373-96c2-4b04-8bd8-c2a2fe6444c1 +# CLIENT_SECRET = 6hT8Q~glL~jloWpGmvXw3UY37QFwjUE0r._jobEM +# RELAY_ROOT = https://ar-ns-lz-pedw-d365test-uks-01.servicebus.windows.net/ar-hc-lz-pedw-d365test-uks-01/ +# RELAYURI = "ar-ns-lz-pedw-d365test-uks-01.servicebus.windows.net" +# RELAYPATH = "ar-hc-lz-pedw-d365test-uks-01" +# CRMURL = "ts-pedw.crm11.dynamics.com" +# CRMURL_VERSION = "v9.2" + +# D365 PP WGO +# CLIENT_ID = ab6c4678-b31a-4eb3-b429-e039120e488a +# CLIENT_SECRET = V288Q~P1lFMvvX9Xnkx2pLixK~GOREH983Owqc1E +# RELAY_ROOT = https://ar-ns-lz-pedw-d365ppe-uks-01.servicebus.windows.net/ar-hc-lz-pedw-d365ppe-uks-01/ +# RELAYURI = "ar-ns-lz-pedw-d365ppe-uks-01.servicebus.windows.net" +# RELAYPATH = "ar-hc-lz-pedw-d365ppe-uks-01" +# CRMURL = "wg-pp-pedw.crm11.dynamics.com" +# CRMURL_VERSION = "v9.2" + //GOOGLE_TAG_MANAGER = GTM-T78CBC3 @@ -69,8 +112,8 @@ SHOWMAPS = false API_ROOT = "http://localhost:3000" CY_API_ROOT = "http://cymru.local:3000" I18N_DOMAIN = "cymru.local" -# DOCAPI_OFFLINE = "12:00:00,14:00:00" -DOCAPI_OFFLINE = "02:00:00,04:00:00" +DOCAPI_OFFLINE = "07:00:00,14:00:00" +# DOCAPI_OFFLINE = "01:00:00,04:00:00" SHOWFILTERDOCS = true @@ -84,7 +127,7 @@ DATABASE_URL = sqlserver://sql-srv-plt-pedw-cnr-uks-01.database.windows.net:1433 // dev test sql server # DATABASE_URL = sqlserver://pedw-dev-test-sql-svr.database.windows.net:1433;database=PEDWDev;user=RobBondSQL;password=GhWKr62F5sQ69dZx;encrypt=true;trustServerCertificate=true;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryPassword -// DATABASE_URL = sqlserver://sql-srv-plt-pedw-dev-uks-01.database.windows.net:1433;database=PEDW;user=PedwSqlAdmin;password=GhWKr62F5sQ69dZx;encrypt=true;trustServerCertificate=true;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryPassword + DATABASE_URL = sqlserver://sql-srv-plt-pedw-dev-uks-01.database.windows.net:1433;database=PEDW;user=PedwSqlAdmin;password=GhWKr62F5sQ69dZx;encrypt=true;trustServerCertificate=true;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryPassword // test sql server DATABASE_URL = sqlserver://sql-srv-plt-pedw-test-uks-01.database.windows.net:1433;database=PEDW;user=PedwSqlAdmin;password=GhWKr62F5sQ69dZx;encrypt=true;trustServerCertificate=true;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryPassword @@ -97,7 +140,18 @@ DATABASE_URL = sqlserver://sql-srv-plt-pedw-cnr-uks-01.database.windows.net:1433 # DATABASE_URL = sqlserver://pp-pedw-sql.database.windows.net:1433;database=PEDWPreProd;user=RobBondSQL;password=GhWKr62F5sQ69dZx;encrypt=true;trustServerCertificate=true;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryPassword // Prod sql server - DATABASE_URL = sqlserver://sql-srv-plt-pedw-prod-uks-01.database.windows.net:1433;database=PEDW;user=PedwSqlAdmin;password=GhWKr62F5sQ69dZx;encrypt=true;trustServerCertificate=true;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryPassword +# DATABASE_URL = sqlserver://sql-srv-plt-pedw-prod-uks-01.database.windows.net:1433;database=PEDW;user=PedwSqlAdmin;password=GhWKr62F5sQ69dZx;encrypt=true;trustServerCertificate=true;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryPassword + +// D365 CNR sql server +# DATABASE_URL = sqlserver://sql-srv-plt-pedw-d365cnr-uks-01.database.windows.net:1433;database=PEDW;user=PedwSqlAdmin;password=GhWKr62F5sQ69dZx;encrypt=true;trustServerCertificate=true;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryPassword + +// D365 Test sql server +# DATABASE_URL = sqlserver://sql-srv-plt-pedw-d365test-uks-01.database.windows.net:1433;database=PEDW;user=PedwSqlAdmin;password=GhWKr62F5sQ69dZx;encrypt=true;trustServerCertificate=true;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryPassword + + +// D365 PPE sql server +# DATABASE_URL = sqlserver://sql-srv-plt-pedw-d365ppe-uks-01.database.windows.net:1433;database=PEDW;user=PedwSqlAdmin;password=GhWKr62F5sQ69dZx;encrypt=true;trustServerCertificate=true;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryPassword + SECRET = SuperSecret @@ -119,9 +173,9 @@ AZURE_STORAGE_ACCOUNT_KEY = MmrsrCjBfop9pI2oImsP/+lJUWU2DrXsAB8nq5bOVROTmPovSGxj AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwdevuks.blob.core.windows.net // New test key -AZURE_STORAGE_ACCOUNT_NAME = "sapltpedwtestuks" -AZURE_STORAGE_ACCOUNT_KEY = bqJAz5pYQVGee8dnVhlsqW+xHwinNTjgY2AmojNyB+d4uAotMR20WcO4Op13yB+9dFaXAkkjtIA4+AStHT/IYg== -AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwtestuks.blob.core.windows.net +# AZURE_STORAGE_ACCOUNT_NAME = "sapltpedwtestuks" +# AZURE_STORAGE_ACCOUNT_KEY = 5NdDO9GGlbTFLHFOXZRw8H2C0Ow5bFTvHwUHEbStyzg2mbd1uwHSeBSvM/dkN+HfdwrFzqAkwL5P+AStdO6cig== +# AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwtestuks.blob.core.windows.net // New preprod key # AZURE_STORAGE_ACCOUNT_NAME = "sapltpedwppeuks" @@ -135,9 +189,32 @@ AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwtestuks.blob.core.windows.net # AZURE_PEDW_STORAGE_ENDPOINT = https://pedwprod.blob.core.windows.net # // New PROD key -AZURE_STORAGE_ACCOUNT_NAME = "sapltpedwproduks" -AZURE_STORAGE_ACCOUNT_KEY = OfV9MFqXcLLPam9GhEdMWadIOwIzhc5hd5TUqzIuQoswFdtV4ybaemjatSv+Kht0Ny1qQKjLplwY+AStUTZJrg== -AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwproduks.blob.core.windows.net +# AZURE_STORAGE_ACCOUNT_NAME = "sapltpedwproduks" +# AZURE_STORAGE_ACCOUNT_KEY = OfV9MFqXcLLPam9GhEdMWadIOwIzhc5hd5TUqzIuQoswFdtV4ybaemjatSv+Kht0Ny1qQKjLplwY+AStUTZJrg== +# AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwproduks.blob.core.windows.net + +# D365 CNR key +# AZURE_STORAGE_ACCOUNT_NAME = "sapltpedwd365cnruks" +# AZURE_STORAGE_ACCOUNT_KEY = nEwCp+4FuVf/9a4DOyKuQO2yRRV4Ypigg20ch1YaiL09YmEhWMEO5pNLpYtI3WnQCkIqeu1CUqwM+AStCLW01g== +# AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwd365cnruks.blob.core.windows.net + + +# D365 DEV key +# AZURE_STORAGE_ACCOUNT_NAME = "sapltpedwd365devuks" +# AZURE_STORAGE_ACCOUNT_KEY = XzwQxuZXSwYrVgemDUD2ymTpYTIfWqhypbmEALw1ZEEP0M4D3b3Gh28clSymxwFu7Gu9mhwkW4/V+AStZ6RF8Q== +# AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwd365devuks.blob.core.windows.net + + +# D365 TEST key +# AZURE_STORAGE_ACCOUNT_NAME = "sapltpedwd365testuks" +# AZURE_STORAGE_ACCOUNT_KEY = 5NdDO9GGlbTFLHFOXZRw8H2C0Ow5bFTvHwUHEbStyzg2mbd1uwHSeBSvM/dkN+HfdwrFzqAkwL5P+AStdO6cig== +# AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwd365testuks.blob.core.windows.net + + +# D365 PP key +# AZURE_STORAGE_ACCOUNT_NAME = "sapltpedwd365ppeuks" +# AZURE_STORAGE_ACCOUNT_KEY = xQz4FWDnQDE09uPOCj1t7axRiquvZG9vbxL88ywzVsJnTsAgewrHnuAGkxGPBCp2xGE53ffYOme6+AStSr8OoQ== +# AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwd365ppeuks.blob.core.windows.net AZURE_PEDW_CONTAINER = "pedwapplications" diff --git a/.gitignore b/.gitignore index fbab4145..ad55dc2c 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,6 @@ public/swagger.json certificates/cert-key.pem certificates/cert.pem gitclean.sh - -dockerfile -Jenkinsfile +pages/admin/logcheck.js +pages/admin/logchecker.js +pages/baracuda.html diff --git a/actions/azurestorage.js b/actions/azurestorage.js index f83b1850..b9280e4d 100644 --- a/actions/azurestorage.js +++ b/actions/azurestorage.js @@ -210,31 +210,31 @@ export const getBlobs = async (containerName, casefolderID) => { "/api/file/downloadblob?container=" + containerName + "&casefolderID=" + - casefolderID + + encodeURIComponent(casefolderID) + "&blobname=" + - blob.name, + encodeURIComponent(blob.name), "hashedfilepath": hashAPIPath( "/api/file/downloadblob?container=" + containerName + "&casefolderID=" + - casefolderID + + encodeURIComponent(casefolderID) + "&blobname=" + - blob.name.split("/")[2] + encodeURIComponent(blob.name.split("/")[2]) ), "deletepath": "/api/file/deleteblob?container=" + containerName + "&casefolderID=" + - casefolderID + + encodeURIComponent(casefolderID) + "&blobname=" + - blob.name.split("/")[2], + encodeURIComponent(blob.name.split("/")[2]), "hasheddeletepath": hashAPIPath( "/api/file/deleteblob?container=" + containerName + "&casefolderID=" + - casefolderID + + encodeURIComponent(casefolderID) + "&blobname=" + - blob.name.split("/")[2] + encodeURIComponent(blob.name.split("/")[2]) ), "hashgetblobs": hashAPIPath( "/api/file/getbloblist?container=" + @@ -432,7 +432,6 @@ export const createRepPDFBlob = async ( containerid: containerName, caseID: caseID, blobType: "Representation PDF", - documentLocationId: whichLocation, ishareLocation: whichLocation, documentLocationId: whichLocation, }; @@ -624,7 +623,6 @@ export const uploadFile = async (formContent, containerName, foldername) => { caseID: foldername.split("/")[0], documentType: files[prop][0].fieldName.split(".")[1], blobType: "RepresentationFile", - documentLocationId: whichLocation, ishareLocation: whichLocation, documentLocationId: whichLocation, }; @@ -1244,17 +1242,17 @@ export const getProgressBlobs = async (containerName, caseReference) => { "/api/file/downloadblob?container=" + containerName + "&casefolderID=" + - caseReference + + encodeURIComponent(caseReference) + "&blobname=" + - blob.name.split("/")[1] + encodeURIComponent(blob.name.split("/")[1]) ), "hasheddeletepath": hashAPIPath( "/api/file/deleteblob?container=" + containerName + "&casefolderID=" + - caseReference + + encodeURIComponent(caseReference) + "&blobname=" + - blob.name.split("/")[1] + encodeURIComponent(blob.name.split("/")[1]) ), "hashgetblobs": hashAPIPath( "/api/file/getbloblist?container=" + @@ -1415,20 +1413,20 @@ export const getRepsFilesBlobs = async ( "/api/file/downloadblob?container=" + containerName + "&casefolderID=" + - blob.name.split("/")[0] + - "/" + - blob.name.split("/")[1] + + encodeURIComponent( + blob.name.split("/")[0] + "/" + blob.name.split("/")[1] + ) + "&blobname=" + - blob.name.split("/")[3], + encodeURIComponent(blob.name.split("/")[3]), "hashedfilepath": hashAPIPath( "/api/file/downloadblob?container=" + containerName + "&casefolderID=" + - blob.name.split("/")[0] + - "/" + - blob.name.split("/")[1] + + encodeURIComponent( + blob.name.split("/")[0] + "/" + blob.name.split("/")[1] + ) + "&blobname=" + - blob.name + encodeURIComponent(blob.name.split("/")[3]) ), "deletepath": "/api/file/deleteblob?container=" + @@ -1443,11 +1441,11 @@ export const getRepsFilesBlobs = async ( "/api/file/deleteblob?container=" + containerName + "&casefolderID=" + - blob.name.split("/")[0] + - "/" + - blob.name.split("/")[1] + + encodeURIComponent( + blob.name.split("/")[0] + "/" + blob.name.split("/")[1] + ) + "&blobname=" + - blob.name.split("/")[3] + encodeURIComponent(blob.name.split("/")[3]) ), "hashgetblobs": hashAPIPath( "/api/file/getbloblist?container=" + @@ -1459,7 +1457,7 @@ export const getRepsFilesBlobs = async ( ), }); } - //console.log("blobObj:", blobObj); + console.log("blobObj:", blobObj); return blobObj; }; diff --git a/actions/index.js b/actions/index.js index 2fbe3561..f0fbafe0 100644 --- a/actions/index.js +++ b/actions/index.js @@ -389,7 +389,7 @@ export const getDNSCoords = () => { }); }; -export const getDNSList = async (searchString) => { +export const getDNSList = (searchString) => { return axios .get(BASE_URL + "/api/endpoint/getdnslist_api") .then((res) => res.data) @@ -544,23 +544,42 @@ export const getBasicSearchDetailsPaged = async ( appealTypeName, caseReference, primaryIdAttribute, - incidentID + incidentIDs ) => { - //console.log("check appealtype:", appealType, caseReference); + if (!incidentIDs || incidentIDs.length === 0) return []; - return axios - .get( - "/api/endpoint/getbasicsearchdetailspaged_api?appealTypeName=" + - appealTypeName + - "&primaryIdAttribute=" + - primaryIdAttribute + - "&incidentID=" + - incidentID - ) - .then((res) => res.data) - .catch((error) => { - consoleLogger(error); - }); + console.log("=======", primaryIdAttribute); + // Build OData filter correctly + // const filter = incidentIDs + // .map((id) => `_${primaryIdAttribute}s_value eq ${id}`) + // .join(" or "); + + const filter = incidentIDs + .map((id) => { + const suffix = + primaryIdAttribute === "pinswg_sipscase" + ? `_${primaryIdAttribute}_value` + : `_${primaryIdAttribute}s_value`; + + return `${suffix} eq ${id}`; + }) + .join(" or "); + + const url = `/api/endpoint/getbasicsearchdetailspaged_api?appealTypeName=${appealTypeName}&primaryIdAttribute=${primaryIdAttribute}&incidentID=${encodeURIComponent( + filter + )}`; + + console.log( + `Fetching ${incidentIDs.length} incidents for appeal type: ${appealTypeName}` + ); + + try { + const res = await axios.get(url); + return res.data.value || []; // ensure returning an array + } catch (error) { + consoleLogger(error); + return []; + } }; export const getSearchDocumentDetails = (incidentID) => { @@ -1507,10 +1526,9 @@ export const deleteMyRepresentationsFromBlob = ( export const deleteWatchedCases = async (watchedCaseID) => { var queryUrl = - "/api/endpoint/deletewatchedcasesproxy_api?watchedCaseID=" + - watchedCaseID; + "/api/endpoint/deletewatchedcases_api?watchedCaseID=" + watchedCaseID; var config = { - method: "get", + method: "delete", url: queryUrl, }; @@ -1824,9 +1842,9 @@ export const deleteBlob = async ( "/api/file/deleteblob?container=" + containerName + "&casefolderID=" + - casefolderID + + encodeURIComponent(casefolderID) + "&blobname=" + - blobName + + encodeURIComponent(blobName) + deleteblobhash ); return res.data; @@ -1847,11 +1865,9 @@ export const deleteRepBlob = async ( "/api/file/deleteblob?container=" + containerName + "&casefolderID=" + - casefolderID + - "/" + - filenamePrefix + + encodeURIComponent(casefolderID + "/" + filenamePrefix) + "&blobname=" + - blobName + + encodeURIComponent(blobName) + deleteblobhash ); return res.data; @@ -1905,14 +1921,14 @@ export const getProgressFromBlob = async (containerName, casereference) => { const res = await axios.get( BASE_URL + "/api/file/getprogressobjblob?container=" + - containerName + + encodeURIComponent(containerName) + "&casefolderID=" + - casereference + + encodeURIComponent(casereference) + hashAPIPath( "/api/file/getprogressobjblob?container=" + - containerName + + encodeURIComponent(containerName) + "&casefolderID=" + - casereference + encodeURIComponent(casereference) ) ); return res.data; @@ -2220,7 +2236,6 @@ export const getAppealPDFDocument = async (incidentid) => { return error.response; }); }; - export const getNewAppeals = async (searchString) => { try { const res = await axios.get(BASE_URL + "/api/admin/getnewappeals_api"); @@ -2266,8 +2281,25 @@ export const getNewDocumentsPaged = async ( fieldSort, showNumberOfRecords, documentType, + documentOrigin, selectedWeeks ) => { + console.log( + "/api/admin/getlatestdocuments_api?pageNumber=" + + pageNumber + + "&orderby=" + + orderBy + + "&fieldSort=" + + fieldSort + + "&showNumberOfRecords=" + + showNumberOfRecords + + "&documentType=" + + documentType + + "&numberWeeks=" + + selectedWeeks + + "&documentOrigin=" + + documentOrigin + ); return axios .get( "/api/admin/getlatestdocuments_api?pageNumber=" + @@ -2281,7 +2313,9 @@ export const getNewDocumentsPaged = async ( "&documentType=" + documentType + "&numberWeeks=" + - selectedWeeks + selectedWeeks + + "&documentOrigin=" + + documentOrigin ) .then((res) => { return res.data; diff --git a/actions/selectQueryTypes.js b/actions/selectQueryTypes.js index d72777a7..75a4a51c 100644 --- a/actions/selectQueryTypes.js +++ b/actions/selectQueryTypes.js @@ -4,7 +4,7 @@ export const getSelectQuery = (appealTypeName) => { return "&$select=pinswg_detailedeiascreeningrequired,pinswg_speacialistcaseprocess,modifiedon,_pinswg_appellant_value,pinswg_name,pinswg_siteaddresscounty,pinswg_siteaddressline1,pinswg_siteaddressline2,pinswg_siteaddresspostcode,pinswg_siteaddresstown,_pinswg_associatedlpa_value,pinswg_casedecisiondate,pinswg_dateeventrequested,pinswg_decision,pinswg_procedure,pinswg_questionnaireduedate,statuscode,pinswg_startdateoftheevent,pinswg_typeofevent,pinswg_startdate,pinswg_otherpartiesstatement,pinswg_finalcommentsduedate,pinswg_proofsofevidencewrittenstatementsofeviden,pinswg_speacialistcaseprocess,pinswg_statementduedate"; break; case "pinswg_callinss77s": - return "&$select=pinswg_eiarequired,modifiedon,_pinswg_appellant_value,pinswg_name,pinswg_siteaddresscounty,pinswg_siteaddressline1,pinswg_siteaddressline2,pinswg_siteaddresspostcode,pinswg_siteaddresstown,_pinswg_associatedlpa_value,pinswg_casedecisiondate,pinswg_dateeventrequested,pinswg_decision,pinswg_finalcommentsduedate,pinswg_procedure,pinswg_proofsofevidencewrittenstatementsofeviden,pinswg_questionnaireduedate,pinswg_startdate,statuscode,pinswg_dateexportedtoiss,pinswg_startdateofevent,pinswg_typeofevent,pinswg_statementsduedate"; + return "&$select=pinswg_eiarequired,modifiedon,_pinswg_appellant_value,pinswg_name,pinswg_siteaddresscounty,pinswg_siteaddressline1,pinswg_siteaddressline2,pinswg_siteaddresspostcode,pinswg_siteaddresstown,_pinswg_associatedlpa_value,pinswg_casedecisiondate,pinswg_dateeventrequested,pinswg_decision,pinswg_finalcommentsduedate,pinswg_procedure,pinswg_proofsofevidencewrittenstatementsofeviden,pinswg_questionnaireduedate,pinswg_startdate,statuscode,pinswg_dateexportedtoiss,pinswg_startdateofevent,pinswg_typeofevent,pinswg_statementsduedate,pinswg_otherpartiesstatement"; break; case "pinswg_commonlands": return "&$select=pinswg_detailedeiascreeningrequired,modifiedon,_pinswg_appellant_value,pinswg_name,pinswg_siteaddresscounty,pinswg_siteaddressline1,pinswg_siteaddressline2,pinswg_siteaddresspostcode,pinswg_siteaddresstown,_pinswg_associatedlpa_value,pinswg_casedecisiondate,pinswg_dateeventrequested,pinswg_decision,pinswg_finalcommentsduedate,pinswg_otherpartiesstatement,pinswg_procedure,pinswg_proofsofevidencewrittenstatementsofeviden,pinswg_questionnaireduedate,pinswg_relevantauthorityname,pinswg_startdate,statuscode,pinswg_starttimeoftheevent,pinswg_typeofevent,pinswg_statementsduedate"; diff --git a/components/admin/tabs/accounts.js b/components/admin/tabs/accounts.js index 97498248..8d802860 100644 --- a/components/admin/tabs/accounts.js +++ b/components/admin/tabs/accounts.js @@ -26,6 +26,8 @@ export default function AccountsTab({ userData }) { > {user.email} +
+ {user.id}
Last accessed: {user.created} diff --git a/components/admin/tabs/appeals.js b/components/admin/tabs/appeals.js index ce39a05f..bfa2115e 100644 --- a/components/admin/tabs/appeals.js +++ b/components/admin/tabs/appeals.js @@ -221,7 +221,7 @@ function AppealsTab(props) { resultsArr.map((item, key) => { let detailsObj = jsonpath({ path: - '$..value[?(@ && @.ticketnumber=="' + + '$..[?(@ && @.ticketnumber=="' + item.ticketnumber + '")]', json: searchDetailsObj, diff --git a/components/admin/tabs/documents.js b/components/admin/tabs/documents.js index 23818da2..94bc359a 100644 --- a/components/admin/tabs/documents.js +++ b/components/admin/tabs/documents.js @@ -7,6 +7,9 @@ import { connect } from "react-redux"; import transLookup from "../../../data/lookuptranslations.json"; import { formatDates } from "../../utils"; import PaginationControl from "../../../components/case/pagination"; +import DocumentLink from "../../utils/downloads"; +import { useDownloadQueue } from "../../utils/downloadmanager"; + import { sendGAEvent } from "@next/third-parties/google"; import { @@ -170,6 +173,21 @@ const getDocumentTypes = [ }, ]; +const getOrigin = [ + { + "Value": 846040000, + "Label": "MDU", + }, + { + "Value": 846040001, + "Label": "Portal", + }, + { + "Value": 846040002, + "Label": "Manual", + }, +]; + const DocumentsTab = (props) => { let { t, lang } = useTranslation(); const firstRender = useRef(false); @@ -189,12 +207,15 @@ const DocumentsTab = (props) => { const { locale } = router; var documentDetailsArr = documentDetailsObj || []; - + const { startDownload, getStatus } = useDownloadQueue(3); const [selectedOption, setSelectedOption] = useState(10); const [selectedWeeks, setSelectedWeeks] = useState(1); const [documentTypes, setDocumentTypes] = useState(getDocumentTypes); + const [documentOrigin, setDocumentOrigin] = useState(getOrigin); const [selectedDocumentType, setSelectedDocumentType] = useState("all"); + const [selectedDocumentOrigin, setSelectedDocumentOrigin] = useState("all"); const [selectAll, setSelectAll] = useState(false); + const [selectOriginAll, setSelectOriginAll] = useState(false); const [checkedItems, setCheckedItems] = useState( documentTypes.reduce((acc, item) => { @@ -203,6 +224,13 @@ const DocumentsTab = (props) => { }, {}) ); + const [checkedOriginItems, setCheckedOriginItems] = useState( + documentOrigin.reduce((acc, item) => { + acc[item.pinswg_origin] = false; + return acc; + }, {}) + ); + const handleCheckboxChange = (e) => { const { name, checked } = e.target; @@ -213,6 +241,16 @@ const DocumentsTab = (props) => { setSelectAll(false); }; + const handleCheckboxOriginChange = (e) => { + const { name, checked } = e.target; + + setCheckedOriginItems((prev) => ({ + ...prev, + [name]: checked, + })); + setSelectOriginAll(false); + }; + const handleSelectAllChange = (e) => { const { checked } = e.target; setSelectAll(checked); // Set the 'Select All' state @@ -230,16 +268,42 @@ const DocumentsTab = (props) => { } }; + const handleSelectOriginAllChange = (e) => { + const { checked } = e.target; + setSelectOriginAll(checked); // Set the 'Select All' state + if (checked) { + // If 'Select All' is checked, check all the other checkboxes + + setCheckedOriginItems( + documentOrigin.reduce((acc, item) => { + acc[item.pinswg_origin] = true; + return acc; + }, {}) + ); + } else { + clearCheckboxes(); + } + }; + const clearCheckboxes = () => { setSelectAll(false); + setSelectOriginAll(false); setCheckedItems( documentTypes.reduce((acc, item) => { acc[item.pinswg_isharedocumentlocations] = false; return acc; }, {}), + setSelectedDocumentType("all"), setCurrentPage(1) ); + setCheckedOriginItems( + documentOrigin.reduce((acc, item) => { + acc["origin_" + item.pinswg_origin] = false; + return acc; + }, {}), + setSelectedDocumentOrigin("all") + ); }; let ShowDocLinks = false; let checkShowDocLinks = docsOffline != false ? true : false; @@ -314,6 +378,9 @@ const DocumentsTab = (props) => { {t("search:clear-filter-button-label")} +

+ Document Type +

{" "} <> @@ -335,17 +402,6 @@ const DocumentsTab = (props) => { {router.locale == "cy" ? "Pawb" : "All"}{" "} - - ( - {documentTypes.reduce( - (accumulator, currentItem) => { - return ( - accumulator + - currentItem.count - ); - }, - 0 - )} - )
@@ -389,17 +445,105 @@ const DocumentsTab = (props) => { ); })} {" "} +

+ Document Origin +

+
+ {" "} + <> +
+ + +
+ + {documentOrigin.map((item, key) => { + return ( +
+ + +
+ ); + })} +
{" "}
{/*
@@ -931,9 +925,8 @@ const DocumentDetails = (props) => { selectedOption, selectedDocumentType ) - // .then((data) => data) + .then((data) => data) .then((data) => { - setDocumentSearchState(true); setDocumentDetails(data); setShowSpinnerState(false); }); @@ -1003,10 +996,8 @@ const DocumentDetails = (props) => { }; const [showDocumentSearchState, setDocumentSearchState] = useState(false); - const [orderByState, setOrderbyState] = useState( - "pinswg_documentpublisheddate" - ); - const [fieldSortState, setfieldSortState] = useState("desc"); + const [orderByState, setOrderbyState] = useState("createdon"); + const [fieldSortState, setfieldSortState] = useState("asc"); let documentSearchState = () => { showDocumentSearchState == true @@ -1016,14 +1007,14 @@ const DocumentDetails = (props) => { const sortDataBy = (whichField) => { setOrderbyState(whichField); - setfieldSortState("desc"); + setfieldSortState("asc"); getDocumentResults(1, whichField, fieldSortState); whichField == orderByState - ? fieldSortState == "asc" - ? setfieldSortState("desc") - : setfieldSortState("asc") - : setfieldSortState("desc"); + ? fieldSortState == "desc" + ? setfieldSortState("asc") + : setfieldSortState("desc") + : setfieldSortState("asc"); setCurrentPage(1); setShowSpinnerState(true); }; @@ -1058,7 +1049,7 @@ const DocumentDetails = (props) => { return docObj; }; - //const searchDocumentResultsObj = docDetailsObj(); + const searchDocumentResultsObj = docDetailsObj(); }, [ incidentid, setDocumentDetails, diff --git a/components/case/summary.js b/components/case/summary.js index f30aa0fd..c82cf6c5 100644 --- a/components/case/summary.js +++ b/components/case/summary.js @@ -57,6 +57,7 @@ const CaseSummary = (props) => { const router = useRouter(); const { locale } = router; const { + appealTypeID, incidentid, caseReference, myCases, @@ -79,7 +80,6 @@ const CaseSummary = (props) => { ticketnumber, } = props; - let appealTypeID = props.appealTypeID || router.query.appealType; const showLoginCheck = currentType == "directResultsObj" ? "true" : currentView.showLogin; const { data: session, status } = useSession(); @@ -482,14 +482,19 @@ const CaseSummary = (props) => {
{getFormCollectionByID( - appealTypeID + appealTypeID || + setCaseQueryObj.value[0] + .pinswg_appealcasetype ).PrimaryIdAttribute == "pinswg_dnsid" ? t( "case:summary-applicantonly-label" ) : getFormCollectionByID( - appealTypeID + appealTypeID || + setCaseQueryObj + .value[0] + .pinswg_appealcasetype ).PrimaryIdAttribute == "pinswg_nonvalidationid" ? t( @@ -542,7 +547,9 @@ const CaseSummary = (props) => {
{getFormCollectionByID( - appealTypeID + appealTypeID || + setCaseQueryObj.value[0] + .pinswg_appealcasetype ).PrimaryIdAttribute == "pinswg_dnsid" || getFormCollectionByID( @@ -1980,6 +1987,67 @@ const CaseSummary = (props) => { expDate.setDate(now.getDate() + 365); + function formatDates(dateObj, showTime) { + var dateObj = new Date(dateObj); + var dd = String(dateObj.getDate()).padStart(2, "0"); + var mm = String(dateObj.getMonth() + 1).padStart(2, "0"); //January is 0! + var yyyy = dateObj.getFullYear(); + var hh = dateObj.getHours(); + hh = ("0" + hh).slice(-2); + var mins = dateObj.getMinutes(); + mins = ("0" + mins).slice(-2); + + const dateStr = showTime + ? hh == 0 && mins == 0 + ? "" + : hh + ":" + mins + : dd + "/" + mm + "/" + yyyy + " "; + return dateStr; + } + + function linkedCasesList(linkedCasesReference) { + //console.log(linkedCasesReference); + + linkedCasesReference = linkedCasesReference.value || []; + + return ( + <> + + + ); + } + + function showReps(startDate, endDate) { + let date = new Date(); + date = new Date(date.toDateString()); + const start = new Date(startDate); + const end = new Date(endDate); + + return date >= start && date <= end ? true : false; + } + function showRepButton(appealType) { switch (appealType) { case 846040012: @@ -2027,6 +2095,14 @@ const CaseSummary = (props) => { } } + function showRepsEnded(startDate, endDate) { + let date = new Date(); + date = new Date(date.toDateString()); + const start = new Date(startDate); + const end = new Date(endDate); + return date > start && date > end ? true : false; + } + if (status == "authenticated" && cookies.pedwWatchCase != null) { selectWatchedCase( props.accountDetails.accountDetails.contactid, diff --git a/components/elements/index.js b/components/elements/index.js index 98560a22..418d6cbe 100644 --- a/components/elements/index.js +++ b/components/elements/index.js @@ -748,7 +748,7 @@ const RenderDatePicker = ({ : value : null } //{parseISO(value) || null} - className="govuk-input govuk-input--width-12" + className="govuk-input govuk-input--width-10" minDate={minDate} maxDate={maxDate} autoComplete="off" @@ -1973,10 +1973,6 @@ const RenderFileUpload = (field) => { case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": return "/assets/images/documenttypes/xlsx.png"; break; - - case "application/zip": - return "/assets/images/documenttypes/zip.png"; - break; case "image/jpeg": return "/assets/images/documenttypes/jpg.png"; case "image/png": @@ -2145,7 +2141,6 @@ const RenderFileUpload = (field) => { [".docx"], "image/tiff": [".tif", ".tiff"], "image/jpeg": [".jpg", ".jpeg"], - "application/zip": [".zip"], "image/png": [".png"], "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": [".xlsx"], diff --git a/components/myportal.js b/components/myportal.js index 6a15097b..7c8bdfc3 100644 --- a/components/myportal.js +++ b/components/myportal.js @@ -19,7 +19,7 @@ import { JSONPath as jsonpath } from "jsonpath-plus"; import transLookup from "../data/lookuptranslations.json"; const MyPortal = (props) => { - const { showFileUpload, showLoginCheck } = props; + const { showFileUpload, showLoginCheck, docsOffline } = props; let { t } = useTranslation(); const router = useRouter(); @@ -126,7 +126,9 @@ const MyPortal = (props) => {
- {!isLPA && } + {!isLPA && ( + + )} {/* {props.awaitingSubmission.awaitingSubmission[ "@odata.count" diff --git a/components/myportal/awaitingsubmission.js b/components/myportal/awaitingsubmission.js index e29e3b0b..12c4ee22 100644 --- a/components/myportal/awaitingsubmission.js +++ b/components/myportal/awaitingsubmission.js @@ -34,7 +34,7 @@ const AwaitingSubmission = (props) => {
{ + var startTime = docsOffline.split(",")[0]; + var endTime = docsOffline.split(",")[1]; + + // console.log(startTime); + // console.log(endTime); + + var currentDate = new Date(); + + var startDate = new Date(currentDate.getTime()); + startDate.setHours(startTime.split(":")[0]); + startDate.setMinutes(startTime.split(":")[1]); + startDate.setSeconds(startTime.split(":")[2]); + + var endDate = new Date(currentDate.getTime()); + endDate.setHours(endTime.split(":")[0]); + endDate.setMinutes(endTime.split(":")[1]); + endDate.setSeconds(endTime.split(":")[2]); + + // console.log(startDate); + // console.log(endDate); + // console.log(currentDate); + + var valid = startDate < currentDate && endDate > currentDate; + ShowDocLinks = valid; + }; + + console.log("docsOffline", docsOffline, getDocLink(docsOffline)); + return (
@@ -22,11 +57,29 @@ export default function MakeNewAppeal() {

{t("myportal:makenewappeal-card-paragraph-two")}

-
- - {t("myportal:makenewappeal-card-button-label")} - -
+ {ShowDocLinks ? ( +
+ + + + Notice + + {t("myportal:docsoffline-newappeal-label")} + +
+ ) : ( +
+ + {t("myportal:makenewappeal-card-button-label")} + +
+ )} +

{t("myportal:makenewappeal-card-paragraph-three")}

diff --git a/components/myportal/myrepresentationsfromblob.js b/components/myportal/myrepresentationsfromblob.js index 79383c82..129cfd1e 100644 --- a/components/myportal/myrepresentationsfromblob.js +++ b/components/myportal/myrepresentationsfromblob.js @@ -33,7 +33,7 @@ const MyRepresentations = (props) => {
{
{props.accountDetails.accountDetails.firstname}{" "} {props.accountDetails.accountDetails.lastname} diff --git a/components/search/searchresults.js b/components/search/searchresults.js index 198ec13e..aa8bf582 100644 --- a/components/search/searchresults.js +++ b/components/search/searchresults.js @@ -66,7 +66,7 @@ const SearchResults = (props) => { const [showSpinnerState, setShowSpinnerState] = useState(false); const [selectedOption, setSelectedOption] = useState(10); const [orderByState, setOrderbyState] = useState("createdon"); - const [fieldSortState, setfieldSortState] = useState("asc"); + const [fieldSortState, setfieldSortState] = useState("desc"); const [loginToWatch, setLoginToWatch] = useState("true"); let spinnerState = () => { diff --git a/components/utils/downloadmanager.js b/components/utils/downloadmanager.js new file mode 100644 index 00000000..eb16d0bd --- /dev/null +++ b/components/utils/downloadmanager.js @@ -0,0 +1,48 @@ +import { useState, useCallback } from "react"; + +export function useDownloadQueue(maxConcurrent = 3) { + const [queue, setQueue] = useState([]); // queued tasks + const [activeCount, setActiveCount] = useState(0); + const [statuses, setStatuses] = useState({}); // { id: "idle"|"queued"|"downloading"|"done"|"failed" } + + const scheduleNext = useCallback(() => { + setQueue((queueCurr) => { + if (activeCount >= maxConcurrent || queueCurr.length === 0) + return queueCurr; + + const [task, ...rest] = queueCurr; + + setStatuses((s) => ({ ...s, [task.id]: "downloading" })); + setActiveCount((c) => c + 1); + + // Run the download fully in async IIFE + (async () => { + try { + await task.downloadFn(); // triggers fetch + blob + click + setStatuses((s) => ({ ...s, [task.id]: "done" })); + } catch (err) { + console.error(err); + setStatuses((s) => ({ ...s, [task.id]: "failed" })); + } finally { + setActiveCount((c) => c - 1); + scheduleNext(); // promote next in queue + } + })(); + + return rest; + }); + }, [activeCount, maxConcurrent]); + + const startDownload = useCallback( + (id, downloadFn) => { + setStatuses((s) => ({ ...s, [id]: "queued" })); + setQueue((curr) => [...curr, { id, downloadFn }]); + scheduleNext(); + }, + [scheduleNext] + ); + + const getStatus = useCallback((id) => statuses[id] || "idle", [statuses]); + + return { startDownload, getStatus }; +} diff --git a/components/utils/downloads.js b/components/utils/downloads.js new file mode 100644 index 00000000..6e8c81b4 --- /dev/null +++ b/components/utils/downloads.js @@ -0,0 +1,325 @@ +// // import { useState } from "react"; + +// // export default function DocumentLink({ detailsObj, caseReference }) { +// // const [downloadStatus, setDownloadStatus] = useState(""); +// // const [progress, setProgress] = useState(null); + +// // const handleDownload = async () => { +// // setDownloadStatus("Downloading"); +// // setProgress(0); + +// // try { +// // const res = await fetch(detailsObj.pinswg_hashlink); +// // if (!res.ok) throw new Error("Download failed"); + +// // const contentDisposition = res.headers.get("content-disposition"); +// // const filename = contentDisposition +// // ? contentDisposition.split("filename=")[1] +// // : detailsObj.pinswg_name || "document"; + +// // const contentLength = res.headers.get("content-length"); +// // const totalBytes = contentLength +// // ? parseInt(contentLength, 10) +// // : null; + +// // const reader = res.body.getReader(); +// // let receivedBytes = 0; +// // const chunks = []; + +// // while (true) { +// // const { done, value } = await reader.read(); +// // if (done) break; + +// // chunks.push(value); +// // receivedBytes += value.length; + +// // if (totalBytes) { +// // const percent = Math.round( +// // (receivedBytes / totalBytes) * 100 +// // ); +// // setProgress(percent); +// // } +// // } + +// // // Combine chunks into one blob +// // const blob = new Blob(chunks); +// // const url = window.URL.createObjectURL(blob); + +// // const link = document.createElement("a"); +// // link.href = url; +// // link.download = filename; +// // document.body.appendChild(link); +// // link.click(); +// // document.body.removeChild(link); + +// // setDownloadStatus("Download complete!"); +// // setProgress(100); + +// // // Fire GA event +// // window.gtag?.("event", "DownloadedFile", { +// // caseReference, +// // filename, +// // }); +// // } catch (err) { +// // console.error(err); +// // setDownloadStatus("Download failed"); +// // setProgress(null); +// // } +// // }; + +// // return ( +// // <> +// // {detailsObj.pinswg_publishtoweb ? ( +// // +// // ) : ( +// // <> +// // +// // Document name: +// // +// // {detailsObj.pinswg_name || ""} +// // +// // )} + +// // {downloadStatus && ( +// //

+// // {downloadStatus} +// // {/* {progress !== null && ` (${progress}%)`} */} +// //

+// // )} +// // +// // ); +// // } +// import { useState } from "react"; + +// export default function DocumentLink({ +// id, +// detailsObj, +// caseReference, +// startDownload, +// getStatus, +// }) { +// const [downloadStatus, setDownloadStatus] = useState(""); +// const [progress, setProgress] = useState(null); + +// const downloadFile = async () => { +// setDownloadStatus("Downloading"); +// setProgress(0); + +// try { +// const res = await fetch(detailsObj.pinswg_hashlink); +// if (!res.ok) throw new Error("Download failed"); + +// const contentDisposition = res.headers.get("content-disposition"); +// const filename = contentDisposition +// ? contentDisposition.split("filename=")[1] +// : detailsObj.pinswg_name || "document"; + +// const contentLength = res.headers.get("content-length"); +// const totalBytes = contentLength +// ? parseInt(contentLength, 10) +// : null; + +// const reader = res.body.getReader(); +// let receivedBytes = 0; +// const chunks = []; + +// while (true) { +// const { done, value } = await reader.read(); +// if (done) break; + +// chunks.push(value); +// receivedBytes += value.length; + +// if (totalBytes) { +// const percent = Math.round( +// (receivedBytes / totalBytes) * 100 +// ); +// setProgress(percent); +// } +// } + +// const blob = new Blob(chunks); +// const url = window.URL.createObjectURL(blob); + +// const link = document.createElement("a"); +// link.href = url; +// link.download = filename; +// document.body.appendChild(link); +// link.click(); +// document.body.removeChild(link); + +// setDownloadStatus("Download complete!"); +// setProgress(100); + +// window.gtag?.("event", "DownloadedFile", { +// caseReference, +// filename, +// }); +// } catch (err) { +// console.error(err); +// setDownloadStatus("Download failed"); +// setProgress(null); +// } +// }; + +// const handleClick = () => { +// if (getStatus(id) !== "idle") return; // don't double enqueue +// startDownload(id, downloadFile); +// }; + +// const status = getStatus(id); + +// return ( +// <> +// {detailsObj.pinswg_publishtoweb ? ( +// +// ) : ( +// <> +// +// Document name: +// +// {detailsObj.pinswg_name || ""} +// +// )} + +// {status !== "idle" && ( +//

+// {status === "downloading" && progress !== null +// ? `Downloading (${progress}%)` +// : status === "queued" +// ? "Queued" +// : status === "done" +// ? "Download complete!" +// : status === "failed" +// ? "Download failed" +// : ""} +//

+// )} +// +// ); +// } + +import { useState } from "react"; +import useTranslation from "next-translate/useTranslation"; + +export default function DocumentLink({ + id, + detailsObj, + caseReference, + startDownload, + getStatus, +}) { + const [progress, setProgress] = useState(null); + let { t } = useTranslation(); + const downloadFile = async () => { + setProgress(0); + + const res = await fetch(detailsObj.pinswg_hashlink); + if (!res.ok) throw new Error("Download failed"); + + const contentDisposition = res.headers.get("content-disposition"); + const filename = contentDisposition + ? contentDisposition.split("filename=")[1] + : detailsObj.pinswg_name || "document"; + + const contentLength = res.headers.get("content-length"); + const totalBytes = contentLength ? parseInt(contentLength, 10) : null; + + const reader = res.body.getReader(); + let receivedBytes = 0; + const chunks = []; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + chunks.push(value); + receivedBytes += value.length; + + if (totalBytes) { + const percent = Math.round((receivedBytes / totalBytes) * 100); + setProgress(percent); + } + } + + const blob = new Blob(chunks); + const url = window.URL.createObjectURL(blob); + + const link = document.createElement("a"); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + window.URL.revokeObjectURL(url); + + setProgress(100); + window.gtag?.("event", "DownloadedFile", { caseReference, filename }); + }; + + const handleClick = () => { + if (getStatus(id) !== "idle") return; // avoid double enqueue + startDownload(id, downloadFile); + }; + + const status = getStatus(id); + + return ( +
+ {detailsObj.pinswg_publishtoweb ? ( + + ) : ( + + {detailsObj.pinswg_name || ""} + + )} + + {status !== "idle" && ( +

+ {status === "downloading" && progress !== null + ? t("case:downloading-label") + : status === "queued" + ? t("case:download-queued-label") + : status === "done" + ? t("case:download-complete-label") + : status === "failed" + ? t("case:download-failed-label") + : ""} +

+ )} +
+ ); +} diff --git a/components/utils/index.js b/components/utils/index.js index b0ca0a1f..606cf400 100644 --- a/components/utils/index.js +++ b/components/utils/index.js @@ -179,29 +179,58 @@ export const getPartSavedDetails = (searchResultsObj) => { * Get the details of a case from the appeal type * @param object searchResultsObj */ -export const getSearchDetailsPaged = (searchResultsObj) => { - let detailsArr = []; +export const getSearchDetailsPaged = async (searchResultsObj) => { searchResultsObj = searchResultsObj.value; - const detailsObj = searchResultsObj.map((searchDetail, index) => { - if (searchDetail.pinswg_appealcasetype == null) { - console.log(searchDetail.title); - } else { - let formMeta = getFormCollectionByID( - searchDetail.pinswg_appealcasetype - ); - // formMeta?.LogicalCollectionName && - // formMeta?.LogicalCollectionName != "pinswg_sipses" && - detailsArr.push( - getBasicSearchDetailsPaged( - formMeta.LogicalCollectionName, - searchDetail.title, - formMeta.PrimaryIdAttribute, - searchDetail.incidentid - ) - ); + + // Group incidents by appeal type + const groupedByAppealType = searchResultsObj.reduce((acc, detail) => { + const appealType = detail.pinswg_appealcasetype; + if (!appealType) { + console.log("No appeal type for:", detail.title); + return acc; } - }); - return Promise.all(detailsArr); + + if (!acc[appealType]) acc[appealType] = []; + acc[appealType].push({ + incidentID: detail.incidentid, + title: detail.title, + }); + return acc; + }, {}); + + const allDetails = []; + + // For each appeal type, call getBasicSearchDetailsPaged once with all incident IDs + for (const [appealType, incidents] of Object.entries(groupedByAppealType)) { + const incidentIDs = incidents.map((i) => i.incidentID); + + // Get the form meta for this appeal type to access primaryIdAttribute + const formMeta = getFormCollectionByID(appealType); + + if (!formMeta || !formMeta.PrimaryIdAttribute) { + console.log(`Missing form metadata for appeal type: ${appealType}`); + continue; + } + + // console.log( + // `Fetching ${incidentIDs.length} incidents for appeal type: ${appealType} with primaryIdAttribute: ${formMeta.PrimaryIdAttribute}` + // ); + + try { + const details = await getBasicSearchDetailsPaged( + formMeta.LogicalCollectionName, // appealTypeName + null, // caseReference is no longer used in batch + formMeta.PrimaryIdAttribute, // primaryIdAttribute + incidentIDs // pass array of incidentIDs + ); + + allDetails.push(...details); + } catch (err) { + consoleLogger(err); + } + } + + return allDetails; }; // export const absoluteUrl = (req, setLocalhost) => { diff --git a/locales/cy/case.json b/locales/cy/case.json index 40f96591..e6a2a62e 100644 --- a/locales/cy/case.json +++ b/locales/cy/case.json @@ -114,6 +114,11 @@ "representation-date-passed-label": "Mae'r cyfnod cyflwyno sylwadau ar gyfer yr apĂȘl hon rhwng {{startDate}} a {{endDate}}", "representation-date-passed-additional-label": "Gallwch wneud sylw drwy e-bostio,", "date-raised": "Dyddiad codi", + "download-complete-label": "Lawrlwytho wedi'i gwblhau!", + "downloading-label": "Wrthi'n llwytho i lawr", + "download-failed-label": "Methodd y lawrlwythiad", + "download-queued-label": "Wedi'i giwio", + "docsoffline-reps-label": "Oherwydd cynnal a chadw'r system, ni allwn dderbyn sylwadau ar hyn o bryd. Rhowch gynnig arall arni yn nes ymlaen.", "event-title": "Digwyddiadau", "event-name-label": " Enw", "event-event-name-label": "Enw'r digwyddiad ", diff --git a/locales/cy/myportal.json b/locales/cy/myportal.json index d83cd0b7..1d447e4b 100644 --- a/locales/cy/myportal.json +++ b/locales/cy/myportal.json @@ -52,5 +52,6 @@ "date-raised": "Dyddiad codi", "representation-submitted": "Cyflwynwyd y sylw", "submission-processing-label": "Prosesu cyflwyniad", - "appeal-pending-label": "Yn yr arfaeth" + "appeal-pending-label": "Yn yr arfaeth", + "docsoffline-newappeal-label": "Oherwydd gwaith cynnal a chadw'r system, ni allwn dderbyn apeliadau newydd ar hyn o bryd. Rhowch gynnig arall arni yn nes ymlaen." } \ No newline at end of file diff --git a/locales/en/case.json b/locales/en/case.json index cd412589..64661ad3 100644 --- a/locales/en/case.json +++ b/locales/en/case.json @@ -115,6 +115,11 @@ "representation-date-passed-label": "The representation period for this appeal is between {{startDate}} and {{endDate}}", "representation-date-passed-additional-label": "You can make a representation by emailing,", "date-raised": "Date raised", + "download-complete-label": "Download complete!", + "downloading-label": "Downloading", + "download-failed-label": "Download failed", + "download-queued-label": "Queued", + "docsoffline-reps-label": "Due to system maintenance, we are unable to accept representations at this time. Please try again later.", "event-title": "Events", "event-name-label": "Name", "event-event-name-label": "Event name", diff --git a/locales/en/myportal.json b/locales/en/myportal.json index 13631904..d2639346 100644 --- a/locales/en/myportal.json +++ b/locales/en/myportal.json @@ -52,5 +52,6 @@ "date-raised": "Date raised", "representation-submitted": "Representation submitted", "submission-processing-label": "Submission processing", - "appeal-pending-label": "Pending" + "appeal-pending-label": "Pending", + "docsoffline-newappeal-label": "Due to system maintenance, we are unable to accept new appeals at this time. Please try again later." } \ No newline at end of file diff --git a/pages/admin/index.js b/pages/admin/index.js index ab21835b..86ce577a 100644 --- a/pages/admin/index.js +++ b/pages/admin/index.js @@ -54,10 +54,10 @@ const StoragePage = (props) => { {/* Tabs */}
{/* Panels */} + +
+ +
+
+ +
+
{ >
- -
- -
- -
- -
diff --git a/pages/advancedsearchresults.js b/pages/advancedsearchresults.js index 05848822..f2135ea9 100644 --- a/pages/advancedsearchresults.js +++ b/pages/advancedsearchresults.js @@ -124,8 +124,8 @@ export const getServerSideProps = wrapper.getServerSideProps( // "=============================== Advanced Search results:", // searchResultsObj // ); - const searchDetailsObj = - (await getSearchDetails(searchResultsObj)) || null; + // const searchDetailsObj = + // (await getSearchDetails(searchResultsObj)) || null; // console.log( // "=============================== Advanced Search results details:", // searchDetailsObj @@ -136,7 +136,7 @@ export const getServerSideProps = wrapper.getServerSideProps( store.dispatch(setShowReps(showReps, showLoginCheck)); store.dispatch(setSearchResults(searchResultsObj)); - store.dispatch(setSearchDetails(searchDetailsObj)); + // store.dispatch(setSearchDetails(searchDetailsObj)); store.dispatch(setSearch(Object.entries(query))); return { props: { diff --git a/pages/api/admin/getlatestdocuments_api.js b/pages/api/admin/getlatestdocuments_api.js index c5742dab..36b7117c 100644 --- a/pages/api/admin/getlatestdocuments_api.js +++ b/pages/api/admin/getlatestdocuments_api.js @@ -64,9 +64,11 @@ export default async function ApiProxy(req, res) { var fieldSort = req.query.fieldSort || "desc"; var showNumberOfRecords = req.query.showNumberOfRecords || 10; var documentType = req.query.documentType || "all"; + var documentOrigin = req.query.documentOrigin || "all"; var numberOfWeeks = req.query.numberWeeks || 1; var docTypeQueryString = ""; + var docOriginQueryString = ""; if (documentType != "all") { if (documentType.indexOf(",") >= 0) { @@ -91,6 +93,27 @@ export default async function ApiProxy(req, res) { } } + if (documentOrigin != "all") { + if (documentOrigin.indexOf(",") >= 0) { + const documentOriginArr = documentOrigin.split(","); + + docOriginQueryString = " and ("; + documentOriginArr.forEach(function (item, index) { + console.log(item, index); + + if (item != "all") { + docOriginQueryString += " pinswg_origin eq " + item; + + docOriginQueryString += + index < documentOriginArr.length - 1 ? " or " : ""; + } + }); + docOriginQueryString += " )"; + } else { + docOriginQueryString += " and pinswg_origin eq " + documentOrigin; + } + } + function daysAgoISO(days) { const today = new Date(); const resultDate = new Date(today); @@ -103,6 +126,7 @@ export default async function ApiProxy(req, res) { "pinswg_documents?$select=pinswg_name,createdon,pinswg_publishtoweb,_pinswg_documentids_value,pinswg_isharedocumentlocations,pinswg_isharedocumentreference,pinswg_uploadurl,pinswg_uploadstatus,pinswg_origin&$filter=createdon ge " + daysAgoISO(numberOfWeeks) + docTypeQueryString + + docOriginQueryString + "&$orderby=" + orderby + " " + @@ -115,7 +139,7 @@ export default async function ApiProxy(req, res) { console.log( "\n==========================================\n", - "\nnew docs search ", + "\nnew docs search---- ", "\n\nQuery url: " + queryUrl, "\n\nRelay link: " + WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), "\n==========================================\n" diff --git a/pages/api/documents/download/[id].js b/pages/api/documents/download/[id].js index 044e62b9..cbc8db9c 100644 --- a/pages/api/documents/download/[id].js +++ b/pages/api/documents/download/[id].js @@ -1,110 +1,203 @@ -/** - * @swagger - * /api/documents/download/{id}: - * get: - * tags: - * - Documents - * description: Get document - * parameters: - * - name: id - * in: path - * description: iShare ID - * type: string - * required: true - * default: A41567436 - * - name: hash - * in: query - * description: Hash string - * default: e0a1c9cd2817826a25bca67e60b807eae747cbed769e3ec42cf997541c32be71 - * responses: - * 200: - * description: Success - */ +// /** +// * @swagger +// * /api/documents/download/{id}: +// * get: +// * tags: +// * - Documents +// * description: Get document +// * parameters: +// * - name: id +// * in: path +// * description: iShare ID +// * type: string +// * required: true +// * default: A41567436 +// * - name: hash +// * in: query +// * description: Hash string +// * default: e0a1c9cd2817826a25bca67e60b807eae747cbed769e3ec42cf997541c32be71 +// * responses: +// * 200: +// * description: Success +// */ + +// import axios from "axios"; +// import CryptoJS from "crypto-js"; +// import { getToken, consoleLogger } from "../../../../actions"; + +// const WORDKEY = process.env.HASHKEY; +// const accessTokenEndpoint = process.env.ACCESS_TOKEN_ENDPOINT; +// const tenantId = process.env.TENANT; + +// const WEBAPI_URL = +// process.env.RELAY_ROOT || +// "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; + +// const hashAPIPath = (queryPath) => { +// var hashlink = CryptoJS.HmacSHA256( +// queryPath, +// CryptoJS.enc.Hex.parse(WORDKEY) +// ); +// hashlink = hashlink.toString(CryptoJS.enc.Hex); + +// //return "&hash=" + hashlink; + +// return (queryPath.indexOf("?") > -1 ? "&hash=" : "?hash=") + hashlink; +// }; + +// const azureHeadersPaged = (access_token) => { +// return { +// headers: { +// "OData-MaxVersion": "4.0", +// "OData-Version": "4.0", +// "Accept": "application/json;odata.metadata=none", +// "Prefer": +// 'odata.include-annotations="*",return=representation, odata.maxpagesize=10', +// "Content-Type": "application/json", +// "Authorization": "Bearer " + access_token, +// }, +// }; +// }; + +// export default async function ApiProxy(req, res) { +// var token = await getToken(); + +// var docRef = req.query; + +// var queryUrl = "documents/download/" + docRef.id + "?hash=" + docRef.hash; + +// const configDocument = (access_token) => { +// return { +// headers: { +// "OData-MaxVersion": "4.0", +// "OData-Version": "4.0", +// "Accept": "application/json", +// "Prefer": 'odata.include-annotations="*",return=representation', +// "Content-Type": "application/json", +// "Authorization": "Bearer " + access_token, +// }, +// responseType: "arraybuffer", +// }; +// }; + +// return axios +// .get( +// WEBAPI_URL + queryUrl, // + hashAPIPath(queryUrl), +// configDocument(token.access_token) +// ) +// .then((response) => { +// const bytes = response.data.byteLength; +// console.log(bytes); + +// res.setHeader( +// "content-disposition", +// "attachment; filename=" + +// response.headers["content-disposition"].split( +// "filename=" +// )[1] +// ); +// return res.status(200).send(response.data); +// }) +// .then(() => { +// console.log(docRef.id + "has downloaded"); + +// return "downloadComplete"; +// }) +// .catch((error) => { +// consoleLogger(error); +// res.redirect("/filenotavailable"); +// }); +// } import axios from "axios"; -import CryptoJS from "crypto-js"; import { getToken, consoleLogger } from "../../../../actions"; -const WORDKEY = process.env.HASHKEY; -const accessTokenEndpoint = process.env.ACCESS_TOKEN_ENDPOINT; -const tenantId = process.env.TENANT; - const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; -const hashAPIPath = (queryPath) => { - var hashlink = CryptoJS.HmacSHA256( - queryPath, - CryptoJS.enc.Hex.parse(WORDKEY) - ); - hashlink = hashlink.toString(CryptoJS.enc.Hex); - - //return "&hash=" + hashlink; - - return (queryPath.indexOf("?") > -1 ? "&hash=" : "?hash=") + hashlink; -}; - -const azureHeadersPaged = (access_token) => { - return { - headers: { - "OData-MaxVersion": "4.0", - "OData-Version": "4.0", - "Accept": "application/json;odata.metadata=none", - "Prefer": - 'odata.include-annotations="*",return=representation, odata.maxpagesize=10', - "Content-Type": "application/json", - "Authorization": "Bearer " + access_token, - }, - }; -}; - -export default async function ApiProxy(req, res) { - var token = await getToken(); - - var docRef = req.query; - - var queryUrl = "documents/download/" + docRef.id + "?hash=" + docRef.hash; - - const configDocument = (access_token) => { - return { - headers: { - "OData-MaxVersion": "4.0", - "OData-Version": "4.0", - "Accept": "application/json", - "Prefer": 'odata.include-annotations="*",return=representation', - "Content-Type": "application/json", - "Authorization": "Bearer " + access_token, - }, - responseType: "arraybuffer", - }; - }; - - return axios - .get( - WEBAPI_URL + queryUrl, // + hashAPIPath(queryUrl), - configDocument(token.access_token) - ) - .then((response) => { - const bytes = response.data.byteLength; - console.log(bytes); - - res.setHeader( - "content-disposition", - "attachment; filename=" + - response.headers["content-disposition"].split( - "filename=" - )[1] - ); - return res.status(200).send(response.data); - }) - .then(() => { - console.log(docRef.id + "has downloaded"); - - return "downloadComplete"; - }) - .catch((error) => { - consoleLogger(error); - res.redirect("/filenotavailable"); - }); +// Retry utility with logging +async function retry(fn, retries = 3, delay = 1000) { + for (let attempt = 1; attempt <= retries; attempt++) { + try { + return { response: await fn(), attempts: attempt }; + } catch (err) { + if (attempt === retries) throw err; + console.log(`Retry ${attempt} failed, retrying in ${delay}ms...`); + await new Promise((res) => setTimeout(res, delay)); + delay *= 2; // exponential backoff + } + } } + +const ApiProxy = async (req, res) => { + const docRef = req.query; + + try { + const token = await getToken(); + const startTime = Date.now(); + + const fetchStream = async () => { + const queryUrl = + "documents/download/" + docRef.id + "?hash=" + docRef.hash; + + return axios.get(WEBAPI_URL + queryUrl, { + headers: { + "OData-MaxVersion": "4.0", + "OData-Version": "4.0", + "Accept": "application/json", + "Prefer": + 'odata.include-annotations="*",return=representation', + "Content-Type": "application/json", + "Authorization": "Bearer " + token.access_token, + }, + responseType: "stream", + }); + }; + + const { response, attempts } = await retry(fetchStream, 3, 1000); + + // Extract filename + const contentDisposition = response.headers["content-disposition"]; + const filename = contentDisposition + ? contentDisposition.split("filename=")[1] + : docRef.id; + + res.setHeader( + "Content-Disposition", + `attachment; filename=${filename}` + ); + res.setHeader("Content-Type", "application/octet-stream"); + + let totalBytes = 0; + + response.data.on("data", (chunk) => { + totalBytes += chunk.length; + }); + + response.data.pipe(res); + + response.data.on("end", () => { + const durationMs = Date.now() - startTime; + console.log( + `[Download Complete] Document: ${filename}, ID: ${docRef.id}, Size: ${totalBytes} bytes, Duration: ${durationMs}ms, Attempts: ${attempts}` + ); + }); + + response.data.on("error", (err) => { + consoleLogger(err); + if (!res.headersSent) res.redirect("/filenotavailable"); + }); + } catch (error) { + consoleLogger(error); + if (!res.headersSent) res.redirect("/filenotavailable"); + } +}; + +export const config = { + api: { + responseLimit: false, + }, +}; + +export default ApiProxy; diff --git a/pages/api/endpoint/getbasicsearchdetailspaged_api.js b/pages/api/endpoint/getbasicsearchdetailspaged_api.js index 1d841bab..417203c2 100644 --- a/pages/api/endpoint/getbasicsearchdetailspaged_api.js +++ b/pages/api/endpoint/getbasicsearchdetailspaged_api.js @@ -64,13 +64,16 @@ export default async function ApiProxy(req, res) { primaryIdAttribute ).NavigationProperty; + // "?$filter=_" + + // (primaryIdAttribute == "pinswg_sipscase" + // ? "pinswg_sipscase_value" + // : primaryIdAttribute + "s_value ") + + // " eq " + + // incidentID + + var queryUrl = appealTypeName + - "?$filter=_" + - (primaryIdAttribute == "pinswg_sipscase" - ? "pinswg_sipscase_value" - : primaryIdAttribute + "s_value ") + - " eq " + + "?$filter=" + incidentID + "&$count=true" + "&$expand=" + diff --git a/pages/dnsapplications.js b/pages/dnsapplications.js index eb859939..a1315141 100644 --- a/pages/dnsapplications.js +++ b/pages/dnsapplications.js @@ -124,38 +124,41 @@ export const getServerSideProps = wrapper.getServerSideProps( "public, s-maxage=10, stale-while-revalidate=59" ); - let searchResultsObj = await getBasicDNSSearch(); + // let searchResultsObj = await getBasicDNSSearch(); - searchResultsObj = - searchResultsObj.status == 502 - ? { - value: [], - errorCode: searchResultsObj.status, - errorMsg: searchResultsObj.statusText, - } - : searchResultsObj; + // searchResultsObj = + // searchResultsObj.status == 502 + // ? { + // value: [], + // errorCode: searchResultsObj.status, + // errorMsg: searchResultsObj.statusText, + // } + // : searchResultsObj; const showLoginCheck = process.env.SHOWLOGIN || false; const showMapCheck = process.env.SHOWMAPS || false; - if (_.has(searchResultsObj, "status") == true) { - return { - redirect: { - //destination: "/dns-not-found", - destination: "/error", - permanent: false, - }, - }; - } else { - const searchDetailsObj = await getSearchDetails( - searchResultsObj - ); - const dnsCoords = await getDNSCoords(); - store.dispatch(setSearchResults(searchResultsObj)); - store.dispatch(setSearchDetails(searchDetailsObj)); - store.dispatch(setSearch("DNS")); - store.dispatch(setDNSCoords(dnsCoords)); - } + // if (_.has(searchResultsObj, "status") == true) { + // return { + // redirect: { + // //destination: "/dns-not-found", + // destination: "/error", + // permanent: false, + // }, + // }; + // } else { + // // const searchDetailsObj = await getSearchDetails( + // // searchResultsObj + // // ); + // const dnsCoords = await getDNSCoords(); + // // store.dispatch(setSearchResults(searchResultsObj)); + // // store.dispatch(setSearchDetails(searchDetailsObj)); + // store.dispatch(setSearch("DNS")); + // store.dispatch(setDNSCoords(dnsCoords)); + // } + const dnsCoords = await getDNSCoords(); + store.dispatch(setSearch("DNS")); + store.dispatch(setDNSCoords(dnsCoords)); return { props: { diff --git a/pages/myportal/case/[ticketnumber].js b/pages/myportal/case/[ticketnumber].js index bbac2d10..de7e227a 100644 --- a/pages/myportal/case/[ticketnumber].js +++ b/pages/myportal/case/[ticketnumber].js @@ -106,8 +106,11 @@ const CaseHome = (props) => { props.awaitingSubmission.awaitingSubmission } caseReference={ - props.searchResultsObj.searchResultsObj.value[0] - .ticketnumber + props.currentView.caseReference.currentReference + } + ticketnumber={ + props.currentView.caseReference.ticketnumber || + router.query.ticketnumber } ticketnumber={ props.currentView.caseReference.ticketnumber || @@ -115,13 +118,9 @@ const CaseHome = (props) => { } currentType={props.currentType} appealTypeID={ - props.searchResultsObj.searchResultsObj.value[0] - .pinswg_appealcasetype - } - incidentid={ - props.searchResultsObj.searchResultsObj.value[0] - .incidentid + props.currentView.caseReference.appealType } + incidentid={props.currentView.caseReference.incidentid} representationsObj={ props.searchResultsObj.representationsObj } diff --git a/pages/myportal/index.js b/pages/myportal/index.js index 41ab4218..33540836 100644 --- a/pages/myportal/index.js +++ b/pages/myportal/index.js @@ -58,9 +58,10 @@ const Home = (props) => { showLoginCheck, currentView, mySubmittedReps, + docsOffline, } = props; - const { t } = useTranslation(); + let { t, lang } = useTranslation(); const router = useRouter(); const { locale, query } = router; const { appealtypes } = query; @@ -150,6 +151,7 @@ const Home = (props) => { containerID={accountDetails.containerID} showLoginCheck={showLoginCheck} currentView={currentView} + docsOffline={docsOffline} />