-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[MAGE] add delete user option #1577
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"semi": true, | ||
"singleQuote": false, | ||
"trailingComma": "es5", | ||
"printWidth": 100 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
-- DropForeignKey | ||
ALTER TABLE "SocialLogin" DROP CONSTRAINT "SocialLogin_userId_fkey"; | ||
|
||
-- AddForeignKey | ||
ALTER TABLE "SocialLogin" ADD CONSTRAINT "SocialLogin_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,7 +42,11 @@ export const ResultPage = () => { | |
data: appGenerationResult, | ||
isError, | ||
isLoading, | ||
} = useQuery(getAppGenerationResult, { appId }, { enabled: !!appId && !generationDone, refetchInterval: 3000 }); | ||
} = useQuery( | ||
getAppGenerationResult, | ||
{ appId }, | ||
{ enabled: !!appId && !generationDone, refetchInterval: 3000 } | ||
); | ||
const [activeFilePath, setActiveFilePath] = useState(null); | ||
const [currentStatus, setCurrentStatus] = useState({ | ||
status: "idle", | ||
|
@@ -211,7 +215,10 @@ export const ResultPage = () => { | |
|
||
return ( | ||
<div className="container"> | ||
<Header currentStatus={currentStatus} StatusPill={!!appGenerationResult?.project && StatusPill}> | ||
<Header | ||
currentStatus={currentStatus} | ||
StatusPill={!!appGenerationResult?.project && StatusPill} | ||
> | ||
<FaqButton /> | ||
<HomeButton /> | ||
<ProfileButton /> | ||
|
@@ -225,7 +232,8 @@ export const ResultPage = () => { | |
{isError && ( | ||
<div className="mb-4 bg-red-50 p-8 rounded-xl"> | ||
<div className="text-red-500"> | ||
We couldn't find the app generation result. Maybe the link is incorrect or the app generation has failed. | ||
We couldn't find the app generation result. Maybe the link is incorrect or the app | ||
generation has failed. | ||
</div> | ||
<Link className="button gray sm mt-4 inline-block" to="/"> | ||
Generate a new one | ||
|
@@ -244,7 +252,16 @@ export const ResultPage = () => { | |
</> | ||
)} | ||
|
||
<Logs logs={logs} status={currentStatus.status} onRetry={retry} /> | ||
{appGenerationResult?.project.status.includes("deleted") ? ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This "deleted" here, would be great to have it imported from somewhere, so we don't have duplication. But this project in general is not very careful about this, so if all the other statuses are handled badly like this then we can postpone this refactoring to be refactored all together. |
||
<div className="flex flex-col items-center justify-center gap-1 mb-4 bg-red-50 text-gray-700 p-8 rounded-xl"> | ||
<span>This app has been deleted. </span> | ||
<Link className="underline sm inline-block" to="/"> | ||
← Go back and generate a new app | ||
</Link> | ||
</div> | ||
) : ( | ||
<Logs logs={logs} status={currentStatus.status} onRetry={retry} /> | ||
)} | ||
|
||
<div | ||
className="overflow-hidden | ||
|
@@ -264,31 +281,45 @@ export const ResultPage = () => { | |
onClick={() => window.open("https://github.com/wasp-lang/wasp/tree/wasp-ai")} | ||
> | ||
<span> | ||
🔮 This is a Wasp powered project. If you like it, <span className="underline">star us on GitHub</span>! | ||
🔮 This is a Wasp powered project. If you like it,{" "} | ||
<span className="underline">star us on GitHub</span>! | ||
</span> | ||
</span> | ||
</div> | ||
</div> | ||
|
||
{currentStatus.status === "pending" && ( | ||
<WaitingRoomContent numberOfProjectsAheadInQueue={appGenerationResult?.numberOfProjectsAheadInQueue || 0} /> | ||
<WaitingRoomContent | ||
numberOfProjectsAheadInQueue={appGenerationResult?.numberOfProjectsAheadInQueue || 0} | ||
/> | ||
)} | ||
|
||
{interestingFilePaths.length > 0 && ( | ||
<> | ||
<div className="mb-2 flex items-center justify-between"> | ||
<h2 className="text-xl font-bold text-gray-800">{appGenerationResult?.project?.name}</h2> | ||
<h2 className="text-xl font-bold text-gray-800"> | ||
{appGenerationResult?.project?.name} | ||
</h2> | ||
</div> | ||
<button className="button gray block w-full mb-4 md:hidden" onClick={toggleMobileFileBrowser}> | ||
{isMobileFileBrowserOpen ? "Close" : "Open"} file browser ({interestingFilePaths.length} files) | ||
<button | ||
className="button gray block w-full mb-4 md:hidden" | ||
onClick={toggleMobileFileBrowser} | ||
> | ||
{isMobileFileBrowserOpen ? "Close" : "Open"} file browser ({interestingFilePaths.length}{" "} | ||
files) | ||
</button> | ||
<div className="grid gap-4 md:grid-cols-[320px_1fr] mt-4 overflow-x-auto md:overflow-x-visible"> | ||
<aside className={isMobileFileBrowserOpen ? "" : "hidden md:block"}> | ||
<div className="mb-2"> | ||
<RunTheAppModal onDownloadZip={downloadZip} disabled={currentStatus.status !== "success"} /> | ||
<RunTheAppModal | ||
onDownloadZip={downloadZip} | ||
disabled={currentStatus.status !== "success"} | ||
/> | ||
</div> | ||
{currentStatus.status !== "success" && ( | ||
<small className="text-gray-500 text-center block my-2">The app is still being generated.</small> | ||
<small className="text-gray-500 text-center block my-2"> | ||
The app is still being generated. | ||
</small> | ||
)} | ||
<div> | ||
<ShareButton /> | ||
|
@@ -353,6 +384,7 @@ function getStatusPillData(generationResult) { | |
success: "success", | ||
failure: "error", | ||
cancelled: "cancelled", | ||
deleted: "deleted" | ||
}; | ||
|
||
const queueCardinalNumber = getCardinalNumber(generationResult.numberOfProjectsAheadInQueue); | ||
|
@@ -363,6 +395,7 @@ function getStatusPillData(generationResult) { | |
success: "Finished", | ||
failure: "There was an error", | ||
cancelled: "The generation was cancelled", | ||
deleted: "The project was deleted" | ||
}; | ||
|
||
return { | ||
|
@@ -392,12 +425,16 @@ export function OnSuccessModal({ isOpen, setIsOpen, appGenerationResult }) { | |
const logText = appGenerationResult?.project?.logs?.find((log) => | ||
log.content.includes("tokens usage") | ||
)?.content; | ||
const regex = /total tokens usage: ~(\d+(\.\d+)?)/i; | ||
const match = logText?.match(regex); | ||
if (match && match[1]) { | ||
const num = Number(match[1]) * 1000; | ||
if (Number.isInteger(num)) { | ||
setNumTokensSpent(num); | ||
|
||
if (logText) { | ||
const regex = /Total\s+tokens\s+usage\s*:\s*~\s*(\d+(?:\.\d+){0,1})\s*k\b/; | ||
const match = logText.match(regex); | ||
|
||
if (match) { | ||
const num = parseFloat(match[1]); | ||
setNumTokensSpent(num * 1000); | ||
} else { | ||
console.log("Failed to parse total number of tokens used: no regex match."); | ||
} | ||
} | ||
}, [appGenerationResult]); | ||
|
@@ -416,10 +453,15 @@ export function OnSuccessModal({ isOpen, setIsOpen, appGenerationResult }) { | |
} | ||
|
||
return ( | ||
<MyDialog isOpen={isOpen} onClose={() => setIsOpen(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 😇 | ||
We've made this tool completely <span className="font-semibold">free</span> and cover all | ||
the costs 😇 | ||
</p> | ||
{numTokensSpent > 0 && ( | ||
<table className="bg-slate-50 rounded-lg divide-y divide-gray-100 w-full text-base leading-relaxed text-gray-500 text-sm"> | ||
|
@@ -435,7 +477,9 @@ export function OnSuccessModal({ isOpen, setIsOpen, appGenerationResult }) { | |
<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>{" "} | ||
<FormattedText>{`$${((Number(numTokensSpent) / 1000) * 0.004).toFixed( | ||
2 | ||
)}`}</FormattedText>{" "} | ||
</td> | ||
</tr> | ||
{numTotalProjects && ( | ||
|
@@ -477,7 +521,9 @@ export default function RunTheAppModal({ disabled, onDownloadZip }) { | |
return ( | ||
<> | ||
<button | ||
className={`button flex items-center justify-center gap-1 w-full${!disabled ? " animate-jumping" : ""}`} | ||
className={`button flex items-center justify-center gap-1 w-full${ | ||
!disabled ? " animate-jumping" : "" | ||
}`} | ||
disabled={disabled} | ||
onClick={() => setShowModal(true)} | ||
> | ||
|
@@ -497,11 +543,16 @@ export default function RunTheAppModal({ disabled, onDownloadZip }) { | |
Congrats, your full-stack web app is ready! 🎉 | ||
<br /> | ||
App is implemented in{" "} | ||
<a href="https://wasp-lang.dev" target="_blank" rel="noopener noreferrer" className="underline"> | ||
<a | ||
href="https://wasp-lang.dev" | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
className="underline" | ||
> | ||
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 /> | ||
|
@@ -511,7 +562,11 @@ export default function RunTheAppModal({ disabled, onDownloadZip }) { | |
<div className="mt-6 bg-slate-100 rounded-lg p-4 text-base text-slate-800"> | ||
<h2 className="font-bold flex items-center space-x-1"> | ||
<span>1. Install Wasp CLI</span> | ||
<a href="https://wasp-lang.dev/docs/quick-start#installation-1" target="blank" rel="noopener noreferrer"> | ||
<a | ||
href="https://wasp-lang.dev/docs/quick-start#installation-1" | ||
target="blank" | ||
rel="noopener noreferrer" | ||
> | ||
{" "} | ||
<RxQuestionMarkCircled className="text-base" />{" "} | ||
</a> | ||
|
@@ -521,8 +576,14 @@ 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> | ||
<button className="button flex items-center justify-center gap-1 w-full mt-2" onClick={onDownloadZip}> | ||
<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} | ||
> | ||
Download ZIP <PiDownloadDuotone className="inline-block" size={20} /> | ||
</button> | ||
|
||
|
@@ -536,11 +597,17 @@ export default function RunTheAppModal({ disabled, onDownloadZip }) { | |
</pre> | ||
</div> | ||
|
||
<p className="text-base leading-relaxed text-gray-500">Congratulations, you are now running your app! 🎉</p> | ||
<p className="text-base leading-relaxed text-gray-500"> | ||
Congratulations, you are now running your app! 🎉 | ||
</p> | ||
|
||
<div className="bg-pink-50 text-pink-800 p-4 rounded"> | ||
If you like this project,{" "} | ||
<a href="https://github.com/wasp-lang/wasp" target="_blank" className="underline text-pink-600"> | ||
<a | ||
href="https://github.com/wasp-lang/wasp" | ||
target="_blank" | ||
className="underline text-pink-600" | ||
> | ||
star us on GitHub | ||
</a>{" "} | ||
⭐️ | ||
|
@@ -559,8 +626,8 @@ 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" | ||
|
@@ -578,8 +645,8 @@ function WarningAboutAI() { | |
> | ||
Discord | ||
</a> | ||
! You can also try generating the app again to get different results (try playing with the creativity | ||
level). | ||
! You can also try generating the app again to get different results (try playing with | ||
the creativity level). | ||
</p> | ||
</div> | ||
</div> | ||
|
@@ -632,17 +699,22 @@ function Feedback({ projectId }) { | |
> | ||
<form onSubmit={handleSubmit}> | ||
<label className="text-slate-700 block mb-2 mt-8"> | ||
How likely are you to recommend this tool to a friend? <span className="text-red-500">*</span> | ||
How likely are you to recommend this tool to a friend?{" "} | ||
<span className="text-red-500">*</span> | ||
</label> | ||
<div className="mx-auto w-full max-w-md"> | ||
<RadioGroup value={score} onChange={setScore}> | ||
<div className="flex space-x-2"> | ||
{scoreOptions.map((option) => ( | ||
<RadioGroup.Option value={option}> | ||
<RadioGroup.Option key={option} value={option}> | ||
{({ active, checked }) => ( | ||
<div | ||
className={` | ||
${active ? "ring-2 ring-white ring-opacity-60 ring-offset-2 ring-offset-sky-300" : ""} | ||
${ | ||
active | ||
? "ring-2 ring-white ring-opacity-60 ring-offset-2 ring-offset-sky-300" | ||
: "" | ||
} | ||
|
||
|
||
${checked ? "bg-sky-900 bg-opacity-75 text-white" : ""} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thought: once projects are deleted, we will never know they existed, so our number of created projects will become smaller. Would be cool if they only thing that is left is this idea of "there was a project that was deleted". But to be honest I am not sure myself how to implement that simply, so probably best to just ignore it, doesn't matter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this isnt for projects. when the user is deleted it deletes the social login entity, but leaves their projects in the DB.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine to leave the generated apps in the database in case they've been shared. there is no connection whatsoever to the user that created it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would still be good if they can delete their projects, since they were authored by them.
I would suggest on of the following:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok I've gone ahead and deleted all user-relevant info leaving just the project shell :)