Add fun to the Wasp AI queue

This commit is contained in:
Filip Sodić 2023-07-11 15:32:57 +02:00
parent 3be17bdc9e
commit 0f4be2a6d5
4 changed files with 175 additions and 31 deletions

View File

@ -0,0 +1,95 @@
import { availableColors } from "./Color";
import Tilt from "react-parallax-tilt";
export function WaitingRoomContent() {
return (
<>
<header className="relative mb-4 bg-slate-50 p-8 rounded-xl text-gray-500">
<p className="text-gray-500">Hello there! 🤖</p>
<br />
<p>
Thanks for trying out the AI web App Generator. Looks like it might be
a couple of minutes before we can start building your app. In the
meantime, you can take a look at some of our already generated apps
below.
</p>
<br />
<p>
Come back to this page to check the status of your app at any time!
</p>
</header>
<h3 class="text-xl font-semibold mb-4 text-slate-800">
Examples of already generated apps:
</h3>
<div className="flex flex-row items-stretch gap-x-10">
{showcaseSamples.map((sample) => (
<ShowcaseCard {...sample} />
))}
</div>
</>
);
}
export const showcaseSamples = [
{
name: "TodoApp",
specimenURL:
"https://magic-app-generator.wasp-lang.dev/result/07ed440a-3155-4969-b3f5-2031fb1f622f",
description:
"A simple todo app with one main page that lists all the tasks." +
" User can create new tasks by providing their description, toggle existing ones, or edit their description." +
" User owns tasks. User can only see and edit their own tasks. Tasks are saved in the database.",
color: availableColors.find((color) => color.name === "sky"),
complexity: "simple",
},
{
name: "MyPlants",
specimenURL:
"https://magic-app-generator.wasp-lang.dev/result/3bb5dca2-f134-4f96-89d6-0812deab6e0c",
description:
"An app where user can track their plants and their watering schedule." +
"\nHome page lists all of the user's plants. For each plant, number of days left until it needs to be watered is shown, as well as the plant's name, and a 'Water' button." +
" Home page also has a 'Add plant' button, that takes you to a new page where user can create a new plant." +
"\nWhen creating a new plant, user should give it a name and specify how often it needs to be watered (in the number of days)." +
" Plants are saved in the database. User can access only their own plants.",
color: availableColors.find((color) => color.name === "green"),
complexity: "moderate",
},
];
export function ShowcaseCard({ name, specimenURL, description, color }) {
return (
<Tilt
tiltMaxAngleX={10}
tiltMaxAngleY={10}
perspective={1000}
transitionSpeed={1000}
scale={1.05}
>
<div
className="h-full bg-slate-50 p-8 rounded-xl mt-2 flex flex-col items-center cursor-pointer hover:shadow-lg transition-all"
onClick={() => {
window.open(specimenURL, "_blank");
}}
>
<div className="idea">
<div className="flex justify-between items-center mb-4">
<h4 className="text-xl font-semibold text-slate-700 mb-1">
<span
className="inline-block w-4 h-4 rounded-full mr-2"
style={{ backgroundColor: color.color }}
></span>
{name}
</h4>
<button className="button sm gray">See how it looks</button>
</div>
<div className="text-base leading-relaxed text-slate-500 line-clamp-[10]">
{description.split("\n").map((str) => (
<p>{str}</p>
))}
</div>
</div>
</div>
</Tilt>
);
}

View File

@ -97,7 +97,7 @@ const MainPage = () => {
id="appName"
required
type="text"
placeholder="e.g. TodoApp or FlowerShop"
placeholder="e.g. TodoApp or MyPlants"
value={appName}
onChange={(e) => setAppNameIfValid(e.target.value)}
disabled={currentStatus.status === "inProgress"}
@ -111,7 +111,7 @@ const MainPage = () => {
id="appDesc"
required
placeholder="Describe your web app in a couple of sentences (check examples below).
Based on it, our AI code agent will then generate a full stack web app in Wasp, React, NodeJS and Prisma for you!"
Based on your description, our AI code agent will generate a full-stack web app in Wasp, React, NodeJS and Prisma!"
value={appDesc}
rows="5"
cols="50"

View File

@ -11,6 +11,7 @@ import { useHistory } from "react-router-dom";
import { Loader } from "../components/Loader";
import { MyDialog } from "../components/Dialog";
import { Logs } from "../components/Logs";
import { WaitingRoomContent } from "../components/WaitingRoomContent";
import { Header } from "../components/Header";
import { Faq } from "../components/Faq";
import {
@ -42,20 +43,6 @@ export const ResultPage = () => {
const [isMobileFileBrowserOpen, setIsMobileFileBrowserOpen] = useState(false);
useEffect(() => {
const backendStatusToPillStatus = {
pending: "pending",
"in-progress": "inProgress",
success: "success",
failure: "error",
cancelled: "cancelled",
};
const backendStatusToPillText = {
pending: "In the queue",
"in-progress": "Generating app",
success: "Finished",
failure: "There was an error",
cancelled: "The generation was cancelled",
};
if (!appGenerationResult?.project) {
return;
}
@ -67,10 +54,7 @@ export const ResultPage = () => {
) {
setGenerationDone(true);
}
setCurrentStatus({
status: backendStatusToPillStatus[appGenerationResult.project.status],
message: backendStatusToPillText[appGenerationResult.project.status],
});
setCurrentStatus(getStatusPillData(appGenerationResult));
}, [appGenerationResult, isError]);
useEffect(() => {
@ -230,8 +214,11 @@ export const ResultPage = () => {
</header>
</>
)}
<Logs logs={logs} status={currentStatus.status} onRetry={retry} />
{currentStatus.status === "pending" &&
appGenerationResult.numberOfProjectsAheadInQueue > 1 && (
<WaitingRoomContent />
)}
{interestingFilePaths.length > 0 && (
<>
@ -313,12 +300,52 @@ export const ResultPage = () => {
</>
)}
<div className="mt-8">
<Faq/>
<Faq />
</div>
</div>
);
};
function getStatusPillData(generationResult) {
const backendStatusToPillStatus = {
pending: "pending",
"in-progress": "inProgress",
success: "success",
failure: "error",
cancelled: "cancelled",
};
const queueCardinalNumber = getCardinalNumber(
generationResult.numberOfProjectsAheadInQueue
);
const backendStatusToPillText = {
pending: `${queueCardinalNumber} in the queue`,
"in-progress": "Generating app",
success: "Finished",
failure: "There was an error",
cancelled: "The generation was cancelled",
};
return {
status: backendStatusToPillStatus[generationResult.project.status],
message: backendStatusToPillText[generationResult.project.status],
};
}
function getCardinalNumber(number) {
const lastDigit = number % 10;
if (lastDigit === 1) {
return `${number}st`;
} else if (lastDigit === 2) {
return `${number}nd`;
} else if (lastDigit === 3) {
return `${number}rd`;
} else {
return `${number}th`;
}
}
export default function RunTheAppModal({ disabled, onDownloadZip }) {
const [showModal, setShowModal] = useState(false);
return (
@ -346,7 +373,7 @@ export default function RunTheAppModal({ disabled, onDownloadZip }) {
<div className="mt-6 space-y-6">
<p className="text-base leading-relaxed text-gray-500">
Congrats, your full-stack web app is ready! 🎉
<br/>
<br />
App is implemented in{" "}
<a
href="https://wasp-lang.dev"
@ -356,7 +383,8 @@ export default function RunTheAppModal({ disabled, onDownloadZip }) {
>
Wasp
</a>{" "}
web framework, using React, Node.js and Prisma, and is completely full-stack (frontend + backend + database).
web framework, using React, Node.js and Prisma, and is completely
full-stack (frontend + backend + database).
</p>
<WarningAboutAI />
@ -371,7 +399,10 @@ export default function RunTheAppModal({ disabled, onDownloadZip }) {
curl -sSL https://get.wasp-lang.dev/installer.sh | sh
</pre>
<h2 className="font-bold mt-4"> 2. Download the generated app files and unzip them: </h2>
<h2 className="font-bold mt-4">
{" "}
2. Download the generated app files and unzip them:{" "}
</h2>
<button
className="button flex items-center justify-center gap-1 w-full mt-2"
onClick={onDownloadZip}
@ -380,11 +411,15 @@ export default function RunTheAppModal({ disabled, onDownloadZip }) {
<PiDownloadDuotone className="inline-block" size={20} />
</button>
<h2 className="font-bold mt-4"> 3. Position into the unzipped dir and run the app: </h2>
<h2 className="font-bold mt-4">
{" "}
3. Position into the unzipped dir and run the app:{" "}
</h2>
<pre className="mt-2 bg-slate-800 p-4 rounded-lg text-sm text-slate-200">
cd {"<your-app-name>"}
<br />
wasp db migrate-dev <span className="text-slate-400"># init the db</span>
wasp db migrate-dev{" "}
<span className="text-slate-400"># init the db</span>
<br />
wasp start
</pre>
@ -419,8 +454,9 @@ function WarningAboutAI() {
<p className="text-sm leading-5 font-medium"> Experimental tech</p>
<div className="mt-2 text-sm leading-5">
<p>
Since this is a GPT generated app, it might contain some mistakes, proportional to how complex the app is.
If there are some in your app, check out {" "}
Since this is a GPT generated app, it might contain some mistakes,
proportional to how complex the app is. If there are some in your
app, check out{" "}
<a
href="https://wasp-lang.dev/docs"
target="_blank"
@ -428,8 +464,9 @@ function WarningAboutAI() {
className="font-medium text-yellow-600 hover:text-yellow-500 transition ease-in-out duration-150 underline"
>
Wasp docs
</a>
{" "}for help while fixing them, and also feel free to reach out to us on{" "}
</a>{" "}
for help while fixing them, and also feel free to reach out to us
on{" "}
<a
href="https://discord.gg/rzdnErX"
target="_blank"

View File

@ -66,8 +66,20 @@ export const getAppGenerationResult = (async (args, context) => {
},
},
});
const numberOfProjectsAheadInQueue =
(await Project.count({
where: {
createdAt: {
lt: project.createdAt,
},
status: "pending",
},
})) + 1;
return {
project,
numberOfProjectsAheadInQueue,
};
} catch (e) {
throw new HttpError(404, "App not found.");