Use webp over png and lazy load images (#314)
* use webp and lazy load images * fix hero * update app_diff * fix app_diff * fix head * remove async decoding
@ -1,6 +1,6 @@
|
|||||||
public/public-banner.png
|
src/client/static/avatar-placeholder.webp
|
||||||
src/client/static/avatar-placeholder.png
|
src/client/static/da-boi.webp
|
||||||
src/client/static/open-saas-banner.png
|
src/client/static/open-saas-banner.webp
|
||||||
src/landing-page/logos/SalesforceLogo.tsx
|
src/landing-page/logos/SalesforceLogo.tsx
|
||||||
src/payment/lemonSqueezy/checkoutUtils.ts
|
src/payment/lemonSqueezy/checkoutUtils.ts
|
||||||
src/payment/lemonSqueezy/paymentDetails.ts
|
src/payment/lemonSqueezy/paymentDetails.ts
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
--- template/app/main.wasp
|
--- template/app/main.wasp
|
||||||
+++ opensaas-sh/app/main.wasp
|
+++ opensaas-sh/app/main.wasp
|
||||||
@@ -3,24 +3,24 @@
|
@@ -3,30 +3,30 @@
|
||||||
version: "^0.15.0"
|
version: "^0.15.0"
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -8,35 +8,47 @@
|
|||||||
+ title: "Open SaaS",
|
+ title: "Open SaaS",
|
||||||
|
|
||||||
head: [
|
head: [
|
||||||
"<meta property='og:type' content='website' />",
|
"<meta charset='utf-8' />",
|
||||||
- "<meta property='og:title' content='My Open SaaS App' />",
|
- "<meta name='description' content='Your apps main description and features.' />",
|
||||||
+ "<meta property='og:title' content='Open SaaS' />",
|
- "<meta name='author' content='Your (App) Name' />",
|
||||||
"<meta property='og:url' content='https://opensaas.sh' />",
|
- "<meta name='keywords' content='saas, solution, product, app, service' />",
|
||||||
- "<meta property='og:description' content='I made a SaaS App. Buy my stuff.' />",
|
-
|
||||||
- "<meta property='og:image' content='https://opensaas.sh/public-banner.png' />",
|
+ "<meta name='description' content='Build and launch your SaaS application faster with our free, open-source starter kit. Features include auth, payments, AI example app, and admin dashboard.' />",
|
||||||
- "<meta name='twitter:image' content='https://opensaas.sh/public-banner.png' />",
|
+ "<meta name='author' content='Open SaaS' />",
|
||||||
+ "<meta property='og:description' content='Free, open-source SaaS boilerplate starter for React & NodeJS.' />",
|
+ "<meta name='keywords' content='saas, starter, boilerplate, free, open source, authentication, payments' />",
|
||||||
+ "<meta property='og:image' content='https://opensaas.sh/banner.png' />",
|
|
||||||
+
|
+
|
||||||
+ "<meta name=\"twitter:title\" content=\"Open SaaS\" />",
|
+ "<meta property='og:site_name' content='Open SaaS' />",
|
||||||
+ "<meta name=\"twitter:text:title\" content=\"Open SaaS\" />",
|
"<meta property='og:type' content='website' />",
|
||||||
+ "<meta name='twitter:image' content='https://opensaas.sh/banner.png' />",
|
- "<meta property='og:title' content='Your Open SaaS App' />",
|
||||||
+ "<meta name=\"twitter:image:alt\" content=\"Open SaaS\" />",
|
- "<meta property='og:site_name' content='Your Open SaaS App' />",
|
||||||
"<meta name='twitter:image:width' content='800' />",
|
- "<meta property='og:url' content='https://your-saas-app.com' />",
|
||||||
"<meta name='twitter:image:height' content='400' />",
|
- "<meta property='og:description' content='Your apps main description and features.' />",
|
||||||
"<meta name='twitter:card' content='summary_large_image' />",
|
- "<meta property='og:image' content='https://your-saas-app.com/public-banner.webp' />",
|
||||||
- // TODO: You can put your Plausible analytics scripts below (https://docs.opensaas.sh/guides/analytics/):
|
- "<meta name='twitter:image' content='https://your-saas-app.com/public-banner.webp' />",
|
||||||
- // NOTE: Plausible does not use Cookies, so you can simply add the scripts here.
|
+ "<meta property='og:title' content='Open SaaS' />",
|
||||||
- // Google, on the other hand, does, so you must instead add the script dynamically
|
+ "<meta property='og:url' content='https://opensaas.sh' />",
|
||||||
- // via the Cookie Consent component after the user clicks the "Accept" cookies button.
|
+ "<meta property='og:description' content='Free, open-source SaaS boilerplate starter for React & NodeJS.' />",
|
||||||
- "<script defer data-domain='<your-site-id>' src='https://plausible.io/js/script.js'></script>", // for production
|
+ "<meta property='og:image' content='https://opensaas.sh/public-banner.webp' />",
|
||||||
- "<script defer data-domain='<your-site-id>' src='https://plausible.io/js/script.local.js'></script>", // for development
|
+
|
||||||
+ "<script defer data-domain='opensaas.sh' src='https://plausible.apps.twoducks.dev/js/script.js'></script>",
|
+ "<meta name=\"twitter:title\" content=\"Open SaaS\" />",
|
||||||
+ "<script defer data-domain='opensaas.sh' src='https://plausible.apps.twoducks.dev/js/script.local.js'></script>",
|
+ "<meta name=\"twitter:text:title\" content=\"Open SaaS\" />",
|
||||||
|
+ "<meta name='twitter:image' content='https://opensaas.sh/public-banner.webp' />",
|
||||||
|
+ "<meta name=\"twitter:image:alt\" content=\"Open SaaS\" />",
|
||||||
|
"<meta name='twitter:image:width' content='800' />",
|
||||||
|
"<meta name='twitter:image:height' content='400' />",
|
||||||
|
"<meta name='twitter:card' content='summary_large_image' />",
|
||||||
|
- // TODO: You can put your Plausible analytics scripts below (https://docs.opensaas.sh/guides/analytics/):
|
||||||
|
- // NOTE: Plausible does not use Cookies, so you can simply add the scripts here.
|
||||||
|
- // Google, on the other hand, does, so you must instead add the script dynamically
|
||||||
|
- // via the Cookie Consent component after the user clicks the "Accept" cookies button.
|
||||||
|
- "<script defer data-domain='<your-site-id>' src='https://plausible.io/js/script.js'></script>", // for production
|
||||||
|
- "<script defer data-domain='<your-site-id>' src='https://plausible.io/js/script.local.js'></script>", // for development
|
||||||
|
+ "<script defer data-domain='opensaas.sh' src='https://plausible.apps.twoducks.dev/js/script.js'></script>",
|
||||||
|
+ "<script defer data-domain='opensaas.sh' src='https://plausible.apps.twoducks.dev/js/script.local.js'></script>",
|
||||||
],
|
],
|
||||||
|
|
||||||
// 🔐 Auth out of the box! https://wasp-lang.dev/docs/auth/overview
|
// 🔐 Auth out of the box! https://wasp-lang.dev/docs/auth/overview
|
||||||
@@ -32,7 +32,7 @@
|
@@ -38,7 +38,7 @@
|
||||||
email: {
|
email: {
|
||||||
fromField: {
|
fromField: {
|
||||||
name: "Open SaaS App",
|
name: "Open SaaS App",
|
||||||
@ -45,7 +57,7 @@
|
|||||||
},
|
},
|
||||||
emailVerification: {
|
emailVerification: {
|
||||||
clientRoute: EmailVerificationRoute,
|
clientRoute: EmailVerificationRoute,
|
||||||
@@ -44,21 +44,18 @@
|
@@ -50,21 +50,18 @@
|
||||||
},
|
},
|
||||||
userSignupFields: import { getEmailUserFields } from "@src/auth/userSignupFields",
|
userSignupFields: import { getEmailUserFields } from "@src/auth/userSignupFields",
|
||||||
},
|
},
|
||||||
@ -79,7 +91,7 @@
|
|||||||
},
|
},
|
||||||
onAfterSignup: import { onAfterSignup } from "@src/auth/hooks",
|
onAfterSignup: import { onAfterSignup } from "@src/auth/hooks",
|
||||||
onAuthFailedRedirectTo: "/login",
|
onAuthFailedRedirectTo: "/login",
|
||||||
@@ -81,11 +78,11 @@
|
@@ -87,11 +84,11 @@
|
||||||
// NOTE: "Dummy" provider is just for local development purposes.
|
// NOTE: "Dummy" provider is just for local development purposes.
|
||||||
// Make sure to check the server logs for the email confirmation url (it will not be sent to an address)!
|
// Make sure to check the server logs for the email confirmation url (it will not be sent to an address)!
|
||||||
// Once you are ready for production, switch to e.g. "SendGrid" or "Mailgun" providers. Check out https://docs.opensaas.sh/guides/email-sending/ .
|
// Once you are ready for production, switch to e.g. "SendGrid" or "Mailgun" providers. Check out https://docs.opensaas.sh/guides/email-sending/ .
|
||||||
@ -93,7 +105,7 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -206,9 +203,9 @@
|
@@ -212,9 +209,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
api paymentsWebhook {
|
api paymentsWebhook {
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
--- template/app/migrations/20241031103046_remove_checkout_session_id/migration.sql
|
||||||
|
+++ opensaas-sh/app/migrations/20241031103046_remove_checkout_session_id/migration.sql
|
||||||
|
@@ -0,0 +1,8 @@
|
||||||
|
+/*
|
||||||
|
+ Warnings:
|
||||||
|
+
|
||||||
|
+ - You are about to drop the column `checkoutSessionId` on the `User` table. All the data in the column will be lost.
|
||||||
|
+
|
||||||
|
+*/
|
||||||
|
+-- AlterTable
|
||||||
|
+ALTER TABLE "User" DROP COLUMN "checkoutSessionId";
|
@ -1,7 +1,7 @@
|
|||||||
--- template/app/src/landing-page/components/Clients.tsx
|
--- template/app/src/landing-page/components/Clients.tsx
|
||||||
+++ opensaas-sh/app/src/landing-page/components/Clients.tsx
|
+++ opensaas-sh/app/src/landing-page/components/Clients.tsx
|
||||||
@@ -1,23 +1,64 @@
|
@@ -1,23 +1,64 @@
|
||||||
+import logo from '../../client/static/logo.png';
|
+import logo from '../../client/static/logo.webp';
|
||||||
import AstroLogo from "../logos/AstroLogo";
|
import AstroLogo from "../logos/AstroLogo";
|
||||||
-import OpenAILogo from "../logos/OpenAILogo";
|
-import OpenAILogo from "../logos/OpenAILogo";
|
||||||
import PrismaLogo from "../logos/PrismaLogo";
|
import PrismaLogo from "../logos/PrismaLogo";
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
>
|
>
|
||||||
<NavLogo />
|
<NavLogo />
|
||||||
- <span className='ml-2 text-sm font-semibold leading-6 dark:text-white'>Your Saas</span>
|
- <span className='ml-2 text-sm font-semibold leading-6 dark:text-white'>Your Saas</span>
|
||||||
+ <span className='ml-2 text-sm font-semibold leading-6 dark:text-white'>Open Saas</span>
|
+ <span className='ml-2 text-sm font-semibold leading-6 dark:text-white'>Open SaaS</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex lg:hidden'>
|
<div className='flex lg:hidden'>
|
||||||
@ -47,6 +47,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
|
@@ -78,7 +79,7 @@
|
||||||
|
<Dialog.Panel className='fixed inset-y-0 right-0 z-50 w-full overflow-y-auto bg-white px-6 py-6 sm:max-w-sm sm:ring-1 sm:ring-gray-900/10 dark:bg-boxdark dark:text-white'>
|
||||||
|
<div className='flex items-center justify-between'>
|
||||||
|
<a href='/' className='-m-1.5 p-1.5'>
|
||||||
|
- <span className='sr-only'>Your SaaS</span>
|
||||||
|
+ <span className='sr-only'>Open SaaS</span>
|
||||||
|
<NavLogo />
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
@@ -107,8 +108,8 @@
|
@@ -107,8 +108,8 @@
|
||||||
<div className='py-6'>
|
<div className='py-6'>
|
||||||
{isUserLoading ? null : !user ? (
|
{isUserLoading ? null : !user ? (
|
||||||
@ -58,13 +67,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
@@ -125,3 +126,27 @@
|
@@ -125,3 +126,26 @@
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
+
|
+
|
||||||
+const ContestURL =
|
+const ContestURL = 'https://x.com/WaspLang';
|
||||||
+ 'https://docs.opensaas.sh/blog/';
|
|
||||||
+
|
+
|
||||||
+function Announcement() {
|
+function Announcement() {
|
||||||
+ return (
|
+ return (
|
||||||
@ -75,7 +83,7 @@
|
|||||||
+ onClick={() => window.open(ContestURL, '_blank')}
|
+ onClick={() => window.open(ContestURL, '_blank')}
|
||||||
+ className='hidden lg:block cursor-pointer rounded-full bg-neutral-700 px-2.5 py-1 text-xs hover:bg-neutral-600 tracking-wider'
|
+ className='hidden lg:block cursor-pointer rounded-full bg-neutral-700 px-2.5 py-1 text-xs hover:bg-neutral-600 tracking-wider'
|
||||||
+ >
|
+ >
|
||||||
+ Enter here and win prizes! →
|
+ Vote for the winner here! →
|
||||||
+ </div>
|
+ </div>
|
||||||
+ <div
|
+ <div
|
||||||
+ onClick={() => window.open(ContestURL, '_blank')}
|
+ onClick={() => window.open(ContestURL, '_blank')}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
--- template/app/src/landing-page/components/Hero.tsx
|
--- template/app/src/landing-page/components/Hero.tsx
|
||||||
+++ opensaas-sh/app/src/landing-page/components/Hero.tsx
|
+++ opensaas-sh/app/src/landing-page/components/Hero.tsx
|
||||||
@@ -1,7 +1,25 @@
|
@@ -1,7 +1,25 @@
|
||||||
-import openSaasBanner from '../../client/static/open-saas-banner.png';
|
-import openSaasBannerWebp from '../../client/static/open-saas-banner.webp';
|
||||||
-import { DocsUrl } from '../../shared/common';
|
-import { DocsUrl } from '../../shared/common';
|
||||||
+import { useState, useEffect } from 'react';
|
+import { useState, useEffect } from 'react';
|
||||||
+import { AiFillGithub } from 'react-icons/ai';
|
+import { AiFillGithub } from 'react-icons/ai';
|
||||||
@ -26,9 +26,9 @@
|
|||||||
+ }, []);
|
+ }, []);
|
||||||
+
|
+
|
||||||
return (
|
return (
|
||||||
<div className='relative pt-14 w-full '>
|
<div className='relative pt-14 w-full'>
|
||||||
<div
|
<TopGradient />
|
||||||
@@ -29,30 +47,47 @@
|
@@ -9,31 +27,47 @@
|
||||||
<div className='py-24 sm:py-32'>
|
<div className='py-24 sm:py-32'>
|
||||||
<div className='mx-auto max-w-8xl px-6 lg:px-8'>
|
<div className='mx-auto max-w-8xl px-6 lg:px-8'>
|
||||||
<div className='lg:mb-18 mx-auto max-w-3xl text-center'>
|
<div className='lg:mb-18 mx-auto max-w-3xl text-center'>
|
||||||
@ -69,15 +69,17 @@
|
|||||||
+ </a>
|
+ </a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-14 flow-root sm:mt-14 '>
|
- <div className='mt-14 flow-root sm:mt-14'>
|
||||||
- <div className='-m-2 rounded-xl lg:-m-4 lg:rounded-2xl lg:p-4'>
|
- <div className='-m-2 flex justify-center rounded-xl lg:-m-4 lg:rounded-2xl lg:p-4'>
|
||||||
- <img
|
- <img
|
||||||
- src={openSaasBanner}
|
- src={openSaasBannerWebp}
|
||||||
- alt='App screenshot'
|
- alt='App screenshot'
|
||||||
- width={2432}
|
- width={1000}
|
||||||
- height={1442}
|
- height={530}
|
||||||
|
- loading='lazy'
|
||||||
- className='rounded-md shadow-2xl ring-1 ring-gray-900/10'
|
- className='rounded-md shadow-2xl ring-1 ring-gray-900/10'
|
||||||
- />
|
- />
|
||||||
|
+ <div className='mt-14 flow-root sm:mt-14 '>
|
||||||
+ <div className='-m-2 mx-auto rounded-xl lg:-m-4 lg:rounded-2xl lg:p-4'>
|
+ <div className='-m-2 mx-auto rounded-xl lg:-m-4 lg:rounded-2xl lg:p-4'>
|
||||||
+ <iframe
|
+ <iframe
|
||||||
+ className=' mx-auto w-full md:w-[85%] aspect-[4/3] shadow-2xl'
|
+ className=' mx-auto w-full md:w-[85%] aspect-[4/3] shadow-2xl'
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
+++ opensaas-sh/app/src/landing-page/contentSections.ts
|
+++ opensaas-sh/app/src/landing-page/contentSections.ts
|
||||||
@@ -1,74 +1,131 @@
|
@@ -1,74 +1,131 @@
|
||||||
-import { DocsUrl, BlogUrl } from '../shared/common';
|
-import { DocsUrl, BlogUrl } from '../shared/common';
|
||||||
-import daBoiAvatar from '../client/static/da-boi.png';
|
-import daBoiAvatar from '../client/static/da-boi.webp';
|
||||||
-import avatarPlaceholder from '../client/static/avatar-placeholder.png';
|
-import avatarPlaceholder from '../client/static/avatar-placeholder.webp';
|
||||||
-import { routes } from 'wasp/client/router';
|
-import { routes } from 'wasp/client/router';
|
||||||
+import { DocsUrl, BlogUrl, GithubUrl } from '../shared/common';
|
+import { DocsUrl, BlogUrl, GithubUrl } from '../shared/common';
|
||||||
|
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
--- template/app/tailwind.config.cjs
|
--- template/app/tailwind.config.cjs
|
||||||
+++ opensaas-sh/app/tailwind.config.cjs
|
+++ opensaas-sh/app/tailwind.config.cjs
|
||||||
@@ -8,6 +8,7 @@
|
@@ -8,7 +8,8 @@
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
|
- satoshi: ['Satoshi', 'system-ui', 'sans-serif'],
|
||||||
+ sans: ['ui-monospace', 'Liberation Mono', 'Menlo', 'monospace'],
|
+ sans: ['ui-monospace', 'Liberation Mono', 'Menlo', 'monospace'],
|
||||||
satoshi: ['Satoshi', 'sans-serif'],
|
+ satoshi: ['Satoshi', 'sans-serif'],
|
||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
|
current: 'currentColor',
|
||||||
@@ -246,6 +247,9 @@
|
@@ -246,6 +247,9 @@
|
||||||
'spin-2': 'spin 2s linear infinite',
|
'spin-2': 'spin 2s linear infinite',
|
||||||
'spin-3': 'spin 3s linear infinite',
|
'spin-3': 'spin 3s linear infinite',
|
||||||
|
@ -14,7 +14,7 @@ export default defineConfig({
|
|||||||
description: 'Open SaaS is a free, open-source, full-stack SaaS starter kit for React + NodeJS.',
|
description: 'Open SaaS is a free, open-source, full-stack SaaS starter kit for React + NodeJS.',
|
||||||
customCss: ['./src/styles/tailwind.css'],
|
customCss: ['./src/styles/tailwind.css'],
|
||||||
logo: {
|
logo: {
|
||||||
src: '/src/assets/logo.png',
|
src: '/src/assets/logo.webp',
|
||||||
alt: 'Open SaaS',
|
alt: 'Open SaaS',
|
||||||
},
|
},
|
||||||
head: [
|
head: [
|
||||||
|
Before Width: | Height: | Size: 24 KiB |
BIN
opensaas-sh/blog/src/assets/logo.webp
Normal file
After Width: | Height: | Size: 13 KiB |
@ -22,7 +22,7 @@ app SaaSTemplate {
|
|||||||
"<meta property='og:url' content='https://opensaas.sh' />",
|
"<meta property='og:url' content='https://opensaas.sh' />",
|
||||||
"<meta property='og:title' content='Open SaaS' />",
|
"<meta property='og:title' content='Open SaaS' />",
|
||||||
"<meta property='og:description' content='Free, open-source SaaS boilerplate starter for React & NodeJS.' />",
|
"<meta property='og:description' content='Free, open-source SaaS boilerplate starter for React & NodeJS.' />",
|
||||||
"<meta property='og:image' content='https://opensaas.sh/public-banner.png' />",
|
"<meta property='og:image' content='https://opensaas.sh/public-banner.webp' />",
|
||||||
//...
|
//...
|
||||||
],
|
],
|
||||||
//...
|
//...
|
||||||
|
@ -60,7 +60,7 @@ If you are using an older version of the OpenSaaS template with Wasp `v0.13.x` o
|
|||||||
.
|
.
|
||||||
├── main.wasp # Wasp Config file. You define your app structure here.
|
├── main.wasp # Wasp Config file. You define your app structure here.
|
||||||
├── .wasp/ # Output dir for Wasp. DON'T MODIFY THESE FILES!
|
├── .wasp/ # Output dir for Wasp. DON'T MODIFY THESE FILES!
|
||||||
├── public/ # Public assets dir, e.g. www.yourdomain.com/banner.png
|
├── public/ # Public assets dir, e.g. www.yourdomain.com/public-banner.webp
|
||||||
├── src/ # Your code goes here.
|
├── src/ # Your code goes here.
|
||||||
│ ├── admin/ # Admin dashboard related pages and components.
|
│ ├── admin/ # Admin dashboard related pages and components.
|
||||||
│ ├── analytics/ # Logic and background jobs for processing analytics.
|
│ ├── analytics/ # Logic and background jobs for processing analytics.
|
||||||
@ -320,11 +320,11 @@ But before you start setting up the main features, let's walk through the custom
|
|||||||
|
|
||||||
#### Customizing the Look / Style of the App
|
#### Customizing the Look / Style of the App
|
||||||
- [ ] Update your favicon at `public/favicon.ico`.
|
- [ ] Update your favicon at `public/favicon.ico`.
|
||||||
- [ ] Update the banner image used when posting links to your site at `public/public-banner.png`.
|
- [ ] Update the banner image used when posting links to your site at `public/public-banner.webp`.
|
||||||
- [ ] Update the URL for this banner at `og:image` and `twitter:image` in `app.head` of the `main.wasp` file.
|
- [ ] Update the URL for this banner at `og:image` and `twitter:image` in `app.head` of the `main.wasp` file.
|
||||||
- [ ] Make changes to your landing page, `landingPage.tsx`.
|
- [ ] Make changes to your landing page, `landingPage.tsx`.
|
||||||
- [ ] Customize the `navBar`, `features`, `testimonials`, and `faqs` in the `contentSections.ts` file.
|
- [ ] Customize the `navBar`, `features`, `testimonials`, and `faqs` in the `contentSections.ts` file.
|
||||||
- [ ] Change/rename the `logo.png` and main banner (`open-saas-banner.png`) in the `static` folder.
|
- [ ] Change/rename the `logo.webp` and main hero banner (`open-saas-banner.webp`) in the `static` folder.
|
||||||
- [ ] If you want to make changes to the global styles of the app, you can do so in `tailwind.config.cjs`. **Be aware that the current custom global styles defined already are mostly used in the app's Admin Dashboard!**
|
- [ ] If you want to make changes to the global styles of the app, you can do so in `tailwind.config.cjs`. **Be aware that the current custom global styles defined already are mostly used in the app's Admin Dashboard!**
|
||||||
|
|
||||||
#### Customizing the Analytics & Admin Dashboard
|
#### Customizing the Analytics & Admin Dashboard
|
||||||
|
@ -6,21 +6,27 @@ app OpenSaaS {
|
|||||||
title: "My Open SaaS App",
|
title: "My Open SaaS App",
|
||||||
|
|
||||||
head: [
|
head: [
|
||||||
"<meta property='og:type' content='website' />",
|
"<meta charset='utf-8' />",
|
||||||
"<meta property='og:title' content='My Open SaaS App' />",
|
"<meta name='description' content='Your apps main description and features.' />",
|
||||||
"<meta property='og:url' content='https://opensaas.sh' />",
|
"<meta name='author' content='Your (App) Name' />",
|
||||||
"<meta property='og:description' content='I made a SaaS App. Buy my stuff.' />",
|
"<meta name='keywords' content='saas, solution, product, app, service' />",
|
||||||
"<meta property='og:image' content='https://opensaas.sh/public-banner.png' />",
|
|
||||||
"<meta name='twitter:image' content='https://opensaas.sh/public-banner.png' />",
|
"<meta property='og:type' content='website' />",
|
||||||
"<meta name='twitter:image:width' content='800' />",
|
"<meta property='og:title' content='Your Open SaaS App' />",
|
||||||
"<meta name='twitter:image:height' content='400' />",
|
"<meta property='og:site_name' content='Your Open SaaS App' />",
|
||||||
"<meta name='twitter:card' content='summary_large_image' />",
|
"<meta property='og:url' content='https://your-saas-app.com' />",
|
||||||
// TODO: You can put your Plausible analytics scripts below (https://docs.opensaas.sh/guides/analytics/):
|
"<meta property='og:description' content='Your apps main description and features.' />",
|
||||||
// NOTE: Plausible does not use Cookies, so you can simply add the scripts here.
|
"<meta property='og:image' content='https://your-saas-app.com/public-banner.webp' />",
|
||||||
// Google, on the other hand, does, so you must instead add the script dynamically
|
"<meta name='twitter:image' content='https://your-saas-app.com/public-banner.webp' />",
|
||||||
// via the Cookie Consent component after the user clicks the "Accept" cookies button.
|
"<meta name='twitter:image:width' content='800' />",
|
||||||
"<script defer data-domain='<your-site-id>' src='https://plausible.io/js/script.js'></script>", // for production
|
"<meta name='twitter:image:height' content='400' />",
|
||||||
"<script defer data-domain='<your-site-id>' src='https://plausible.io/js/script.local.js'></script>", // for development
|
"<meta name='twitter:card' content='summary_large_image' />",
|
||||||
|
// TODO: You can put your Plausible analytics scripts below (https://docs.opensaas.sh/guides/analytics/):
|
||||||
|
// NOTE: Plausible does not use Cookies, so you can simply add the scripts here.
|
||||||
|
// Google, on the other hand, does, so you must instead add the script dynamically
|
||||||
|
// via the Cookie Consent component after the user clicks the "Accept" cookies button.
|
||||||
|
"<script defer data-domain='<your-site-id>' src='https://plausible.io/js/script.js'></script>", // for production
|
||||||
|
"<script defer data-domain='<your-site-id>' src='https://plausible.io/js/script.local.js'></script>", // for development
|
||||||
],
|
],
|
||||||
|
|
||||||
// 🔐 Auth out of the box! https://wasp-lang.dev/docs/auth/overview
|
// 🔐 Auth out of the box! https://wasp-lang.dev/docs/auth/overview
|
||||||
|
Before Width: | Height: | Size: 840 KiB |
Before Width: | Height: | Size: 246 KiB |
BIN
template/app/public/public-banner.webp
Normal file
After Width: | Height: | Size: 145 KiB |
@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { NavLink, useLocation } from 'react-router-dom';
|
import { NavLink, useLocation } from 'react-router-dom';
|
||||||
import Logo from '../../client/static/logo.png';
|
import Logo from '../../client/static/logo.webp';
|
||||||
import SidebarLinkGroup from './SidebarLinkGroup';
|
import SidebarLinkGroup from './SidebarLinkGroup';
|
||||||
import { cn } from '../../client/cn';
|
import { cn } from '../../client/cn';
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import { updateCurrentUser } from 'wasp/client/operations';
|
|||||||
import './Main.css';
|
import './Main.css';
|
||||||
import AppNavBar from './components/AppNavBar';
|
import AppNavBar from './components/AppNavBar';
|
||||||
import CookieConsentBanner from './components/cookie-consent/Banner';
|
import CookieConsentBanner from './components/cookie-consent/Banner';
|
||||||
import { useMemo, useEffect, ReactNode } from 'react';
|
import { useMemo, useEffect } from 'react';
|
||||||
import { Outlet, useLocation } from 'react-router-dom';
|
import { Outlet, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
src: url('/fonts/Satoshi-Regular.woff2') format('woff2');
|
src: url('/fonts/Satoshi-Regular.woff2') format('woff2');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* third-party libraries CSS */
|
/* third-party libraries CSS */
|
||||||
|
@ -5,7 +5,7 @@ import { Dialog } from '@headlessui/react';
|
|||||||
import { BiLogIn } from 'react-icons/bi';
|
import { BiLogIn } from 'react-icons/bi';
|
||||||
import { AiFillCloseCircle } from 'react-icons/ai';
|
import { AiFillCloseCircle } from 'react-icons/ai';
|
||||||
import { HiBars3 } from 'react-icons/hi2';
|
import { HiBars3 } from 'react-icons/hi2';
|
||||||
import logo from '../static/logo.png';
|
import logo from '../static/logo.webp';
|
||||||
import DropdownUser from '../../user/DropdownUser';
|
import DropdownUser from '../../user/DropdownUser';
|
||||||
import { UserMenuItems } from '../../user/UserMenuItems';
|
import { UserMenuItems } from '../../user/UserMenuItems';
|
||||||
import { DocsUrl, BlogUrl } from '../../shared/common';
|
import { DocsUrl, BlogUrl } from '../../shared/common';
|
||||||
|
Before Width: | Height: | Size: 9.2 KiB |
BIN
template/app/src/client/static/avatar-placeholder.webp
Normal file
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 178 KiB |
BIN
template/app/src/client/static/da-boi.webp
Normal file
After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 24 KiB |
BIN
template/app/src/client/static/logo.webp
Normal file
After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 840 KiB |
BIN
template/app/src/client/static/open-saas-banner.webp
Normal file
After Width: | Height: | Size: 145 KiB |
@ -1,10 +1,4 @@
|
|||||||
import {
|
import { features, navigation, faqs, footerNavigation, testimonials } from './contentSections';
|
||||||
features,
|
|
||||||
navigation,
|
|
||||||
faqs,
|
|
||||||
footerNavigation,
|
|
||||||
testimonials
|
|
||||||
} from './contentSections';
|
|
||||||
import Header from './components/Header';
|
import Header from './components/Header';
|
||||||
import Hero from './components/Hero';
|
import Hero from './components/Hero';
|
||||||
import Clients from './components/Clients';
|
import Clients from './components/Clients';
|
||||||
|
@ -5,7 +5,7 @@ import { AiFillCloseCircle } from 'react-icons/ai';
|
|||||||
import { Dialog } from '@headlessui/react';
|
import { Dialog } from '@headlessui/react';
|
||||||
import { Link } from 'wasp/client/router';
|
import { Link } from 'wasp/client/router';
|
||||||
import { useAuth } from 'wasp/client/auth';
|
import { useAuth } from 'wasp/client/auth';
|
||||||
import logo from '../../client/static/logo.png';
|
import logo from '../../client/static/logo.webp';
|
||||||
import DarkModeSwitcher from '../../client/components/DarkModeSwitcher';
|
import DarkModeSwitcher from '../../client/components/DarkModeSwitcher';
|
||||||
import DropdownUser from '../../user/DropdownUser';
|
import DropdownUser from '../../user/DropdownUser';
|
||||||
import { UserMenuItems } from '../../user/UserMenuItems';
|
import { UserMenuItems } from '../../user/UserMenuItems';
|
||||||
|
@ -1,31 +1,11 @@
|
|||||||
import openSaasBanner from '../../client/static/open-saas-banner.png';
|
import openSaasBannerWebp from '../../client/static/open-saas-banner.webp';
|
||||||
import { DocsUrl } from '../../shared/common';
|
import { DocsUrl } from '../../shared/common';
|
||||||
|
|
||||||
export default function Hero() {
|
export default function Hero() {
|
||||||
return (
|
return (
|
||||||
<div className='relative pt-14 w-full '>
|
<div className='relative pt-14 w-full'>
|
||||||
<div
|
<TopGradient />
|
||||||
className='absolute top-0 right-0 -z-10 transform-gpu overflow-hidden w-full blur-3xl sm:top-0 '
|
<BottomGradient />
|
||||||
aria-hidden='true'
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className='aspect-[1020/880] w-[55rem] flex-none sm:right-1/4 sm:translate-x-1/2 dark:hidden bg-gradient-to-tr from-amber-400 to-purple-300 opacity-40'
|
|
||||||
style={{
|
|
||||||
clipPath: 'polygon(80% 20%, 90% 55%, 50% 100%, 70% 30%, 20% 50%, 50% 0)',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className='absolute inset-x-0 top-[calc(100%-40rem)] sm:top-[calc(100%-65rem)] -z-10 transform-gpu overflow-hidden blur-3xl'
|
|
||||||
aria-hidden='true'
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className='relative aspect-[1020/880] sm:-left-3/4 sm:translate-x-1/4 dark:hidden bg-gradient-to-br from-amber-400 to-purple-300 opacity-50 w-[72.1875rem]'
|
|
||||||
style={{
|
|
||||||
clipPath: 'ellipse(80% 30% at 80% 50%)',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className='py-24 sm:py-32'>
|
<div className='py-24 sm:py-32'>
|
||||||
<div className='mx-auto max-w-8xl px-6 lg:px-8'>
|
<div className='mx-auto max-w-8xl px-6 lg:px-8'>
|
||||||
<div className='lg:mb-18 mx-auto max-w-3xl text-center'>
|
<div className='lg:mb-18 mx-auto max-w-3xl text-center'>
|
||||||
@ -44,13 +24,14 @@ export default function Hero() {
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-14 flow-root sm:mt-14 '>
|
<div className='mt-14 flow-root sm:mt-14'>
|
||||||
<div className='-m-2 rounded-xl lg:-m-4 lg:rounded-2xl lg:p-4'>
|
<div className='-m-2 flex justify-center rounded-xl lg:-m-4 lg:rounded-2xl lg:p-4'>
|
||||||
<img
|
<img
|
||||||
src={openSaasBanner}
|
src={openSaasBannerWebp}
|
||||||
alt='App screenshot'
|
alt='App screenshot'
|
||||||
width={2432}
|
width={1000}
|
||||||
height={1442}
|
height={530}
|
||||||
|
loading='lazy'
|
||||||
className='rounded-md shadow-2xl ring-1 ring-gray-900/10'
|
className='rounded-md shadow-2xl ring-1 ring-gray-900/10'
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -60,3 +41,35 @@ export default function Hero() {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TopGradient() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className='absolute top-0 right-0 -z-10 transform-gpu overflow-hidden w-full blur-3xl sm:top-0'
|
||||||
|
aria-hidden='true'
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className='aspect-[1020/880] w-[55rem] flex-none sm:right-1/4 sm:translate-x-1/2 dark:hidden bg-gradient-to-tr from-amber-400 to-purple-300 opacity-40'
|
||||||
|
style={{
|
||||||
|
clipPath: 'polygon(80% 20%, 90% 55%, 50% 100%, 70% 30%, 20% 50%, 50% 0)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function BottomGradient() {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className='absolute inset-x-0 top-[calc(100%-40rem)] sm:top-[calc(100%-65rem)] -z-10 transform-gpu overflow-hidden blur-3xl'
|
||||||
|
aria-hidden='true'
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className='relative aspect-[1020/880] sm:-left-3/4 sm:translate-x-1/4 dark:hidden bg-gradient-to-br from-amber-400 to-purple-300 opacity-50 w-[72.1875rem]'
|
||||||
|
style={{
|
||||||
|
clipPath: 'ellipse(80% 30% at 80% 50%)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -22,7 +22,7 @@ export default function Testimonials({ testimonials }: { testimonials: Testimoni
|
|||||||
</blockquote>
|
</blockquote>
|
||||||
<figcaption className='mt-6 text-base text-white'>
|
<figcaption className='mt-6 text-base text-white'>
|
||||||
<a href={testimonial.socialUrl} className='flex items-center gap-x-2'>
|
<a href={testimonial.socialUrl} className='flex items-center gap-x-2'>
|
||||||
<img src={testimonial.avatarSrc} className='h-12 w-12 rounded-full' />
|
<img src={testimonial.avatarSrc} loading='lazy' className='h-12 w-12 rounded-full' />
|
||||||
<div>
|
<div>
|
||||||
<div className='font-semibold hover:underline'>{testimonial.name}</div>
|
<div className='font-semibold hover:underline'>{testimonial.name}</div>
|
||||||
<div className='mt-1'>{testimonial.role}</div>
|
<div className='mt-1'>{testimonial.role}</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { DocsUrl, BlogUrl } from '../shared/common';
|
import { DocsUrl, BlogUrl } from '../shared/common';
|
||||||
import daBoiAvatar from '../client/static/da-boi.png';
|
import daBoiAvatar from '../client/static/da-boi.webp';
|
||||||
import avatarPlaceholder from '../client/static/avatar-placeholder.png';
|
import avatarPlaceholder from '../client/static/avatar-placeholder.webp';
|
||||||
import { routes } from 'wasp/client/router';
|
import { routes } from 'wasp/client/router';
|
||||||
|
|
||||||
export const navigation = [
|
export const navigation = [
|
||||||
|
@ -8,7 +8,7 @@ module.exports = {
|
|||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
satoshi: ['Satoshi', 'sans-serif'],
|
satoshi: ['Satoshi', 'system-ui', 'sans-serif'],
|
||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
current: 'currentColor',
|
current: 'currentColor',
|
||||||
|
@ -14,7 +14,7 @@ export default defineConfig({
|
|||||||
customCss: ['./src/styles/tailwind.css'],
|
customCss: ['./src/styles/tailwind.css'],
|
||||||
description: 'Documentation for your SaaS.',
|
description: 'Documentation for your SaaS.',
|
||||||
logo: {
|
logo: {
|
||||||
src: '/src/assets/logo.png',
|
src: '/src/assets/logo.webp',
|
||||||
alt: 'Your SaaS',
|
alt: 'Your SaaS',
|
||||||
},
|
},
|
||||||
head: [
|
head: [
|
||||||
|