Skip to content

Commit c3c72d1

Browse files
committed
feat!: add admin ui project and move to a monorepo
1 parent edf5448 commit c3c72d1

40 files changed

+8830
-859
lines changed

apps/admin-ui/.eslintrc.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/** @type {import('eslint').Linter.Config} */
2+
module.exports = {
3+
extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node"],
4+
};

apps/admin-ui/.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/.cache
2+
/build
3+
/dist
4+
/public/build
5+
/.mf
6+
.env
7+
8+
/app/tailwind.css

apps/admin-ui/README.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Welcome to Remix!
2+
3+
- [Remix Docs](https://remix.run/docs)
4+
5+
## Development
6+
7+
You will be running two processes during development:
8+
9+
- The Miniflare server (miniflare is a local environment for Cloudflare Workers)
10+
- The Remix development server
11+
12+
Both are started with one command:
13+
14+
```sh
15+
npm run dev
16+
```
17+
18+
Open up [http://127.0.0.1:8787](http://127.0.0.1:8787) and you should be ready to go!
19+
20+
If you want to check the production build, you can stop the dev server and run following commands:
21+
22+
```sh
23+
npm run build
24+
npm start
25+
```
26+
27+
Then refresh the same URL in your browser (no live reload for production builds).
28+
29+
## Deployment
30+
31+
If you don't already have an account, then [create a cloudflare account here](https://dash.cloudflare.com/sign-up) and after verifying your email address with Cloudflare, go to your dashboard and set up your free custom Cloudflare Workers subdomain.
32+
33+
Once that's done, you should be able to deploy your app:
34+
35+
```sh
36+
npm run deploy
37+
```
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { SVGAttributes } from "react";
2+
3+
export function TrashIcon(svgAttributes: SVGAttributes<SVGElement>) {
4+
return (
5+
<svg
6+
fill="none"
7+
stroke="currentColor"
8+
strokeWidth={1.5}
9+
viewBox="0 0 24 24"
10+
xmlns="http://www.w3.org/2000/svg"
11+
aria-hidden="true"
12+
{...svgAttributes}
13+
>
14+
<path
15+
strokeLinecap="round"
16+
strokeLinejoin="round"
17+
d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
18+
/>
19+
</svg>
20+
);
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export default function Heading({ title }: { title: string }) {
2+
return (
3+
<>
4+
<header className="bg-white shadow">
5+
<div className="mx-auto max-w-7xl py-6 px-4 sm:px-6 lg:px-8">
6+
<h1 className="text-3xl font-bold tracking-tight text-gray-900">
7+
{title}
8+
</h1>
9+
</div>
10+
</header>
11+
</>
12+
);
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export default function Main({ children }: { children: React.ReactNode }) {
2+
return (
3+
<>
4+
<main>
5+
<div className="mx-auto max-w-7xl py-6 sm:px-6 lg:px-8">
6+
{/* Replace with your content */}
7+
{children}
8+
{/* /End replace */}
9+
</div>
10+
</main>
11+
</>
12+
);
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { Link } from "@remix-run/react";
2+
3+
export default function Nav() {
4+
return (
5+
<>
6+
<nav className="bg-gray-800">
7+
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
8+
<div className="flex h-16 items-center justify-between">
9+
<div className="flex items-center">
10+
<div className="flex-shrink-0">
11+
<span className="text-gray-200 text-3xl">Flargd</span>
12+
</div>
13+
<div className="hidden md:block">
14+
<div className="ml-10 flex items-baseline space-x-4">
15+
{/* Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" */}
16+
<Link
17+
to="/"
18+
className="bg-gray-900 text-white px-3 py-2 rounded-md text-sm font-medium"
19+
aria-current="page"
20+
>
21+
Home
22+
</Link>
23+
24+
{/* <a
25+
href="/"
26+
className="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium"
27+
>
28+
Reports
29+
</a> */}
30+
</div>
31+
</div>
32+
</div>
33+
</div>
34+
</div>
35+
</nav>
36+
37+
{/* <header className="bg-white shadow">
38+
<div className="mx-auto max-w-7xl py-6 px-4 sm:px-6 lg:px-8">
39+
<h1 className="text-3xl font-bold tracking-tight text-gray-900">
40+
Dashboard
41+
</h1>
42+
</div>
43+
</header>
44+
<main>
45+
<div className="mx-auto max-w-7xl py-6 sm:px-6 lg:px-8">
46+
<div className="px-4 py-6 sm:px-0">
47+
48+
<div className="h-96 rounded-lg border-4 border-dashed border-gray-200"></div>
49+
</div>
50+
</div>
51+
</main> */}
52+
</>
53+
);
54+
}

apps/admin-ui/app/constant.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
export const CF_GEOGRAPHIC_PROPERTIES = {
2+
city: "City",
3+
country: "Country",
4+
continent: "Continent",
5+
postalCode: "Postal Code",
6+
region: "Region",
7+
ip: "IP",
8+
} as const;
9+
10+
export const CONDITIONS = {
11+
is: "Is",
12+
is_not: "Is not",
13+
contains: "Contains",
14+
does_not_contain: "Doesn't contain",
15+
in_list: "Is in list",
16+
not_in_list: "Is not in list",
17+
} as const;
18+
19+
export type ContionalAttribute = keyof typeof CF_GEOGRAPHIC_PROPERTIES;
20+
export type ConditionKeys = keyof typeof CONDITIONS;
21+
export type Condition = {
22+
attribute: ContionalAttribute;
23+
condition: ConditionKeys;
24+
target: string | string[];
25+
};

apps/admin-ui/app/entry.client.tsx

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { RemixBrowser } from "@remix-run/react";
2+
import { startTransition, StrictMode } from "react";
3+
import { hydrateRoot } from "react-dom/client";
4+
5+
function hydrate() {
6+
startTransition(() => {
7+
hydrateRoot(
8+
document,
9+
<StrictMode>
10+
<RemixBrowser />
11+
</StrictMode>
12+
);
13+
});
14+
}
15+
16+
if (typeof requestIdleCallback === "function") {
17+
requestIdleCallback(hydrate);
18+
} else {
19+
// Safari doesn't support requestIdleCallback
20+
// https://caniuse.com/requestidlecallback
21+
setTimeout(hydrate, 1);
22+
}

apps/admin-ui/app/entry.server.tsx

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { EntryContext } from "@remix-run/cloudflare";
2+
import { RemixServer } from "@remix-run/react";
3+
import { renderToString } from "react-dom/server";
4+
5+
export default function handleRequest(
6+
request: Request,
7+
responseStatusCode: number,
8+
responseHeaders: Headers,
9+
remixContext: EntryContext
10+
) {
11+
const markup = renderToString(
12+
<RemixServer context={remixContext} url={request.url} />
13+
);
14+
15+
responseHeaders.set("Content-Type", "text/html");
16+
17+
return new Response("<!DOCTYPE html>" + markup, {
18+
status: responseStatusCode,
19+
headers: responseHeaders,
20+
});
21+
}

apps/admin-ui/app/root.tsx

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type { LinksFunction, MetaFunction } from "@remix-run/cloudflare";
2+
import {
3+
Links,
4+
LiveReload,
5+
Meta,
6+
Outlet,
7+
Scripts,
8+
ScrollRestoration,
9+
} from "@remix-run/react";
10+
import Nav from "./components/layout/nav";
11+
import styles from "./tailwind.css";
12+
import tagifyStyles from "@yaireo/tagify/dist/tagify.css";
13+
14+
export const links: LinksFunction = () => [
15+
{ rel: "stylesheet", href: tagifyStyles }, // This could have just been in the edit/create pages but because I don't want to override all the styles and still have the tagify input looking like other inputs, I have to add it in the root
16+
{ rel: "stylesheet", href: styles },
17+
];
18+
19+
export const meta: MetaFunction = () => ({
20+
charset: "utf-8",
21+
title: "Flargd - Feature Management",
22+
viewport: "width=device-width,initial-scale=1",
23+
});
24+
25+
export default function App() {
26+
return (
27+
<html lang="en" className="h-full bg-gray-100">
28+
<head>
29+
<Meta />
30+
<Links />
31+
</head>
32+
<body className="h-full">
33+
<div className="min-h-full">
34+
<Nav />
35+
<Outlet />
36+
</div>
37+
<ScrollRestoration />
38+
<Scripts />
39+
<LiveReload />
40+
</body>
41+
</html>
42+
);
43+
}

0 commit comments

Comments
 (0)