mirror of
https://github.com/sergi0g/cup.git
synced 2025-11-16 00:53:47 -05:00
V3
Many many many changes, honestly just read the release notes
This commit is contained in:
@@ -8,6 +8,18 @@ import { Data } from "./types";
|
||||
import { theme } from "./theme";
|
||||
import RefreshButton from "./components/RefreshButton";
|
||||
import Search from "./components/Search";
|
||||
import { Server } from "./components/Server";
|
||||
|
||||
const SORT_ORDER = [
|
||||
"monitored_images",
|
||||
"updates_available",
|
||||
"major_updates",
|
||||
"minor_updates",
|
||||
"patch_updates",
|
||||
"other_updates",
|
||||
"up_to_date",
|
||||
"unknown",
|
||||
];
|
||||
|
||||
function App() {
|
||||
const [data, setData] = useState<Data | null>(null);
|
||||
@@ -15,41 +27,65 @@ function App() {
|
||||
if (!data) return <Loading onLoad={setData} />;
|
||||
return (
|
||||
<div
|
||||
className={`flex justify-center min-h-screen bg-${theme}-50 dark:bg-${theme}-950`}
|
||||
className={`flex min-h-screen justify-center bg-white dark:bg-${theme}-950`}
|
||||
>
|
||||
<div className="lg:px-8 sm:px-6 px-4 max-w-[80rem] mx-auto h-full w-full">
|
||||
<div className="flex flex-col max-w-[48rem] mx-auto h-full my-8">
|
||||
<div className="mx-auto h-full w-full max-w-[80rem] px-4 sm:px-6 lg:px-8">
|
||||
<div className="mx-auto my-8 flex h-full max-w-[48rem] flex-col">
|
||||
<div className="flex items-center gap-1">
|
||||
<h1 className="text-5xl lg:text-6xl font-bold dark:text-white">
|
||||
<h1 className="text-5xl font-bold tracking-tight lg:text-6xl dark:text-white">
|
||||
Cup
|
||||
</h1>
|
||||
<Logo />
|
||||
</div>
|
||||
<div
|
||||
className={`shadow-sm bg-white dark:bg-${theme}-900 rounded-md my-8`}
|
||||
className={`border shadow-sm border-${theme}-200 dark:border-${theme}-900 my-8 rounded-md`}
|
||||
>
|
||||
<dl className="lg:grid-cols-4 md:grid-cols-2 gap-1 grid-cols-1 grid overflow-hidden *:relative">
|
||||
{Object.entries(data.metrics).map(([name, value]) => (
|
||||
<Statistic name={name} value={value} key={name} />
|
||||
))}
|
||||
<dl className="grid grid-cols-2 gap-1 overflow-hidden *:relative lg:grid-cols-4">
|
||||
{Object.entries(data.metrics)
|
||||
.sort((a, b) => {
|
||||
return SORT_ORDER.indexOf(a[0]) - SORT_ORDER.indexOf(b[0]);
|
||||
})
|
||||
.map(([name]) => (
|
||||
<Statistic
|
||||
name={name as keyof typeof data.metrics}
|
||||
metrics={data.metrics}
|
||||
key={name}
|
||||
/>
|
||||
))}
|
||||
</dl>
|
||||
</div>
|
||||
<div
|
||||
className={`shadow-sm bg-white dark:bg-${theme}-900 rounded-md my-8`}
|
||||
className={`border shadow-sm border-${theme}-200 dark:border-${theme}-900 my-8 rounded-md`}
|
||||
>
|
||||
<div
|
||||
className={`flex justify-between items-center px-6 py-4 text-${theme}-500`}
|
||||
className={`flex items-center justify-between px-6 py-4 text-${theme}-500`}
|
||||
>
|
||||
<LastChecked datetime={data.last_updated} />
|
||||
<RefreshButton />
|
||||
</div>
|
||||
<Search onChange={setSearchQuery}/>
|
||||
<ul
|
||||
className={`*:py-4 *:px-6 *:flex *:items-center *:gap-3 dark:divide-${theme}-800 divide-y dark:text-white`}
|
||||
>
|
||||
{Object.entries(data.images).filter(([name]) => name.includes(searchQuery)).map(([name, status]) => (
|
||||
<Image name={name} status={status} key={name} />
|
||||
))}
|
||||
<Search onChange={setSearchQuery} />
|
||||
<ul>
|
||||
{Object.entries(
|
||||
data.images.reduce<Record<string, typeof data.images>>(
|
||||
(acc, image) => {
|
||||
const server = image.server ?? "";
|
||||
if (!acc[server]) acc[server] = [];
|
||||
acc[server].push(image);
|
||||
return acc;
|
||||
},
|
||||
{},
|
||||
),
|
||||
)
|
||||
.sort()
|
||||
.map(([server, images]) => (
|
||||
<Server name={server} key={server}>
|
||||
{images
|
||||
.filter((image) => image.reference.includes(searchQuery))
|
||||
.map((image) => (
|
||||
<Image data={image} key={image.reference} />
|
||||
))}
|
||||
</Server>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user