From f52bc42de1ed0c70d2a65469aa05906c38bc6fbe Mon Sep 17 00:00:00 2001 From: vincanger <70215737+vincanger@users.noreply.github.com> Date: Mon, 15 Jul 2024 16:06:50 +0200 Subject: [PATCH] Openai demo app vertical reorg (#234) * organize demo ai app vertically * Update main.wasp.diff * Update guided-tour.md * Leftover vertical org changes (#233) * newsletter dir & leftovers * update app_diff * Update guided-tour.md * Update guided-tour.md --- opensaas-sh/app_diff/main.wasp.diff | 2 +- .../client/landing-page/LandingPage.tsx.diff | 4 +- .../landing-page/contentSections.ts.diff | 4 +- .../app_diff/src/{ => shared}/common.ts.diff | 5 +- .../src/content/docs/start/guided-tour.md | 55 +++---- template/app/main.wasp | 146 +++++++++--------- .../app/src/client/components/AppNavBar.tsx | 2 +- .../src/client/landing-page/LandingPage.tsx | 2 +- .../client/landing-page/contentSections.ts | 2 +- .../app => demo-ai-app}/DemoAppPage.tsx | 4 +- .../actions.ts => demo-ai-app/operations.ts} | 51 ++++-- .../app/src/{gpt => demo-ai-app}/schedule.ts | 0 .../sendNewsletter.ts} | 6 +- template/app/src/payment/stripe/webhook.ts | 2 +- template/app/src/server/queries.ts | 32 ---- template/app/src/server/scripts/dbSeeds.ts | 2 +- template/app/src/{ => shared}/common.ts | 0 template/app/src/{ => shared}/utils.ts | 0 18 files changed, 163 insertions(+), 156 deletions(-) rename opensaas-sh/app_diff/src/{ => shared}/common.ts.diff (64%) rename template/app/src/{client/app => demo-ai-app}/DemoAppPage.tsx (99%) rename template/app/src/{server/actions.ts => demo-ai-app/operations.ts} (84%) rename template/app/src/{gpt => demo-ai-app}/schedule.ts (100%) rename template/app/src/{server/workers/checkAndQueueEmails.ts => newsletter/sendNewsletter.ts} (88%) delete mode 100644 template/app/src/server/queries.ts rename template/app/src/{ => shared}/common.ts (100%) rename template/app/src/{ => shared}/utils.ts (100%) diff --git a/opensaas-sh/app_diff/main.wasp.diff b/opensaas-sh/app_diff/main.wasp.diff index a2ee6eb..fb47525 100644 --- a/opensaas-sh/app_diff/main.wasp.diff +++ b/opensaas-sh/app_diff/main.wasp.diff @@ -84,7 +84,7 @@ }, }, } -@@ -195,7 +193,10 @@ +@@ -125,7 +123,10 @@ email String? @unique username String? @unique lastActiveTimestamp DateTime @default(now()) diff --git a/opensaas-sh/app_diff/src/client/landing-page/LandingPage.tsx.diff b/opensaas-sh/app_diff/src/client/landing-page/LandingPage.tsx.diff index f7c8a05..2102255 100644 --- a/opensaas-sh/app_diff/src/client/landing-page/LandingPage.tsx.diff +++ b/opensaas-sh/app_diff/src/client/landing-page/LandingPage.tsx.diff @@ -22,8 +22,8 @@ +} from './contentSections'; import DropdownUser from '../../user/DropdownUser'; import { UserMenuItems } from '../../user/UserMenuItems'; --import { DocsUrl } from '../../common'; -+import { DocsUrl, GithubUrl } from '../../common'; +-import { DocsUrl } from '../../shared/common'; ++import { DocsUrl, GithubUrl } from '../../shared/common'; import DarkModeSwitcher from '../components/DarkModeSwitcher'; export default function LandingPage() { diff --git a/opensaas-sh/app_diff/src/client/landing-page/contentSections.ts.diff b/opensaas-sh/app_diff/src/client/landing-page/contentSections.ts.diff index 6d443ea..bbbf4d5 100644 --- a/opensaas-sh/app_diff/src/client/landing-page/contentSections.ts.diff +++ b/opensaas-sh/app_diff/src/client/landing-page/contentSections.ts.diff @@ -1,11 +1,11 @@ --- template/app/src/client/landing-page/contentSections.ts +++ opensaas-sh/app/src/client/landing-page/contentSections.ts @@ -1,74 +1,126 @@ --import { DocsUrl, BlogUrl } from '../../common'; +-import { DocsUrl, BlogUrl } from '../../shared/common'; -import daBoiAvatar from '../static/da-boi.png'; -import avatarPlaceholder from '../static/avatar-placeholder.png'; -import { routes } from 'wasp/client/router'; -+import { DocsUrl, BlogUrl, GithubUrl } from '../../common'; ++import { DocsUrl, BlogUrl, GithubUrl } from '../../shared/common'; export const navigation = [ { name: 'Features', href: '#features' }, diff --git a/opensaas-sh/app_diff/src/common.ts.diff b/opensaas-sh/app_diff/src/shared/common.ts.diff similarity index 64% rename from opensaas-sh/app_diff/src/common.ts.diff rename to opensaas-sh/app_diff/src/shared/common.ts.diff index b7e8efe..f0fa527 100644 --- a/opensaas-sh/app_diff/src/common.ts.diff +++ b/opensaas-sh/app_diff/src/shared/common.ts.diff @@ -1,6 +1,7 @@ ---- template/app/src/common.ts -+++ opensaas-sh/app/src/common.ts +--- template/app/src/shared/common.ts ++++ opensaas-sh/app/src/shared/common.ts @@ -1,2 +1,3 @@ export const DocsUrl = 'https://docs.opensaas.sh'; export const BlogUrl = 'https://docs.opensaas.sh/blog'; +export const GithubUrl = 'https://github.com/wasp-lang/open-saas'; +\ No newline at end of file diff --git a/opensaas-sh/blog/src/content/docs/start/guided-tour.md b/opensaas-sh/blog/src/content/docs/start/guided-tour.md index d6dd990..e23836a 100644 --- a/opensaas-sh/blog/src/content/docs/start/guided-tour.md +++ b/opensaas-sh/blog/src/content/docs/start/guided-tour.md @@ -47,10 +47,14 @@ At the root of our project, you will see three folders: `e2e-tests` contains the end-to-end tests using Playwright, which you can run to test your app's functionality. +### App File Structure + +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. + Let's check out what's in the `app` folder in more detail: -:::caution[v0.11 and below] -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! πŸ˜… +:::caution[v0.13 and below] +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! πŸ˜… ::: ```sh @@ -59,11 +63,17 @@ If you are using a version of the OpenSaaS template with Wasp `v0.11.x` or below β”œβ”€β”€ .wasp/ # Output dir for Wasp. DON'T MODIFY THESE FILES! β”œβ”€β”€ public/ # Public assets dir, e.g. www.yourdomain.com/banner.png β”œβ”€β”€ src/ # Your code goes here. -β”‚Β Β  β”œβ”€β”€ client/ # Your client code (React) goes here. -β”‚Β Β  β”œβ”€β”€ server/ # Your server code (NodeJS) goes here. +β”‚Β Β  β”œβ”€β”€ admin/ # Admin dashboard related pages and components. +β”‚Β Β  β”œβ”€β”€ analytics/ # Logic and background jobs for processing analytics. β”‚Β Β  β”œβ”€β”€ auth/ # All auth-related pages/components and logic. +β”‚Β Β  β”œβ”€β”€ client/ # Shared components, hooks, landing page, and other client code (React). +β”‚Β Β  β”œβ”€β”€ demo-ai-app/ # Logic for the example AI-powered demo app. β”‚Β Β  β”œβ”€β”€ file-upload/ # Logic for uploading files to S3. +β”‚Β Β  β”œβ”€β”€ messages # Logic for app user messages. +β”‚Β Β  β”œβ”€β”€ newsletter/ # Logic for scheduled recurring newsletter sending. β”‚Β Β  β”œβ”€β”€ payment/ # Logic for handling Stripe payments and webhooks. +β”‚Β Β  β”œβ”€β”€ server/ # Scripts, shared server utils, and other server-specific code (NodeJS). +β”‚Β Β  β”œβ”€β”€ shared/ # Shared constants and util functions. β”‚Β Β  └── user/ # Logic related to users and their accounts. β”œβ”€β”€ .env.server # Dev environment variables for your server code. β”œβ”€β”€ .env.client # Dev environment variables for your client code. @@ -71,14 +81,9 @@ If you are using a version of the OpenSaaS template with Wasp `v0.11.x` or below β”œβ”€β”€ tailwind.config.js # TailwindCSS configuration. β”œβ”€β”€ package.json β”œβ”€β”€ package-lock.json - └── .wasproot ``` -:::tip[File Structure] -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. -::: - ### The Wasp Config file 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 ### Client -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. +The `src/client` folder contains any additional client-side code that doesn't belong to a feature: ```sh . └── client - Β Β  β”œβ”€β”€ admin # Admin dashboard pages and components - Β  β”œβ”€β”€ app # Your user-facing app that sits behind the paywall/login. - Β  β”œβ”€β”€ components # Your shared React components. - Β  β”œβ”€β”€ hooks # Your shared React hooks. - Β Β  β”œβ”€β”€ landing-page # Landing page related code - Β Β  β”œβ”€β”€ 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. - Β Β  └── Main.css + β”œβ”€β”€ components # Your shared React components. + β”œβ”€β”€ fonts # Extra fonts + β”œβ”€β”€ hooks # Your shared React hooks. + β”œβ”€β”€ icons # Your shared SVG icons. + β”œβ”€β”€ landing-page # Landing page related code + β”œβ”€β”€ 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. + β”œβ”€β”€ cn.ts # Helper function for dynamic and conditional Tailwind CSS classes. + └── Main.css ``` ### Server -The `src/server` folder contains the code that runs on the server. Wasp compiles everything into a NodeJS server for you. - -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. +The `src/server` folder contains any additional server-side code that does not belong to a specific feature: ```sh └── server - Β  β”œβ”€β”€ scripts # Scripts to run via Wasp, e.g. database seeding. - Β  β”œβ”€β”€ 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. - Β Β  └── utils.ts + β”œβ”€β”€ scripts # Scripts to run via Wasp, e.g. database seeding. + └── utils.ts ``` ## Main Features @@ -257,7 +258,7 @@ To do that, we've leveraged Wasp's [Jobs feature](https://wasp-lang.dev/docs/adv job dailyStatsJob { executor: PgBoss, perform: { - fn: import { calculateDailyStats } from "@src/server/workers/calculateDailyStats.js" + fn: import { calculateDailyStats } from "@src/analytics/stats" }, schedule: { cron: "0 * * * *" // runs every hour diff --git a/template/app/main.wasp b/template/app/main.wasp index 0f81d0e..c2e9070 100644 --- a/template/app/main.wasp +++ b/template/app/main.wasp @@ -85,29 +85,6 @@ app OpenSaaS { }, } -entity GptResponse {=psl - id String @id @default(uuid()) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - user User @relation(fields: [userId], references: [id]) - userId String - - content String -psl=} - -entity Task {=psl - id String @id @default(uuid()) - createdAt DateTime @default(now()) - - user User @relation(fields: [userId], references: [id]) - userId String - - description String - time String @default("1") - isDone Boolean @default(false) -psl=} - route LandingPageRoute { path: "/", to: LandingPage } page LandingPage { component: import LandingPage from "@src/client/landing-page/LandingPage" @@ -140,53 +117,6 @@ page EmailVerificationPage { } //#endregion -route DemoAppRoute { path: "/demo-app", to: DemoAppPage } -page DemoAppPage { - authRequired: true, - component: import DemoAppPage from "@src/client/app/DemoAppPage" -} - -action generateGptResponse { - fn: import { generateGptResponse } from "@src/server/actions.js", - entities: [User, Task, GptResponse] -} - -action createTask { - fn: import { createTask } from "@src/server/actions.js", - entities: [Task] -} - -action deleteTask { - fn: import { deleteTask } from "@src/server/actions.js", - entities: [Task] -} - -action updateTask { - fn: import { updateTask } from "@src/server/actions.js", - entities: [Task] -} - -query getGptResponses { - fn: import { getGptResponses } from "@src/server/queries.js", - entities: [User, GptResponse] -} - -query getAllTasksByUser { - fn: import { getAllTasksByUser } from "@src/server/queries.js", - entities: [Task] -} - -job emailChecker { - executor: PgBoss, - perform: { - fn: import { checkAndQueueEmails } from "@src/server/workers/checkAndQueueEmails.js" - }, - schedule: { - cron: "0 7 * * 1" // at 7:00 am every Monday - }, - entities: [User] -} - //#region User entity User {=psl id String @id @default(uuid()) @@ -201,7 +131,7 @@ entity User {=psl checkoutSessionId String? subscriptionStatus String? // 'active', 'canceled', 'past_due', 'deleted' subscriptionPlan String? // 'hobby', 'pro' - sendEmail Boolean @default(false) + sendNewsletter Boolean @default(false) datePaid DateTime? credits Int @default(3) @@ -233,6 +163,67 @@ action updateUserById { } //#endregion +//#region Demo AI App +route DemoAppRoute { path: "/demo-app", to: DemoAppPage } +page DemoAppPage { + authRequired: true, + component: import DemoAppPage from "@src/demo-ai-app/DemoAppPage" +} + +action generateGptResponse { + fn: import { generateGptResponse } from "@src/demo-ai-app/operations", + entities: [User, Task, GptResponse] +} + +action createTask { + fn: import { createTask } from "@src/demo-ai-app/operations", + entities: [Task] +} + +action deleteTask { + fn: import { deleteTask } from "@src/demo-ai-app/operations", + entities: [Task] +} + +action updateTask { + fn: import { updateTask } from "@src/demo-ai-app/operations", + entities: [Task] +} + +query getGptResponses { + fn: import { getGptResponses } from "@src/demo-ai-app/operations", + entities: [User, GptResponse] +} + +query getAllTasksByUser { + fn: import { getAllTasksByUser } from "@src/demo-ai-app/operations", + entities: [Task] +} + +entity GptResponse {=psl + id String @id @default(uuid()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + user User @relation(fields: [userId], references: [id]) + userId String + + content String +psl=} + +entity Task {=psl + id String @id @default(uuid()) + createdAt DateTime @default(now()) + + user User @relation(fields: [userId], references: [id]) + userId String + + description String + time String @default("1") + isDone Boolean @default(false) +psl=} +//#endregion + //#region Payment route PricingPageRoute { path: "/pricing", to: PricingPage } page PricingPage { @@ -426,3 +417,16 @@ entity ContactFormMessage {=psl repliedAt DateTime? psl=} //#endregion + +//#region Newsletter +job sendNewsletter { + executor: PgBoss, + perform: { + fn: import { checkAndQueueNewsletterEmails } from "@src/newsletter/sendNewsletter" + }, + schedule: { + cron: "0 7 * * 1" // at 7:00 am every Monday + }, + entities: [User] +} +//#endregion \ No newline at end of file diff --git a/template/app/src/client/components/AppNavBar.tsx b/template/app/src/client/components/AppNavBar.tsx index 291a43e..cdf5d35 100644 --- a/template/app/src/client/components/AppNavBar.tsx +++ b/template/app/src/client/components/AppNavBar.tsx @@ -8,7 +8,7 @@ import { HiBars3 } from 'react-icons/hi2'; import logo from '../static/logo.png'; import DropdownUser from '../../user/DropdownUser'; import { UserMenuItems } from '../../user/UserMenuItems'; -import { DocsUrl, BlogUrl } from '../../common'; +import { DocsUrl, BlogUrl } from '../../shared/common'; import DarkModeSwitcher from './DarkModeSwitcher'; const navigation = [ diff --git a/template/app/src/client/landing-page/LandingPage.tsx b/template/app/src/client/landing-page/LandingPage.tsx index d8627ed..1b28360 100644 --- a/template/app/src/client/landing-page/LandingPage.tsx +++ b/template/app/src/client/landing-page/LandingPage.tsx @@ -10,7 +10,7 @@ import openSaasBanner from '../static/open-saas-banner.png'; import { features, navigation, faqs, footerNavigation, testimonials } from './contentSections'; import DropdownUser from '../../user/DropdownUser'; import { UserMenuItems } from '../../user/UserMenuItems'; -import { DocsUrl } from '../../common'; +import { DocsUrl } from '../../shared/common'; import DarkModeSwitcher from '../components/DarkModeSwitcher'; export default function LandingPage() { diff --git a/template/app/src/client/landing-page/contentSections.ts b/template/app/src/client/landing-page/contentSections.ts index 07b3e4f..be7960e 100644 --- a/template/app/src/client/landing-page/contentSections.ts +++ b/template/app/src/client/landing-page/contentSections.ts @@ -1,4 +1,4 @@ -import { DocsUrl, BlogUrl } from '../../common'; +import { DocsUrl, BlogUrl } from '../../shared/common'; import daBoiAvatar from '../static/da-boi.png'; import avatarPlaceholder from '../static/avatar-placeholder.png'; import { routes } from 'wasp/client/router'; diff --git a/template/app/src/client/app/DemoAppPage.tsx b/template/app/src/demo-ai-app/DemoAppPage.tsx similarity index 99% rename from template/app/src/client/app/DemoAppPage.tsx rename to template/app/src/demo-ai-app/DemoAppPage.tsx index cdf8c0b..2c0a0fc 100644 --- a/template/app/src/client/app/DemoAppPage.tsx +++ b/template/app/src/demo-ai-app/DemoAppPage.tsx @@ -12,8 +12,8 @@ import { import { useState, useMemo } from 'react'; import { CgSpinner } from 'react-icons/cg'; import { TiDelete } from 'react-icons/ti'; -import type { GeneratedSchedule, MainTask, SubTask } from '../../gpt/schedule'; -import { cn } from '../cn'; +import type { GeneratedSchedule, MainTask, SubTask } from './schedule'; +import { cn } from '../client/cn'; export default function DemoAppPage() { return ( diff --git a/template/app/src/server/actions.ts b/template/app/src/demo-ai-app/operations.ts similarity index 84% rename from template/app/src/server/actions.ts rename to template/app/src/demo-ai-app/operations.ts index a55bcb5..3ec4b55 100644 --- a/template/app/src/server/actions.ts +++ b/template/app/src/demo-ai-app/operations.ts @@ -1,12 +1,7 @@ -import { type User, type Task } from 'wasp/entities'; +import type { Task, GptResponse } from 'wasp/entities'; +import type { GenerateGptResponse, CreateTask, DeleteTask, UpdateTask, GetGptResponses, GetAllTasksByUser } from 'wasp/server/operations'; import { HttpError } from 'wasp/server'; -import { - type GenerateGptResponse, - type CreateTask, - type DeleteTask, - type UpdateTask, -} from 'wasp/server/operations'; -import { GeneratedSchedule } from '../gpt/schedule'; +import { GeneratedSchedule } from './schedule'; import OpenAI from 'openai'; const openai = setupOpenAI(); @@ -17,6 +12,7 @@ function setupOpenAI() { return new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); } +//#region Actions type GptPayload = { hours: string; }; @@ -45,7 +41,12 @@ export const generateGptResponse: GenerateGptResponse, Task> = async ({ id }, con return task; }; +//#endregion + +//#region Queries +export const getGptResponses: GetGptResponses = async (_args, context) => { + if (!context.user) { + throw new HttpError(401); + } + return context.entities.GptResponse.findMany({ + where: { + user: { + id: context.user.id, + }, + }, + }); +}; + +export const getAllTasksByUser: GetAllTasksByUser = async (_args, context) => { + if (!context.user) { + throw new HttpError(401); + } + return context.entities.Task.findMany({ + where: { + user: { + id: context.user.id, + }, + }, + orderBy: { + createdAt: 'desc', + }, + }); +}; +//#endregion \ No newline at end of file diff --git a/template/app/src/gpt/schedule.ts b/template/app/src/demo-ai-app/schedule.ts similarity index 100% rename from template/app/src/gpt/schedule.ts rename to template/app/src/demo-ai-app/schedule.ts diff --git a/template/app/src/server/workers/checkAndQueueEmails.ts b/template/app/src/newsletter/sendNewsletter.ts similarity index 88% rename from template/app/src/server/workers/checkAndQueueEmails.ts rename to template/app/src/newsletter/sendNewsletter.ts index 31f3e65..e09fc8b 100644 --- a/template/app/src/server/workers/checkAndQueueEmails.ts +++ b/template/app/src/newsletter/sendNewsletter.ts @@ -1,4 +1,4 @@ -import { type EmailChecker } from 'wasp/server/jobs'; +import { type SendNewsletter } from 'wasp/server/jobs'; import { type User } from 'wasp/entities'; import { emailSender } from 'wasp/server/email'; @@ -22,7 +22,7 @@ const emailToSend: Email = { }; // you could use this function to send newsletters, expiration notices, etc. -export const checkAndQueueEmails: EmailChecker = async (_args, context) => { +export const checkAndQueueNewsletterEmails: SendNewsletter = async (_args, context) => { // e.g. you could send an offer email 2 weeks before their subscription expires const currentDate = new Date(); const twoWeeksFromNow = new Date(currentDate.getTime() + 14 * 24 * 60 * 60 * 1000); @@ -32,7 +32,7 @@ export const checkAndQueueEmails: EmailChecker = async (_args, cont datePaid: { equals: twoWeeksFromNow, }, - sendEmail: true, + sendNewsletter: true, }, })) as User[]; diff --git a/template/app/src/payment/stripe/webhook.ts b/template/app/src/payment/stripe/webhook.ts index ed99ea6..19f81cf 100644 --- a/template/app/src/payment/stripe/webhook.ts +++ b/template/app/src/payment/stripe/webhook.ts @@ -7,7 +7,7 @@ import { stripe } from './stripeClient'; import { paymentPlans, PaymentPlanId, SubscriptionStatus } from '../plans'; import { updateUserStripePaymentDetails } from './paymentDetails'; import { emailSender } from 'wasp/server/email'; -import { assertUnreachable } from '../../utils'; +import { assertUnreachable } from '../../shared/utils'; import { requireNodeEnvVar } from '../../server/utils'; import { z } from 'zod'; diff --git a/template/app/src/server/queries.ts b/template/app/src/server/queries.ts deleted file mode 100644 index 316fe69..0000000 --- a/template/app/src/server/queries.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { type GptResponse, type Task } from 'wasp/entities'; -import { HttpError } from 'wasp/server'; -import { type GetGptResponses, type GetAllTasksByUser } from 'wasp/server/operations'; - -export const getGptResponses: GetGptResponses = async (args, context) => { - if (!context.user) { - throw new HttpError(401); - } - return context.entities.GptResponse.findMany({ - where: { - user: { - id: context.user.id, - }, - }, - }); -}; - -export const getAllTasksByUser: GetAllTasksByUser = async (_args, context) => { - if (!context.user) { - throw new HttpError(401); - } - return context.entities.Task.findMany({ - where: { - user: { - id: context.user.id, - }, - }, - orderBy: { - createdAt: 'desc', - }, - }); -}; diff --git a/template/app/src/server/scripts/dbSeeds.ts b/template/app/src/server/scripts/dbSeeds.ts index f5f6af7..a5204b6 100644 --- a/template/app/src/server/scripts/dbSeeds.ts +++ b/template/app/src/server/scripts/dbSeeds.ts @@ -35,7 +35,7 @@ function generateMockUserData(): MockUserData { createdAt, lastActiveTimestamp, isAdmin: false, - sendEmail: false, + sendNewsletter: false, credits, subscriptionStatus, stripeId: hasUserPaidOnStripe ? `cus_test_${faker.string.uuid()}` : null, diff --git a/template/app/src/common.ts b/template/app/src/shared/common.ts similarity index 100% rename from template/app/src/common.ts rename to template/app/src/shared/common.ts diff --git a/template/app/src/utils.ts b/template/app/src/shared/utils.ts similarity index 100% rename from template/app/src/utils.ts rename to template/app/src/shared/utils.ts