Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package org.transitclock.reports;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.transitclock.db.GenericQuery;

/**
* Postgres-catalog queries for per-table on-disk size, returned as
* Google Charts DataTable JSON. Used by status/dbDiskSpace.jsp (server-side
* render) and by the JAX-RS endpoint at /api/status/db-disk-space (external
* authorized clients). Centralized here so the SQL doesn't drift between
* the two callers.
* Single source of truth for per-table on-disk-size SQL. Centralized so the
* chart, dashboard, and API callers can't drift out of sync on filters.
*/
public final class DbDiskSpaceQuery {

Expand Down Expand Up @@ -58,6 +58,22 @@ public final class DbDiskSpaceQuery {
+ " AND nspname !~ '^pg_toast' "
+ "ORDER BY pg_total_relation_size(C.oid) DESC";

// Filters must mirror TOTALS_SQL's inner SELECT so the dashboard bar
// chart and the dbDiskSpace page report the same per-table sizes.
// LIMIT is appended at runtime from a clamped int.
private static final String TOP_TABLES_SQL =
"SELECT relname, "
+ "pg_size_pretty(pg_total_relation_size(C.oid)) AS total_size, "
+ "pg_total_relation_size(C.oid) AS total_bytes "
+ "FROM pg_class C "
+ "LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace) "
+ "WHERE nspname NOT IN ('pg_catalog', 'information_schema') "
+ " AND C.relkind <> 'i' "
+ " AND nspname !~ '^pg_toast' "
+ "ORDER BY pg_total_relation_size(C.oid) DESC";

public record TableSize(String tableName, String prettySize, long bytes) {}

private DbDiskSpaceQuery() {}

public static String getTotalsJson(String agencyId) throws SQLException {
Expand All @@ -67,4 +83,37 @@ public static String getTotalsJson(String agencyId) throws SQLException {
public static String getDetailsJson(String agencyId) throws SQLException {
return ChartGenericJsonQuery.getJsonString(agencyId, DETAILS_SQL);
}

/**
* Top-N largest user tables by on-disk size, biggest first. Limit is
* clamped to [1, 1000] before being inlined into the SQL — the value is
* caller-supplied but never user-controlled; the clamp guards against
* accidental zero/negative or absurd page sizes.
*/
public static List<TableSize> getTopTables(String agencyId, int limit) throws SQLException {
int clamped = Math.max(1, Math.min(limit, 1000));
return TopTablesQuery.run(agencyId, TOP_TABLES_SQL + " LIMIT " + clamped);
}

private static final class TopTablesQuery extends GenericQuery {
private final List<TableSize> rows = new ArrayList<>();

private TopTablesQuery(String agencyId) throws SQLException {
super(agencyId);
}

static List<TableSize> run(String agencyId, String sql) throws SQLException {
TopTablesQuery q = new TopTablesQuery(agencyId);
q.doQuery(sql);
return q.rows;
}

@Override
protected void addRow(List<Object> values) {
String name = String.valueOf(values.get(0));
String pretty = String.valueOf(values.get(1));
long bytes = ((Number) values.get(2)).longValue();
rows.add(new TableSize(name, pretty, bytes));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
hello.label.welcome = Welcome
div.agencies = Agencies
div.dashboard = Dashboard
div.maps = Maps
div.reports = Reports
div.api = Api
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
hello.label.welcome = Witaj
div.agencies = Agencje
div.dashboard = Pulpit
div.maps = Mapy
div.reports = Raporty
div.api = Api
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<%@ tag pageEncoding="UTF-8" %>
<%@ taglib prefix="fmt" uri="jakarta.tags.fmt" %>
<%-- The five-stat strip shown on /status/activeBlocks.jsp and /dashboard.
Both pages mount the active-blocks Stimulus controller; the data-field
names here are the controller's #renderSummary contract. Keep that
contract here in one place so callers can't drift. --%>
<div data-active-blocks-target="summary"
class="flex items-stretch bg-white border border-gray-200 rounded-lg overflow-hidden shadow-[0_1px_2px_rgba(0,0,0,0.03)]">
<div class="flex-1 min-w-0 px-4 py-3 border-r border-gray-200">
<div class="text-[11px] font-semibold uppercase tracking-wider text-gray-500"><fmt:message key="div.blocks"/></div>
<div class="flex items-baseline gap-1.5 mt-0.5">
<span data-field="total-blocks" class="text-[22px] font-bold tabular-nums text-gray-900">—</span>
<span class="text-xs text-gray-500 font-medium">total</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Localize the remaining hard-coded label.

total is the only visible label in this shared component that is still English, so Polish and future locales will render it inconsistently.

Suggested fix
-      <span class="text-xs text-gray-500 font-medium">total</span>
+      <span class="text-xs text-gray-500 font-medium"><fmt:message key="div.total"/></span>

Add the matching div.total entry to the locale bundles as well.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<span class="text-xs text-gray-500 font-medium">total</span>
<span class="text-xs text-gray-500 font-medium"><fmt:message key="div.total"/></span>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@transitclockWebapp/src/main/webapp/WEB-INF/tags/activeBlocksSummary.tag` at
line 13, Replace the hard-coded English label in activeBlocksSummary.tag (the
<span class="text-xs text-gray-500 font-medium">total</span>) with a
localization lookup (use the same message key naming used elsewhere in this
component), and add a matching "div.total" entry to each locale bundle so Polish
and other locales render correctly; ensure the tag uses the same resource bundle
reference pattern as other labels in activeBlocksSummary.tag.

</div>
</div>
<div class="flex-1 min-w-0 px-4 py-3 border-r border-gray-200">
<div class="text-[11px] font-semibold uppercase tracking-wider text-gray-500"><fmt:message key="div.assigned"/></div>
<div class="flex items-baseline gap-1.5 mt-0.5">
<span data-field="percent-assigned" class="text-[22px] font-bold tabular-nums text-brand-accent">—</span>
<span data-field="assigned-detail" class="text-xs text-gray-500 font-medium tabular-nums"></span>
</div>
</div>
<div class="flex-1 min-w-0 px-4 py-3 border-r border-gray-200">
<div class="text-[11px] font-semibold uppercase tracking-wider text-gray-500"><fmt:message key="div.contime"/></div>
<div class="flex items-baseline gap-1.5 mt-0.5">
<span data-field="percent-on-time" class="text-[22px] font-bold tabular-nums text-status-on-time-ink">—</span>
<span data-field="on-time-count" class="text-xs text-gray-500 font-medium tabular-nums"></span>
</div>
</div>
<div class="flex-1 min-w-0 px-4 py-3 border-r border-gray-200">
<div class="text-[11px] font-semibold uppercase tracking-wider text-gray-500"><fmt:message key="div.clate"/></div>
<div class="flex items-baseline gap-1.5 mt-0.5">
<span data-field="percent-late" class="text-[22px] font-bold tabular-nums text-status-late">—</span>
<span data-field="late-count" class="text-xs text-gray-500 font-medium tabular-nums"></span>
</div>
</div>
<div class="flex-1 min-w-0 px-4 py-3">
<div class="text-[11px] font-semibold uppercase tracking-wider text-gray-500"><fmt:message key="div.cearly"/></div>
<div class="flex items-baseline gap-1.5 mt-0.5">
<span data-field="percent-early" class="text-[22px] font-bold tabular-nums text-status-early">—</span>
<span data-field="early-count" class="text-xs text-gray-500 font-medium tabular-nums"></span>
</div>
</div>
</div>
9 changes: 9 additions & 0 deletions transitclockWebapp/src/main/webapp/WEB-INF/tags/layout.tag
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<%-- Any /reports/* page (except the API calls section) keeps the
Reports nav highlighted so subpages don't appear unrooted. --%>
<c:set var="onReports" value="${fn:startsWith(path, '/reports/') and not onApi}"/>
<c:set var="onDashboard" value="${path == '/dashboard/index.jsp'}"/>
<c:set var="onActiveBlocks" value="${path == '/status/activeBlocks.jsp'}"/>
<c:set var="onServerStatus" value="${path == '/status/serverStatus.jsp'}"/>
<c:set var="onDbDiskSpace" value="${path == '/status/dbDiskSpace.jsp'}"/>
Expand Down Expand Up @@ -97,6 +98,7 @@
<c:set var="mapsExpanded" value="${mapForActive or mapInclActive or schAdhMapActive}"/>
<c:set var="reportsActive" value="${onReports and agencyMatch}"/>
<c:set var="apiActive" value="${onApi and agencyMatch}"/>
<c:set var="dashboardActive" value="${onDashboard and agencyMatch}"/>
<c:set var="activeBlocksActive" value="${onActiveBlocks and agencyMatch}"/>
<c:set var="serverStatusActive" value="${onServerStatus and agencyMatch}"/>
<c:set var="dbDiskSpaceActive" value="${onDbDiskSpace and agencyMatch}"/>
Expand All @@ -105,6 +107,13 @@
<div>
<h3 class="px-2.5 mb-2 text-[11px] font-bold tracking-[0.08em] uppercase text-gray-500"><c:out value="${agency.agencyName}"/></h3>
<ul class="space-y-0.5">
<li>
<a class="${navBase} ${dashboardActive ? navActive : navInactive}" <c:if test="${dashboardActive}">aria-current="page"</c:if> href="${ctx}/dashboard/index.jsp${qs}">
<c:if test="${dashboardActive}"><span class="absolute left-0 top-1.5 bottom-1.5 w-[3px] rounded-r-sm bg-brand-accent"></span></c:if>
<svg class="size-4 ${dashboardActive ? iconActive : iconIdle}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="7" height="9" rx="1"/><rect x="14" y="3" width="7" height="5" rx="1"/><rect x="14" y="12" width="7" height="9" rx="1"/><rect x="3" y="16" width="7" height="5" rx="1"/></svg>
<fmt:message key="div.dashboard"/>
</a>
</li>
<li data-controller="disclosure">
<button type="button" aria-expanded="${mapsExpanded}"
data-action="click->disclosure#toggle"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<%@ tag pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<%@ taglib prefix="fmt" uri="jakarta.tags.fmt" %>
<%@ attribute name="monitorResults" required="false" type="java.util.List" %>
<%@ attribute name="error" required="false" %>
<c:choose>
<c:when test="${not empty error}">
<div class="rounded-lg border border-red-200 bg-red-50 p-4">
<h3 class="text-sm font-semibold text-red-900"><fmt:message key="div.coreUnreachable"/></h3>
<p class="mt-1 text-sm text-red-800 font-mono break-all"><c:out value="${error}"/></p>
</div>
</c:when>
<c:otherwise>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<c:forEach var="monitorResult" items="${monitorResults}">
<c:if test="${not empty monitorResult.message}">
<article class="bg-white border border-gray-200 rounded-lg p-4">
<h3 class="text-xs font-semibold uppercase tracking-wider text-gray-500">
<c:out value="${monitorResult.type}"/>
</h3>
<c:choose>
<c:when test="${not empty monitorResult.stats}">
<dl class="mt-3 grid grid-cols-2 gap-x-4 gap-y-1.5 text-sm">
<c:forEach var="stat" items="${monitorResult.stats}">
<div class="flex items-baseline gap-1.5 col-span-2 sm:col-span-1">
<dt class="text-gray-500"><c:out value="${stat.key}"/>:</dt>
<dd class="font-mono text-gray-900"><c:out value="${stat.value}"/></dd>
</div>
</c:forEach>
</dl>
</c:when>
<c:otherwise>
<p class="mt-2 text-sm text-gray-900 leading-relaxed tabular-nums">
<c:out value="${monitorResult.message}"/>
</p>
</c:otherwise>
</c:choose>
</article>
</c:if>
</c:forEach>
</div>
</c:otherwise>
</c:choose>
84 changes: 0 additions & 84 deletions transitclockWebapp/src/main/webapp/css/general.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,6 @@
/*
* This file contains general CSS config for TheTransitClock pages
*/
body {
font-family: sans-serif;
font-size: large;
margin: 0px 0px 10px 0px;
}

#mainDiv {
margin-left: auto;
margin-right: auto;
width: 700px;
}

#title {
margin-top: 40px;
margin-bottom: 2px;
font-weight: normal;
font-size: xx-large;
text-align: center;
}

#subtitle {
text-align: center;
font-size: x-large;
}

/* For list of choices to be centered in page, e.g. list of reports
*/
.choicesList {
width: 350px;
margin-left: auto;
margin-right: auto;
line-height: 125%;
margin-top: 5px;
}

.ui-tooltip {
/* Change background color of tooltips a bit and use a reasonable font size */
background: #faf7f1;
font-size: small;
padding: 4px;
}

/* Pre-Tailwind input chrome. Scoped to .param so it doesn't bleed onto
.form-control or partials that have already migrated to Tailwind
(jsonXmlFormat.jsp, submitApiCall.jsp). /reports/apiCalls/* pages
render hybrid until they're fully ported. */
.param > input, .param > select {
background-color: #f8f8f8;
font-size: large;
}

.form-control {
display: block;
Expand Down Expand Up @@ -88,37 +38,3 @@ select.form-control:not([multiple]) {
padding-right: 2.5rem;
}

.earlyColor {
background: #f2b7cd;
border-radius: 4px;
}

.lateColor {
background: #fbfd97;
border-radius: 4px;
}

.problemColor {
background: #f2b7cd;
border-radius: 4px;
}

/* For tables showing data in reports */
#dataTable {
margin-left: auto;
margin-right: auto;
}

#dataTable, #dataTable th, #dataTable td {
border: 1px solid #BBB;
border-collapse: collapse;
}

#dataTable th, #dataTable td {
padding: 2px 10px;
}

#dataTable th, #dataTable thead {
background: #ddd;
}

Loading
Loading