Fixed add case bug
This commit is contained in:
@@ -40,12 +40,14 @@
|
||||
<link href="/matrix/css/matrix-shell-v2.css?v=7" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/tool-workspace-v3.css?v=2" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/punch-workspace-v3.css?v=2" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/add-cases-v4.css?v=6" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/add-cases-v4.css?v=6" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/date-time-input-v1.css?v=1" rel="stylesheet" type="text/css" />
|
||||
<script src="/matrix/js/bootstrap-5.3.8.bundle.min.js" defer></script>
|
||||
<script src="/matrix/js/matrix-shell-v2.js" defer></script>
|
||||
<link href="/matrix/css/matrix-frame-dialog.css?v=6" rel="stylesheet" type="text/css" />
|
||||
<script src="/matrix/js/matrix-frame-dialog.js?v=2" defer></script>
|
||||
<script src="/matrix/js/matrix-accessibility-v1.js?v=1" defer></script>
|
||||
<script src="/matrix/js/matrix-accessibility-v1.js?v=1" defer></script>
|
||||
<script src="/matrix/js/lib/date-time-input-v1.js?v=1" defer></script>
|
||||
<link href="/matrix/css/matrix-theme-v2.css?v=4" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
|
||||
@@ -26,14 +26,14 @@
|
||||
<link href="/matrix/css/matrix-v2.css?v=6" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/matrix-shell-v2.css?v=7" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/tool-workspace-v3.css?v=2" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/mis-filter-v4.css?v=1" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/date-time-input-v1.css?v=1" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/mis-filter-v4.css?v=2" rel="stylesheet" type="text/css" />
|
||||
<link href="/matrix/css/date-time-input-v1.css?v=2" rel="stylesheet" type="text/css" />
|
||||
<script src="/matrix/js/bootstrap-5.3.8.bundle.min.js" defer></script>
|
||||
<script src="/matrix/js/matrix-shell-v2.js" defer></script>
|
||||
<link href="/matrix/css/matrix-frame-dialog.css?v=6" rel="stylesheet" type="text/css" />
|
||||
<script src="/matrix/js/matrix-frame-dialog.js?v=2" defer></script>
|
||||
<script src="/matrix/js/matrix-accessibility-v1.js?v=1" defer></script>
|
||||
<script src="/matrix/js/lib/date-time-input-v1.js?v=1" defer></script>
|
||||
<script src="/matrix/js/lib/date-time-input-v1.js?v=2" defer></script>
|
||||
<link href="/matrix/css/matrix-theme-v2.css?v=4" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
.matrix-date-control {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.matrix-date-control > .matrix-date-input {
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
padding-right: 38px !important;
|
||||
}
|
||||
|
||||
.matrix-date-control__button {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
right: 1px;
|
||||
bottom: 1px;
|
||||
display: grid;
|
||||
width: 34px;
|
||||
padding: 0;
|
||||
place-items: center;
|
||||
border: 0;
|
||||
border-left: 1px solid #c4d0da;
|
||||
border-radius: 0 3px 3px 0;
|
||||
background: #edf3f7;
|
||||
color: #285f8f;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.matrix-date-control__button:hover,
|
||||
.matrix-date-control__button:focus-visible {
|
||||
background: #dfeaf3;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.matrix-date-control__picker {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel .matrix-date-field {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
@@ -64,25 +64,43 @@
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel {
|
||||
padding: 20px 18px;
|
||||
height: auto !important;
|
||||
min-height: 0 !important;
|
||||
padding: 16px 18px;
|
||||
border: 1px solid #c9d4de;
|
||||
border-radius: 0 0 5px 5px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel table {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel table tbody,
|
||||
.matrix-mis-filter .FormPanel table tr,
|
||||
.matrix-mis-filter .FormPanel table tr td {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel table tr td {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: end;
|
||||
gap: 14px 18px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel table tr:first-child td {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel table tr:last-child td {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 14px;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel .widget {
|
||||
display: flex;
|
||||
min-width: 230px;
|
||||
@@ -124,12 +142,15 @@
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel input.button {
|
||||
width: auto !important;
|
||||
min-width: 96px;
|
||||
min-height: 34px;
|
||||
padding: 6px 14px 6px 30px !important;
|
||||
border-color: #7f8b96;
|
||||
border-radius: 4px;
|
||||
background-color: #e5e8eb;
|
||||
float: none !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.matrix-mis-filter .FormPanel br {
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
(() => {
|
||||
"use strict";
|
||||
|
||||
const pad = (value) => String(value).padStart(2, "0");
|
||||
|
||||
const parseDisplayValue = (value, mode) => {
|
||||
const pattern = mode === "datetime"
|
||||
? /^(\d{2})\/(\d{2})\/(\d{4}) (\d{2}):(\d{2}):(\d{2})$/
|
||||
: /^(\d{2})\/(\d{2})\/(\d{4})$/;
|
||||
const match = value.trim().match(pattern);
|
||||
if (!match) return "";
|
||||
|
||||
const [, day, month, year, hour = "00", minute = "00", second = "00"] = match;
|
||||
const date = new Date(Number(year), Number(month) - 1, Number(day), Number(hour), Number(minute), Number(second));
|
||||
if (date.getFullYear() !== Number(year) || date.getMonth() !== Number(month) - 1
|
||||
|| date.getDate() !== Number(day) || date.getHours() !== Number(hour)
|
||||
|| date.getMinutes() !== Number(minute) || date.getSeconds() !== Number(second)) return "";
|
||||
|
||||
const isoDate = `${year}-${month}-${day}`;
|
||||
return mode === "datetime" ? `${isoDate}T${hour}:${minute}:${second}` : isoDate;
|
||||
};
|
||||
|
||||
const formatPickerValue = (value, mode) => {
|
||||
if (!value) return "";
|
||||
const [datePart, timePart = "00:00:00"] = value.split("T");
|
||||
const [year, month, day] = datePart.split("-");
|
||||
if (!year || !month || !day) return "";
|
||||
if (mode === "date") return `${day}/${month}/${year}`;
|
||||
const [hour = "00", minute = "00", second = "00"] = timePart.split(":");
|
||||
return `${day}/${month}/${year} ${pad(hour)}:${pad(minute)}:${pad(second)}`;
|
||||
};
|
||||
|
||||
const initialise = (input) => {
|
||||
if (input.dataset.datePickerReady === "true") return;
|
||||
input.dataset.datePickerReady = "true";
|
||||
|
||||
const mode = input.dataset.dateMode === "date" ? "date" : "datetime";
|
||||
const wrapper = document.createElement("span");
|
||||
wrapper.className = "matrix-date-control";
|
||||
input.parentNode.insertBefore(wrapper, input);
|
||||
wrapper.appendChild(input);
|
||||
|
||||
const picker = document.createElement("input");
|
||||
picker.type = mode === "datetime" ? "datetime-local" : "date";
|
||||
picker.className = "matrix-date-control__picker";
|
||||
picker.tabIndex = -1;
|
||||
picker.setAttribute("aria-hidden", "true");
|
||||
if (mode === "datetime") picker.step = "1";
|
||||
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "matrix-date-control__button";
|
||||
button.setAttribute("aria-label", mode === "datetime" ? "Choose date and time" : "Choose date");
|
||||
button.innerHTML = "📅";
|
||||
|
||||
wrapper.append(picker, button);
|
||||
input.closest(".widget")?.classList.add("matrix-date-field");
|
||||
|
||||
const syncPicker = () => {
|
||||
const parsed = parseDisplayValue(input.value, mode);
|
||||
if (parsed) picker.value = parsed;
|
||||
};
|
||||
syncPicker();
|
||||
|
||||
picker.addEventListener("change", () => {
|
||||
input.value = formatPickerValue(picker.value, mode);
|
||||
input.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
input.focus();
|
||||
});
|
||||
input.addEventListener("change", syncPicker);
|
||||
button.addEventListener("click", () => {
|
||||
syncPicker();
|
||||
if (typeof picker.showPicker === "function") picker.showPicker();
|
||||
else picker.click();
|
||||
});
|
||||
};
|
||||
|
||||
const initialiseAll = (root = document) => root.querySelectorAll(".matrix-date-input").forEach(initialise);
|
||||
document.addEventListener("DOMContentLoaded", () => initialiseAll());
|
||||
window.CygnusDateTimeInputs = { initialise: initialiseAll };
|
||||
})();
|
||||
@@ -6,10 +6,12 @@ import java.util.Arrays;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
|
||||
import matrix.nimble.mis.model.Filters;
|
||||
import matrix.nimble.mis.model.MISHandler;
|
||||
import matrix.nimble.model.Session;
|
||||
import matrix.nimble.mis.model.Filters;
|
||||
import matrix.nimble.mis.model.MISHandler;
|
||||
import matrix.nimble.cloud.identity.CloudSessionMapper;
|
||||
import matrix.nimble.model.Session;
|
||||
import matrix.nimble.utilities.GlobalClass;
|
||||
import matrix.services.commons.CommonService;
|
||||
import matrix.services.mis.HourlyMisService;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
@@ -27,9 +29,31 @@ import org.springframework.http.ResponseEntity;
|
||||
@SessionAttributes({"Sessvals"})
|
||||
public class MIS {
|
||||
private final HourlyMisService hourlyMisService;
|
||||
private final CommonService commonService;
|
||||
private final CloudSessionMapper cloudSessionMapper;
|
||||
|
||||
public MIS(HourlyMisService hourlyMisService) {
|
||||
public MIS(
|
||||
HourlyMisService hourlyMisService,
|
||||
CommonService commonService,
|
||||
CloudSessionMapper cloudSessionMapper) {
|
||||
this.hourlyMisService = hourlyMisService;
|
||||
this.commonService = commonService;
|
||||
this.cloudSessionMapper = cloudSessionMapper;
|
||||
}
|
||||
|
||||
@ModelAttribute("Sessvals")
|
||||
public Session legacySession(HttpSession httpSession) {
|
||||
Session legacySession = commonService.getSession(httpSession);
|
||||
if (legacySession != null) {
|
||||
return legacySession;
|
||||
}
|
||||
var userSession = commonService.getUserSession(httpSession);
|
||||
if (userSession == null) {
|
||||
return null;
|
||||
}
|
||||
legacySession = cloudSessionMapper.toLegacy(userSession);
|
||||
commonService.storeSession(httpSession, legacySession);
|
||||
return legacySession;
|
||||
}
|
||||
@RequestMapping(value="datetimerange",method=RequestMethod.POST )
|
||||
public String DateRangeFilter(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals,HttpSession session)
|
||||
|
||||
@@ -12,18 +12,33 @@ public final class CloudQuerySource implements QuerySource {
|
||||
|
||||
@Override
|
||||
public String fetch(int queryId) {
|
||||
return client.fetchQuery(queryId)
|
||||
.map(response -> response.query())
|
||||
.blockOptional()
|
||||
.filter(query -> !query.isBlank())
|
||||
.orElseThrow(() -> new QueryProviderException(
|
||||
"Cloud query was empty: " + queryId));
|
||||
try {
|
||||
return client.fetchQuery(queryId)
|
||||
.map(response -> response.query())
|
||||
.blockOptional()
|
||||
.filter(query -> !query.isBlank())
|
||||
.orElseThrow(() -> new QueryProviderException(
|
||||
"Cloud query was empty: " + queryId));
|
||||
} catch (QueryProviderException exception) {
|
||||
throw exception;
|
||||
} catch (RuntimeException exception) {
|
||||
throw new QueryProviderException(
|
||||
"Cloud query request failed for query " + queryId, exception);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fetch(String queryKey) {
|
||||
return client.fetchQuery(queryKey).map(response -> response.query()).blockOptional()
|
||||
.filter(query -> !query.isBlank())
|
||||
.orElseThrow(() -> new QueryProviderException("Cloud query was empty: " + queryKey));
|
||||
try {
|
||||
return client.fetchQuery(queryKey).map(response -> response.query()).blockOptional()
|
||||
.filter(query -> !query.isBlank())
|
||||
.orElseThrow(() -> new QueryProviderException(
|
||||
"Cloud query was empty: " + queryKey));
|
||||
} catch (QueryProviderException exception) {
|
||||
throw exception;
|
||||
} catch (RuntimeException exception) {
|
||||
throw new QueryProviderException(
|
||||
"Cloud query request failed for query " + queryKey, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import lib.models.UserSession;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -151,6 +152,9 @@ public class CommonService {
|
||||
}
|
||||
return cloudIdentityClient.fetchData(scope, data)
|
||||
.map(items -> items.stream().map(this::option).toList())
|
||||
.onErrorMap(TimeoutException.class, exception -> new IllegalStateException(
|
||||
"Cloud platform-data request timed out for scope: " + scope,
|
||||
exception))
|
||||
.blockOptional()
|
||||
.orElseGet(List::of);
|
||||
}
|
||||
|
||||
@@ -1,38 +1,45 @@
|
||||
package matrix.services.mis;
|
||||
|
||||
import com.cygnus.db.CygnusDbExecutor;
|
||||
import com.cygnus.db.RowView;
|
||||
import com.cygnus.client.CloudIdentityClient;
|
||||
import com.cygnus.client.model.CloudDataItem;
|
||||
import lib.models.ReportColumnDefinition;
|
||||
import lib.models.ReportDefinition;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
@Service
|
||||
public class ReportDefinitionService {
|
||||
private static final String DEFINITION_QUERY = "report-definition";
|
||||
private final CygnusDbExecutor dbExecutor;
|
||||
private static final String DEFINITION_SCOPE = "report-definition";
|
||||
private final CloudIdentityClient cloudIdentityClient;
|
||||
|
||||
public ReportDefinitionService(CygnusDbExecutor dbExecutor) {
|
||||
this.dbExecutor = dbExecutor;
|
||||
public ReportDefinitionService(CloudIdentityClient cloudIdentityClient) {
|
||||
this.cloudIdentityClient = cloudIdentityClient;
|
||||
}
|
||||
|
||||
public ReportDefinition hourlyMis(String fallbackTitle) {
|
||||
try {
|
||||
List<RowView> rows = dbExecutor.query(DEFINITION_QUERY, new Object[] { "hourly-mis" });
|
||||
List<CloudDataItem> rows = cloudIdentityClient
|
||||
.fetchData(DEFINITION_SCOPE, Map.of("reportKey", "hourly-mis"))
|
||||
.onErrorMap(TimeoutException.class,
|
||||
exception -> new IllegalStateException("Cloud report definition request timed out", exception))
|
||||
.blockOptional().orElseGet(List::of);
|
||||
if (rows.isEmpty()) return defaults(fallbackTitle);
|
||||
RowView first = rows.getFirst();
|
||||
Map<String, Object> first = rows.getFirst().data();
|
||||
List<ReportColumnDefinition> columns = new ArrayList<>(rows.size());
|
||||
for (RowView row : rows) {
|
||||
for (CloudDataItem item : rows) {
|
||||
Map<String, Object> row = item.data();
|
||||
columns.add(new ReportColumnDefinition(
|
||||
text(row, "column_key"), text(row, "column_label"),
|
||||
number(row, "display_order"), text(row, "alignment"),
|
||||
number(row, "column_width"), bool(row, "is_visible"),
|
||||
bool(row, "is_totalled")));
|
||||
text(row, "columnKey"), text(row, "columnLabel"),
|
||||
number(row, "displayOrder"), text(row, "alignment"),
|
||||
number(row, "columnWidth"), bool(row, "visible"),
|
||||
bool(row, "totalled")));
|
||||
}
|
||||
return new ReportDefinition("hourly-mis", text(first, "report_title"),
|
||||
bool(first, "hide_leading_zero_columns"), List.copyOf(columns));
|
||||
return new ReportDefinition("hourly-mis", text(first, "reportTitle"),
|
||||
bool(first, "hideLeadingZeroColumns"), List.copyOf(columns));
|
||||
} catch (RuntimeException unavailable) {
|
||||
return defaults(fallbackTitle);
|
||||
}
|
||||
@@ -49,7 +56,7 @@ public class ReportDefinitionService {
|
||||
return new ReportDefinition("hourly-mis", title, true, List.copyOf(columns));
|
||||
}
|
||||
|
||||
private String text(RowView row, String column) { return String.valueOf(row.get(column)); }
|
||||
private int number(RowView row, String column) { return ((Number) row.get(column)).intValue(); }
|
||||
private boolean bool(RowView row, String column) { return Boolean.TRUE.equals(row.get(column)); }
|
||||
private String text(Map<String, Object> row, String column) { return String.valueOf(row.get(column)); }
|
||||
private int number(Map<String, Object> row, String column) { return ((Number) row.get(column)).intValue(); }
|
||||
private boolean bool(Map<String, Object> row, String column) { return Boolean.TRUE.equals(row.get(column)); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user