Skip to content

Commit

Permalink
Merge pull request #5 from esbnet/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
esbnet authored Mar 20, 2024
2 parents 1119b53 + fecbea2 commit 7f134db
Show file tree
Hide file tree
Showing 44 changed files with 787 additions and 175 deletions.
9 changes: 6 additions & 3 deletions .env.semple
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/dashboard

OPENAI_API_KEY=
OPENAI_API_KEY=
OPENAI_ORGANIZATION_ID=
REPLICATE_API_TOKEN=

DATABASE_URL=
DATABASE_URL="mysql://root:example@localhost:3306/iasaas"

STRIPE_API_KEY=
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_PRICE_ID=

NEXT_PUBLIC_APP_URL="http://localhost:3000"
NEXT_PUBLIC_APP_URL="http://localhost:3000"
132 changes: 110 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,124 @@
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).
# Synthetic

## Getting Started
O Synthetic e um modelo de aplicativo SaaS, totalmente funcional, mostra, de forma didática, um ambiente para gerar conteúdo através de IA. Para utilizá-lo você faz o registro com e-mail ou autorização via rede social (Google ou Facebook, etc). Após o registro a plataforma permite a assinatura do serviço que permite gerar conteúdo através de APIs de Inteligência Artificial.

First, run the development server:
Aos interessados, existe a possibilidade de substituir o serviço por qualquer outro, aproveitando a estrutura e os recursos da aplicação. Este modelo foi elaborado para exemplificar, de forma didática, o processo de um modelo de um Micro SaaS.

![alt text](./doc/land-page.png)

## Pré-requisitos

- node (20.11.1 - utilizado)
- npm (10.2.4 - utilizado) ou gerenciador de biblioteca de sua preferência
- git (2.34.1 - utilizado)

## Techs

- React 18 - linguagem de programação - <https://react.dev>
- Next 14 - framework - <https://nextjs.org>
- Typescript - tipagem - <https://www.typescriptlang.org>
- Shadcn ui - componentes - <https://ui.shadcn.com>
- Tailwind - estilização e ui - <https://tailwindui.com>
- Lucide-react - ícones - <https://lucide.dev/icons>
- Prisma - orm / persistência - <https://www.prisma.io>
- Next-intl - internacionalização - <https://next-intl-docs.vercel.app>
- Eslint - padronização, qualidade e estilo de código - <https://eslint.org>
- Axios - Cliente HTTP baseado em Promise - <https://axios-http.com/>
- React-markdown - React component to render markdown - <https://www.npmjs.com/package/react-markdown>
- Zod - Biblioteca de declaração e validação de esquema TypeScript-first - <https://zod.dev>

[](#servicos)

## Serviços na web

- Clerk/nextjs - Autenticação e Autorização - <https://clerk.com/docs>
- Stripe - gateway de pagamento - <https://stripe.com/br>
- Openai - Inteligência Artificial - <https://platform.openai.com/docs/introduction>
- Replicate - Inteligência Artificial - <https://replicate.com/docs>
- Crisp - Plataforma de mensagens multifunções e multicanal - <https://crisp.chat/pt>
- Database - mySql - <https://www.mysql.com>

![alt text](./doc/dashboard.png)

## Instalar e Executar

1. Clone

Escolha a pasta onde deseja armazenar o projeto e digite os comandos abaixo:

```
git clone https://github.com/esbnet/synthetic
```

2. Instalar dependências

Na pasta raiz do projeto, digite o seguinte comando:

```
npm install
```

3. Configurar variáveis de ambiente

Para que o sistema rode é necessário configurar as variáveis de ambiente. Para isso, deverá ser consultado a documentação de cada serviço utilizado. <a id="#servicos">Link acima</a>.
Na pasta raiz, crie o arquivo `.env` e inclua as variáveis abaixo com seus respectivos valores.

```
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/dashboard
# IA's
OPENAI_API_KEY=
OPENAI_API_KEY=
OPENAI_ORGANIZATION_ID=
REPLICATE_API_TOKEN=
# Database
DATABASE_URL="mysql://root:example@localhost:3306/iasaas" //Config your database URL
# STRIPE_SECRET_KEY=
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_PRICE_ID=
NEXT_PUBLIC_APP_URL="http://localhost:3000"
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
4. Criar banco de dados e tabelas

É pré-requisito configurar as variáveis de ambiente para que o sistema tenha as credenciais de acesso ao Supabase.
Na pasta raiz, digite:

```
npx prisma migrate dev
```

Este é o comando que criará o banco de dados e as tabelas no ambiente do Bando de Dados.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
5. Executar o projeto

This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
Ainda na pasta raiz do projeto, após realizar todos os procedimentos acima, rode o comando:

## Learn More
```
npm run dev
```

To learn more about Next.js, take a look at the following resources:
</br></br>

- [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.
<center>
Bons estudos...

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
</br></br></br>

## Deploy on Vercel
Me pagar um café (pix): :coffee:

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.
![me pague um café](./doc/pix.png)

Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
</center>
15 changes: 11 additions & 4 deletions app/(dashboard)/(routes)/code/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { useState } from "react";

import Empty from "@/components/empty";
import Heading from "@/components/heading";
import { Heading } from "@/components/heading";
import Loader from "@/components/loader";
import BotAvatar from "@/components/ui/bot-avatar";
import { Button } from "@/components/ui/button";
Expand All @@ -20,6 +20,8 @@ import ReactMarkdown from "react-markdown";

import { useForm } from "react-hook-form";

import { useProModal } from "@/hooks/use-pro-modal";
import toast from "react-hot-toast";
import * as z from "zod";
import { formSchema } from "./constants";

Expand All @@ -29,6 +31,8 @@ type userMessage = {
};

export default function CodePage() {
const proModal = useProModal();

const [messages, setMessages] = useState<userMessage[]>([]);
const router = useRouter();
const form = useForm<z.infer<typeof formSchema>>({
Expand Down Expand Up @@ -60,9 +64,12 @@ export default function CodePage() {
console.log(messages);

form.reset();
} catch (error) {
// TODO: Open Pro Modal
console.log(error);
} catch (error: any) {
if (error?.response?.status === 403) {
proModal.onOpen();
} else {
toast.error("Something went wrong");
}
} finally {
// form.reset();
router.refresh();
Expand Down
16 changes: 11 additions & 5 deletions app/(dashboard)/(routes)/conversation/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import { useState } from "react";

import Empty from "@/components/empty";
import Heading from "@/components/heading";
import Loader from "@/components/loader";
import BotAvatar from "@/components/ui/bot-avatar";
import { Button } from "@/components/ui/button";
Expand All @@ -21,6 +20,9 @@ import remarkGfm from "remark-gfm";

import { useForm } from "react-hook-form";

import { Heading } from "@/components/heading";
import { useProModal } from "@/hooks/use-pro-modal";
import toast from "react-hot-toast";
import * as z from "zod";
import { formSchema } from "./constants";

Expand All @@ -30,6 +32,7 @@ type userMessage = {
};

export default function Conversation() {
const proModal = useProModal();
const [messages, setMessages] = useState<userMessage[]>([]);
const router = useRouter();
const form = useForm<z.infer<typeof formSchema>>({
Expand Down Expand Up @@ -61,11 +64,14 @@ export default function Conversation() {
console.log(messages);

form.reset();
} catch (error) {
// TODO: Open Pro Modal
console.log(error);
} catch (error: any) {
if (error?.response?.status === 403) {
proModal.onOpen();
} else {
toast.error("Something went wrong");
}
} finally {
// form.reset();
form.reset();
router.refresh();
}
};
Expand Down
15 changes: 11 additions & 4 deletions app/(dashboard)/(routes)/image/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useForm } from "react-hook-form";
import * as z from "zod";

import Empty from "@/components/empty";
import Heading from "@/components/heading";
import { Heading } from "@/components/heading";
import Loader from "@/components/loader";
import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem } from "@/components/ui/form";
Expand All @@ -26,14 +26,18 @@ import { Card, CardFooter } from "@/components/ui/card";
import Image from "next/image";
import { amountOptions, formSchema, resolutionOptions } from "./constants";

import { useProModal } from "@/hooks/use-pro-modal";
import { Download, Image as ImageIcon } from "lucide-react";
import toast from "react-hot-toast";

type userMessage = {
role: "user";
content: string;
};

export default function ImagePage() {
const proModal = useProModal();

const router = useRouter();
const [images, setImages] = useState<string[]>([]);

Expand All @@ -58,9 +62,12 @@ export default function ImagePage() {
setImages(urls);

form.reset();
} catch (error) {
// TODO: Open Pro Modal
console.log(error);
} catch (error: any) {
if (error?.response?.status === 403) {
proModal.onOpen();
} else {
toast.error("Something went wrong");
}
} finally {
// form.reset();
router.refresh();
Expand Down
15 changes: 11 additions & 4 deletions app/(dashboard)/(routes)/music/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import { useState } from "react";

import Empty from "@/components/empty";
import Heading from "@/components/heading";
import Loader from "@/components/loader";
import { Button } from "@/components/ui/button";
import { Form, FormControl, FormField, FormItem } from "@/components/ui/form";
Expand All @@ -15,11 +14,16 @@ import { useRouter } from "next/navigation";

import { useForm } from "react-hook-form";

import { Heading } from "@/components/heading";
import { useProModal } from "@/hooks/use-pro-modal";
import { Music } from "lucide-react";
import toast from "react-hot-toast";
import * as z from "zod";
import { formSchema } from "./constants";

export default function MusicPage() {
const proModal = useProModal();

const [music, setMusic] = useState<string>();

const router = useRouter();
Expand All @@ -41,9 +45,12 @@ export default function MusicPage() {
setMusic(response.data.audio);

form.reset();
} catch (error) {
// TODO: Open Pro Modal
console.log(error);
} catch (error: any) {
if (error?.response?.status === 403) {
proModal.onOpen();
} else {
toast.error("Something went wrong");
}
} finally {
// form.reset();
router.refresh();
Expand Down
7 changes: 7 additions & 0 deletions app/(dashboard)/(routes)/settings/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as z from "zod";

export const formSchema = z.object({
prompt: z.string().min(1, {
message: "Prompt is required.",
}),
});
31 changes: 31 additions & 0 deletions app/(dashboard)/(routes)/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Settings } from "lucide-react";

import { Heading } from "@/components/heading";
import { SubscriptionButton } from "@/components/subscription-button";
import { checkSubscription } from "@/lib/subscription";

const SettingsPage = async () => {
const isPro = await checkSubscription();

return (
<div>
<Heading
title="Settings"
description="Manage account settings."
icon={Settings}
iconColor="text-gray-700"
bgColor="bg-gray-700/10"
/>
<div className="px-4 lg:px-8 space-y-4">
<div className="text-muted-foreground text-sm">
{isPro
? "You are currently on a Pro plan."
: "You are currently on a free plan."}
</div>
<SubscriptionButton isPro={isPro} />
</div>
</div>
);
};

export default SettingsPage;
2 changes: 1 addition & 1 deletion app/(dashboard)/(routes)/video/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ import * as z from "zod";

export const formSchema = z.object({
prompt: z.string().min(1, {
message: "Prompt is required.",
message: "Video Prompt is required.",
}),
});
Loading

0 comments on commit 7f134db

Please sign in to comment.