Change frontend deps

This commit is contained in:
Torjus Håkestad 2025-03-09 03:20:38 +01:00
parent ec6c0cc45b
commit 5ade1c0593
Signed by: torjus
SSH Key Fingerprint: SHA256:KjAds8wHfD2mBYK2H815s/+ABcSdcIHUndwHEdSxml4
8 changed files with 4856 additions and 62 deletions

View File

@ -57,7 +57,7 @@
src = ./frontend; src = ./frontend;
npmDepsHash = "sha256-cN+xm9wBRcpDHuYy8PelV1YWhudw4y8B6Ap4+3En8/4="; npmDepsHash = "sha256-4gjtxGquuFzk4AOnm0lZC5x7+pVAMrt6CeQvj1Cd8W8=";
installPhase = '' installPhase = ''
mkdir -p $out mkdir -p $out

7
frontend/jest.config.js Normal file
View File

@ -0,0 +1,7 @@
/** @type {import('ts-jest').JestConfigWithTsJest} **/
module.exports = {
testEnvironment: "jsdom",
transform: {
"^.+\.tsx?$": ["ts-jest",{}],
},
};

File diff suppressed because it is too large Load Diff

View File

@ -2,27 +2,35 @@
"name": "apiary-frontend", "name": "apiary-frontend",
"scripts": { "scripts": {
"start": "parcel src/index.html", "start": "parcel src/index.html",
"build": "parcel build src/index.html" "build": "parcel build src/index.html",
"test": "jest"
}, },
"devDependencies": { "devDependencies": {
"@ngneat/falso": "^7.3.0", "@ngneat/falso": "^7.3.0",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@testing-library/user-event": "^14.6.1",
"@types/humanize-duration": "^3.27.4",
"@types/jest": "^29.5.14",
"@types/node": "^22.13.9",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"parcel": "^2.13.3", "parcel": "^2.13.3",
"process": "^0.11.10", "process": "^0.11.10",
"svgo": "^3.3.2", "svgo": "^3.3.2",
"ts-jest": "^29.2.6",
"ts-loader": "^9.5.2", "ts-loader": "^9.5.2",
"typescript": "^5.8.2" "typescript": "^5.8.2"
}, },
"dependencies": { "dependencies": {
"@types/humanize-duration": "^3.27.4",
"@types/node": "^22.13.9",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"chart.js": "^4.4.8", "chart.js": "^4.4.8",
"humanize-duration": "^3.32.1", "humanize-duration": "^3.32.1",
"react": "^19.0.0", "react": "^19.0.0",
"react-chartjs-2": "^5.3.0", "react-chartjs-2": "^5.3.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-router": "^7.2.0", "react-router": "^7.2.0"
"swr": "^2.3.2"
} }
} }

View File

@ -132,7 +132,9 @@ a {
} }
#menu-logo { #menu-logo {
padding: 5px; padding: 5px;
content: url("../assets/apiary.svg");
width: 30px;
height: 30px;
} }
.menu-link { .menu-link {
color: var(--menu-color-text); color: var(--menu-color-text);

View File

@ -8,7 +8,6 @@ import { Pie } from "react-chartjs-2";
import humanizeDuration from "humanize-duration"; import humanizeDuration from "humanize-duration";
ChartJS.register(Tooltip, ArcElement, Legend); ChartJS.register(Tooltip, ArcElement, Legend);
const logo = new URL("../assets/apiary.svg", import.meta.url)
interface AppProps { interface AppProps {
api: ApiaryAPI api: ApiaryAPI
@ -34,14 +33,13 @@ let chartColors = [
"#d35400", "#d35400",
"#c0392b", "#c0392b",
"#bdc3c7", "#bdc3c7",
"#7f8c8d" "#7f8c8d",
] ]
export function App({ api }: AppProps) { export function App({ api }: AppProps) {
const [mode, setMode] = useState("light"); const [mode, setMode] = useState("light");
const headerProps: HeaderMenuProps = { const headerProps: HeaderMenuProps = {
title: "apiary.home.2rjus.net", title: "apiary.home.2rjus.net",
logo: logo,
items: [ items: [
{ {
name: "Totals", name: "Totals",
@ -90,7 +88,7 @@ export function App({ api }: AppProps) {
<> <>
<BrowserRouter> <BrowserRouter>
<div id="app"> <div id="app">
<HeaderMenu title={headerProps.title} logo={logo} items={headerProps.items} /> <HeaderMenu title={headerProps.title} items={headerProps.items} />
<div className="content"> <div className="content">
<Routes> <Routes>
<Route path="/" element={<Home api={api} />} /> <Route path="/" element={<Home api={api} />} />
@ -113,7 +111,7 @@ export interface StatsProps {
} }
export function Stats({ api, type }: StatsProps) { export function Stats({ api, type }: StatsProps) {
const [stats, setStats] = useState<StatsResult[]|null>(null) const [stats, setStats] = useState<StatsResult[] | null>(null)
const [currentType, setCurrentType] = useState(type) const [currentType, setCurrentType] = useState(type)
useEffect(() => { useEffect(() => {
@ -250,14 +248,14 @@ export function Live({ api }: AppProps) {
export interface LiveListProps { export interface LiveListProps {
list: LoginAttempt[] list: LoginAttempt[]
}; };
export interface DateTDProps { export interface DateTDProps {
date: Date date: Date
now: Date now: Date
} }
export function DateTD({ date, now}: DateTDProps) { export function DateTD({ date, now }: DateTDProps) {
const [displayDate, setDisplayDate] = useState(date.toLocaleString()); const [displayDate, setDisplayDate] = useState(date.toLocaleString());
useEffect(() => { useEffect(() => {
@ -270,12 +268,12 @@ export function DateTD({ date, now}: DateTDProps) {
}, [displayDate, now]) }, [displayDate, now])
return ( return (
<td className="live-table-date">{displayDate}</td> <td className="live-table-date">{displayDate}</td>
) )
} }
export function LiveList({ list }: LiveListProps) { export function LiveList({ list }: LiveListProps) {
const [now, setNow] = useState(new Date()) const [now, setNow] = useState(new Date())
let items = list.map((a) => { let items = list.map((a) => {
const attemptDate = new Date(a.date); const attemptDate = new Date(a.date);
@ -389,21 +387,21 @@ interface HeaderItem {
interface HeaderMenuProps { interface HeaderMenuProps {
title: string title: string
logo: URL
items: Array<HeaderItem> items: Array<HeaderItem>
} }
export function HeaderMenu({ title, logo, items }: HeaderMenuProps) { export function HeaderMenu({ title, items }: HeaderMenuProps) {
const menuItems = items.map((item) => { const menuItems = items.map((item) => {
return ( return (
<li key={item.path}> <li key={item.path}>
<NavLink to={item.path} className={({ isActive }) => isActive ? "menu-link-active" : "menu-link"}>{item.name}</NavLink> <NavLink to={item.path} className={({ isActive }) => isActive ? "menu-link-active" : "menu-link"}>{item.name}</NavLink>
</li> </li>
)}) )
})
return ( return (
<div className="navbar"> <div className="navbar">
<img id="menu-logo" src={logo.href}></img> <img id="menu-logo"></img>
<h2 id="menu-title">{title}</h2> <h2 id="menu-title">{title}</h2>
<nav className="nav-flex"> <nav className="nav-flex">
<ul> <ul>

View File

@ -0,0 +1,17 @@
import { DummyApiaryAPIClient, TotalStats } from "../js/api";
describe("DummyApiaryAPIClient", () => {
const api = new DummyApiaryAPIClient()
test("totals returns expeced value", async () => {
let totals = await api.totals()
const expected: TotalStats = {
password: 1,
username: 1,
ip: 1,
attempts: 1,
country: 1,
}
expect(totals).toEqual(expected)
})
});

View File

@ -8,7 +8,7 @@
"strict": true, "strict": true,
"sourceMap": true, "sourceMap": true,
"target": "esnext", "target": "esnext",
"types": ["node"] "types": ["node", "jest"]
}, },
"exclude": ["node_modules"], "exclude": ["node_modules"],
"include": ["src/**/*.ts", "src/**/*.tsx"] "include": ["src/**/*.ts", "src/**/*.tsx"]