mirror of
https://github.com/wasp-lang/open-saas.git
synced 2025-04-10 04:49:03 +02:00
update doc intro
This commit is contained in:
parent
546a5eb6ac
commit
fefe37a312
@ -1,44 +1,57 @@
|
||||
---
|
||||
---
|
||||
title: Introduction
|
||||
---
|
||||
|
||||
## Welcome to your new SaaS App!
|
||||
|
||||
// TODO: add a screenshot of the app
|
||||
<!-- {/* TODO: add a screenshot of the app */} -->
|
||||
|
||||
You've decided to build a SaaS app with this template. Great choice! 🎉
|
||||
|
||||
This template is:
|
||||
1. fully open-source
|
||||
2. completely free to use and distribute
|
||||
3. comes with a lot a ton of features out of the box!
|
||||
This template is:
|
||||
|
||||
Try it out here: // TODO: add link
|
||||
Check Out the Code: // TODO: add repo link
|
||||
1. fully open-source
|
||||
2. completely free to use and distribute
|
||||
3. comes with a ton of features out of the box!
|
||||
|
||||
<!-- // TODO: add links -->
|
||||
Try it out here:
|
||||
Check Out the Code:
|
||||
|
||||
## What's inside?
|
||||
|
||||
The template itself is built on top of some very powerful tools and frameworks, including:
|
||||
|
||||
- 🐝 [Wasp](https://wasp-lang.dev) - a full-stack React, NodeJS, Prisma framework with superpowers
|
||||
- 🚀 [Astro](https://starlight.astro.build/) - Astro's lightweight "Starlight" template for documentation and blog
|
||||
- 💸 [Stripe](https://stripe.com) - for products and payments
|
||||
- 📈 [Plausible](https://plausible.io) or [Google](https://analytics.google.com/) Analytics
|
||||
- 🤖 [OpenAI](https://openai.com) - OpenAI API integrated into the app
|
||||
- 📧 [SendGrid](https://sendgrid.com), [MailGun](https://mailgun.com), or SMTP - for email sending
|
||||
- 💅 [TailwindCSS](https://tailwindcss.com) - for styling
|
||||
- 🧑💼 [TailAdmin](https://tailadmin.com/) - admin dashboard & components for TailwindCSS
|
||||
|
||||
Because we're using Wasp as the full-stack framework, we can leverage a lot of its features to build our SaaS in record time, including:
|
||||
|
||||
- 🔐 [Full-stack Authentication](https://wasp-lang.dev/docs/auth/overview) - Email verified + social Auth in a few lines of code.
|
||||
- ⛑ [End-to-end Type Safety](https://wasp-lang.dev/docs/data-model/operations/overview) - Type your backend functions and get inferred types on the front-end automatically, without the need to install or configure any third-party libraries. Oh, and type-safe Links, too!
|
||||
- ⛑ [End-to-end Type Safety](https://wasp-lang.dev/docs/data-model/operations/overview) - Type your backend functions and get inferred types on the front-end automatically, without the need to install or configure any third-party libraries. Oh, and type-safe Links, too!
|
||||
- 🤖 [Jobs](https://wasp-lang.dev/docs/language/features#jobs) - Run cron jobs in the background or set up queues simply by defining a function in the config file.
|
||||
- 🚀 [One-command Deploy](https://wasp-lang.dev/docs/advanced/deployment/overview) - Easily deploy via the CLI to [Fly.io](https://fly.io), or to other provides like [Railway](https://railway.app) and [Netlify](https://netlify.com).
|
||||
|
||||
You also get access to Wasp's diverse, helpful community if you get stuck or need help.
|
||||
- 🤝 [Wasp Discord](https://discord.gg/aCamt5wCpS)
|
||||
|
||||
:::tip["Work In Progress"]
|
||||
We've tried to get as many of the core features of a SaaS app into this template as possible, but there still might be some missing features or functionality.
|
||||
|
||||
We could always use some help tying up loose ends, so consider [contributing]()!
|
||||
<!-- {/* TODO: add link */} -->
|
||||
:::
|
||||
|
||||
## Getting acquainted with the codebase
|
||||
|
||||
At the root of our project, you will see the following folders and files:
|
||||
|
||||
```sh
|
||||
.
|
||||
├── .gitignore
|
||||
@ -56,9 +69,10 @@ At the root of our project, you will see the following folders and files:
|
||||
|
||||
### Wasp Config file
|
||||
|
||||
The `main.wasp` file is where you define your app structure.
|
||||
The `main.wasp` file is where you define your app structure.
|
||||
|
||||
In this template, we've already defined a number of things for you, including:
|
||||
|
||||
- Auth
|
||||
- Routes and Pages
|
||||
- Prisma Database Models
|
||||
@ -68,7 +82,7 @@ In this template, we've already defined a number of things for you, including:
|
||||
|
||||
By defining these things in the config file, Wasp continuously handles the boilerplate necessary with putting all these features together. You just need to focus on the business logic of your app!
|
||||
|
||||
Wasp abstracts away some things that you would normally be used to doing during development. For exmaple, you may notice there's no `package.json` file at the root of the project.
|
||||
Wasp abstracts away some things that you would normally be used to doing during development. For exmaple, you may notice there's no `package.json` file at the root of the project.
|
||||
|
||||
That's why we highly suggest you get acquainted with Wasp. A great starting point is the intro tutorial in the [Wasp docs](https://wasp-lang.dev/docs/) which takes ~20 minutes.
|
||||
|
||||
@ -82,13 +96,13 @@ The `src/client` folder contains all the code that runs in the browser. It's a s
|
||||
└── client
|
||||
├── admin # Admin dashboard pages and components
|
||||
├── app # Your user-facing app that sits behind the login.
|
||||
├── auth # All auth-related pages and components.
|
||||
├── auth # All auth-related pages and components.
|
||||
├── components # Your shared React components.
|
||||
├── hooks # Your shared React hooks.
|
||||
├── landing-page # Landing page related code
|
||||
├── public # Assets that are publicly accessible, e.g. www.yourdomain.com/banner.png
|
||||
├── static # Assets that you need access to in your code, e.g. import logo from 'static/logo.png'
|
||||
├── App.tsx # Main app component to wrap all child components. Useful for global state, navbars, etc.
|
||||
├── App.tsx # Main app component to wrap all child components. Useful for global state, navbars, etc.
|
||||
└── Main.css
|
||||
|
||||
```
|
||||
@ -105,8 +119,8 @@ The `src/server` folder contains all the code that runs on the server. Wasp comp
|
||||
├── workers # Functions that run in the background as Wasp Jobs, e.g. daily stats calculation.
|
||||
├── actions.ts # Your server-side write/mutation functions.
|
||||
├── queries.ts # Your server-side read functions.
|
||||
├── stripeUtils.ts
|
||||
├── stripeUtils.ts
|
||||
├── static # Assets that you need access to in your code, e.g. import logo from 'static/logo.png'
|
||||
├── types.ts # Main app component to wrap all child components. Useful for global state, navbars, etc.
|
||||
├── types.ts # Main app component to wrap all child components. Useful for global state, navbars, etc.
|
||||
└── Main.css
|
||||
```
|
||||
```
|
||||
|
10
main.wasp
10
main.wasp
@ -12,7 +12,7 @@ app SaaSTemplate {
|
||||
"<meta name='twitter:image:width' content='800' />",
|
||||
"<meta name='twitter:image:height' content='400' />",
|
||||
"<meta name='twitter:card' content='summary_large_image' />",
|
||||
// you can put your analytics scripts here, too!
|
||||
// you can put your analytics scripts here
|
||||
"<script defer data-domain='localhost' src='https://plausible.apps.twoducks.dev/js/script.js'></script>",
|
||||
// plausible has script extension `script.local.js` for local development
|
||||
"<script defer data-domain='localhost' src='https://plausible.apps.twoducks.dev/js/script.local.js'></script>",
|
||||
@ -122,7 +122,6 @@ entity SocialLogin {=psl
|
||||
@@unique([provider, providerId, userId])
|
||||
psl=}
|
||||
|
||||
// This can be anything. In most cases, this will be your product
|
||||
entity GptResponse {=psl
|
||||
id String @id @default(uuid())
|
||||
content String
|
||||
@ -132,6 +131,8 @@ entity GptResponse {=psl
|
||||
updatedAt DateTime @updatedAt
|
||||
psl=}
|
||||
|
||||
// TODO: add functionality to allow users to send messages to admin
|
||||
// and make them accessible via the admin dashboard
|
||||
entity ContactFormMessage {=psl
|
||||
id String @id @default(uuid())
|
||||
content String
|
||||
@ -212,11 +213,6 @@ page GptPage {
|
||||
component: import GptPage from "@client/app/GptPage"
|
||||
}
|
||||
|
||||
route PricingRoute { path: "/pricing", to: PricingPage }
|
||||
page PricingPage {
|
||||
component: import Pricing from "@client/app/PricingPage"
|
||||
}
|
||||
|
||||
route AccountRoute { path: "/account", to: AccountPage }
|
||||
page AccountPage {
|
||||
authRequired: true,
|
||||
|
@ -1,94 +0,0 @@
|
||||
import { AiOutlineCheck } from 'react-icons/ai';
|
||||
import stripePayment from '@wasp/actions/stripePayment';
|
||||
import { useState } from 'react';
|
||||
|
||||
// TODO: fix this page
|
||||
|
||||
|
||||
const prices = [
|
||||
{
|
||||
name: 'Credits',
|
||||
id: 'credits',
|
||||
href: '',
|
||||
price: '$2.95',
|
||||
description: 'Buy credits to use for your projects.',
|
||||
features: ['10 credits', 'Use them any time', 'No expiration date'],
|
||||
disabled: true,
|
||||
},
|
||||
{
|
||||
name: 'Monthly Subscription',
|
||||
id: 'monthly',
|
||||
href: '#',
|
||||
priceMonthly: '$9.99',
|
||||
description: 'Get unlimited usage for your projects.',
|
||||
features: ['Unlimited usage of all features', 'Priority support', 'Cancel any time'],
|
||||
},
|
||||
];
|
||||
|
||||
export default function PricingPage() {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const clickHandler = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await stripePayment('hobby');
|
||||
if (response?.sessionUrl) {
|
||||
window.open(response.sessionUrl, '_self');
|
||||
}
|
||||
} catch (e) {
|
||||
alert('Something went wrong. Please try again.');
|
||||
console.error(e);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className='mt-10 pb-24 sm:pb-32'>
|
||||
<div className='mx-auto max-w-7xl px-6 lg:px-8'>
|
||||
<div className='mx-auto grid max-w-md grid-cols-1 gap-8 lg:max-w-4xl lg:grid-cols-2'>
|
||||
{prices.map((price) => (
|
||||
<div
|
||||
key={price.id}
|
||||
className='flex flex-col justify-between rounded-3xl bg-white p-8 shadow-xl ring-1 ring-gray-900/10 sm:p-10'
|
||||
>
|
||||
<div>
|
||||
<h3 id={price.id} className='text-base font-semibold leading-7 text-indigo-600'>
|
||||
{price.name}
|
||||
</h3>
|
||||
<div className='mt-4 flex items-baseline gap-x-2'>
|
||||
<span className='text-5xl font-bold tracking-tight text-gray-900'>
|
||||
{price.priceMonthly || price.price}
|
||||
</span>
|
||||
{price.priceMonthly && (
|
||||
<span className='text-base font-semibold leading-7 text-gray-600'>/month</span>
|
||||
)}
|
||||
</div>
|
||||
<p className='mt-6 text-base leading-7 text-gray-600'>{price.description}</p>
|
||||
<ul role='list' className='mt-10 space-y-4 text-sm leading-6 text-gray-600'>
|
||||
{price.features.map((feature) => (
|
||||
<li key={feature} className='flex gap-x-3'>
|
||||
<AiOutlineCheck className='h-6 w-5 flex-none text-indigo-600' aria-hidden='true' />
|
||||
{feature}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<button
|
||||
onClick={clickHandler}
|
||||
aria-describedby={price.id}
|
||||
disabled={price.disabled}
|
||||
className={`${
|
||||
price.disabled && 'disabled:opacity-25 disabled:cursor-not-allowed'
|
||||
} mt-8 block rounded-md bg-yellow-400 px-3.5 py-2 text-center text-sm font-semibold leading-6 text-black shadow-sm hover:bg-yellow-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-yellow-600`}
|
||||
>
|
||||
{isLoading ? 'Loading...' : 'Buy Now'}
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user