Fixed add case bug

This commit is contained in:
2026-08-12 18:10:39 +05:30
parent a757799724
commit 03dfdd6218
16 changed files with 302 additions and 56 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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 = "&#128197;";
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 };
})();

View File

@@ -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)

View File

@@ -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);
}
}
}

View File

@@ -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);
}

View File

@@ -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)); }
}