Skip to content

Add example admin page at /_fake/admin that shows thing resources #4

@seveibar

Description

@seveibar

/bounty $10

Use react for the admin page using ctx.react

Relevant middleware example:

import { renderToString } from "react-dom/server"
import { Middleware } from "winterspec"
import { ReactNode } from "react"
import { timeAgo } from "lib/admin/time-ago"

export const withCtxReact: Middleware<
  {},
  { react: (component: ReactNode) => Response }
> = async (req, ctx, next) => {
  ctx.react = (component: ReactNode) => {
    const pathComponents = new URL(req.url).pathname.split("/").filter(Boolean)
    const timezone = req.headers.get("X-Timezone") || "UTC"
    return new Response(
      renderToString(
        <html lang="en">
          <script src="https://cdn.tailwindcss.com" />
          <style
            type="text/tailwindcss"
            // biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation>
            dangerouslySetInnerHTML={{
              __html: `
.btn {
  @apply text-white visited:text-white m-1 bg-blue-500 hover:bg-blue-700 font-bold py-2 px-4 rounded
}
a {
   @apply underline text-blue-600 hover:text-blue-800 visited:text-purple-800 m-1
}
h2 {
  @apply text-xl font-bold my-2
}
input, select {
  @apply border border-gray-300 rounded p-1 ml-0.5
}
form {
  @apply inline-flex flex-col gap-2 border border-gray-300 rounded p-2 m-2 text-xs
}
button {
  @apply bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded
}
`,
            }}
          />
          <body>
            <div>
              <div className="border-b border-gray-300 py-1 flex justify-between items-center">
                <div>
                  <span className="px-1 pr-2">admin panel</span>
                  {pathComponents.map((component, index) => {
                    return (
                      <span key={index}>
                        <span className="px-0.5 text-gray-500">/</span>
                        <a
                          href={`/${pathComponents.slice(0, index + 1).join("/")}`}
                        >
                          {component}
                        </a>
                      </span>
                    )
                  })}
                </div>
                <div className="mr-2 flex items-center">
                  <div className="text-xs text-gray-500 mr-1">
                    {timeAgo(new Date(), timezone)}
                  </div>
                  <div
                    // biome-ignore lint/security/noDangerouslySetInnerHtml: <explanation>
                    dangerouslySetInnerHTML={{
                      __html: `
                  <select
                    id="timezone-select"
                    class="text-xs"
                    value="${timezone}"
                    onchange="document.cookie = 'timezone=' + this.value + ';path=/'; location.reload();"
                  >
                    <option ${timezone === "UTC" ? "selected" : ""} value="UTC">UTC</option>
                    <option ${timezone === "America/Los_Angeles" ? "selected" : ""} value="America/Los_Angeles">Pacific</option>
                    <option ${timezone === "Asia/Kolkata" ? "selected" : ""} value="Asia/Kolkata">IST</option>
                  </select>
                  `,
                    }}
                  />
                </div>
              </div>
              <div className="flex flex-col text-xs p-1">{component}</div>
            </div>
          </body>
        </html>,
      ),
      {
        headers: {
          "Content-Type": "text/html",
        },
      },
    )
  }

  return await next(req, ctx)
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions