mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-08-16 18:50:44 +03:00
check prev state for modal
This commit is contained in:
parent
124a3d4522
commit
3d25c4ae0b
@ -42,10 +42,6 @@ app waspAi {
|
||||
userEntity: User,
|
||||
externalAuthEntity: SocialLogin,
|
||||
methods: {
|
||||
gitHub: {
|
||||
configFn: import { getGitHubAuthConfig } from "@server/auth.js",
|
||||
getUserFieldsFn: import { getGitHubUserFields } from "@server/auth.js",
|
||||
},
|
||||
google: {
|
||||
configFn: import { getGoogleAuthConfig } from "@server/auth.js",
|
||||
getUserFieldsFn: import { getGoogleUserFields } from "@server/auth.js",
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { StatusPill } from './StatusPill';
|
||||
import { Title } from './Title';
|
||||
import { signInUrl as gitHubSignInUrl } from '@wasp/auth/helpers/GitHub';
|
||||
import { AiFillGithub } from 'react-icons/ai';
|
||||
|
||||
export function Header({ currentStatus, isStatusVisible }) {
|
||||
return (
|
||||
@ -14,12 +12,4 @@ export function Header({ currentStatus, isStatusVisible }) {
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function GithubLoginButton() {
|
||||
return (
|
||||
<button className='button gray flex !text-gray-800 hover:bg-slate-300 shadow-md' onClick={() => window.location.href = gitHubSignInUrl}>
|
||||
<AiFillGithub className='w-6 h-6 mr-2' /> Sign in with GitHub
|
||||
</button>
|
||||
)
|
||||
}
|
@ -14,7 +14,7 @@ import { MyDialog } from "../components/Dialog";
|
||||
const MainPage = () => {
|
||||
const [appName, setAppName] = useState("");
|
||||
const [appDesc, setAppDesc] = useState("");
|
||||
const [isGhModalOpen, setIsGhModalOpen] = useState(false);
|
||||
const [isPowerUserModalOpen, setIsPowerUserModalOpen] = useState(false);
|
||||
const [currentStatus, setCurrentStatus] = useState({
|
||||
status: "idle",
|
||||
message: "Waiting for instructions",
|
||||
@ -78,7 +78,7 @@ const MainPage = () => {
|
||||
localStorage.setItem("appNum", 0);
|
||||
}
|
||||
if (appNum === 2) {
|
||||
setIsGhModalOpen(true);
|
||||
setIsPowerUserModalOpen(true);
|
||||
}
|
||||
if (appDetails) {
|
||||
setAppName(appDetails.appName);
|
||||
@ -151,7 +151,7 @@ const MainPage = () => {
|
||||
<div className="container">
|
||||
<Header currentStatus={currentStatus} isStatusVisible={true} />
|
||||
|
||||
<GhModal isGhModalOpen={isGhModalOpen} setIsGhModalOpen={setIsGhModalOpen} />
|
||||
<PowerUserModal isOpen={isPowerUserModalOpen} setIsOpen={setIsPowerUserModalOpen} />
|
||||
|
||||
<form onSubmit={startGenerating} className="bg-slate-50 p-8 rounded-xl">
|
||||
<div className="mb-6 flex flex-col gap-3">
|
||||
@ -188,64 +188,38 @@ The simpler and more specific the app is, the better the generated app will be."
|
||||
</div>
|
||||
<div className="grid md:grid-cols-3 gap-3">
|
||||
<div>
|
||||
<label
|
||||
htmlFor="appPrimaryColor"
|
||||
className="text-slate-700 block mb-2"
|
||||
>
|
||||
<label htmlFor="appPrimaryColor" className="text-slate-700 block mb-2">
|
||||
App brand color
|
||||
</label>
|
||||
<MyDropdown
|
||||
value={appPrimaryColor}
|
||||
onChange={setAppPrimaryColor}
|
||||
options={availableColors}
|
||||
/>
|
||||
<MyDropdown value={appPrimaryColor} onChange={setAppPrimaryColor} options={availableColors} />
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="creativityLevel"
|
||||
className="text-slate-700 block mb-2"
|
||||
>
|
||||
<label htmlFor="creativityLevel" className="text-slate-700 block mb-2">
|
||||
Creativity level
|
||||
</label>
|
||||
<MyDropdown
|
||||
value={creativityLevel}
|
||||
onChange={setCreativityLevel}
|
||||
options={availableCreativityLevels}
|
||||
/>
|
||||
<MyDropdown value={creativityLevel} onChange={setCreativityLevel} options={availableCreativityLevels} />
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
htmlFor="appAuthMethod"
|
||||
className="text-slate-700 block mb-2"
|
||||
>
|
||||
<label htmlFor="appAuthMethod" className="text-slate-700 block mb-2">
|
||||
Auth method
|
||||
</label>
|
||||
<MyDropdown
|
||||
value={appAuthMethod}
|
||||
onChange={setAppAuthMethod}
|
||||
options={availableAuthMethods}
|
||||
/>
|
||||
<MyDropdown value={appAuthMethod} onChange={setAppAuthMethod} options={availableAuthMethods} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
className="button mr-2"
|
||||
disabled={currentStatus.status === "inProgress"}
|
||||
>
|
||||
<button className="button mr-2" disabled={currentStatus.status === "inProgress"}>
|
||||
Generate the app <PiMagicWandDuotone className="inline-block ml-1" />
|
||||
</button>
|
||||
</form>
|
||||
<div className="mt-8">
|
||||
<h3 className="text-xl font-semibold mb-4 text-slate-800">
|
||||
Some example ideas
|
||||
</h3>
|
||||
<h3 className="text-xl font-semibold mb-4 text-slate-800">Some example ideas</h3>
|
||||
<div className="grid grid-cols-1 gap-2 lg:grid-cols-3 lg:gap-4">
|
||||
{exampleIdeas.map((idea) => (
|
||||
<ExampleCard key={idea.name} idea={idea} useIdea={useIdea} />
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-8">
|
||||
<Faq/>
|
||||
<Faq />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -253,9 +227,9 @@ The simpler and more specific the app is, the better the generated app will be."
|
||||
};
|
||||
export default MainPage;
|
||||
|
||||
export function GhModal({ isGhModalOpen, setIsGhModalOpen }) {
|
||||
export function PowerUserModal({ isOpen, setIsOpen }) {
|
||||
return (
|
||||
<MyDialog isOpen={isGhModalOpen} onClose={() => setIsGhModalOpen(false)} title={<span>With Great Power Comes Great Responsibility! 🧙</span>}>
|
||||
<MyDialog isOpen={isOpen} onClose={() => setIsOpen(false)} title={<span>With Great Power Comes Great Responsibility! 🧙</span>}>
|
||||
<div className="mt-6 space-y-5">
|
||||
<p className="text-base leading-relaxed text-gray-500">
|
||||
We've made this tool completely <span className="font-semibold">free</span> and cover all the costs 😇
|
||||
|
@ -34,7 +34,8 @@ const jsConfetti = new JSConfetti();
|
||||
export const ResultPage = () => {
|
||||
const { appId } = useParams();
|
||||
const [generationDone, setGenerationDone] = useState(false);
|
||||
const [isStarRepoOpen, setIsStarRepoOpen] = useState(false);
|
||||
const [isRunning, setIsRunning] = useState(false);
|
||||
const [isSuccessModalOpen, setIsSuccessModalOpen] = useState(false);
|
||||
const {
|
||||
data: appGenerationResult,
|
||||
isError,
|
||||
@ -46,7 +47,7 @@ export const ResultPage = () => {
|
||||
);
|
||||
const [activeFilePath, setActiveFilePath] = useState(null);
|
||||
const [currentStatus, setCurrentStatus] = useState({
|
||||
status: "idle",
|
||||
status: "idle",
|
||||
message: "Waiting",
|
||||
});
|
||||
const [currentFiles, setCurrentFiles] = useState({});
|
||||
@ -73,10 +74,16 @@ export const ResultPage = () => {
|
||||
}, [appId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentStatus.status === "success") {
|
||||
setIsStarRepoOpen(true);
|
||||
const currentAppStatus = appGenerationResult?.project?.status;
|
||||
if (currentAppStatus === "in-progress") {
|
||||
setIsRunning(true)
|
||||
} else if (currentAppStatus === "success" && isRunning) {
|
||||
setIsSuccessModalOpen(true)
|
||||
setIsRunning(false)
|
||||
} else if (isRunning) {
|
||||
setIsRunning(false)
|
||||
}
|
||||
}, [currentStatus])
|
||||
}, [appGenerationResult?.project?.status])
|
||||
|
||||
const logs = appGenerationResult?.project?.logs.map((log) => log.content);
|
||||
|
||||
@ -217,7 +224,7 @@ export const ResultPage = () => {
|
||||
currentStatus={currentStatus}
|
||||
isStatusVisible={!!appGenerationResult?.project}
|
||||
/>
|
||||
<StarOurRepoModal isStarRepoOpen={isStarRepoOpen} setIsStarRepoOpen={setIsStarRepoOpen} appGenerationResult={appGenerationResult}/>
|
||||
<OnSuccessModal isOpen={isSuccessModalOpen} setIsOpen={setIsSuccessModalOpen} appGenerationResult={appGenerationResult}/>
|
||||
|
||||
{isError && (
|
||||
<div className="mb-4 bg-red-50 p-8 rounded-xl">
|
||||
@ -406,59 +413,71 @@ function getCardinalNumber(number) {
|
||||
}
|
||||
}
|
||||
|
||||
export function StarOurRepoModal({ isStarRepoOpen, setIsStarRepoOpen, appGenerationResult }) {
|
||||
const [tokens, setTokens] = useState(0)
|
||||
const { data: numTotalProjects } = useQuery(getNumProjects, {}, { enabled: isStarRepoOpen })
|
||||
|
||||
const tokenNumberStr = appGenerationResult?.project?.logs?.filter(log => log.content.toLowerCase().includes("tokens usage") === true)[0]?.content.split(':')[1]?.trim()
|
||||
export function OnSuccessModal({ isOpen, setIsOpen, appGenerationResult }) {
|
||||
const [numTokensSpent, setNumTokensSpent] = useState(0)
|
||||
const { data: numTotalProjects } = useQuery(getNumProjects, {}, { enabled: isOpen })
|
||||
|
||||
useEffect(() => {
|
||||
if (!!tokenNumberStr) {
|
||||
const num = tokenNumberStr.slice(1, -1) * 1000
|
||||
setTokens(num)
|
||||
const logText = appGenerationResult?.project?.logs?.find((log) => /tokens usage/i.test(log.content))?.content;
|
||||
const tokenNumberStr = logText ? logText.split(":")[1]?.trim() : null;
|
||||
if (tokenNumberStr) {
|
||||
const num = Number(tokenNumberStr.replace(/[\s~k]/gi, '')) * 1000;
|
||||
if (num.toString().match(/^[0-9]+$/)) {
|
||||
setNumTokensSpent(num);
|
||||
}
|
||||
}
|
||||
}, [tokenNumberStr])
|
||||
}, [appGenerationResult]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isStarRepoOpen) {
|
||||
if (isOpen) {
|
||||
jsConfetti.addConfetti({
|
||||
emojis: ['🐝'],
|
||||
emojiSize: 120,
|
||||
})
|
||||
}
|
||||
}, [isStarRepoOpen])
|
||||
}, [isOpen])
|
||||
|
||||
function FormattedText({ children }) {
|
||||
return <span className="py-1 px-2 font-semibold text-pink-800 rounded">{children}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<MyDialog isOpen={isStarRepoOpen} onClose={() => setIsStarRepoOpen(false)} title={<span>Your App is Ready! 🎉</span>}>
|
||||
<MyDialog isOpen={isOpen} onClose={() => setIsOpen(false)} title={<span>Your App is Ready! 🎉</span>}>
|
||||
<div className="mt-6 space-y-5">
|
||||
<p className="text-base leading-relaxed text-gray-500">
|
||||
We've made this tool completely <span className="font-semibold">free</span> and cover all the costs 😇
|
||||
</p>
|
||||
<table className="bg-slate-50 rounded-lg divide-y divide-gray-100 w-full text-base leading-relaxed text-gray-500 text-sm">
|
||||
{/* <caption>Fun Stats</caption> */}
|
||||
<tr>
|
||||
<td className="p-2 text-gray-600"> Number of tokens your app used: </td>
|
||||
<td className="p-2 text-gray-600">
|
||||
{" "}
|
||||
<FormattedText>{tokens.toLocaleString(2) ?? "~22k"}</FormattedText>{" "}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="p-2 text-gray-600"> Cost to generate your app: </td>
|
||||
<td className="p-2 text-gray-600">
|
||||
{" "}
|
||||
<FormattedText>{tokens ? `$${((tokens / 1000) * 0.004).toFixed(2)}` : "~ $0.50"}</FormattedText>{" "}
|
||||
</td>
|
||||
</tr>
|
||||
{numTotalProjects && (
|
||||
<tr className="p-2 text-gray-600">
|
||||
<td className="p-2 text-gray-600"> Total number of apps generated with Mage: </td>
|
||||
<td className="p-2 text-gray-600">
|
||||
{" "}
|
||||
<FormattedText>{numTotalProjects.toLocaleString()}</FormattedText>{" "}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</table>
|
||||
{numTokensSpent && (
|
||||
<table className="bg-slate-50 rounded-lg divide-y divide-gray-100 w-full text-base leading-relaxed text-gray-500 text-sm">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="p-2 text-gray-600"> Number of tokens your app used: </td>
|
||||
<td className="p-2 text-gray-600">
|
||||
{" "}
|
||||
<FormattedText>{numTokensSpent.toLocaleString()}</FormattedText>{" "}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="p-2 text-gray-600"> Cost to generate your app: </td>
|
||||
<td className="p-2 text-gray-600">
|
||||
{" "}
|
||||
<FormattedText>
|
||||
{`$${((Number(numTokensSpent) / 1000) * 0.004).toFixed(2)}`}
|
||||
</FormattedText>{" "}
|
||||
</td>
|
||||
</tr>
|
||||
{numTotalProjects && (
|
||||
<tr className="p-2 text-gray-600">
|
||||
<td className="p-2 text-gray-600"> Total number of apps generated with Mage: </td>
|
||||
<td className="p-2 text-gray-600">
|
||||
{" "}
|
||||
<FormattedText>{numTotalProjects.toLocaleString()}</FormattedText>{" "}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
<p className="text-base leading-relaxed text-gray-500">
|
||||
But you can still show your support by starring us on GitHub:
|
||||
</p>
|
||||
@ -478,15 +497,9 @@ export function StarOurRepoModal({ isStarRepoOpen, setIsStarRepoOpen, appGenerat
|
||||
<p className="text-base leading-relaxed text-gray-500">We'd very much appreciate it! 🧙</p>
|
||||
</div>
|
||||
</MyDialog>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function FormattedText({ children }) {
|
||||
return (
|
||||
<span className='py-1 px-2 font-semibold text-pink-800 rounded'>{children}</span>
|
||||
)
|
||||
}
|
||||
|
||||
export default function RunTheAppModal({ disabled, onDownloadZip }) {
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
return (
|
||||
|
@ -13,19 +13,3 @@ export const getGoogleAuthConfig = () => {
|
||||
scope: ["profile", "email"],
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
export const getGitHubUserFields: GetUserFieldsFn = async (_context, args) => {
|
||||
// NOTE: if we don't want to access users' emails, we can use scope ["user:read"]
|
||||
// instead of ["user"] and access args.profile.username instead
|
||||
const email = args.profile.emails[0].value;
|
||||
return { email };
|
||||
};
|
||||
|
||||
export function getGitHubAuthConfig() {
|
||||
return {
|
||||
clientID: process.env.GITHUB_CLIENT_ID, // look up from env or elsewhere
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET, // look up from env or elsewhere
|
||||
scope: ["user"],
|
||||
};
|
||||
}
|
@ -225,9 +225,7 @@ export const getStats = (async (_args, context) => {
|
||||
}) satisfies GetStats<{}>;
|
||||
|
||||
export const getNumProjects = (async (_args, context) => {
|
||||
const { Project } = context.entities;
|
||||
const numProjects = await Project.count();
|
||||
return numProjects;
|
||||
return context.entities.Project.count();
|
||||
}) satisfies GetNumProjects<{}>;
|
||||
|
||||
function getDownloadStats(projects: Project[]) {
|
||||
|
Loading…
Reference in New Issue
Block a user