Merged PR 2070: media tab and player

Related work items: #21254, #21255, #21256
This commit is contained in:
Robert Bond
2026-02-04 11:23:21 +00:00
18 changed files with 1279 additions and 152 deletions
+103 -90
View File
@@ -34,7 +34,7 @@ export const getIP = (req) => {
console.info( console.info(
"\n====================\n Client IP address: " + ip, "\n====================\n Client IP address: " + ip,
"\n=====================\n" "\n=====================\n",
); );
}; };
@@ -42,7 +42,7 @@ export const consoleLogger = (err) => {
console.log( console.log(
"\n\n/////////////////////////////////////////////////\nRaw Error " + "\n\n/////////////////////////////////////////////////\nRaw Error " +
err + err +
"\n\n/////////////////////////////////////////////////\n" "\n\n/////////////////////////////////////////////////\n",
); );
var errStr = var errStr =
"\n\n/////////////////////////////////////////////////\nServer Error " + "\n\n/////////////////////////////////////////////////\nServer Error " +
@@ -102,7 +102,7 @@ export const getToken = () => {
.post( .post(
`${accessTokenEndpoint}${tenantId}/oauth2/v2.0/token`, `${accessTokenEndpoint}${tenantId}/oauth2/v2.0/token`,
tokenBody, tokenBody,
tokenConfig tokenConfig,
) )
.then((res) => res.data) .then((res) => res.data)
.then((data) => { .then((data) => {
@@ -191,7 +191,7 @@ export const azureHeadersPagedCustom = (access_token, showNumberOfRecords) => {
export const hashAPIPath = (queryPath) => { export const hashAPIPath = (queryPath) => {
var hashlink = CryptoJS.HmacSHA256( var hashlink = CryptoJS.HmacSHA256(
queryPath, queryPath,
CryptoJS.enc.Hex.parse(WORDKEY) CryptoJS.enc.Hex.parse(WORDKEY),
); );
hashlink = hashlink.toString(CryptoJS.enc.Hex); hashlink = hashlink.toString(CryptoJS.enc.Hex);
return (queryPath.indexOf("?") > -1 ? "&hash=" : "?hash=") + hashlink; return (queryPath.indexOf("?") > -1 ? "&hash=" : "?hash=") + hashlink;
@@ -216,7 +216,7 @@ export const getBasicSearch = (searchString) => {
.get( .get(
BASE_URL + BASE_URL +
"/api/endpoint/getbasicsearch_api?searchString=" + "/api/endpoint/getbasicsearch_api?searchString=" +
searchString searchString,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -244,7 +244,7 @@ export const getIncidentbyID = (searchString) => {
.get( .get(
BASE_URL + BASE_URL +
"/api/endpoint/getincidentbyid_api?searchString=" + "/api/endpoint/getincidentbyid_api?searchString=" +
searchString searchString,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -258,7 +258,7 @@ export const getIncidentbyID = (searchString) => {
export const getIsPublishedbyID = (searchString) => { export const getIsPublishedbyID = (searchString) => {
return axios return axios
.get( .get(
"/api/endpoint/getispublishedbyid_api?searchString=" + searchString "/api/endpoint/getispublishedbyid_api?searchString=" + searchString,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -274,7 +274,7 @@ export const getPartSavedAppeal = (searchString) => {
.get( .get(
BASE_URL + BASE_URL +
"/api/endpoint/getpartsavedappeal_api?searchString=" + "/api/endpoint/getpartsavedappeal_api?searchString=" +
searchString searchString,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -290,7 +290,7 @@ export const getBasicDNSURLSearch = (searchString) => {
.get( .get(
BASE_URL + BASE_URL +
"/api/endpoint/getbasicdnsurlsearch_api?searchString=" + "/api/endpoint/getbasicdnsurlsearch_api?searchString=" +
searchString searchString,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -313,12 +313,24 @@ export const getSIPSEvents = async (caseid) => {
}); });
}; };
export const getSIPSMedia = async (caseid) => {
return axios
.get(BASE_URL + "/api/endpoint/getsipsmedia_api?caseid=" + caseid)
.then((res) => {
return res.data;
})
.catch((error) => {
consoleLogger(error);
return error.response;
});
};
export const getAddressSearchPaged = ( export const getAddressSearchPaged = (
searchString, searchString,
pageNumber, pageNumber,
orderBy, orderBy,
fieldSort, fieldSort,
showNumberOfRecords showNumberOfRecords,
) => { ) => {
return axios return axios
.get( .get(
@@ -331,7 +343,7 @@ export const getAddressSearchPaged = (
"&fieldSort=" + "&fieldSort=" +
fieldSort + fieldSort +
"&showNumberOfRecords=" + "&showNumberOfRecords=" +
showNumberOfRecords showNumberOfRecords,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -347,7 +359,7 @@ export const getBasicSearchPaged = (
pageNumber, pageNumber,
orderBy, orderBy,
fieldSort, fieldSort,
showNumberOfRecords showNumberOfRecords,
) => { ) => {
return axios return axios
.get( .get(
@@ -360,7 +372,7 @@ export const getBasicSearchPaged = (
"&fieldSort=" + "&fieldSort=" +
fieldSort + fieldSort +
"&showNumberOfRecords=" + "&showNumberOfRecords=" +
showNumberOfRecords showNumberOfRecords,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -413,7 +425,7 @@ export const getBasicDNSSearchPaged = (
pageNumber, pageNumber,
orderBy, orderBy,
fieldSort, fieldSort,
showNumberOfRecords showNumberOfRecords,
) => { ) => {
return axios return axios
.get( .get(
@@ -424,7 +436,7 @@ export const getBasicDNSSearchPaged = (
"&fieldSort=" + "&fieldSort=" +
fieldSort + fieldSort +
"&showNumberOfRecords=" + "&showNumberOfRecords=" +
showNumberOfRecords showNumberOfRecords,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -438,7 +450,7 @@ export const getAdvancedSearch = (searchString) => {
.get( .get(
BASE_URL + BASE_URL +
"/api/endpoint/getadvancedsearch_api?searchstring=" + "/api/endpoint/getadvancedsearch_api?searchstring=" +
JSON.stringify(searchString) JSON.stringify(searchString),
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -458,7 +470,7 @@ export const getAdvancedSearchPaged = (
pageNumber, pageNumber,
orderBy, orderBy,
fieldSort, fieldSort,
showNumberOfRecords showNumberOfRecords,
) => { ) => {
return axios return axios
.get( .get(
@@ -471,7 +483,7 @@ export const getAdvancedSearchPaged = (
"&fieldSort=" + "&fieldSort=" +
fieldSort + fieldSort +
"&showNumberOfRecords=" + "&showNumberOfRecords=" +
showNumberOfRecords showNumberOfRecords,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -490,7 +502,7 @@ export const getBasicSearchDetails = async (
appealTypeName, appealTypeName,
caseReference, caseReference,
primaryIdAttribute, primaryIdAttribute,
incidentID incidentID,
) => { ) => {
return axios return axios
.get( .get(
@@ -500,7 +512,7 @@ export const getBasicSearchDetails = async (
"&primaryIdAttribute=" + "&primaryIdAttribute=" +
primaryIdAttribute + primaryIdAttribute +
"&incidentID=" + "&incidentID=" +
incidentID incidentID,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -512,7 +524,7 @@ export const getBasicPartSavedDetails = (
appealTypeName, appealTypeName,
caseReference, caseReference,
primaryIdAttribute, primaryIdAttribute,
incidentID incidentID,
) => { ) => {
//console.log( //console.log(
// "///////api call: ", // "///////api call: ",
@@ -532,7 +544,7 @@ export const getBasicPartSavedDetails = (
"&primaryIdAttribute=" + "&primaryIdAttribute=" +
primaryIdAttribute + primaryIdAttribute +
"&incidentID=" + "&incidentID=" +
incidentID incidentID,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -544,7 +556,7 @@ export const getBasicSearchDetailsPaged = async (
appealTypeName, appealTypeName,
caseReference, caseReference,
primaryIdAttribute, primaryIdAttribute,
incidentIDs incidentIDs,
) => { ) => {
if (!incidentIDs || incidentIDs.length === 0) return []; if (!incidentIDs || incidentIDs.length === 0) return [];
@@ -566,11 +578,11 @@ export const getBasicSearchDetailsPaged = async (
.join(" or "); .join(" or ");
const url = `/api/endpoint/getbasicsearchdetailspaged_api?appealTypeName=${appealTypeName}&primaryIdAttribute=${primaryIdAttribute}&incidentID=${encodeURIComponent( const url = `/api/endpoint/getbasicsearchdetailspaged_api?appealTypeName=${appealTypeName}&primaryIdAttribute=${primaryIdAttribute}&incidentID=${encodeURIComponent(
filter filter,
)}`; )}`;
console.log( console.log(
`Fetching ${incidentIDs.length} incidents for appeal type: ${appealTypeName}` `Fetching ${incidentIDs.length} incidents for appeal type: ${appealTypeName}`,
); );
try { try {
@@ -586,7 +598,7 @@ export const getSearchDocumentDetails = (incidentID) => {
return axios return axios
.get( .get(
"/api/endpoint/getsearchdocumentdetails_api?incidentid=" + "/api/endpoint/getsearchdocumentdetails_api?incidentid=" +
incidentID incidentID,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -598,7 +610,7 @@ export const getSearchDocumentDetails = (incidentID) => {
export const getSearchDocumentTypes = (incidentID) => { export const getSearchDocumentTypes = (incidentID) => {
return axios return axios
.get( .get(
"/api/endpoint/getsearchdocumentTypes_api?incidentid=" + incidentID "/api/endpoint/getsearchdocumentTypes_api?incidentid=" + incidentID,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -611,7 +623,7 @@ export const getAppealPDFDocs = (incidentID) => {
.get( .get(
BASE_URL + BASE_URL +
"/api/endpoint/getappealpdfdocuments_api?incidentid=" + "/api/endpoint/getappealpdfdocuments_api?incidentid=" +
incidentID incidentID,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -625,7 +637,7 @@ export const getSearchDocumentDetailsPaged = (
orderBy, orderBy,
fieldSort, fieldSort,
showNumberOfRecords, showNumberOfRecords,
documentType documentType,
) => { ) => {
//console.log("to api:", documentType); //console.log("to api:", documentType);
return axios return axios
@@ -641,7 +653,7 @@ export const getSearchDocumentDetailsPaged = (
"&showNumberOfRecords=" + "&showNumberOfRecords=" +
showNumberOfRecords + showNumberOfRecords +
"&documentType=" + "&documentType=" +
documentType documentType,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -653,7 +665,7 @@ export const getLinkedCases = (parentIncidentid) => {
return axios return axios
.get( .get(
"/api/endpoint/getlinkedcases_api?parentincidentid=" + "/api/endpoint/getlinkedcases_api?parentincidentid=" +
parentIncidentid parentIncidentid,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -745,7 +757,7 @@ export const getMandatoryFields = (whichForm) => {
.get( .get(
BASE_URL + BASE_URL +
"/api/endpoint/getmandatoryfields_api?whichForm=" + "/api/endpoint/getmandatoryfields_api?whichForm=" +
whichForm whichForm,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -767,7 +779,7 @@ export const getPersonalAccount = (contactid) => {
.get( .get(
BASE_URL + BASE_URL +
"/api/endpoint/getpersonalaccount_api?contactid=" + "/api/endpoint/getpersonalaccount_api?contactid=" +
contactid contactid,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -787,7 +799,7 @@ export const getPersonalAccount = (contactid) => {
export const getAppealID = ( export const getAppealID = (
caseReference, caseReference,
updateFormCollection, updateFormCollection,
primaryAttribute primaryAttribute,
) => { ) => {
//console.log( //console.log(
// "is this the collection?:", // "is this the collection?:",
@@ -803,11 +815,11 @@ export const getAppealID = (
"&primaryAttribute=" + "&primaryAttribute=" +
primaryAttribute + primaryAttribute +
"&caseReference=" + "&caseReference=" +
caseReference caseReference,
) )
.then((res) => { .then((res) => {
const result = Object.entries(res.data.value[0]).filter( const result = Object.entries(res.data.value[0]).filter(
([key]) => !key.startsWith("_") ([key]) => !key.startsWith("_"),
)[0][1]; )[0][1];
// console.log("result:", res.data); // console.log("result:", res.data);
var appealID = result; var appealID = result;
@@ -824,7 +836,7 @@ export const getLogin = (emailAddress, pwd) => {
"/api/endpoint/getlogin_api?emailAddress=" + "/api/endpoint/getlogin_api?emailAddress=" +
emailAddress + emailAddress +
"&pwd=" + "&pwd=" +
pwd pwd,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -837,7 +849,7 @@ export const getMyCases = (loggedInUserId) => {
.get( .get(
BASE_URL + BASE_URL +
"/api/endpoint/getmycases_api?loggedInUserId=" + "/api/endpoint/getmycases_api?loggedInUserId=" +
loggedInUserId loggedInUserId,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -852,7 +864,7 @@ export const getMyInvolvements = async (loggedInUserId) => {
const res = await axios.get( const res = await axios.get(
BASE_URL + BASE_URL +
"/api/endpoint/getmyinvolvements_api?loggedInUserId=" + "/api/endpoint/getmyinvolvements_api?loggedInUserId=" +
loggedInUserId loggedInUserId,
); );
return res.data; return res.data;
} catch (error) { } catch (error) {
@@ -876,7 +888,7 @@ export const getMyRepresentations = (loggedInUserId) => {
.get( .get(
BASE_URL + BASE_URL +
"/api/endpoint/getmyrepresentations_api?loggedInUserId=" + "/api/endpoint/getmyrepresentations_api?loggedInUserId=" +
loggedInUserId loggedInUserId,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -888,7 +900,7 @@ export const getMyRepresentationsProxy = (loggedInUserId) => {
return axios return axios
.get( .get(
"/api/endpoint/getmyrepresentationsproxy_api?loggedInUserId=" + "/api/endpoint/getmyrepresentationsproxy_api?loggedInUserId=" +
loggedInUserId loggedInUserId,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -908,7 +920,8 @@ export const getRepresentations = (incidentID) => {
export const getRepresentationsProxy = (incidentID) => { export const getRepresentationsProxy = (incidentID) => {
return axios return axios
.get( .get(
"/api/endpoint/getrepresentationsproxy_api?incidentID=" + incidentID "/api/endpoint/getrepresentationsproxy_api?incidentID=" +
incidentID,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -921,7 +934,7 @@ export const getWatchedCases = (loggedInUserId) => {
.get( .get(
BASE_URL + BASE_URL +
"/api/endpoint/getwatchedcases_api?loggedInUserId=" + "/api/endpoint/getwatchedcases_api?loggedInUserId=" +
loggedInUserId loggedInUserId,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -933,7 +946,7 @@ export const getWatchedCasesProxy = (loggedInUserId) => {
return axios return axios
.get( .get(
"/api/endpoint/getwatchedcasesproxy_api?loggedInUserId=" + "/api/endpoint/getwatchedcasesproxy_api?loggedInUserId=" +
loggedInUserId loggedInUserId,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -945,7 +958,7 @@ export const getAwaitingSubmissionProxy = (loggedInUserId) => {
return axios return axios
.get( .get(
"/api/endpoint/getawaitingsubmissionproxy_api?loggedInUserId=" + "/api/endpoint/getawaitingsubmissionproxy_api?loggedInUserId=" +
loggedInUserId loggedInUserId,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -958,7 +971,7 @@ export const getAwaitingSubmission = (loggedInUserId) => {
.get( .get(
BASE_URL + BASE_URL +
"/api/endpoint/getawaitingsubmission_api?loggedInUserId=" + "/api/endpoint/getawaitingsubmission_api?loggedInUserId=" +
loggedInUserId loggedInUserId,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -988,8 +1001,8 @@ export const getAwaitingSubmissionFromBlob = (containerName) => {
containerName + containerName +
hashAPIPath( hashAPIPath(
"/api/file/getawaitingsubmissionfromblob?container=" + "/api/file/getawaitingsubmissionfromblob?container=" +
containerName containerName,
) ),
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -1015,7 +1028,7 @@ export const getRepsFromBlob = (containerName) => {
BASE_URL + BASE_URL +
"/api/file/getrepsblob?container=" + "/api/file/getrepsblob?container=" +
containerName + containerName +
hashAPIPath("/api/file/getrepsblob?container=" + containerName) hashAPIPath("/api/file/getrepsblob?container=" + containerName),
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -1042,7 +1055,7 @@ export const getRepsFromBlobProxy = async (containerName) => {
try { try {
const res = await axios.get( const res = await axios.get(
// BASE_URL + // BASE_URL +
"/api/file/getrepsblobproxy?container=" + containerName "/api/file/getrepsblobproxy?container=" + containerName,
// + // +
// hashAPIPath( // hashAPIPath(
// "/api/file/getawaitingsubmissionfromblob?container=" + // "/api/file/getawaitingsubmissionfromblob?container=" +
@@ -1061,14 +1074,14 @@ export const getAwaitingSubmissionFromBlobProxy = async (containerName) => {
containerName, containerName,
"/api/file/getawaitingsubmissionfromblobproxy?container=" + "/api/file/getawaitingsubmissionfromblobproxy?container=" +
containerName, containerName,
"\n///////////////////////\n" "\n///////////////////////\n",
); );
try { try {
const res = await axios.get( const res = await axios.get(
BASE_URL + BASE_URL +
"/api/file/getawaitingsubmissionfromblobproxy?container=" + "/api/file/getawaitingsubmissionfromblobproxy?container=" +
containerName containerName,
// + // +
// hashAPIPath( // hashAPIPath(
// "/api/file/getawaitingsubmissionfromblob?container=" + // "/api/file/getawaitingsubmissionfromblob?container=" +
@@ -1129,7 +1142,7 @@ export const getPortalModuleDetailsProxy = (appealType, caseReference) => {
"/api/endpoint/getportalmoduledetailsproxy_api?appealType=" + "/api/endpoint/getportalmoduledetailsproxy_api?appealType=" +
appealType + appealType +
"&caseReference=" + "&caseReference=" +
caseReference.replace(/\'/g, "''") caseReference.replace(/\'/g, "''"),
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -1141,7 +1154,7 @@ export const getEmailAccountCheck = (emailAddress) => {
return axios return axios
.get( .get(
"/api/endpoint/getemailaccountcheck_api?emailAddress=" + "/api/endpoint/getemailaccountcheck_api?emailAddress=" +
emailAddress emailAddress,
) )
.then((res) => res.data) .then((res) => res.data)
.catch((error) => { .catch((error) => {
@@ -1213,7 +1226,7 @@ export const createNewCase = (
lpaID, lpaID,
contactid, contactid,
createBody, createBody,
containerName containerName,
) => { ) => {
appealTypeId = parseInt(appealTypeId); appealTypeId = parseInt(appealTypeId);
@@ -1250,7 +1263,7 @@ export const createNewCaseBlob = (
contactid, contactid,
createBody, createBody,
containerName, containerName,
lpaName lpaName,
) => { ) => {
appealTypeId = parseInt(appealTypeId); appealTypeId = parseInt(appealTypeId);
@@ -1302,14 +1315,14 @@ export const updateCase = async (
updateBody, updateBody,
updateFormCollection, updateFormCollection,
primaryAttribute, primaryAttribute,
caseReference caseReference,
) => { ) => {
var data = updateBody; var data = updateBody;
var appealObj = await getAppealID( var appealObj = await getAppealID(
caseReference, caseReference,
updateFormCollection, updateFormCollection,
primaryAttribute primaryAttribute,
); );
var queryUrl = var queryUrl =
@@ -1340,14 +1353,14 @@ export const updateCaseBlob = async (
updateBody, updateBody,
updateFormCollection, updateFormCollection,
primaryAttribute, primaryAttribute,
caseReference caseReference,
) => { ) => {
var data = updateBody; var data = updateBody;
var appealObj = await getAppealID( var appealObj = await getAppealID(
caseReference, caseReference,
updateFormCollection, updateFormCollection,
primaryAttribute primaryAttribute,
); );
var queryUrl = var queryUrl =
@@ -1415,7 +1428,7 @@ export const getCaseByID = (incidentID) => {
// ); // );
return axios return axios
.get( .get(
BASE_URL + "/api/endpoint/getcasebyid_api?incidentID=" + incidentID BASE_URL + "/api/endpoint/getcasebyid_api?incidentID=" + incidentID,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -1473,7 +1486,7 @@ export const deleteAwaitingSubmissions = (incidentID) => {
export const deleteAwaitingSubmissionsFromBlob = ( export const deleteAwaitingSubmissionsFromBlob = (
containerID, containerID,
casefolderID casefolderID,
) => { ) => {
var queryUrl = var queryUrl =
"/api/file/deleteblobcase?container=" + "/api/file/deleteblobcase?container=" +
@@ -1499,7 +1512,7 @@ export const deleteAwaitingSubmissionsFromBlob = (
export const deleteMyRepresentationsFromBlob = ( export const deleteMyRepresentationsFromBlob = (
containerID, containerID,
casefolderID, casefolderID,
repfile repfile,
) => { ) => {
var queryUrl = var queryUrl =
"/api/file/deleteblobrep?container=" + "/api/file/deleteblobrep?container=" +
@@ -1563,7 +1576,7 @@ export const uploadFiles = async (
formValues, formValues,
filesObj, filesObj,
containerID, containerID,
casefolderID casefolderID,
) => { ) => {
let files = filesObj; let files = filesObj;
@@ -1644,7 +1657,7 @@ export const uploadRepFiles = async (
formValues, formValues,
filesObj, filesObj,
containerID, containerID,
casefolderID casefolderID,
) => { ) => {
let files = filesObj; let files = filesObj;
@@ -1721,7 +1734,7 @@ export const generateAppealPDF = async (
containerID, containerID,
casefolderID, casefolderID,
appealType, appealType,
fileList fileList,
) => { ) => {
//let files = filesObj; //let files = filesObj;
@@ -1779,8 +1792,8 @@ export const getFilesFromBlob = (containerName, casefolderID) => {
"/api/file/getbloblist?container=" + "/api/file/getbloblist?container=" +
containerName + containerName +
"&casefolderID=" + "&casefolderID=" +
casefolderID casefolderID,
) ),
) )
.then((res) => { .then((res) => {
//console.log("//////////----", res.data); //console.log("//////////----", res.data);
@@ -1798,7 +1811,7 @@ export const getFilesFromBlobproxy = (containerName, casefolderID) => {
"/api/file/getbloblistproxy?container=" + "/api/file/getbloblistproxy?container=" +
containerName + containerName +
"&casefolderID=" + "&casefolderID=" +
casefolderID casefolderID,
) )
.then((res) => { .then((res) => {
//console.log("//////////----", res.data); //console.log("//////////----", res.data);
@@ -1812,7 +1825,7 @@ export const getFilesFromBlobproxy = (containerName, casefolderID) => {
export const getFilesFromBlobHashed = ( export const getFilesFromBlobHashed = (
containerName, containerName,
getblobshash, getblobshash,
casefolderID casefolderID,
) => { ) => {
return axios return axios
.get( .get(
@@ -1820,7 +1833,7 @@ export const getFilesFromBlobHashed = (
containerName + containerName +
"&casefolderID=" + "&casefolderID=" +
casefolderID + casefolderID +
getblobshash getblobshash,
) )
.then((res) => { .then((res) => {
//console.log("//////////----", res.data); //console.log("//////////----", res.data);
@@ -1835,7 +1848,7 @@ export const deleteBlob = async (
containerName, containerName,
blobName, blobName,
deleteblobhash, deleteblobhash,
casefolderID casefolderID,
) => { ) => {
try { try {
const res = await axios.get( const res = await axios.get(
@@ -1845,7 +1858,7 @@ export const deleteBlob = async (
encodeURIComponent(casefolderID) + encodeURIComponent(casefolderID) +
"&blobname=" + "&blobname=" +
encodeURIComponent(blobName) + encodeURIComponent(blobName) +
deleteblobhash deleteblobhash,
); );
return res.data; return res.data;
} catch (error) { } catch (error) {
@@ -1858,7 +1871,7 @@ export const deleteRepBlob = async (
blobName, blobName,
deleteblobhash, deleteblobhash,
casefolderID, casefolderID,
filenamePrefix filenamePrefix,
) => { ) => {
try { try {
const res = await axios.get( const res = await axios.get(
@@ -1868,7 +1881,7 @@ export const deleteRepBlob = async (
encodeURIComponent(casefolderID + "/" + filenamePrefix) + encodeURIComponent(casefolderID + "/" + filenamePrefix) +
"&blobname=" + "&blobname=" +
encodeURIComponent(blobName) + encodeURIComponent(blobName) +
deleteblobhash deleteblobhash,
); );
return res.data; return res.data;
} catch (error) { } catch (error) {
@@ -1884,14 +1897,14 @@ export const downloadBlob = (containerName, blobName) => {
containerName.toLowerCase() + containerName.toLowerCase() +
"&blobname=" + "&blobname=" +
blobName, blobName,
{ responseType: "blob" } { responseType: "blob" },
) )
.then((response) => { .then((response) => {
//console.log("Downloaded blob content:"); //console.log("Downloaded blob content:");
res.setHeader( res.setHeader(
"content-disposition", "content-disposition",
"attachment; filename=" + blobName "attachment; filename=" + blobName,
); );
//console.log("has downloaded"); //console.log("has downloaded");
@@ -1928,8 +1941,8 @@ export const getProgressFromBlob = async (containerName, casereference) => {
"/api/file/getprogressobjblob?container=" + "/api/file/getprogressobjblob?container=" +
encodeURIComponent(containerName) + encodeURIComponent(containerName) +
"&casefolderID=" + "&casefolderID=" +
encodeURIComponent(casereference) encodeURIComponent(casereference),
) ),
); );
return res.data; return res.data;
} catch (error) { } catch (error) {
@@ -1941,7 +1954,7 @@ export const sendEmail = async (
templateId, templateId,
emailAddress, emailAddress,
personalisation, personalisation,
reference reference,
) => { ) => {
var mailData = { var mailData = {
"templateId": templateId, "templateId": templateId,
@@ -2042,7 +2055,7 @@ export const createContainerProxy = (containerName) => {
export const sendCaseCompleteMessage = async ( export const sendCaseCompleteMessage = async (
containerID, containerID,
caseReference, caseReference,
inv inv,
) => { ) => {
var queryUrl = var queryUrl =
"/api/file/createappealcompletemessage_api?container=" + "/api/file/createappealcompletemessage_api?container=" +
@@ -2071,7 +2084,7 @@ export const sendCaseCompleteMessage = async (
export const sendCaseCompleteMessageProxy = async ( export const sendCaseCompleteMessageProxy = async (
containerID, containerID,
caseReference caseReference,
) => { ) => {
var queryUrl = var queryUrl =
"/api/file/createappealcompletemessageproxy_api?container=" + "/api/file/createappealcompletemessageproxy_api?container=" +
@@ -2098,7 +2111,7 @@ export const sendCaseCompleteMessageProxy = async (
export const sendRepCompleteMessage = async ( export const sendRepCompleteMessage = async (
containerID, containerID,
caseReference, caseReference,
fileName fileName,
) => { ) => {
var queryUrl = var queryUrl =
"/api/file/createrepcompletemessage_api?container=" + "/api/file/createrepcompletemessage_api?container=" +
@@ -2210,7 +2223,7 @@ export const getPreferredLanguage = async (email) => {
.get( .get(
BASE_URL + BASE_URL +
"/api/endpoint/getpreferredlanguage_api?emailAddress=" + "/api/endpoint/getpreferredlanguage_api?emailAddress=" +
email email,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -2226,7 +2239,7 @@ export const getAppealPDFDocument = async (incidentid) => {
.get( .get(
BASE_URL + BASE_URL +
"/api/endpoint/getappealpdfdocuments_api?incidentid=" + "/api/endpoint/getappealpdfdocuments_api?incidentid=" +
incidentid incidentid,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -2251,7 +2264,7 @@ export const getNewAppealsPage = async (
pageNumber, pageNumber,
orderBy, orderBy,
fieldSort, fieldSort,
showNumberOfRecords showNumberOfRecords,
) => { ) => {
return axios return axios
.get( .get(
@@ -2264,7 +2277,7 @@ export const getNewAppealsPage = async (
"&fieldSort=" + "&fieldSort=" +
fieldSort + fieldSort +
"&showNumberOfRecords=" + "&showNumberOfRecords=" +
showNumberOfRecords showNumberOfRecords,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
@@ -2282,7 +2295,7 @@ export const getNewDocumentsPaged = async (
showNumberOfRecords, showNumberOfRecords,
documentType, documentType,
documentOrigin, documentOrigin,
selectedWeeks selectedWeeks,
) => { ) => {
console.log( console.log(
"/api/admin/getlatestdocuments_api?pageNumber=" + "/api/admin/getlatestdocuments_api?pageNumber=" +
@@ -2298,7 +2311,7 @@ export const getNewDocumentsPaged = async (
"&numberWeeks=" + "&numberWeeks=" +
selectedWeeks + selectedWeeks +
"&documentOrigin=" + "&documentOrigin=" +
documentOrigin documentOrigin,
); );
return axios return axios
.get( .get(
@@ -2315,7 +2328,7 @@ export const getNewDocumentsPaged = async (
"&numberWeeks=" + "&numberWeeks=" +
selectedWeeks + selectedWeeks +
"&documentOrigin=" + "&documentOrigin=" +
documentOrigin documentOrigin,
) )
.then((res) => { .then((res) => {
return res.data; return res.data;
+2
View File
@@ -41,7 +41,9 @@ const Case = (props) => {
docsOffline={props.props.docsOffline} docsOffline={props.props.docsOffline}
documentDetailsObj={props.documentDetailsObj} documentDetailsObj={props.documentDetailsObj}
showFilteredDocs={props.showFilteredDocs} showFilteredDocs={props.showFilteredDocs}
showLoginCheck={props.showLoginCheck}
eventsObj={props.eventsObj} eventsObj={props.eventsObj}
mediaObj={props.mediaObj}
/> />
{showRepresentations == true && ( {showRepresentations == true && (
<RepresentationList <RepresentationList
+646
View File
@@ -0,0 +1,646 @@
// // import { setEventDetails } from "../../store/searchOutput/action";
// // import { setCurrentPage } from "../../store/currentView/action";
// // import { connect } from "react-redux";
// // import useTranslation from "next-translate/useTranslation";
// // import { formatDates } from "../utils";
// // const MediaDetails = (props) => {
// // let { t } = useTranslation();
// // const MediaView = (mediaArr) => {
// // mediaArr = mediaArr.value;
// // return mediaArr.map((item, key) => {
// // <div>{item.pinswg_name}</div>;
// // });
// // };
// // const mediaArr = props.mediaObj?.value || [];
// // // event publishiing flag set to true displa
// // function hasOwnPropertyAndNotNull(obj, property) {
// // return obj.hasOwnProperty(property) && obj[property] !== null;
// // }
// // //console.log(eventsArr.length > 0);
// // return (
// // mediaArr.length > 0 && (
// // <div className="govuk-grid-row">
// // <div className="card">
// // <div className="card-body">
// // <h2 className="govuk-heading-m ">
// // {t("case:event-title")}Media
// // </h2>
// // {mediaArr.map(
// // (item, key) =>
// // item.pinswg_publishtoweb && (
// // <>
// // {" "}
// // <div className="">
// // <div className="">
// // <div>
// // <b>
// // {" "}
// // {t(
// // "case:event-name-label",
// // )}
// // </b>
// // : {item.pinswg_name}
// // </div>
// // {hasOwnPropertyAndNotNull(
// // item,
// // "pinswg_description",
// // ) && (
// // <div>
// // <b>
// // {t(
// // "case:event-event-name-label",
// // )}
// // </b>
// // :{" "}
// // {
// // item.pinswg_description
// // }
// // </div>
// // )}
// // </div>
// // </div>
// // </>
// // ),
// // )}{" "}
// // </div>
// // </div>
// // </div>
// // )
// // );
// // };
// // const mapStateToProps = (state) => {
// // return {
// // ...state,
// // };
// // };
// // const mapDispatchToProps = (dispatch) => {
// // return {
// // setEventDetails: (mediaObj) => {
// // dispatch(setEventDetails(mediaObj));
// // },
// // setCurrentPage: (currentPage) => {
// // dispatch(setCurrentPage(currentPage));
// // },
// // };
// // };
// // export default connect(mapStateToProps, mapDispatchToProps)(MediaDetails);
// import { useEffect, useMemo, useState } from "react";
// import { connect } from "react-redux";
// import useTranslation from "next-translate/useTranslation";
// import { useRouter } from "next/router";
// const extractYouTubeId = (input) => {
// if (!input) return null;
// if (/^[a-zA-Z0-9_-]{11}$/.test(input)) return input;
// try {
// const url = new URL(input);
// if (url.hostname.includes("youtu.be"))
// return url.pathname.slice(1) || null;
// const v = url.searchParams.get("v");
// if (v) return v;
// const embedMatch = url.pathname.match(/\/embed\/([^/]+)/);
// if (embedMatch?.[1]) return embedMatch[1];
// const shortsMatch = url.pathname.match(/\/shorts\/([^/]+)/);
// if (shortsMatch?.[1]) return shortsMatch[1];
// return null;
// } catch {
// return null;
// }
// };
// export default function MediaDetails({ mediaObj, whichTab }) {
// const router = useRouter();
// const mediaArr = mediaObj?.value || [];
// const videoItems = useMemo(() => {
// const isWelsh = router.locale === "cy";
// return (mediaArr || [])
// .filter((x) => x?.pinswg_publishtoweb)
// .map((item) => {
// const url = isWelsh
// ? item.pinswg_mediaLinkCY
// : item.pinswg_mediaLinkEN;
// const videoId = extractYouTubeId(url);
// if (!videoId) return null;
// return {
// key:
// item.pinswg_documentid ||
// `${item.pinswg_name}-${videoId}`,
// videoId,
// name: item.pinswg_name || "Video",
// description: item.pinswg_description || "",
// thumbnail: `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`,
// };
// })
// .filter(Boolean);
// }, [mediaArr, router.locale]);
// const [watchedIds, setWatchedIds] = useState(new Set());
// const [selectedIndex, setSelectedIndex] = useState(0);
// // Forces a fresh iframe instance whenever we need to hard-stop playback
// const [playerInstance, setPlayerInstance] = useState(0);
// const isActive = whichTab === "case-media";
// const selected = videoItems[selectedIndex] || null;
// // When leaving the media tab, hard-stop playback by unmounting (and bumping instance)
// useEffect(() => {
// if (!isActive) {
// setPlayerInstance((n) => n + 1);
// }
// }, [isActive]);
// // When list/locale changes, reset to first (no autoplay)
// useEffect(() => {
// setSelectedIndex(0);
// setPlayerInstance((n) => n + 1);
// }, [videoItems.length, router.locale]);
// if (!videoItems.length) return null;
// const embedBase = "https://www.youtube-nocookie.com/embed";
// // IMPORTANT:
// // - autoplay=0 for initial load
// // - autoplay=1 only when user clicks an item
// const [autoplay, setAutoplay] = useState(false);
// useEffect(() => {
// // whenever we leave tab or reset player, don't autoplay next time
// if (!isActive) setAutoplay(false);
// }, [isActive]);
// const embedSrc = selected
// ? `${embedBase}/${selected.videoId}?rel=0&modestbranding=1&playsinline=1&autoplay=${autoplay ? "1" : "0"}`
// : "";
// return (
// <div className="govuk-grid-row">
// <div className="card">
// <div className="card-body">
// <h2 className="govuk-heading-m">Media</h2>
// {/* Player: only render when tab is active */}
// {isActive && (
// <div className="videoWrapper govuk-!-margin-bottom-4">
// <iframe
// key={`${selected?.videoId}-${playerInstance}`}
// src={embedSrc}
// title={selected?.name || "YouTube video"}
// frameBorder="0"
// allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
// allowFullScreen
// />
// </div>
// )}
// <h3 className="govuk-heading-s">Videos</h3>
// <ul className="govuk-list govuk-list--spaced">
// {videoItems.map((v, idx) => {
// const isSelected = idx === selectedIndex;
// return (
// <li key={v.key}>
// <button
// type="button"
// className={`videoTabButton ${isSelected ? "active" : ""}`}
// onClick={() => {
// setSelectedIndex(idx);
// setAutoplay(true); // play only on user action
// setPlayerInstance((n) => n + 1); // ensure clean switch / stop previous
// setWatchedIds((prev) =>
// new Set(prev).add(v.videoId),
// );
// }}
// >
// <div className="videoTabContent">
// <div className="thumbnailWrapper">
// <img
// src={v.thumbnail}
// alt=""
// className="videoThumbnail"
// loading="lazy"
// />
// <span
// className="playIcon"
// aria-hidden="true"
// >
// ▶
// </span>
// {/* Duration badge (see section 2) */}
// {v.duration && (
// <span className="durationBadge">
// {v.duration}
// </span>
// )}
// {/* Watched indicator (see section 3) */}
// {watchedIds.has(v.videoId) && (
// <span className="watchedBadge">
// Watched
// </span>
// )}
// </div>
// <span className="videoTitle">
// {v.name}
// </span>
// </div>
// </button>
// </li>
// );
// })}
// </ul>
// </div>
// </div>
// <style jsx>{`
// .videoWrapper {
// position: relative;
// padding-bottom: 56.25%;
// height: 0;
// overflow: hidden;
// border: 1px solid #b1b4b6;
// border-radius: 4px;
// }
// .videoWrapper iframe {
// position: absolute;
// top: 0;
// left: 0;
// width: 100%;
// height: 100%;
// }
// .videoTabButton {
// width: 100%;
// text-align: left;
// background: transparent;
// border: 1px solid #b1b4b6;
// border-radius: 4px;
// padding: 10px 12px;
// cursor: pointer;
// }
// .videoTabButton:hover {
// background: #f3f2f1;
// }
// .videoTabButton.active {
// border-color: #1d70b8;
// box-shadow: inset 0 0 0 1px #1d70b8;
// background: #eef4ff;
// }
// .videoTabContent {
// display: flex;
// gap: 12px;
// align-items: center;
// }
// .videoThumbnail {
// width: 120px;
// aspect-ratio: 16 / 9;
// object-fit: cover;
// border-radius: 4px;
// border: 1px solid #b1b4b6;
// background: #000;
// }
// .videoTitle {
// font-weight: 600;
// }
// @media (max-width: 40.0625em) {
// .videoThumbnail {
// width: 90px;
// }
// }
// .thumbnailWrapper {
// position: relative;
// width: 120px;
// flex-shrink: 0;
// }
// .videoThumbnail {
// width: 100%;
// aspect-ratio: 16 / 9;
// object-fit: cover;
// border-radius: 4px;
// border: 1px solid #b1b4b6;
// background: #000;
// }
// /* ▶ Play icon */
// .playIcon {
// position: absolute;
// inset: 0;
// display: flex;
// align-items: center;
// justify-content: center;
// font-size: 32px;
// color: white;
// text-shadow: 0 0 6px rgba(0, 0, 0, 0.7);
// pointer-events: none;
// }
// /* ⏱ Duration badge */
// .durationBadge {
// position: absolute;
// bottom: 6px;
// right: 6px;
// background: rgba(0, 0, 0, 0.8);
// color: white;
// font-size: 12px;
// padding: 2px 6px;
// border-radius: 3px;
// }
// /* 👁 Watched badge */
// .watchedBadge {
// position: absolute;
// top: 6px;
// left: 6px;
// background: #00703c; /* GOV green */
// color: white;
// font-size: 11px;
// padding: 2px 6px;
// border-radius: 3px;
// }
// .videoTabContent {
// display: flex;
// gap: 12px;
// align-items: center;
// }
// .videoTitle {
// font-weight: 600;
// }
// `}</style>
// </div>
// );
// }
import { useEffect, useMemo, useState } from "react";
import useTranslation from "next-translate/useTranslation";
import { useRouter } from "next/router";
/**
* MediaDetails
* - Renders a YouTube player + a list of videos from mediaObj.value
* - Loads first item by default (no autoplay)
* - Plays only when user selects an item
* - Stops playback when leaving the "case-media" parent tab (pass whichTab)
* - Shows thumbnail with play overlay
* - Optional duration badge (if you supply v.duration)
* - "Watched" badge once a video has been played (session only)
*/
const extractYouTubeId = (input) => {
if (!input) return null;
// Already looks like a YouTube ID?
if (/^[a-zA-Z0-9_-]{11}$/.test(input)) return input;
try {
const url = new URL(input);
// youtu.be/<id>
if (url.hostname.includes("youtu.be")) {
return url.pathname.replace("/", "") || null;
}
// youtube.com/watch?v=<id>
const v = url.searchParams.get("v");
if (v) return v;
// youtube.com/embed/<id>
const embedMatch = url.pathname.match(/\/embed\/([^/]+)/);
if (embedMatch?.[1]) return embedMatch[1];
// youtube.com/shorts/<id>
const shortsMatch = url.pathname.match(/\/shorts\/([^/]+)/);
if (shortsMatch?.[1]) return shortsMatch[1];
return null;
} catch {
return null;
}
};
const MediaDetails = ({ mediaObj, whichTab, title, useNoCookie = true }) => {
const { t } = useTranslation();
const router = useRouter();
const mediaArr = mediaObj?.value || [];
const isActive = whichTab ? whichTab === "case-media" : true;
// Build list of playable items (publishable only)
const videoItems = useMemo(() => {
const isWelsh = router.locale === "cy";
return (mediaArr || [])
.filter((x) => x?.pinswg_publishtoweb)
.map((item) => {
const link = isWelsh
? item.pinswg_mediaLinkCY
: item.pinswg_mediaLinkEN;
const videoId = extractYouTubeId(link);
if (!videoId) return null;
return {
key:
item.pinswg_documentid ||
item.pinswg_mediaid ||
`${item.pinswg_name}-${videoId}`,
videoId,
name: item.pinswg_name || "Video",
description: item.pinswg_description || "",
// If you later store duration in CRM, map it here (e.g. "03:42")
duration: item.pinswg_duration || null,
thumbnailHQ: `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`,
thumbnailMax: `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`,
raw: item,
};
})
.filter(Boolean);
}, [mediaArr, router.locale]);
const [selectedIndex, setSelectedIndex] = useState(0);
// Default false so first item loads but does not play.
const [autoplay, setAutoplay] = useState(false);
// Bump this to force iframe remount (hard stop playback).
const [playerInstance, setPlayerInstance] = useState(0);
// Track which videos have been played (session only).
const [watchedIds, setWatchedIds] = useState(() => new Set());
// When list or locale changes: reset selection and do not autoplay
useEffect(() => {
setSelectedIndex(0);
setAutoplay(false);
setPlayerInstance((n) => n + 1);
}, [videoItems.length, router.locale]);
// When leaving the media tab: hard stop playback
useEffect(() => {
if (!isActive) {
setAutoplay(false);
setPlayerInstance((n) => n + 1);
}
}, [isActive]);
const selected = videoItems[selectedIndex] || null;
const domain = useNoCookie
? "https://www.youtube-nocookie.com"
: "https://www.youtube.com";
const embedSrc = selected
? `${domain}/embed/${selected.videoId}?rel=0&modestbranding=1&playsinline=1&autoplay=${
autoplay ? "1" : "0"
}`
: "";
if (!videoItems.length) return null;
return (
<div className="govuk-grid-row">
<div className="card">
<div className="card-body">
<h2 className="govuk-heading-m">{t("case:media-title")}</h2>
{/* Player: unmount when not active (guarantees playback stops) */}
{isActive && (
<div className="videoWrapper govuk-!-margin-bottom-4">
<iframe
key={`${selected?.videoId}-${playerInstance}`}
src={embedSrc}
title={selected?.name || "YouTube video"}
frameBorder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
/>
</div>
)}
{/* Selected info */}
{selected && (
<div className="govuk-!-margin-bottom-4">
<div className="govuk-body">
<b>{t("case:event-name-label")}</b>:{" "}
{selected.name}
</div>
{!!selected.description && (
<div className="govuk-body">
<b>{t("case:event-event-name-label")}</b>:{" "}
{selected.description}
</div>
)}
</div>
)}
{/* Video list */}
<h3 className="govuk-heading-s">
{t("case:video-heading")}
</h3>
<ul className="govuk-list govuk-list--spaced">
{videoItems.map((v, idx) => {
const isSelected = idx === selectedIndex;
return (
<li key={v.key}>
<button
type="button"
className={`videoTabButton ${
isSelected ? "active" : ""
}`}
aria-current={
isSelected ? "true" : undefined
}
onClick={() => {
setSelectedIndex(idx);
// Start playback only when user selects
setAutoplay(true);
// Force remount to stop previous + start clean
setPlayerInstance((n) => n + 1);
// Mark watched
setWatchedIds((prev) => {
const next = new Set(prev);
next.add(v.videoId);
return next;
});
}}
>
<div className="videoTabContent">
<div className="thumbnailWrapper">
<img
src={v.thumbnailMax}
onError={(e) => {
// fallback for videos without maxres
e.currentTarget.src =
v.thumbnailHQ;
}}
alt=""
className="videoThumbnail"
loading="lazy"
/>
<span
className="playIcon"
aria-hidden="true"
>
</span>
{!!v.duration && (
<span className="durationBadge">
{v.duration}
</span>
)}
{watchedIds.has(v.videoId) && (
<span className="watchedBadge">
Watched
</span>
)}
</div>
<span className="videoTitle">
{v.name}
</span>
</div>
</button>
</li>
);
})}
</ul>
</div>
</div>
</div>
);
};
export default MediaDetails;
+42 -3
View File
@@ -8,6 +8,7 @@ import { connect } from "react-redux";
import transLookup from "../../data/lookuptranslations.json"; import transLookup from "../../data/lookuptranslations.json";
import Documents from "./documents"; import Documents from "./documents";
import EventsDetails from "./events"; import EventsDetails from "./events";
import MediaDetails from "./media";
import { import {
createWatchedCases, createWatchedCases,
@@ -81,8 +82,10 @@ const CaseSummary = (props) => {
ticketnumber, ticketnumber,
} = props; } = props;
const showLoginCheck = // const showLoginCheck =
currentType == "directResultsObj" ? "true" : currentView.showLogin; // currentType == "directResultsObj" ? "true" : currentView.showLogin;
let showLoginCheck = props.showLoginCheck;
const { data: session, status } = useSession(); const { data: session, status } = useSession();
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
@@ -416,7 +419,7 @@ const CaseSummary = (props) => {
> >
<a <a
className="govuk-tabs__tab" className="govuk-tabs__tab"
href="#case-events" href="#case-eia"
onClick={() => setWhichTab("case-eia")} onClick={() => setWhichTab("case-eia")}
> >
{" "} {" "}
@@ -444,6 +447,26 @@ const CaseSummary = (props) => {
</a> </a>
</li> </li>
)} )}
{isObjectNotEmpty(props, "mediaObj") && (
<li
className={
whichTab == "case-media"
? "govuk-tabs__list-item govuk-tabs__list-item--selected"
: "govuk-tabs__list-item "
}
>
<a
className="govuk-tabs__tab"
href="#case-media"
onClick={() =>
setWhichTab("case-media")
}
>
{" "}
{t("case:summary-tab-media-label")}{" "}
</a>
</li>
)}
<li <li
className={ className={
whichTab == "case-documents" whichTab == "case-documents"
@@ -1741,6 +1764,22 @@ const CaseSummary = (props) => {
</div> </div>
)} )}
{isObjectNotEmpty(props, "mediaObj") && (
<div
className={
whichTab == "case-media"
? "govuk-tabs__panel "
: "govuk-tabs__panel govuk-tabs__panel--hidden"
}
id="case-media"
>
<MediaDetails
mediaObj={props.mediaObj}
whichTab={whichTab}
/>
</div>
)}
<div <div
className={ className={
whichTab == "case-documents" whichTab == "case-documents"
+3 -2
View File
@@ -64,6 +64,7 @@ const Header = (props) => {
"/myportal/contactus", "/myportal/contactus",
"/unsubscribe/[watchlistid]", "/unsubscribe/[watchlistid]",
"/unsubscribeall/[watchlistid]", "/unsubscribeall/[watchlistid]",
"/status",
]; ];
const hasContactLink = [ const hasContactLink = [
@@ -84,14 +85,14 @@ const Header = (props) => {
const handleLogout = () => { const handleLogout = () => {
console.info("////////////\n" + "logout" + "\n////////////"); console.info("////////////\n" + "logout" + "\n////////////");
window.localStorage.clear(), (window.localStorage.clear(),
destroyCookie(null, "next-auth.csrf-token", { path: "/" }), destroyCookie(null, "next-auth.csrf-token", { path: "/" }),
destroyCookie(null, "next-auth.callback-url", { path: "/" }), destroyCookie(null, "next-auth.callback-url", { path: "/" }),
destroyCookie(null, "pedw_locale", { path: "/" }), destroyCookie(null, "pedw_locale", { path: "/" }),
destroyCookie(null, "pinsUser", { path: "/" }), destroyCookie(null, "pinsUser", { path: "/" }),
signOut({ signOut({
callbackUrl: locale == "cy" ? "/cy/allgofnodi" : "/logout", callbackUrl: locale == "cy" ? "/cy/allgofnodi" : "/logout",
}); }));
}; };
//const switchLocale = locale === "en" ? "cy" : "en"; //const switchLocale = locale === "en" ? "cy" : "en";
+3 -1
View File
@@ -24,8 +24,10 @@ const MainHome = (props) => {
<div className="flex-container grid-row govuk-body "> <div className="flex-container grid-row govuk-body ">
{showLogin != "true" && ( {showLogin != "true" && (
<> <>
<LoginSoon /> {/* <LoginSoon />
<CaseSearch /> */}
<CaseSearch /> <CaseSearch />
<Login session={session} siteurl={siteurl} />
</> </>
)} )}
{showLogin == "true" && ( {showLogin == "true" && (
+98 -26
View File
@@ -1,46 +1,118 @@
import useTranslation from "next-translate/useTranslation"; import useTranslation from "next-translate/useTranslation";
import router from "next/router"; import router from "next/router";
import Link from "next/link"; import Link from "next/link";
import CaseNoticeBanner from "./case/caseNoticeBanner";
export default function ServiceBanner(props) { export default function ServiceBanner(props) {
let { t, lang } = useTranslation(); let { t, lang } = useTranslation();
const showSigninFromEnv = true;
const showNoticeFromEnv = false;
// keep backwards-compat with existing usage
const showSignin = props?.noSignin === true ? false : showSigninFromEnv;
const signInButton = () => { const signInButton = () => {
router.push( router.push(
`${ `${
router.locale == "cy" ? "/awd/mewngofnodi" : "/auth/signin" router.locale == "cy" ? "/awd/mewngofnodi" : "/auth/signin"
}?callbackUrl=${encodeURIComponent( }?callbackUrl=${encodeURIComponent(
window.location.href window.location.href,
)}&error=EmailSignin` )}&error=EmailSignin`,
); );
}; };
return ( return (
<div className="servicebanner"> <>
<h1> <div className="servicebanner">
<img <h1>
className={ <img
router.locale == "cy" className={
? "govuk-!-margin-top-4 serviceTitle cy" router.locale == "cy"
: "govuk-!-margin-top-4 serviceTitle" ? "govuk-!-margin-top-4 serviceTitle cy"
} : "govuk-!-margin-top-4 serviceTitle"
src={ }
router.locale == "cy" src={
? "/assets/images/planning-casework-cy.svg" router.locale == "cy"
: "/assets/images/planning-casework-en.svg" ? "/assets/images/planning-casework-cy.svg"
} : "/assets/images/planning-casework-en.svg"
alt="Planning casework" }
/> alt="Planning casework"
</h1> />
{props.noSignin != true && ( </h1>
<div className="serviceBanner_userDetails">
<button {showSignin && props.noSignin != true && (
onClick={() => signInButton()} <div className="serviceBanner_userDetails">
className="govuk-header__link " <button
onClick={() => signInButton()}
className="govuk-header__link "
>
{t("common:signin-label")}
</button>
</div>
)}
</div>
{showNoticeFromEnv && (
<div>
<div
className="govuk-warning-text noticebanner govuk-!-margin-bottom-0"
key={1}
> >
{t("common:signin-label")} <strong className="govuk-warning-text__text">
</button> <span className="govuk-visually-hidden">
Announcement
</span>
</strong>
<div>
<div className=" govuk-body-s">
<h2 className="govuk-heading-s">
{router.locale == "cy"
? "Cyhoeddiad"
: "Announcement"}
</h2>
</div>
<div className="govuk-body-s">
{router.locale == "cy" ? (
<>
<p>
Oherwydd gwaith cynnal a chadw
hanfodol, rydym yn disgwyl rhywfaint
o darfu ar ein gwasanaethau Porth
Gwaith Achos rhwng 9-13 Chwefror
2026.{" "}
</p>
<p>
Ymddiheurwn am yr anghyfleustra. Am
ymholiadau brys yn ystod y cyfnod
hwn, cysylltwch â{" "}
<a href="mailto:pedw.gwaithachos@llyw.cymru">
pedw.gwaithachos@llyw.cymru
</a>
</p>
</>
) : (
<>
<p>
Due to essential maintenance, we are
expecting some disruption to our
Casework Portal services between
9-13 February 2026.
</p>
<p>
We apologise for the inconvenience.
For urgent enquries during this time
please contact{" "}
<a href="mailto:pedw.casework@gov.wales">
pedw.casework@gov.wales
</a>
</p>
</>
)}
</div>
</div>
</div>
</div> </div>
)} )}
</div> </>
); );
} }
+4 -1
View File
@@ -6,6 +6,7 @@
"summary-tab-map-label": " Map", "summary-tab-map-label": " Map",
"summary-tab-events-label": " Digwyddiadau", "summary-tab-events-label": " Digwyddiadau",
"summary-tab-documents-label": "Dogfennau", "summary-tab-documents-label": "Dogfennau",
"summary-tab-media-label": "Cyfryngau",
"summary-reference-label": "Cyfeirnod", "summary-reference-label": "Cyfeirnod",
"summary-applicant-label": "Apelydd", "summary-applicant-label": "Apelydd",
"summary-applicantonly-label": "Ymgeisydd", "summary-applicantonly-label": "Ymgeisydd",
@@ -139,5 +140,7 @@
"summary-reporting-decision-sub-title": "Adrodd/Penderfyniad", "summary-reporting-decision-sub-title": "Adrodd/Penderfyniad",
"summary-report-issued-to-welsh-ministers": "Adroddiad a Gyhoeddwyd i Weinidogion Cymru", "summary-report-issued-to-welsh-ministers": "Adroddiad a Gyhoeddwyd i Weinidogion Cymru",
"summary-decision-issued-to-applicant": "Penderfyniad a Gyhoeddwyd i'r Ymgeisydd", "summary-decision-issued-to-applicant": "Penderfyniad a Gyhoeddwyd i'r Ymgeisydd",
"summary-decision-outcome": "Canlyniad y Penderfyniad" "summary-decision-outcome": "Canlyniad y Penderfyniad",
"media-title": "Cyfryngau",
"video-heading": "Fideos"
} }
+4 -1
View File
@@ -6,6 +6,7 @@
"summary-tab-map-label": "Map", "summary-tab-map-label": "Map",
"summary-tab-events-label": "Events", "summary-tab-events-label": "Events",
"summary-tab-documents-label": "Documents", "summary-tab-documents-label": "Documents",
"summary-tab-media-label": "Media",
"summary-reference-label": "Reference", "summary-reference-label": "Reference",
"summary-applicant-label": "Appellant", "summary-applicant-label": "Appellant",
"summary-applicantonly-label": "Applicant", "summary-applicantonly-label": "Applicant",
@@ -140,5 +141,7 @@
"summary-reporting-decision-sub-title": "Reporting/Decision", "summary-reporting-decision-sub-title": "Reporting/Decision",
"summary-report-issued-to-welsh-ministers": "Report Issued to Welsh Ministers", "summary-report-issued-to-welsh-ministers": "Report Issued to Welsh Ministers",
"summary-decision-issued-to-applicant": "Decision Issued to Applicant", "summary-decision-issued-to-applicant": "Decision Issued to Applicant",
"summary-decision-outcome": "Decision Outcome" "summary-decision-outcome": "Decision Outcome",
"media-title": "Media",
"video-heading": "Fideos"
} }
+6 -6
View File
@@ -9,12 +9,12 @@ export function middleware(request) {
process.env.NODE_ENV === "production" ? "" : `'unsafe-eval'` process.env.NODE_ENV === "production" ? "" : `'unsafe-eval'`
}; };
style-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-eval' 'unsafe-inline';
img-src 'self' 'unsafe-inline' https://www.gov.wales https://gov.wales https://fonts.gstatic.com https://www.googletagmanager.com blob: data:; img-src 'self' 'unsafe-inline' https://www.gov.wales https://gov.wales https://fonts.gstatic.com https://www.googletagmanager.com blob: data: https://img.youtube.com;
connect-src 'self' http://127.0.0.1 https://uksouth-1.in.applicationinsights.azure.com https://ukwest-0.in.applicationinsights.azure.com https://js.monitor.azure.com https://region1.google-analytics.com; connect-src 'self' http://127.0.0.1 https://uksouth-1.in.applicationinsights.azure.com https://ukwest-0.in.applicationinsights.azure.com https://js.monitor.azure.com https://region1.google-analytics.com;
font-src 'self' https://pro.fontawesome.com/ https://fonts.gstatic.com/ ; font-src 'self' https://pro.fontawesome.com/ https://fonts.gstatic.com/ ;
object-src 'none'; object-src 'none';
base-uri 'self'; base-uri 'self';
frame-src https://datamap.gov.wales/; frame-src https://datamap.gov.wales/ https://www.youtube.com https://www.youtube-nocookie.com;
form-action 'self' ` + form-action 'self' ` +
process.env.NEXTAUTH_URL + process.env.NEXTAUTH_URL +
`; `;
@@ -49,7 +49,7 @@ export function middleware(request) {
// CSP policy // CSP policy
requestHeaders.set( requestHeaders.set(
"Content-Security-Policy", "Content-Security-Policy",
contentSecurityPolicyHeaderValue contentSecurityPolicyHeaderValue,
); );
const response = NextResponse.next({ const response = NextResponse.next({
@@ -59,7 +59,7 @@ export function middleware(request) {
}); });
response.headers.set( response.headers.set(
"Content-Security-Policy", "Content-Security-Policy",
contentSecurityPolicyHeaderValue contentSecurityPolicyHeaderValue,
); );
// XSS protection (legacy) // XSS protection (legacy)
@@ -77,13 +77,13 @@ export function middleware(request) {
// Permissions lockdown // Permissions lockdown
response.headers.set( response.headers.set(
"Permissions-Policy", "Permissions-Policy",
"geolocation=(), camera=(), microphone=(), fullscreen=(self)" "geolocation=(), camera=(), microphone=(), fullscreen=(self)",
); );
// Enforce HTTPS via HSTS // Enforce HTTPS via HSTS
response.headers.set( response.headers.set(
"Strict-Transport-Security", "Strict-Transport-Security",
"max-age=63072000; includeSubDomains; preload" "max-age=63072000; includeSubDomains; preload",
); );
return response; return response;
+149
View File
@@ -0,0 +1,149 @@
import axios from "axios";
import CryptoJS from "crypto-js";
import { azureHeaders, consoleLogger, getToken } from "../../../actions";
const WORDKEY = process.env.HASHKEY;
const WEBAPI_URL =
process.env.RELAY_ROOT ||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
const hashAPIPath = (queryPath) => {
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;
};
// export default async function ApiProxy(req, res) {
// var caseid = req.query.caseid;
// var token = await getToken();
// var queryUrl =
// "pinswg_sipsevents?$filter=_pinswg_sipseventsid_value eq " +
// caseid +
// "&$count=true";
// return axios
// .get(
// WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
// azureHeaders(token.access_token),
// )
// .then(({ data }) => {
// res.status(200).json(data);
// })
// .catch((error) => {
// consoleLogger(error);
// res.status(400).json(error);
// });
// }
export default function ApiProx(req, res) {
const data = {
"@odata.count": 4,
"value": [
{
"pinswg_publishtoweb@OData.Community.Display.V1.FormattedValue":
"Yes",
"pinswg_publishtoweb": true,
"pinswg_uploadstatus": 846040000,
"pinswg_name":
"Puffin Paradise? Exploring Wales Cutest Wildlife Spot (Skomer Travel Guide)",
"pinswg_description":
"Skomer Island is a tiny uninhabited isle off the coast of Pembrokeshire in western Wales. Although it's just ten minutes from the mainland by ferry, it's home to over 40,000 puffins, the largest colony in the world of Manx Shearwaters, and lots of seals.",
"pinswg_latestpublishedversion": "1.0",
"pinswg_latestpublisheddate@OData.Community.Display.V1.FormattedValue":
"2/2/2026",
"pinswg_latestpublisheddate": "2026-02-02T10:20:46Z",
"pinswg_documentpublisheddate": "2026-02-02T10:20:46Z",
"pinswg_documentid": "9d4550b7-2000-f111-aa10-002248008e24",
"createdon@OData.Community.Display.V1.FormattedValue":
"2/2/2026 10:19 AM",
"createdon": "2026-02-02T10:19:55Z",
"pinswg_hashlink":
"/api/documents/download/A22258739?hash=51d64c1330ed2fe1fb9aff4658b8eedadf1b062d2b0313b39bc5af786228324b",
"pinswg_mediaLinkEN":
"https://www.youtube.com/watch?v=H7S6CfF_7PI",
"pinswg_mediaLinkCY":
"https://www.youtube.com/watch?v=H7S6CfF_7PI",
},
{
"pinswg_publishtoweb@OData.Community.Display.V1.FormattedValue":
"Yes",
"pinswg_publishtoweb": true,
"pinswg_uploadstatus": 846040000,
"pinswg_name": "Redstone Cross - side roads",
"pinswg_description": "Redstone Cross - side roads",
"pinswg_latestpublishedversion": "1.0",
"pinswg_latestpublisheddate@OData.Community.Display.V1.FormattedValue":
"2/2/2026",
"pinswg_latestpublisheddate": "2026-02-02T10:20:46Z",
"pinswg_documentpublisheddate": "2026-02-02T10:20:46Z",
"pinswg_documentid": "9d4550b7-2000-f111-aa10-002248008e24",
"createdon@OData.Community.Display.V1.FormattedValue":
"2/2/2026 10:19 AM",
"createdon": "2026-02-02T10:19:55Z",
"pinswg_hashlink":
"/api/documents/download/A22258739?hash=51d64c1330ed2fe1fb9aff4658b8eedadf1b062d2b0313b39bc5af786228324b",
"pinswg_mediaLinkEN":
"https://www.youtube.com/watch?v=OhxeLV_jPyE",
"pinswg_mediaLinkCY":
"https://www.youtube.com/watch?v=OhxeLV_jPyE",
},
{
"pinswg_publishtoweb@OData.Community.Display.V1.FormattedValue":
"Yes",
"pinswg_publishtoweb": true,
"pinswg_uploadstatus": 846040000,
"pinswg_name":
"Apprenticeships: Dinorwig Power Station (bilingual)",
"pinswg_description": "blah blah blah",
"pinswg_latestpublishedversion": "1.0",
"pinswg_latestpublisheddate@OData.Community.Display.V1.FormattedValue":
"2/2/2026",
"pinswg_latestpublisheddate": "2026-02-02T10:20:46Z",
"pinswg_documentpublisheddate": "2026-02-02T10:20:46Z",
"pinswg_documentid": "9d4550b7-2000-f111-aa10-002248008e24",
"createdon@OData.Community.Display.V1.FormattedValue":
"2/2/2026 10:19 AM",
"createdon": "2026-02-02T10:19:55Z",
"pinswg_hashlink":
"/api/documents/download/A22258739?hash=51d64c1330ed2fe1fb9aff4658b8eedadf1b062d2b0313b39bc5af786228324b",
"pinswg_mediaLinkEN":
"https://www.youtube.com/watch?v=9nsmadjgW94",
"pinswg_mediaLinkCY":
"https://www.youtube.com/watch?v=9nsmadjgW94",
},
{
"pinswg_publishtoweb@OData.Community.Display.V1.FormattedValue":
"Yes",
"pinswg_publishtoweb": true,
"pinswg_uploadstatus": 846040000,
"pinswg_name":
"Climate Action Wales - Cardiff Cycle Workshop - Sustainable Cycling in Cardiff",
"pinswg_description": "blah blah blah",
"pinswg_latestpublishedversion": "1.0",
"pinswg_latestpublisheddate@OData.Community.Display.V1.FormattedValue":
"2/2/2026",
"pinswg_latestpublisheddate": "2026-02-02T10:20:46Z",
"pinswg_documentpublisheddate": "2026-02-02T10:20:46Z",
"pinswg_documentid": "9d4550b7-2000-f111-aa10-002248008e24",
"createdon@OData.Community.Display.V1.FormattedValue":
"2/2/2026 10:19 AM",
"createdon": "2026-02-02T10:19:55Z",
"pinswg_hashlink":
"/api/documents/download/A22258739?hash=51d64c1330ed2fe1fb9aff4658b8eedadf1b062d2b0313b39bc5af786228324b",
"pinswg_mediaLinkEN":
"https://www.youtube.com/watch?v=tRW3tm2Mby8",
"pinswg_mediaLinkCY":
"https://www.youtube.com/watch?v=tRW3tm2Mby8",
},
],
};
return res.status(200).json(data);
}
+10 -8
View File
@@ -84,7 +84,9 @@ const SignIn = (props) => {
<div className="govuk-body-m"> <div className="govuk-body-m">
<legend className="govuk-fieldset__legend govuk-fieldset__legend--l"> <legend className="govuk-fieldset__legend govuk-fieldset__legend--l">
<h1 className="govuk-fieldset__heading govuk-heading-m"> <h1 className="govuk-fieldset__heading govuk-heading-m">
{t("auth:auth-page-title")}{" "} {t(
"auth:auth-page-title",
)}{" "}
</h1> </h1>
</legend> </legend>
@@ -122,7 +124,7 @@ const SignIn = (props) => {
htmlFor="email" htmlFor="email"
> >
{t( {t(
"auth:auth-email-label" "auth:auth-email-label",
)} )}
</label> </label>
@@ -142,7 +144,7 @@ const SignIn = (props) => {
} }
> >
{t( {t(
"auth:auth-signin-button-label" "auth:auth-signin-button-label",
)} )}
</button> </button>
</div> </div>
@@ -163,10 +165,10 @@ const SignIn = (props) => {
}; };
export async function getServerSideProps(context) { export async function getServerSideProps(context) {
if (process.env.SHOWLOGIN == false) { if (process.env.SHOWLOGIN === "false" || process.env.SHOWLOGIN === false) {
return { return {
redirect: { redirect: {
destination: "/404", destination: "/status",
permanent: false, permanent: false,
}, },
}; };
@@ -177,7 +179,7 @@ export async function getServerSideProps(context) {
"\n//////////////////////\n", "\n//////////////////////\n",
"Sign in callbackurl: " + context.query.callbackUrl, "Sign in callbackurl: " + context.query.callbackUrl,
context.locale, context.locale,
"\n//////////////////////\n" "\n//////////////////////\n",
); );
let formURL = context.req.headers.host; let formURL = context.req.headers.host;
//console.log("formUrl:", formURL); //console.log("formUrl:", formURL);
@@ -207,7 +209,7 @@ export async function getServerSideProps(context) {
// Append the query parameters to the new URL // Append the query parameters to the new URL
params.forEach((value, key) => params.forEach((value, key) =>
toUrlObj.searchParams.append(key, value) toUrlObj.searchParams.append(key, value),
); );
return toUrlObj.toString(); // Return the full new URL as a string return toUrlObj.toString(); // Return the full new URL as a string
@@ -216,7 +218,7 @@ export async function getServerSideProps(context) {
typeof context.query.callbackUrl != "undefined" && typeof context.query.callbackUrl != "undefined" &&
(formURL = appendParamsAndPathToNewUrl( (formURL = appendParamsAndPathToNewUrl(
context.query.callbackUrl, context.query.callbackUrl,
formURL formURL,
)); ));
//formURL = appendParamsAndPathToNewUrl(context.query.callbackUrl, formURL); //formURL = appendParamsAndPathToNewUrl(context.query.callbackUrl, formURL);
+3 -3
View File
@@ -236,10 +236,10 @@ export async function getServerSideProps(context) {
}; };
} }
if (process.env.SHOWLOGIN == false) { if (process.env.SHOWLOGIN === "false" || process.env.SHOWLOGIN === false) {
return { return {
redirect: { redirect: {
destination: "/404", destination: "/status",
permanent: false, permanent: false,
}, },
}; };
@@ -250,7 +250,7 @@ export async function getServerSideProps(context) {
"\n//////////////////////\n", "\n//////////////////////\n",
"Sign in callbackurl: " + context.req.headers.host, "Sign in callbackurl: " + context.req.headers.host,
context.locale, context.locale,
csrfToken + "\n//////////////////////\n" csrfToken + "\n//////////////////////\n",
); );
let formURL = context.req.headers.host; let formURL = context.req.headers.host;
console.log("formUrl:", formURL); console.log("formUrl:", formURL);
+18 -6
View File
@@ -10,6 +10,7 @@ import {
getPersonalAccount, getPersonalAccount,
getPortalLogin, getPortalLogin,
getSIPSEvents, getSIPSEvents,
getSIPSMedia,
} from "../../actions"; } from "../../actions";
import Breadcrumbs from "../../components/breadcrumbs"; import Breadcrumbs from "../../components/breadcrumbs";
import Case from "../../components/case"; import Case from "../../components/case";
@@ -24,6 +25,7 @@ import {
setEventDetails, setEventDetails,
setSearchDetails, setSearchDetails,
setSearchResults, setSearchResults,
setMediaDetails,
} from "../../store/searchOutput/action"; } from "../../store/searchOutput/action";
import { wrapper } from "../../store/store"; import { wrapper } from "../../store/store";
import ServiceBanner from "../../components/servicebanner"; import ServiceBanner from "../../components/servicebanner";
@@ -104,11 +106,12 @@ const CaseHome = (props) => {
representationsObj={ representationsObj={
props.searchResultsObj.representationsObj props.searchResultsObj.representationsObj
} }
showLoginCheck={true} showLoginCheck={props.showLoginCheck}
messagesObj={props.messagesObj} messagesObj={props.messagesObj}
showFilteredDocs={props.showFilteredDocs} showFilteredDocs={props.showFilteredDocs}
showMaps={props.showMaps} showMaps={props.showMaps}
eventsObj={props.searchResultsObj.eventsObj} eventsObj={props.searchResultsObj.eventsObj}
mediaObj={props.searchResultsObj.mediaObj}
/> />
</div> </div>
<Footer <Footer
@@ -159,14 +162,22 @@ export const getServerSideProps = wrapper.getServerSideProps(
const searchDetailsObj = await getSearchDetails(searchResultsObj); const searchDetailsObj = await getSearchDetails(searchResultsObj);
var eventsObj = {}; var eventsObj = {};
var mediaObj = {};
if (searchResultsObj.value[0].pinswg_appealcasetype == 846040002) { if (searchResultsObj.value[0].pinswg_appealcasetype == 846040002) {
eventsObj = await getSIPSEvents( eventsObj = await getSIPSEvents(
searchDetailsObj[0].value[0].pinswg_sipsid searchDetailsObj[0].value[0].pinswg_sipsid,
); );
store.dispatch(setEventDetails(eventsObj)); store.dispatch(setEventDetails(eventsObj));
} }
if (searchResultsObj.value[0].pinswg_appealcasetype == 846040002) {
mediaObj = await getSIPSMedia(
searchDetailsObj[0].value[0].pinswg_sipsid,
);
store.dispatch(setMediaDetails(mediaObj));
}
//console.log( //console.log(
// "\n======================================\n", // "\n======================================\n",
// searchResultsObj, // searchResultsObj,
@@ -182,7 +193,7 @@ export const getServerSideProps = wrapper.getServerSideProps(
console.log( console.log(
searchResultsObj["@odata.count"] > 1 || searchResultsObj["@odata.count"] > 1 ||
searchResultsObj["@odata.count"] < 1 searchResultsObj["@odata.count"] < 1,
); );
if (searchResultsObj["@odata.count"] < 1) { if (searchResultsObj["@odata.count"] < 1) {
@@ -223,7 +234,7 @@ export const getServerSideProps = wrapper.getServerSideProps(
searchDetailsObj[0].value[0] searchDetailsObj[0].value[0]
.pinswg_specialistcaseprocess .pinswg_specialistcaseprocess
: "", : "",
}) }),
); );
} }
} }
@@ -234,17 +245,18 @@ export const getServerSideProps = wrapper.getServerSideProps(
searchResultsObj: searchResultsObj, searchResultsObj: searchResultsObj,
searchDetailsObj: searchDetailsObj, searchDetailsObj: searchDetailsObj,
eventsObj: eventsObj, eventsObj: eventsObj,
mediaObj: mediaObj,
}, },
currentType: "directResultsObj", currentType: "directResultsObj",
docsOffline: process.env.DOCAPI_OFFLINE || false, docsOffline: process.env.DOCAPI_OFFLINE || false,
messagesObj: await getCaseMessage( messagesObj: await getCaseMessage(
searchResultsObj.value[0].incidentid searchResultsObj.value[0].incidentid,
), ),
showFilteredDocs: process.env.SHOWFILTERDOCS || false, showFilteredDocs: process.env.SHOWFILTERDOCS || false,
showMaps: process.env.SHOWMAPS, showMaps: process.env.SHOWMAPS,
}, },
}; };
} },
); );
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
+70 -5
View File
@@ -1,28 +1,93 @@
import useTranslation from "next-translate/useTranslation"; import useTranslation from "next-translate/useTranslation";
import Head from "next/head"; import Head from "next/head";
import CookieBanner from "../components/cookieBanner";
import Header from "../components/header";
import Footer from "../components/footer";
import router from "next/router";
const FourOhFour = (props) => { const StatusPage = (props) => {
let { t, lang } = useTranslation(); let { t, lang } = useTranslation();
const { footerLinks, pages } = props; const { footerLinks, pages } = props;
return ( return (
<div> <div>
<Head> <Head>
<title>Status Up</title> <title>Site Status</title>
</Head> </Head>
<div id="page_wrapper"> <div id="page_wrapper">
<CookieBanner />
<Header />
<div className="govuk-width-container govuk-!-padding-bottom-9"> <div className="govuk-width-container govuk-!-padding-bottom-9">
<main className="govuk-main-wrapper govuk-!-padding-top-9 govuk-!-padding-bottom-9"> <main className="govuk-main-wrapper govuk-!-padding-top-9 govuk-!-padding-bottom-9">
<div className="govuk-grid-row"> <div className="govuk-grid-row">
<div className="govuk-grid-column-two-thirds "> <div
<h1>Status Up</h1> className="govuk-warning-text noticebanner govuk-!-margin-bottom-0"
key={1}
>
<strong className="govuk-warning-text__text">
<span className="govuk-visually-hidden">
Announcement
</span>
</strong>
<div>
<div className=" govuk-body-s">
<h2 className="govuk-heading-s">
{router.locale == "cy"
? "Cyhoeddiad"
: "Announcement"}
</h2>
</div>
<div className="govuk-body-s">
{router.locale == "cy" ? (
<>
<p>
Oherwydd gwaith cynnal a
chadw hanfodol, rydym yn
disgwyl rhywfaint o darfu ar
ein gwasanaethau Porth
Gwaith Achos rhwng 9-13
Chwefror 2026.{" "}
</p>
<p>
Ymddiheurwn am yr
anghyfleustra. Am ymholiadau
brys yn ystod y cyfnod hwn,
cysylltwch â{" "}
<a href="mailto:pedw.gwaithachos@llyw.cymru">
pedw.gwaithachos@llyw.cymru
</a>
</p>
</>
) : (
<>
<p>
Due to essential
maintenance, we are
expecting some disruption to
our Casework Portal services
between 9-13 February 2026.
</p>
<p>
We apologise for the
inconvenience. For urgent
enquries during this time
please contact{" "}
<a href="mailto:pedw.casework@gov.wales">
pedw.casework@gov.wales
</a>
</p>
</>
)}
</div>
</div>
</div> </div>
</div> </div>
</main> </main>
</div> </div>
<Footer footerLinks={footerLinks} ticketnumber={props} />
</div> </div>
</div> </div>
); );
}; };
export default FourOhFour; export default StatusPage;
+8
View File
@@ -8,6 +8,7 @@ export const searchResultsActionTypes = {
SETREPRESENTATIONS: "SETREPRESENTATIONS", SETREPRESENTATIONS: "SETREPRESENTATIONS",
SETDNSCOORDS: "SETDNSCOORDS", SETDNSCOORDS: "SETDNSCOORDS",
SETEVENTDETAILS: "SETEVENTDETAILS", SETEVENTDETAILS: "SETEVENTDETAILS",
SETMEDIADETAILS: "SETMEDIADETAILS",
}; };
export const getSearchResultshObj = () => (dispatch) => { export const getSearchResultshObj = () => (dispatch) => {
@@ -63,3 +64,10 @@ export const setEventDetails = (eventsDetails) => (dispatch) => {
eventDetailsObj: eventsDetails, eventDetailsObj: eventsDetails,
}); });
}; };
export const setMediaDetails = (mediaDetails) => (dispatch) => {
return dispatch({
type: searchResultsActionTypes.SETMEDIADETAILS,
mediaDetailsObj: mediaDetails,
});
};
+6
View File
@@ -7,6 +7,7 @@ const searchResultsInitialState = {
representationsObj: {}, representationsObj: {},
dnsCoordsObj: {}, dnsCoordsObj: {},
eventDetailsObj: {}, eventDetailsObj: {},
mediaDetailsObj: {},
}; };
export default function reducer(state = searchResultsInitialState, action) { export default function reducer(state = searchResultsInitialState, action) {
@@ -42,6 +43,11 @@ export default function reducer(state = searchResultsInitialState, action) {
...state, ...state,
eventDetailsObj: action.eventDetailsObj, eventDetailsObj: action.eventDetailsObj,
}; };
case searchResultsActionTypes.SETMEDIADETAILS:
return {
...state,
mediaDetailsObj: action.mediaDetailsObj,
};
default: default:
return state; return state;
} }
+104
View File
@@ -3165,3 +3165,107 @@ ul.subFieldList {
box-decoration-break: clone; box-decoration-break: clone;
line-height: 1.4; line-height: 1.4;
} }
#case-media {
.videoWrapper {
position: relative;
padding-bottom: 56.25%;
height: 0;
overflow: hidden;
border: 1px solid #b1b4b6;
border-radius: 4px;
}
.videoWrapper iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.videoTabButton {
width: 100%;
text-align: left;
background: transparent;
border: 1px solid #b1b4b6;
border-radius: 4px;
padding: 10px 12px;
cursor: pointer;
}
.videoTabButton:hover {
background: #f3f2f1;
}
.videoTabButton.active {
border-color: #1d70b8;
box-shadow: inset 0 0 0 1px #1d70b8;
background: #eef4ff;
}
.videoTabContent {
display: flex;
gap: 12px;
align-items: center;
}
.thumbnailWrapper {
position: relative;
width: 120px;
flex-shrink: 0;
}
.videoThumbnail {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
border-radius: 4px;
border: 1px solid #b1b4b6;
background: #000;
}
.playIcon {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 32px;
color: white;
text-shadow: 0 0 6px rgba(0, 0, 0, 0.7);
pointer-events: none;
}
.durationBadge {
position: absolute;
bottom: 6px;
right: 6px;
background: rgba(0, 0, 0, 0.8);
color: white;
font-size: 12px;
padding: 2px 6px;
border-radius: 3px;
}
.watchedBadge {
position: absolute;
top: 6px;
left: 6px;
background: #00703c;
color: white;
font-size: 11px;
padding: 2px 6px;
border-radius: 3px;
}
.videoTitle {
font-weight: 600;
}
@media (max-width: 40.0625em) {
.thumbnailWrapper {
width: 90px;
}
}
}