Skip to content

Commit 675a969

Browse files
added remix admin template
1 parent 559155a commit 675a969

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+14355
-0
lines changed

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
SUPABASE_URL=
2+
SUPABASE_ANON_KEY=

.eslintrc.cjs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* This is intended to be a basic starting point for linting in your app.
3+
* It relies on recommended configs out of the box for simplicity, but you can
4+
* and should modify this configuration to best suit your team's needs.
5+
*/
6+
7+
/** @type {import('eslint').Linter.Config} */
8+
module.exports = {
9+
root: true,
10+
parserOptions: {
11+
ecmaVersion: "latest",
12+
sourceType: "module",
13+
ecmaFeatures: {
14+
jsx: true,
15+
},
16+
},
17+
env: {
18+
browser: true,
19+
commonjs: true,
20+
es6: true,
21+
},
22+
ignorePatterns: ["!**/.server", "!**/.client"],
23+
24+
// Base config
25+
extends: ["eslint:recommended"],
26+
27+
overrides: [
28+
// React
29+
{
30+
files: ["**/*.{js,jsx,ts,tsx}"],
31+
plugins: ["react", "jsx-a11y"],
32+
extends: [
33+
"plugin:react/recommended",
34+
"plugin:react/jsx-runtime",
35+
"plugin:react-hooks/recommended",
36+
"plugin:jsx-a11y/recommended",
37+
],
38+
settings: {
39+
react: {
40+
version: "detect",
41+
},
42+
formComponents: ["Form"],
43+
linkComponents: [
44+
{ name: "Link", linkAttribute: "to" },
45+
{ name: "NavLink", linkAttribute: "to" },
46+
],
47+
"import/resolver": {
48+
typescript: {},
49+
},
50+
},
51+
},
52+
53+
// Typescript
54+
{
55+
files: ["**/*.{ts,tsx}"],
56+
plugins: ["@typescript-eslint", "import"],
57+
parser: "@typescript-eslint/parser",
58+
settings: {
59+
"import/internal-regex": "^~/",
60+
"import/resolver": {
61+
node: {
62+
extensions: [".ts", ".tsx"],
63+
},
64+
typescript: {
65+
alwaysTryTypes: true,
66+
},
67+
},
68+
},
69+
extends: [
70+
"plugin:@typescript-eslint/recommended",
71+
"plugin:import/recommended",
72+
"plugin:import/typescript",
73+
],
74+
},
75+
76+
// Node
77+
{
78+
files: [".eslintrc.cjs"],
79+
env: {
80+
node: true,
81+
},
82+
},
83+
],
84+
};

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
node_modules
2+
3+
/.cache
4+
/build
5+
.env
6+
7+
.DS_Store

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Welcome to Remix Admin Template!
2+
3+
- 📖 [Remix docs](https://remix.run/docs)
4+
5+
## Development
6+
7+
Run the dev server:
8+
9+
```shellscript
10+
npm run dev
11+
```
12+
13+
## Deployment
14+
15+
First, build your app for production:
16+
17+
```sh
18+
npm run build
19+
```
20+
21+
Then run the app in production mode:
22+
23+
```sh
24+
npm start
25+
```
26+
27+
Now you'll need to pick a host to deploy it to.
28+
29+
### DIY
30+
31+
If you're familiar with deploying Node applications, the built-in Remix app server is production-ready.
32+
33+
Make sure to deploy the output of `npm run build`
34+
35+
- `build/server`
36+
- `build/client`
37+
38+
## Styling
39+
40+
This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever css framework you prefer. See the [Vite docs on css](https://vitejs.dev/guide/features.html#css) for more information.

app/components/Button.tsx

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { Link } from "@remix-run/react";
2+
import type { ButtonHTMLAttributes, ReactNode } from "react";
3+
import React from "react";
4+
5+
import LoadingSpinner from "./icons/LoadingSpinner";
6+
7+
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
8+
className?: string;
9+
variant?: "contained" | "outlined";
10+
size?: "small" | "medium" | "large";
11+
type?: "submit" | "reset" | "button";
12+
loading?: boolean;
13+
to?: string;
14+
target?: string;
15+
disabled?: boolean;
16+
onClick?: VoidFunction;
17+
children?: ReactNode;
18+
}
19+
20+
const baseStyles =
21+
"group inline-flex items-center justify-center gap-1 text-sm/5 tracking-wide rounded-md transition focus:outline-none";
22+
23+
const variantStyles = {
24+
contained: "bg-cyan-500 text-white hover:bg-cyan-500/90",
25+
outlined: "bg-transparent text-cyan-600 ring ring-cyan-300 hover:bg-cyan-50",
26+
};
27+
28+
const sizeStyles = {
29+
small: "py-2 px-3",
30+
medium: "py-2 px-4",
31+
large: "py-3 px-6",
32+
};
33+
34+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
35+
(
36+
{
37+
variant = "contained",
38+
size = "medium",
39+
children,
40+
className,
41+
loading = false,
42+
disabled = false,
43+
type,
44+
target,
45+
to,
46+
onClick,
47+
...rest
48+
},
49+
ref
50+
) => {
51+
return (
52+
<>
53+
{to ? (
54+
<Link
55+
to={to}
56+
className={[
57+
baseStyles,
58+
sizeStyles[size],
59+
variantStyles[variant],
60+
className,
61+
]
62+
.filter(Boolean)
63+
.join(" ")}
64+
target={target}
65+
>
66+
{children}
67+
</Link>
68+
) : (
69+
<button
70+
ref={ref}
71+
disabled={disabled}
72+
onClick={onClick}
73+
className={[
74+
baseStyles,
75+
sizeStyles[size],
76+
disabled || loading
77+
? "opacity-50 bg-slate-700 text-white hover:bg-slate-700 hover:text-white"
78+
: variantStyles[variant],
79+
"cursor-pointer relative",
80+
className,
81+
]
82+
.filter(Boolean)
83+
.join(" ")}
84+
{...rest}
85+
>
86+
{loading && (
87+
<span className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2">
88+
<LoadingSpinner />
89+
</span>
90+
)}
91+
<span className={loading ? "invisible" : undefined}>
92+
{children}
93+
</span>
94+
</button>
95+
)}
96+
</>
97+
);
98+
}
99+
);
100+
101+
export default Button;

app/components/CodeBlock.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { useEffect, useRef } from "react";
2+
import { createHighlighter } from "shiki";
3+
4+
type CodeBlockProps = {
5+
code: string;
6+
language: "sql" | "csv";
7+
};
8+
9+
const CodeBlock = ({ code, language }: CodeBlockProps) => {
10+
const codeRef = useRef<HTMLPreElement>(null);
11+
12+
useEffect(() => {
13+
const highlightCode = async () => {
14+
const highlighter = await createHighlighter({
15+
themes: ["github-light"],
16+
langs: [language],
17+
});
18+
19+
if (codeRef.current) {
20+
const html = highlighter.codeToHtml(code, {
21+
lang: language,
22+
themes: { light: "github-light" },
23+
});
24+
const innerContent = html
25+
.replace(/<pre[^>]*>/, "")
26+
.replace(/<\/pre>$/, "");
27+
codeRef.current.innerHTML = innerContent;
28+
}
29+
};
30+
31+
highlightCode();
32+
}, [code, language]);
33+
34+
return (
35+
<pre
36+
ref={codeRef}
37+
className="p-4 overflow-auto text-sm border rounded-md border-slate-200 not-prose"
38+
/>
39+
);
40+
};
41+
42+
export default CodeBlock;

0 commit comments

Comments
 (0)