visual fixes
This commit is contained in:
@@ -31,6 +31,7 @@ export const DEFAULT_UNIT_DEFINITIONS = [
|
|||||||
{ symbol: "V", label: "Volt", isSystem: true },
|
{ symbol: "V", label: "Volt", isSystem: true },
|
||||||
{ symbol: "A", label: "Ampere", isSystem: true },
|
{ symbol: "A", label: "Ampere", isSystem: true },
|
||||||
{ symbol: "Pa", label: "Pascal", isSystem: true },
|
{ symbol: "Pa", label: "Pascal", isSystem: true },
|
||||||
|
{ symbol: "bool", label: "Digital / Bool", isSystem: true },
|
||||||
];
|
];
|
||||||
|
|
||||||
function safeParse(jsonValue) {
|
function safeParse(jsonValue) {
|
||||||
@@ -267,6 +268,7 @@ export async function getPreferences(redis, userId) {
|
|||||||
theme: "system",
|
theme: "system",
|
||||||
csvDelimiter: ";",
|
csvDelimiter: ";",
|
||||||
sidebarPinned: true,
|
sidebarPinned: true,
|
||||||
|
trendLineWidth: 1.8,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,6 +280,7 @@ export async function savePreferences(redis, userId, preferences) {
|
|||||||
theme: preferences.theme || current.theme || "system",
|
theme: preferences.theme || current.theme || "system",
|
||||||
csvDelimiter: preferences.csvDelimiter || current.csvDelimiter || ";",
|
csvDelimiter: preferences.csvDelimiter || current.csvDelimiter || ";",
|
||||||
sidebarPinned: typeof preferences.sidebarPinned === "boolean" ? preferences.sidebarPinned : current.sidebarPinned,
|
sidebarPinned: typeof preferences.sidebarPinned === "boolean" ? preferences.sidebarPinned : current.sidebarPinned,
|
||||||
|
trendLineWidth: Number.isFinite(Number(preferences.trendLineWidth)) ? Number(preferences.trendLineWidth) : Number(current.trendLineWidth || 1.8),
|
||||||
updatedAt: new Date().toISOString(),
|
updatedAt: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
await redis.set(`preferences:${userId}`, JSON.stringify(payload));
|
await redis.set(`preferences:${userId}`, JSON.stringify(payload));
|
||||||
|
|||||||
+77153
File diff suppressed because one or more lines are too long
+23
-3
@@ -59,7 +59,7 @@ const emptyTrendRange = {
|
|||||||
presetDays: "1",
|
presetDays: "1",
|
||||||
from: "",
|
from: "",
|
||||||
to: "",
|
to: "",
|
||||||
maxRows: 4000,
|
maxRows: 20000,
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultSelectionDraft = {
|
const defaultSelectionDraft = {
|
||||||
@@ -700,6 +700,7 @@ export default function App() {
|
|||||||
const [themeMode, setThemeMode] = useState("system");
|
const [themeMode, setThemeMode] = useState("system");
|
||||||
const [csvDelimiter, setCsvDelimiter] = useState(";");
|
const [csvDelimiter, setCsvDelimiter] = useState(";");
|
||||||
const [sidebarPinned, setSidebarPinned] = useState(true);
|
const [sidebarPinned, setSidebarPinned] = useState(true);
|
||||||
|
const [trendLineWidth, setTrendLineWidth] = useState("1.8");
|
||||||
const [sidebarHover, setSidebarHover] = useState(false);
|
const [sidebarHover, setSidebarHover] = useState(false);
|
||||||
const [trendFocus, setTrendFocus] = useState(false);
|
const [trendFocus, setTrendFocus] = useState(false);
|
||||||
const [adminIsp, setAdminIsp] = useState("");
|
const [adminIsp, setAdminIsp] = useState("");
|
||||||
@@ -738,6 +739,8 @@ export default function App() {
|
|||||||
const sidebarCollapsed = !sidebarPinned && !sidebarHover;
|
const sidebarCollapsed = !sidebarPinned && !sidebarHover;
|
||||||
const currentView = views.find((view) => view.id === activeView) || views[0];
|
const currentView = views.find((view) => view.id === activeView) || views[0];
|
||||||
const selectionListClassName = selections.length <= 2 ? "selection-list compact" : "selection-list";
|
const selectionListClassName = selections.length <= 2 ? "selection-list compact" : "selection-list";
|
||||||
|
const trendMaxRows = Number(trendRange.maxRows) || 0;
|
||||||
|
const showTrendLimitWarning = trendMaxRows > 20000;
|
||||||
const roleOptions = roles.length ? roles : [
|
const roleOptions = roles.length ? roles : [
|
||||||
{ name: "viewer", label: "Viewer" },
|
{ name: "viewer", label: "Viewer" },
|
||||||
{ name: "technician", label: "Techniker" },
|
{ name: "technician", label: "Techniker" },
|
||||||
@@ -888,6 +891,7 @@ export default function App() {
|
|||||||
setThemeMode(pref.theme || "system");
|
setThemeMode(pref.theme || "system");
|
||||||
setCsvDelimiter(pref.csvDelimiter || ";");
|
setCsvDelimiter(pref.csvDelimiter || ";");
|
||||||
setSidebarPinned(typeof pref.sidebarPinned === "boolean" ? pref.sidebarPinned : true);
|
setSidebarPinned(typeof pref.sidebarPinned === "boolean" ? pref.sidebarPinned : true);
|
||||||
|
setTrendLineWidth(String(pref.trendLineWidth || 1.8));
|
||||||
setIsps(ispResult.isps || []);
|
setIsps(ispResult.isps || []);
|
||||||
setSelections(selectionResult.selections);
|
setSelections(selectionResult.selections);
|
||||||
setDashboard(dashboardResult);
|
setDashboard(dashboardResult);
|
||||||
@@ -967,8 +971,9 @@ export default function App() {
|
|||||||
theme: themeMode,
|
theme: themeMode,
|
||||||
csvDelimiter,
|
csvDelimiter,
|
||||||
sidebarPinned,
|
sidebarPinned,
|
||||||
|
trendLineWidth: Number(trendLineWidth),
|
||||||
}).catch(() => undefined);
|
}).catch(() => undefined);
|
||||||
}, [csvDelimiter, preferencesReady, sidebarPinned, themeMode, token]);
|
}, [csvDelimiter, preferencesReady, sidebarPinned, themeMode, token, trendLineWidth]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!selectedIsp || metadataCache[selectedIsp]) {
|
if (!selectedIsp || metadataCache[selectedIsp]) {
|
||||||
@@ -2180,6 +2185,7 @@ export default function App() {
|
|||||||
onCsvDelimiterChange={setCsvDelimiter}
|
onCsvDelimiterChange={setCsvDelimiter}
|
||||||
allowExports
|
allowExports
|
||||||
allowFocusMode
|
allowFocusMode
|
||||||
|
lineWidth={Number(trendLineWidth)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="panel empty-state slim-empty">
|
<div className="panel empty-state slim-empty">
|
||||||
@@ -2258,7 +2264,18 @@ export default function App() {
|
|||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>Max. Zeilen</span>
|
<span>Max. Zeilen</span>
|
||||||
<input type="number" min="100" max="10000" value={trendRange.maxRows} onChange={(event) => setTrendRange((current) => ({ ...current, maxRows: event.target.value }))} />
|
<input type="number" min="100" value={trendRange.maxRows} onChange={(event) => setTrendRange((current) => ({ ...current, maxRows: event.target.value }))} />
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<span>Liniendicke</span>
|
||||||
|
<select value={trendLineWidth} onChange={(event) => setTrendLineWidth(event.target.value)}>
|
||||||
|
<option value="1">1.0 px</option>
|
||||||
|
<option value="1.4">1.4 px</option>
|
||||||
|
<option value="1.8">1.8 px</option>
|
||||||
|
<option value="2.2">2.2 px</option>
|
||||||
|
<option value="2.8">2.8 px</option>
|
||||||
|
<option value="3.4">3.4 px</option>
|
||||||
|
</select>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
<span>Start</span>
|
<span>Start</span>
|
||||||
@@ -2270,6 +2287,8 @@ export default function App() {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{showTrendLimitWarning ? <p className="warning-note">{"Mehr als 20000 Datensätze sind möglich, können aber zu längeren Ladezeiten führen."}</p> : null}
|
||||||
|
|
||||||
<div className="button-row">
|
<div className="button-row">
|
||||||
<button className="primary-button compact-button" type="button" onClick={runTrendQuery}>{busy ? "Lädt ..." : "Trend anzeigen"}</button>
|
<button className="primary-button compact-button" type="button" onClick={runTrendQuery}>{busy ? "Lädt ..." : "Trend anzeigen"}</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -2328,6 +2347,7 @@ export default function App() {
|
|||||||
onCsvDelimiterChange={setCsvDelimiter}
|
onCsvDelimiterChange={setCsvDelimiter}
|
||||||
allowExports
|
allowExports
|
||||||
allowFocusMode
|
allowFocusMode
|
||||||
|
lineWidth={Number(trendLineWidth)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="panel empty-state slim-empty">
|
<div className="panel empty-state slim-empty">
|
||||||
|
|||||||
@@ -72,9 +72,12 @@ export default function TrendChart({
|
|||||||
allowExports = true,
|
allowExports = true,
|
||||||
allowFocusMode = true,
|
allowFocusMode = true,
|
||||||
compact = false,
|
compact = false,
|
||||||
|
lineWidth = 1.8,
|
||||||
}) {
|
}) {
|
||||||
const wrapperRef = useRef(null);
|
const wrapperRef = useRef(null);
|
||||||
const chartHostRef = useRef(null);
|
const chartHostRef = useRef(null);
|
||||||
|
const dragBoxRef = useRef(null);
|
||||||
|
const domainRef = useRef({ x: [0, 1], y: [0, 1] });
|
||||||
const [selectedKeys, setSelectedKeys] = useState([]);
|
const [selectedKeys, setSelectedKeys] = useState([]);
|
||||||
const [dragBox, setDragBox] = useState(null);
|
const [dragBox, setDragBox] = useState(null);
|
||||||
const [zoomDomain, setZoomDomain] = useState(null);
|
const [zoomDomain, setZoomDomain] = useState(null);
|
||||||
@@ -116,17 +119,23 @@ export default function TrendChart({
|
|||||||
const xDomain = zoomDomain?.x || baseXDomain;
|
const xDomain = zoomDomain?.x || baseXDomain;
|
||||||
const yDomain = zoomDomain?.y || baseYDomain;
|
const yDomain = zoomDomain?.y || baseYDomain;
|
||||||
const hasFocusSelection = selectedKeys.length > 0 && selectedKeys.length < series.length;
|
const hasFocusSelection = selectedKeys.length > 0 && selectedKeys.length < series.length;
|
||||||
|
const normalizedLineWidth = Number.isFinite(Number(lineWidth)) ? Number(lineWidth) : 1.8;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
domainRef.current = { x: xDomain, y: yDomain };
|
||||||
|
}, [xDomain, yDomain]);
|
||||||
|
|
||||||
const mapDragToDomain = (clientX, clientY) => {
|
const mapDragToDomain = (clientX, clientY) => {
|
||||||
const rect = wrapperRef.current?.getBoundingClientRect();
|
const rect = chartHostRef.current?.getBoundingClientRect() || wrapperRef.current?.getBoundingClientRect();
|
||||||
if (!rect) {
|
if (!rect) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const activeDomain = domainRef.current;
|
||||||
const xRatio = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
|
const xRatio = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
|
||||||
const yRatio = Math.max(0, Math.min(1, (clientY - rect.top) / rect.height));
|
const yRatio = Math.max(0, Math.min(1, (clientY - rect.top) / rect.height));
|
||||||
const xValue = xDomain[0] + (xDomain[1] - xDomain[0]) * xRatio;
|
const xValue = activeDomain.x[0] + (activeDomain.x[1] - activeDomain.x[0]) * xRatio;
|
||||||
const yValue = yDomain[1] - (yDomain[1] - yDomain[0]) * yRatio;
|
const yValue = activeDomain.y[1] - (activeDomain.y[1] - activeDomain.y[0]) * yRatio;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
xValue,
|
xValue,
|
||||||
@@ -145,7 +154,9 @@ export default function TrendChart({
|
|||||||
if (!mapped) {
|
if (!mapped) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setDragBox({ start: mapped, end: mapped });
|
const nextDragBox = { start: mapped, end: mapped };
|
||||||
|
dragBoxRef.current = nextDragBox;
|
||||||
|
setDragBox(nextDragBox);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateDragBox = (clientX, clientY) => {
|
const updateDragBox = (clientX, clientY) => {
|
||||||
@@ -153,7 +164,14 @@ export default function TrendChart({
|
|||||||
if (!mapped) {
|
if (!mapped) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setDragBox((current) => current ? { ...current, end: mapped } : current);
|
setDragBox((current) => {
|
||||||
|
if (!current) {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
const nextDragBox = { ...current, end: mapped };
|
||||||
|
dragBoxRef.current = nextDragBox;
|
||||||
|
return nextDragBox;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const finalizeDragBox = (box) => {
|
const finalizeDragBox = (box) => {
|
||||||
@@ -163,15 +181,18 @@ export default function TrendChart({
|
|||||||
|
|
||||||
const deltaX = Math.abs(box.end.xPixel - box.start.xPixel);
|
const deltaX = Math.abs(box.end.xPixel - box.start.xPixel);
|
||||||
const deltaY = Math.abs(box.end.yPixel - box.start.yPixel);
|
const deltaY = Math.abs(box.end.yPixel - box.start.yPixel);
|
||||||
if (deltaX < 12 || deltaY < 12) {
|
if (deltaX < 12 && deltaY < 12) {
|
||||||
|
dragBoxRef.current = null;
|
||||||
setDragBox(null);
|
setDragBox(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const currentDomain = domainRef.current;
|
||||||
setZoomDomain({
|
setZoomDomain({
|
||||||
x: [Math.min(box.start.xValue, box.end.xValue), Math.max(box.start.xValue, box.end.xValue)],
|
x: deltaX < 12 ? currentDomain.x : [Math.min(box.start.xValue, box.end.xValue), Math.max(box.start.xValue, box.end.xValue)],
|
||||||
y: [Math.min(box.start.yValue, box.end.yValue), Math.max(box.start.yValue, box.end.yValue)],
|
y: deltaY < 12 ? currentDomain.y : [Math.min(box.start.yValue, box.end.yValue), Math.max(box.start.yValue, box.end.yValue)],
|
||||||
});
|
});
|
||||||
|
dragBoxRef.current = null;
|
||||||
setDragBox(null);
|
setDragBox(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -185,7 +206,7 @@ export default function TrendChart({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleWindowMouseUp = () => {
|
const handleWindowMouseUp = () => {
|
||||||
finalizeDragBox(dragBox);
|
finalizeDragBox(dragBoxRef.current);
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("mousemove", handleWindowMouseMove);
|
window.addEventListener("mousemove", handleWindowMouseMove);
|
||||||
@@ -444,10 +465,11 @@ export default function TrendChart({
|
|||||||
<Line
|
<Line
|
||||||
key={entry.key}
|
key={entry.key}
|
||||||
yAxisId={entry.kind === "digital" ? "digital" : "analog"}
|
yAxisId={entry.kind === "digital" ? "digital" : "analog"}
|
||||||
type={entry.kind === "digital" ? "stepAfter" : "monotone"}
|
type={entry.kind === "digital" ? "stepAfter" : "linear"}
|
||||||
dataKey={entry.key}
|
dataKey={entry.key}
|
||||||
dot={false}
|
dot={false}
|
||||||
strokeWidth={selectedKeys.includes(entry.key) ? 2.35 : 1.8}
|
connectNulls
|
||||||
|
strokeWidth={selectedKeys.includes(entry.key) ? normalizedLineWidth + 0.55 : normalizedLineWidth}
|
||||||
stroke={entry.color}
|
stroke={entry.color}
|
||||||
strokeOpacity={isolated ? 0.1 : 1}
|
strokeOpacity={isolated ? 0.1 : 1}
|
||||||
isAnimationActive={false}
|
isAnimationActive={false}
|
||||||
|
|||||||
+94
-10
@@ -525,27 +525,42 @@ span {
|
|||||||
.user-row,
|
.user-row,
|
||||||
.stat-card {
|
.stat-card {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 0.5rem 0.58rem;
|
padding: 0.42rem 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.point-copy,
|
.point-copy,
|
||||||
.user-copy {
|
.user-copy {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.06rem;
|
gap: 0.06rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.point-item {
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.42rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.point-item .point-copy strong,
|
||||||
|
.point-item .point-copy span {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.selection-chips {
|
.selection-chips {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
gap: 0.34rem;
|
gap: 0.28rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chip {
|
.chip {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.12rem;
|
gap: 0.1rem;
|
||||||
border-left: 3px solid var(--brand-2);
|
border-left: 2px solid var(--brand-2);
|
||||||
min-width: 94px;
|
min-width: 0;
|
||||||
padding: 0.34rem 0.42rem;
|
max-width: 100%;
|
||||||
border-radius: 12px;
|
padding: 0.26rem 0.36rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
flex: 1 1 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chip strong,
|
.chip strong,
|
||||||
@@ -556,8 +571,21 @@ span {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chip strong,
|
||||||
.chip span {
|
.chip span {
|
||||||
font-size: 11px;
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip strong {
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 1.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chip span {
|
||||||
|
font-size: 10px;
|
||||||
|
line-height: 1.15;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-panel {
|
.chart-panel {
|
||||||
@@ -572,6 +600,8 @@ span {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 320px;
|
min-height: 320px;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
cursor: crosshair;
|
||||||
|
touch-action: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-wrapper.dragging {
|
.chart-wrapper.dragging {
|
||||||
@@ -641,7 +671,8 @@ span {
|
|||||||
|
|
||||||
.latest-grid {
|
.latest-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
|
||||||
|
gap: 0.42rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.trend-layout {
|
.trend-layout {
|
||||||
@@ -674,6 +705,48 @@ span {
|
|||||||
border-left: 4px solid var(--brand-2);
|
border-left: 4px solid var(--brand-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.latest-card {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.45rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-card > div:first-child {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-card small,
|
||||||
|
.latest-card strong,
|
||||||
|
.latest-card .value-stamp {
|
||||||
|
display: block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-card small {
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-card strong {
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 1.18;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-card .stat-value {
|
||||||
|
justify-content: flex-end;
|
||||||
|
min-width: max-content;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.latest-card .stat-value span {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.stat-value {
|
.stat-value {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
@@ -791,6 +864,12 @@ span {
|
|||||||
border: 1px solid rgba(37, 99, 235, 0.22);
|
border: 1px solid rgba(37, 99, 235, 0.22);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.warning-note {
|
||||||
|
margin: 0;
|
||||||
|
color: #c2410c;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
.checkbox-field {
|
.checkbox-field {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -889,9 +968,14 @@ span {
|
|||||||
.protocol-grid,
|
.protocol-grid,
|
||||||
.inline-fields,
|
.inline-fields,
|
||||||
.inline-fields.multi-line,
|
.inline-fields.multi-line,
|
||||||
.trend-layout {
|
.trend-layout,
|
||||||
|
.latest-card {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.latest-card .stat-value {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user