forked from github-starred/komodo
basic frontend layout
This commit is contained in:
@@ -7,7 +7,7 @@ import {
|
||||
|
||||
export const CORE_SERVER_NAME = getStringFromEnv("CORE_SERVER_NAME", "Monitor Core");
|
||||
export const SECRETS = readJSONFile("/secrets/secrets.json");
|
||||
export const LOGGER = getBooleanFromEnv("LOGGER", true);
|
||||
export const LOGGER = getBooleanFromEnv("LOGGER", false);
|
||||
export const PORT = getNumberFromEnv("PORT", 9000);
|
||||
export const HOST = getStringFromEnv("HOST", "http://localhost:" + PORT);
|
||||
export const MONGO_URL = getStringFromEnv(
|
||||
|
||||
@@ -36,6 +36,7 @@ async function main() {
|
||||
app.log.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
console.log(app.core)
|
||||
if (!LOGGER) console.log(`monitor core listening at ${address}`);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ declare module "fastify" {
|
||||
builds: Model<Build>;
|
||||
updates: Model<Update>;
|
||||
servers: Model<Server>;
|
||||
core: Server;
|
||||
core: Server & { _id: string };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,12 @@ const db = fp(async (app: FastifyInstance, _: {}, done: () => void) => {
|
||||
.register(builds)
|
||||
.register(updates);
|
||||
|
||||
app.after(async () => {
|
||||
const server = await app.servers.findOne({ isCore: true });
|
||||
server!._id = server?._id?.toString();
|
||||
app.decorate("core", server);
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
@@ -15,10 +15,6 @@ const servers = fp((app: FastifyInstance, _: {}, done: () => void) => {
|
||||
|
||||
app.decorate("servers", model(app, "Server", schema));
|
||||
|
||||
app.after(async () => {
|
||||
app.decorate("core", await app.servers.findOne({ isCore: true }));
|
||||
});
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ const deployments = fp((app: FastifyInstance, _: {}, done: () => void) => {
|
||||
// returns the core deployments if no serverID is specified
|
||||
const { serverID } = req.query as { serverID?: string };
|
||||
const deployments = app.deployments.findCollection(
|
||||
serverID ? { serverID } : { serverID: app.core._id! }
|
||||
serverID ? { serverID } : { serverID: app.core._id }
|
||||
);
|
||||
res.send(deployments);
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<div id="root" class="root"></div>
|
||||
|
||||
<script src="/src/index.tsx" type="module"></script>
|
||||
</body>
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.26.1",
|
||||
"solid-js": "^1.3.10",
|
||||
"@monitor/util": "1.0.0"
|
||||
"solid-js": "^1.3.10"
|
||||
}
|
||||
}
|
||||
|
||||
13
frontend/public/icons/build.svg
Normal file
13
frontend/public/icons/build.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
|
||||
<g id="Rectangle_1_11_">
|
||||
<g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.43,16.67L9.31,7.81l1.47-1.56c0.41-0.44-0.15-0.8,0.15-1.6
|
||||
c1.08-2.76,4.19-2.99,4.19-2.99s0.45-0.47,0.87-0.92C11.98-1,9.26,0.7,8.04,1.8L3.83,6.25L2.97,7.17c-0.48,0.51-0.48,1.33,0,1.84
|
||||
L2.1,9.93c-0.48-0.51-1.26-0.51-1.74,0s-0.48,1.33,0,1.84l1.74,1.84c0.48,0.51,1.26,0.51,1.74,0c0.48-0.51,0.48-1.33,0-1.84
|
||||
l0.87-0.92c0.48,0.51,1.26,0.51,1.74,0l1.41-1.49l8.81,10.07c0.76,0.76,2,0.76,2.76,0C20.19,18.67,20.19,17.43,19.43,16.67z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 875 B |
12
frontend/public/icons/menu.svg
Normal file
12
frontend/public/icons/menu.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
|
||||
<g id="menu_1_">
|
||||
<g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1,6h18c0.55,0,1-0.45,1-1c0-0.55-0.45-1-1-1H1C0.45,4,0,4.45,0,5
|
||||
C0,5.55,0.45,6,1,6z M19,9H1c-0.55,0-1,0.45-1,1c0,0.55,0.45,1,1,1h18c0.55,0,1-0.45,1-1C20,9.45,19.55,9,19,9z M19,14H1
|
||||
c-0.55,0-1,0.45-1,1c0,0.55,0.45,1,1,1h18c0.55,0,1-0.45,1-1C20,14.45,19.55,14,19,14z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 715 B |
14
frontend/src/App.tsx
Normal file
14
frontend/src/App.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Component } from "solid-js";
|
||||
import Sidebar from "./components/sidebar/Sidebar";
|
||||
import Topbar from "./components/topbar/Topbar";
|
||||
|
||||
const App: Component = () => {
|
||||
return (
|
||||
<>
|
||||
<Topbar />
|
||||
<Sidebar />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Component } from "solid-js";
|
||||
import UserInfo from "./UserInfo";
|
||||
|
||||
const App: Component = () => {
|
||||
return (
|
||||
<>
|
||||
<UserInfo />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Component, Show } from "solid-js";
|
||||
import { getAuthProvider } from "../util/helpers";
|
||||
import Flex from "./util/layout/Flex";
|
||||
import Grid from "./util/layout/Grid";
|
||||
import { User } from "@monitor/types";
|
||||
import { pushNotification } from "..";
|
||||
import { useUser } from "../state/UserProvider";
|
||||
import { useAppState } from "../state/StateProvider";
|
||||
|
||||
const UserInfo: Component<{}> = (p) => {
|
||||
const { user, logout } = useUser();
|
||||
const { ws } = useAppState();
|
||||
return (
|
||||
<Grid style={{ "font-size": "2rem" }}>
|
||||
<div>provider: {getAuthProvider(user() as User)}</div>
|
||||
<Flex alignItems="center">
|
||||
<div>username: {user().username}</div>
|
||||
<Show when={user().avatar}>
|
||||
<img src={user().avatar} style={{ width: "2rem", height: "2rem" }} />
|
||||
</Show>
|
||||
</Flex>
|
||||
<button style={{ width: "100%" }} onClick={() => {
|
||||
logout();
|
||||
ws.close();
|
||||
pushNotification("ok", "logged out");
|
||||
}}>
|
||||
logout
|
||||
</button>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserInfo;
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component } from "solid-js";
|
||||
import s from "./login.module.css";
|
||||
import Input from "../util/Input";
|
||||
import Grid from "../util/layout/Grid";
|
||||
import { createStore } from "solid-js/store";
|
||||
@@ -44,24 +45,26 @@ const Login: Component<{}> = (p) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Input
|
||||
placeholder="username"
|
||||
value={info.username}
|
||||
onEdit={(value) => set("username", value)}
|
||||
/>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="password"
|
||||
value={info.password}
|
||||
onEdit={(value) => set("password", value)}
|
||||
/>
|
||||
<Flex style={{ width: "100%" }} justifyContent="space-between">
|
||||
<button onClick={login}>log in</button>
|
||||
<button onClick={signup}>sign up</button>
|
||||
</Flex>
|
||||
<button onClick={() => client.loginGithub()}>log in with github</button>
|
||||
</Grid>
|
||||
<div class={s.Login}>
|
||||
<Grid>
|
||||
<Input
|
||||
placeholder="username"
|
||||
value={info.username}
|
||||
onEdit={(value) => set("username", value)}
|
||||
/>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="password"
|
||||
value={info.password}
|
||||
onEdit={(value) => set("password", value)}
|
||||
/>
|
||||
<Flex style={{ width: "100%" }} justifyContent="space-between">
|
||||
<button onClick={login}>log in</button>
|
||||
<button onClick={signup}>sign up</button>
|
||||
</Flex>
|
||||
<button onClick={() => client.loginGithub()}>log in with github</button>
|
||||
</Grid>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
6
frontend/src/components/login/login.module.css
Normal file
6
frontend/src/components/login/login.module.css
Normal file
@@ -0,0 +1,6 @@
|
||||
.Login {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
.Sidebar {
|
||||
|
||||
grid-area: sidebar;
|
||||
background-color: blue;
|
||||
}
|
||||
@@ -1,12 +1,30 @@
|
||||
import { Component } from "solid-js";
|
||||
import { Component, For, Show } from "solid-js";
|
||||
import { useAppDimensions } from "../../state/DimensionProvider";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { inPx } from "../../util/helpers";
|
||||
import { TOPBAR_HEIGHT } from "../topbar/Topbar";
|
||||
import Grid from "../util/layout/Grid";
|
||||
import Server from "./server/Server";
|
||||
import s from "./sidebar.module.css";
|
||||
|
||||
const Sidebar: Component<{}> = (p) => {
|
||||
return (
|
||||
<div class={s.Sidebar} >
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const SIDEBAR_WIDTH = 350;
|
||||
|
||||
export default Sidebar;
|
||||
const Sidebar: Component<{}> = (p) => {
|
||||
const { sidebar, servers } = useAppState();
|
||||
const { height } = useAppDimensions();
|
||||
return (
|
||||
<Show when={servers.loaded() && sidebar.open()}>
|
||||
<Grid
|
||||
class={s.Sidebar}
|
||||
style={{
|
||||
width: inPx(SIDEBAR_WIDTH),
|
||||
height: inPx(height() - TOPBAR_HEIGHT),
|
||||
}}
|
||||
>
|
||||
<For each={servers.ids()}>{(id) => <Server id={id} />}</For>
|
||||
</Grid>
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebar;
|
||||
|
||||
@@ -10,11 +10,11 @@ const Server: Component<{ id: string }> = (p) => {
|
||||
const { servers, deployments } = useAppState();
|
||||
const server = () => servers.get(p.id);
|
||||
const deploymentIDs = createMemo(() => {
|
||||
return Object.keys(deployments.collection()!).filter(
|
||||
return deployments.loaded() && deployments.ids()!.filter(
|
||||
(id) => deployments.get(id)?.serverID === p.id
|
||||
);
|
||||
});
|
||||
const [open, toggleOpen] = useLocalStorageToggle(false, p.id);
|
||||
const [open, toggleOpen] = useLocalStorageToggle(p.id);
|
||||
return (
|
||||
<div class={s.Server}>
|
||||
<Flex justifyContent="space-between" onClick={toggleOpen}>
|
||||
|
||||
32
frontend/src/components/topbar/Topbar.tsx
Normal file
32
frontend/src/components/topbar/Topbar.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Component } from "solid-js";
|
||||
import { useAppState } from "../../state/StateProvider";
|
||||
import { inPx } from "../../util/helpers";
|
||||
import Icon from "../util/icons/Icon";
|
||||
import Flex from "../util/layout/Flex";
|
||||
import s from "./topbar.module.css";
|
||||
|
||||
export const TOPBAR_HEIGHT = 40;
|
||||
|
||||
const Topbar: Component<{}> = (p) => {
|
||||
const { sidebar } = useAppState();
|
||||
return (
|
||||
<Flex
|
||||
class={s.Topbar}
|
||||
justifyContent="space-between"
|
||||
style={{ height: inPx(TOPBAR_HEIGHT) }}
|
||||
>
|
||||
{/* right side */}
|
||||
<Flex>
|
||||
<button onClick={sidebar.toggle}>
|
||||
<Icon type="menu" />
|
||||
</button>
|
||||
<div>monitor</div>
|
||||
</Flex>
|
||||
|
||||
{/* left side */}
|
||||
<Flex></Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default Topbar;
|
||||
5
frontend/src/components/topbar/topbar.module.css
Normal file
5
frontend/src/components/topbar/topbar.module.css
Normal file
@@ -0,0 +1,5 @@
|
||||
.Topbar {
|
||||
grid-area: topbar;
|
||||
width: 100%;
|
||||
background-color: red;
|
||||
}
|
||||
@@ -14,11 +14,13 @@ export type IconType =
|
||||
| "star"
|
||||
| "chevron-left"
|
||||
| "trash"
|
||||
| "info-sign";
|
||||
| "info-sign"
|
||||
| "menu"
|
||||
| "build";
|
||||
|
||||
const Icon: Component<{
|
||||
type: IconType;
|
||||
alt: string;
|
||||
alt?: string;
|
||||
className?: string;
|
||||
style?: JSX.CSSProperties;
|
||||
width?: string;
|
||||
@@ -30,7 +32,7 @@ const Icon: Component<{
|
||||
<img
|
||||
className={p.className}
|
||||
src={`/icons/${p.type}.svg`}
|
||||
alt={p.alt}
|
||||
alt={p.alt || p.type}
|
||||
title={p.title}
|
||||
style={{
|
||||
...p.style,
|
||||
|
||||
@@ -5,6 +5,18 @@ body {
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background-color: lightskyblue;
|
||||
}
|
||||
|
||||
.root {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr 2fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-template-areas:
|
||||
"topbar topbar topbar"
|
||||
"sidebar left right";
|
||||
}
|
||||
|
||||
code {
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
import { render } from "solid-js/web";
|
||||
|
||||
import "./index.css";
|
||||
import App from "./components/App";
|
||||
import App from "./App";
|
||||
import Client from "./util/client";
|
||||
import makeNotifications from "./components/util/notification/Notifications";
|
||||
import { UserProvider } from "./state/UserProvider";
|
||||
import { WidthProvider } from "./state/WidthProvider";
|
||||
import { DimensionProvider } from "./state/DimensionProvider";
|
||||
import LoginGuard from "./components/login/LoginGuard";
|
||||
import { AppStateProvider } from "./state/StateProvider";
|
||||
|
||||
@@ -18,7 +18,7 @@ export const { Notifications, pushNotification } = makeNotifications();
|
||||
|
||||
render(
|
||||
() => [
|
||||
<WidthProvider>
|
||||
<DimensionProvider>
|
||||
<UserProvider>
|
||||
<LoginGuard>
|
||||
<AppStateProvider>
|
||||
@@ -26,7 +26,7 @@ render(
|
||||
</AppStateProvider>
|
||||
</LoginGuard>
|
||||
</UserProvider>
|
||||
</WidthProvider>,
|
||||
</DimensionProvider>,
|
||||
<Notifications />,
|
||||
],
|
||||
document.getElementById("root") as HTMLElement
|
||||
|
||||
29
frontend/src/state/DimensionProvider.tsx
Normal file
29
frontend/src/state/DimensionProvider.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Accessor, Component, createContext, useContext } from "solid-js";
|
||||
import { useInnerHeight, useInnerWidth } from "../util/hooks";
|
||||
|
||||
type DimensionState = {
|
||||
width: Accessor<number>;
|
||||
height: Accessor<number>;
|
||||
isMobile: () => boolean;
|
||||
};
|
||||
|
||||
const DimensionContext = createContext<DimensionState>();
|
||||
|
||||
export const DimensionProvider: Component = (p) => {
|
||||
const width = useInnerWidth();
|
||||
const height = useInnerHeight();
|
||||
const context = {
|
||||
width,
|
||||
height,
|
||||
isMobile: () => width() < 700,
|
||||
};
|
||||
return (
|
||||
<DimensionContext.Provider value={context}>
|
||||
{p.children}
|
||||
</DimensionContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export function useAppDimensions() {
|
||||
return useContext(DimensionContext) as DimensionState;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Component, createContext, useContext } from "solid-js";
|
||||
import { Accessor, Component, createContext, useContext } from "solid-js";
|
||||
import { useLocalStorageToggle } from "../util/hooks";
|
||||
import {
|
||||
useBuilds,
|
||||
useDeployments,
|
||||
@@ -12,16 +13,25 @@ export type State = {
|
||||
builds: ReturnType<typeof useBuilds>;
|
||||
deployments: ReturnType<typeof useDeployments>;
|
||||
updates: ReturnType<typeof useUpdates>;
|
||||
sidebar: {
|
||||
open: Accessor<boolean>,
|
||||
toggle: () => void;
|
||||
};
|
||||
};
|
||||
|
||||
const context = createContext<State & { ws: ReturnType<typeof socket> }>();
|
||||
|
||||
export const AppStateProvider: Component<{}> = (p) => {
|
||||
const [sidebarOpen, toggleSidebarOpen] = useLocalStorageToggle("sidebar-open");
|
||||
const state: State = {
|
||||
servers: useServers(),
|
||||
builds: useBuilds(),
|
||||
deployments: useDeployments(),
|
||||
updates: useUpdates(),
|
||||
sidebar: {
|
||||
open: sidebarOpen,
|
||||
toggle: toggleSidebarOpen,
|
||||
}
|
||||
};
|
||||
|
||||
// created state before attaching ws, to pass state easily
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import { Accessor, Component, createContext, useContext } from "solid-js";
|
||||
import { useInnerWidth } from "../util/hooks";
|
||||
|
||||
type WidthState = {
|
||||
width: Accessor<number>;
|
||||
isMobile: () => boolean;
|
||||
};
|
||||
|
||||
const WidthContext = createContext<WidthState>();
|
||||
|
||||
export const WidthProvider: Component = (p) => {
|
||||
const width = useInnerWidth();
|
||||
const context = {
|
||||
width,
|
||||
isMobile: () => width() < 700,
|
||||
};
|
||||
return (
|
||||
<WidthContext.Provider value={context}>{p.children}</WidthContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export function useAppWidth() {
|
||||
return useContext(WidthContext) as WidthState;
|
||||
}
|
||||
29
frontend/src/state/actions.ts
Normal file
29
frontend/src/state/actions.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/* BUILD */
|
||||
export const CREATE_BUILD = "CREATE_BUILD";
|
||||
export const DELETE_BUILD = "DELETE_BUILD";
|
||||
export const UPDATE_BUILD = "UPDATE_BUILD";
|
||||
export const PULL = "PULL";
|
||||
export const BUILD = "BUILD";
|
||||
export const CLONE_BUILD_REPO = "CLONE_REPO";
|
||||
|
||||
/* DEPLOY */
|
||||
export const CREATE_DEPLOYMENT = "CREATE_DEPLOYMENT";
|
||||
export const DELETE_DEPLOYMENT = "DELETE_DEPLOYMENT";
|
||||
export const UPDATE_DEPLOYMENT = "UPDATE_DEPLOYMENT";
|
||||
export const DEPLOY = "DEPLOY";
|
||||
export const START_CONTAINER = "START_CONTAINER";
|
||||
export const STOP_CONTAINER = "STOP_CONTAINER";
|
||||
export const DELETE_CONTAINER = "DELETE_CONTAINER";
|
||||
export const REFRESH_CONTAINER_STATUS = "REFRESH_CONTAINER_STATUS";
|
||||
export const COPY_ENV = "COPY_ENV";
|
||||
export const CLONE_DEPLOYMENT_REPO = "CLONE_REPO";
|
||||
|
||||
/* SERVER */
|
||||
export const ADD_SERVER = "ADD_SERVER";
|
||||
export const REMOVE_SERVER = "REMOVE_SERVER";
|
||||
export const UPDATE_SERVER = "UPDATE_SERVER";
|
||||
export const PRUNE_SERVER = "PRUNE_SERVER";
|
||||
export const GET_SERVER_STATS = "GET_SERVER_STATS";
|
||||
|
||||
/* UPDATE */
|
||||
export const ADD_UPDATE = "ADD_UPDATE";
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Collection } from "@monitor/types";
|
||||
import { filterOutFromObj } from "@monitor/util";
|
||||
import { createResource } from "solid-js";
|
||||
import { client, WS_URL } from "..";
|
||||
import { Collection, Update } from "@monitor/types";
|
||||
import { createEffect, createResource } from "solid-js";
|
||||
import { filterOutFromObj } from "../util/helpers";
|
||||
import {
|
||||
getBuilds,
|
||||
getDeployments,
|
||||
@@ -22,34 +21,55 @@ export function useDeployments(query?: Parameters<typeof getDeployments>[0]) {
|
||||
}
|
||||
|
||||
export function useUpdates(query?: Parameters<typeof getUpdates>[0]) {
|
||||
const [collection, { refetch }] = createResource(() => getUpdates(query));
|
||||
return useArray(() => getUpdates(query));
|
||||
}
|
||||
|
||||
export function useArray<T>(query: () => Promise<T[]>) {
|
||||
const [collection, { mutate }] = createResource(query);
|
||||
const push = (update: Update) => {
|
||||
mutate((updates: any) => [update, ...updates]);
|
||||
};
|
||||
const loaded = () => collection() ? true : false;
|
||||
return {
|
||||
collection,
|
||||
refetch,
|
||||
push,
|
||||
loaded
|
||||
};
|
||||
}
|
||||
|
||||
export function useCollection<T>(query: () => Promise<Collection<T>>) {
|
||||
const [collection, { mutate }] = createResource(query);
|
||||
|
||||
const add = (items: Collection<T>) => {
|
||||
const add = (item: T & { _id?: string }) => {
|
||||
mutate((collection: any) => ({ ...collection, [item._id!]: item }));
|
||||
};
|
||||
const addMany = (items: Collection<T>) => {
|
||||
mutate((collection: any) => ({ ...collection, ...items }));
|
||||
};
|
||||
const del = (id: string) => {
|
||||
mutate((collection: any) => filterOutFromObj(collection, [id]));
|
||||
};
|
||||
const update = (item: T & { _id?: string }) => {
|
||||
mutate((collection: any) => ({ ...collection, [item._id!]: item }));
|
||||
mutate((collection: any) => ({
|
||||
...collection,
|
||||
[item._id!]: { ...collection[item._id!], ...item },
|
||||
}));
|
||||
};
|
||||
const get = (id: string) => {
|
||||
return collection() && collection()![id];
|
||||
};
|
||||
const ids = () => collection() && Object.keys(collection()!);
|
||||
const loaded = () => collection() ? true : false;
|
||||
|
||||
createEffect(() => console.log(collection()))
|
||||
|
||||
return {
|
||||
collection,
|
||||
add,
|
||||
addMany,
|
||||
delete: del,
|
||||
update,
|
||||
get,
|
||||
ids,
|
||||
loaded,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
UPDATE_BUILD,
|
||||
UPDATE_DEPLOYMENT,
|
||||
UPDATE_SERVER,
|
||||
} from "@monitor/util";
|
||||
} from "../state/actions";
|
||||
import { State } from "./StateProvider";
|
||||
|
||||
function socket(state: State) {
|
||||
@@ -44,37 +44,47 @@ function handleMessage(
|
||||
switch (message.type) {
|
||||
/* Deployments */
|
||||
case CREATE_DEPLOYMENT:
|
||||
deployments.add(message.deployment);
|
||||
break;
|
||||
|
||||
case DELETE_DEPLOYMENT:
|
||||
deployments.delete(message.deploymentID);
|
||||
break;
|
||||
|
||||
case UPDATE_DEPLOYMENT:
|
||||
deployments.update(message.deployment);
|
||||
break;
|
||||
|
||||
/* Builds */
|
||||
case CREATE_BUILD:
|
||||
builds.add(message.build);
|
||||
break;
|
||||
|
||||
case DELETE_BUILD:
|
||||
builds.delete(message.buildID);
|
||||
break;
|
||||
|
||||
case UPDATE_BUILD:
|
||||
builds.update(message.build);
|
||||
break;
|
||||
|
||||
/* Servers */
|
||||
case ADD_SERVER:
|
||||
servers.add(message.server);
|
||||
break;
|
||||
|
||||
case REMOVE_SERVER:
|
||||
servers.delete(message.serverID);
|
||||
break;
|
||||
|
||||
case UPDATE_SERVER:
|
||||
servers.update(message.server);
|
||||
break;
|
||||
|
||||
/* Updates */
|
||||
case ADD_UPDATE:
|
||||
break;
|
||||
/* Updates */
|
||||
case ADD_UPDATE:
|
||||
updates.push(message.update);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,3 +22,22 @@ export function generateQuery(query?: Collection<string | number | undefined>) {
|
||||
return q && `?${q}`;
|
||||
} else return "";
|
||||
}
|
||||
|
||||
export function objFrom2Arrays<T>(
|
||||
keys: string[],
|
||||
entries: T[]
|
||||
): Collection<T | undefined> {
|
||||
return Object.fromEntries(
|
||||
keys.map((id, index) => {
|
||||
return [id, entries[index]];
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function filterOutFromObj<T>(obj: T, idsToFilterOut: string[]) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(obj).filter((entry) => {
|
||||
return !idsToFilterOut.includes(entry[0]);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,10 +24,10 @@ export function useToggleTimeout(
|
||||
}
|
||||
|
||||
export function useLocalStorageToggle(
|
||||
initial: boolean,
|
||||
key: string
|
||||
key: string,
|
||||
initial?: boolean
|
||||
): [Accessor<boolean>, () => void] {
|
||||
const [s, set] = useLocalStorage(initial, key);
|
||||
const [s, set] = useLocalStorage(initial || false, key);
|
||||
const toggle = () => set((s) => !s);
|
||||
return [s, toggle];
|
||||
}
|
||||
@@ -53,17 +53,26 @@ export function useLocalStorage<T>(
|
||||
return [stored, set];
|
||||
}
|
||||
|
||||
|
||||
export function useInnerWidth(): Accessor<number> {
|
||||
const [width, setWidth] = createSignal(window.innerWidth);
|
||||
onMount(() => {
|
||||
const listener = () => setWidth(window.innerWidth);
|
||||
window.addEventListener("resize", listener);
|
||||
onCleanup(() => window.removeEventListener("resize", listener));
|
||||
})
|
||||
});
|
||||
return width;
|
||||
}
|
||||
|
||||
export function useInnerHeight(): Accessor<number> {
|
||||
const [height, setHeight] = createSignal(window.innerHeight);
|
||||
onMount(() => {
|
||||
const listener = () => setHeight(window.innerHeight);
|
||||
window.addEventListener("resize", listener);
|
||||
onCleanup(() => window.removeEventListener("resize", listener));
|
||||
});
|
||||
return height;
|
||||
}
|
||||
|
||||
export function useWidth(): [
|
||||
Accessor<number>,
|
||||
(el: HTMLDivElement) => void,
|
||||
@@ -91,4 +100,4 @@ export function useKeyDown(key: string, action: () => void) {
|
||||
window.addEventListener("keydown", listener);
|
||||
onCleanup(() => window.removeEventListener("keydown", listener));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user