From e8a653d344d0ce7733d7ea193747ce4420fddeb3 Mon Sep 17 00:00:00 2001 From: Sean Cassiere <33615041+SeanCassiere@users.noreply.github.com> Date: Tue, 2 Jul 2024 15:56:35 +1200 Subject: [PATCH] refactor(ui): add htmx to simplify the loading requirements of the sidebar content (#43) * feat(ui): add htmx import via script tag * feat(ui): use htmx for loading in the sidebar organizations --- .../app/components/sidebar-organizations.tsx | 26 ++++++++++ src/routers/app/hx-router.tsx | 29 +++++++++++ src/routers/app/index.tsx | 48 +++++-------------- src/routers/app/layouts/app-container.tsx | 46 +++++++----------- src/routers/app/layouts/root-document.tsx | 5 ++ .../pages/app.$workspace.$serviceId.edit.tsx | 13 ++--- .../pages/app.$workspace.$serviceId.index.tsx | 10 ++-- src/routers/app/pages/app.$workspace.edit.tsx | 8 ++-- .../app/pages/app.$workspace.index.tsx | 8 ++-- src/routers/app/pages/app.index.tsx | 4 +- src/server.mts | 2 +- 11 files changed, 109 insertions(+), 90 deletions(-) create mode 100644 src/routers/app/components/sidebar-organizations.tsx create mode 100644 src/routers/app/hx-router.tsx diff --git a/src/routers/app/components/sidebar-organizations.tsx b/src/routers/app/components/sidebar-organizations.tsx new file mode 100644 index 0000000..67ac22b --- /dev/null +++ b/src/routers/app/components/sidebar-organizations.tsx @@ -0,0 +1,26 @@ +import type { FC } from "hono/jsx"; + +import type { TenantRecord } from "@/types/db.mjs"; + +export const SidebarOrganizations: FC<{ + workspace: string; + tenants: Array; +}> = ({ workspace, tenants }) => { + return tenants.length > 0 ? ( + + ) : ( +

😞 You have no organizations.

+ ); +}; diff --git a/src/routers/app/hx-router.tsx b/src/routers/app/hx-router.tsx new file mode 100644 index 0000000..5f53b2f --- /dev/null +++ b/src/routers/app/hx-router.tsx @@ -0,0 +1,29 @@ +import { Hono } from "hono"; + +import { db } from "@/config/db/drizzle.mjs"; +import type { ServerContext } from "@/types/hono.mjs"; + +import { SidebarOrganizations } from "./components/sidebar-organizations.js"; + +import { checkUserAuthed } from "./utils/middleware.mjs"; + +const app = new Hono(); + +app.get("/sidebar-organizations", checkUserAuthed, async (c) => { + const user = c.var.user!; + + // DO NOT USE THIS PARAMETER FOR AUTH + // THIS IS PURELY BEING USED FOR STYLING + const workspace = c.req.query("current_workspace") || ""; + + const relationships = await db.query.usersToTenants.findMany({ + where: (fields, { eq }) => eq(fields.userId, user.id), + with: { tenant: true }, + }); + + const tenants = relationships.map((r) => r.tenant); + + return c.html(); +}); + +export default app; diff --git a/src/routers/app/index.tsx b/src/routers/app/index.tsx index f8bd65b..25af8ef 100644 --- a/src/routers/app/index.tsx +++ b/src/routers/app/index.tsx @@ -12,12 +12,16 @@ import { ServiceEditPage } from "./pages/app.$workspace.$serviceId.edit.js"; import { checkTenantMembership, checkUserAuthed, checkServiceTenantMembership } from "./utils/middleware.mjs"; +import hxRouter from "./hx-router.js"; + import type { ServerContext } from "@/types/hono.mjs"; const app = new Hono(); app.use("*", sessionMiddleware); +app.route("/hx", hxRouter); + app.get("/", checkUserAuthed, async (c) => { const user = c.var.user!; const view_all = c.req.query("view_all") || "false"; @@ -34,7 +38,7 @@ app.get("/", checkUserAuthed, async (c) => { return c.redirect(`/app/${tenant.workspace}`); } - return c.html(); + return c.html(); }); app.get("/login", async (c) => { @@ -48,50 +52,31 @@ app.get("/login", async (c) => { }); app.get("/:workspace", checkUserAuthed, checkTenantMembership, async (c) => { + const workspace = c.req.param("workspace"); const tenant = c.var.tenant!; const user = c.var.user!; - const relationships = await db.query.usersToTenants.findMany({ - where: (fields, { eq }) => eq(fields.userId, user.id), - with: { tenant: true }, - }); - - const tenants = relationships.map((r) => r.tenant); - const services = await db.query.services.findMany({ where: (fields, { eq }) => eq(fields.tenantId, tenant.id), }); - return c.html(); + return c.html(); }); app.get("/:workspace/edit", checkUserAuthed, checkTenantMembership, async (c) => { + const workspace = c.req.param("workspace"); const tenant = c.var.tenant!; const user = c.var.user!; - const relationships = await db.query.usersToTenants.findMany({ - where: (fields, { eq }) => eq(fields.userId, user.id), - with: { tenant: true }, - }); - - const tenants = relationships.map((r) => r.tenant); - - return c.html(); + return c.html(); }); app.get("/:workspace/:service_id", checkUserAuthed, checkTenantMembership, checkServiceTenantMembership, async (c) => { - const tenant = c.var.tenant!; + const workspace = c.req.param("workspace"); const service = c.var.service!; const user = c.var.user!; - const relationships = await db.query.usersToTenants.findMany({ - where: (fields, { eq }) => eq(fields.userId, user.id), - with: { tenant: true }, - }); - - const tenants = relationships.map((r) => r.tenant); - - return c.html(); + return c.html(); }); app.get( @@ -100,18 +85,11 @@ app.get( checkTenantMembership, checkServiceTenantMembership, async (c) => { - const tenant = c.var.tenant!; + const workspace = c.req.param("workspace"); const service = c.var.service!; const user = c.var.user!; - const relationships = await db.query.usersToTenants.findMany({ - where: (fields, { eq }) => eq(fields.userId, user.id), - with: { tenant: true }, - }); - - const tenants = relationships.map((r) => r.tenant); - - return c.html(); + return c.html(); }, ); diff --git a/src/routers/app/layouts/app-container.tsx b/src/routers/app/layouts/app-container.tsx index 3aae1a8..c516ee6 100644 --- a/src/routers/app/layouts/app-container.tsx +++ b/src/routers/app/layouts/app-container.tsx @@ -1,52 +1,38 @@ import type { FC, PropsWithChildren } from "hono/jsx"; import { User } from "lucia"; -import type { TenantRecord } from "@/types/db.mjs"; - export interface AppContainerProps { user: User; - tenant: TenantRecord | null; - tenants: Array; + workspace: string; mainClass?: string; } -export const AppContainer: FC> = ({ - user, - tenant, - tenants, - children, - mainClass, -}) => { +export const AppContainer: FC> = (props) => { + const { user, children, mainClass } = props; + return ( -
+
-
{children}
+
{children}
); }; + +const SidebarFetcher: FC> = ({ user, workspace }) => { + return ( +
0 ? `?current_workspace=${workspace}` : ""].join("")} + >
+ ); +}; diff --git a/src/routers/app/layouts/root-document.tsx b/src/routers/app/layouts/root-document.tsx index 5cbc628..f471141 100644 --- a/src/routers/app/layouts/root-document.tsx +++ b/src/routers/app/layouts/root-document.tsx @@ -7,6 +7,11 @@ export const RootDocument: FC> = ({ title, {title} + {children} diff --git a/src/routers/app/pages/app.$workspace.$serviceId.edit.tsx b/src/routers/app/pages/app.$workspace.$serviceId.edit.tsx index 488b723..3073d34 100644 --- a/src/routers/app/pages/app.$workspace.$serviceId.edit.tsx +++ b/src/routers/app/pages/app.$workspace.$serviceId.edit.tsx @@ -7,19 +7,14 @@ import { AppContainer, type AppContainerProps } from "../layouts/app-container.j import type { ServiceRecord } from "@/types/db.mjs"; -export const ServiceEditPage: FC<{ service: ServiceRecord } & AppContainerProps> = ({ - user, - tenant, - tenants, - service, -}) => { +export const ServiceEditPage: FC<{ service: ServiceRecord } & AppContainerProps> = ({ user, workspace, service }) => { return ( - - + +
diff --git a/src/routers/app/pages/app.$workspace.$serviceId.index.tsx b/src/routers/app/pages/app.$workspace.$serviceId.index.tsx index 7349dc5..418674e 100644 --- a/src/routers/app/pages/app.$workspace.$serviceId.index.tsx +++ b/src/routers/app/pages/app.$workspace.$serviceId.index.tsx @@ -11,17 +11,17 @@ export const ServiceLandingPage: FC< { service: ServiceRecord; } & AppContainerProps -> = ({ user, tenant, tenants, service }) => { +> = ({ user, workspace, service }) => { return ( - - + +
diff --git a/src/routers/app/pages/app.$workspace.edit.tsx b/src/routers/app/pages/app.$workspace.edit.tsx index 83164cd..8c19e66 100644 --- a/src/routers/app/pages/app.$workspace.edit.tsx +++ b/src/routers/app/pages/app.$workspace.edit.tsx @@ -7,14 +7,14 @@ import { AppContainer, type AppContainerProps } from "../layouts/app-container.j import type { TenantRecord } from "@/types/db.mjs"; -export const WorkspaceEditPage: FC<{ tenant: TenantRecord } & AppContainerProps> = ({ user, tenants, tenant }) => { +export const WorkspaceEditPage: FC<{ tenant: TenantRecord } & AppContainerProps> = ({ user, workspace, tenant }) => { return ( - - + +
diff --git a/src/routers/app/pages/app.$workspace.index.tsx b/src/routers/app/pages/app.$workspace.index.tsx index 7f7640d..a946387 100644 --- a/src/routers/app/pages/app.$workspace.index.tsx +++ b/src/routers/app/pages/app.$workspace.index.tsx @@ -13,14 +13,14 @@ export const WorkspaceLandingPage: FC< tenant: TenantRecord; services: Array; } & AppContainerProps -> = ({ user, tenants, tenant, services }) => { +> = ({ user, workspace, tenant, services }) => { return ( - - + +
diff --git a/src/routers/app/pages/app.index.tsx b/src/routers/app/pages/app.index.tsx index 65fac50..045269f 100644 --- a/src/routers/app/pages/app.index.tsx +++ b/src/routers/app/pages/app.index.tsx @@ -4,10 +4,10 @@ import { RootDocument } from "../layouts/root-document.js"; import { AppContainer, type AppContainerProps } from "../layouts/app-container.js"; import { Button } from "../components/button.js"; -export const NoOrganizationPage: FC<{} & Omit> = ({ user, tenants }) => { +export const NoOrganizationPage: FC<{} & Omit> = ({ user, workspace }) => { return ( - +

You are not a part of any active organizations.

Create your own organization!.

diff --git a/src/server.mts b/src/server.mts index 3a69cd5..3288cb9 100644 --- a/src/server.mts +++ b/src/server.mts @@ -28,7 +28,7 @@ app.use(secureHeaders()); app.use(trimTrailingSlash()); const limiter = rateLimiter({ - windowMs: 15 * 60 * 1000, // 15 minutes + windowMs: 8 * 60 * 1000, // 8 minutes limit: 100, standardHeaders: "draft-6", keyGenerator: (c) => c.req.header("CF-Connecting-IP") ?? c.req.header("x-forwarded-for") ?? "",