mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-10-06 13:55:39 -04:00
fix: restore listener Home access and contain landscape insights
This commit is contained in:
6 files changed
+56
-20
No files matched your search
@@ -74,6 +74,7 @@ public class AdminAuthenticationMiddlewareTests
|
||||
|
||||
[Theory]
|
||||
[InlineData("/api/admin/ui/schema", "GET")]
|
||||
[InlineData("/api/admin/ui/home", "GET")]
|
||||
[InlineData("/api/admin/provider-accounts", "GET")]
|
||||
[InlineData("/api/admin/provider-accounts", "POST")]
|
||||
[InlineData("/api/admin/provider-accounts/019f48f2-5f28-7b11-b42d-0d9b76b73b40", "DELETE")]
|
||||
@@ -150,6 +151,9 @@ public class AdminAuthenticationMiddlewareTests
|
||||
|
||||
[Theory]
|
||||
[InlineData("/api/admin/ui/schema", "POST")]
|
||||
[InlineData("/api/admin/ui/home", "POST")]
|
||||
[InlineData("/api/admin/ui/home-extra", "GET")]
|
||||
[InlineData("/api/admin/ui/now-playing", "GET")]
|
||||
[InlineData("/api/admin/config", "GET")]
|
||||
[InlineData("/api/admin/status", "GET")]
|
||||
[InlineData("/api/admin/library-index-spoof", "POST")]
|
||||
|
||||
@@ -89,7 +89,8 @@ public class AdminAuthenticationMiddleware
|
||||
}
|
||||
|
||||
if (HttpMethods.IsGet(method) &&
|
||||
path.Equals("/api/admin/ui/schema", StringComparison.OrdinalIgnoreCase))
|
||||
(path.Equals("/api/admin/ui/schema", StringComparison.OrdinalIgnoreCase) ||
|
||||
path.Equals("/api/admin/ui/home", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,8 @@ Allstarr, measures login latency, limits discovery to that user, and logs out
|
||||
the newly created session on exit. It never logs out a caller-supplied token.
|
||||
Both endpoint URLs are required; no private deployment is built into the tool.
|
||||
Set `ADMIN_BASE` to the dashboard origin when using test-account credentials to
|
||||
also verify dashboard login, session cookies, and the now-playing endpoint. Its
|
||||
also verify dashboard login, session cookies, private Home data, and now-playing
|
||||
permissions (listeners must receive 403 for the operator's live-session list). Its
|
||||
temporary dashboard session is logged out independently on exit.
|
||||
|
||||
Run `python3 tools/tests/test_live_jellyfin_smoke.py` for offline harness checks.
|
||||
|
||||
@@ -584,7 +584,7 @@ check_stream_provenance() {
|
||||
|
||||
check_dashboard_session() {
|
||||
[[ -n "$ADMIN_BASE" ]] || return 0
|
||||
local code result ttfb path filter
|
||||
local code result ttfb path filter expected administrator
|
||||
result="$(jq -cn '{username:env.JELLYFIN_USERNAME,password:env.JELLYFIN_PASSWORD,rememberMe:false}' |
|
||||
curl -s --max-time "$TIMEOUT_SECONDS" --cookie-jar "$admin_cookies_file" \
|
||||
-H 'Content-Type: application/json' -H "Origin: $ADMIN_BASE" --data-binary @- \
|
||||
@@ -597,15 +597,30 @@ check_dashboard_session() {
|
||||
return
|
||||
fi
|
||||
issued_admin_session=1
|
||||
administrator="$(jq -r '.user.isAdministrator == true' "$response_file")"
|
||||
printf 'PASS dashboard login ttfb_ms=%.1f\n' "$(awk -v value="$ttfb" 'BEGIN {print value * 1000}')"
|
||||
for path in auth/me ui/now-playing; do
|
||||
filter='type == "array"'
|
||||
[[ "$path" != auth/me ]] || filter='.authenticated == true'
|
||||
for path in auth/me ui/home ui/now-playing; do
|
||||
expected=200
|
||||
case "$path" in
|
||||
auth/me) filter='.authenticated == true' ;;
|
||||
ui/home)
|
||||
filter='(.stats | type == "object") and (.schema | type == "object")'
|
||||
if [[ "$administrator" != true ]]; then
|
||||
filter+=' and .stats.cacheTracks == null and .stats.keptTracks == null and
|
||||
(.providerHealth.providers | length == 0) and (.activity.items | length == 0)'
|
||||
fi ;;
|
||||
ui/now-playing)
|
||||
filter='.items | type == "array"'
|
||||
if [[ "$administrator" != true ]]; then
|
||||
expected=403
|
||||
filter='.error == "Administrator permissions required"'
|
||||
fi ;;
|
||||
esac
|
||||
code="$(curl -s --max-time "$TIMEOUT_SECONDS" --cookie "$admin_cookies_file" \
|
||||
-o "$response_file" -w '%{http_code}' "$ADMIN_BASE/api/admin/$path" || true)"
|
||||
checks=$((checks + 1))
|
||||
if [[ "$code" == 200 ]] && jq -e "$filter" "$response_file" >/dev/null; then
|
||||
echo "PASS dashboard $path"
|
||||
if [[ "$code" == "$expected" ]] && jq -e "$filter" "$response_file" >/dev/null; then
|
||||
echo "PASS dashboard $path status=$code role-appropriate"
|
||||
else
|
||||
echo "FAIL dashboard $path status=${code:-000}"
|
||||
failures=$((failures + 1))
|
||||
|
||||
@@ -26,9 +26,22 @@ class StreamFixture(BaseHTTPRequestHandler):
|
||||
|
||||
def do_GET(self):
|
||||
if self.path.startswith("/api/admin/"):
|
||||
valid_cookie = self.headers.get("Cookie") == "fixture=session"
|
||||
body = json.dumps({"authenticated": valid_cookie} if self.path.endswith("auth/me") else []).encode()
|
||||
self.send_response(200 if valid_cookie else 401)
|
||||
cookie = self.headers.get("Cookie")
|
||||
valid_cookie = cookie in ("fixture=admin", "fixture=listener")
|
||||
administrator = cookie == "fixture=admin"
|
||||
status = 200 if valid_cookie else 401
|
||||
if self.path.endswith("auth/me"):
|
||||
value = {"authenticated": valid_cookie}
|
||||
elif self.path.endswith("ui/home"):
|
||||
value = {"schema": {}, "stats": {"cacheTracks": None, "keptTracks": None},
|
||||
"providerHealth": {"providers": []}, "activity": {"items": []}}
|
||||
elif administrator:
|
||||
value = {"items": []}
|
||||
else:
|
||||
status = 403 if valid_cookie else 401
|
||||
value = {"error": "Administrator permissions required"}
|
||||
body = json.dumps(value).encode()
|
||||
self.send_response(status)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
@@ -54,11 +67,12 @@ class StreamFixture(BaseHTTPRequestHandler):
|
||||
|
||||
def do_POST(self):
|
||||
login = json.loads(self.rfile.read(int(self.headers.get("Content-Length", "0"))))
|
||||
valid = login.get("username") == "fixture" and login.get("password") == "fixture-password"
|
||||
body = json.dumps({"authenticated": valid}).encode()
|
||||
valid = login.get("username") in ("fixture", "fixture-admin") and login.get("password") == "fixture-password"
|
||||
administrator = login.get("username") == "fixture-admin"
|
||||
body = json.dumps({"authenticated": valid, "user": {"isAdministrator": administrator}}).encode()
|
||||
self.send_response(200 if valid else 400)
|
||||
if valid:
|
||||
self.send_header("Set-Cookie", "fixture=session; Path=/; HttpOnly")
|
||||
self.send_header("Set-Cookie", f"fixture={'admin' if administrator else 'listener'}; Path=/; HttpOnly")
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
@@ -134,8 +148,10 @@ class JellyfinSmokeTests(unittest.TestCase):
|
||||
self.assertIn("RESULT 1 0 0", self.stream("/timeout"))
|
||||
|
||||
def test_dashboard_cookie_session_and_now_playing(self):
|
||||
for password, expected in (("fixture-password", "RESULT 3 0 1"), ("invalid", "RESULT 1 1 0")):
|
||||
with self.subTest(password=password), tempfile.TemporaryDirectory() as directory:
|
||||
for username, password, expected in (("fixture", "fixture-password", "RESULT 4 0 1"),
|
||||
("fixture-admin", "fixture-password", "RESULT 4 0 1"),
|
||||
("fixture", "invalid", "RESULT 1 1 0")):
|
||||
with self.subTest(username=username, password=password), tempfile.TemporaryDirectory() as directory:
|
||||
code = "\n".join([
|
||||
"set -euo pipefail",
|
||||
"checks=0; failures=0; issued_admin_session=0; TIMEOUT_SECONDS=2",
|
||||
@@ -146,7 +162,7 @@ class JellyfinSmokeTests(unittest.TestCase):
|
||||
])
|
||||
result = subprocess.run(["bash", "-c", code], cwd=directory, text=True, capture_output=True, timeout=5,
|
||||
env={**os.environ, "ADMIN_BASE": f"http://127.0.0.1:{self.server.server_port}",
|
||||
"JELLYFIN_USERNAME": "fixture", "JELLYFIN_PASSWORD": password})
|
||||
"JELLYFIN_USERNAME": username, "JELLYFIN_PASSWORD": password})
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn(expected, result.stdout)
|
||||
self.assertNotIn(password, result.stdout)
|
||||
|
||||
@@ -777,10 +777,9 @@
|
||||
|
||||
<style>
|
||||
.history-workspace { min-width: 0; grid-template-columns: minmax(0, 1fr); }
|
||||
.history-heading { flex-wrap: wrap; }
|
||||
.history-heading, .top-card > header { flex-wrap: wrap; }
|
||||
.history-heading > div { min-width: 0; }
|
||||
.history-workspace{min-width:0}
|
||||
.history-workspace{display:grid;gap:1rem}.history-heading,.history-list-card>header,.activity-card>header,.top-card>header,.import-card>header{display:flex;align-items:start;justify-content:space-between;gap:1rem}.history-heading h3,.history-list-card h3,.activity-card h3,.top-card h3,.import-card h3{margin:.2rem 0}.history-heading p:last-child,.import-card>header p:last-child{max-width:70ch;margin:0;color:var(--color-ink-muted)}.history-toolbar{display:grid;grid-template-columns:minmax(16rem,1fr) auto auto;align-items:end;gap:.75rem;padding:1rem}.history-toolbar :global(.period-tabs){width:22rem}.history-toolbar details{grid-column:1/-1}.history-toolbar summary{cursor:pointer}.advanced-filters{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:.75rem;margin-top:.75rem}.history-stats{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:.75rem}.history-stats article{display:grid;gap:.2rem;padding:1rem}.history-stats strong{font-family:var(--font-display);font-size:1.35rem}.history-stats span{color:var(--color-ink-muted);font-size:.78rem}.now-playing{display:flex;align-items:center;gap:.8rem;width:100%;padding:1rem;text-align:left}.now-playing>span:first-child{display:grid;place-items:center;width:2.5rem;height:2.5rem;border-radius:50%;background:var(--color-signal);color:var(--color-on-signal)}.now-playing small,.now-playing strong{display:block}.recap-card{display:grid;gap:.65rem;padding:1.15rem}.recap-card h3,.recap-card p{margin:0}.history-insights{display:grid;grid-template-columns:1fr 1fr;gap:1rem}.activity-card,.top-card,.history-list-card,.import-card{padding:1.15rem}.top-card ol,.breakdown-card ol{margin:.75rem 0 0;padding:0;list-style:none}.top-card li,.breakdown-card li{display:flex;justify-content:space-between;gap:1rem;border-top:1px solid var(--color-edge);padding:.65rem 0}.top-card li span:first-child strong,.top-card li span:first-child small,.breakdown-card li span:first-child strong,.breakdown-card li span:first-child small{display:block}.top-card li small,.top-card li>span:last-child,.breakdown-card li small,.breakdown-card li>span:last-child{color:var(--color-ink-muted)}.history-breakdowns{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1rem}.breakdown-card{padding:1.15rem}.breakdown-card h3{margin:.2rem 0}.history-list{display:grid;margin:.6rem 0 0;padding:0;list-style:none}.history-list>li>button{display:grid;grid-template-columns:auto minmax(0,1fr) minmax(10rem,.45fr) auto;align-items:center;gap:.85rem;width:100%;border-top:1px solid var(--color-edge);padding:.8rem 0;text-align:left}.history-copy strong,.history-copy small,.history-route strong,.history-route small{display:block}.history-copy small,.history-route small{color:var(--color-ink-muted)}.history-route{text-align:right}:global(.load-more){display:block;margin:1rem auto 0}.import-card{display:grid;gap:1.25rem}.import-readiness,.import-batch-actions{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:.8rem;border:1px solid var(--color-edge);border-radius:var(--radius-md);background:var(--color-panel-raised);padding:.8rem}.import-readiness.warning{border-color:color-mix(in srgb,var(--color-warning) 40%,var(--color-edge))}.import-readiness>span:nth-child(2),.import-batch-actions>span{display:grid;gap:.15rem}.import-readiness small,.import-batch-actions small{color:var(--color-ink-muted)}.import-picker{display:grid;gap:.75rem}.upload-zone{position:relative;display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:1rem;min-height:7rem;border:1px dashed color-mix(in srgb,var(--color-signal) 55%,var(--color-edge));border-radius:var(--radius-lg);background:color-mix(in srgb,var(--color-signal) 4%,var(--color-panel-raised));padding:1rem;cursor:pointer}.upload-zone:hover,.upload-zone.dragging{border-color:var(--color-signal);background:color-mix(in srgb,var(--color-signal) 9%,var(--color-panel-raised))}.upload-zone:focus-within{outline:2px solid var(--focus-ring);outline-offset:2px}.upload-zone input{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer}.upload-symbol,.file-marker{display:grid;place-items:center;border-radius:var(--radius-md);background:color-mix(in srgb,var(--color-signal) 12%,transparent);color:var(--color-signal-text)}.upload-symbol{width:3rem;height:3rem}.upload-copy,.file-copy{display:grid;min-width:0;gap:.2rem}.upload-copy strong{font-family:var(--font-display);font-size:1rem}.upload-copy small,.file-copy small,.import-result header small{color:var(--color-ink-muted)}.upload-browse{border:1px solid var(--color-edge);border-radius:var(--radius-md);background:var(--color-panel-raised);padding:.65rem .9rem;font-size:var(--text-sm);font-weight:700}.upload-queue,.import-results{display:grid;margin:0;padding:0;list-style:none}.upload-queue{gap:.4rem}.upload-queue li{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:.75rem;min-height:3.5rem;border-radius:var(--radius-md);background:var(--color-panel-raised);padding:.55rem .65rem}.upload-queue li.error{background:rgb(255 107 122 / 8%)}.file-marker{width:2rem;height:2rem}.file-copy strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.upload-queue .icon-button{width:var(--control-sm);height:var(--control-sm)}.import-batch-summary{display:grid;grid-template-columns:repeat(4,1fr);margin:0;border:1px solid var(--color-edge);border-radius:var(--radius-md)}.import-batch-summary div{display:flex;flex-direction:column-reverse;padding:.75rem}.import-batch-summary div+div{border-left:1px solid var(--color-edge)}.import-batch-summary dd{margin:0;font-family:var(--font-display);font-size:1.2rem;font-weight:750}.import-batch-summary dt{color:var(--color-ink-muted);font-size:.76rem}.import-results{gap:1.25rem}.import-result{display:grid;gap:.85rem;border-top:1px solid var(--color-edge);padding-top:1.25rem}.import-result>header{display:flex;align-items:start;justify-content:space-between;gap:1rem}.import-result-select{display:flex;align-items:start;gap:.65rem}.import-result-select span>*{display:block}.import-preview{display:grid;grid-template-columns:repeat(4,1fr);margin:0;border-block:1px solid var(--color-edge)}.import-preview div{display:flex;flex-direction:column-reverse;gap:.15rem;padding:.75rem}.import-preview div+div{border-left:1px solid var(--color-edge)}.import-preview dd{margin:0;font-family:var(--font-display);font-size:1.2rem;font-weight:750}.import-preview dt{color:var(--color-ink-muted);font-size:.76rem}.credential-safety{margin:0;color:var(--color-ink-muted);font-size:var(--text-sm)}.import-result>footer{display:flex;justify-content:flex-end;gap:.5rem}
|
||||
.history-workspace{display:grid;gap:1rem}.history-heading,.history-list-card>header,.activity-card>header,.top-card>header,.import-card>header{display:flex;align-items:start;justify-content:space-between;gap:1rem}.history-heading h3,.history-list-card h3,.activity-card h3,.top-card h3,.import-card h3{margin:.2rem 0}.history-heading p:last-child,.import-card>header p:last-child{max-width:70ch;margin:0;color:var(--color-ink-muted)}.history-toolbar{display:grid;grid-template-columns:minmax(16rem,1fr) auto auto;align-items:end;gap:.75rem;padding:1rem}.history-toolbar :global(.period-tabs){width:22rem}.history-toolbar details{grid-column:1/-1}.history-toolbar summary{cursor:pointer}.advanced-filters{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:.75rem;margin-top:.75rem}.history-stats{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:.75rem}.history-stats article{display:grid;gap:.2rem;padding:1rem}.history-stats strong{font-family:var(--font-display);font-size:1.35rem}.history-stats span{color:var(--color-ink-muted);font-size:.78rem}.now-playing{display:flex;align-items:center;gap:.8rem;width:100%;padding:1rem;text-align:left}.now-playing>span:first-child{display:grid;place-items:center;width:2.5rem;height:2.5rem;border-radius:50%;background:var(--color-signal);color:var(--color-on-signal)}.now-playing small,.now-playing strong{display:block}.recap-card{display:grid;gap:.65rem;padding:1.15rem}.recap-card h3,.recap-card p{margin:0}.history-insights{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(24rem,100%),1fr));gap:1rem}.activity-card,.top-card,.history-list-card,.import-card{padding:1.15rem}.top-card ol,.breakdown-card ol{margin:.75rem 0 0;padding:0;list-style:none}.top-card li,.breakdown-card li{display:flex;justify-content:space-between;gap:1rem;border-top:1px solid var(--color-edge);padding:.65rem 0}.top-card li span:first-child strong,.top-card li span:first-child small,.breakdown-card li span:first-child strong,.breakdown-card li span:first-child small{display:block}.top-card li small,.top-card li>span:last-child,.breakdown-card li small,.breakdown-card li>span:last-child{color:var(--color-ink-muted)}.history-breakdowns{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1rem}.breakdown-card{padding:1.15rem}.breakdown-card h3{margin:.2rem 0}.history-list{display:grid;margin:.6rem 0 0;padding:0;list-style:none}.history-list>li>button{display:grid;grid-template-columns:auto minmax(0,1fr) minmax(10rem,.45fr) auto;align-items:center;gap:.85rem;width:100%;border-top:1px solid var(--color-edge);padding:.8rem 0;text-align:left}.history-copy strong,.history-copy small,.history-route strong,.history-route small{display:block}.history-copy small,.history-route small{color:var(--color-ink-muted)}.history-route{text-align:right}:global(.load-more){display:block;margin:1rem auto 0}.import-card{display:grid;gap:1.25rem}.import-readiness,.import-batch-actions{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:.8rem;border:1px solid var(--color-edge);border-radius:var(--radius-md);background:var(--color-panel-raised);padding:.8rem}.import-readiness.warning{border-color:color-mix(in srgb,var(--color-warning) 40%,var(--color-edge))}.import-readiness>span:nth-child(2),.import-batch-actions>span{display:grid;gap:.15rem}.import-readiness small,.import-batch-actions small{color:var(--color-ink-muted)}.import-picker{display:grid;gap:.75rem}.upload-zone{position:relative;display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:1rem;min-height:7rem;border:1px dashed color-mix(in srgb,var(--color-signal) 55%,var(--color-edge));border-radius:var(--radius-lg);background:color-mix(in srgb,var(--color-signal) 4%,var(--color-panel-raised));padding:1rem;cursor:pointer}.upload-zone:hover,.upload-zone.dragging{border-color:var(--color-signal);background:color-mix(in srgb,var(--color-signal) 9%,var(--color-panel-raised))}.upload-zone:focus-within{outline:2px solid var(--focus-ring);outline-offset:2px}.upload-zone input{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer}.upload-symbol,.file-marker{display:grid;place-items:center;border-radius:var(--radius-md);background:color-mix(in srgb,var(--color-signal) 12%,transparent);color:var(--color-signal-text)}.upload-symbol{width:3rem;height:3rem}.upload-copy,.file-copy{display:grid;min-width:0;gap:.2rem}.upload-copy strong{font-family:var(--font-display);font-size:1rem}.upload-copy small,.file-copy small,.import-result header small{color:var(--color-ink-muted)}.upload-browse{border:1px solid var(--color-edge);border-radius:var(--radius-md);background:var(--color-panel-raised);padding:.65rem .9rem;font-size:var(--text-sm);font-weight:700}.upload-queue,.import-results{display:grid;margin:0;padding:0;list-style:none}.upload-queue{gap:.4rem}.upload-queue li{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:.75rem;min-height:3.5rem;border-radius:var(--radius-md);background:var(--color-panel-raised);padding:.55rem .65rem}.upload-queue li.error{background:rgb(255 107 122 / 8%)}.file-marker{width:2rem;height:2rem}.file-copy strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.upload-queue .icon-button{width:var(--control-sm);height:var(--control-sm)}.import-batch-summary{display:grid;grid-template-columns:repeat(4,1fr);margin:0;border:1px solid var(--color-edge);border-radius:var(--radius-md)}.import-batch-summary div{display:flex;flex-direction:column-reverse;padding:.75rem}.import-batch-summary div+div{border-left:1px solid var(--color-edge)}.import-batch-summary dd{margin:0;font-family:var(--font-display);font-size:1.2rem;font-weight:750}.import-batch-summary dt{color:var(--color-ink-muted);font-size:.76rem}.import-results{gap:1.25rem}.import-result{display:grid;gap:.85rem;border-top:1px solid var(--color-edge);padding-top:1.25rem}.import-result>header{display:flex;align-items:start;justify-content:space-between;gap:1rem}.import-result-select{display:flex;align-items:start;gap:.65rem}.import-result-select span>*{display:block}.import-preview{display:grid;grid-template-columns:repeat(4,1fr);margin:0;border-block:1px solid var(--color-edge)}.import-preview div{display:flex;flex-direction:column-reverse;gap:.15rem;padding:.75rem}.import-preview div+div{border-left:1px solid var(--color-edge)}.import-preview dd{margin:0;font-family:var(--font-display);font-size:1.2rem;font-weight:750}.import-preview dt{color:var(--color-ink-muted);font-size:.76rem}.credential-safety{margin:0;color:var(--color-ink-muted);font-size:var(--text-sm)}.import-result>footer{display:flex;justify-content:flex-end;gap:.5rem}
|
||||
|
||||
:global(.history-detail-dialog){--dialog-width:44rem;--dialog-max-height:48rem;display:grid;grid-template-rows:auto minmax(0,1fr);overflow:hidden}.history-detail-form{display:grid;grid-template-columns:1fr 1fr;gap:1rem;overflow:auto;padding:1rem}.history-detail-form dl,.target-statuses,.history-detail-form footer{grid-column:1/-1}.history-detail-form dl{display:grid;grid-template-columns:repeat(3,1fr);gap:.75rem;margin:0}.history-detail-form dl div{border:1px solid var(--color-edge);border-radius:var(--radius-md);padding:.7rem}.history-detail-form dt{color:var(--color-ink-muted);font-size:.72rem}.history-detail-form dd{margin:.2rem 0 0}.target-statuses ul{margin:0;padding:0;list-style:none}.target-statuses li{display:flex;justify-content:space-between;gap:1rem}.target-statuses small{color:var(--color-ink-muted)}.history-detail-form footer{display:flex;justify-content:space-between;gap:1rem;border-top:1px solid var(--color-edge);padding-top:1rem}.history-detail-form footer>span{display:flex;gap:.5rem}
|
||||
@media(max-width:900px){.history-toolbar{grid-template-columns:1fr auto}.history-toolbar>:global(nav[aria-label="History period"]){grid-column:1/-1;grid-row:2}.advanced-filters{grid-template-columns:1fr 1fr}.import-preview{grid-template-columns:1fr 1fr}}
|
||||
|
||||
Reference in new issue
Block a user