Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(ui): add htmx to simplify the loading requirements of the sidebar content #43

Merged
merged 2 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/routers/app/components/sidebar-organizations.tsx
Original file line number Diff line number Diff line change
@@ -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<TenantRecord>;
}> = ({ workspace, tenants }) => {
return tenants.length > 0 ? (
<ul>
{tenants.map((item) => (
<li>
<a
href={`/app/${item.workspace}`}
class='block py-1 data-[active-tenant="true"]:text-blue-500'
data-active-tenant={workspace.length ? item.workspace === workspace : false}
>
{item.name}
</a>
</li>
))}
</ul>
) : (
<p>😞 You have no organizations.</p>
);
};
29 changes: 29 additions & 0 deletions src/routers/app/hx-router.tsx
Original file line number Diff line number Diff line change
@@ -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<ServerContext>();

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(<SidebarOrganizations workspace={workspace} tenants={tenants} />);
});

export default app;
48 changes: 13 additions & 35 deletions src/routers/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<ServerContext>();

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";
Expand All @@ -34,7 +38,7 @@ app.get("/", checkUserAuthed, async (c) => {
return c.redirect(`/app/${tenant.workspace}`);
}

return c.html(<NoOrganizationPage user={user} tenants={tenants} />);
return c.html(<NoOrganizationPage user={user} workspace="" />);
});

app.get("/login", async (c) => {
Expand All @@ -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(<WorkspaceLandingPage user={user} tenants={tenants} tenant={tenant} services={services} />);
return c.html(<WorkspaceLandingPage user={user} workspace={workspace} tenant={tenant} services={services} />);
});

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(<WorkspaceEditPage user={user} tenants={tenants} tenant={tenant} />);
return c.html(<WorkspaceEditPage user={user} workspace={workspace} tenant={tenant} />);
});

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(<ServiceLandingPage user={user} tenant={tenant} tenants={tenants} service={service} />);
return c.html(<ServiceLandingPage user={user} workspace={workspace} service={service} />);
});

app.get(
Expand All @@ -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(<ServiceEditPage user={user} tenant={tenant} tenants={tenants} service={service} />);
return c.html(<ServiceEditPage user={user} workspace={workspace} service={service} />);
},
);

Expand Down
46 changes: 16 additions & 30 deletions src/routers/app/layouts/app-container.tsx
Original file line number Diff line number Diff line change
@@ -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<TenantRecord>;
workspace: string;
mainClass?: string;
}

export const AppContainer: FC<PropsWithChildren<AppContainerProps>> = ({
user,
tenant,
tenants,
children,
mainClass,
}) => {
export const AppContainer: FC<PropsWithChildren<AppContainerProps>> = (props) => {
const { user, children, mainClass } = props;

return (
<div className="flex flex-col md:grid md:grid-cols-4 lg:grid-cols-5 min-h-full">
<div class="flex flex-col md:grid md:grid-cols-4 lg:grid-cols-5 min-h-full">
<aside class="h-[250px] border-b flex flex-col bg-gray-100 dark:bg-gray-800 md:h-auto md:col-span-1 md:border-r md:border-b-0">
<div class="flex-grow p-2">
<p class="pb-2">Hello {user.username}!</p>
<p class="pb-2 border-b">Your organizations</p>
{tenants.length > 0 ? (
<ul>
{tenants.map((item) => (
<li>
<a
href={`/app/${item.workspace}`}
class='block py-1 data-[active-tenant="true"]:text-blue-500'
data-active-tenant={tenant ? item.id === tenant.id : false}
>
{item.name}
</a>
</li>
))}
</ul>
) : (
<p>😞 You have no organizations.</p>
)}
<SidebarFetcher {...props} />
<span class="border-t" />
</div>
<div class="p-2">
<a href="/auth/logout">👋🏼 Logout</a>
</div>
</aside>
<main className={["md:col-span-3 lg:col-span-4", mainClass].filter(Boolean).join(" ")}>{children}</main>
<main class={["md:col-span-3 lg:col-span-4", mainClass].filter(Boolean).join(" ")}>{children}</main>
</div>
);
};

const SidebarFetcher: FC<Pick<AppContainerProps, "user" | "workspace">> = ({ user, workspace }) => {
return (
<div
hx-trigger="load"
hx-get={["/app/hx/sidebar-organizations", workspace.length > 0 ? `?current_workspace=${workspace}` : ""].join("")}
></div>
);
};
5 changes: 5 additions & 0 deletions src/routers/app/layouts/root-document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ export const RootDocument: FC<PropsWithChildren<{ title: string }>> = ({ title,
<meta charset="UTF-8"></meta>
<meta name="viewport" content="width=device-width, initial-scale=1.0"></meta>
<title>{title}</title>
<script
src="https://unpkg.com/[email protected]"
integrity="sha384-wS5l5IKJBvK6sPTKa2WZ1js3d947pvWXbPJ1OmWfEuxLgeHcEbjUUA5i9V5ZkpCw"
crossorigin="anonymous"
></script>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-50 text-gray-950 dark:bg-gray-950 dark:text-gray-50">{children}</body>
Expand Down
13 changes: 4 additions & 9 deletions src/routers/app/pages/app.$workspace.$serviceId.edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<RootDocument title={`${tenant?.name} edit`}>
<AppContainer user={user} tenant={tenant} tenants={tenants} mainClass="grid place-items-center p-2 md:p-4">
<RootDocument title={`${service.name} edit - ${workspace}`}>
<AppContainer user={user} workspace={workspace} mainClass="grid place-items-center p-2 md:p-4">
<Card class="max-w-2xl w-full">
<div class="p-4 grid gap-2">
<div class="flex gap-1">
<a class={getButtonStyles("secondary", "xs")} href={`/app/${tenant?.workspace}/${service.id}`}>
<a class={getButtonStyles("secondary", "xs")} href={`/app/${workspace}/${service.id}`}>
Back ⬅️
</a>
</div>
Expand Down
10 changes: 5 additions & 5 deletions src/routers/app/pages/app.$workspace.$serviceId.index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@ export const ServiceLandingPage: FC<
{
service: ServiceRecord;
} & AppContainerProps
> = ({ user, tenant, tenants, service }) => {
> = ({ user, workspace, service }) => {
return (
<RootDocument title="Simple Logging Server">
<AppContainer user={user} tenant={tenant} tenants={tenants} mainClass="grid place-items-center p-2 md:p-4">
<RootDocument title={`${service.name} - ${workspace}`}>
<AppContainer user={user} workspace={workspace} mainClass="grid place-items-center p-2 md:p-4">
<Card class="max-w-2xl w-full">
<div class="p-4 grid gap-2">
<div class="flex gap-1">
<a class={getButtonStyles("secondary", "xs")} href={`/app/${tenant?.workspace}`}>
<a class={getButtonStyles("secondary", "xs")} href={`/app/${workspace}`}>
⬅️ Back
</a>
<a class={getButtonStyles("secondary", "xs")} href={`/app/${tenant?.workspace}/${service.id}/edit`}>
<a class={getButtonStyles("secondary", "xs")} href={`/app/${workspace}/${service.id}/edit`}>
✏️ Edit service
</a>
</div>
Expand Down
8 changes: 4 additions & 4 deletions src/routers/app/pages/app.$workspace.edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<RootDocument title={`${tenant.name} edit`}>
<AppContainer user={user} tenants={tenants} tenant={tenant} mainClass="grid place-items-center p-2 md:p-4">
<RootDocument title={`${workspace} edit`}>
<AppContainer user={user} workspace={workspace} mainClass="grid place-items-center p-2 md:p-4">
<Card class="max-w-2xl w-full">
<div class="p-4 grid gap-2">
<div class="flex gap-1">
<a class={getButtonStyles("secondary", "xs")} href={`/app/${tenant.workspace}`}>
<a class={getButtonStyles("secondary", "xs")} href={`/app/${workspace}`}>
⬅️ Back
</a>
</div>
Expand Down
8 changes: 4 additions & 4 deletions src/routers/app/pages/app.$workspace.index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ export const WorkspaceLandingPage: FC<
tenant: TenantRecord;
services: Array<ServiceRecord>;
} & AppContainerProps
> = ({ user, tenants, tenant, services }) => {
> = ({ user, workspace, tenant, services }) => {
return (
<RootDocument title={`${tenant.name}`}>
<AppContainer user={user} tenant={tenant} tenants={tenants} mainClass="grid place-items-center p-2 md:p-4">
<RootDocument title={`${workspace}`}>
<AppContainer user={user} workspace={workspace} mainClass="grid place-items-center p-2 md:p-4">
<Card class="max-w-4xl w-full">
<div class="p-4 grid gap-2">
<div class="flex gap-1">
<a class={getButtonStyles("secondary", "xs")} href={`/app/${tenant.workspace}/edit`}>
<a class={getButtonStyles("secondary", "xs")} href={`/app/${workspace}/edit`}>
✏️ Edit organization
</a>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/routers/app/pages/app.index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<AppContainerProps, "tenant">> = ({ user, tenants }) => {
export const NoOrganizationPage: FC<{} & Omit<AppContainerProps, "tenant">> = ({ user, workspace }) => {
return (
<RootDocument title="Simple Logging Server">
<AppContainer user={user} tenants={tenants} tenant={null} mainClass="p-2 md:p-4">
<AppContainer user={user} workspace={workspace} mainClass="p-2 md:p-4">
<div class="grid gap-2">
<p>You are not a part of any active organizations.</p>
<p>Create your own organization!.</p>
Expand Down
2 changes: 1 addition & 1 deletion src/server.mts
Original file line number Diff line number Diff line change
Expand Up @@ -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") ?? "",
Expand Down