admin: ui polish pass, thirty findings from the emil audit
selected states stop changing font weight, buttons gain a pressed scale and scoped 120ms transitions with a reduced-motion override, every loading and empty state reserves its height, charts measure before first paint, hit targets rise to the 44px enhanced target where layout permits, long domains clamp to two lines on an unpadded inner span, chips and name cells truncate, tabular figures on counts and time columns, a z-index layer scale replaces magic numbers and fixes the dialog-over-confirm tie, history's empty state gains a clear-filters action, page headings balance, font smoothing and color-scheme land on the html reset.
This commit is contained in:
@@ -24,8 +24,17 @@ import { queriesFilterOf, type ActivitySearch } from "./search";
|
||||
const styles = stylex.create({
|
||||
empty: {
|
||||
marginTop: "1.5rem",
|
||||
minHeight: "6rem",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
gap: "0.75rem",
|
||||
color: colors.textMuted,
|
||||
},
|
||||
/** About a default page of rows, so the table does not jump in under the reader. */
|
||||
loading: {
|
||||
minHeight: "24rem",
|
||||
},
|
||||
tableWrap: {
|
||||
marginTop: "1rem",
|
||||
overflowX: "auto",
|
||||
@@ -75,7 +84,13 @@ function errorMessage(error: unknown): string {
|
||||
return error instanceof Error ? error.message : String(error);
|
||||
}
|
||||
|
||||
export default function HistoryActivity({ search }: { search: ActivitySearch }) {
|
||||
interface Props {
|
||||
search: ActivitySearch;
|
||||
/** The toolbar's own Clear, so the empty state offers the way out it names. */
|
||||
onClear: () => void;
|
||||
}
|
||||
|
||||
export default function HistoryActivity({ search, onClear }: Props) {
|
||||
const filter = queriesFilterOf(search);
|
||||
const base = useInfiniteQuery(queriesInfiniteQuery(filter));
|
||||
const clientNames = useClientNames();
|
||||
@@ -107,7 +122,7 @@ export default function HistoryActivity({ search }: { search: ActivitySearch })
|
||||
}
|
||||
if (base.data === undefined) {
|
||||
return (
|
||||
<p {...stylex.props(styles.empty, shared.pulse)} role="status">
|
||||
<p {...stylex.props(styles.empty, styles.loading, shared.pulse)} role="status">
|
||||
Loading activity…
|
||||
</p>
|
||||
);
|
||||
@@ -126,16 +141,15 @@ export default function HistoryActivity({ search }: { search: ActivitySearch })
|
||||
</p>
|
||||
)}
|
||||
{rows.length === 0 ? (
|
||||
<>
|
||||
<p {...stylex.props(styles.empty)}>
|
||||
{filterActive ? "No queries match the current filters." : "No queries logged yet."}
|
||||
</p>
|
||||
{coverage !== undefined && (
|
||||
<div {...stylex.props(styles.spacedTop)}>
|
||||
<CoverageNotice coverage={coverage} variant="note" />
|
||||
</div>
|
||||
<div {...stylex.props(styles.empty)}>
|
||||
<p>{filterActive ? "No queries match the current filters." : "No queries logged yet."}</p>
|
||||
{filterActive && (
|
||||
<button type="button" onClick={onClear} {...stylex.props(shared.button, shared.focusRing)}>
|
||||
Clear filters
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
{coverage !== undefined && <CoverageNotice coverage={coverage} variant="note" />}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div {...stylex.props(styles.tableWrap)}>
|
||||
@@ -169,7 +183,9 @@ export default function HistoryActivity({ search }: { search: ActivitySearch })
|
||||
</div>
|
||||
<div {...stylex.props(styles.footer)}>
|
||||
<p {...stylex.props(styles.note)}>
|
||||
Showing {rows.length} {rows.length === 1 ? "query" : "queries"}
|
||||
{/* The count is the one part of this line that moves as pages load. */}
|
||||
Showing <span {...stylex.props(shared.tabularNums)}>{rows.length}</span>{" "}
|
||||
{rows.length === 1 ? "query" : "queries"}
|
||||
{hasMore ? "" : " — end of log"}
|
||||
</p>
|
||||
{coverage !== undefined && <CoverageNotice coverage={coverage} variant="note" />}
|
||||
|
||||
Reference in New Issue
Block a user