Skip to content

Commit

Permalink
initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
deiucanta committed May 9, 2022
1 parent 0dad152 commit 7eb8380
Show file tree
Hide file tree
Showing 16 changed files with 1,846 additions and 251 deletions.
109 changes: 89 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,103 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
# Easypanel Templates

## Getting Started
In this repository, you will find the templates available in Easypanel.

First, run the development server:
Here is how you define a template.

```bash
npm run dev
# or
yarn dev
```
```ts
import type { AppService } from "~templates-utils";
import { createTemplate } from "~templates-utils";

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
export default createTemplate({
name: "Adminer",
schema: {
type: "object",
required: ["projectName", "serviceName", "domain"],
properties: {
projectName: {
type: "string",
title: "Project Name",
},
serviceName: {
type: "string",
title: "Service Name",
default: "adminer",
},
domain: {
type: "string",
title: "Domain",
},
},
} as const,
generate({ projectName, serviceName, domain }) {
const appService: AppService = {
projectName,
serviceName,
source: {
type: "image",
image: "adminer",
},
proxy: {
port: 8080,
secure: true,
},
domains: [{ name: domain }],
};

You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
return {
services: [{ type: "app", data: appService }],
};
},
});
```

[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
## Template Properties

The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
- `name`
- `schema` is a JSON Schema. This is used to generate the form and validate the input. Important: do not remove the `as const` at the end of your schema in order to keep the type inference working.
- `generate` is the core of the template. It receives the form data and returns the template schema.
- `validate` (optional) is used for custom validation rules
- `transformErrors` (optional) is used for custom error messages

## Learn More
## Defining Templates

To learn more about Next.js, take a look at the following resources:
1. Duplicate any template from the `/templates` directory
2. Re-export the newly created template from `/templates/_list.ts`
3. Run `yarn dev` to open the testing playground
4. Customize your template.
5. Test your template. Inside an Easypanel instance, you can create a template from JSON. Use that feature to test the output of your template.
6. Send a PR.

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
## Type Safety

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
Templates are written in Typescript. We try to infer as much as possible from the `schema`. The `createTemplate` function doesn't do anything at the runtime. It is only used to give you better type inference.

## Deploy on Vercel
## Custom Validation Rules

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
```ts
{
// ...
validate(formData, errors) {
if (formData.pass1 !== formData.pass2) {
errors.pass2.addError("Passwords don't match");
}
return errors;
}
}
```

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
## Custom Error Messages

```ts
{
// ...
transformErrors(errors) {
return errors.map((error) => {
if (error.name === "pattern") {
error.message = "Only digits are allowed";
}
return error;
});
}
}
```
11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,18 @@
"lint": "next lint"
},
"dependencies": {
"@chakra-ui/icons": "^1.1.7",
"@chakra-ui/react": "^1.8.8",
"@emotion/react": "^11",
"@emotion/styled": "^11",
"@rjsf/chakra-ui": "^4.1.1",
"@rjsf/core": "^4.1.1",
"framer-motion": "^6",
"json-schema-to-ts": "^2.3.0",
"next": "12.1.6",
"react": "18.1.0",
"react-dom": "18.1.0"
"react-dom": "18.1.0",
"zod": "^3.14.4"
},
"devDependencies": {
"@types/node": "17.0.31",
Expand Down
38 changes: 34 additions & 4 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import { ChakraProvider, extendTheme } from "@chakra-ui/react";
import type { AppProps } from "next/app";
import Head from "next/head";

const theme = extendTheme({
colors: {
brand: {
50: "#ecfdf5",
100: "#d1fae5",
200: "#a7f3d0",
300: "#6ee7b7",
400: "#34d399",
500: "#10b981",
600: "#059669",
700: "#047857",
800: "#065f46",
900: "#064e3b",
},
},
});

function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
return (
<ChakraProvider theme={theme}>
<Head>
<title>Easypanel Templates Playground</title>
<meta
name="description"
content="Testing playground for Easypanel Templates."
/>
<link rel="icon" href="/favicon.ico" />
</Head>
<Component {...pageProps} />
</ChakraProvider>
);
}

export default MyApp
export default MyApp;
13 changes: 0 additions & 13 deletions pages/api/hello.ts

This file was deleted.

166 changes: 101 additions & 65 deletions pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,72 +1,108 @@
import type { NextPage } from 'next'
import Head from 'next/head'
import Image from 'next/image'
import styles from '../styles/Home.module.css'
import {
Box,
Button,
Flex,
Select,
Stack,
Text,
useClipboard,
} from "@chakra-ui/react";
import Form from "@rjsf/chakra-ui";
import type { NextPage } from "next";
import { useEffect, useState } from "react";
import * as templates from "../templates/_list";

const Home: NextPage = () => {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>

<main className={styles.main}>
<h1 className={styles.title}>
Welcome to <a href="https://nextjs.org">Next.js!</a>
</h1>

<p className={styles.description}>
Get started by editing{' '}
<code className={styles.code}>pages/index.tsx</code>
</p>
const [selected, setSelected] = useState<keyof typeof templates | "">("");
const [formData, setFormData] = useState<any>();
const [output, setOutput] = useState<string>("");
const { hasCopied, onCopy } = useClipboard(output);
const template = selected ? templates[selected] : null;

<div className={styles.grid}>
<a href="https://nextjs.org/docs" className={styles.card}>
<h2>Documentation &rarr;</h2>
<p>Find in-depth information about Next.js features and API.</p>
</a>
useEffect(() => {
setOutput("");
}, [selected]);

<a href="https://nextjs.org/learn" className={styles.card}>
<h2>Learn &rarr;</h2>
<p>Learn about Next.js in an interactive course with quizzes!</p>
</a>

<a
href="https://github.com/vercel/next.js/tree/canary/examples"
className={styles.card}
>
<h2>Examples &rarr;</h2>
<p>Discover and deploy boilerplate example Next.js projects.</p>
</a>

<a
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className={styles.card}
return (
<Box
display={{ lg: "grid" }}
gridTemplateRows="auto 1fr"
gridTemplateColumns="400px 1fr"
height={{ lg: "100vh" }}
bg="gray.800"
>
<Box
shadow="md"
zIndex="100"
gridRow="1 / 2"
gridColumn="1 / 3"
px="4"
py="2"
bg="white"
>
<Flex direction="row" alignItems="center">
<Text fontWeight="bold" fontSize="lg" mr="6">
Templates
</Text>
<Select
width="64"
value={selected}
onChange={(e) => {
setSelected(e.target.value as any);
}}
>
<h2>Deploy &rarr;</h2>
<p>
Instantly deploy your Next.js site to a public URL with Vercel.
</p>
</a>
</div>
</main>

<footer className={styles.footer}>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
<option value="">Select Template</option>
{Object.entries(templates).map(([key, value]) => (
<option key={key} value={key}>
{value.name}
</option>
))}
</Select>
</Flex>
</Box>
{template && (
<Box
gridRow="2 / 3"
gridColumn="1 / 2"
overflow="auto"
p="4"
bg="white"
>
Powered by{' '}
<span className={styles.logo}>
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
</span>
</a>
</footer>
</div>
)
}
<Form
schema={template.schema as any}
formData={formData}
onChange={(event) => setFormData(event.formData)}
onSubmit={({ formData }) => {
try {
const output = template.generate(formData);
setOutput(JSON.stringify(output, null, 2));
} catch (error) {
setOutput(`Error: ${(error as Error).message}`);
}
}}
noHtml5Validate
showErrorList={false}
validate={template.validate}
transformErrors={template.transformErrors}
>
<Button type="submit" colorScheme="brand">
Generate
</Button>
</Form>
</Box>
)}
<Box gridRow="2 / 3" gridColumn="2 / 3" overflow="auto" p="4">
{output && (
<Stack alignItems="start">
<Button onClick={onCopy}>{hasCopied ? "Copied" : "Copy"}</Button>
<Box as="pre" overflow="auto" w="100%" color="white">
{output}
</Box>
</Stack>
)}
</Box>
</Box>
);
};

export default Home
export default Home;
Binary file modified public/favicon.ico
Binary file not shown.
4 changes: 0 additions & 4 deletions public/vercel.svg

This file was deleted.

Loading

0 comments on commit 7eb8380

Please sign in to comment.