From 3f4bd8d52ed3738b4984d0ba279ffdc3863a58de Mon Sep 17 00:00:00 2001
From: robbond
Date: Fri, 28 Nov 2025 08:45:13 +0000
Subject: [PATCH] partial synch with ph2 updates
---
.env.local | 182 ++-
.gitignore | 6 +-
actions/azurestorage.js | 153 ++-
actions/index.js | 192 ++-
actions/selectQueryTypes.js | 2 +-
components/admin/tabs/accounts.js | 40 +
components/admin/tabs/appeals.js | 568 ++++++++
components/admin/tabs/documents.js | 1214 +++++++++++++++++
components/admin/tabs/storage.js | 266 ++++
components/admin/utils/adminHelper.js | 81 ++
components/admin/utils/prisma.js | 14 +
components/admin/utils/serverside.js | 166 +++
components/case/documents.js | 174 ++-
components/case/summary.js | 95 +-
components/dnssearchresults.js | 11 +-
components/elements/index.js | 7 +-
components/myportal.js | 7 +-
components/myportal/awaitingsubmission.js | 7 +-
.../myportal/awaitingsubmissionfromblob.js | 7 +-
components/myportal/makenewappeal.js | 65 +-
components/myportal/mycases.js | 6 +-
components/myportal/myrepresentations.js | 7 +-
.../myportal/myrepresentationsfromblob.js | 7 +-
.../myportal/mysubmittedrepresentations.js | 7 +-
components/myportal/servicebanner.js | 12 +-
components/myportal/watchedcases.js | 8 +-
components/search/addresssearchresults.js | 4 +-
components/search/searchresults.js | 2 +-
components/servicebanner.js | 6 +-
components/utils/downloadmanager.js | 48 +
components/utils/downloads.js | 325 +++++
components/utils/index.js | 80 +-
data/collections.json | 24 +-
i18n.js | 1 +
locales/cy/case.json | 6 +
locales/cy/myportal.json | 3 +-
locales/en/case.json | 6 +
locales/en/myportal.json | 3 +-
next-env.d.ts | 3 +-
pages/admin/index.js | 216 +++
pages/advancedsearchresults.js | 6 +-
pages/api/admin/getlatestdocuments_api.js | 180 +++
pages/api/admin/getnewappeals_api.js | 90 ++
pages/api/documents/download/[id].js | 295 ++--
.../endpoint/getbasicsearch_by_address_api.js | 2 +-
.../getbasicsearchdetailspaged_api.js | 34 +-
pages/auth/signin2.js | 29 +
pages/dns/[developmentName].js | 7 +
pages/dnsapplications.js | 66 +-
pages/myportal/case/[ticketnumber].js | 28 +-
pages/myportal/index.js | 5 +-
pages/searchresults.js | 47 +-
styles/sass/patterns/_card.scss | 14 +
styles/sass/welshgov/_application.scss | 49 +-
54 files changed, 4395 insertions(+), 488 deletions(-)
create mode 100644 components/admin/tabs/accounts.js
create mode 100644 components/admin/tabs/appeals.js
create mode 100644 components/admin/tabs/documents.js
create mode 100644 components/admin/tabs/storage.js
create mode 100644 components/admin/utils/adminHelper.js
create mode 100644 components/admin/utils/prisma.js
create mode 100644 components/admin/utils/serverside.js
create mode 100644 components/utils/downloadmanager.js
create mode 100644 components/utils/downloads.js
create mode 100644 pages/admin/index.js
create mode 100644 pages/api/admin/getlatestdocuments_api.js
create mode 100644 pages/api/admin/getnewappeals_api.js
diff --git a/.env.local b/.env.local
index fb79879b..548fde8a 100644
--- a/.env.local
+++ b/.env.local
@@ -13,31 +13,34 @@ HASHKEY = 028ffbae928d7191c0017f298260995f901d78eabde1436c0af0423459cc3715832136
# CRMURL = "devcrm2016.llcdt.gov.wales/DVPINS"
# //New Dev oauth ESNR
-# CLIENT_ID = 9b834097-03d7-409b-b038-ecfec31a394a
-# CLIENT_SECRET = .Jf8Q~tKMObCJjhh_hy3J5AIctFrBalU4FS~tdxk
-# RELAY_ROOT = https://ar-ns-lz-pedw-dev-uks-01.servicebus.windows.net/ar-hc-lz-pedw-dev-uks-01/
-# 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"
+CLIENT_ID = 9b834097-03d7-409b-b038-ecfec31a394a
+CLIENT_SECRET = .Jf8Q~tKMObCJjhh_hy3J5AIctFrBalU4FS~tdxk
+RELAY_ROOT = https://ar-ns-lz-pedw-dev-uks-01.servicebus.windows.net/ar-hc-lz-pedw-dev-uks-01/
+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
-# CLIENT_SECRET = 6hT8Q~glL~jloWpGmvXw3UY37QFwjUE0r._jobEM
-# RELAY_ROOT = https://ar-ns-lz-pedw-test-uks-01.servicebus.windows.net/ar-hc-lz-pedw-test-uks-01/
-# 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"
+CLIENT_ID = d40b9373-96c2-4b04-8bd8-c2a2fe6444c1
+CLIENT_SECRET = 6hT8Q~glL~jloWpGmvXw3UY37QFwjUE0r._jobEM
+RELAY_ROOT = https://ar-ns-lz-pedw-test-uks-01.servicebus.windows.net/ar-hc-lz-pedw-test-uks-01/
+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
-CLIENT_SECRET = V288Q~P1lFMvvX9Xnkx2pLixK~GOREH983Owqc1E
-RELAY_ROOT = https://ar-ns-lz-pedw-ppe-uks-01.servicebus.windows.net/ar-hc-lz-pedw-ppe-uks-01/
-RELAYURI = "ar-ns-lz-pedw-ppe-uks-01.servicebus.windows.net"
-RELAYPATH = "ar-hc-lz-pedw-ppe-uks-01"
-# RELAY_ROOT = https://ar-ns-lz-pedw-ppe-ukw-01.servicebus.windows.net/ar-hc-lz-pedw-ppe-ukw-01/
-# 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"
+# # //New PreProd
+# CLIENT_ID = ab6c4678-b31a-4eb3-b429-e039120e488a
+# CLIENT_SECRET = V288Q~P1lFMvvX9Xnkx2pLixK~GOREH983Owqc1E
+# RELAY_ROOT = https://ar-ns-lz-pedw-ppe-uks-01.servicebus.windows.net/ar-hc-lz-pedw-ppe-uks-01/
+# RELAYURI = "ar-ns-lz-pedw-ppe-uks-01.servicebus.windows.net"
+# RELAYPATH = "ar-hc-lz-pedw-ppe-uks-01"
+# # RELAY_ROOT = https://ar-ns-lz-pedw-ppe-ukw-01.servicebus.windows.net/ar-hc-lz-pedw-ppe-ukw-01/
+# # 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
@@ -48,6 +51,57 @@ CRMURL = "ppcrm2016.llcdt.gov.wales/PPPINSWales"
# RELAYPATH = "prod-pedw-hc"
# 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.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
GOOGLE_TAG_MANAGER = G-GTWW3JT03Z
@@ -58,8 +112,9 @@ 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"
-SHOWFILTERDOCS = false
+DOCAPI_OFFLINE = "07:00:00,14:00:00"
+# DOCAPI_OFFLINE = "01:00:00,04:00:00"
+SHOWFILTERDOCS = true
// Next Auth vars
@@ -68,18 +123,35 @@ NEXTAUTH_URL_INTERNAL = $API_ROOT
NEXTAUTH_SECRET = SuperSecret
// CNR sql server
-# DATABASE_URL = sqlserver://sql-srv-plt-pedw-cnr-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-cnr-uks-01.database.windows.net:1433;database=PEDW;user=PedwSqlAdmin;password=GhWKr62F5sQ69dZx;encrypt=true;trustServerCertificate=true;hostNameInCertificate=*.database.windows.net;loginTimeout=30;authentication=ActiveDirectoryPassword
// 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
+
// Pre prod sql server
-DATABASE_URL = sqlserver://sql-srv-plt-pedw-ppe-uks-01.database.windows.net:1433;database=PEDW;user=PedwSqlAdmin;password=GhWKr62F5sQ69dZx;encrypt=true;trustservercertificate=true;hostnameincertificate=*.database.windows.net;logintimeout=30;authentication=SqlPassword
+# DATABASE_URL = sqlserver://sql-srv-plt-pedw-ppe-uks-01.database.windows.net:1433;database=PEDW;user=PedwSqlAdmin;password=GhWKr62F5sQ69dZx;encrypt=true;trustservercertificate=true;hostnameincertificate=*.database.windows.net;logintimeout=30;authentication=SqlPassword
// Prod sql server
# 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
+
+// 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
@@ -91,24 +163,24 @@ AZURE_TENANT_ID = $TENANT
AZURE_CLIENT_SECRET = $CLIENT_SECRET
// CNR key
-# AZURE_STORAGE_ACCOUNT_NAME = "sapltpedwcnruks"
-# AZURE_STORAGE_ACCOUNT_KEY = hRffHq4IwctpaTKdS/U33Xq6nyTDF1t++WJgIwsw7gzstktZin/rRwbTIAL9KKqesVsK7EHrTTG6+ASt4+7skw==
-# AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwcnruks.blob.core.windows.net
+AZURE_STORAGE_ACCOUNT_NAME = "sapltpedwcnruks"
+AZURE_STORAGE_ACCOUNT_KEY = hRffHq4IwctpaTKdS/U33Xq6nyTDF1t++WJgIwsw7gzstktZin/rRwbTIAL9KKqesVsK7EHrTTG6+ASt4+7skw==
+AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwcnruks.blob.core.windows.net
// new dev key
-# AZURE_STORAGE_ACCOUNT_NAME = "sapltpedwdevuks"
-# AZURE_STORAGE_ACCOUNT_KEY = MmrsrCjBfop9pI2oImsP/+lJUWU2DrXsAB8nq5bOVROTmPovSGxjCjKw9+TAFr00k8WDUK9r6mVx+AStcMLyAA==
-# AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwdevuks.blob.core.windows.net
+AZURE_STORAGE_ACCOUNT_NAME = "sapltpedwdevuks"
+AZURE_STORAGE_ACCOUNT_KEY = MmrsrCjBfop9pI2oImsP/+lJUWU2DrXsAB8nq5bOVROTmPovSGxjCjKw9+TAFr00k8WDUK9r6mVx+AStcMLyAA==
+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_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"
-AZURE_STORAGE_ACCOUNT_KEY = B4GRKr+cA9b9mNM/GHFivzhzflJIZltG8bMmooXlYvGYySPAspTTHistAT30sHipnR+hh6dqUc9q+ASttAjPRQ==
-AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwppeuks.blob.core.windows.net
+# AZURE_STORAGE_ACCOUNT_NAME = "sapltpedwppeuks"
+# AZURE_STORAGE_ACCOUNT_KEY = B4GRKr+cA9b9mNM/GHFivzhzflJIZltG8bMmooXlYvGYySPAspTTHistAT30sHipnR+hh6dqUc9q+ASttAjPRQ==
+# AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwppeuks.blob.core.windows.net
// PROD key
@@ -116,6 +188,34 @@ AZURE_PEDW_STORAGE_ENDPOINT = https://sapltpedwppeuks.blob.core.windows.net
# AZURE_STORAGE_ACCOUNT_KEY = koZ46STRqapSa2Dod8MLL6aju1eYLhhyJ6DXpCLMTnFLJvbDT6CHUv/jwr5U/Ta8a60MWzwToHQy+AStADO7sg==
# 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
+
+# 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"
AZURE_PEDW_QUEUE_ENDPOINT = https://$AZURE_STORAGE_ACCOUNT_NAME.queue.core.windows.net
@@ -126,7 +226,7 @@ NOTIFY_API_KEY = pedwnextauthlivekey-90f93710-9dae-423e-aea1-11eccd0dc132-5c0ed1
SHOW_DEBUG = true
-
+
// DEV Connection string
//NEXT_PUBLIC_APP_INSIGHT = InstrumentationKey=4b7568b3-70bd-4591-9dc2-267c0a6501d1;IngestionEndpoint=https://ukwest-0.in.applicationinsights.azure.com/;LiveEndpoint=https://ukwest.livediagnostics.monitor.azure.com/;ApplicationId=447d38b5-5d40-4238-8fb3-0ef4741b3db1
@@ -146,4 +246,8 @@ NEXT_PUBLIC_APP_INSIGHT = InstrumentationKey=a41b91bf-f7dc-4f44-bb66-a67df4eee43
//NEXT_PUBLIC_APP_INSIGHT = InstrumentationKey=61f358c2-8075-4164-b897-17bc6dd31fee;IngestionEndpoint=https://ukwest-0.in.applicationinsights.azure.com/;LiveEndpoint=https://ukwest.livediagnostics.monitor.azure.com/;ApplicationId=0c297349-396b-4dc6-8b56-85f5c680adc6
-HIDEERRORLOGS = false
\ No newline at end of file
+HIDEERRORLOGS = false
+
+SHOWSIPS = true
+
+ALLOWED_IPS=::1,203.0.113.42,198.51.100.17
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 6b1791c1..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=" +
@@ -309,7 +309,7 @@ export const createRepBlob = async (formContent, containerName, caseref) => {
const tags = {
containerid: containerName,
- caseID: formContent.caseRef,
+ caseID: formContent.ticketnumber,
blobType: "Representation",
};
@@ -317,7 +317,7 @@ export const createRepBlob = async (formContent, containerName, caseref) => {
const withTags = await blockBlobClient.setTags(tags);
const withMeta = await blockBlobClient.setMetadata(tags);
- return formContent.pinswg_name;
+ return formContent.ticketnumber;
};
export const createAppealPDFBlob = async (
@@ -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;
};
@@ -1702,3 +1700,100 @@ export const listBlobHierarchical = async (
}
}
};
+
+function formatTimeSince(date) {
+ if (!date) return "Unknown date";
+
+ const now = new Date();
+ const diffMs = now - date;
+ const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
+
+ if (diffDays < 7) {
+ return `${diffDays} day${diffDays !== 1 ? "s" : ""} ago`;
+ } else {
+ const diffWeeks = Math.floor(diffDays / 7);
+ return `${diffWeeks} week${diffWeeks !== 1 ? "s" : ""} ago`;
+ }
+}
+
+function formatDateDisplay(date) {
+ return date
+ ? `${date.toLocaleString("en-GB")} (${formatTimeSince(date)})`
+ : "Unknown";
+}
+
+export async function listBlobHierarchicalForUser(containerClient) {
+ const blobNames = [];
+ for await (const blob of containerClient.listBlobsFlat()) {
+ const lastModifiedDate = blob.properties?.lastModified || null;
+ const createdOnDate = blob.properties?.createdOn || null;
+
+ let displayDate = "";
+
+ if (createdOnDate && lastModifiedDate) {
+ if (
+ createdOnDate.toDateString() === lastModifiedDate.toDateString()
+ ) {
+ // Same day — show just one
+ displayDate = `Date: ${formatDateDisplay(lastModifiedDate)}`;
+ } else {
+ // Different days — show both
+ displayDate = `Created: ${formatDateDisplay(
+ createdOnDate
+ )}, Modified: ${formatDateDisplay(lastModifiedDate)}`;
+ }
+ } else if (lastModifiedDate) {
+ displayDate = `Modified: ${formatDateDisplay(lastModifiedDate)}`;
+ } else if (createdOnDate) {
+ displayDate = `Created: ${formatDateDisplay(createdOnDate)}`;
+ } else {
+ displayDate = "Unknown date";
+ }
+
+ blobNames.push({
+ name: blob.name,
+ size: blob.properties.contentLength || 0,
+ lastModified: lastModifiedDate
+ ? lastModifiedDate.toISOString()
+ : null,
+ createdOn: createdOnDate ? createdOnDate.toISOString() : null,
+ displayDate,
+ });
+ }
+ return blobNames;
+}
+
+export async function listContainersForUser(
+ blobServiceClient,
+ containerNamePrefix,
+ emailAddress
+) {
+ const options = {
+ includeDeleted: false,
+ includeMetadata: true,
+ includeSystem: true,
+ prefix: containerNamePrefix,
+ };
+
+ const results = [];
+
+ for await (const containerItem of blobServiceClient.listContainers(
+ options
+ )) {
+ const containerClient = blobServiceClient.getContainerClient(
+ containerItem.name
+ );
+ const blobNames = await listBlobHierarchicalForUser(
+ containerClient
+ ).catch(console.error);
+
+ if (blobNames && blobNames.length > 0) {
+ results.push({
+ container: containerItem.name,
+ blobs: blobNames,
+ });
+ }
+ }
+
+ return results; // array of non-empty containers
+}
diff --git a/actions/index.js b/actions/index.js
index 072e4fa0..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) => {
@@ -828,6 +847,19 @@ export const getMyCases = (loggedInUserId) => {
});
};
+export const getMyInvolvements = async (loggedInUserId) => {
+ try {
+ const res = await axios.get(
+ BASE_URL +
+ "/api/endpoint/getmyinvolvements_api?loggedInUserId=" +
+ loggedInUserId
+ );
+ return res.data;
+ } catch (error) {
+ consoleLogger(error);
+ }
+};
+
export const getMyLPACases = (lpaid) => {
return axios
.get(BASE_URL + "/api/endpoint/getmylpacases_api?lpaid=" + lpaid)
@@ -1492,23 +1524,20 @@ export const deleteMyRepresentationsFromBlob = (
});
};
-export const deleteWatchedCases = (watchedCaseID) => {
+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,
};
- return axios(config)
- .then((res) => {
- //console.log("deleted ", res.data);
- return res.data;
- })
- .catch((error) => {
- consoleLogger(error);
- });
+ try {
+ const res = await axios(config);
+ return res.data;
+ } catch (error) {
+ consoleLogger(error);
+ }
};
export const createAccount = (formValues) => {
@@ -1813,9 +1842,9 @@ export const deleteBlob = async (
"/api/file/deleteblob?container=" +
containerName +
"&casefolderID=" +
- casefolderID +
+ encodeURIComponent(casefolderID) +
"&blobname=" +
- blobName +
+ encodeURIComponent(blobName) +
deleteblobhash
);
return res.data;
@@ -1836,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;
@@ -1894,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;
@@ -2209,3 +2236,92 @@ 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");
+ return res.data;
+ } catch (error) {
+ consoleLogger(error);
+ return error.response;
+ }
+};
+
+export const getNewAppealsPage = async (
+ searchString,
+ pageNumber,
+ orderBy,
+ fieldSort,
+ showNumberOfRecords
+) => {
+ return axios
+ .get(
+ "/api/admin/getnewappeals_api?searchString=" +
+ searchString +
+ "&pageNumber=" +
+ pageNumber +
+ "&orderby=" +
+ orderBy +
+ "&fieldSort=" +
+ fieldSort +
+ "&showNumberOfRecords=" +
+ showNumberOfRecords
+ )
+ .then((res) => {
+ return res.data;
+ })
+ .catch((error) => {
+ consoleLogger(error);
+ return error.response;
+ });
+};
+
+export const getNewDocumentsPaged = async (
+ pageNumber,
+ orderBy,
+ 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=" +
+ pageNumber +
+ "&orderby=" +
+ orderBy +
+ "&fieldSort=" +
+ fieldSort +
+ "&showNumberOfRecords=" +
+ showNumberOfRecords +
+ "&documentType=" +
+ documentType +
+ "&numberWeeks=" +
+ selectedWeeks +
+ "&documentOrigin=" +
+ documentOrigin
+ )
+ .then((res) => {
+ return res.data;
+ })
+ .catch((error) => {
+ consoleLogger(error);
+ return error.response;
+ });
+};
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
new file mode 100644
index 00000000..8d802860
--- /dev/null
+++ b/components/admin/tabs/accounts.js
@@ -0,0 +1,40 @@
+// components/admin/tabs/AccountsTab.js
+import Link from "next/link";
+
+export default function AccountsTab({ userData }) {
+ return (
+
+
User Accounts
+
Number of accounts created: {userData.length}
+
+
+
+
-
+ Name
+
+ -
+ Last accessed
+
+
+
+ {userData.map((user) => (
+
+
-
+
+ {user.email}
+
+
+ {user.id}
+
+ -
+ Last accessed: {user.created}
+
+
+ ))}
+
+
+ );
+}
diff --git a/components/admin/tabs/appeals.js b/components/admin/tabs/appeals.js
new file mode 100644
index 00000000..bfa2115e
--- /dev/null
+++ b/components/admin/tabs/appeals.js
@@ -0,0 +1,568 @@
+// components/admin/tabs/AppealsTab.js
+import useTranslation from "next-translate/useTranslation";
+import { useCallback, useState, useEffect } from "react";
+import { JSONPath as jsonpath } from "jsonpath-plus";
+import Link from "next/link";
+import { useRouter } from "next/router";
+import PaginationControl from "../../../components/search/pagination";
+import { connect } from "react-redux";
+
+import {
+ setSearchResults,
+ setSearchDetails,
+} from "../../../store/searchOutput/action";
+
+import { getDetailsProxy, getSearchDetailsPaged } from "../../utils";
+import { getAdvancedSearchPaged, getNewAppealsPage } from "../../../actions";
+import {
+ setCurrentPage,
+ setCurrentReference,
+} from "../../../store/currentView/action";
+
+function AppealsTab(props) {
+ const [searchLoaded, setSearchLoaded] = useState(false);
+ const [showSpinnerState, setShowSpinnerState] = useState(false);
+ const [selectedOption, setSelectedOption] = useState(10);
+ const [orderByState, setOrderbyState] = useState("createdon");
+ const [fieldSortState, setfieldSortState] = useState("desc");
+ const [loginToWatch, setLoginToWatch] = useState("true");
+
+ const searchDetailsObj = props.searchResultsObj.searchDetailsObj || {};
+ const resultsArr = props.searchResultsObj.searchResultsObj.value || [];
+ const searchResultsObj = props.searchResultsObj.searchResultsObj;
+
+ const { t } = useTranslation();
+ const router = useRouter();
+ const myportal = false;
+ const advancedSearch = false;
+ const searchString = "";
+
+ const spinnerState = () => {
+ setShowSpinnerState((prev) => !prev);
+ };
+
+ const pagesCount = Math.ceil(searchResultsObj["@odata.count"] / 10);
+
+ let currentPage = 0;
+ try {
+ currentPage = decodeURI(searchResultsObj["@odata.nextLink"]).split(
+ "skiptoken="
+ )[1];
+ currentPage =
+ typeof currentPage !== "undefined"
+ ? parseInt(
+ decodeURI(currentPage)
+ .split('pagenumber="')[1]
+ .slice(
+ 0,
+ decodeURI(currentPage)
+ .split('pagenumber="')[1]
+ .indexOf('"')
+ ) - 1
+ )
+ : pagesCount;
+ } catch (e) {
+ currentPage = pagesCount;
+ }
+
+ const getSearchPageResults = useCallback(
+ (pageNumber, orderBy, fieldSort) => {
+ getNewAppealsPage(
+ searchString,
+ pageNumber,
+ orderBy,
+ fieldSort,
+ selectedOption
+ ).then((data) => {
+ props.setSearchResults(data); // ✅ use props, not import
+ return getSearchDetailsPaged(data).then((details) => {
+ props.setSearchDetails(details); // ✅ use props, not import
+ setShowSpinnerState(false);
+ });
+ });
+ },
+ [
+ advancedSearch,
+ searchString,
+ selectedOption,
+ props.setSearchDetails,
+ props.setSearchResults,
+ ]
+ );
+
+ useEffect(() => {
+ if (searchLoaded) {
+ if (selectedOption) {
+ setShowSpinnerState(true);
+ getSearchPageResults(1, orderByState, fieldSortState);
+ }
+ } else {
+ setSearchLoaded(true);
+ }
+ }, [
+ selectedOption,
+ fieldSortState,
+ orderByState,
+ searchLoaded,
+ getSearchPageResults,
+ router.query,
+ ]);
+
+ const ResultsView = (resultsArr) => {
+ return (
+ <>
+
+
+
-
+
+
+ -
+ {t("search:searchresults-site-address-label")}
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+ {/* {t("search:searchresults-status-label")} */}
+
+
+
+ -
+ Origin
+
+
+ {showSpinnerState == true ? (
+
+
+ {t("common:spinner-fetching-data")}
+

+
+
+ ) : (
+ resultsArr.map((item, key) => {
+ let detailsObj = jsonpath({
+ path:
+ '$..[?(@ && @.ticketnumber=="' +
+ item.ticketnumber +
+ '")]',
+ json: searchDetailsObj,
+ eval: true,
+ });
+
+ detailsObj = detailsObj[0] || {};
+ return (
+
+
+
+ {item.pinswg_publishtoweb == true ? (
+ {
+ setCurrentReference({
+ "ticketnumber":
+ item.ticketnumber,
+ "currentReference":
+ item.title,
+ "currentType":
+ "searchResultsObj",
+ "incidentid":
+ item.incidentid,
+ "appealType":
+ item.pinswg_appealcasetype,
+ "showLoginCheck":
+ props.showLoginCheck,
+ "specialistProcess":
+ detailsObj.hasOwnProperty(
+ "pinswg_speacialistcaseprocess"
+ )
+ ? detailsObj.pinswg_speacialistcaseprocess
+ : detailsObj.hasOwnProperty(
+ "pinswg_specialistcaseprocess"
+ )
+ ? detailsObj.pinswg_specialistcaseprocess
+ : "",
+ });
+ }}
+ >
+
+ {t(
+ "search:searchresults-case-reference-label"
+ )}
+ :
+
+ {item.title}
+
+ ) : (
+ item.title
+ )}
+
+
+ {new Date(
+ item.createdon
+ ).toLocaleString()}
+
+
+
+ {t(
+ "search:searchresults-site-address-label"
+ )}
+ :
+
+ {_.has(
+ 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]
+ : ""
+ : ""}
+ {_.has(
+ detailsObj,
+ "pinswg_siteaddressline1"
+ )
+ ? detailsObj.pinswg_siteaddressline1
+ : ""}
+
+ {/* {detailsObj.pinswg_siteaddressline1 !=
+ null &&
} */}
+
+ {_.has(
+ detailsObj,
+ "pinswg_siteaddressline1"
+ ) &&
+ detailsObj.pinswg_siteaddressline1 !=
+ null &&
}
+
+ {_.has(
+ detailsObj,
+ "pinswg_siteaddressline1"
+ )
+ ? detailsObj.pinswg_siteaddressline2
+ : ""}
+
+ {_.has(
+ detailsObj,
+ "pinswg_siteaddressline2"
+ ) &&
+ detailsObj.pinswg_siteaddressline2 !=
+ null &&
}
+ {_.has(
+ detailsObj,
+ "pinswg_siteaddresstown"
+ )
+ ? detailsObj.pinswg_siteaddresstown
+ : ""}
+ {_.has(
+ detailsObj,
+ "pinswg_siteaddresstown"
+ ) &&
+ detailsObj.pinswg_siteaddresstown !=
+ null &&
}
+
+ {_.has(
+ detailsObj,
+ "pinswg_siteaddresscounty"
+ )
+ ? detailsObj.pinswg_siteaddresscounty
+ : ""}
+ {_.has(
+ detailsObj,
+ "pinswg_siteaddresscounty"
+ ) &&
+ detailsObj.pinswg_siteaddresscounty !=
+ null &&
}
+ {_.has(
+ detailsObj,
+ "pinswg_siteaddresspostcode"
+ )
+ ? detailsObj.pinswg_siteaddresspostcode
+ : ""}
+
+
+
+ {t(
+ "search:searchresults-applicant-label"
+ )}
+ :
+
+ {item.pinswg_appellantagent == 846040001
+ ? item.pinswg_appellantfirstname +
+ " " +
+ item.pinswg_appellantlastname
+ : _.has(detailsObj, [
+ "_pinswg_appellant_value@OData.Community.Display.V1.FormattedValue",
+ ])
+ ? detailsObj[
+ "_pinswg_appellant_value@OData.Community.Display.V1.FormattedValue"
+ ]
+ : ""}
+
+
+
+ {t(
+ "search:searchresults-authority-label"
+ )}
+ :
+
+ {_.has(item, [
+ "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue",
+ ])
+ ? router.locale == "cy"
+ ? jsonpath({
+ path:
+ '$..[?(@ && @.value=="' +
+ item[
+ "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue"
+ ] +
+ '")].value_cy',
+ json: transLookup,
+ eval: true,
+ })
+ : item[
+ "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue"
+ ] || "N/A"
+ : ""}
+
+
+
+ {t(
+ "search:searchresults-case-type-label"
+ )}
+ :
+
+ {router.locale == "cy"
+ ? jsonpath({
+ path:
+ '$..[?(@ && @.value=="' +
+ item[
+ "pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue"
+ ] +
+ '")].value_cy',
+ json: transLookup,
+ eval: true,
+ })
+ : item[
+ "pinswg_appealcasetype@OData.Community.Display.V1.FormattedValue"
+ ] || "N/A"}
+
+
+
+ {t(
+ "search:searchresults-status-label"
+ )}
+ :
+
+
+ {router.locale == "cy"
+ ? jsonpath({
+ path:
+ '$..[?(@ && @.value=="' +
+ item[
+ "statuscode@OData.Community.Display.V1.FormattedValue"
+ ] +
+ '")].value_cy',
+ json: transLookup,
+ eval: true,
+ })
+ : item[
+ "statuscode@OData.Community.Display.V1.FormattedValue"
+ ] || "N/A"}
+
+
+ {item[
+ "caseorigincode@OData.Community.Display.V1.FormattedValue"
+ ] || "N/A"}
+
+
+ );
+ })
+ )}
+
+
+ >
+ );
+ };
+
+ return (
+
+
Appeals
+ {searchResultsObj["@odata.count"] > 5 && (
+
+
+
+
+
+ )}
+ {resultsArr.length > 0 ? (
+ ResultsView(resultsArr)
+ ) : (
+
+
+
{t("search:searchresults-no-records-label")}
+
+
+ )}
+
+ );
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ setCurrentReference: (currentReference) => {
+ dispatch(setCurrentReference(currentReference));
+ },
+ setSearchResults: (searchResultsObj) => {
+ dispatch(setSearchResults(searchResultsObj));
+ },
+ setSearchDetails: (searchDetailsObj) => {
+ dispatch(setSearchDetails(searchDetailsObj));
+ },
+ setCurrentPage: (currentPage) => {
+ dispatch(setCurrentPage(currentPage));
+ },
+ };
+};
+
+const mapStateToProps = (state) => {
+ return {
+ searchResultsObj: state.searchResultsObj,
+ };
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(AppealsTab);
diff --git a/components/admin/tabs/documents.js b/components/admin/tabs/documents.js
new file mode 100644
index 00000000..94bc359a
--- /dev/null
+++ b/components/admin/tabs/documents.js
@@ -0,0 +1,1214 @@
+import { JSONPath as jsonpath } from "jsonpath-plus";
+import useTranslation from "next-translate/useTranslation";
+import Link from "next/link";
+import { useRouter } from "next/router";
+import { useCallback, useEffect, useRef, useState } from "react";
+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 {
+ getSearchDocumentDetails,
+ getSearchDocumentDetailsPaged,
+ getSearchDocumentTypes,
+ getNewDocumentsPaged,
+} from "../../../actions";
+import { setCurrentPage } from "../../../store/currentView/action";
+
+import { setDocumentDetails } from "../../../store/searchOutput/action";
+const getDocumentTypes = [
+ {
+ "Value": 846040009,
+ "Label": "Main Party - Pre-Submission Docs",
+ },
+ {
+ "Value": 846040000,
+ "Label": "Main Party - Submission Document",
+ },
+ {
+ "Value": 846040001,
+ "Label": "Main Party - Questionnaire",
+ },
+ {
+ "Value": 846040002,
+ "Label": "Main Party - Statements",
+ },
+ {
+ "Value": 846040003,
+ "Label": "Main Party - Comments",
+ },
+ {
+ "Value": 846040004,
+ "Label": "Main Party - Written Statements of Evidence",
+ },
+ {
+ "Value": 846040010,
+ "Label": "Main Party - Environmental Statements",
+ },
+ {
+ "Value": 846040005,
+ "Label": "Interested Party Representations",
+ },
+ {
+ "Value": 846040006,
+ "Label": "Correspondence",
+ },
+ {
+ "Value": 846040008,
+ "Label": "Inspector Notes",
+ },
+ {
+ "Value": 846040007,
+ "Label": "Final Decision/Report",
+ },
+ {
+ "Value": 846040011,
+ "Label": "Post Decision Correspondence",
+ },
+ {
+ "Value": 846040012,
+ "Label": "High Court Challenge Correspondence",
+ },
+ {
+ "Value": 846040013,
+ "Label": "Event - Correspondence",
+ },
+ {
+ "Value": 846040014,
+ "Label": "Event - Documents",
+ },
+ {
+ "Value": 846040015,
+ "Label": "Pre-Application - General",
+ },
+ {
+ "Value": 846040016,
+ "Label": "Pre-Application - EIA Screening",
+ },
+ {
+ "Value": 846040017,
+ "Label": "Pre-Application - EIA Scoping",
+ },
+ {
+ "Value": 846040018,
+ "Label": "EIA Screening",
+ },
+ {
+ "Value": 846040019,
+ "Label": "EIA Scoping",
+ },
+ {
+ "Value": 846040020,
+ "Label": "Pre-Application Services Documents",
+ },
+ {
+ "Value": 846040021,
+ "Label": "Notification",
+ },
+ {
+ "Value": 846040022,
+ "Label":
+ "Submission - Environmental Statement - written statement and NTS",
+ },
+ {
+ "Value": 846040023,
+ "Label": "Submission - Environmental Statement - Appendices",
+ },
+ {
+ "Value": 846040024,
+ "Label": "Submission - Environmental Statement - Figures",
+ },
+ {
+ "Value": 846040025,
+ "Label": "Submission - Draft Infrastructure Consent Orders",
+ },
+ {
+ "Value": 846040026,
+ "Label": "Submission - Compulsory Acquisition Information",
+ },
+ {
+ "Value": 846040027,
+ "Label": "Submission - Application Documents",
+ },
+ {
+ "Value": 846040028,
+ "Label": "Examination Notices",
+ },
+ {
+ "Value": 846040029,
+ "Label": "Local/Marine Impact Reports",
+ },
+ {
+ "Value": 846040030,
+ "Label": "Submission - Further Information",
+ },
+ {
+ "Value": 846040031,
+ "Label": "Formal Variation requests",
+ },
+ {
+ "Value": 846040032,
+ "Label": "Further Information - Interested Party Representations",
+ },
+ {
+ "Value": 846040033,
+ "Label": "Hearing Statements",
+ },
+ {
+ "Value": 846040034,
+ "Label": "Events - Recordings",
+ },
+ {
+ "Value": 846040035,
+ "Label": "Internal Correspondence",
+ },
+ {
+ "Value": 846040036,
+ "Label": "Consultation Response",
+ },
+];
+
+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);
+ const {
+ incidentid,
+ caseReference,
+ searchResultsObj,
+ searchDetailsObj,
+ documentDetailsObj,
+ setDocumentDetails,
+ setCurrentPage,
+ docsOffline,
+ showFilteredDocs,
+ } = props;
+
+ const router = useRouter();
+ 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) => {
+ acc[item.pinswg_isharedocumentlocations] = false;
+ return acc;
+ }, {})
+ );
+
+ const [checkedOriginItems, setCheckedOriginItems] = useState(
+ documentOrigin.reduce((acc, item) => {
+ acc[item.pinswg_origin] = false;
+ return acc;
+ }, {})
+ );
+
+ const handleCheckboxChange = (e) => {
+ const { name, checked } = e.target;
+
+ setCheckedItems((prev) => ({
+ ...prev,
+ [name]: checked,
+ }));
+ 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
+ if (checked) {
+ // If 'Select All' is checked, check all the other checkboxes
+
+ setCheckedItems(
+ documentTypes.reduce((acc, item) => {
+ acc[item.pinswg_isharedocumentlocations] = true;
+ return acc;
+ }, {})
+ );
+ } else {
+ clearCheckboxes();
+ }
+ };
+
+ 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;
+
+ const getDocLink = (docsOffline) => {
+ 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;
+ };
+
+ checkShowDocLinks && getDocLink(docsOffline);
+
+ const FilteredDocumentView = (documentDetailsArr) => {
+ documentDetailsArr = documentDetailsArr.value;
+
+ return (
+ <>
+ {ShowDocLinks && (
+
+
+ !
+
+
+
+ Notice
+
+
+ {t("case:documents-not-available-label")}
+
+
+ )}
+
+
+
+ {" "}
+
+
+ {documentDetailsObj["@odata.count"] > 1
+ ? t("case:documents-count-label-1")
+ : t("case:documents-count-label-1a")}{" "}
+ {documentDetailsObj["@odata.count"]}{" "}
+ {documentDetailsObj["@odata.count"] > 1
+ ? t("case:documents-count-label-2")
+ : t("case:documents-count-label-2a")}
+
+
+
+
+
+ Document Type
+
+
{" "}
+
+ Document Origin
+
+
{" "}
+
+
+
+ {documentDetailsObj["@odata.count"] > 5 && (
+
+
+
+
+
+ )}
+
+
+
+
+
+
+ {_.isEmpty(documentDetailsObj.value) ? (
+
+
+
+ {/* {showDocumentSearchState == false
+ ? t("case:documents-checking-label")
+ : t(
+ "case:documents-non-available-label"
+ )} */}
+ No documents found
+
+
+
+ ) : (
+ <>
+
+
+
-
+
+
+ -
+ {/* {t("case:documents-doc-type-label")} */}
+
+
+ -
+ {/* {t("case:documents-date-published-label")} */}
+
+
+ -
+ {/* {t("case:documents-date-published-label")} */}
+ Case
+
+ -
+ {/* {t("case:documents-date-published-label")} */}
+ Origin
+
+ -
+ {/* {t("case:documents-date-published-label")} */}
+ Published
+
+
+ {/* -
+ {t("case:documents-size-label")}
+
*/}
+
+ {showSpinnerState == true ? (
+
+
+ {t(
+ "common:spinner-fetching-data"
+ )}
+

+
+
+ ) : (
+ documentDetailsArr.map((item, key) => {
+ let detailsObj = jsonpath({
+ path:
+ "$..[?(@ && @._pinswg_documentids_value=='" +
+ incidentid +
+ "')]",
+ json: item,
+ eval: true,
+ });
+ detailsObj = item;
+ return (
+
+
-
+ {showDownloadingState ===
+ key && (
+
+ {/*

*/}
+
+ )}
+
+ {!ShowDocLinks ? (
+ detailsObj.pinswg_publishtoweb ? (
+ // {
+ // toggleDownLoadLink(
+ // key
+ // ),
+ // sendGAEvent(
+ // "event",
+ // "DownloadedFile",
+ // {
+ // caseReference:
+ // props.caseReference,
+ // filename:
+ // detailsObj.pinswg_name,
+ // }
+ // );
+ // }}
+ // >
+ //
+ // {t(
+ // "case:documents-name-label"
+ // )}
+ // :
+ //
+ // {_.has(
+ // detailsObj,
+ // "pinswg_name"
+ // )
+ // ? detailsObj.pinswg_name
+ // : ""}
+ //
+
+ ) : (
+ <>
+
+ {t(
+ "case:documents-name-label"
+ )}
+ :
+
+ {_.has(
+ detailsObj,
+ "pinswg_name"
+ )
+ ? detailsObj.pinswg_name
+ : ""}
+ >
+ )
+ ) : (
+ <>
+
+ {t(
+ "case:documents-name-label"
+ )}
+ :
+
+ {_.has(
+ detailsObj,
+ "pinswg_name"
+ )
+ ? detailsObj.pinswg_name
+ : ""}
+ >
+ )}
+
+
-
+
+ {t(
+ "case:documents-doc-type-label"
+ )}
+ :
+
+
+ {router.locale == "cy"
+ ? jsonpath({
+ path:
+ '$..[?(@ && @.value=="' +
+ detailsObj[
+ "pinswg_isharedocumentlocations@OData.Community.Display.V1.FormattedValue"
+ ] +
+ '")].value_cy',
+ json: transLookup,
+ eval: true,
+ })
+ : detailsObj[
+ "pinswg_isharedocumentlocations@OData.Community.Display.V1.FormattedValue"
+ ] ||
+ t(
+ "case:summary-no-date-entered-label"
+ )}
+
+
-
+
+ {t(
+ "case:documents-date-published-label"
+ )}
+ :
+
+
+ {_.has(
+ detailsObj,
+ "createdon"
+ )
+ ? new Date(
+ detailsObj.createdon
+ ).toLocaleString()
+ : ""}
+
+
-
+ {detailsObj.publishtoweb ==
+ true ? (
+
+ {
+ detailsObj[
+ "_pinswg_documentids_value@OData.Community.Display.V1.FormattedValue"
+ ]
+ }
+
+ ) : (
+ detailsObj[
+ "_pinswg_documentids_value@OData.Community.Display.V1.FormattedValue"
+ ]
+ )}
+
+
-
+ {
+ detailsObj[
+ "pinswg_origin@OData.Community.Display.V1.FormattedValue"
+ ]
+ }
+
+
-
+ {
+ detailsObj[
+ "pinswg_publishtoweb@OData.Community.Display.V1.FormattedValue"
+ ]
+ }
+
+
+ {/*
-
+
+ {t("case:documents-size-label")}:
+
+ 92
+
*/}
+
+ );
+ })
+ )}
+
+
+ >
+ )}
+
+
+ >
+ );
+ };
+
+ const getDocumentResults = useCallback(
+ (pageNumber, orderBy, fieldSort) => {
+ getNewDocumentsPaged(
+ pageNumber,
+ orderBy,
+ fieldSort,
+ selectedOption,
+ selectedDocumentType,
+ selectedDocumentOrigin,
+ selectedWeeks
+ )
+ .then((data) => data)
+ .then((data) => {
+ props.setDocumentDetails(data);
+ setShowSpinnerState(false);
+ });
+ },
+ [
+ props.incidentid,
+ selectedOption,
+ selectedDocumentType,
+ selectedWeeks,
+ selectedDocumentOrigin,
+ props.setDocumentDetails,
+ ]
+ );
+
+ const getDocumentDetails = async () => {
+ let docObj = await getSearchDocumentDetailsPaged(
+ incidentid,
+ pageNumber,
+ orderBy,
+ fieldSort
+ ).then((data) => {
+ props.setDocumentDetails(data);
+ return data;
+ });
+
+ return docObj;
+ };
+
+ const pagesCount = Math.ceil(documentDetailsObj["@odata.count"] / 10);
+
+ let currentPage = 0;
+
+ currentPage = decodeURI(documentDetailsObj["@odata.nextLink"]).split(
+ "skiptoken="
+ )[1];
+ currentPage =
+ typeof currentPage != "undefined"
+ ? parseInt(
+ decodeURI(currentPage)
+ .split('pagenumber="')[1]
+ .slice(
+ 0,
+ decodeURI(currentPage)
+ .split('pagenumber="')[1]
+ .indexOf('"')
+ ) - 1
+ )
+ : pagesCount;
+
+ const [showSpinnerState, setShowSpinnerState] = useState(false);
+
+ const [showDownloadingState, setShowDownloadingState] = useState(null);
+
+ const toggleDownLoadLink = (i) => {
+ setShowDownloadingState(i);
+
+ const downloadTime = setTimeout(hideDownloadCover, 5000);
+ downloadTime;
+ };
+
+ function hideDownloadCover() {
+ setShowDownloadingState(null);
+ }
+
+ let spinnerState = () => {
+ showSpinnerState == true
+ ? setShowSpinnerState(false)
+ : setShowSpinnerState(true);
+ };
+
+ const [showDocumentSearchState, setDocumentSearchState] = useState(false);
+ const [orderByState, setOrderbyState] = useState("createdon");
+ const [fieldSortState, setfieldSortState] = useState("desc");
+
+ let documentSearchState = () => {
+ showDocumentSearchState == true
+ ? setDocumentSearchState(false)
+ : setDocumentSearchState(true);
+ };
+
+ const sortDataBy = (whichField) => {
+ setOrderbyState(whichField);
+ setfieldSortState("asc");
+
+ getDocumentResults(1, whichField, fieldSortState);
+ whichField == orderByState
+ ? fieldSortState == "desc"
+ ? setfieldSortState("asc")
+ : setfieldSortState("desc")
+ : setfieldSortState("asc");
+ setCurrentPage(1);
+ setShowSpinnerState(true);
+ };
+
+ useEffect(() => {
+ if (selectedOption) {
+ setShowSpinnerState(true);
+ getDocumentResults(1, orderByState, fieldSortState);
+ }
+
+ if (selectedWeeks) {
+ setShowSpinnerState(true);
+ getDocumentResults(1, orderByState, fieldSortState);
+ }
+
+ if (selectedDocumentType) {
+ setShowSpinnerState(true);
+ getDocumentResults(
+ 1,
+ orderByState,
+ fieldSortState,
+ selectedDocumentType
+ );
+ }
+
+ const docDetailsObj = async () => {
+ let docObj = await getNewDocumentsPaged(
+ pageNumber,
+ orderBy,
+ fieldSort,
+ selectedOption,
+ selectedDocumentType,
+ selectedWeeks
+ ).then((data) => {
+ props.setDocumentDetails(data);
+ setDocumentSearchState(true);
+
+ return data;
+ });
+
+ return docObj;
+ };
+
+ //const searchDocumentResultsObj = docDetailsObj();
+ }, [
+ incidentid,
+ props.setDocumentDetails,
+ selectedOption,
+ selectedDocumentType,
+ fieldSortState,
+ orderByState,
+ getDocumentTypes,
+ getOrigin,
+ getDocumentResults,
+ lang,
+ selectedWeeks,
+ ]);
+
+ function countFieldValue(obj, field, targetValue) {
+ let count = 0;
+
+ function traverse(node) {
+ if (Array.isArray(node)) {
+ node.forEach(traverse);
+ } else if (node !== null && typeof node === "object") {
+ // If this object has the field, check it
+ if (node.hasOwnProperty(field) && node[field] === targetValue) {
+ count++;
+ }
+ // Keep traversing children
+ Object.values(node).forEach(traverse);
+ }
+ }
+
+ traverse(obj);
+ return count;
+ }
+
+ return (
+
+
+
+ {t("case:documents-heading-label")}
+
+ {/*
+ MDU :{" "}
+ {countFieldValue(
+ documentDetailsObj,
+ "pinswg_origin",
+ 846040000
+ )}{" "}
+ / Portal :{" "}
+ {countFieldValue(
+ documentDetailsObj,
+ "pinswg_origin",
+ 846040001
+ )}
+
*/}
+
+
+
+
+
+ {FilteredDocumentView(documentDetailsArr)}
+
+
+ );
+};
+
+const mapStateToProps = (state) => {
+ return {
+ ...state,
+ };
+};
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ setDocumentDetails: (detailsObj) => {
+ dispatch(setDocumentDetails(detailsObj));
+ },
+ setCurrentPage: (currentPage) => {
+ dispatch(setCurrentPage(currentPage));
+ },
+ };
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(DocumentsTab);
diff --git a/components/admin/tabs/storage.js b/components/admin/tabs/storage.js
new file mode 100644
index 00000000..fcbfb15f
--- /dev/null
+++ b/components/admin/tabs/storage.js
@@ -0,0 +1,266 @@
+// components/admin/tabs/StorageTab.js
+import { useState } from "react";
+import Link from "next/link";
+import { bytesToSize } from "../../../components/utils";
+import {
+ deleteMyRepresentationsFromBlob,
+ deleteAwaitingSubmissionsFromBlob,
+ sendRepCompleteMessage,
+} from "../../../actions";
+
+import { formatBlobDates } from "../utils/adminHelper";
+
+export default function StorageTab({ data, repCompleteCount }) {
+ const [showRepJson, setShowRepJson] = useState(true);
+ const [showTmpFile, setShowTmpFile] = useState(true);
+ const [listData, setListData] = useState(data);
+
+ const toggleAll = () => {
+ const newState = !(showRepJson || showTmpFile);
+ setShowRepJson(newState);
+ setShowTmpFile(newState);
+ };
+
+ return (
+
+
Storage Account Contents
+ {repCompleteCount > 0 && (
+
Number of Orphaned Reps: {repCompleteCount}
+ )}
+
+ {/* Toggle buttons */}
+
+
+
+
+
+
+ {/* Render users & containers */}
+ {listData.map((user) => (
+
+ {user.containers.map((c) => (
+
+
+
+ {c.container} -{" "}
+
+ {user.email}
+
+
+ {/* folders */}
+ {c.folders.map((folder) => (
+
+
{folder.folderPath || "Root"}
+
+ {/* Buttons for TMP + Rep actions go here (same logic as before) */}
+
+ {folder.hasTmpFile && (
+ <>
+
+ >
+ )}
+ {folder.hasRepJson && (
+
+ {" "}
+
+ {folder.repComplete && (
+
+ )}
+ {folder.repComplete && (
+
+ )}
+
+ )}
+
+ {folder.blobs.map((blob) => (
+ -
+
+ {blob.name} (
+ {bytesToSize(
+ blob.contentLength ||
+ blob.size
+ )}
+ )
+ {" "}
+ {blob.lastModified && (
+
+ {/* {
+ blob.displayDate
+ } */}
+ {formatBlobDates(
+ blob
+ )}
+
+ )}
+
+ ))}
+
+
+ ))}
+
+
+ ))}
+
+ ))}
+
+ );
+}
diff --git a/components/admin/utils/adminHelper.js b/components/admin/utils/adminHelper.js
new file mode 100644
index 00000000..87223cf1
--- /dev/null
+++ b/components/admin/utils/adminHelper.js
@@ -0,0 +1,81 @@
+// components/admin/utils/adminStorage.js
+
+/**
+ * Get the folder path up to the last slash.
+ * Used for grouping blobs by "case folder".
+ */
+export function getUpToLastSlash(url) {
+ let lastSlash = url.lastIndexOf("/");
+
+ url = url.substring(0, lastSlash);
+
+ url =
+ url.lastIndexOf("/files") > 0
+ ? url.substring(0, url.lastIndexOf("/files"))
+ : url;
+ return url;
+}
+
+/**
+ * Convert a stream (Azure blob download) to a string.
+ */
+export async function streamToString(readableStream) {
+ return new Promise((resolve, reject) => {
+ const chunks = [];
+ readableStream.on("data", (data) => {
+ chunks.push(data.toString());
+ });
+ readableStream.on("end", () => {
+ resolve(chunks.join(""));
+ });
+ readableStream.on("error", reject);
+ });
+}
+
+/**
+ * Format how long ago a date occurred.
+ */
+export function formatTimeSince(date) {
+ if (!date) return "Unknown date";
+
+ const now = new Date();
+ const diffMs = now - date;
+ const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
+
+ if (diffDays < 7) {
+ return `${diffDays} day${diffDays !== 1 ? "s" : ""} ago`;
+ } else {
+ const diffWeeks = Math.floor(diffDays / 7);
+ return `${diffWeeks} week${diffWeeks !== 1 ? "s" : ""} ago`;
+ }
+}
+
+/**
+ * Format Azure blob created/modified dates into a readable string.
+ */
+export function formatBlobDates(blob) {
+ const created = blob.createdOn ? new Date(blob.createdOn) : null;
+ const modified = blob.lastModified ? new Date(blob.lastModified) : null;
+
+ if (!created && !modified) return "Unknown";
+
+ // If both exist and are the same
+ if (created && modified && created.getTime() === modified.getTime()) {
+ return `${created.toLocaleString("en-GB")} (${formatTimeSince(
+ created
+ )})`;
+ }
+
+ // If modified exists and is newer than created
+ if (created && modified && modified.getTime() > created.getTime()) {
+ return `Created: ${created.toLocaleString("en-GB")} (${formatTimeSince(
+ created
+ )}), Modified: ${modified.toLocaleString("en-GB")} (${formatTimeSince(
+ modified
+ )})`;
+ }
+
+ // Only one date exists
+ const date = modified || created;
+ return `${date.toLocaleString("en-GB")} (${formatTimeSince(date)})`;
+}
diff --git a/components/admin/utils/prisma.js b/components/admin/utils/prisma.js
new file mode 100644
index 00000000..ed7b9045
--- /dev/null
+++ b/components/admin/utils/prisma.js
@@ -0,0 +1,14 @@
+// lib/prisma.js
+import { PrismaClient } from "@prisma/client";
+
+const globalForPrisma = globalThis;
+
+export const prisma =
+ globalForPrisma.prisma ||
+ new PrismaClient({
+ log: ["query", "error", "warn"],
+ });
+
+if (process.env.NODE_ENV !== "production") {
+ globalForPrisma.prisma = prisma;
+}
diff --git a/components/admin/utils/serverside.js b/components/admin/utils/serverside.js
new file mode 100644
index 00000000..9bb35048
--- /dev/null
+++ b/components/admin/utils/serverside.js
@@ -0,0 +1,166 @@
+// components/admin/utils/serverStorage.js
+import { DefaultAzureCredential } from "@azure/identity";
+import { BlobServiceClient } from "@azure/storage-blob";
+import { PrismaClient } from "@prisma/client";
+import CryptoJS from "crypto-js";
+import { getUpToLastSlash, streamToString } from "../utils/adminHelper";
+import { listContainersForUser } from "../../../actions/azurestorage";
+
+const prisma = global.prisma || new PrismaClient();
+
+/**
+ * Generate HMAC hash for blob API link.
+ */
+function hashAPIPath(queryPath, WORDKEY) {
+ let hashlink = CryptoJS.HmacSHA256(
+ queryPath,
+ CryptoJS.enc.Hex.parse(WORDKEY)
+ );
+ return hashlink.toString(CryptoJS.enc.Hex);
+}
+
+/**
+ * Fetch blob structure + users for admin storage page.
+ */
+export async function fetchAdminStorageData() {
+ const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME;
+ const creds = new DefaultAzureCredential();
+ const blobServiceClient = new BlobServiceClient(
+ `https://${accountName}.blob.core.windows.net`,
+ creds
+ );
+
+ const prisma = global.prisma || new PrismaClient();
+
+ const users = await prisma.user.findMany();
+
+ let data = await Promise.all(
+ users.map(async (user) => {
+ const containers = await listContainersForUser(
+ blobServiceClient,
+ `${user.id}`,
+ user.email
+ );
+
+ const containersWithHashes = await Promise.all(
+ containers.map(async (container) => {
+ const groupedByFolder = await container.blobs.reduce(
+ async (accP, blob) => {
+ const acc = await accP;
+ const folderPath =
+ getUpToLastSlash(blob.name.trim()) || "";
+
+ const hashedfilepath = hashAPIPath(
+ `/api/file/downloadblob?container=${
+ container.container
+ }&casefolderID=${folderPath}&blobname=${blob.name
+ .substring(blob.name.lastIndexOf("/") + 1)
+ .trim()}`,
+ process.env.HASHKEY
+ );
+
+ const fileNameOnly = blob.name.substring(
+ blob.name.lastIndexOf("/") + 1
+ );
+ const isTmpFile = fileNameOnly.startsWith("TMP-");
+
+ const blobWithHash = {
+ ...blob,
+ hashedfilepath,
+ isTmpFile,
+ };
+
+ let folderGroup = acc.find(
+ (f) => f.folderPath === folderPath
+ );
+ if (!folderGroup) {
+ folderGroup = {
+ folderPath,
+ blobs: [],
+ hasRepJson: false,
+ repJsonBlob: null,
+ repComplete: false,
+ hasTmpFile: false,
+ };
+ acc.push(folderGroup);
+ }
+
+ folderGroup.blobs.push(blobWithHash);
+
+ if (isTmpFile) {
+ folderGroup.hasTmpFile = true;
+ }
+
+ // rep.json handling
+ if (blob.name.toLowerCase().endsWith("rep.json")) {
+ folderGroup.hasRepJson = true;
+ folderGroup.repJsonBlob = blobWithHash;
+
+ const containerClient =
+ blobServiceClient.getContainerClient(
+ container.container
+ );
+ const blockBlobClient =
+ containerClient.getBlockBlobClient(
+ blob.name
+ );
+
+ try {
+ const downloadResponse =
+ await blockBlobClient.download(0);
+ const downloaded = await streamToString(
+ downloadResponse.readableStreamBody
+ );
+ const json = JSON.parse(downloaded);
+ folderGroup.repComplete = Boolean(
+ json.repComplete
+ );
+ } catch (err) {
+ console.error(
+ "Error reading rep.json",
+ err
+ );
+ folderGroup.repComplete = false;
+ }
+ }
+
+ return acc;
+ },
+ Promise.resolve([])
+ );
+
+ return { ...container, folders: groupedByFolder };
+ })
+ );
+
+ return containersWithHashes.length > 0
+ ? {
+ id: user.id,
+ email: user.email,
+ containers: containersWithHashes,
+ }
+ : null;
+ })
+ );
+
+ data = data.filter(Boolean);
+
+ let userData = users
+ .filter((user) => user.email && user.email.trim() !== "")
+ .map((user) => ({
+ id: user.id,
+ email: user.email,
+ created: user.emailVerified?.toLocaleString("en-GB") || "",
+ }));
+
+ let repCompleteCount = 0;
+ data.forEach((user) => {
+ user.containers.forEach((container) => {
+ container.folders.forEach((folder) => {
+ if (folder.repComplete === true) repCompleteCount++;
+ });
+ });
+ });
+
+ return { data, userData, repCompleteCount };
+}
diff --git a/components/case/documents.js b/components/case/documents.js
index 315b15b3..35f6db4a 100644
--- a/components/case/documents.js
+++ b/components/case/documents.js
@@ -17,9 +17,11 @@ import {
import { setCurrentPage } from "../../store/currentView/action";
import { setDocumentDetails } from "../../store/searchOutput/action";
+import DocumentLink from "../utils/downloads";
+import { useDownloadQueue } from "../utils/downloadmanager";
const DocumentDetails = (props) => {
- let { t } = useTranslation();
+ let { t, lang } = useTranslation();
const firstRender = useRef(false);
const {
incidentid,
@@ -37,7 +39,7 @@ const DocumentDetails = (props) => {
const { locale } = router;
var documentDetailsArr = documentDetailsObj || [];
-
+ const { startDownload, getStatus } = useDownloadQueue(3);
const [selectedOption, setSelectedOption] = useState(10);
const [documentTypes, setDocumentTypes] = useState([{}]);
const [selectedDocumentType, setSelectedDocumentType] = useState("all");
@@ -287,12 +289,9 @@ const DocumentDetails = (props) => {
{/* {t("case:documents-date-published-label")} */}
@@ -584,8 +583,8 @@ const DocumentDetails = (props) => {
'$..[?(@ && @.value=="' +
item.pinswg_isharedocumentlocationsLabel +
'")].value_cy',
- jsonL: transLookup,
- sanbox: {},
+ json: transLookup,
+ eval: true,
})
: item.pinswg_isharedocumentlocationsLabel ||
t(
@@ -611,7 +610,7 @@ const DocumentDetails = (props) => {
setCurrentPage(1);
}}
>
- {t("case:documents-filter-button")}
+ {t("search:filter-documents-label")}
{documentDetailsObj["@odata.count"] > 5 && (
@@ -700,14 +699,9 @@ const DocumentDetails = (props) => {
{/* {t("case:documents-date-published-label")} */}