diff --git a/examples/todo-typescript/.gitignore b/examples/todo-typescript/.gitignore new file mode 100644 index 000000000..c51177f6d --- /dev/null +++ b/examples/todo-typescript/.gitignore @@ -0,0 +1,3 @@ +/.wasp/ +/.env.server +/.env.client diff --git a/examples/todo-typescript/.wasproot b/examples/todo-typescript/.wasproot new file mode 100644 index 000000000..ca2cfdb48 --- /dev/null +++ b/examples/todo-typescript/.wasproot @@ -0,0 +1 @@ +File marking the root of Wasp project. diff --git a/examples/todo-typescript/main.wasp b/examples/todo-typescript/main.wasp new file mode 100644 index 000000000..ec442ad9d --- /dev/null +++ b/examples/todo-typescript/main.wasp @@ -0,0 +1,68 @@ +app TodoTypescript { + wasp: { + version: "^0.7.3" + }, + title: "ToDo TypeScript", + + auth: { + userEntity: User, + methods: { + usernameAndPassword: {}, + }, + onAuthFailedRedirectTo: "/login", + } +} + +// Use Prisma Schema Language (PSL) to define our entities: https://www.prisma.io/docs/concepts/components/prisma-schema +// Run `wasp db migrate-dev` in the CLI to create the database tables +// Then run `wasp db studio` to open Prisma Studio and view your db models +entity User {=psl + id Int @id @default(autoincrement()) + username String @unique + password String + tasks Task[] +psl=} + +entity Task {=psl + id Int @id @default(autoincrement()) + description String + isDone Boolean @default(false) + user User? @relation(fields: [userId], references: [id]) + userId Int? +psl=} + +route RootRoute { path: "/", to: MainPage } +page MainPage { + authRequired: true, + component: import { MainPage } from "@client/MainPage" +} + +route LoginRoute { path: "/login", to: LoginPage } +page LoginPage { + component: import { LoginPage } from "@client/LoginPage" +} + +route SignupRoute { path: "/signup", to: SignupPage } +page SignupPage { + component: import { SignupPage } from "@client/SignupPage" +} + +query getTasks { + // We specify the JS implementation of our query (which is an async JS function) + // Even if you use TS and have a queries.ts file, you will still need to import it using the .js extension. + // see here for more info: https://wasp-lang.dev/docs/tutorials/todo-app/03-listing-tasks#wasp-declaration + fn: import { getTasks } from "@server/queries.js", + // We tell Wasp that this query is doing something with the `Task` entity. With that, Wasp will + // automatically refresh the results of this query when tasks change. + entities: [Task] +} + +action createTask { + fn: import { createTask } from "@server/actions.js", + entities: [Task] +} + +action updateTask { + fn: import { updateTask } from "@server/actions.js", + entities: [Task] +} diff --git a/examples/todo-typescript/migrations/20221219114539_init/migration.sql b/examples/todo-typescript/migrations/20221219114539_init/migration.sql new file mode 100644 index 000000000..7b6262767 --- /dev/null +++ b/examples/todo-typescript/migrations/20221219114539_init/migration.sql @@ -0,0 +1,18 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "username" TEXT NOT NULL, + "password" TEXT NOT NULL +); + +-- CreateTable +CREATE TABLE "Task" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "description" TEXT NOT NULL, + "isDone" BOOLEAN NOT NULL DEFAULT false, + "userId" INTEGER, + CONSTRAINT "Task_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); diff --git a/examples/todo-typescript/migrations/migration_lock.toml b/examples/todo-typescript/migrations/migration_lock.toml new file mode 100644 index 000000000..e5e5c4705 --- /dev/null +++ b/examples/todo-typescript/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "sqlite" \ No newline at end of file diff --git a/examples/todo-typescript/src/.waspignore b/examples/todo-typescript/src/.waspignore new file mode 100644 index 000000000..1c432f30d --- /dev/null +++ b/examples/todo-typescript/src/.waspignore @@ -0,0 +1,3 @@ +# Ignore editor tmp files +**/*~ +**/#*# diff --git a/examples/todo-typescript/src/client/LoginPage.tsx b/examples/todo-typescript/src/client/LoginPage.tsx new file mode 100644 index 000000000..3243c3167 --- /dev/null +++ b/examples/todo-typescript/src/client/LoginPage.tsx @@ -0,0 +1,16 @@ +import { Link } from 'react-router-dom'; +import LoginForm from '@wasp/auth/forms/Login'; + +export function LoginPage() { + return ( +
+

Login

+ {/** Wasp has built-in auth forms & flows, which you can also opt-out of, if you wish :) */} + +
+ + I don't have an account yet (go to signup). + +
+ ); +}; diff --git a/examples/todo-typescript/src/client/Main.css b/examples/todo-typescript/src/client/Main.css new file mode 100644 index 000000000..5c086b1dc --- /dev/null +++ b/examples/todo-typescript/src/client/Main.css @@ -0,0 +1,53 @@ +* { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + box-sizing: border-box; +} + +main { + padding: 1rem 0; + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +h1 { + padding: 0; + margin: 1rem 0; +} + +main p { + font-size: 1rem; +} + +img { + max-height: 100px; +} + +button { + margin-top: 1rem; +} + +code { + border-radius: 5px; + padding: 0.2rem; + background: #efefef; + font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, + Bitstream Vera Sans Mono, Courier New, monospace; +} + +.auth-form h2 { + margin-top: 0.5rem; + font-size: 1.2rem; +} + +.tasklist { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: center; + width: 300px; + margin-top: 1rem; +} diff --git a/examples/todo-typescript/src/client/MainPage.tsx b/examples/todo-typescript/src/client/MainPage.tsx new file mode 100644 index 000000000..917036a27 --- /dev/null +++ b/examples/todo-typescript/src/client/MainPage.tsx @@ -0,0 +1,88 @@ +import './Main.css'; +import React, { useEffect, FormEventHandler, FormEvent } from 'react'; +import logout from '@wasp/auth/logout.js'; +import useAuth from '@wasp/auth/useAuth.js'; +import { useQuery } from '@wasp/queries'; // Wasp uses a thin wrapper around react-query +import getTasks from '@wasp/queries/getTasks'; +import createTask from '@wasp/actions/createTask'; +import updateTask from '@wasp/actions/updateTask'; +import waspLogo from './waspLogo.png'; +import { Task } from './types' + +export function MainPage() { + const { data: user } = useAuth(); + const { data: tasks, isLoading, error } = useQuery(getTasks); + + useEffect(() => { + console.log(user); + }, [user]); + + if (isLoading) return 'Loading...'; + if (error) return 'Error: ' + error; + + return ( +
+ wasp logo +

+ {user.username} + {`'s tasks :)`} +

+ + {tasks && } + +
+ ); +}; + +function Todo({ id, isDone, description }: Task) { + const handleIsDoneChange = async (event: FormEventHandler) => { + try { + await updateTask({ + taskId: id, + isDone: event.currentTarget.checked, + }); + } catch (err: any) { + window.alert('Error while updating task ' + err?.message); + } + }; + + return ( +
  • + + {description} +
  • + ); +}; + +function TasksList({tasks}: { tasks: Task[] }) { + if (tasks.length === 0) return

    No tasks yet.

    ; + return ( +
      + {tasks.map((tsk, idx) => ( + + ))} +
    + ); +}; + +function NewTaskForm() { + const handleSubmit = async (event: FormEvent) => { + event.preventDefault(); + + try { + const description = event.currentTarget.description.value; + console.log(description) + event.currentTarget.reset(); + await createTask({ description }); + } catch (err: any) { + window.alert('Error: ' + err?.message); + } + }; + + return ( +
    + + +
    + ); +}; diff --git a/examples/todo-typescript/src/client/SignupPage.tsx b/examples/todo-typescript/src/client/SignupPage.tsx new file mode 100644 index 000000000..fa237fcbf --- /dev/null +++ b/examples/todo-typescript/src/client/SignupPage.tsx @@ -0,0 +1,16 @@ +import { Link } from 'react-router-dom'; +import SignupForm from '@wasp/auth/forms/Signup'; + +export function SignupPage() { + return ( +
    +

    Sign Up

    + {/** Wasp has built-in auth forms & flows, which you can also opt-out of, if you wish :) */} + +
    + + I already have an account (go to login). + +
    + ); +}; diff --git a/examples/todo-typescript/src/client/react-app-env.d.ts b/examples/todo-typescript/src/client/react-app-env.d.ts new file mode 100644 index 000000000..e80934ce3 --- /dev/null +++ b/examples/todo-typescript/src/client/react-app-env.d.ts @@ -0,0 +1,60 @@ +declare module '*.avif' { + const src: string; + export default src; +} + +declare module '*.bmp' { + const src: string; + export default src; +} + +declare module '*.gif' { + const src: string; + export default src; +} + +declare module '*.jpg' { + const src: string; + export default src; +} + +declare module '*.jpeg' { + const src: string; + export default src; +} + +declare module '*.png' { + const src: string; + export default src; +} + +declare module '*.webp' { + const src: string; + export default src; +} + +declare module '*.svg' { + import * as React from 'react'; + + export const ReactComponent: React.FunctionComponent & { title?: string }>; + + const src: string; + export default src; +} + +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} + +declare module '*.module.scss' { + const classes: { readonly [key: string]: string }; + export default classes; +} + +declare module '*.module.sass' { + const classes: { readonly [key: string]: string }; + export default classes; +} diff --git a/examples/todo-typescript/src/client/tsconfig.json b/examples/todo-typescript/src/client/tsconfig.json new file mode 100644 index 000000000..d501a4193 --- /dev/null +++ b/examples/todo-typescript/src/client/tsconfig.json @@ -0,0 +1,55 @@ +// =============================== IMPORTANT ================================= +// +// This file is only used for Wasp IDE support. You can change it to configure +// your IDE checks, but none of these options will affect the TypeScript +// compiler. Proper TS compiler configuration in Wasp is coming soon :) +{ + "compilerOptions": { + // JSX support + "jsx": "preserve", + "strict": true, + // Allow default imports. + "esModuleInterop": true, + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + // Wasp needs the following settings enable IDE support in your source + // files. Editing them might break features like import autocompletion and + // definition lookup. Don't change them unless you know what you're doing. + // + // The relative path to the generated web app's root directory. This must be + // set to define the "paths" option. + "baseUrl": "../../.wasp/out/web-app/", + "paths": { + // Resolve all "@wasp" imports to the generated source code. + "@wasp/*": [ + "src/*" + ], + // Resolve all non-relative imports to the correct node module. Source: + // https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping + "*": [ + // Start by looking for the definiton inside the node modules root + // directory... + "node_modules/*", + // ... If that fails, try to find it inside definitely-typed type + // definitions. + "node_modules/@types/*" + ] + }, + // Correctly resolve types: https://www.typescriptlang.org/tsconfig#typeRoots + "typeRoots": [ + "../../.wasp/out/web-app/node_modules/@types" + ], + // Since this TS config is used only for IDE support and not for + // compilation, the following directory doesn't exist. We need to specify + // it to prevent this error: + // https://stackoverflow.com/questions/42609768/typescript-error-cannot-write-file-because-it-would-overwrite-input-file + "outDir": "phantom" + }, + "exclude": [ + "phantom" + ], +} \ No newline at end of file diff --git a/examples/todo-typescript/src/client/types.ts b/examples/todo-typescript/src/client/types.ts new file mode 100644 index 000000000..7739835bb --- /dev/null +++ b/examples/todo-typescript/src/client/types.ts @@ -0,0 +1,6 @@ +export type Task = { + id: number; + description: string; + isDone: boolean; + userId: number | null; +}; diff --git a/examples/todo-typescript/src/client/waspLogo.png b/examples/todo-typescript/src/client/waspLogo.png new file mode 100644 index 000000000..d39a9443a Binary files /dev/null and b/examples/todo-typescript/src/client/waspLogo.png differ diff --git a/examples/todo-typescript/src/server/actions.ts b/examples/todo-typescript/src/server/actions.ts new file mode 100644 index 000000000..9162db4ca --- /dev/null +++ b/examples/todo-typescript/src/server/actions.ts @@ -0,0 +1,34 @@ +import HttpError from '@wasp/core/HttpError.js'; +import { Context, Task } from './serverTypes' + +type CreateArgs = Pick; + +export async function createTask({ description }: CreateArgs, context: Context) { + if (!context.user) { + throw new HttpError(401); + } + + return context.entities.Task.create({ + data: { + description, + user: { connect: { id: context.user.id } }, + }, + }); +}; + +// type UpdateArgs = { taskId: Task['id']; isDone: Task['isDone'] }; +type UpdateArgs = Pick; + +export async function updateTask({ id, isDone }: UpdateArgs, context: Context) { + if (!context.user) { + throw new HttpError(401); + } + + return context.entities.Task.updateMany({ + where: { + id, + user: { id: context.user.id }, + }, + data: { isDone }, + }); +}; diff --git a/examples/todo-typescript/src/server/queries.ts b/examples/todo-typescript/src/server/queries.ts new file mode 100644 index 000000000..47a90cdf0 --- /dev/null +++ b/examples/todo-typescript/src/server/queries.ts @@ -0,0 +1,9 @@ +import HttpError from '@wasp/core/HttpError.js'; +import { Context, Task } from './serverTypes' + +export async function getTasks(args: unknown, context: Context): Promise { + if (!context.user) { + throw new HttpError(401); + } + return context.entities.Task.findMany({ where: { user: { id: context.user.id } } }); +}; diff --git a/examples/todo-typescript/src/server/serverTypes.ts b/examples/todo-typescript/src/server/serverTypes.ts new file mode 100644 index 000000000..3f1b705b6 --- /dev/null +++ b/examples/todo-typescript/src/server/serverTypes.ts @@ -0,0 +1,11 @@ +import { User, Prisma } from '@prisma/client'; + +export { Task } from '@prisma/client'; + +export type Context = { + user: User; + entities: { + Task: Prisma.TaskDelegate<{}>; + User: Prisma.UserDelegate<{}>; + }; +}; diff --git a/examples/todo-typescript/src/server/tsconfig.json b/examples/todo-typescript/src/server/tsconfig.json new file mode 100644 index 000000000..70a79b44e --- /dev/null +++ b/examples/todo-typescript/src/server/tsconfig.json @@ -0,0 +1,48 @@ +// =============================== IMPORTANT ================================= +// +// This file is only used for Wasp IDE support. You can change it to configure +// your IDE checks, but none of these options will affect the TypeScript +// compiler. Proper TS compiler configuration in Wasp is coming soon :) +{ + "compilerOptions": { + // Allows default imports. + "esModuleInterop": true, + "allowJs": true, + "strict": true, + // Wasp needs the following settings enable IDE support in your source + // files. Editing them might break features like import autocompletion and + // definition lookup. Don't change them unless you know what you're doing. + // + // The relative path to the generated web app's root directory. This must be + // set to define the "paths" option. + "baseUrl": "../../.wasp/out/server/", + "paths": { + // Resolve all "@wasp" imports to the generated source code. + "@wasp/*": [ + "src/*" + ], + // Resolve all non-relative imports to the correct node module. Source: + // https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping + "*": [ + // Start by looking for the definiton inside the node modules root + // directory... + "node_modules/*", + // ... If that fails, try to find it inside definitely-typed type + // definitions. + "node_modules/@types/*" + ] + }, + // Correctly resolve types: https://www.typescriptlang.org/tsconfig#typeRoots + "typeRoots": [ + "../../.wasp/out/server/node_modules/@types" + ], + // Since this TS config is used only for IDE support and not for + // compilation, the following directory doesn't exist. We need to specify + // it to prevent this error: + // https://stackoverflow.com/questions/42609768/typescript-error-cannot-write-file-because-it-would-overwrite-input-file + "outDir": "phantom", + }, + "exclude": [ + "phantom" + ], +} \ No newline at end of file diff --git a/examples/todo-typescript/src/shared/tsconfig.json b/examples/todo-typescript/src/shared/tsconfig.json new file mode 100644 index 000000000..f78b58a77 --- /dev/null +++ b/examples/todo-typescript/src/shared/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + // Enable default imports in TypeScript. + "esModuleInterop": true, + "allowJs": true, + // The following settings enable IDE support in user-provided source files. + // Editing them might break features like import autocompletion and + // definition lookup. Don't change them unless you know what you're doing. + // + // The relative path to the generated web app's root directory. This must be + // set to define the "paths" option. + "baseUrl": "../../.wasp/out/server/", + "paths": { + // Resolve all non-relative imports to the correct node module. Source: + // https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping + "*": [ + // Start by looking for the definiton inside the node modules root + // directory... + "node_modules/*", + // ... If that fails, try to find it inside definitely-typed type + // definitions. + "node_modules/@types/*" + ] + }, + // Correctly resolve types: https://www.typescriptlang.org/tsconfig#typeRoots + "typeRoots": ["../../.wasp/out/server/node_modules/@types"], + } +} diff --git a/web/blog/2022-11-26-erlis-amicus-usecase.md b/web/blog/2022-11-26-erlis-amicus-usecase.md index a36edeadc..fa616be07 100644 --- a/web/blog/2022-11-26-erlis-amicus-usecase.md +++ b/web/blog/2022-11-26-erlis-amicus-usecase.md @@ -16,9 +16,9 @@ import ImgWithCaption from './components/ImgWithCaption' ![amicus hero shot](../static/img/amicus-usecase/amicus-hero-shot.png) -[Erlis Kllogjri](https://github.com/ErlisK) is an engineer based in San Francisco with broad experience ranging from mechanical engineering and C/C++ microcontroller programming to Python and web app development. In his free time, Erlis enjoys working on side projects, which is also how Amicus started out. +[Erlis Kllogjri](https://github.com/ErlisK) is an engineer based in San Francisco with broad experience ranging from mechanical engineering and C/C++ microcontroller programming to Python and web app development. In his free time, Erlis enjoys working on side projects, which is also how [Amicus](https://www.amicus.work/) started out. -Amicus is a SaaS for legal teams - think about it as "Asana for lawyers", but with features and workflows tailored to the domain of law. +[Amicus](https://www.amicus.work/) is a SaaS for legal teams - think about it as "Asana for lawyers", but with features and workflows tailored to the domain of law. Read on to learn how long it took Erlis to develop the first version of his SaaS with Wasp, how he got his first paying customers, and what features he plans to add next! diff --git a/web/blog/2023-01-11-betathon-review.md b/web/blog/2023-01-11-betathon-review.md new file mode 100644 index 000000000..126d54531 --- /dev/null +++ b/web/blog/2023-01-11-betathon-review.md @@ -0,0 +1,132 @@ +--- +title: 'Hosting Our First Hackathon: Results & Review' +authors: [vinny] +tags: [fullstack, webdev, hackathon, startups] +--- + +import ImgWithCaption from './components/ImgWithCaption' + +To finalize the Wasp Beta launch week, we held a Beta Hackathon, which we dubbed the “Betathon”. The idea was to hold a simple, open, and fun hackathon to encourage users to build with Wasp, and that’s exactly what they did! + +As Wasp is still in its early days, we weren’t sure what the response would be, or if there’d be any response at all. Considering that we didn’t do much promotion of the Hackathon outside of our own channels, we were surprised by the results. + +In this post, I’ll give you a quick run-down of: + +- the hackathon results 🏆 +- how the hackathon was organized +- how we promoted it +- the community response + + +## …and the Winners Are: + +What’s a hackathon without the participants!? Let’s get this post off to a proper start by congratulating our winners and showcasing their work. 🔍 + + +### 🥇 Tim’s Job Board + + +Tim really went for it and created a feature-rich Job Board: + +- View the [App](https://client-production-54e7.up.railway.app/) & [GitHub Repo](https://github.com/tskaggs/wasp-jobs) +- Follow [Tim on Twitter](https://twitter.com/tskaggs) +- 🎉 Prizes: Wasp-colored Mechanical Keyboard, Wasp swag, $200 [Railway.app](http://Railway.app) credits + + +> “***Wasp is very awesome!*** *Easy setup and start-up especially if you're familiar with the Prisma ORM and Tailwind CSS. The stack is small but powerful... I'm going to use Wasp on a few MVP projects this year.”* - Tim +> + + +### 🥈Chris’s “Cook Wherever” Recipes App + + +Chris created an extensive database of recipes in a slick app: + +- View the [App](https://cookwherever.com) & [GitHub Repo](https://github.com/cookwherever/cookwherever) +- Follow [Chris on Twitter](https://twitter.com/breadchris) +- 🎉 Prizes: Wasp swag, $125 [Railway.app](http://Railway.app) credits + +> “***This was the best app dev experience I ever had!*** *…Walking through the docs, I immediately figured out how to use Wasp and was able to make a prototype in a couple of days.”* - Chris +> + + +### 🥉 Richard’s Roadmap & Feature Voting App + + +- View the [App](https://droad.netlify.app/) & [GitHub Repo](https://github.com/Fecony/droad) +- Follow [Richard on Twitter](https://twitter.com/webrickony) +- 🎉 Prizes: Wasp Shirt, $75 [Railway.app](http://Railway.app) credits + +> “***I liked how Wasp simplified writing query/actions*** *that are used to interact with the backend and frontend. How everything is defined and configured in wasp file and just works. Also […] login/signup was really easy to do since Wasp provides these two methods for use.”* - +> + + +### 🥉 Emmanuel’s Notes App + + +- View the [GitHub Repo](https://github.com/EmmanuelTheCoder/noteapp-with-wasp) +- Follow [Emmanuel on Twitter](https://twitter.com/EmmanuelCoder) +- 🎉 Prizes: Wasp Shirt, $75 [Railway.app](http://Railway.app) credits + +> *I joined the hackathon less than 48 hours before the submission deadline.* ***Wasp made it look easy because it handled the hard parts for me.*** *For example, username/password authentication took less than 7 lines of code to implement. -* excerpt from [Emmanuel’s Betathon Blog Post](https://dev.to/emmanuelthecoder/making-something-waspy-a-review-of-wasp-571j) +> + + +## Hackathon How-to + +Personally, I’ve never organized a hackathon before, and this was Wasp’s first hackathon as well, so when you’re a complete newbie at something, you often look towards others for inspiration. Being admirers of the work and style of Supabase, we drew a lot of inspiration from their “[launch week](https://supabase.com/blog/launch-week-5-hackathon)” approach when preparing for our own Beta launch and hacakthon. + + + +With some good inspiration in hand, we set off to create a simple, easy-going Hackathon experience. We weren’t certain we’d get many participants, so we decided to make the process as open as possible: *two weeks to work on any project using Wasp, alone or in a team of up to 4 people, submitted on our [Betathon Homepage](https://betathon.wasp-lang.dev/) before the deadline*. That was it. + +When you’re an early-stage startup, you can’t offer big cash prizes, so we asked Railway if they’d be interested in sponsoring some prizes, as we’re big fans of their deployment and hosting platform. Luckily, they agreed (thanks, Railway 🙏🚂). It was also a great match, since we already had the documentation for deploying Wasp apps to Railway on our website, making it an obvious choice for the participants to deploy their Hackathon apps with. + + + +On top of that, we decided that a cool grand prize could be a Wasp-colored mechanical keyboard. Nothing fancy, but keyboards are an item a lot of programmers love. We also threw in some Wasp beanies and shirts, and stated that we’d spotlight the winner’s on our platforms and social media accounts. + + +## Promotion + +For the Wasp Beta Launch Week, we were active and publicising Wasp on many platforms. We didn’t outright promote the hackathon on those platforms, but we were getting a lot of incoming interest to our Website and Discord, so we made noise about it there. We posted banners on the homepage, and made announcements on Discord and Twitter that directed people to a [Beta Hacakthon homepage](https://betathon.wasp-lang.dev) we created. + +The homepage was nice to have as a central spot for all the rules and relevant info. We also added a fun intro video to give the hackathon a more personal touch. I also think the effort put into making an intro video gives participants the feeling that they’re entering into a serious contest and committing to something of substance. + + + +As an extra bonus, we wrote the Betathon Homepage with Wasp, and put the [source code up on our GitHub](https://github.com/wasp-lang/wasp/tree/main/examples/hackathon). We thought it might inspire people to build with Wasp, using it as a guide while creating their own projects for the hackathon, plus it could be used by others in the future if they want to host their own hackathon. 💻 + +### The Response + +The response overall was small but significant, considering Wasp’s age. We were also extremely happy with the quality of the engagement. We had thirteen participants register overall, a nice number considering we only started promoting the hackathon on the day that we announced it (this is probably something we’d do differently next time)! + +We also asked participants for their feedback on participating in the Hackathon, and they were all pleased with the open, straight-forward approach we took, so we’ll most likely be repeating this for future versions. Other good signs were the many comments that participants were eager to take part in our next hackathon, as well as some dedicated new community members, which makes it all the more motivating for us. 💪 + +--- + +**A big THANK YOU again to all the participants for their hard work and feedback. Here’s to the next one! 🍻** \ No newline at end of file diff --git a/web/blog/components/ImgWithCaption.js b/web/blog/components/ImgWithCaption.js index 7bb2bf04d..5e9472b58 100644 --- a/web/blog/components/ImgWithCaption.js +++ b/web/blog/components/ImgWithCaption.js @@ -4,10 +4,12 @@ import useBaseUrl from "@docusaurus/useBaseUrl"; const ImgWithCaption = (props) => { return (
    -

    +

    - {props.alt} -
    {props.caption}
    + {props.alt} +
    + {props.caption} +

    diff --git a/web/docs/_addExternalAuthEnvVarsReminder.md b/web/docs/_addExternalAuthEnvVarsReminder.md new file mode 100644 index 000000000..a4ec07201 --- /dev/null +++ b/web/docs/_addExternalAuthEnvVarsReminder.md @@ -0,0 +1,5 @@ +:::tip Using an external auth method? + +if your app is using an external authentication method(s) supported by Wasp (such as [Google](/docs/language/features#google) or [GitHub](/docs/language/features#github)), make sure to set the necessary environment variables. + +::: diff --git a/web/docs/deploying.md b/web/docs/deploying.md index d1110c64a..c1cdc1eed 100644 --- a/web/docs/deploying.md +++ b/web/docs/deploying.md @@ -3,6 +3,8 @@ title: Deploying --- import useBaseUrl from '@docusaurus/useBaseUrl'; +import AddExternalAuthEnvVarsReminder from './_addExternalAuthEnvVarsReminder.md' + :::info Wasp is in beta, so keep in mind there might be some kinks / bugs, and possibly a bit bigger changes in the future. If you encounter any issues, reach out to us on [Discord](https://discord.gg/rzdnErX) and we will make sure to help you out! @@ -43,6 +45,8 @@ Server uses following environment variables, so you need to ensure they are set - `WASP_WEB_CLIENT_URL` -> The URL of where the frontend app is running (e.g. `https://.netlify.app`), which is necessary for CORS. - `JWT_SECRET` -> You need this if you are using Wasp's `auth` feature. Set it to a random string (password), at least 32 characters long. + + ### Deploying to Fly.io (free, recommended) Fly.io offers a variety of free services that are perfect for deploying your first Wasp app! You will need a Fly.io account and the [`flyctl` CLI](https://fly.io/docs/hands-on/install-flyctl/). @@ -99,8 +103,14 @@ Next, let's add a few more environment variables: flyctl secrets set PORT=8080 flyctl secrets set JWT_SECRET= flyctl secrets set WASP_WEB_CLIENT_URL= + +# If you are using an external auth method (Google or GitHub), make sure to add their vars too! +# flyctl secrets set GOOGLE_CLIENT_ID= +# flyctl secrets set GOOGLE_CLIENT_SECRET= ``` + + NOTE: If you do not know what your frontend URL is yet, don't worry. You can set `WASP_WEB_CLIENT_URL` after you deploy your frontend. If you want to make sure you've added your secrets correctly, run `flyctl secrets list` in the terminal. Note that you will see hashed versions of your secrets to protect your sensitive data. @@ -156,8 +166,13 @@ heroku create Unless you have external Postgres database that you want to use, let's create new database on Heroku and attach it to our app: ``` -heroku addons:create --app heroku-postgresql:hobby-dev +heroku addons:create --app heroku-postgresql:mini ``` +:::caution + +Heroku does not offer a free plan anymore and `mini` is their cheapest database instance - it costs $5/mo. + +::: Heroku will also set `DATABASE_URL` env var for us at this point. If you are using external database, you will have to set it yourself. @@ -375,6 +390,8 @@ Go to the server instance's `Settings` tab, and click `Generate Domain`. Do the The Postgres database is already initialized with a domain, so click on the Postgres instance, go to the **Connect** tab and copy the `Postgres Connection URL`. Go back to your `server` instance and navigate to its `Variables` tab. Now add the copied Postgres URL as `DATABASE_URL`, as well as the client's domain as `WASP_WEB_CLIENT_URL`. + + Next, copy the server's domain, move over to the client's `Variables` tab and add the generated server domain as a new variable called `REACT_APP_API_URL`. diff --git a/web/docusaurus.config.js b/web/docusaurus.config.js index 3c75f409f..c2be4e1e4 100644 --- a/web/docusaurus.config.js +++ b/web/docusaurus.config.js @@ -127,7 +127,7 @@ module.exports = { '@docusaurus/preset-classic', { gtag: { - trackingID: 'G-3ZEDH3BVGE', + trackingID: 'GTM-WJX89HZ', anonymizeIP: true, }, docs: { diff --git a/web/src/components/ExampleWaspApps.js b/web/src/components/ExampleWaspApps.js index 466049140..0036ab979 100644 --- a/web/src/components/ExampleWaspApps.js +++ b/web/src/components/ExampleWaspApps.js @@ -13,7 +13,7 @@ const examples = [ authorImg: 'https://avatars.githubusercontent.com/u/55102317', repoName: "waspello-example-app", repoUrl: "https://github.com/wasp-lang/wasp/tree/main/examples/waspello", - //demoUrl: "https://waspello.netlify.app/", + demoUrl: "https://waspello-demo.netlify.app/", // todo: try in GitPod/Replit url }, { @@ -122,9 +122,12 @@ const ExampleCard = (props) => (
    - {props.demoUrl && ( - - )} + {/* Demo apps are not mobile-friendly yet so hiding them on mobile for now. */} + + {props.demoUrl && ( + + )} +
    diff --git a/web/static/img/betathon/betathonpage.png b/web/static/img/betathon/betathonpage.png new file mode 100644 index 000000000..f99582811 Binary files /dev/null and b/web/static/img/betathon/betathonpage.png differ diff --git a/web/static/img/betathon/chris.png b/web/static/img/betathon/chris.png new file mode 100644 index 000000000..fa9419cc7 Binary files /dev/null and b/web/static/img/betathon/chris.png differ diff --git a/web/static/img/betathon/emmanuel.png b/web/static/img/betathon/emmanuel.png new file mode 100644 index 000000000..267ecc052 Binary files /dev/null and b/web/static/img/betathon/emmanuel.png differ diff --git a/web/static/img/betathon/github.png b/web/static/img/betathon/github.png new file mode 100644 index 000000000..2670a760b Binary files /dev/null and b/web/static/img/betathon/github.png differ diff --git a/web/static/img/betathon/keyboard.png b/web/static/img/betathon/keyboard.png new file mode 100644 index 000000000..ece5e5fb0 Binary files /dev/null and b/web/static/img/betathon/keyboard.png differ diff --git a/web/static/img/betathon/richard.png b/web/static/img/betathon/richard.png new file mode 100644 index 000000000..79db7a5cc Binary files /dev/null and b/web/static/img/betathon/richard.png differ diff --git a/web/static/img/betathon/tim.png b/web/static/img/betathon/tim.png new file mode 100644 index 000000000..26309367e Binary files /dev/null and b/web/static/img/betathon/tim.png differ