diff --git a/.eslintrc.js b/.eslintrc.js index 260b678ef9a..eb023dbdfae 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -56,6 +56,15 @@ module.exports = { // Turning react/jsx-key back on. // https://github.com/airbnb/javascript/blob/5155aa5fc1ea9bb2c6493a06ddbd5c7a05414c86/packages/eslint-config-airbnb/rules/react.js#L94 'react/jsx-key': ['error', { checkKeyMustBeforeSpread: true, warnOnDuplicates: true }], + // This got turned of in the mono-repo: + // See https://github.com/mui/mui-toolpad/pull/866#discussion_r957222171 + 'react/no-unused-prop-types': [ + 'error', + { + customValidators: [], + skipShapeProps: true, + }, + ], }, overrides: [ { diff --git a/README.md b/README.md index 58397017d62..af4cd9ba155 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Quickly build internal tools. MUI Toolpad is a self-hosted low-code internal too -![Editor screenshot](./docs/public/static/toolpad/hero-screenshot.png) +![Editor screenshot](./docs/public/static/toolpad/docs/hero-screenshot.png) ## Notice diff --git a/docs/data/toolpad/getting-started/tutorial.md b/docs/data/toolpad/getting-started/tutorial.md index 7bf1107cbb5..3cc5b59355b 100644 --- a/docs/data/toolpad/getting-started/tutorial.md +++ b/docs/data/toolpad/getting-started/tutorial.md @@ -12,19 +12,19 @@ Make sure you have a running Toolpad instance. If you don't know how to do that, 1. Open Toolpad by navigating to [http://localhost:3000/](http://localhost:3000/) if you went through the docker setup, or otherwise, the URL Toolpad is hosted under. You'll be presented with an overview screen with all the applications available. If you've started from a fresh instance, there should be none present. - ![Apps overview](/static/toolpad/apps-overview.png) + ![Apps overview](/static/toolpad/docs/apps-overview.png) 1. Click "create new" and choose a name for your application in the popup, then click "create". This will create a new Toolpad application and the page will navigate to the application editor. - ![Editor overview](/static/toolpad/editor-overview.png) + ![Editor overview](/static/toolpad/docs/editor-overview.png) 1. In the left navigation bar, you will see all the objects that are present in this application. You'll see "connections", which represent credentials needed to connect to a data source. "APIs" allow you to query your data and make it available to pages. Next to the "pages" entry click + the icon to add a new page, give it a name, and click "create". The page designer will open. - ![Page editor](/static/toolpad/page-editor.png) + ![Page editor](/static/toolpad/docs/page-editor.png) 1. You are now in the page designer. You can access the components catalog by hovering over the bar on the left side containing the word "components". When the catalog folds out, grab a "TextField" and drag it into the canvas over the green box that says "insert here". When the box highlights, drop the TextField. An MUI TextField will now appear on the page canvas and the component properties editor opens. This input will be used to enter the name of the person we want to greet. In the component editor, change the name to "nameInput". We will use this name, later on, to refer to this TextField, so that we can bind properties to their value. - ![Added TextField](/static/toolpad/add-textfield.png) + ![Added TextField](/static/toolpad/docs/add-textfield.png) 1. Open the component catalog again and drag a "Typography" component on the page. This will contain our greeting. In the component editor on the right-hand side, locate the text field for the "value" property. Try typing in this field. You'll notice the text on the screen changes. We can also make this value dynamic. To do this, click the link icon to the right of the text field to open the binding editor. @@ -32,11 +32,11 @@ Make sure you have a running Toolpad instance. If you don't know how to do that, Now, change the JavaScript expression to `` `Hello ${nameInput.value}!` ``. Notice how we refer to our textField by the name "nameInput" that we set before. Now click "update binding" and close the binding editor. - ![Added TextField](/static/toolpad/updated-binding.png) + ![Added TextField](/static/toolpad/docs/updated-binding.png) 1. Select the TextField and try typing your name. See how the Text follows? - ![Result](/static/toolpad/result.png) + ![Result](/static/toolpad/docs/result.png) ## Deploying your first application @@ -44,7 +44,7 @@ Now that we have our first simple application, let's make sure we can use it out 1. The first thing we need to do is to create a release for our application. When you create a release, the current state of the application is captured as a snapshot and stored. This snapshot will never change, we will always be able to access this snapshot and see the state of the application at the time that we took the snapshot. To create a release, click the launch icon in the top right corner of the menu bar. In the dialog that opens, write "Initial release" in the description field and click "create". You will be directed to the release's page. Here you'll get a list of all the pages present in this release. Go ahead and click the "open page" icon to see what your released page looks like. - ![Release](/static/toolpad/release.png) + ![Release](/static/toolpad/docs/release.png) 1. You just created a versioned release of your application. Click the "deploy" button to host this version under the production URL of your page. Navigate to Toolpad home to get an overview of your application and click the "open" button on the application you just deployed. You'll be presented with an overview of the pages available in this application. Click on a page to see the production version of the application. @@ -56,18 +56,18 @@ A Toolpad aplication wouldn't be useful if you couldn't import your data into it 1. the first step is to make a connection to your data source. The connection holds the necessary credentials. In the case of a database, this may be the connection string, or in the case of a REST API this may contain an API token. For demo purposes, we will just create a dummy connection that we can use to make public HTTP requests. To get started, click on the plus button next to "Connections" in the left menu. In the popup that opens, select the "Fetch" type and click "create". In the window that opens, give your connection a name "ourData". - ![Create Connection](/static/toolpad/create-connection.png) + ![Create Connection](/static/toolpad/docs/create-connection.png) 1. Now that we can connect to our data source, we can make an API endpoint for it on the Toolpad application. Click the plus button next to "Apis" in the left menu and select the "ourData" connection that we will make an API for. In the editor that opens, give it a name "ourDataApi" and in the url fill in `https://gist.githubusercontent.com/Janpot/c9d1f00be8472f2a510680b7e716a43b/raw/f001e0c3c501f5a0da35474ba4854cfc69acfd78/sample-data.json`. This contains a sample dataset for us to use. If everything went well, you will see a preview in the bottom half of the page. Click "update" to accept the parameters for this API. - ![Create Api](/static/toolpad/create-api.png) + ![Create Api](/static/toolpad/docs/create-api.png) 1. Now we need to bring this data to our page. To do this, make a new page and click the "create query state" button in the page properties. In the popup that opens, give the state a name "ourDataState" and select the API we created before. We now have the data from this API available on the page in a variable `ourDataState` and we can bind it to components on the page. - ![Create State](/static/toolpad/create-state.png) + ![Create State](/static/toolpad/docs/create-state.png) 1. Now add a DataGrid on the page and in its properties click the link button next to "DataQuery". This will open our trusty binding editor in which you can type `ourDataState` and click "update binding". Close the binding editor to see the result. - ![Bind Query State](/static/toolpad/bind-query-state.png) + ![Bind Query State](/static/toolpad/docs/bind-query-state.png) Read our deep dive into data fetching in the [data fetching documentation](/toolpad/data-fetching/). diff --git a/docs/next.config.js b/docs/next.config.js index f282da0d0b7..89a71893a7a 100644 --- a/docs/next.config.js +++ b/docs/next.config.js @@ -10,6 +10,24 @@ const pkg = require('../package.json'); const { findPages } = require('./src/modules/utils/find'); const { LANGUAGES, LANGUAGES_SSR } = require('./src/modules/constants'); +let DEPLOY_ENV = 'development'; + +// Same as process.env.PULL_REQUEST === 'true' +if (process.env.CONTEXT === 'deploy-preview') { + DEPLOY_ENV = 'pull-request'; +} + +if (process.env.CONTEXT === 'production' || process.env.CONTEXT === 'branch-deploy') { + DEPLOY_ENV = 'production'; +} + +if ( + process.env.CONTEXT === 'branch-deploy' && + (process.env.HEAD === 'master' || process.env.HEAD === 'next') +) { + DEPLOY_ENV = 'staging'; +} + const MONORPO_PATH = path.resolve(__dirname, './node_modules/@mui/monorepo'); const MONOREPO_PACKAGES = { '@mui/base': path.resolve(MONORPO_PATH, './packages/mui-base/src'), @@ -46,14 +64,12 @@ if (reactStrictMode) { console.log(`Using React.StrictMode.`); } -const isDeployment = process.env.NETLIFY === 'true'; - module.exports = withTM({ eslint: { ignoreDuringBuilds: true, }, // Avoid conflicts with the other Next.js apps hosted under https://mui.com/ - assetPrefix: isDeployment ? '/toolpad' : '', + assetPrefix: DEPLOY_ENV === 'development' ? '' : '/toolpad', typescript: { // Motivated by https://github.com/zeit/next.js/issues/7687 ignoreDevErrors: true, @@ -65,14 +81,12 @@ module.exports = withTM({ ENABLE_AD: process.env.ENABLE_AD, GITHUB_AUTH: process.env.GITHUB_AUTH, LIB_VERSION: pkg.version, - PULL_REQUEST: process.env.PULL_REQUEST === 'true', REACT_STRICT_MODE: reactStrictMode, FEEDBACK_URL: process.env.FEEDBACK_URL, - // Set by Netlify - GRID_EXPERIMENTAL_ENABLED: process.env.PULL_REQUEST === 'false' ? 'false' : 'true', // #default-branch-switch SOURCE_CODE_ROOT_URL: 'https://github.com/mui/mui-toolpad/blob/master', SOURCE_CODE_REPO: 'https://github.com/mui/mui-toolpad', + DEPLOY_ENV, }, webpack5: true, webpack: (config) => { diff --git a/docs/pages/toolpad/index.js b/docs/pages/toolpad/index.js index 01ae5b747be..deea653a523 100644 --- a/docs/pages/toolpad/index.js +++ b/docs/pages/toolpad/index.js @@ -1,25 +1,58 @@ import * as React from 'react'; +import NoSsr from '@mui/material/NoSsr'; import Head from 'docs/src/modules/components/Head'; import CssBaseline from '@mui/material/CssBaseline'; import BrandingProvider from 'docs/src/BrandingProvider'; import AppHeader from 'docs/src/layouts/AppHeader'; import AppFooter from 'docs/src/layouts/AppFooter'; import AppHeaderBanner from 'docs/src/components/banner/AppHeaderBanner'; -import ToolpadHero from '../../src/components/landing/ToolpadHero'; +import ToolpadHero from '../../src/components/landing/Hero'; +import SignUpToast from '../../src/components/landing/SignUpToast'; +import CardGrid from '../../src/components/landing/CardGrid'; +import Banner from '../../src/components/landing/Banner'; +import Pricing from '../../src/components/landing/PricingTable'; +import Marquee from '../../src/components/landing/Marquee'; +import features from '../../src/content/features'; +import upvote from '../../src/content/upvote'; +import useCases from '../../src/content/useCases'; +import marquee from '../../src/content/marquee'; +import { + Headline, + plans, + planInfo, + rowHeaders, + communityData, + commercialData, +} from '../../src/content/pricing'; export default function Home() { return ( + + + -
+
+ + + + + diff --git a/docs/public/static/branding/pricing/block-blue.svg b/docs/public/static/branding/pricing/block-blue.svg new file mode 100644 index 00000000000..dbde67ddbf2 --- /dev/null +++ b/docs/public/static/branding/pricing/block-blue.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/public/static/branding/pricing/block-gold.svg b/docs/public/static/branding/pricing/block-gold.svg new file mode 100644 index 00000000000..f5945b77b2c --- /dev/null +++ b/docs/public/static/branding/pricing/block-gold.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/public/static/branding/pricing/block-green.svg b/docs/public/static/branding/pricing/block-green.svg new file mode 100644 index 00000000000..c6f705ea6f9 --- /dev/null +++ b/docs/public/static/branding/pricing/block-green.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/public/static/branding/pricing/no-dark.svg b/docs/public/static/branding/pricing/no-dark.svg new file mode 100644 index 00000000000..ac3044c903e --- /dev/null +++ b/docs/public/static/branding/pricing/no-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/docs/public/static/branding/pricing/no-light.svg b/docs/public/static/branding/pricing/no-light.svg new file mode 100644 index 00000000000..e5b49bb34da --- /dev/null +++ b/docs/public/static/branding/pricing/no-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/docs/public/static/branding/pricing/time-dark.svg b/docs/public/static/branding/pricing/time-dark.svg new file mode 100644 index 00000000000..109c1ab9d71 --- /dev/null +++ b/docs/public/static/branding/pricing/time-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/public/static/branding/pricing/time-light.svg b/docs/public/static/branding/pricing/time-light.svg new file mode 100644 index 00000000000..c06b69498f8 --- /dev/null +++ b/docs/public/static/branding/pricing/time-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/public/static/branding/pricing/yes-dark.svg b/docs/public/static/branding/pricing/yes-dark.svg new file mode 100644 index 00000000000..ae619a9d910 --- /dev/null +++ b/docs/public/static/branding/pricing/yes-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/public/static/branding/pricing/yes-light.svg b/docs/public/static/branding/pricing/yes-light.svg new file mode 100644 index 00000000000..368c5d5e2c4 --- /dev/null +++ b/docs/public/static/branding/pricing/yes-light.svg @@ -0,0 +1 @@ + diff --git a/docs/public/static/branding/product-toolpad-dark.svg b/docs/public/static/branding/product-toolpad-dark.svg new file mode 100644 index 00000000000..4fbac23e38c --- /dev/null +++ b/docs/public/static/branding/product-toolpad-dark.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/static/branding/product-toolpad-light.svg b/docs/public/static/branding/product-toolpad-light.svg new file mode 100644 index 00000000000..7df3d98cfdb --- /dev/null +++ b/docs/public/static/branding/product-toolpad-light.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/static/toolpad/add-textfield.png b/docs/public/static/toolpad/docs/add-textfield.png similarity index 100% rename from docs/public/static/toolpad/add-textfield.png rename to docs/public/static/toolpad/docs/add-textfield.png diff --git a/docs/public/static/toolpad/apps-overview.png b/docs/public/static/toolpad/docs/apps-overview.png similarity index 100% rename from docs/public/static/toolpad/apps-overview.png rename to docs/public/static/toolpad/docs/apps-overview.png diff --git a/docs/public/static/toolpad/bind-query-state.png b/docs/public/static/toolpad/docs/bind-query-state.png similarity index 100% rename from docs/public/static/toolpad/bind-query-state.png rename to docs/public/static/toolpad/docs/bind-query-state.png diff --git a/docs/public/static/toolpad/create-api.png b/docs/public/static/toolpad/docs/create-api.png similarity index 100% rename from docs/public/static/toolpad/create-api.png rename to docs/public/static/toolpad/docs/create-api.png diff --git a/docs/public/static/toolpad/create-connection.png b/docs/public/static/toolpad/docs/create-connection.png similarity index 100% rename from docs/public/static/toolpad/create-connection.png rename to docs/public/static/toolpad/docs/create-connection.png diff --git a/docs/public/static/toolpad/create-state.png b/docs/public/static/toolpad/docs/create-state.png similarity index 100% rename from docs/public/static/toolpad/create-state.png rename to docs/public/static/toolpad/docs/create-state.png diff --git a/docs/public/static/toolpad/editor-overview.png b/docs/public/static/toolpad/docs/editor-overview.png similarity index 100% rename from docs/public/static/toolpad/editor-overview.png rename to docs/public/static/toolpad/docs/editor-overview.png diff --git a/docs/public/static/toolpad/page-editor.png b/docs/public/static/toolpad/docs/page-editor.png similarity index 100% rename from docs/public/static/toolpad/page-editor.png rename to docs/public/static/toolpad/docs/page-editor.png diff --git a/docs/public/static/toolpad/release.png b/docs/public/static/toolpad/docs/release.png similarity index 100% rename from docs/public/static/toolpad/release.png rename to docs/public/static/toolpad/docs/release.png diff --git a/docs/public/static/toolpad/result.png b/docs/public/static/toolpad/docs/result.png similarity index 100% rename from docs/public/static/toolpad/result.png rename to docs/public/static/toolpad/docs/result.png diff --git a/docs/public/static/toolpad/updated-binding.png b/docs/public/static/toolpad/docs/updated-binding.png similarity index 100% rename from docs/public/static/toolpad/updated-binding.png rename to docs/public/static/toolpad/docs/updated-binding.png diff --git a/docs/public/static/toolpad/hero-screenshot.png b/docs/public/static/toolpad/marketing/hero-screenshot.png similarity index 100% rename from docs/public/static/toolpad/hero-screenshot.png rename to docs/public/static/toolpad/marketing/hero-screenshot.png diff --git a/docs/public/static/toolpad/marketing/hero-video.mp4 b/docs/public/static/toolpad/marketing/hero-video.mp4 new file mode 100644 index 00000000000..7ea28985c9d Binary files /dev/null and b/docs/public/static/toolpad/marketing/hero-video.mp4 differ diff --git a/docs/public/static/toolpad/marketing/toolpad-admin-panel.svg b/docs/public/static/toolpad/marketing/toolpad-admin-panel.svg new file mode 100644 index 00000000000..bb61d9329f9 --- /dev/null +++ b/docs/public/static/toolpad/marketing/toolpad-admin-panel.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/static/toolpad/marketing/toolpad-custom-cms.svg b/docs/public/static/toolpad/marketing/toolpad-custom-cms.svg new file mode 100644 index 00000000000..96903f0bfd4 --- /dev/null +++ b/docs/public/static/toolpad/marketing/toolpad-custom-cms.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/public/static/toolpad/marketing/toolpad-monitoring-dashboard.svg b/docs/public/static/toolpad/marketing/toolpad-monitoring-dashboard.svg new file mode 100644 index 00000000000..006bd442eb2 --- /dev/null +++ b/docs/public/static/toolpad/marketing/toolpad-monitoring-dashboard.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/components/landing/Banner.js b/docs/src/components/landing/Banner.js new file mode 100644 index 00000000000..0e08e55af55 --- /dev/null +++ b/docs/src/components/landing/Banner.js @@ -0,0 +1,74 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Container from '@mui/material/Container'; +import Typography from '@mui/material/Typography'; +import Stack from '@mui/material/Stack'; +import Button from '@mui/material/Button'; +import Link from 'docs/src/modules/components/Link'; +import KeyboardArrowRightRounded from '@mui/icons-material/KeyboardArrowRightRounded'; + +export default function Banner(props) { + const { content } = props; + return ( + + (theme.palette.mode === 'dark' ? 'primaryDark.900' : 'primary.50'), + border: '1px solid', + borderColor: (theme) => + theme.palette.mode === 'dark' ? 'primaryDark.500' : 'primary.100', + display: 'flex', + flexDirection: { + xs: 'column', + sm: 'row', + }, + justifyContent: 'space-between', + alignItems: { xs: 'flex-start', sm: 'center' }, + }} + > +
+ + {content.title} + + + {content.description} + +
+ +
+
+ ); +} + +Banner.propTypes = { + content: PropTypes.shape({ + action: PropTypes.shape({ + href: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + }).isRequired, + description: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, + }).isRequired, +}; diff --git a/docs/src/components/landing/CardGrid.js b/docs/src/components/landing/CardGrid.js new file mode 100644 index 00000000000..c6407217fc1 --- /dev/null +++ b/docs/src/components/landing/CardGrid.js @@ -0,0 +1,97 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import Container from '@mui/material/Container'; +import Paper from '@mui/material/Paper'; +import Typography from '@mui/material/Typography'; +import Grid from '@mui/material/Grid'; +import SectionHeadline from 'docs/src/components/typography/SectionHeadline'; +import IconImage from 'docs/src/components/icon/IconImage'; + +const cardRootStyle = (imageUrl) => ({ + px: imageUrl ? 0 : 2, + pb: 3, + pt: imageUrl ? 0 : 3, + height: '100%', + maxWidth: 360, +}); + +const cardMediaStyle = (imageUrl) => ({ + display: 'block', + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center', + objectFit: 'contain', + background: `#001E3C url(${imageUrl})`, + backgroundSize: 'contain', + borderTopLeftRadius: '10px', + borderTopRightRadius: '10px', + borderWidth: '0px', + // https://stackoverflow.com/questions/600743/how-to-get-div-height-to-auto-adjust-to-background-size + paddingTop: '55.55%', +}); + +const cardContentRootStyle = (imageUrl) => ({ + px: imageUrl ? 2 : 0, + pt: imageUrl ? 2 : 0, + pb: 0, +}); + +export default function CardGrid(props) { + const { content } = props; + return ( + (theme.palette.mode === 'dark' ? 'primaryDark.900' : 'grey.50'), + }} + > + + + + {content.cards.map(({ icon, title, wip, imageUrl, description }) => ( + + + {imageUrl ? : null} + + + {icon ?? null} + + {title} + + {wip ? ( + + ) : null} + + + {description} + + + + + ))} + + + + ); +} + +CardGrid.propTypes = { + content: PropTypes.shape({ + cards: PropTypes.arrayOf( + PropTypes.shape({ + description: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired, + icon: PropTypes.node, + imageUrl: PropTypes.string, + title: PropTypes.string.isRequired, + wip: PropTypes.bool, + }), + ), + Headline: PropTypes.node.isRequired, + overline: PropTypes.string.isRequired, + }).isRequired, +}; diff --git a/docs/src/components/landing/DemoVideo.js b/docs/src/components/landing/DemoVideo.js new file mode 100644 index 00000000000..50d964c6754 --- /dev/null +++ b/docs/src/components/landing/DemoVideo.js @@ -0,0 +1,50 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import { styled } from '@mui/material/styles'; + +const Video = styled('video')(({ theme }) => ({ + borderStyle: 'solid', + borderColor: + theme.palette.mode === 'dark' ? theme.palette.primaryDark[900] : theme.palette.grey[100], + borderRadius: '10px 10px 0 0 ', + overflow: 'hidden', + objectFit: 'cover', + width: '100%', + height: '100%', +})); + +export default function DemoVideo() { + return ( + ({ + borderStyle: 'solid', + maxWidth: 1100, + height: { xs: 200, sm: 390, md: 445, lg: 615 }, + width: { + xs: 360, + sm: theme.breakpoints.values.sm + 100, + md: theme.breakpoints.values.md - 100, + lg: theme.breakpoints.values.lg - 100, + }, + borderColor: + theme.palette.mode === 'dark' ? theme.palette.primaryDark[400] : theme.palette.grey[300], + borderRadius: '10px 10px 0 0', + borderWidth: '1px 1px 0 1px', + })} + > + + + ); +} diff --git a/docs/src/components/landing/Hero.js b/docs/src/components/landing/Hero.js new file mode 100644 index 00000000000..04b2f715fdf --- /dev/null +++ b/docs/src/components/landing/Hero.js @@ -0,0 +1,75 @@ +import * as React from 'react'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import IconImage from 'docs/src/components/icon/IconImage'; +import GradientText from 'docs/src/components/typography/GradientText'; +import SignUp from './SignUp'; +import ToolpadHeroContainer from '../../layouts/ToolpadHeroContainer'; +import DemoVideo from './DemoVideo'; + +export default function Hero() { + return ( + + + (theme.palette.mode === 'dark' ? 'primary.400' : 'primary.600')} + sx={{ + display: 'flex', + alignItems: 'center', + justifyContent: { xs: 'center', md: 'center' }, + '& > *': { mr: 1, height: 28 }, + }} + > + + + MUI Toolpad + + + + Low-code +
+ admin builder +
+ + Drag and drop pre-built components, connect to data sources and APIs, and build your + internal tools 10x faster. Open-source and powered by MUI. + + (theme.palette.mode === 'dark' ? '#fff' : `text.secondary`)} + sx={{ fontWeight: 'bold', display: 'block', mb: 2, mx: 'auto' }} + htmlFor="email-landing" + > + Sign up for early access to the beta release. + + div': { + display: 'flex', + flexDirection: { xs: 'column', sm: 'row' }, + justifyContent: 'center', + maxWidth: 'initial', + }, + }} + /> +
+ + + +
+ ); +} diff --git a/docs/src/components/landing/Marquee.js b/docs/src/components/landing/Marquee.js new file mode 100644 index 00000000000..bbfa8f0bb63 --- /dev/null +++ b/docs/src/components/landing/Marquee.js @@ -0,0 +1,60 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Container from '@mui/material/Container'; +import Button from '@mui/material/Button'; +import Link from 'docs/src/modules/components/Link'; +import Typography from '@mui/material/Typography'; +import KeyboardArrowRightRounded from '@mui/icons-material/KeyboardArrowRightRounded'; + +const Marquee = ({ content }) => { + return ( + + + {content.title} + + + + ); +}; + +Marquee.propTypes = { + content: PropTypes.shape({ + action: PropTypes.shape({ + href: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + }).isRequired, + title: PropTypes.string.isRequired, + }).isRequired, +}; + +export default Marquee; diff --git a/docs/src/components/landing/PricingTable.js b/docs/src/components/landing/PricingTable.js new file mode 100644 index 00000000000..26db2ec528c --- /dev/null +++ b/docs/src/components/landing/PricingTable.js @@ -0,0 +1,478 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import { alpha } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import Container from '@mui/material/Container'; +import Fade from '@mui/material/Fade'; +import Paper from '@mui/material/Paper'; +import Tabs from '@mui/material/Tabs'; +import Tab from '@mui/material/Tab'; +import Divider from '@mui/material/Divider'; +import Typography from '@mui/material/Typography'; +// import IconImage from 'docs/src/components/icon/IconImage'; +import SectionHeadline from 'docs/src/components/typography/SectionHeadline'; + +function PlanName({ planInfo, centered = false, disableDescription = false }) { + const { title, description } = planInfo; + return ( + + {centered ? ( + + {/* */} {title} + + ) : ( + + {title} {/* */} + + )} + {!disableDescription ? ( + + {description} + + ) : null} + + ); +} + +PlanName.propTypes = { + centered: PropTypes.bool, + disableDescription: PropTypes.bool, + planInfo: PropTypes.shape({ + color: PropTypes.string.isRequired, + description: PropTypes.string, + title: PropTypes.string.isRequired, + }), +}; + +const Cell = ({ highlighted = false, ...props }) => ( + (theme.palette.mode === 'dark' ? 'primaryDark.700' : 'grey.100'), + bgcolor: (theme) => + theme.palette.mode === 'dark' + ? alpha(theme.palette.primaryDark[900], 0.5) + : alpha(theme.palette.grey[50], 0.5), + }), + ...props.sx, + }} + /> +); + +Cell.propTypes = { + highlighted: PropTypes.bool, + sx: PropTypes.object, +}; + +const RowHead = ({ children, startIcon, ...props }) => ( + (theme.palette.mode === 'dark' ? 'primaryDark.900' : 'grey.50'), + p: 1, + transition: 'none', + typography: 'body2', + fontWeight: 700, + display: 'flex', + alignItems: 'center', + ...props.sx, + }} + > + {startIcon ? {startIcon} : null} + {children} + +); + +RowHead.propTypes = { + children: PropTypes.node.isRequired, + startIcon: PropTypes.node, + sx: PropTypes.object, +}; + +const RowCategory = (props) => ( + (theme.palette.mode === 'dark' ? 'primaryDark.900' : 'grey.50'), + py: 1, + ml: 1, + pl: 1.5, + borderBottom: '1px solid', + borderColor: (theme) => (theme.palette.mode === 'dark' ? 'primaryDark.600' : 'grey.200'), + ...props.sx, + }} + /> +); + +RowCategory.propTypes = { + sx: PropTypes.object, +}; + +const StickyHead = ({ plans, planInfo, container, disableCalculation = false }) => { + const [hidden, setHidden] = React.useState(true); + React.useEffect(() => { + function handleScroll() { + if (container.current) { + const rect = container.current.getBoundingClientRect(); + const appHeaderHeight = 64; + const headHeight = 41; + const tablePaddingTop = 40; + if ( + rect.top + appHeaderHeight < 0 && + rect.height + rect.top - appHeaderHeight - headHeight - tablePaddingTop > 0 + ) { + setHidden(false); + } else { + setHidden(true); + } + } + } + if (!disableCalculation) { + document.addEventListener('scroll', handleScroll); + return () => { + document.removeEventListener('scroll', handleScroll); + }; + } + return () => {}; + }, [container, disableCalculation]); + return ( + + `inset 0px -1px 1px ${ + theme.palette.mode === 'dark' ? theme.palette.primaryDark[700] : theme.palette.grey[100] + }`, + backgroundColor: (theme) => + theme.palette.mode === 'dark' + ? alpha(theme.palette.primaryDark[900], 0.72) + : 'rgba(255,255,255,0.72)', + }} + > + + + Plans + + {plans.map((plan) => ( + + + + ))} + + + ); +}; + +StickyHead.propTypes = { + container: PropTypes.shape({ + current: PropTypes.shape({ + getBoundingClientRect: PropTypes.func.isRequired, + }), + }).isRequired, + disableCalculation: PropTypes.bool, + planInfo: PropTypes.object, + plans: PropTypes.arrayOf(PropTypes.string).isRequired, +}; + +const divider = ; + +function PricingTable({ + columnHeaderHidden, + planInfo, + plans, + rowHeaders, + communityData, + commercialData, + ...props +}) { + const tableRef = React.useRef(null); + const gridSx = { + display: 'grid', + gridTemplateColumns: `minmax(160px, 1fr) repeat(${plans.length}, minmax(${ + columnHeaderHidden ? '0px' : '240px' + }, 1fr))`, + }; + function renderRow(key) { + return ( + + theme.palette.mode === 'dark' + ? alpha(theme.palette.primaryDark[900], 0.3) + : alpha(theme.palette.grey[50], 0.4), + '@media (hover: none)': { + bgcolor: 'initial', + }, + }, + }} + > + {rowHeaders[key]} + {plans.map((id, index) => ( + + {id === 'community' ? communityData[key] : null} + {id === 'commercial' ? commercialData[key] : null} + + ))} + + ); + } + return ( + + + {!columnHeaderHidden ? ( + + + {plans.map((plan) => ( + + + + ))} + + ) : null} + Hosting + {renderRow('Self-hosting')} + {divider} + {renderRow('Cloud hosting')} + Apps + {renderRow('Apps')} + {divider} + {renderRow('Data sources')} + {divider} + {renderRow('Export to code')} + {divider} + {renderRow('Plugging API')} + {divider} + {renderRow('Built-in version control')} + {divider} + {renderRow('Git version control')} + {divider} + {renderRow('Staging environments')} + {divider} + {renderRow('White label')} + {divider} + {renderRow('Custom themes')} + Components + {renderRow('Components')} + {divider} + {renderRow('Custom components')} + {divider} + {renderRow('Premium components')} + Users + {renderRow('Real-time commenting')} + {divider} + {renderRow('Multiple organizations')} + Security + {renderRow('OAuth2/OpenID SSO')} + {divider} + {renderRow('SSO enforcement')} + {divider} + {renderRow('SAML-based SSO')} + {divider} + {renderRow('Granular permissions')} + {divider} + {renderRow('Audit logs')} + Support + {renderRow('Support level')} + {divider} + + ); +} + +PricingTable.propTypes = { + columnHeaderHidden: PropTypes.bool, + commercialData: PropTypes.object, + communityData: PropTypes.object, + planInfo: PropTypes.object, + plans: PropTypes.arrayOf(PropTypes.string), + rowHeaders: PropTypes.object, + sx: PropTypes.object, +}; + +const Plan = ({ planInfo, benefits, unavailable, sx, ...props }) => { + return ( + + + + ); +}; + +Plan.propTypes = { + benefits: PropTypes.arrayOf(PropTypes.string), + planInfo: PropTypes.object, + sx: PropTypes.object, + unavailable: PropTypes.bool, +}; + +function PricingList({ plans, planInfo, rowHeaders, commercialData, communityData }) { + const [selectedPlan, setSelectedPlan] = React.useState(plans[0]); + const selectedIndex = React.useMemo(() => plans.indexOf(selectedPlan), [plans, selectedPlan]); + return ( + + setSelectedPlan(plans[value])} + sx={{ + mb: 2, + position: 'sticky', + top: 55, + bgcolor: 'background.paper', + zIndex: 1, + mx: { xs: -2, sm: -3 }, + borderTop: '1px solid', + borderColor: 'divider', + '& .MuiTab-root': { + borderBottom: '1px solid', + borderColor: 'divider', + '&.Mui-selected': { + bgcolor: (theme) => (theme.palette.mode === 'dark' ? 'primaryDark.700' : 'grey.50'), + }, + }, + }} + > + {plans.map((plan, index) => ( + + ))} + + +
+ + +
+
+
+ ); +} + +PricingList.propTypes = { + commercialData: PropTypes.object, + communityData: PropTypes.object, + planInfo: PropTypes.object.isRequired, + plans: PropTypes.arrayOf(PropTypes.string).isRequired, + rowHeaders: PropTypes.object, +}; + +export default function Pricing({ + Headline, + plans, + planInfo, + rowHeaders, + commercialData, + communityData, +}) { + return ( + + + + + With the intention to have affordable plans for all kind of business needs, we will + announce our exact pricing later this year. + + + + + + {/* Desktop */} + + + ); +} + +Pricing.propTypes = { + commercialData: PropTypes.object, + communityData: PropTypes.object, + Headline: PropTypes.node, + planInfo: PropTypes.object.isRequired, + plans: PropTypes.arrayOf(PropTypes.string).isRequired, + rowHeaders: PropTypes.object, +}; diff --git a/docs/src/components/landing/SignUp.js b/docs/src/components/landing/SignUp.js new file mode 100644 index 00000000000..d3392d34583 --- /dev/null +++ b/docs/src/components/landing/SignUp.js @@ -0,0 +1,161 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Alert from '@mui/material/Alert'; +import AlertTitle from '@mui/material/AlertTitle'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded'; +import FormHelperText from '@mui/material/FormHelperText'; +import InputBase, { inputBaseClasses } from '@mui/material/InputBase'; +import KeyboardArrowRightRounded from '@mui/icons-material/KeyboardArrowRightRounded'; +import { styled } from '@mui/material/styles'; +import ROUTES from '../../route'; + +const Form = styled('form')({}); + +const SignUp = ({ sx }) => { + const [form, setForm] = React.useState({ + email: '', + status: 'initial', + }); + const handleSubmit = async (event) => { + event.preventDefault(); + setForm((current) => ({ ...current, status: 'loading' })); + try { + await fetch(ROUTES.TOOLPAD_SIGN_UP_URL, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + mode: 'no-cors', + body: new URLSearchParams({ + EMAIL: form.email, + email_address_check: '', + locale: 'en', + }), + }); + setForm((current) => ({ ...current, status: 'sent' })); + window.ga('send', { + hitType: 'event', + eventCategory: 'ToolpadLanding', + eventAction: 'SignUp', + eventLabel: 'Sign Up', + }); + } catch (error) { + setForm((current) => ({ ...current, status: 'failure' })); + } + }; + if (form.status === 'sent') { + return ( + (theme.palette.mode === 'dark' ? 'primaryDark.700' : 'success.50'), + }} + iconMapping={{ + success: ( + (theme.palette.mode === 'dark' ? 'success.600' : 'success.700'), + }} + /> + ), + }} + > + + Thanks! Check your email. + + You should get a confirmation email soon. Open it up and confirm your email + address so that we can keep you up to date. + + ); + } + return ( +
+ + setForm({ email: event.target.value, status: 'initial' })} + sx={{ + mr: { xs: 0, sm: 1 }, + mb: { xs: 1, sm: 0 }, + minWidth: { xs: 220, sm: 360 }, + width: { xs: '100%', sm: 'auto' }, + bgcolor: (theme) => + theme.palette.mode === 'dark' ? theme.palette.primaryDark[900] : '#fff', + boxShadow: (theme) => + theme.palette.mode === 'dark' + ? '0 1px 2px 0 rgba(0 0 0 / 1)' + : '0 1px 2px 0 rgba(0 0 0 / 0.1)', + borderRadius: 1, + border: '1px solid', + borderColor: (theme) => + theme.palette.mode === 'dark' ? theme.palette.primaryDark[500] : 'grey.400', + px: 1, + py: 0.5, + height: 50, + typography: 'body2', + '&:hover': { + borderColor: (theme) => + theme.palette.mode === 'dark' ? theme.palette.primaryDark[300] : 'grey.400', + boxShadow: (theme) => + theme.palette.mode === 'dark' + ? '0 1px 2px 0 rgba(0 0 0 / 1)' + : '0 1px 2px 0 rgba(0 0 0 / 0.2)', + }, + [`&.${inputBaseClasses.focused}`]: { + borderColor: (theme) => + theme.palette.mode === 'dark' + ? theme.palette.primaryDark[300] + : theme.palette.primary[500], + outline: '3px solid', + outlineColor: (theme) => + theme.palette.mode === 'dark' + ? theme.palette.primaryDark[500] + : theme.palette.primary[300], + }, + }} + /> + + + {form.status === 'failure' ? ( + (theme.palette.mode === 'dark' ? 'warning.500' : 'warning.800') }} + > + Oops! something went wrong, please try again later. + + ) : null} +
+ ); +}; + +SignUp.propTypes = { + sx: PropTypes.object, +}; + +export default SignUp; diff --git a/docs/src/components/landing/SignUpToast.js b/docs/src/components/landing/SignUpToast.js new file mode 100644 index 00000000000..0f144cf778f --- /dev/null +++ b/docs/src/components/landing/SignUpToast.js @@ -0,0 +1,77 @@ +import * as React from 'react'; +import { useRouter } from 'next/router'; +import Slide from '@mui/material/Slide'; +import Box from '@mui/material/Box'; +import Card from '@mui/material/Card'; +import IconButton from '@mui/material/IconButton'; +import Typography from '@mui/material/Typography'; +import CloseRounded from '@mui/icons-material/CloseRounded'; +import MarkEmailReadTwoTone from '@mui/icons-material/MarkEmailReadTwoTone'; + +export default function NewsletterToast() { + const router = useRouter(); + const [hidden, setHidden] = React.useState(router.query.signedUp !== 'true'); + React.useEffect(() => { + if (router.query.signedUp === 'true') { + setHidden(false); + router.replace(router.pathname); + } + }, [router.query.newsletter, router]); + React.useEffect(() => { + const time = setTimeout(() => { + if (!hidden) { + setHidden(true); + } + }, 4000); + return () => { + clearTimeout(time); + }; + }, [hidden]); + return ( + + + + + + ); +} diff --git a/docs/src/components/landing/ToolpadHero.js b/docs/src/components/landing/ToolpadHero.js deleted file mode 100644 index eeafab8f0e2..00000000000 --- a/docs/src/components/landing/ToolpadHero.js +++ /dev/null @@ -1,37 +0,0 @@ -import * as React from 'react'; -import Box from '@mui/material/Box'; -import Typography from '@mui/material/Typography'; -import GradientText from 'docs/src/components/typography/GradientText'; -import IconImage from 'docs/src/components/icon/IconImage'; -import ToolpadHeroContainer from '../../layouts/ToolpadHeroContainer'; - -export default function ToolpadHero() { - return ( - - - (theme.palette.mode === 'dark' ? 'primary.400' : 'primary.600')} - sx={{ - display: 'flex', - alignItems: 'center', - justifyContent: { xs: 'center', md: 'center' }, - '& > *': { mr: 1, width: 28, height: 28 }, - }} - > - Toolpad - - - Low-code -
- admin builder -
- - Drag and drop pre-built components, connect to data sources, APIs and build your internal - tools 10x faster. Open-source and powered by MUI. - -
-
- ); -} diff --git a/docs/src/content/features.js b/docs/src/content/features.js new file mode 100644 index 00000000000..0889813b532 --- /dev/null +++ b/docs/src/content/features.js @@ -0,0 +1,90 @@ +import * as React from 'react'; +import BuildOutlinedIcon from '@mui/icons-material/BuildOutlined'; +import CodeIcon from '@mui/icons-material/Code'; +import LinkIcon from '@mui/icons-material/Link'; +import ViewModuleIcon from '@mui/icons-material/ViewModule'; +import ThumbUpOutlinedIcon from '@mui/icons-material/ThumbUpOutlined'; +import GppGoodIcon from '@mui/icons-material/GppGood'; +import FastForwardIcon from '@mui/icons-material/FastForward'; +import EditIcon from '@mui/icons-material/Edit'; +import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome'; +import Typography from '@mui/material/Typography'; +import GradientText from 'docs/src/components/typography/GradientText'; + +const features = { + overline: 'Features', + Headline: ( + + Production-ready +
+ in minutes +
+ ), + cards: [ + { + icon: , + title: 'Build visually', + wip: false, + description: + 'Use the drag & drop canvas to build applications fast. Create a beautiful, functional user interface without writing a single line of CSS.', + }, + { + icon: , + title: 'Extensible with code', + wip: false, + description: + 'Build low-code with pro-code extensibility. You can write JavaScript anywhere in Toolpad as soon as you feel limited by the built-in features.', + }, + { + icon: , + title: 'Connect any data source', + wip: true, + description: + 'Find all the integrations that you need. So far, it supports REST, Google sheets, PostgreSQL. We are working on adding MySQL, GraphQL, and a plugin system to add new ones.', + }, + { + icon: , + title: 'MUI component library', + wip: true, + description: + 'Access the full suite of pre-built MUI components, including both MUI Core and MUI X—or bring your own custom components to your Toolpad app.', + }, + { + icon: , + title: 'Self-host for security', + wip: false, + description: + 'Host Toolpad on your own infrastructure to keep full control over where your data goes. In the future, we will provide airgapping guarantees.', + }, + { + icon: , + title: 'Collaboration', + wip: true, + description: + 'Toolpad enables your team to communicate and collaborate in one seamless building experience—no more shuffling between platforms.', + }, + { + icon: , + title: 'Instant deployment ', + wip: false, + description: + 'App deployment in Toolpad is instant and veiwing past deployments makes it easier to access previous work.', + }, + { + icon: , + title: 'Git sync', + wip: true, + description: + 'Git allows you to push, pull and manage your code. Sync your Toolpad app to work on multiple branches and submit PRs. Toolpad will support GitHub and other providers.', + }, + { + icon: , + title: 'Custom theming', + wip: true, + description: + "Write CSS in Toolpad to manage the finer details. Import styles or define them locally to take full control of your app's theme.", + }, + ], +}; + +export default features; diff --git a/docs/src/content/marquee.js b/docs/src/content/marquee.js new file mode 100644 index 00000000000..68b476878d7 --- /dev/null +++ b/docs/src/content/marquee.js @@ -0,0 +1,11 @@ +import ROUTES from '../route'; + +const marquee = { + title: 'Want to try it first? Download the alpha version.', + action: { + href: ROUTES.toolpadDocs, + label: 'Self-host', + }, +}; + +export default marquee; diff --git a/docs/src/content/pricing.js b/docs/src/content/pricing.js new file mode 100644 index 00000000000..c350e6316a0 --- /dev/null +++ b/docs/src/content/pricing.js @@ -0,0 +1,267 @@ +import * as React from 'react'; +import PropTypes from 'prop-types'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Tooltip from '@mui/material/Tooltip'; +import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined'; +import Link from 'docs/src/modules/components/Link'; +import IconImage from 'docs/src/components/icon/IconImage'; +import LaunchRounded from '@mui/icons-material/LaunchRounded'; +import GradientText from 'docs/src/components/typography/GradientText'; + +const ColumnHead = ({ label, metadata, tooltip, nested = false, href }) => { + const text = ( + svg': { color: 'primary.main' }, + ...(href && { + fontWeight: 500, + '&:hover > svg': { + opacity: 1, + ml: 0.5, + }, + }), + }} + > + {label}{' '} + {href ? ( + + ) : null} + {tooltip ? ( + + ) : null} + + ); + return ( + + {tooltip ? ( + + {text} + + ) : ( + text + )} + {metadata ? ( + + {metadata} + + ) : null} + + ); +}; + +ColumnHead.propTypes = { + href: PropTypes.string, + label: PropTypes.string.isRequired, + metadata: PropTypes.string, + nested: PropTypes.bool, + tooltip: PropTypes.string, +}; + +const plans = ['community', 'commercial']; + +const Headline = ( + + Different plan sizes to fit
+ every need +
+); + +const planInfo = { + community: { + color: 'green', + title: 'Community', + description: + 'Get started with a powerful open-source low-code platform, MIT-licensed, free forever', + }, + commercial: { + color: 'blue', + title: 'Commercial', + description: + 'Paid plans for large teams and organizations with advanced security, control, and support needs', + }, +}; + +const rowHeaders = { + // Hosting + 'Self-hosting': , + 'Cloud hosting': , + // Apps + Apps: , + 'Data sources': , + 'Export to code': , + 'Plugging API': ( + + ), + 'Built-in version control': ( + + ), + 'Git version control': ( + + ), + 'Staging environments': , + 'White label': , + 'Custom themes': , + // Components + Components: , + 'Custom components': ( + + ), + 'Premium components': , + // Users + 'Real-time commenting': ( + + ), + 'Multiple organizations': ( + + ), + // Security + 'OAuth2/OpenID SSO': , + 'SSO enforcement': , + 'SAML-based SSO': ( + + ), + 'Granular permissions': ( + + ), + 'Audit logs': ( + + ), + // Support + 'Support level': , +}; + +const yes = ; +const pending = ; +const no = ; + +function Info(props) { + const { value, metadata } = props; + return ( + + {typeof value === 'string' ? {value} : value} + {metadata ? ( + + {metadata} + + ) : null} + + ); +} + +Info.propTypes = { + metadata: PropTypes.string, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, +}; + +const communityData = { + // Hosting + 'Self-hosting': yes, + 'Cloud hosting': no, + // Apps + Apps: , + 'Data sources': , + 'Export to code': pending, + 'Plugging API': pending, + 'Built-in version control': no, + 'Git version control': ( + + ), + 'Staging environments': , + 'White label': no, + 'Custom themes': no, + // Components + Components: yes, + 'Custom components': yes, + 'Premium components': no, + // Users + 'Real-time commenting': no, + 'Multiple organizations': no, + // Security + 'OAuth2/OpenID SSO': pending, + 'SSO enforcement': no, + 'SAML-based SSO': no, + 'Granular permissions': no, + 'Audit logs': no, + // Support + 'Support level': , +}; + +const commercialData = { + // Hosting + 'Self-hosting': pending, + 'Cloud hosting': pending, + // Apps + Apps: , + 'Data sources': , + 'Export to code': pending, + 'Plugging API': pending, + 'Built-in version control': pending, + 'Git version control': , + 'Staging environments': , + 'White label': pending, + 'Custom themes': pending, + // Components + Components: pending, + 'Custom components': pending, + 'Premium components': pending, + // Users + 'Real-time commenting': pending, + 'Multiple organizations': pending, + // Security + 'OAuth2/OpenID SSO': pending, + 'SSO enforcement': pending, + 'SAML-based SSO': pending, + 'Granular permissions': pending, + 'Audit logs': pending, + // Support + 'Support level': , +}; + +export { Headline, planInfo, plans, rowHeaders, communityData, commercialData }; diff --git a/docs/src/content/upvote.js b/docs/src/content/upvote.js new file mode 100644 index 00000000000..17370039a05 --- /dev/null +++ b/docs/src/content/upvote.js @@ -0,0 +1,13 @@ +import ROUTES from '../route'; + +const upvote = { + title: '👍 Upvote the features you want to get prioritized', + description: + 'Want to request a feature? Head over to our repo and if not already listed, feel free to open an issue explaining the use case.', + action: { + href: ROUTES.toolpadUpvote, + label: 'Upvote', + }, +}; + +export default upvote; diff --git a/docs/src/content/useCases.js b/docs/src/content/useCases.js new file mode 100644 index 00000000000..da0a82ecb8a --- /dev/null +++ b/docs/src/content/useCases.js @@ -0,0 +1,37 @@ +import * as React from 'react'; +import Typography from '@mui/material/Typography'; +import GradientText from 'docs/src/components/typography/GradientText'; + +const useCases = { + overline: 'Use cases', + Headline: ( + + Toolpad is ideal for building + + ), + cards: [ + { + title: 'Monitoring dashboard', + wip: false, + imageUrl: '/static/toolpad/marketing/toolpad-monitoring-dashboard.svg', + description: + 'Quickly provide the latest numbers and status reports that fuel data-driven decisions. Build and share your dashboard by combining data from multiple sources with dependable deployment mechanism when needed.', + }, + { + title: 'Admin panel', + wip: false, + imageUrl: '/static/toolpad/marketing/toolpad-admin-panel.svg', + description: + 'Enable your teams to quickly view and manage customer orders, queries, and refunds by creating apps powered by native Toolpad integrations and third party APIs from providers like Stripe, Twilio, etc. Toolpad allows end users to create, read, update or delete records.', + }, + { + title: 'Custom CMS', + wip: false, + imageUrl: '/static/toolpad/marketing/toolpad-custom-cms.svg', + description: + 'Enable your operations teams to efficiently manage day to day chores using task specific content management apps. Built with MUI components, apps are fast, manageable and scalable. Toolpad allows you to bind data the pro-code way by writing JavaScript anywhere. ', + }, + ], +}; + +export default useCases; diff --git a/docs/src/layouts/ToolpadHeroContainer.tsx b/docs/src/layouts/ToolpadHeroContainer.tsx index fd4ccbc793d..a0aec100a3d 100644 --- a/docs/src/layouts/ToolpadHeroContainer.tsx +++ b/docs/src/layouts/ToolpadHeroContainer.tsx @@ -1,25 +1,28 @@ import * as React from 'react'; -import Box from '@mui/material/Box'; +import { SxProps } from '@mui/material/styles'; import Container from '@mui/material/Container'; -import Grid from '@mui/material/Grid'; -export default function HeroContainer({ children }: { children: React.ReactNode }) { +export default function HeroContainer({ + children, + sx, +}: { + children: React.ReactNode; + sx: SxProps; +}) { return ( - - - - - {children} - - - - + + {children} + ); } diff --git a/docs/src/route.ts b/docs/src/route.ts index 562ce3c345d..85bea37a4c9 100644 --- a/docs/src/route.ts +++ b/docs/src/route.ts @@ -1,5 +1,11 @@ const ROUTES = { toolpadDocs: '/toolpad/getting-started/setup', + toolpadUpvote: 'https://github.com/mui/mui-toolpad/labels/waiting%20for%20%F0%9F%91%8D', + // https://docs.netlify.com/site-deploys/overview/#deploy-contexts + TOOLPAD_SIGN_UP_URL: + process.env.DEPLOY_ENV !== 'production' + ? 'https://f0433e60.sibforms.com/serve/MUIEANZyAq3x75eMN1roZIpmPb4TE8EFhTZAvbUcFysXtDEaSpWoWwukcd26APxXYlq2qPzESe-CyKn52WqDgXIXUPiMd-8woOfv4ohmvQofRqFmkluZziib-OT4zdgZSMERv5osdP4HpbmOMEQ06hNHcEBKOVzBIUMJyZ9OFCjMMfg9cg_eRqVmarrsi0AZ1x4Hrt0dXSmuyCw1' + : 'https://f0433e60.sibforms.com/serve/MUIEADGEjwspZFTdhelJvoqYipRwwszCE3d2K_wzbDDbLwVzsi_-xrh0yEItuylHQ7CCxE__clpeu8zG2X4CylBK1PPKFhI65Id51tGoiPnc_HEdyJtJOOXRyDTiMWFO9pXsA93tx2yCWkT6rGCS-uxh_2yJMNATGODr9buDsLN7R7R0cTjBMZoUzNy3l8CtuuX-PwSct9wuJvV0', }; export default ROUTES; diff --git a/yarn.lock b/yarn.lock index f4d1d0fcbb8..64c5d7ccca0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2040,8 +2040,8 @@ react-transition-group "^4.4.2" "@mui/monorepo@https://github.com/mui/material-ui.git": - version "5.8.7" - resolved "https://github.com/mui/material-ui.git#6e69c8e8db54305f7602e11ea76b9b13adcf04cd" + version "5.10.1" + resolved "https://github.com/mui/material-ui.git#8d6bae2e39b9c6e28caf49e0fb930ccfba4b711f" "@mui/private-theming@^5.8.0": version "5.8.6"