mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-12-29 20:12:28 +03:00
Add fun to the Wasp AI queue
This commit is contained in:
parent
3be17bdc9e
commit
0f4be2a6d5
95
wasp-ai/src/client/components/WaitingRoomContent.jsx
Normal file
95
wasp-ai/src/client/components/WaitingRoomContent.jsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -97,7 +97,7 @@ const MainPage = () => {
|
|||||||
id="appName"
|
id="appName"
|
||||||
required
|
required
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="e.g. TodoApp or FlowerShop"
|
placeholder="e.g. TodoApp or MyPlants"
|
||||||
value={appName}
|
value={appName}
|
||||||
onChange={(e) => setAppNameIfValid(e.target.value)}
|
onChange={(e) => setAppNameIfValid(e.target.value)}
|
||||||
disabled={currentStatus.status === "inProgress"}
|
disabled={currentStatus.status === "inProgress"}
|
||||||
@ -111,7 +111,7 @@ const MainPage = () => {
|
|||||||
id="appDesc"
|
id="appDesc"
|
||||||
required
|
required
|
||||||
placeholder="Describe your web app in a couple of sentences (check examples below).
|
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}
|
value={appDesc}
|
||||||
rows="5"
|
rows="5"
|
||||||
cols="50"
|
cols="50"
|
||||||
|
@ -11,6 +11,7 @@ import { useHistory } from "react-router-dom";
|
|||||||
import { Loader } from "../components/Loader";
|
import { Loader } from "../components/Loader";
|
||||||
import { MyDialog } from "../components/Dialog";
|
import { MyDialog } from "../components/Dialog";
|
||||||
import { Logs } from "../components/Logs";
|
import { Logs } from "../components/Logs";
|
||||||
|
import { WaitingRoomContent } from "../components/WaitingRoomContent";
|
||||||
import { Header } from "../components/Header";
|
import { Header } from "../components/Header";
|
||||||
import { Faq } from "../components/Faq";
|
import { Faq } from "../components/Faq";
|
||||||
import {
|
import {
|
||||||
@ -42,20 +43,6 @@ export const ResultPage = () => {
|
|||||||
const [isMobileFileBrowserOpen, setIsMobileFileBrowserOpen] = useState(false);
|
const [isMobileFileBrowserOpen, setIsMobileFileBrowserOpen] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
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) {
|
if (!appGenerationResult?.project) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -67,10 +54,7 @@ export const ResultPage = () => {
|
|||||||
) {
|
) {
|
||||||
setGenerationDone(true);
|
setGenerationDone(true);
|
||||||
}
|
}
|
||||||
setCurrentStatus({
|
setCurrentStatus(getStatusPillData(appGenerationResult));
|
||||||
status: backendStatusToPillStatus[appGenerationResult.project.status],
|
|
||||||
message: backendStatusToPillText[appGenerationResult.project.status],
|
|
||||||
});
|
|
||||||
}, [appGenerationResult, isError]);
|
}, [appGenerationResult, isError]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -230,8 +214,11 @@ export const ResultPage = () => {
|
|||||||
</header>
|
</header>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Logs logs={logs} status={currentStatus.status} onRetry={retry} />
|
<Logs logs={logs} status={currentStatus.status} onRetry={retry} />
|
||||||
|
{currentStatus.status === "pending" &&
|
||||||
|
appGenerationResult.numberOfProjectsAheadInQueue > 1 && (
|
||||||
|
<WaitingRoomContent />
|
||||||
|
)}
|
||||||
|
|
||||||
{interestingFilePaths.length > 0 && (
|
{interestingFilePaths.length > 0 && (
|
||||||
<>
|
<>
|
||||||
@ -319,6 +306,46 @@ export const ResultPage = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 }) {
|
export default function RunTheAppModal({ disabled, onDownloadZip }) {
|
||||||
const [showModal, setShowModal] = useState(false);
|
const [showModal, setShowModal] = useState(false);
|
||||||
return (
|
return (
|
||||||
@ -356,7 +383,8 @@ export default function RunTheAppModal({ disabled, onDownloadZip }) {
|
|||||||
>
|
>
|
||||||
Wasp
|
Wasp
|
||||||
</a>{" "}
|
</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>
|
</p>
|
||||||
|
|
||||||
<WarningAboutAI />
|
<WarningAboutAI />
|
||||||
@ -371,7 +399,10 @@ export default function RunTheAppModal({ disabled, onDownloadZip }) {
|
|||||||
curl -sSL https://get.wasp-lang.dev/installer.sh | sh
|
curl -sSL https://get.wasp-lang.dev/installer.sh | sh
|
||||||
</pre>
|
</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
|
<button
|
||||||
className="button flex items-center justify-center gap-1 w-full mt-2"
|
className="button flex items-center justify-center gap-1 w-full mt-2"
|
||||||
onClick={onDownloadZip}
|
onClick={onDownloadZip}
|
||||||
@ -380,11 +411,15 @@ export default function RunTheAppModal({ disabled, onDownloadZip }) {
|
|||||||
<PiDownloadDuotone className="inline-block" size={20} />
|
<PiDownloadDuotone className="inline-block" size={20} />
|
||||||
</button>
|
</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">
|
<pre className="mt-2 bg-slate-800 p-4 rounded-lg text-sm text-slate-200">
|
||||||
cd {"<your-app-name>"}
|
cd {"<your-app-name>"}
|
||||||
<br />
|
<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 />
|
<br />
|
||||||
wasp start
|
wasp start
|
||||||
</pre>
|
</pre>
|
||||||
@ -419,8 +454,9 @@ function WarningAboutAI() {
|
|||||||
<p className="text-sm leading-5 font-medium">⚠️ Experimental tech</p>
|
<p className="text-sm leading-5 font-medium">⚠️ Experimental tech</p>
|
||||||
<div className="mt-2 text-sm leading-5">
|
<div className="mt-2 text-sm leading-5">
|
||||||
<p>
|
<p>
|
||||||
Since this is a GPT generated app, it might contain some mistakes, proportional to how complex the app is.
|
Since this is a GPT generated app, it might contain some mistakes,
|
||||||
If there are some in your app, check out {" "}
|
proportional to how complex the app is. If there are some in your
|
||||||
|
app, check out{" "}
|
||||||
<a
|
<a
|
||||||
href="https://wasp-lang.dev/docs"
|
href="https://wasp-lang.dev/docs"
|
||||||
target="_blank"
|
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"
|
className="font-medium text-yellow-600 hover:text-yellow-500 transition ease-in-out duration-150 underline"
|
||||||
>
|
>
|
||||||
Wasp docs
|
Wasp docs
|
||||||
</a>
|
</a>{" "}
|
||||||
{" "}for help while fixing them, and also feel free to reach out to us on{" "}
|
for help while fixing them, and also feel free to reach out to us
|
||||||
|
on{" "}
|
||||||
<a
|
<a
|
||||||
href="https://discord.gg/rzdnErX"
|
href="https://discord.gg/rzdnErX"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
@ -66,8 +66,20 @@ export const getAppGenerationResult = (async (args, context) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const numberOfProjectsAheadInQueue =
|
||||||
|
(await Project.count({
|
||||||
|
where: {
|
||||||
|
createdAt: {
|
||||||
|
lt: project.createdAt,
|
||||||
|
},
|
||||||
|
status: "pending",
|
||||||
|
},
|
||||||
|
})) + 1;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
project,
|
project,
|
||||||
|
numberOfProjectsAheadInQueue,
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new HttpError(404, "App not found.");
|
throw new HttpError(404, "App not found.");
|
||||||
|
Loading…
Reference in New Issue
Block a user