Skip to content

Commit f52bc42

Browse files
authored
Openai demo app vertical reorg (wasp-lang#234)
* organize demo ai app vertically * Update main.wasp.diff * Update guided-tour.md * Leftover vertical org changes (wasp-lang#233) * newsletter dir & leftovers * update app_diff * Update guided-tour.md * Update guided-tour.md
1 parent c9d4358 commit f52bc42

File tree

18 files changed

+163
-156
lines changed

18 files changed

+163
-156
lines changed

opensaas-sh/app_diff/main.wasp.diff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
},
8585
},
8686
}
87-
@@ -195,7 +193,10 @@
87+
@@ -125,7 +123,10 @@
8888
email String? @unique
8989
username String? @unique
9090
lastActiveTimestamp DateTime @default(now())

opensaas-sh/app_diff/src/client/landing-page/LandingPage.tsx.diff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
+} from './contentSections';
2323
import DropdownUser from '../../user/DropdownUser';
2424
import { UserMenuItems } from '../../user/UserMenuItems';
25-
-import { DocsUrl } from '../../common';
26-
+import { DocsUrl, GithubUrl } from '../../common';
25+
-import { DocsUrl } from '../../shared/common';
26+
+import { DocsUrl, GithubUrl } from '../../shared/common';
2727
import DarkModeSwitcher from '../components/DarkModeSwitcher';
2828

2929
export default function LandingPage() {

opensaas-sh/app_diff/src/client/landing-page/contentSections.ts.diff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
--- template/app/src/client/landing-page/contentSections.ts
22
+++ opensaas-sh/app/src/client/landing-page/contentSections.ts
33
@@ -1,74 +1,126 @@
4-
-import { DocsUrl, BlogUrl } from '../../common';
4+
-import { DocsUrl, BlogUrl } from '../../shared/common';
55
-import daBoiAvatar from '../static/da-boi.png';
66
-import avatarPlaceholder from '../static/avatar-placeholder.png';
77
-import { routes } from 'wasp/client/router';
8-
+import { DocsUrl, BlogUrl, GithubUrl } from '../../common';
8+
+import { DocsUrl, BlogUrl, GithubUrl } from '../../shared/common';
99

1010
export const navigation = [
1111
{ name: 'Features', href: '#features' },
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
--- template/app/src/common.ts
2-
+++ opensaas-sh/app/src/common.ts
1+
--- template/app/src/shared/common.ts
2+
+++ opensaas-sh/app/src/shared/common.ts
33
@@ -1,2 +1,3 @@
44
export const DocsUrl = 'https://docs.opensaas.sh';
55
export const BlogUrl = 'https://docs.opensaas.sh/blog';
66
+export const GithubUrl = 'https://github.com/wasp-lang/open-saas';
7+
\ No newline at end of file

opensaas-sh/blog/src/content/docs/start/guided-tour.md

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,14 @@ At the root of our project, you will see three folders:
4747

4848
`e2e-tests` contains the end-to-end tests using Playwright, which you can run to test your app's functionality.
4949

50+
### App File Structure
51+
52+
We've structured this full-stack app template vertically (by feature). That means that most directories within `app/src` contain both the React client code and NodeJS server code necessary for implementing its logic.
53+
5054
Let's check out what's in the `app` folder in more detail:
5155

52-
:::caution[v0.11 and below]
53-
If you are using a version of the OpenSaaS template with Wasp `v0.11.x` or below, you may see a slightly different file structure. But don't worry, the vast majority of the code and features are the same! 😅
56+
:::caution[v0.13 and below]
57+
If you are using an older version of the OpenSaaS template with Wasp `v0.13.x` or below, you may see a slightly different file structure. But don't worry, the vast majority of the code and features are the same! 😅
5458
:::
5559

5660
```sh
@@ -59,26 +63,27 @@ If you are using a version of the OpenSaaS template with Wasp `v0.11.x` or below
5963
├── .wasp/ # Output dir for Wasp. DON'T MODIFY THESE FILES!
6064
├── public/ # Public assets dir, e.g. www.yourdomain.com/banner.png
6165
├── src/ # Your code goes here.
62-
│   ├── client/ # Your client code (React) goes here.
63-
│   ├── server/ # Your server code (NodeJS) goes here.
66+
│   ├── admin/ # Admin dashboard related pages and components.
67+
│   ├── analytics/ # Logic and background jobs for processing analytics.
6468
│   ├── auth/ # All auth-related pages/components and logic.
69+
│   ├── client/ # Shared components, hooks, landing page, and other client code (React).
70+
│   ├── demo-ai-app/ # Logic for the example AI-powered demo app.
6571
│   ├── file-upload/ # Logic for uploading files to S3.
72+
│   ├── messages # Logic for app user messages.
73+
│   ├── newsletter/ # Logic for scheduled recurring newsletter sending.
6674
│   ├── payment/ # Logic for handling Stripe payments and webhooks.
75+
│   ├── server/ # Scripts, shared server utils, and other server-specific code (NodeJS).
76+
│   ├── shared/ # Shared constants and util functions.
6777
│   └── user/ # Logic related to users and their accounts.
6878
├── .env.server # Dev environment variables for your server code.
6979
├── .env.client # Dev environment variables for your client code.
7080
├── .prettierrc # Prettier configuration.
7181
├── tailwind.config.js # TailwindCSS configuration.
7282
├── package.json
7383
├── package-lock.json
74-
7584
└── .wasproot
7685
```
7786

78-
:::tip[File Structure]
79-
Note that since Wasp v0.12, the `src` folder does not need to be organized between `client` and `server` code. You can organize your code however you like, e.g. by feature, but we've chosen to keep the traditional structure for this template.
80-
:::
81-
8287
### The Wasp Config file
8388

8489
This template at its core is a Wasp project, where [Wasp](https://wasp-lang.dev) is a full-stack web app framework that let’s you write your app in React, NodeJS, and Prisma and will manage the "boilerplatey" work for you, allowing you to just take care of the fun stuff!
@@ -104,35 +109,31 @@ It's possible to learn Wasp's feature set simply through using this template, bu
104109

105110
### Client
106111

107-
The `src/client` folder contains the code that runs in the browser. It's a standard React app, with a few Wasp-specific things sprinkled in.
112+
The `src/client` folder contains any additional client-side code that doesn't belong to a feature:
108113

109114
```sh
110115
.
111116
└── client
112-
   ├── admin # Admin dashboard pages and components
113-
  ├── app # Your user-facing app that sits behind the paywall/login.
114-
  ├── components # Your shared React components.
115-
  ├── hooks # Your shared React hooks.
116-
   ├── landing-page # Landing page related code
117-
   ├── static # Assets that you need access to in your code, e.g. import logo from 'static/logo.png'
118-
   ├── App.tsx # Main app component to wrap all child components. Useful for global state, navbars, etc.
119-
   └── Main.css
117+
├── components # Your shared React components.
118+
├── fonts # Extra fonts
119+
├── hooks # Your shared React hooks.
120+
├── icons # Your shared SVG icons.
121+
├── landing-page # Landing page related code
122+
├── static # Assets that you need access to in your code, e.g. import logo from 'static/logo.png'
123+
├── App.tsx # Main app component to wrap all child components. Useful for global state, navbars, etc.
124+
├── cn.ts # Helper function for dynamic and conditional Tailwind CSS classes.
125+
└── Main.css
120126

121127
```
122128

123129
### Server
124130

125-
The `src/server` folder contains the code that runs on the server. Wasp compiles everything into a NodeJS server for you.
126-
127-
All you have to do is define your server-side functions in the `main.wasp` file, write the logic in a function within `src/server` and Wasp will generate the boilerplate code for you.
131+
The `src/server` folder contains any additional server-side code that does not belong to a specific feature:
128132

129133
```sh
130134
└── server
131-
  ├── scripts # Scripts to run via Wasp, e.g. database seeding.
132-
  ├── workers # Functions that run in the background as Wasp Jobs, e.g. daily stats calculation.
133-
  ├── actions.ts # Your server-side write/mutation functions.
134-
   ├── queries.ts # Your server-side read functions.
135-
   └── utils.ts
135+
├── scripts # Scripts to run via Wasp, e.g. database seeding.
136+
└── utils.ts
136137
```
137138

138139
## Main Features
@@ -257,7 +258,7 @@ To do that, we've leveraged Wasp's [Jobs feature](https://wasp-lang.dev/docs/adv
257258
job dailyStatsJob {
258259
executor: PgBoss,
259260
perform: {
260-
fn: import { calculateDailyStats } from "@src/server/workers/calculateDailyStats.js"
261+
fn: import { calculateDailyStats } from "@src/analytics/stats"
261262
},
262263
schedule: {
263264
cron: "0 * * * *" // runs every hour

template/app/main.wasp

Lines changed: 75 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -85,29 +85,6 @@ app OpenSaaS {
8585
},
8686
}
8787

88-
entity GptResponse {=psl
89-
id String @id @default(uuid())
90-
createdAt DateTime @default(now())
91-
updatedAt DateTime @updatedAt
92-
93-
user User @relation(fields: [userId], references: [id])
94-
userId String
95-
96-
content String
97-
psl=}
98-
99-
entity Task {=psl
100-
id String @id @default(uuid())
101-
createdAt DateTime @default(now())
102-
103-
user User @relation(fields: [userId], references: [id])
104-
userId String
105-
106-
description String
107-
time String @default("1")
108-
isDone Boolean @default(false)
109-
psl=}
110-
11188
route LandingPageRoute { path: "/", to: LandingPage }
11289
page LandingPage {
11390
component: import LandingPage from "@src/client/landing-page/LandingPage"
@@ -140,53 +117,6 @@ page EmailVerificationPage {
140117
}
141118
//#endregion
142119

143-
route DemoAppRoute { path: "/demo-app", to: DemoAppPage }
144-
page DemoAppPage {
145-
authRequired: true,
146-
component: import DemoAppPage from "@src/client/app/DemoAppPage"
147-
}
148-
149-
action generateGptResponse {
150-
fn: import { generateGptResponse } from "@src/server/actions.js",
151-
entities: [User, Task, GptResponse]
152-
}
153-
154-
action createTask {
155-
fn: import { createTask } from "@src/server/actions.js",
156-
entities: [Task]
157-
}
158-
159-
action deleteTask {
160-
fn: import { deleteTask } from "@src/server/actions.js",
161-
entities: [Task]
162-
}
163-
164-
action updateTask {
165-
fn: import { updateTask } from "@src/server/actions.js",
166-
entities: [Task]
167-
}
168-
169-
query getGptResponses {
170-
fn: import { getGptResponses } from "@src/server/queries.js",
171-
entities: [User, GptResponse]
172-
}
173-
174-
query getAllTasksByUser {
175-
fn: import { getAllTasksByUser } from "@src/server/queries.js",
176-
entities: [Task]
177-
}
178-
179-
job emailChecker {
180-
executor: PgBoss,
181-
perform: {
182-
fn: import { checkAndQueueEmails } from "@src/server/workers/checkAndQueueEmails.js"
183-
},
184-
schedule: {
185-
cron: "0 7 * * 1" // at 7:00 am every Monday
186-
},
187-
entities: [User]
188-
}
189-
190120
//#region User
191121
entity User {=psl
192122
id String @id @default(uuid())
@@ -201,7 +131,7 @@ entity User {=psl
201131
checkoutSessionId String?
202132
subscriptionStatus String? // 'active', 'canceled', 'past_due', 'deleted'
203133
subscriptionPlan String? // 'hobby', 'pro'
204-
sendEmail Boolean @default(false)
134+
sendNewsletter Boolean @default(false)
205135
datePaid DateTime?
206136
credits Int @default(3)
207137

@@ -233,6 +163,67 @@ action updateUserById {
233163
}
234164
//#endregion
235165

166+
//#region Demo AI App
167+
route DemoAppRoute { path: "/demo-app", to: DemoAppPage }
168+
page DemoAppPage {
169+
authRequired: true,
170+
component: import DemoAppPage from "@src/demo-ai-app/DemoAppPage"
171+
}
172+
173+
action generateGptResponse {
174+
fn: import { generateGptResponse } from "@src/demo-ai-app/operations",
175+
entities: [User, Task, GptResponse]
176+
}
177+
178+
action createTask {
179+
fn: import { createTask } from "@src/demo-ai-app/operations",
180+
entities: [Task]
181+
}
182+
183+
action deleteTask {
184+
fn: import { deleteTask } from "@src/demo-ai-app/operations",
185+
entities: [Task]
186+
}
187+
188+
action updateTask {
189+
fn: import { updateTask } from "@src/demo-ai-app/operations",
190+
entities: [Task]
191+
}
192+
193+
query getGptResponses {
194+
fn: import { getGptResponses } from "@src/demo-ai-app/operations",
195+
entities: [User, GptResponse]
196+
}
197+
198+
query getAllTasksByUser {
199+
fn: import { getAllTasksByUser } from "@src/demo-ai-app/operations",
200+
entities: [Task]
201+
}
202+
203+
entity GptResponse {=psl
204+
id String @id @default(uuid())
205+
createdAt DateTime @default(now())
206+
updatedAt DateTime @updatedAt
207+
208+
user User @relation(fields: [userId], references: [id])
209+
userId String
210+
211+
content String
212+
psl=}
213+
214+
entity Task {=psl
215+
id String @id @default(uuid())
216+
createdAt DateTime @default(now())
217+
218+
user User @relation(fields: [userId], references: [id])
219+
userId String
220+
221+
description String
222+
time String @default("1")
223+
isDone Boolean @default(false)
224+
psl=}
225+
//#endregion
226+
236227
//#region Payment
237228
route PricingPageRoute { path: "/pricing", to: PricingPage }
238229
page PricingPage {
@@ -426,3 +417,16 @@ entity ContactFormMessage {=psl
426417
repliedAt DateTime?
427418
psl=}
428419
//#endregion
420+
421+
//#region Newsletter
422+
job sendNewsletter {
423+
executor: PgBoss,
424+
perform: {
425+
fn: import { checkAndQueueNewsletterEmails } from "@src/newsletter/sendNewsletter"
426+
},
427+
schedule: {
428+
cron: "0 7 * * 1" // at 7:00 am every Monday
429+
},
430+
entities: [User]
431+
}
432+
//#endregion

template/app/src/client/components/AppNavBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { HiBars3 } from 'react-icons/hi2';
88
import logo from '../static/logo.png';
99
import DropdownUser from '../../user/DropdownUser';
1010
import { UserMenuItems } from '../../user/UserMenuItems';
11-
import { DocsUrl, BlogUrl } from '../../common';
11+
import { DocsUrl, BlogUrl } from '../../shared/common';
1212
import DarkModeSwitcher from './DarkModeSwitcher';
1313

1414
const navigation = [

template/app/src/client/landing-page/LandingPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import openSaasBanner from '../static/open-saas-banner.png';
1010
import { features, navigation, faqs, footerNavigation, testimonials } from './contentSections';
1111
import DropdownUser from '../../user/DropdownUser';
1212
import { UserMenuItems } from '../../user/UserMenuItems';
13-
import { DocsUrl } from '../../common';
13+
import { DocsUrl } from '../../shared/common';
1414
import DarkModeSwitcher from '../components/DarkModeSwitcher';
1515

1616
export default function LandingPage() {

template/app/src/client/landing-page/contentSections.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { DocsUrl, BlogUrl } from '../../common';
1+
import { DocsUrl, BlogUrl } from '../../shared/common';
22
import daBoiAvatar from '../static/da-boi.png';
33
import avatarPlaceholder from '../static/avatar-placeholder.png';
44
import { routes } from 'wasp/client/router';

template/app/src/client/app/DemoAppPage.tsx renamed to template/app/src/demo-ai-app/DemoAppPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import {
1212
import { useState, useMemo } from 'react';
1313
import { CgSpinner } from 'react-icons/cg';
1414
import { TiDelete } from 'react-icons/ti';
15-
import type { GeneratedSchedule, MainTask, SubTask } from '../../gpt/schedule';
16-
import { cn } from '../cn';
15+
import type { GeneratedSchedule, MainTask, SubTask } from './schedule';
16+
import { cn } from '../client/cn';
1717

1818
export default function DemoAppPage() {
1919
return (

0 commit comments

Comments
 (0)