Refactor file upload and toast notifications (#472)

* Refactor file upload and toast notifications

Replaces react-hot-toast with a custom toast system using @radix-ui/react-toast, updating all usages and adding new UI components for toast and dialog. Refactors file upload to use a two-step process: first generating an S3 upload URL, then adding the file to the database, and adds file deletion support with confirmation dialog and S3 cleanup. Updates Prisma schema, removes unused fields, and cleans up navigation and admin settings page.

* Enforce file upload limit and update dependencies on opensaas-sh

Added a check to restrict users to 2 file uploads in the demo, with a new helper function and error handling. Updated navigation items, improved landing page content, and removed unused dependencies (react-hot-toast). Added @radix-ui/react-toast, updated testimonials, and made minor content and code improvements.

* update tests

* Improve file deletion error handling and cleanup

Refactors file deletion to delete the database record before attempting S3 deletion, ensuring the file is removed from the app even if S3 deletion fails. Adds error logging for failed S3 deletions to aid in manual cleanup. Also simplifies error handling in the file upload page and removes unused imports in the demo app page.

* Add credit check and S3 file existence validation

Added logic to decrement user credits or throw an error if out of credits in the AI demo app. Updated file upload operations to validate file existence in S3 before adding to the database, and implemented S3 file existence check utility. Minor UI and code improvements included.

* Update s3Utils.ts

* update app_diff

* fix diff

* Update deletions

* Improve toast UI, error handling, and credit messaging

Updated toast action hover style and icon spacing for better UI consistency. Enhanced error handling in file deletion to display specific error messages. Refined credit/subscription error message in GPT response operation for clarity and removed redundant credit decrement logic.

* Refactor file upload validation and error handling

Replaces error state management with toast notifications for file upload errors and success. Refactors file type validation to use a new ALLOWED_FILE_TYPES_CONST and type AllowedFileTypes. Updates validation logic to throw errors instead of returning error objects, and simplifies type handling across file upload modules.

* Refactor file upload to use s3Key and add cleanup job

Replaces the 'key' field with 's3Key' for file storage references throughout the codebase and database schema. Updates all related logic, types, and API contracts to use 's3Key'. Adds a scheduled job to delete old files from S3 and the database. Cleans up file type validation constants and improves consistency in file upload and download operations.

* add orphaned file clean up

* remove s3 cleanup job from template

removed but added suggestion to docs.

* Update SettingsPage.tsx

* prettier format

* Update  UI, remove unused files

Updated README with deployment and demo details. Removed unused App.tsx and package-lock.json files. Modified Main.css, NavBar constants, file uploading logic, file upload operations, and landing page content sections for improved UI and functionality.

* remove pricing page from isMarketingPage
This commit is contained in:
vincanger
2025-10-15 12:01:08 +02:00
committed by GitHub
parent 57ae0bf5ba
commit 7d36c8f0b1
35 changed files with 2586 additions and 1351 deletions

View File

@@ -1,47 +1,62 @@
--- template/app/src/landing-page/contentSections.tsx
+++ opensaas-sh/app/src/landing-page/contentSections.tsx
@@ -0,0 +1,263 @@
@@ -1,4 +1,9 @@
-import daBoiAvatar from "../client/static/da-boi.webp";
+import { routes } from "wasp/client/router";
+import type { NavigationItem } from "../client/components/NavBar/NavBar";
+import blog from "../client/static/assets/blog.webp";
+import email from "../client/static/assets/email.webp";
+import fileupload from "../client/static/assets/fileupload.webp";
+import ai from "../client/static/assets/openapi.webp";
+import kivo from "../client/static/examples/kivo.webp";
+import messync from "../client/static/examples/messync.webp";
+import microinfluencerClub from "../client/static/examples/microinfluencers.webp";
+import promptpanda from "../client/static/examples/promptpanda.webp";
+import reviewradar from "../client/static/examples/reviewradar.webp";
+import scribeist from "../client/static/examples/scribeist.webp";
+import searchcraft from "../client/static/examples/searchcraft.webp";
import kivo from "../client/static/examples/kivo.webp";
import messync from "../client/static/examples/messync.webp";
import microinfluencerClub from "../client/static/examples/microinfluencers.webp";
@@ -6,161 +11,253 @@
import reviewradar from "../client/static/examples/reviewradar.webp";
import scribeist from "../client/static/examples/scribeist.webp";
import searchcraft from "../client/static/examples/searchcraft.webp";
-import { BlogUrl, DocsUrl } from "../shared/common";
-import type { GridFeature } from "./components/FeaturesGrid";
+import logo from "../client/static/logo.webp";
+import { BlogUrl, DocsUrl, GithubUrl, WaspUrl } from "../shared/common";
+import { GridFeature } from "./components/FeaturesGrid";
+
+export const landingPageNavigationItems: NavigationItem[] = [
+ { name: "Features", to: "#features" },
+ { name: "Documentation", to: DocsUrl },
+ { name: "Blog", to: BlogUrl },
+];
+export const features: GridFeature[] = [
+ {
export const features: GridFeature[] = [
{
- name: "Cool Feature 1",
- description: "Your feature",
- emoji: "🤝",
+ description:
+ "Have a sweet AI-powered app concept? Get your idea shipped to potential customers in days!",
+ icon: <img src={ai} alt="AI illustration" />,
+ href: DocsUrl,
href: DocsUrl,
- size: "small",
+ size: "medium",
+ fullWidthIcon: true,
+ align: "left",
+ },
+ {
},
{
- name: "Cool Feature 2",
- description: "Feature description",
- emoji: "🔐",
+ name: "Full-stack Type Safety",
+ description:
+ "Full support for TypeScript with auto-generated types that span the whole stack. Nothing to configure!",
+ emoji: "🥞",
+ href: DocsUrl,
href: DocsUrl,
- size: "small",
+ size: "medium",
+ },
+ {
},
{
- name: "Cool Feature 3",
- description: "Describe your cool feature here",
- emoji: "🥞",
- href: DocsUrl,
+ description:
+ "File upload examples with AWS S3 presigned URLs are included and fully documented!",
+ icon: (
@@ -52,10 +67,15 @@
+ />
+ ),
+ href: DocsUrl + "/guides/file-uploading/",
+ size: "medium",
size: "medium",
+ fullWidthIcon: true,
+ },
+ {
},
{
- name: "Cool Feature 4",
- description: "Describe your cool feature here",
- emoji: "💸",
- href: DocsUrl,
- size: "large",
+ name: "Email Sending",
+ description:
+ "Email sending built-in. Combine it with the cron jobs feature to easily send emails to your customers.",
@@ -64,16 +84,26 @@
+ size: "medium",
+ fullWidthIcon: true,
+ direction: "col-reverse",
+ },
+ {
},
{
- name: "Cool Feature 5",
- description: "Describe your cool feature here",
- emoji: "💼",
- href: DocsUrl,
- size: "large",
+ name: "Open SaaS",
+ description: "Try the demo app",
+ icon: <img src={logo} alt="Wasp Logo" />,
+ href: routes.LoginRoute.to,
+ size: "medium",
+ highlight: true,
+ },
+ {
},
{
- name: "Cool Feature 6",
- description: "It is cool",
- emoji: "📈",
- href: DocsUrl,
- size: "small",
+ name: "Blog w/ Astro",
+ description:
+ "Built-in blog with the Astro framework. Write your posts in Markdown, and watch your SEO performance take off.",
@@ -81,8 +111,11 @@
+ href: DocsUrl + "/start/guided-tour/",
+ size: "medium",
+ fullWidthIcon: true,
+ },
+ {
},
{
- name: "Cool Feature 7",
- description: "Cool feature",
- emoji: "📧",
+ name: "Deploy Anywhere. Easily.",
+ description:
+ "No vendor lock-in because you own all your code. Deploy yourself, or let Wasp deploy it for you with a single command.",
@@ -93,26 +126,40 @@
+ {
+ name: "Complete Documentation & Support",
+ description: "And a Discord community to help!",
+ href: DocsUrl,
+ size: "small",
+ },
+ {
href: DocsUrl,
size: "small",
},
{
- name: "Cool Feature 8",
- description: "Describe your cool feature here",
- emoji: "🤖",
- href: DocsUrl,
- size: "medium",
+ name: "E2E Tests w/ Playwright",
+ description: "Tests and a CI pipeline w/ GitHub Actions",
+ href: DocsUrl + "/guides/tests/",
+ size: "small",
+ },
+ {
},
{
- name: "Cool Feature 9",
- description: "Describe your cool feature here",
- emoji: "🚀",
+ name: "Open-Source Philosophy",
+ description:
+ "The repo and framework are 100% open-source, and so are the services wherever possible. Still missing something? Contribute!",
+ emoji: "🤝",
+ href: DocsUrl,
+ size: "medium",
+ },
+];
+export const testimonials = [
+ {
href: DocsUrl,
size: "medium",
},
];
-
export const testimonials = [
{
- name: "Da Boi",
- role: "Wasp Mascot",
- avatarSrc: daBoiAvatar,
- socialUrl: "https://twitter.com/wasplang",
- quote: "I don't even know how to code. I'm just a plushie.",
+ name: "Max Khamrovskyi",
+ role: "Senior Eng @ Red Hat",
+ avatarSrc:
@@ -120,8 +167,13 @@
+ socialUrl: "https://twitter.com/maksim36ua",
+ quote:
+ "I used Wasp to build and sell my AI-augmented SaaS app for marketplace vendors within two months!",
+ },
+ {
},
{
- name: "Mr. Foobar",
- role: "Founder @ Cool Startup",
- avatarSrc: daBoiAvatar,
- socialUrl: "",
- quote: "This product makes me cooler than I already am.",
+ name: "Jonathan Cocharan",
+ role: "Entrepreneur",
+ avatarSrc:
@@ -129,16 +181,21 @@
+ socialUrl: "https://twitter.com/JonathanCochran",
+ quote:
+ "In just 6 nights... my SaaS app is live 🎉! Huge thanks to the amazing @wasplang community 🙌 for their guidance along the way. These tools are incredibly efficient 🤯!",
+ },
+ {
},
{
- name: "Jamie",
- role: "Happy Customer",
- avatarSrc: daBoiAvatar,
- socialUrl: "#",
- quote: "My cats love it!",
+ name: "Billy Howell",
+ role: "Entrepreneur",
+ avatarSrc:
+ "https://pbs.twimg.com/profile_images/1877734205561430016/jjpG4mS6_400x400.jpg",
+ socialUrl: "https://twitter.com/billyjhowell",
+ quote:
+ "Congrats! I am loving Wasp. It's really helped me, a self-taught coder increase my confidence. I feel like I've finally found the perfect, versatile stack for all my projects instead of trying out a new one each time.",
+ },
+ "Congrats! I am loving Wasp & Open SaaS. It's really helped me, a self-taught coder increase my confidence. I feel like I've finally found the perfect, versatile stack for all my projects instead of trying out a new one each time.",
},
+ {
+ name: "Tim Skaggs",
+ role: "Founder @ Antler US",
@@ -193,14 +250,18 @@
+ quote:
+ "This is exactly the framework I've been dreaming of ever since I've been waiting to fully venture into the JS Backend Dev world. I believe Wasp will go above 50k stars this year. The documentation alone gives me the confidence that this is my permanent Nodejs framework and I'm staying with Wasp. Phenomenal work by the team... Please keep up your amazing spirits. Thank you",
+ },
+];
+export const faqs = [
+ {
+ id: 1,
];
-
export const faqs = [
{
id: 1,
- question: "Whats the meaning of life?",
- answer: "42.",
- href: "https://en.wikipedia.org/wiki/42_(number)",
+ question: "Why is this SaaS Template free and open-source?",
+ answer:
+ "We believe the best product is made when the community puts their heads together. We also believe a quality starting point for a web app should be free and available to everyone. Our hope is that together we can create the best SaaS template out there and bring our ideas to customers quickly.",
+ },
},
+ {
+ id: 2,
+ question: "What's Wasp?",
@@ -208,59 +269,92 @@
+ answer:
+ "It's the fastest way to develop full-stack React + NodeJS + Prisma apps and it's what gives this template superpowers. Wasp relies on React, NodeJS, and Prisma to define web components and server queries and actions. Wasp's secret sauce is its compiler which takes the client, server code, and config file and outputs the client app, server app and deployment code, supercharging the development experience. Combined with this template, you can build a SaaS app in record time.",
+ },
+];
+export const footerNavigation = {
+ app: [
];
-
export const footerNavigation = {
app: [
+ { name: "Github", href: GithubUrl },
+ { name: "Documentation", href: DocsUrl },
+ { name: "Blog", href: BlogUrl },
+ ],
+ company: [
{ name: "Documentation", href: DocsUrl },
{ name: "Blog", href: BlogUrl },
],
company: [
- { name: "About", href: "https://wasp.sh" },
- { name: "Privacy", href: "#" },
- { name: "Terms of Service", href: "#" },
+ { name: "Terms of Service", href: GithubUrl + "/blob/main/LICENSE" },
+ { name: "Made by the Wasp team = }", href: WaspUrl },
+ ],
+};
+export const examples = [
+ {
],
};
-
export const examples = [
{
- name: "Example #1",
- description: "Describe your example here.",
- imageSrc: kivo,
- href: "#",
+ name: "Microinfluencers",
+ description: "microinfluencer.club",
+ imageSrc: microinfluencerClub,
+ href: "https://microinfluencer.club",
+ },
+ {
},
{
- name: "Example #2",
- description: "Describe your example here.",
- imageSrc: messync,
- href: "#",
+ name: "Kivo",
+ description: "kivo.dev",
+ imageSrc: kivo,
+ href: "https://kivo.dev",
+ },
+ {
},
{
- name: "Example #3",
- description: "Describe your example here.",
- imageSrc: microinfluencerClub,
- href: "#",
+ name: "Searchcraft",
+ description: "searchcraft.io",
+ imageSrc: searchcraft,
+ href: "https://www.searchcraft.io",
+ },
+ {
},
{
- name: "Example #4",
- description: "Describe your example here.",
- imageSrc: promptpanda,
- href: "#",
+ name: "Scribeist",
+ description: "scribeist.com",
+ imageSrc: scribeist,
+ href: "https://scribeist.com",
+ },
+ {
},
{
- name: "Example #5",
- description: "Describe your example here.",
- imageSrc: reviewradar,
- href: "#",
+ name: "Messync",
+ description: "messync.com",
+ imageSrc: messync,
+ href: "https://messync.com",
+ },
+ {
},
{
- name: "Example #6",
- description: "Describe your example here.",
- imageSrc: scribeist,
- href: "#",
+ name: "Prompt Panda",
+ description: "promptpanda.io",
+ imageSrc: promptpanda,
+ href: "https://promptpanda.io",
+ },
+ {
},
{
- name: "Example #7",
- description: "Describe your example here.",
- imageSrc: searchcraft,
- href: "#",
+ name: "Review Radar",
+ description: "reviewradar.ai",
+ imageSrc: reviewradar,
+ href: "https://reviewradar.ai",
+ },
+];
},
];