mirror of
https://github.com/Nezumi-2711/uptime-monitoring.git
synced 2026-09-22 13:48:31 +00:00
feat: migrate icon on app using lib
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
"@tanstack/react-query": "^5.102.7",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"hono": "^4.13.5",
|
||||
"lucide-react": "^1.34.0",
|
||||
"react": "19",
|
||||
"react-dom": "19",
|
||||
},
|
||||
@@ -357,6 +358,8 @@
|
||||
|
||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.33.0", "", { "os": "win32", "cpu": "x64" }, "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA=="],
|
||||
|
||||
"lucide-react": ["lucide-react@1.34.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-vnjGJNI7Htk5+oWW8gXGuaLgwgAb0T6/iZbBrp9JCfRFwdNWZ0YTm3eyxjOLgwN6r8iyAf3UA70zNmBRBNv7yg=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||
|
||||
"miniflare": ["miniflare@5.20260826.0-alpha", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "0.35.2", "undici": "7.29.0", "workerd": "1.20260826.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" } }, "sha512-ZXR3Bieg+B5MK0T/zYIWaZiCGCb9Z3en4+/TleYKhIGPLx/e0cRcG/ZvvTviUapVBBK2zODB+aiypdIojU3x6Q=="],
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"@tanstack/react-query": "^5.102.7",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"hono": "^4.13.5",
|
||||
"lucide-react": "^1.34.0",
|
||||
"react": "19",
|
||||
"react-dom": "19"
|
||||
},
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { type ReactNode, useEffect } from "react";
|
||||
import { LogoMark } from "./icons";
|
||||
import { Zap } from "lucide-react";
|
||||
import { navigate } from "../lib/router";
|
||||
import { useSessionQuery } from "../queries/auth";
|
||||
|
||||
function FullPageLoading() {
|
||||
return (
|
||||
<main className="full-page-loading" aria-busy="true" aria-label="Checking your session">
|
||||
<LogoMark className="loading-mark" />
|
||||
<Zap className="loading-mark" fill="currentColor" />
|
||||
<p>Checking session…</p>
|
||||
</main>
|
||||
);
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
type IconProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function LogoMark({ className }: IconProps) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 32 32" aria-hidden="true">
|
||||
<path d="M17.55 2.42 6.1 17.06c-.6.77-.06 1.9.92 1.9h9.22l-1.08 10.16c-.13 1.2 1.4 1.78 2.08.78L28 14.09c.52-.77-.03-1.81-.96-1.81h-8.56l1.14-8.95c.14-1.14-1.36-1.82-2.07-.91Z" fill="currentColor" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function ArrowIcon({ className }: IconProps) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M3 8h9.5m-3.25-3.5L12.75 8l-3.5 3.5" stroke="currentColor" strokeWidth="1.35" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function RefreshIcon({ className }: IconProps) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="M13 5.5V2.75m0 0h-2.75M13 2.75A6 6 0 1 0 14 9" stroke="currentColor" strokeWidth="1.35" strokeLinecap="round" strokeLinejoin="round" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function DatabaseIcon({ className }: IconProps) {
|
||||
return (
|
||||
<svg className={className} viewBox="0 0 18 18" fill="none" aria-hidden="true">
|
||||
<ellipse cx="9" cy="4" rx="5.75" ry="2.25" stroke="currentColor" strokeWidth="1.3" />
|
||||
<path d="M3.25 4v5.1c0 1.25 2.57 2.27 5.75 2.27s5.75-1.02 5.75-2.27V4M3.25 9.1v4.9c0 1.24 2.57 2.25 5.75 2.25s5.75-1.01 5.75-2.25V9.1" stroke="currentColor" strokeWidth="1.3" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type FormEvent, useState } from "react";
|
||||
import { ArrowRight, Database, RefreshCw, Zap } from "lucide-react";
|
||||
import type { Monitor, MonitorInput, MonitorMethod } from "../api/monitors";
|
||||
import { ArrowIcon, DatabaseIcon, LogoMark, RefreshIcon } from "../components/icons";
|
||||
import { useLogoutMutation } from "../queries/auth";
|
||||
import {
|
||||
useCreateMonitorMutation,
|
||||
@@ -101,7 +101,7 @@ export function DashboardPage() {
|
||||
<header className="dashboard-header">
|
||||
<div className="dashboard-header-inner">
|
||||
<a className="brand" href="/" aria-label="Upwatch dashboard">
|
||||
<LogoMark className="brand-mark" />
|
||||
<Zap className="brand-mark" fill="currentColor" />
|
||||
<span>upwatch</span>
|
||||
</a>
|
||||
<div className="nav-actions">
|
||||
@@ -124,7 +124,7 @@ export function DashboardPage() {
|
||||
<button className="secondary-button" type="button" onClick={closeForm}>Close form</button>
|
||||
) : monitorsQuery.isSuccess && monitors.length > 0 ? (
|
||||
<button className="primary-button" type="button" onClick={openCreateForm}>
|
||||
Add monitor <ArrowIcon />
|
||||
Add monitor <ArrowRight />
|
||||
</button>
|
||||
) : null}
|
||||
</section>
|
||||
@@ -162,7 +162,7 @@ export function DashboardPage() {
|
||||
<div className="panel-heading">
|
||||
<div><h2 id="monitor-list-title">Configured sites</h2><p>Latest result for each monitored endpoint.</p></div>
|
||||
<button className="icon-button" type="button" onClick={() => void monitorsQuery.refetch()} disabled={monitorsQuery.isFetching} aria-label="Refresh monitors">
|
||||
<RefreshIcon className={monitorsQuery.isFetching ? "is-spinning" : ""} />
|
||||
<RefreshCw className={monitorsQuery.isFetching ? "is-spinning" : ""} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -171,7 +171,7 @@ export function DashboardPage() {
|
||||
) : monitorsQuery.isError ? (
|
||||
<div className="panel-state error-state"><strong>Monitors could not be loaded</strong><p>{errorMessage(monitorsQuery.error, "Unknown request error")}</p><button className="secondary-button" type="button" onClick={() => void monitorsQuery.refetch()}>Try again</button></div>
|
||||
) : monitors.length === 0 ? (
|
||||
<div className="panel-state empty-state"><span className="empty-icon"><DatabaseIcon /></span><strong>No monitors yet</strong><p>Add the first endpoint to start collecting availability checks.</p>{!formOpen && <button className="primary-button" type="button" onClick={openCreateForm}>Add first site <ArrowIcon /></button>}</div>
|
||||
<div className="panel-state empty-state"><span className="empty-icon"><Database /></span><strong>No monitors yet</strong><p>Add the first endpoint to start collecting availability checks.</p>{!formOpen && <button className="primary-button" type="button" onClick={openCreateForm}>Add first site <ArrowRight /></button>}</div>
|
||||
) : (
|
||||
<div className="monitor-list">
|
||||
<div className="services-title"><span>Monitor</span><span>Latest result</span><span>Actions</span></div>
|
||||
@@ -182,7 +182,7 @@ export function DashboardPage() {
|
||||
const toggling = updateMutation.isPending && updateMutation.variables?.id === monitor.id;
|
||||
return (
|
||||
<article className={`service-row ${monitor.enabled ? "" : "is-disabled"}`} key={monitor.id}>
|
||||
<div className="service-name"><span className="service-icon"><DatabaseIcon /></span><div><strong>{monitor.name}</strong><small title={monitor.url}>{monitor.url}</small><span className="monitor-meta">{monitor.method} · expect {monitor.expectedStatus} · every {monitor.intervalSeconds / 60}m</span></div></div>
|
||||
<div className="service-name"><span className="service-icon"><Database /></span><div><strong>{monitor.name}</strong><small title={monitor.url}>{monitor.url}</small><span className="monitor-meta">{monitor.method} · expect {monitor.expectedStatus} · every {monitor.intervalSeconds / 60}m</span></div></div>
|
||||
<div className="monitor-result"><span className={`row-status ${checking ? "checking" : status.className}`}><i />{checking ? "Checking" : status.label}</span><code>{monitor.lastStatusCode === null ? "—" : `HTTP ${monitor.lastStatusCode}`} · {monitor.lastLatencyMs === null ? "—" : `${monitor.lastLatencyMs} ms`}</code><small title={monitor.lastError ?? undefined}>{monitor.lastError ?? formatCheckedAt(monitor.lastCheckedAt)}</small></div>
|
||||
<div className="row-actions"><button type="button" onClick={() => checkMutation.mutate(monitor.id)} disabled={checking}>Check now</button><button type="button" onClick={() => openEditForm(monitor)}>Edit</button><button type="button" onClick={() => updateMutation.mutate({ id: monitor.id, input: { enabled: !monitor.enabled } })} disabled={toggling}>{monitor.enabled ? "Disable" : "Enable"}</button><button className="danger-action" type="button" onClick={() => handleDelete(monitor)} disabled={deleting}>{deleting ? "Deleting…" : "Delete"}</button></div>
|
||||
</article>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { type FormEvent, useEffect, useState } from "react";
|
||||
import { LogoMark } from "../components/icons";
|
||||
import { Zap } from "lucide-react";
|
||||
import { navigate } from "../lib/router";
|
||||
import { useLoginMutation, useSessionQuery } from "../queries/auth";
|
||||
|
||||
@@ -27,7 +27,7 @@ export function LoginPage() {
|
||||
return (
|
||||
<main className="auth-page">
|
||||
<div className="auth-brand" aria-label="Upwatch">
|
||||
<LogoMark className="auth-brand-mark" />
|
||||
<Zap className="auth-brand-mark" fill="currentColor" />
|
||||
<span>upwatch</span>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user