Skip to content

Commit

Permalink
Mock out settings UI + random fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
oscartbeaumont committed Jan 31, 2024
1 parent 526c61d commit 9f6275a
Show file tree
Hide file tree
Showing 9 changed files with 291 additions and 66 deletions.
40 changes: 39 additions & 1 deletion api/src/routers/tenant.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import { z } from "zod";
import { authedProcedure, createTRPCRouter, tenantProcedure } from "../trpc";
import { encodeId } from "../utils";
import { db, devices, policies, tenantAccounts, tenants, users } from "../db";
import {
accounts,
db,
devices,
policies,
tenantAccounts,
tenants,
users,
} from "../db";
import { eq } from "drizzle-orm";

export const tenantRouter = createTRPCRouter({
Expand Down Expand Up @@ -45,4 +53,34 @@ export const tenantRouter = createTRPCRouter({

// TODO: Schedule all devices for unenrolment
}),

administrators: tenantProcedure.query(async ({ ctx }) => {
const [ownerId, rows] = await Promise.allSettled([
db
.select({
ownerId: tenants.owner_id,
})
.from(tenants)
.where(eq(tenants.id, ctx.tenantId))
.then((v) => v?.[0]?.ownerId),
db
.select({
id: accounts.id,
name: accounts.name,
email: accounts.email,
})
.from(accounts)
.leftJoin(tenantAccounts, eq(tenantAccounts.accountId, accounts.id))
.where(eq(tenantAccounts.tenantId, ctx.tenantId)),
]);
// This is required. If the owner is not found, we gracefully continue.
if (rows.status === "rejected") throw rows.reason;

return rows.value.map((row) => ({
...row,
isOwner:
ownerId.status === "fulfilled" ? row.id === ownerId.value : false,
id: encodeId("account", row.id),
}));
}),
});
Empty file removed forge/pnpm-lock.yaml
Empty file.
3 changes: 3 additions & 0 deletions forge/src/components/LeftSidebar/CreateTenantDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export function CreateTenantDialog(props: {
await props.refetchSession();
props.setActiveTenant(data.id);
dialog.setOpen(false);

// Ensure the form stays disabled until the dialog is closed
await new Promise((resolve) => setTimeout(resolve, 1000));
},
}));

Expand Down
37 changes: 18 additions & 19 deletions forge/src/components/LeftSidebar/TenantSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { For } from "solid-js";
import { For, Suspense } from "solid-js";
import { Tenant } from "~/lib/globalCtx";
import {
DropdownMenu,
Expand Down Expand Up @@ -35,10 +35,7 @@ export function TenantSwitcher(props: TenantSwitcherProps) {
{/* DialogRoot must be outside the dropdown menu so it's not unrendered when the menu is closed */}
<DialogRoot>
<DropdownMenu controller={controller}>
<DropdownMenuTrigger
class="mt-1 inline-flex w-full justify-between rounded-md bg-brand-secondary text-white px-4 py-2 text-sm font-medium shadow-sm hover:bg-brand-tertiary focus:outline-none focus:ring-2 focus:ring-white disabled:opacity-80 disabled:cursor-not-allowed"
disabled={tenantCount() === 0}
>
<DropdownMenuTrigger class="mt-1 inline-flex w-full justify-between rounded-md bg-brand-secondary text-white px-4 py-2 text-sm font-medium shadow-sm hover:bg-brand-tertiary focus:outline-none focus:ring-2 focus:ring-white disabled:opacity-80 disabled:cursor-not-allowed">
{/* // TODO: Tooltip when truncated */}
<span
class="truncate"
Expand All @@ -54,21 +51,23 @@ export function TenantSwitcher(props: TenantSwitcherProps) {
</KDropdownMenu.Icon>
</DropdownMenuTrigger>
<DropdownMenuContent>
<For each={props.tenants}>
{(tenant) => (
<DropdownMenuItem
class={
"block px-4 py-2 text-sm text-left w-full truncate hover:bg-gray-200"
}
onSelect={() => props.setActiveTenant(tenant.id)}
>
{/* TODO: Use a link here instead of JS for accessibility */}
{tenant.name}
</DropdownMenuItem>
)}
</For>
<Suspense fallback={<></>}>
<For each={props.tenants}>
{(tenant) => (
<DropdownMenuItem
class={
"block px-4 py-2 text-sm text-left w-full truncate hover:bg-gray-200"
}
onSelect={() => props.setActiveTenant(tenant.id)}
>
{/* TODO: Use a link here instead of JS for accessibility */}
{tenant.name}
</DropdownMenuItem>
)}
</For>

<DropdownMenuSeparator />
{props.tenants.length !== 0 && <DropdownMenuSeparator />}
</Suspense>

<DialogTrigger asChild>
<As
Expand Down
1 change: 1 addition & 0 deletions forge/src/components/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export * from "./textarea";
export * from "./tooltip";
export * from "./dropdown-menu";
export * from "./controller";
export * from "./label";
19 changes: 19 additions & 0 deletions forge/src/components/ui/label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Component, ComponentProps } from "solid-js";
import { splitProps } from "solid-js";

import { cn } from "~/lib/utils";

const Label: Component<ComponentProps<"label">> = (props) => {
const [, rest] = splitProps(props, ["class"]);
return (
<label
class={cn(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
props.class
)}
{...rest}
/>
);
};

export { Label };
9 changes: 9 additions & 0 deletions forge/src/routes/(dash).tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ export default function Layout(props: ParentProps) {
else path = ["", id];

navigate(path.join("/"));

// TODO: Why is this timeout required??? After tenant create it doesn't redirect without it.
setTimeout(
() =>
navigate(path.join("/"), {
replace: true,
}),
250
);
};

const refetchSession = async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export function DeleteTenantButton() {
return (
<DialogRoot>
<DialogTrigger asChild>
<As component={Button} variant="destructive">
Delete
<As component={Button} variant="destructive" class="w-full">
Delete Tenant
</As>
</DialogTrigger>

Expand Down
Loading

0 comments on commit 9f6275a

Please sign in to comment.