style: run prettifier plugin on entire frontend

This commit is contained in:
2026-03-25 19:30:21 +00:00
parent 8730af146d
commit aae10c4d9d
20 changed files with 1381 additions and 868 deletions

View File

@@ -2,9 +2,9 @@ import { useEffect, useMemo, useRef, useState } from "react";
import ForceGraph3D from "react-force-graph-3d";
import {
type UserAnalysisResponse,
type InteractionGraph
} from '../types/ApiTypes';
type UserAnalysisResponse,
type InteractionGraph,
} from "../types/ApiTypes";
import StatsStyling from "../styles/stats_styling";
import Card from "./Card";
@@ -12,38 +12,43 @@ import Card from "./Card";
const styles = StatsStyling;
type GraphLink = {
source: string;
target: string;
value: number;
source: string;
target: string;
value: number;
};
function ApiToGraphData(apiData: InteractionGraph) {
const nodes = Object.keys(apiData).map(username => ({ id: username }));
const links: GraphLink[] = [];
for (const [source, targets] of Object.entries(apiData)) {
for (const [target, count] of Object.entries(targets)) {
links.push({ source, target, value: count });
}
const nodes = Object.keys(apiData).map((username) => ({ id: username }));
const links: GraphLink[] = [];
for (const [source, targets] of Object.entries(apiData)) {
for (const [target, count] of Object.entries(targets)) {
links.push({ source, target, value: count });
}
// drop low-value and deleted interactions to reduce clutter
const filteredLinks = links.filter(link =>
link.value >= 2 &&
link.source !== "[deleted]" &&
link.target !== "[deleted]"
);
}
// also filter out nodes that are no longer connected after link filtering
const connectedNodeIds = new Set(filteredLinks.flatMap(link => [link.source, link.target]));
const filteredNodes = nodes.filter(node => connectedNodeIds.has(node.id));
// drop low-value and deleted interactions to reduce clutter
const filteredLinks = links.filter(
(link) =>
link.value >= 2 &&
link.source !== "[deleted]" &&
link.target !== "[deleted]",
);
return { nodes: filteredNodes, links: filteredLinks};
// also filter out nodes that are no longer connected after link filtering
const connectedNodeIds = new Set(
filteredLinks.flatMap((link) => [link.source, link.target]),
);
const filteredNodes = nodes.filter((node) => connectedNodeIds.has(node.id));
return { nodes: filteredNodes, links: filteredLinks };
}
const UserStats = (props: { data: UserAnalysisResponse }) => {
const graphData = useMemo(() => ApiToGraphData(props.data.interaction_graph), [props.data.interaction_graph]);
const graphData = useMemo(
() => ApiToGraphData(props.data.interaction_graph),
[props.data.interaction_graph],
);
const graphContainerRef = useRef<HTMLDivElement | null>(null);
const [graphSize, setGraphSize] = useState({ width: 720, height: 540 });
@@ -63,86 +68,113 @@ const UserStats = (props: { data: UserAnalysisResponse }) => {
const totalUsers = props.data.users.length;
const connectedUsers = graphData.nodes.length;
const totalInteractions = graphData.links.reduce((sum, link) => sum + link.value, 0);
const avgInteractionsPerConnectedUser = connectedUsers ? totalInteractions / connectedUsers : 0;
const totalInteractions = graphData.links.reduce(
(sum, link) => sum + link.value,
0,
);
const avgInteractionsPerConnectedUser = connectedUsers
? totalInteractions / connectedUsers
: 0;
const strongestLink = graphData.links.reduce<GraphLink | null>((best, current) => {
if (!best || current.value > best.value) {
return current;
}
return best;
}, null);
const strongestLink = graphData.links.reduce<GraphLink | null>(
(best, current) => {
if (!best || current.value > best.value) {
return current;
}
return best;
},
null,
);
const highlyInteractiveUser = [...props.data.users].sort((a, b) => b.comment_share - a.comment_share)[0];
const highlyInteractiveUser = [...props.data.users].sort(
(a, b) => b.comment_share - a.comment_share,
)[0];
const mostActiveUser = props.data.top_users.find(u => u.author !== "[deleted]");
const mostActiveUser = props.data.top_users.find(
(u) => u.author !== "[deleted]",
);
return (
<div style={styles.page}>
<div style={{ ...styles.container, ...styles.grid }}>
<Card
label="Users"
value={totalUsers.toLocaleString()}
sublabel={`${connectedUsers.toLocaleString()} users in filtered graph`}
style={{ gridColumn: "span 3" }}
/>
<Card
label="Replies"
value={totalInteractions.toLocaleString()}
sublabel="Links with at least 2 replies"
style={{ gridColumn: "span 3" }}
/>
<Card
label="Replies per Connected User"
value={avgInteractionsPerConnectedUser.toFixed(1)}
sublabel="Average from visible graph links"
style={{ gridColumn: "span 3" }}
/>
<Card
label="Most Active User"
value={mostActiveUser?.author ?? "—"}
sublabel={mostActiveUser ? `${mostActiveUser.count.toLocaleString()} events` : "No user activity found"}
style={{ gridColumn: "span 3" }}
/>
<div style={{ ...styles.container, ...styles.grid }}>
<Card
label="Users"
value={totalUsers.toLocaleString()}
sublabel={`${connectedUsers.toLocaleString()} users in filtered graph`}
style={{ gridColumn: "span 3" }}
/>
<Card
label="Replies"
value={totalInteractions.toLocaleString()}
sublabel="Links with at least 2 replies"
style={{ gridColumn: "span 3" }}
/>
<Card
label="Replies per Connected User"
value={avgInteractionsPerConnectedUser.toFixed(1)}
sublabel="Average from visible graph links"
style={{ gridColumn: "span 3" }}
/>
<Card
label="Most Active User"
value={mostActiveUser?.author ?? "—"}
sublabel={
mostActiveUser
? `${mostActiveUser.count.toLocaleString()} events`
: "No user activity found"
}
style={{ gridColumn: "span 3" }}
/>
<Card
label="Strongest User Link"
value={strongestLink ? `${strongestLink.source} -> ${strongestLink.target}` : "—"}
sublabel={strongestLink ? `${strongestLink.value.toLocaleString()} replies` : "No graph links after filtering"}
style={{ gridColumn: "span 6" }}
/>
<Card
label="Most Comment-Heavy User"
value={highlyInteractiveUser?.author ?? "—"}
sublabel={
highlyInteractiveUser
? `${Math.round(highlyInteractiveUser.comment_share * 100)}% comments`
: "No user distribution available"
}
style={{ gridColumn: "span 6" }}
/>
<Card
label="Strongest User Link"
value={
strongestLink
? `${strongestLink.source} -> ${strongestLink.target}`
: "—"
}
sublabel={
strongestLink
? `${strongestLink.value.toLocaleString()} replies`
: "No graph links after filtering"
}
style={{ gridColumn: "span 6" }}
/>
<Card
label="Most Comment-Heavy User"
value={highlyInteractiveUser?.author ?? "—"}
sublabel={
highlyInteractiveUser
? `${Math.round(highlyInteractiveUser.comment_share * 100)}% comments`
: "No user distribution available"
}
style={{ gridColumn: "span 6" }}
/>
<div style={{ ...styles.card, gridColumn: "span 12" }}>
<h2 style={styles.sectionTitle}>User Interaction Graph</h2>
<p style={styles.sectionSubtitle}>
Each node is a user, and each link shows replies between them.
</p>
<div ref={graphContainerRef} style={{ width: "100%", height: graphSize.height }}>
<ForceGraph3D
width={graphSize.width}
height={graphSize.height}
graphData={graphData}
nodeAutoColorBy="id"
linkDirectionalParticles={1}
linkDirectionalParticleSpeed={0.004}
linkWidth={(link) => Math.sqrt(Number(link.value))}
nodeLabel={(node) => `${node.id}`}
/>
</div>
<div style={{ ...styles.card, gridColumn: "span 12" }}>
<h2 style={styles.sectionTitle}>User Interaction Graph</h2>
<p style={styles.sectionSubtitle}>
Each node is a user, and each link shows replies between them.
</p>
<div
ref={graphContainerRef}
style={{ width: "100%", height: graphSize.height }}
>
<ForceGraph3D
width={graphSize.width}
height={graphSize.height}
graphData={graphData}
nodeAutoColorBy="id"
linkDirectionalParticles={1}
linkDirectionalParticleSpeed={0.004}
linkWidth={(link) => Math.sqrt(Number(link.value))}
nodeLabel={(node) => `${node.id}`}
/>
</div>
</div>
</div>
</div>
);
}
};
export default UserStats;