added theme

This commit is contained in:
Arnaud Fauconnet 2022-10-28 11:41:58 +02:00
parent 99b63d95fd
commit 328a760eb8
67 changed files with 4358 additions and 6 deletions

View File

@ -15,7 +15,9 @@
"@emotion/react": "^11.10.4",
"@emotion/styled": "^11.10.4",
"@hookform/resolvers": "^2.9.10",
"@mui/icons-material": "^5.10.9",
"@mui/material": "^5.10.9",
"@mui/x-date-pickers": "^5.0.5",
"@types/jest": "^29.1.1",
"@types/node": "^18.8.2",
"@types/node-sass": "^4.11.3",
@ -24,10 +26,14 @@
"@types/react-router-dom": "^5.3.3",
"babel-loader": "^8.2.5",
"css-loader": "^6.7.1",
"dayjs": "^1.11.6",
"final-form": "^4.20.7",
"html-webpack-plugin": "^5.5.0",
"markdown-to-jsx": "^7.1.7",
"mini-css-extract-plugin": "^2.6.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-final-form": "^6.5.9",
"react-hook-form": "^7.38.0",
"react-router-dom": "^6.4.2",
"sass": "^1.55.0",
@ -40,5 +46,8 @@
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.11.1",
"yup": "^0.32.11"
},
"dependencies": {
"moment": "^2.29.4"
}
}

View File

@ -0,0 +1,93 @@
import * as React from 'react';
import { Field, Form, FormSpy } from 'react-final-form';
import Box from '@mui/material/Box';
import Typography from './modules/components/Typography';
import AppFooter from './modules/views/AppFooter';
import AppAppBar from './modules/views/AppAppBar';
import AppForm from './modules/views/AppForm';
import { email, required } from './modules/form/validation';
import RFTextField from './modules/form/RFTextField';
import FormButton from './modules/form/FormButton';
import FormFeedback from './modules/form/FormFeedback';
import withRoot from './modules/withRoot';
function ForgotPassword() {
const [sent, setSent] = React.useState(false);
const validate = (values) => {
const errors = required(['email'], values);
if (!errors.email) {
const emailError = email(values.email);
if (emailError) {
errors.email = emailError;
}
}
return errors;
};
const handleSubmit = () => {
setSent(true);
};
return (
<React.Fragment>
<AppAppBar />
<AppForm>
<React.Fragment>
<Typography variant="h3" gutterBottom marked="center" align="center">
Forgot your password?
</Typography>
<Typography variant="body2" align="center">
{"Enter your email address below and we'll " +
'send you a link to reset your password.'}
</Typography>
</React.Fragment>
<Form
onSubmit={handleSubmit}
subscription={{ submitting: true }}
validate={validate}
>
{({ handleSubmit: handleSubmit2, submitting }) => (
<Box component="form" onSubmit={handleSubmit2} noValidate sx={{ mt: 6 }}>
<Field
autoFocus
autoComplete="email"
component={RFTextField}
disabled={submitting || sent}
fullWidth
label="Email"
margin="normal"
name="email"
required
size="large"
/>
<FormSpy subscription={{ submitError: true }}>
{({ submitError }) =>
submitError ? (
<FormFeedback error sx={{ mt: 2 }}>
{submitError}
</FormFeedback>
) : null
}
</FormSpy>
<FormButton
sx={{ mt: 3, mb: 2 }}
disabled={submitting || sent}
size="large"
color="secondary"
fullWidth
>
{submitting || sent ? 'In progress…' : 'Send reset link'}
</FormButton>
</Box>
)}
</Form>
</AppForm>
<AppFooter />
</React.Fragment>
);
}
export default withRoot(ForgotPassword);

View File

@ -0,0 +1,93 @@
import * as React from 'react';
import { Field, Form, FormSpy } from 'react-final-form';
import Box from '@mui/material/Box';
import Typography from './modules/components/Typography';
import AppFooter from './modules/views/AppFooter';
import AppAppBar from './modules/views/AppAppBar';
import AppForm from './modules/views/AppForm';
import { email, required } from './modules/form/validation';
import RFTextField from './modules/form/RFTextField';
import FormButton from './modules/form/FormButton';
import FormFeedback from './modules/form/FormFeedback';
import withRoot from './modules/withRoot';
function ForgotPassword() {
const [sent, setSent] = React.useState(false);
const validate = (values: { [index: string]: string }) => {
const errors = required(['email'], values);
if (!errors.email) {
const emailError = email(values.email);
if (emailError) {
errors.email = emailError;
}
}
return errors;
};
const handleSubmit = () => {
setSent(true);
};
return (
<React.Fragment>
<AppAppBar />
<AppForm>
<React.Fragment>
<Typography variant="h3" gutterBottom marked="center" align="center">
Forgot your password?
</Typography>
<Typography variant="body2" align="center">
{"Enter your email address below and we'll " +
'send you a link to reset your password.'}
</Typography>
</React.Fragment>
<Form
onSubmit={handleSubmit}
subscription={{ submitting: true }}
validate={validate}
>
{({ handleSubmit: handleSubmit2, submitting }) => (
<Box component="form" onSubmit={handleSubmit2} noValidate sx={{ mt: 6 }}>
<Field
autoFocus
autoComplete="email"
component={RFTextField}
disabled={submitting || sent}
fullWidth
label="Email"
margin="normal"
name="email"
required
size="large"
/>
<FormSpy subscription={{ submitError: true }}>
{({ submitError }) =>
submitError ? (
<FormFeedback error sx={{ mt: 2 }}>
{submitError}
</FormFeedback>
) : null
}
</FormSpy>
<FormButton
sx={{ mt: 3, mb: 2 }}
disabled={submitting || sent}
size="large"
color="secondary"
fullWidth
>
{submitting || sent ? 'In progress…' : 'Send reset link'}
</FormButton>
</Box>
)}
</Form>
</AppForm>
<AppFooter />
</React.Fragment>
);
}
export default withRoot(ForgotPassword);

27
themes/onepirate/Home.js Normal file
View File

@ -0,0 +1,27 @@
import * as React from 'react';
import ProductCategories from './modules/views/ProductCategories';
import ProductSmokingHero from './modules/views/ProductSmokingHero';
import AppFooter from './modules/views/AppFooter';
import ProductHero from './modules/views/ProductHero';
import ProductValues from './modules/views/ProductValues';
import ProductHowItWorks from './modules/views/ProductHowItWorks';
import ProductCTA from './modules/views/ProductCTA';
import AppAppBar from './modules/views/AppAppBar';
import withRoot from './modules/withRoot';
function Index() {
return (
<React.Fragment>
<AppAppBar />
<ProductHero />
<ProductValues />
<ProductCategories />
<ProductHowItWorks />
<ProductCTA />
<ProductSmokingHero />
<AppFooter />
</React.Fragment>
);
}
export default withRoot(Index);

27
themes/onepirate/Home.tsx Normal file
View File

@ -0,0 +1,27 @@
import * as React from 'react';
import ProductCategories from './modules/views/ProductCategories';
import ProductSmokingHero from './modules/views/ProductSmokingHero';
import AppFooter from './modules/views/AppFooter';
import ProductHero from './modules/views/ProductHero';
import ProductValues from './modules/views/ProductValues';
import ProductHowItWorks from './modules/views/ProductHowItWorks';
import ProductCTA from './modules/views/ProductCTA';
import AppAppBar from './modules/views/AppAppBar';
import withRoot from './modules/withRoot';
function Index() {
return (
<React.Fragment>
<AppAppBar />
<ProductHero />
<ProductValues />
<ProductCategories />
<ProductHowItWorks />
<ProductCTA />
<ProductSmokingHero />
<AppFooter />
</React.Fragment>
);
}
export default withRoot(Index);

View File

@ -0,0 +1,28 @@
import * as React from 'react';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Markdown from './modules/components/Markdown';
import Typography from './modules/components/Typography';
import AppAppBar from './modules/views/AppAppBar';
import AppFooter from './modules/views/AppFooter';
import withRoot from './modules/withRoot';
import privacy from './modules/views/privacy.md';
function Privacy() {
return (
<React.Fragment>
<AppAppBar />
<Container>
<Box sx={{ mt: 7, mb: 12 }}>
<Typography variant="h3" gutterBottom marked="center" align="center">
Privacy
</Typography>
<Markdown>{privacy}</Markdown>
</Box>
</Container>
<AppFooter />
</React.Fragment>
);
}
export default withRoot(Privacy);

View File

@ -0,0 +1,28 @@
import * as React from 'react';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Markdown from './modules/components/Markdown';
import Typography from './modules/components/Typography';
import AppAppBar from './modules/views/AppAppBar';
import AppFooter from './modules/views/AppFooter';
import withRoot from './modules/withRoot';
import privacy from './modules/views/privacy.md';
function Privacy() {
return (
<React.Fragment>
<AppAppBar />
<Container>
<Box sx={{ mt: 7, mb: 12 }}>
<Typography variant="h3" gutterBottom marked="center" align="center">
Privacy
</Typography>
<Markdown>{privacy}</Markdown>
</Box>
</Container>
<AppFooter />
</React.Fragment>
);
}
export default withRoot(Privacy);

117
themes/onepirate/SignIn.js Normal file
View File

@ -0,0 +1,117 @@
import * as React from 'react';
import { Field, Form, FormSpy } from 'react-final-form';
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import Typography from './modules/components/Typography';
import AppFooter from './modules/views/AppFooter';
import AppAppBar from './modules/views/AppAppBar';
import AppForm from './modules/views/AppForm';
import { email, required } from './modules/form/validation';
import RFTextField from './modules/form/RFTextField';
import FormButton from './modules/form/FormButton';
import FormFeedback from './modules/form/FormFeedback';
import withRoot from './modules/withRoot';
function SignIn() {
const [sent, setSent] = React.useState(false);
const validate = (values) => {
const errors = required(['email', 'password'], values);
if (!errors.email) {
const emailError = email(values.email);
if (emailError) {
errors.email = emailError;
}
}
return errors;
};
const handleSubmit = () => {
setSent(true);
};
return (
<React.Fragment>
<AppAppBar />
<AppForm>
<React.Fragment>
<Typography variant="h3" gutterBottom marked="center" align="center">
Sign In
</Typography>
<Typography variant="body2" align="center">
{'Not a member yet? '}
<Link
href="/premium-themes/onepirate/sign-up/"
align="center"
underline="always"
>
Sign Up here
</Link>
</Typography>
</React.Fragment>
<Form
onSubmit={handleSubmit}
subscription={{ submitting: true }}
validate={validate}
>
{({ handleSubmit: handleSubmit2, submitting }) => (
<Box component="form" onSubmit={handleSubmit2} noValidate sx={{ mt: 6 }}>
<Field
autoComplete="email"
autoFocus
component={RFTextField}
disabled={submitting || sent}
fullWidth
label="Email"
margin="normal"
name="email"
required
size="large"
/>
<Field
fullWidth
size="large"
component={RFTextField}
disabled={submitting || sent}
required
name="password"
autoComplete="current-password"
label="Password"
type="password"
margin="normal"
/>
<FormSpy subscription={{ submitError: true }}>
{({ submitError }) =>
submitError ? (
<FormFeedback error sx={{ mt: 2 }}>
{submitError}
</FormFeedback>
) : null
}
</FormSpy>
<FormButton
sx={{ mt: 3, mb: 2 }}
disabled={submitting || sent}
size="large"
color="secondary"
fullWidth
>
{submitting || sent ? 'In progress…' : 'Sign In'}
</FormButton>
</Box>
)}
</Form>
<Typography align="center">
<Link underline="always" href="/premium-themes/onepirate/forgot-password/">
Forgot password?
</Link>
</Typography>
</AppForm>
<AppFooter />
</React.Fragment>
);
}
export default withRoot(SignIn);

117
themes/onepirate/SignIn.tsx Normal file
View File

@ -0,0 +1,117 @@
import * as React from 'react';
import { Field, Form, FormSpy } from 'react-final-form';
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import Typography from './modules/components/Typography';
import AppFooter from './modules/views/AppFooter';
import AppAppBar from './modules/views/AppAppBar';
import AppForm from './modules/views/AppForm';
import { email, required } from './modules/form/validation';
import RFTextField from './modules/form/RFTextField';
import FormButton from './modules/form/FormButton';
import FormFeedback from './modules/form/FormFeedback';
import withRoot from './modules/withRoot';
function SignIn() {
const [sent, setSent] = React.useState(false);
const validate = (values: { [index: string]: string }) => {
const errors = required(['email', 'password'], values);
if (!errors.email) {
const emailError = email(values.email);
if (emailError) {
errors.email = emailError;
}
}
return errors;
};
const handleSubmit = () => {
setSent(true);
};
return (
<React.Fragment>
<AppAppBar />
<AppForm>
<React.Fragment>
<Typography variant="h3" gutterBottom marked="center" align="center">
Sign In
</Typography>
<Typography variant="body2" align="center">
{'Not a member yet? '}
<Link
href="/premium-themes/onepirate/sign-up/"
align="center"
underline="always"
>
Sign Up here
</Link>
</Typography>
</React.Fragment>
<Form
onSubmit={handleSubmit}
subscription={{ submitting: true }}
validate={validate}
>
{({ handleSubmit: handleSubmit2, submitting }) => (
<Box component="form" onSubmit={handleSubmit2} noValidate sx={{ mt: 6 }}>
<Field
autoComplete="email"
autoFocus
component={RFTextField}
disabled={submitting || sent}
fullWidth
label="Email"
margin="normal"
name="email"
required
size="large"
/>
<Field
fullWidth
size="large"
component={RFTextField}
disabled={submitting || sent}
required
name="password"
autoComplete="current-password"
label="Password"
type="password"
margin="normal"
/>
<FormSpy subscription={{ submitError: true }}>
{({ submitError }) =>
submitError ? (
<FormFeedback error sx={{ mt: 2 }}>
{submitError}
</FormFeedback>
) : null
}
</FormSpy>
<FormButton
sx={{ mt: 3, mb: 2 }}
disabled={submitting || sent}
size="large"
color="secondary"
fullWidth
>
{submitting || sent ? 'In progress…' : 'Sign In'}
</FormButton>
</Box>
)}
</Form>
<Typography align="center">
<Link underline="always" href="/premium-themes/onepirate/forgot-password/">
Forgot password?
</Link>
</Typography>
</AppForm>
<AppFooter />
</React.Fragment>
);
}
export default withRoot(SignIn);

129
themes/onepirate/SignUp.js Normal file
View File

@ -0,0 +1,129 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Link from '@mui/material/Link';
import { Field, Form, FormSpy } from 'react-final-form';
import Typography from './modules/components/Typography';
import AppFooter from './modules/views/AppFooter';
import AppAppBar from './modules/views/AppAppBar';
import AppForm from './modules/views/AppForm';
import { email, required } from './modules/form/validation';
import RFTextField from './modules/form/RFTextField';
import FormButton from './modules/form/FormButton';
import FormFeedback from './modules/form/FormFeedback';
import withRoot from './modules/withRoot';
function SignUp() {
const [sent, setSent] = React.useState(false);
const validate = (values) => {
const errors = required(['firstName', 'lastName', 'email', 'password'], values);
if (!errors.email) {
const emailError = email(values.email);
if (emailError) {
errors.email = emailError;
}
}
return errors;
};
const handleSubmit = () => {
setSent(true);
};
return (
<React.Fragment>
<AppAppBar />
<AppForm>
<React.Fragment>
<Typography variant="h3" gutterBottom marked="center" align="center">
Sign Up
</Typography>
<Typography variant="body2" align="center">
<Link href="/premium-themes/onepirate/sign-in/" underline="always">
Already have an account?
</Link>
</Typography>
</React.Fragment>
<Form
onSubmit={handleSubmit}
subscription={{ submitting: true }}
validate={validate}
>
{({ handleSubmit: handleSubmit2, submitting }) => (
<Box component="form" onSubmit={handleSubmit2} noValidate sx={{ mt: 6 }}>
<Grid container spacing={2}>
<Grid item xs={12} sm={6}>
<Field
autoFocus
component={RFTextField}
disabled={submitting || sent}
autoComplete="given-name"
fullWidth
label="First name"
name="firstName"
required
/>
</Grid>
<Grid item xs={12} sm={6}>
<Field
component={RFTextField}
disabled={submitting || sent}
autoComplete="family-name"
fullWidth
label="Last name"
name="lastName"
required
/>
</Grid>
</Grid>
<Field
autoComplete="email"
component={RFTextField}
disabled={submitting || sent}
fullWidth
label="Email"
margin="normal"
name="email"
required
/>
<Field
fullWidth
component={RFTextField}
disabled={submitting || sent}
required
name="password"
autoComplete="new-password"
label="Password"
type="password"
margin="normal"
/>
<FormSpy subscription={{ submitError: true }}>
{({ submitError }) =>
submitError ? (
<FormFeedback error sx={{ mt: 2 }}>
{submitError}
</FormFeedback>
) : null
}
</FormSpy>
<FormButton
sx={{ mt: 3, mb: 2 }}
disabled={submitting || sent}
color="secondary"
fullWidth
>
{submitting || sent ? 'In progress…' : 'Sign Up'}
</FormButton>
</Box>
)}
</Form>
</AppForm>
<AppFooter />
</React.Fragment>
);
}
export default withRoot(SignUp);

129
themes/onepirate/SignUp.tsx Normal file
View File

@ -0,0 +1,129 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Link from '@mui/material/Link';
import { Field, Form, FormSpy } from 'react-final-form';
import Typography from './modules/components/Typography';
import AppFooter from './modules/views/AppFooter';
import AppAppBar from './modules/views/AppAppBar';
import AppForm from './modules/views/AppForm';
import { email, required } from './modules/form/validation';
import RFTextField from './modules/form/RFTextField';
import FormButton from './modules/form/FormButton';
import FormFeedback from './modules/form/FormFeedback';
import withRoot from './modules/withRoot';
function SignUp() {
const [sent, setSent] = React.useState(false);
const validate = (values: { [index: string]: string }) => {
const errors = required(['firstName', 'lastName', 'email', 'password'], values);
if (!errors.email) {
const emailError = email(values.email);
if (emailError) {
errors.email = emailError;
}
}
return errors;
};
const handleSubmit = () => {
setSent(true);
};
return (
<React.Fragment>
<AppAppBar />
<AppForm>
<React.Fragment>
<Typography variant="h3" gutterBottom marked="center" align="center">
Sign Up
</Typography>
<Typography variant="body2" align="center">
<Link href="/premium-themes/onepirate/sign-in/" underline="always">
Already have an account?
</Link>
</Typography>
</React.Fragment>
<Form
onSubmit={handleSubmit}
subscription={{ submitting: true }}
validate={validate}
>
{({ handleSubmit: handleSubmit2, submitting }) => (
<Box component="form" onSubmit={handleSubmit2} noValidate sx={{ mt: 6 }}>
<Grid container spacing={2}>
<Grid item xs={12} sm={6}>
<Field
autoFocus
component={RFTextField}
disabled={submitting || sent}
autoComplete="given-name"
fullWidth
label="First name"
name="firstName"
required
/>
</Grid>
<Grid item xs={12} sm={6}>
<Field
component={RFTextField}
disabled={submitting || sent}
autoComplete="family-name"
fullWidth
label="Last name"
name="lastName"
required
/>
</Grid>
</Grid>
<Field
autoComplete="email"
component={RFTextField}
disabled={submitting || sent}
fullWidth
label="Email"
margin="normal"
name="email"
required
/>
<Field
fullWidth
component={RFTextField}
disabled={submitting || sent}
required
name="password"
autoComplete="new-password"
label="Password"
type="password"
margin="normal"
/>
<FormSpy subscription={{ submitError: true }}>
{({ submitError }) =>
submitError ? (
<FormFeedback error sx={{ mt: 2 }}>
{submitError}
</FormFeedback>
) : null
}
</FormSpy>
<FormButton
sx={{ mt: 3, mb: 2 }}
disabled={submitting || sent}
color="secondary"
fullWidth
>
{submitting || sent ? 'In progress…' : 'Sign Up'}
</FormButton>
</Box>
)}
</Form>
</AppForm>
<AppFooter />
</React.Fragment>
);
}
export default withRoot(SignUp);

28
themes/onepirate/Terms.js Normal file
View File

@ -0,0 +1,28 @@
import * as React from 'react';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Markdown from './modules/components/Markdown';
import Typography from './modules/components/Typography';
import AppAppBar from './modules/views/AppAppBar';
import AppFooter from './modules/views/AppFooter';
import withRoot from './modules/withRoot';
import terms from './modules/views/terms.md';
function Terms() {
return (
<React.Fragment>
<AppAppBar />
<Container>
<Box sx={{ mt: 7, mb: 12 }}>
<Typography variant="h3" gutterBottom marked="center" align="center">
Terms
</Typography>
<Markdown>{terms}</Markdown>
</Box>
</Container>
<AppFooter />
</React.Fragment>
);
}
export default withRoot(Terms);

View File

@ -0,0 +1,28 @@
import * as React from 'react';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Markdown from './modules/components/Markdown';
import Typography from './modules/components/Typography';
import AppAppBar from './modules/views/AppAppBar';
import AppFooter from './modules/views/AppFooter';
import withRoot from './modules/withRoot';
import terms from './modules/views/terms.md';
function Terms() {
return (
<React.Fragment>
<AppAppBar />
<Container>
<Box sx={{ mt: 7, mb: 12 }}>
<Typography variant="h3" gutterBottom marked="center" align="center">
Terms
</Typography>
<Markdown>{terms}</Markdown>
</Box>
</Container>
<AppFooter />
</React.Fragment>
);
}
export default withRoot(Terms);

1
themes/onepirate/modules.d.ts vendored Normal file
View File

@ -0,0 +1 @@
declare module '*.md';

View File

@ -0,0 +1,8 @@
import * as React from 'react';
import MuiAppBar from '@mui/material/AppBar';
function AppBar(props) {
return <MuiAppBar elevation={0} position="fixed" {...props} />;
}
export default AppBar;

View File

@ -0,0 +1,8 @@
import * as React from 'react';
import MuiAppBar, { AppBarProps } from '@mui/material/AppBar';
function AppBar(props: AppBarProps) {
return <MuiAppBar elevation={0} position="fixed" {...props} />;
}
export default AppBar;

View File

@ -0,0 +1,30 @@
import * as React from 'react';
import { experimentalStyled as styled } from '@mui/material/styles';
import MuiButton from '@mui/material/Button';
const ButtonRoot = styled(MuiButton)(({ theme, size }) => ({
borderRadius: 0,
fontWeight: theme.typography.fontWeightMedium,
fontFamily: theme.typography.h1.fontFamily,
padding: theme.spacing(2, 4),
fontSize: theme.typography.pxToRem(14),
boxShadow: 'none',
'&:active, &:focus': {
boxShadow: 'none',
},
...(size === 'small' && {
padding: theme.spacing(1, 3),
fontSize: theme.typography.pxToRem(13),
}),
...(size === 'large' && {
padding: theme.spacing(2, 5),
fontSize: theme.typography.pxToRem(16),
}),
}));
// See https://mui.com/guides/typescript/#usage-of-component-prop for why the types uses `C`.
function Button(props) {
return <ButtonRoot {...props} />;
}
export default Button;

View File

@ -0,0 +1,32 @@
import * as React from 'react';
import { experimentalStyled as styled } from '@mui/material/styles';
import MuiButton, { ButtonProps } from '@mui/material/Button';
const ButtonRoot = styled(MuiButton)(({ theme, size }) => ({
borderRadius: 0,
fontWeight: theme.typography.fontWeightMedium,
fontFamily: theme.typography.h1.fontFamily,
padding: theme.spacing(2, 4),
fontSize: theme.typography.pxToRem(14),
boxShadow: 'none',
'&:active, &:focus': {
boxShadow: 'none',
},
...(size === 'small' && {
padding: theme.spacing(1, 3),
fontSize: theme.typography.pxToRem(13),
}),
...(size === 'large' && {
padding: theme.spacing(2, 5),
fontSize: theme.typography.pxToRem(16),
}),
}));
// See https://mui.com/guides/typescript/#usage-of-component-prop for why the types uses `C`.
function Button<C extends React.ElementType>(
props: ButtonProps<C, { component?: C }>,
) {
return <ButtonRoot {...props} />;
}
export default Button;

View File

@ -0,0 +1,49 @@
import * as React from 'react';
import ReactMarkdown from 'markdown-to-jsx';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import Link from '@mui/material/Link';
const options = {
overrides: {
h1: {
component: Typography,
props: {
gutterBottom: true,
variant: 'h4',
},
},
h2: {
component: Typography,
props: { gutterBottom: true, variant: 'h6' },
},
h3: {
component: Typography,
props: { gutterBottom: true, variant: 'subtitle1' },
},
h4: {
component: Typography,
props: {
gutterBottom: true,
variant: 'caption',
paragraph: true,
},
},
p: {
component: Typography,
props: { paragraph: true },
},
a: { component: Link },
li: {
component: (props) => (
<Box component="li" sx={{ mt: 1 }}>
<Typography component="span" {...props} />
</Box>
),
},
},
};
export default function Markdown(props) {
return <ReactMarkdown options={options} {...props} />;
}

View File

@ -0,0 +1,49 @@
import * as React from 'react';
import ReactMarkdown from 'markdown-to-jsx';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import Link from '@mui/material/Link';
const options = {
overrides: {
h1: {
component: Typography,
props: {
gutterBottom: true,
variant: 'h4',
},
},
h2: {
component: Typography,
props: { gutterBottom: true, variant: 'h6' },
},
h3: {
component: Typography,
props: { gutterBottom: true, variant: 'subtitle1' },
},
h4: {
component: Typography,
props: {
gutterBottom: true,
variant: 'caption',
paragraph: true,
},
},
p: {
component: Typography,
props: { paragraph: true },
},
a: { component: Link },
li: {
component: (props: any) => (
<Box component="li" sx={{ mt: 1 }}>
<Typography component="span" {...props} />
</Box>
),
},
},
};
export default function Markdown(props: any) {
return <ReactMarkdown options={options} {...props} />;
}

View File

@ -0,0 +1,40 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import MuiPaper from '@mui/material/Paper';
import { styled } from '@mui/material/styles';
const PaperRoot = styled(MuiPaper, {
shouldForwardProp: (prop) => prop !== 'background' && prop !== 'padding',
})(({ theme, background, padding }) => ({
backgroundColor: theme.palette.secondary[background],
...(padding && {
padding: theme.spacing(1),
}),
}));
function Paper(props) {
const { background, classes, className, padding = false, ...other } = props;
return (
<PaperRoot
square
elevation={0}
background={background}
padding={padding}
className={className}
{...other}
/>
);
}
Paper.propTypes = {
background: PropTypes.oneOf(['dark', 'light', 'main']).isRequired,
/**
* Override or extend the styles applied to the component.
*/
classes: PropTypes.object,
className: PropTypes.string,
padding: PropTypes.bool,
};
export default Paper;

View File

@ -0,0 +1,32 @@
import * as React from 'react';
import MuiPaper, { PaperProps } from '@mui/material/Paper';
import { styled } from '@mui/material/styles';
interface ExtraPaperProps {
background: 'light' | 'main' | 'dark';
padding?: boolean;
}
const PaperRoot = styled(MuiPaper, {
shouldForwardProp: (prop) => prop !== 'background' && prop !== 'padding',
})<ExtraPaperProps>(({ theme, background, padding }) => ({
backgroundColor: theme.palette.secondary[background],
...(padding && {
padding: theme.spacing(1),
}),
}));
export default function Paper(props: PaperProps & ExtraPaperProps) {
const { background, classes, className, padding = false, ...other } = props;
return (
<PaperRoot
square
elevation={0}
background={background}
padding={padding}
className={className}
{...other}
/>
);
}

View File

@ -0,0 +1,86 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { styled } from '@mui/material/styles';
import MuiSnackbar from '@mui/material/Snackbar';
import { snackbarContentClasses } from '@mui/material/SnackbarContent';
import Slide from '@mui/material/Slide';
import CloseIcon from '@mui/icons-material/Close';
import InfoIcon from '@mui/icons-material/Info';
import IconButton from '@mui/material/IconButton';
const styles = ({ theme }) => ({
[`& .${snackbarContentClasses.root}`]: {
backgroundColor: theme.palette.secondary.light,
color: theme.palette.text.primary,
flexWrap: 'inherit',
[theme.breakpoints.up('md')]: {
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
borderBottomRightRadius: 4,
borderBottomLeftRadius: 4,
},
},
[`& .${snackbarContentClasses.message}`]: {
fontSize: 16,
display: 'flex',
alignItems: 'center',
},
[`& .${snackbarContentClasses.action}`]: {
paddingLeft: theme.spacing(2),
},
'& .MuiSnackbarContent-info': {
flexShrink: 0,
marginRight: theme.spacing(2),
},
'& .MuiSnackbarContent-close': {
padding: theme.spacing(1),
},
});
function Transition(props) {
return <Slide {...props} direction="down" />;
}
function Snackbar(props) {
const { message, closeFunc, ...other } = props;
const classes = {
info: 'MuiSnackbarContent-info',
close: 'MuiSnackbarContent-close',
};
return (
<MuiSnackbar
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
autoHideDuration={6000}
TransitionComponent={Transition}
message={
<React.Fragment>
<InfoIcon className={classes.info} />
<span>{message}</span>
</React.Fragment>
}
action={[
<IconButton
key="close"
aria-label="close"
color="inherit"
className={classes.close}
onClick={() => closeFunc && closeFunc()}
>
<CloseIcon />
</IconButton>,
]}
{...other}
/>
);
}
Snackbar.propTypes = {
closeFunc: PropTypes.func,
/**
* The message to display.
*/
message: PropTypes.node,
};
export default styled(Snackbar)(styles);

View File

@ -0,0 +1,85 @@
import * as React from 'react';
import { Theme, styled } from '@mui/material/styles';
import MuiSnackbar, { SnackbarProps } from '@mui/material/Snackbar';
import { snackbarContentClasses } from '@mui/material/SnackbarContent';
import Slide from '@mui/material/Slide';
import CloseIcon from '@mui/icons-material/Close';
import InfoIcon from '@mui/icons-material/Info';
import IconButton from '@mui/material/IconButton';
import { TransitionProps } from '@mui/material/transitions/transition';
const styles = ({ theme }: { theme: Theme }) =>
({
[`& .${snackbarContentClasses.root}`]: {
backgroundColor: theme.palette.secondary.light,
color: theme.palette.text.primary,
flexWrap: 'inherit',
[theme.breakpoints.up('md')]: {
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
borderBottomRightRadius: 4,
borderBottomLeftRadius: 4,
},
},
[`& .${snackbarContentClasses.message}`]: {
fontSize: 16,
display: 'flex',
alignItems: 'center',
},
[`& .${snackbarContentClasses.action}`]: {
paddingLeft: theme.spacing(2),
},
'& .MuiSnackbarContent-info': {
flexShrink: 0,
marginRight: theme.spacing(2),
},
'& .MuiSnackbarContent-close': {
padding: theme.spacing(1),
},
} as const);
function Transition(
props: TransitionProps & { children: React.ReactElement<any, any> },
) {
return <Slide {...props} direction="down" />;
}
interface ExtraSnackbarProps {
closeFunc?: () => void;
}
function Snackbar(props: SnackbarProps & ExtraSnackbarProps) {
const { message, closeFunc, ...other } = props;
const classes = {
info: 'MuiSnackbarContent-info',
close: 'MuiSnackbarContent-close',
};
return (
<MuiSnackbar
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
autoHideDuration={6000}
TransitionComponent={Transition}
message={
<React.Fragment>
<InfoIcon className={classes.info} />
<span>{message}</span>
</React.Fragment>
}
action={[
<IconButton
key="close"
aria-label="close"
color="inherit"
className={classes.close}
onClick={() => closeFunc && closeFunc()}
>
<CloseIcon />
</IconButton>,
]}
{...other}
/>
);
}
export default styled(Snackbar)(styles);

View File

@ -0,0 +1,139 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { styled } from '@mui/material/styles';
import MuiTextField from '@mui/material/TextField';
import { selectClasses } from '@mui/material/Select';
import { inputLabelClasses } from '@mui/material/InputLabel';
const inputStyleMappingClasses = {
small: 'OnePirateTextField-inputSizeSmall',
medium: 'OnePirateTextField-inputSizeMedium',
large: 'OnePirateTextField-inputSizeLarge',
xlarge: 'OnePirateTextField-inputSizeXLarge',
};
const classes = {
root: 'OnePirateTextField-root',
input: 'OnePirateTextField-input',
inputBorder: 'OnePirateTextField-inputBorder',
};
const styles = ({ theme }) => ({
[`& .${classes.root}`]: {
padding: 0,
'label + &': {
marginTop: theme.spacing(3),
},
},
[`& .${classes.input}`]: {
minWidth: theme.spacing(6),
backgroundColor: theme.palette.common.white,
'&.Mui-disabled': {
backgroundColor: theme.palette.divider,
},
},
[`& .${classes.inputBorder}`]: {
border: '1px solid #e9ddd0',
'&:focus': {
borderColor: theme.palette.secondary.main,
},
},
[`& .${inputStyleMappingClasses.small}`]: {
fontSize: 14,
padding: theme.spacing(1),
width: `calc(100% - ${theme.spacing(2)})`,
},
[`& .${inputStyleMappingClasses.medium}`]: {
fontSize: 16,
padding: theme.spacing(2),
width: `calc(100% - ${theme.spacing(4)})`,
},
[`& .${inputStyleMappingClasses.large}`]: {
fontSize: 18,
padding: 20,
width: `calc(100% - ${20 * 2}px)`,
},
[`& .${inputStyleMappingClasses.xlarge}`]: {
fontSize: 20,
padding: 25,
width: `calc(100% - ${25 * 2}px)`,
},
[`& .${inputLabelClasses.root}`]: {
fontSize: 18,
},
[`& .${selectClasses.select}`]: {
height: 'auto',
borderRadius: 0,
},
[`& .${selectClasses.icon}`]: {
top: '50%',
marginTop: -12,
},
});
function TextField(props) {
const {
InputProps = {},
InputLabelProps,
noBorder,
size = 'medium',
SelectProps,
...other
} = props;
const {
classes: { input: InputPropsClassesInput, ...InputPropsClassesOther } = {},
...InputPropsOther
} = InputProps;
return (
<MuiTextField
InputProps={{
classes: {
root: classes.root,
input: clsx(
classes.input,
inputStyleMappingClasses[size],
{
[classes.inputBorder]: !noBorder,
},
InputPropsClassesInput,
),
...InputPropsClassesOther,
},
disableUnderline: true,
...InputPropsOther,
}}
InputLabelProps={{
...InputLabelProps,
shrink: true,
}}
SelectProps={SelectProps}
{...other}
/>
);
}
TextField.propTypes = {
/**
* Props applied to the [`InputLabel`](/material-ui/api/input-label/) element.
* Pointer events like `onClick` are enabled if and only if `shrink` is `true`.
*/
InputLabelProps: PropTypes.object,
/**
* Props applied to the Input element.
* It will be a [`FilledInput`](/material-ui/api/filled-input/),
* [`OutlinedInput`](/material-ui/api/outlined-input/) or [`Input`](/material-ui/api/input/)
* component depending on the `variant` prop value.
*/
InputProps: PropTypes.object,
noBorder: PropTypes.bool,
/**
* Props applied to the [`Select`](/material-ui/api/select/) element.
*/
SelectProps: PropTypes.object,
size: PropTypes.oneOf(['large', 'medium', 'small', 'xlarge']),
};
export default styled(TextField)(styles);

View File

@ -0,0 +1,126 @@
import * as React from 'react';
import clsx from 'clsx';
import { styled, Theme } from '@mui/material/styles';
import MuiTextField, {
FilledTextFieldProps,
StandardTextFieldProps,
} from '@mui/material/TextField';
import { selectClasses } from '@mui/material/Select';
import { inputLabelClasses } from '@mui/material/InputLabel';
const inputStyleMappingClasses = {
small: 'OnePirateTextField-inputSizeSmall',
medium: 'OnePirateTextField-inputSizeMedium',
large: 'OnePirateTextField-inputSizeLarge',
xlarge: 'OnePirateTextField-inputSizeXLarge',
};
const classes = {
root: 'OnePirateTextField-root',
input: 'OnePirateTextField-input',
inputBorder: 'OnePirateTextField-inputBorder',
};
const styles = ({ theme }: { theme: Theme }) => ({
[`& .${classes.root}`]: {
padding: 0,
'label + &': {
marginTop: theme.spacing(3),
},
},
[`& .${classes.input}`]: {
minWidth: theme.spacing(6),
backgroundColor: theme.palette.common.white,
'&.Mui-disabled': {
backgroundColor: theme.palette.divider,
},
},
[`& .${classes.inputBorder}`]: {
border: '1px solid #e9ddd0',
'&:focus': {
borderColor: theme.palette.secondary.main,
},
},
[`& .${inputStyleMappingClasses.small}`]: {
fontSize: 14,
padding: theme.spacing(1),
width: `calc(100% - ${theme.spacing(2)})`,
},
[`& .${inputStyleMappingClasses.medium}`]: {
fontSize: 16,
padding: theme.spacing(2),
width: `calc(100% - ${theme.spacing(4)})`,
},
[`& .${inputStyleMappingClasses.large}`]: {
fontSize: 18,
padding: 20,
width: `calc(100% - ${20 * 2}px)`,
},
[`& .${inputStyleMappingClasses.xlarge}`]: {
fontSize: 20,
padding: 25,
width: `calc(100% - ${25 * 2}px)`,
},
[`& .${inputLabelClasses.root}`]: {
fontSize: 18,
},
[`& .${selectClasses.select}`]: {
height: 'auto',
borderRadius: 0,
},
[`& .${selectClasses.icon}`]: {
top: '50%',
marginTop: -12,
},
});
export interface OnePirateTextFieldProps
extends Omit<FilledTextFieldProps | StandardTextFieldProps, 'size'> {
noBorder?: boolean;
size?: 'small' | 'medium' | 'large' | 'xlarge';
}
function TextField(props: OnePirateTextFieldProps) {
const {
InputProps = {},
InputLabelProps,
noBorder,
size = 'medium',
SelectProps,
...other
} = props;
const {
classes: { input: InputPropsClassesInput, ...InputPropsClassesOther } = {},
...InputPropsOther
} = InputProps;
return (
<MuiTextField
InputProps={{
classes: {
root: classes.root,
input: clsx(
classes.input,
inputStyleMappingClasses[size],
{
[classes.inputBorder]: !noBorder,
},
InputPropsClassesInput,
),
...InputPropsClassesOther,
},
disableUnderline: true,
...InputPropsOther,
}}
InputLabelProps={{
...InputLabelProps,
shrink: true,
}}
SelectProps={SelectProps}
{...other}
/>
);
}
export default styled(TextField)(styles);

View File

@ -0,0 +1,11 @@
import { styled } from '@mui/material/styles';
import MuiToolbar from '@mui/material/Toolbar';
const Toolbar = styled(MuiToolbar)(({ theme }) => ({
height: 64,
[theme.breakpoints.up('sm')]: {
height: 70,
},
}));
export default Toolbar;

View File

@ -0,0 +1,11 @@
import { styled } from '@mui/material/styles';
import MuiToolbar from '@mui/material/Toolbar';
const Toolbar = styled(MuiToolbar)(({ theme }) => ({
height: 64,
[theme.breakpoints.up('sm')]: {
height: 70,
},
}));
export default Toolbar;

View File

@ -0,0 +1,118 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { styled } from '@mui/material/styles';
import MuiTypography from '@mui/material/Typography';
const markClassesMapping = {
center: {
h1: '',
h2: 'OnePirateTypography-markedH2Center',
h3: 'OnePirateTypography-markedH3Center',
h4: 'OnePirateTypography-markedH4Center',
h5: '',
h6: '',
},
left: {
h1: '',
h2: '',
h3: '',
h4: '',
h5: '',
h6: 'OnePirateTypography-markedH6Left',
},
none: {
h1: '',
h2: '',
h3: '',
h4: '',
h5: '',
h6: '',
},
};
const styles = ({ theme }) => ({
[`& .${markClassesMapping.center.h2}`]: {
height: 4,
width: 73,
display: 'block',
margin: `${theme.spacing(1)} auto 0`,
backgroundColor: theme.palette.secondary.main,
},
[`& .${markClassesMapping.center.h3}`]: {
height: 4,
width: 55,
display: 'block',
margin: `${theme.spacing(1)} auto 0`,
backgroundColor: theme.palette.secondary.main,
},
[`& .${markClassesMapping.center.h4}`]: {
height: 4,
width: 55,
display: 'block',
margin: `${theme.spacing(1)} auto 0`,
backgroundColor: theme.palette.secondary.main,
},
[`& .${markClassesMapping.left.h6}`]: {
height: 2,
width: 28,
display: 'block',
marginTop: theme.spacing(0.5),
background: 'currentColor',
},
});
const variantMapping = {
h1: 'h1',
h2: 'h1',
h3: 'h1',
h4: 'h1',
h5: 'h3',
h6: 'h2',
subtitle1: 'h3',
};
function Typography(props) {
const { children, variant, marked = 'none', ...other } = props;
let markedClassName = '';
if (variant && variant in markClassesMapping[marked]) {
markedClassName = markClassesMapping[marked][variant];
}
return (
<MuiTypography variantMapping={variantMapping} variant={variant} {...other}>
{children}
{markedClassName ? <span className={markedClassName} /> : null}
</MuiTypography>
);
}
Typography.propTypes = {
/**
* The content of the component.
*/
children: PropTypes.node,
marked: PropTypes.oneOf(['center', 'left', 'none']),
/**
* Applies the theme typography styles.
* @default 'body1'
*/
variant: PropTypes.oneOf([
'body1',
'body2',
'button',
'caption',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'inherit',
'overline',
'subtitle1',
'subtitle2',
]),
};
export default styled(Typography)(styles);

View File

@ -0,0 +1,97 @@
import * as React from 'react';
import { styled, Theme } from '@mui/material/styles';
import MuiTypography, { TypographyProps } from '@mui/material/Typography';
const markClassesMapping: {
[index: string]: { [subindex: string]: string };
} = {
center: {
h1: '',
h2: 'OnePirateTypography-markedH2Center',
h3: 'OnePirateTypography-markedH3Center',
h4: 'OnePirateTypography-markedH4Center',
h5: '',
h6: '',
},
left: {
h1: '',
h2: '',
h3: '',
h4: '',
h5: '',
h6: 'OnePirateTypography-markedH6Left',
},
none: {
h1: '',
h2: '',
h3: '',
h4: '',
h5: '',
h6: '',
},
};
const styles = ({ theme }: { theme: Theme }) => ({
[`& .${markClassesMapping.center.h2}`]: {
height: 4,
width: 73,
display: 'block',
margin: `${theme.spacing(1)} auto 0`,
backgroundColor: theme.palette.secondary.main,
},
[`& .${markClassesMapping.center.h3}`]: {
height: 4,
width: 55,
display: 'block',
margin: `${theme.spacing(1)} auto 0`,
backgroundColor: theme.palette.secondary.main,
},
[`& .${markClassesMapping.center.h4}`]: {
height: 4,
width: 55,
display: 'block',
margin: `${theme.spacing(1)} auto 0`,
backgroundColor: theme.palette.secondary.main,
},
[`& .${markClassesMapping.left.h6}`]: {
height: 2,
width: 28,
display: 'block',
marginTop: theme.spacing(0.5),
background: 'currentColor',
},
});
interface ExtraTypographyProps {
marked?: 'center' | 'left' | 'none';
}
const variantMapping = {
h1: 'h1',
h2: 'h1',
h3: 'h1',
h4: 'h1',
h5: 'h3',
h6: 'h2',
subtitle1: 'h3',
};
function Typography<C extends React.ElementType>(
props: TypographyProps<C, { component?: C }> & ExtraTypographyProps,
) {
const { children, variant, marked = 'none', ...other } = props;
let markedClassName = '';
if (variant && variant in markClassesMapping[marked]) {
markedClassName = markClassesMapping[marked][variant];
}
return (
<MuiTypography variantMapping={variantMapping} variant={variant} {...other}>
{children}
{markedClassName ? <span className={markedClassName} /> : null}
</MuiTypography>
);
}
export default styled(Typography)(styles);

View File

@ -0,0 +1,27 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import Button from '../components/Button';
import defer from './defer';
function FormButton(props) {
const { disabled, mounted, ...others } = props;
return (
<Button
disabled={!mounted || !!disabled}
type="submit"
variant="contained"
{...others}
/>
);
}
FormButton.propTypes = {
/**
* If `true`, the component is disabled.
*/
disabled: PropTypes.bool,
mounted: PropTypes.bool,
};
export default defer(FormButton);

View File

@ -0,0 +1,24 @@
import * as React from 'react';
import { ButtonProps } from '@mui/material';
import Button from '../components/Button';
import defer from './defer';
interface FormButtonProps {
disabled?: boolean;
mounted?: boolean;
}
function FormButton<C extends React.ElementType>(
props: FormButtonProps & ButtonProps<C, { component?: C }>,
) {
const { disabled, mounted, ...others } = props;
return (
<Button
disabled={!mounted || !!disabled}
type="submit"
variant="contained"
{...others}
/>
);
}
export default defer(FormButton);

View File

@ -0,0 +1,38 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { experimentalStyled as styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
import Typography from '../components/Typography';
const BoxStyled = styled(Box, {
shouldForwardProp: (prop) => prop !== 'error' && prop !== 'success',
})(({ theme, error, success }) => ({
padding: theme.spacing(2),
...(error && {
backgroundColor: theme.palette.error.light,
color: theme.palette.error.dark,
}),
...(success && {
backgroundColor: theme.palette.success.light,
color: theme.palette.success.dark,
}),
}));
function FormFeedback(props) {
const { className, children, error, success, ...others } = props;
return (
<BoxStyled error={error} success={success} className={className} {...others}>
<Typography color="inherit">{children}</Typography>
</BoxStyled>
);
}
FormFeedback.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
error: PropTypes.bool,
success: PropTypes.bool,
};
export default FormFeedback;

View File

@ -0,0 +1,37 @@
import * as React from 'react';
import { experimentalStyled as styled } from '@mui/material/styles';
import Box, { BoxProps as MuiBoxProps } from '@mui/material/Box';
import Typography from '../components/Typography';
interface FormFeedbackProps extends MuiBoxProps {
error?: boolean;
success?: boolean;
}
const BoxStyled = styled(Box, {
shouldForwardProp: (prop) => prop !== 'error' && prop !== 'success',
})<FormFeedbackProps>(({ theme, error, success }) => ({
padding: theme.spacing(2),
...(error && {
backgroundColor: theme.palette.error.light,
color: theme.palette.error.dark,
}),
...(success && {
backgroundColor: theme.palette.success.light,
color: theme.palette.success.dark,
}),
}));
function FormFeedback(
props: React.HTMLAttributes<HTMLDivElement> & FormFeedbackProps,
) {
const { className, children, error, success, ...others } = props;
return (
<BoxStyled error={error} success={success} className={className} {...others}>
<Typography color="inherit">{children}</Typography>
</BoxStyled>
);
}
export default FormFeedback;

View File

@ -0,0 +1,79 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import TextField from '../components/TextField';
function RFTextField(props) {
const {
autoComplete,
input,
InputProps,
meta: { touched, error, submitError },
...other
} = props;
return (
<TextField
error={Boolean(!!touched && (error || submitError))}
{...input}
{...other}
InputProps={{
inputProps: {
autoComplete,
},
...InputProps,
}}
helperText={touched ? error || submitError : ''}
variant="standard"
/>
);
}
RFTextField.propTypes = {
/**
* This prop helps users to fill forms faster, especially on mobile devices.
* The name can be confusing, as it's more like an autofill.
* You can learn more about it [following the specification](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill).
*/
autoComplete: PropTypes.string,
input: PropTypes.shape({
checked: PropTypes.bool,
multiple: PropTypes.bool,
name: PropTypes.string.isRequired,
onBlur: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onFocus: PropTypes.func.isRequired,
type: PropTypes.string,
value: PropTypes.string.isRequired,
}).isRequired,
/**
* Props applied to the Input element.
* It will be a [`FilledInput`](/material-ui/api/filled-input/),
* [`OutlinedInput`](/material-ui/api/outlined-input/) or [`Input`](/material-ui/api/input/)
* component depending on the `variant` prop value.
*/
InputProps: PropTypes.object,
meta: PropTypes.shape({
active: PropTypes.bool,
data: PropTypes.object,
dirty: PropTypes.bool,
dirtySinceLastSubmit: PropTypes.bool,
error: PropTypes.any,
initial: PropTypes.string,
invalid: PropTypes.bool,
length: PropTypes.number,
modified: PropTypes.bool,
modifiedSinceLastSubmit: PropTypes.bool,
pristine: PropTypes.bool,
submitError: PropTypes.any,
submitFailed: PropTypes.bool,
submitSucceeded: PropTypes.bool,
submitting: PropTypes.bool,
touched: PropTypes.bool,
valid: PropTypes.bool,
validating: PropTypes.bool,
visited: PropTypes.bool,
}).isRequired,
};
export default RFTextField;

View File

@ -0,0 +1,33 @@
import * as React from 'react';
import { FieldRenderProps } from 'react-final-form';
import TextField, { OnePirateTextFieldProps } from '../components/TextField';
function RFTextField(
props: OnePirateTextFieldProps & FieldRenderProps<string, HTMLElement>,
) {
const {
autoComplete,
input,
InputProps,
meta: { touched, error, submitError },
...other
} = props;
return (
<TextField
error={Boolean(!!touched && (error || submitError))}
{...input}
{...other}
InputProps={{
inputProps: {
autoComplete,
},
...InputProps,
}}
helperText={touched ? error || submitError : ''}
variant="standard"
/>
);
}
export default RFTextField;

View File

@ -0,0 +1,15 @@
import * as React from 'react';
export default function defer(Component) {
function Defer(props) {
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
return <Component mounted={mounted} {...props} />;
}
return Defer;
}

View File

@ -0,0 +1,15 @@
import * as React from 'react';
export default function defer<P>(Component: React.ComponentType<P>) {
function Defer(props: P) {
const [mounted, setMounted] = React.useState(false);
React.useEffect(() => {
setMounted(true);
}, []);
return <Component mounted={mounted} {...props} />;
}
return Defer;
}

View File

@ -0,0 +1,29 @@
/**
* This is a simplified logic.
* Consider using `import isEmail from 'validator/lib/isEmail'` from
* https://github.com/validatorjs/validator.js/blob/7376945b4ce028b65955ae57b8fccbbf3fe58467/src/lib/isEmail.js
* for a more robust version.
*/
function isEmail(string) {
const re =
/^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i;
return re.test(string);
}
export function email(value) {
return value && !isEmail(value.trim()) ? 'Invalid email' : null;
}
function isDirty(value) {
return value || value === 0;
}
export function required(requiredFields, values) {
return requiredFields.reduce(
(fields, field) => ({
...fields,
...(isDirty(values[field]) ? undefined : { [field]: 'Required' }),
}),
{},
);
}

View File

@ -0,0 +1,32 @@
/**
* This is a simplified logic.
* Consider using `import isEmail from 'validator/lib/isEmail'` from
* https://github.com/validatorjs/validator.js/blob/7376945b4ce028b65955ae57b8fccbbf3fe58467/src/lib/isEmail.js
* for a more robust version.
*/
function isEmail(string: string) {
const re =
/^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i;
return re.test(string);
}
export function email(value: string) {
return value && !isEmail(value.trim()) ? 'Invalid email' : null;
}
function isDirty(value: string | number) {
return value || value === 0;
}
export function required(
requiredFields: readonly string[],
values: Record<string, string>,
): Record<string, string> {
return requiredFields.reduce(
(fields, field) => ({
...fields,
...(isDirty(values[field]) ? undefined : { [field]: 'Required' }),
}),
{},
);
}

View File

@ -0,0 +1,107 @@
import { createTheme } from '@mui/material/styles';
import { green, grey, red } from '@mui/material/colors';
const rawTheme = createTheme({
palette: {
primary: {
light: '#69696a',
main: '#28282a',
dark: '#1e1e1f',
},
secondary: {
light: '#fff5f8',
main: '#ff3366',
dark: '#e62958',
},
warning: {
main: '#ffc071',
dark: '#ffb25e',
},
error: {
light: red[50],
main: red[500],
dark: red[700],
},
success: {
light: green[50],
main: green[500],
dark: green[700],
},
},
typography: {
fontFamily: "'Work Sans', sans-serif",
fontSize: 14,
fontWeightLight: 300, // Work Sans
fontWeightRegular: 400, // Work Sans
fontWeightMedium: 700, // Roboto Condensed
},
});
const fontHeader = {
color: rawTheme.palette.text.primary,
fontWeight: rawTheme.typography.fontWeightMedium,
fontFamily: "'Roboto Condensed', sans-serif",
textTransform: 'uppercase',
};
const theme = {
...rawTheme,
palette: {
...rawTheme.palette,
background: {
...rawTheme.palette.background,
default: rawTheme.palette.common.white,
placeholder: grey[200],
},
},
typography: {
...rawTheme.typography,
fontHeader,
h1: {
...rawTheme.typography.h1,
...fontHeader,
letterSpacing: 0,
fontSize: 60,
},
h2: {
...rawTheme.typography.h2,
...fontHeader,
fontSize: 48,
},
h3: {
...rawTheme.typography.h3,
...fontHeader,
fontSize: 42,
},
h4: {
...rawTheme.typography.h4,
...fontHeader,
fontSize: 36,
},
h5: {
...rawTheme.typography.h5,
fontSize: 20,
fontWeight: rawTheme.typography.fontWeightLight,
},
h6: {
...rawTheme.typography.h6,
...fontHeader,
fontSize: 18,
},
subtitle1: {
...rawTheme.typography.subtitle1,
fontSize: 18,
},
body1: {
...rawTheme.typography.body2,
fontWeight: rawTheme.typography.fontWeightRegular,
fontSize: 16,
},
body2: {
...rawTheme.typography.body1,
fontSize: 14,
},
},
};
export default theme;

View File

@ -0,0 +1,107 @@
import { createTheme } from '@mui/material/styles';
import { green, grey, red } from '@mui/material/colors';
const rawTheme = createTheme({
palette: {
primary: {
light: '#69696a',
main: '#28282a',
dark: '#1e1e1f',
},
secondary: {
light: '#fff5f8',
main: '#ff3366',
dark: '#e62958',
},
warning: {
main: '#ffc071',
dark: '#ffb25e',
},
error: {
light: red[50],
main: red[500],
dark: red[700],
},
success: {
light: green[50],
main: green[500],
dark: green[700],
},
},
typography: {
fontFamily: "'Work Sans', sans-serif",
fontSize: 14,
fontWeightLight: 300, // Work Sans
fontWeightRegular: 400, // Work Sans
fontWeightMedium: 700, // Roboto Condensed
},
});
const fontHeader = {
color: rawTheme.palette.text.primary,
fontWeight: rawTheme.typography.fontWeightMedium,
fontFamily: "'Roboto Condensed', sans-serif",
textTransform: 'uppercase',
};
const theme = {
...rawTheme,
palette: {
...rawTheme.palette,
background: {
...rawTheme.palette.background,
default: rawTheme.palette.common.white,
placeholder: grey[200],
},
},
typography: {
...rawTheme.typography,
fontHeader,
h1: {
...rawTheme.typography.h1,
...fontHeader,
letterSpacing: 0,
fontSize: 60,
},
h2: {
...rawTheme.typography.h2,
...fontHeader,
fontSize: 48,
},
h3: {
...rawTheme.typography.h3,
...fontHeader,
fontSize: 42,
},
h4: {
...rawTheme.typography.h4,
...fontHeader,
fontSize: 36,
},
h5: {
...rawTheme.typography.h5,
fontSize: 20,
fontWeight: rawTheme.typography.fontWeightLight,
},
h6: {
...rawTheme.typography.h6,
...fontHeader,
fontSize: 18,
},
subtitle1: {
...rawTheme.typography.subtitle1,
fontSize: 18,
},
body1: {
...rawTheme.typography.body2,
fontWeight: rawTheme.typography.fontWeightRegular,
fontSize: 16,
},
body2: {
...rawTheme.typography.body1,
fontSize: 14,
},
},
};
export default theme;

View File

@ -0,0 +1,54 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import AppBar from '../components/AppBar';
import Toolbar from '../components/Toolbar';
const rightLink = {
fontSize: 16,
color: 'common.white',
ml: 3,
};
function AppAppBar() {
return (
<div>
<AppBar position="fixed">
<Toolbar sx={{ justifyContent: 'space-between' }}>
<Box sx={{ flex: 1 }} />
<Link
variant="h6"
underline="none"
color="inherit"
href="/premium-themes/onepirate/"
sx={{ fontSize: 24 }}
>
{'onepirate'}
</Link>
<Box sx={{ flex: 1, display: 'flex', justifyContent: 'flex-end' }}>
<Link
color="inherit"
variant="h6"
underline="none"
href="/premium-themes/onepirate/sign-in/"
sx={rightLink}
>
{'Sign In'}
</Link>
<Link
variant="h6"
underline="none"
href="/premium-themes/onepirate/sign-up/"
sx={{ ...rightLink, color: 'secondary.main' }}
>
{'Sign Up'}
</Link>
</Box>
</Toolbar>
</AppBar>
<Toolbar />
</div>
);
}
export default AppAppBar;

View File

@ -0,0 +1,54 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Link from '@mui/material/Link';
import AppBar from '../components/AppBar';
import Toolbar from '../components/Toolbar';
const rightLink = {
fontSize: 16,
color: 'common.white',
ml: 3,
};
function AppAppBar() {
return (
<div>
<AppBar position="fixed">
<Toolbar sx={{ justifyContent: 'space-between' }}>
<Box sx={{ flex: 1 }} />
<Link
variant="h6"
underline="none"
color="inherit"
href="/premium-themes/onepirate/"
sx={{ fontSize: 24 }}
>
{'onepirate'}
</Link>
<Box sx={{ flex: 1, display: 'flex', justifyContent: 'flex-end' }}>
<Link
color="inherit"
variant="h6"
underline="none"
href="/premium-themes/onepirate/sign-in/"
sx={rightLink}
>
{'Sign In'}
</Link>
<Link
variant="h6"
underline="none"
href="/premium-themes/onepirate/sign-up/"
sx={{ ...rightLink, color: 'secondary.main' }}
>
{'Sign Up'}
</Link>
</Box>
</Toolbar>
</AppBar>
<Toolbar />
</div>
);
}
export default AppAppBar;

View File

@ -0,0 +1,138 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Link from '@mui/material/Link';
import Container from '@mui/material/Container';
import Typography from '../components/Typography';
import TextField from '../components/TextField';
function Copyright() {
return (
<React.Fragment>
{'© '}
<Link color="inherit" href="https://mui.com/">
Your Website
</Link>{' '}
{new Date().getFullYear()}
</React.Fragment>
);
}
const iconStyle = {
width: 48,
height: 48,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'warning.main',
mr: 1,
'&:hover': {
bgcolor: 'warning.dark',
},
};
const LANGUAGES = [
{
code: 'en-US',
name: 'English',
},
{
code: 'fr-FR',
name: 'Français',
},
];
export default function AppFooter() {
return (
<Typography
component="footer"
sx={{ display: 'flex', bgcolor: 'secondary.light' }}
>
<Container sx={{ my: 8, display: 'flex' }}>
<Grid container spacing={5}>
<Grid item xs={6} sm={4} md={3}>
<Grid
container
direction="column"
justifyContent="flex-end"
spacing={2}
sx={{ height: 120 }}
>
<Grid item sx={{ display: 'flex' }}>
<Box component="a" href="https://mui.com/" sx={iconStyle}>
<img
src="/static/themes/onepirate/appFooterFacebook.png"
alt="Facebook"
/>
</Box>
<Box component="a" href="https://twitter.com/MUI_hq" sx={iconStyle}>
<img
src="/static/themes/onepirate/appFooterTwitter.png"
alt="Twitter"
/>
</Box>
</Grid>
<Grid item>
<Copyright />
</Grid>
</Grid>
</Grid>
<Grid item xs={6} sm={4} md={2}>
<Typography variant="h6" marked="left" gutterBottom>
Legal
</Typography>
<Box component="ul" sx={{ m: 0, listStyle: 'none', p: 0 }}>
<Box component="li" sx={{ py: 0.5 }}>
<Link href="/premium-themes/onepirate/terms/">Terms</Link>
</Box>
<Box component="li" sx={{ py: 0.5 }}>
<Link href="/premium-themes/onepirate/privacy/">Privacy</Link>
</Box>
</Box>
</Grid>
<Grid item xs={6} sm={8} md={4}>
<Typography variant="h6" marked="left" gutterBottom>
Language
</Typography>
<TextField
select
size="medium"
variant="standard"
SelectProps={{
native: true,
}}
sx={{ mt: 1, width: 150 }}
>
{LANGUAGES.map((language) => (
<option value={language.code} key={language.code}>
{language.name}
</option>
))}
</TextField>
</Grid>
<Grid item>
<Typography variant="caption">
{'Icons made by '}
<Link href="https://www.freepik.com" rel="sponsored" title="Freepik">
Freepik
</Link>
{' from '}
<Link href="https://www.flaticon.com" rel="sponsored" title="Flaticon">
www.flaticon.com
</Link>
{' is licensed by '}
<Link
href="https://creativecommons.org/licenses/by/3.0/"
title="Creative Commons BY 3.0"
target="_blank"
rel="noopener noreferrer"
>
CC 3.0 BY
</Link>
</Typography>
</Grid>
</Grid>
</Container>
</Typography>
);
}

View File

@ -0,0 +1,138 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Link from '@mui/material/Link';
import Container from '@mui/material/Container';
import Typography from '../components/Typography';
import TextField from '../components/TextField';
function Copyright() {
return (
<React.Fragment>
{'© '}
<Link color="inherit" href="https://mui.com/">
Your Website
</Link>{' '}
{new Date().getFullYear()}
</React.Fragment>
);
}
const iconStyle = {
width: 48,
height: 48,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'warning.main',
mr: 1,
'&:hover': {
bgcolor: 'warning.dark',
},
};
const LANGUAGES = [
{
code: 'en-US',
name: 'English',
},
{
code: 'fr-FR',
name: 'Français',
},
];
export default function AppFooter() {
return (
<Typography
component="footer"
sx={{ display: 'flex', bgcolor: 'secondary.light' }}
>
<Container sx={{ my: 8, display: 'flex' }}>
<Grid container spacing={5}>
<Grid item xs={6} sm={4} md={3}>
<Grid
container
direction="column"
justifyContent="flex-end"
spacing={2}
sx={{ height: 120 }}
>
<Grid item sx={{ display: 'flex' }}>
<Box component="a" href="https://mui.com/" sx={iconStyle}>
<img
src="/static/themes/onepirate/appFooterFacebook.png"
alt="Facebook"
/>
</Box>
<Box component="a" href="https://twitter.com/MUI_hq" sx={iconStyle}>
<img
src="/static/themes/onepirate/appFooterTwitter.png"
alt="Twitter"
/>
</Box>
</Grid>
<Grid item>
<Copyright />
</Grid>
</Grid>
</Grid>
<Grid item xs={6} sm={4} md={2}>
<Typography variant="h6" marked="left" gutterBottom>
Legal
</Typography>
<Box component="ul" sx={{ m: 0, listStyle: 'none', p: 0 }}>
<Box component="li" sx={{ py: 0.5 }}>
<Link href="/premium-themes/onepirate/terms/">Terms</Link>
</Box>
<Box component="li" sx={{ py: 0.5 }}>
<Link href="/premium-themes/onepirate/privacy/">Privacy</Link>
</Box>
</Box>
</Grid>
<Grid item xs={6} sm={8} md={4}>
<Typography variant="h6" marked="left" gutterBottom>
Language
</Typography>
<TextField
select
size="medium"
variant="standard"
SelectProps={{
native: true,
}}
sx={{ mt: 1, width: 150 }}
>
{LANGUAGES.map((language) => (
<option value={language.code} key={language.code}>
{language.name}
</option>
))}
</TextField>
</Grid>
<Grid item>
<Typography variant="caption">
{'Icons made by '}
<Link href="https://www.freepik.com" rel="sponsored" title="Freepik">
Freepik
</Link>
{' from '}
<Link href="https://www.flaticon.com" rel="sponsored" title="Flaticon">
www.flaticon.com
</Link>
{' is licensed by '}
<Link
href="https://creativecommons.org/licenses/by/3.0/"
title="Creative Commons BY 3.0"
target="_blank"
rel="noopener noreferrer"
>
CC 3.0 BY
</Link>
</Typography>
</Grid>
</Grid>
</Container>
</Typography>
);
}

View File

@ -0,0 +1,36 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Paper from '../components/Paper';
function AppForm(props) {
const { children } = props;
return (
<Box
sx={{
display: 'flex',
backgroundImage: 'url(/static/onepirate/appCurvyLines.png)',
backgroundRepeat: 'no-repeat',
}}
>
<Container maxWidth="sm">
<Box sx={{ mt: 7, mb: 12 }}>
<Paper
background="light"
sx={{ py: { xs: 4, md: 8 }, px: { xs: 3, md: 6 } }}
>
{children}
</Paper>
</Box>
</Container>
</Box>
);
}
AppForm.propTypes = {
children: PropTypes.node,
};
export default AppForm;

View File

@ -0,0 +1,29 @@
import * as React from 'react';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Paper from '../components/Paper';
export default function AppForm(props: React.HTMLAttributes<HTMLDivElement>) {
const { children } = props;
return (
<Box
sx={{
display: 'flex',
backgroundImage: 'url(/static/onepirate/appCurvyLines.png)',
backgroundRepeat: 'no-repeat',
}}
>
<Container maxWidth="sm">
<Box sx={{ mt: 7, mb: 12 }}>
<Paper
background="light"
sx={{ py: { xs: 4, md: 8 }, px: { xs: 3, md: 6 } }}
>
{children}
</Paper>
</Box>
</Container>
</Box>
);
}

View File

@ -0,0 +1,101 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Container from '@mui/material/Container';
import Typography from '../components/Typography';
import TextField from '../components/TextField';
import Snackbar from '../components/Snackbar';
import Button from '../components/Button';
function ProductCTA() {
const [open, setOpen] = React.useState(false);
const handleSubmit = (event) => {
event.preventDefault();
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<Container component="section" sx={{ mt: 10, display: 'flex' }}>
<Grid container>
<Grid item xs={12} md={6} sx={{ zIndex: 1 }}>
<Box
sx={{
display: 'flex',
justifyContent: 'center',
bgcolor: 'warning.main',
py: 8,
px: 3,
}}
>
<Box component="form" onSubmit={handleSubmit} sx={{ maxWidth: 400 }}>
<Typography variant="h2" component="h2" gutterBottom>
Receive offers
</Typography>
<Typography variant="h5">
Taste the holidays of the everyday close to home.
</Typography>
<TextField
noBorder
placeholder="Your email"
variant="standard"
sx={{ width: '100%', mt: 3, mb: 2 }}
/>
<Button
type="submit"
color="primary"
variant="contained"
sx={{ width: '100%' }}
>
Keep me updated
</Button>
</Box>
</Box>
</Grid>
<Grid
item
xs={12}
md={6}
sx={{ display: { md: 'block', xs: 'none' }, position: 'relative' }}
>
<Box
sx={{
position: 'absolute',
top: -67,
left: -67,
right: 0,
bottom: 0,
width: '100%',
background: 'url(/static/themes/onepirate/productCTAImageDots.png)',
}}
/>
<Box
component="img"
src="https://images.unsplash.com/photo-1527853787696-f7be74f2e39a?auto=format&fit=crop&w=750"
alt="call to action"
sx={{
position: 'absolute',
top: -28,
left: -28,
right: 0,
bottom: 0,
width: '100%',
maxWidth: 600,
}}
/>
</Grid>
</Grid>
<Snackbar
open={open}
closeFunc={handleClose}
message="We will send you our best offers, once a week."
/>
</Container>
);
}
export default ProductCTA;

View File

@ -0,0 +1,101 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Container from '@mui/material/Container';
import Typography from '../components/Typography';
import TextField from '../components/TextField';
import Snackbar from '../components/Snackbar';
import Button from '../components/Button';
function ProductCTA() {
const [open, setOpen] = React.useState(false);
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
return (
<Container component="section" sx={{ mt: 10, display: 'flex' }}>
<Grid container>
<Grid item xs={12} md={6} sx={{ zIndex: 1 }}>
<Box
sx={{
display: 'flex',
justifyContent: 'center',
bgcolor: 'warning.main',
py: 8,
px: 3,
}}
>
<Box component="form" onSubmit={handleSubmit} sx={{ maxWidth: 400 }}>
<Typography variant="h2" component="h2" gutterBottom>
Receive offers
</Typography>
<Typography variant="h5">
Taste the holidays of the everyday close to home.
</Typography>
<TextField
noBorder
placeholder="Your email"
variant="standard"
sx={{ width: '100%', mt: 3, mb: 2 }}
/>
<Button
type="submit"
color="primary"
variant="contained"
sx={{ width: '100%' }}
>
Keep me updated
</Button>
</Box>
</Box>
</Grid>
<Grid
item
xs={12}
md={6}
sx={{ display: { md: 'block', xs: 'none' }, position: 'relative' }}
>
<Box
sx={{
position: 'absolute',
top: -67,
left: -67,
right: 0,
bottom: 0,
width: '100%',
background: 'url(/static/themes/onepirate/productCTAImageDots.png)',
}}
/>
<Box
component="img"
src="https://images.unsplash.com/photo-1527853787696-f7be74f2e39a?auto=format&fit=crop&w=750"
alt="call to action"
sx={{
position: 'absolute',
top: -28,
left: -28,
right: 0,
bottom: 0,
width: '100%',
maxWidth: 600,
}}
/>
</Grid>
</Grid>
<Snackbar
open={open}
closeFunc={handleClose}
message="We will send you our best offers, once a week."
/>
</Container>
);
}
export default ProductCTA;

View File

@ -0,0 +1,159 @@
import * as React from 'react';
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
import ButtonBase from '@mui/material/ButtonBase';
import Container from '@mui/material/Container';
import Typography from '../components/Typography';
const ImageBackdrop = styled('div')(({ theme }) => ({
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
background: '#000',
opacity: 0.5,
transition: theme.transitions.create('opacity'),
}));
const ImageIconButton = styled(ButtonBase)(({ theme }) => ({
position: 'relative',
display: 'block',
padding: 0,
borderRadius: 0,
height: '40vh',
[theme.breakpoints.down('md')]: {
width: '100% !important',
height: 100,
},
'&:hover': {
zIndex: 1,
},
'&:hover .imageBackdrop': {
opacity: 0.15,
},
'&:hover .imageMarked': {
opacity: 0,
},
'&:hover .imageTitle': {
border: '4px solid currentColor',
},
'& .imageTitle': {
position: 'relative',
padding: `${theme.spacing(2)} ${theme.spacing(4)} 14px`,
},
'& .imageMarked': {
height: 3,
width: 18,
background: theme.palette.common.white,
position: 'absolute',
bottom: -2,
left: 'calc(50% - 9px)',
transition: theme.transitions.create('opacity'),
},
}));
const images = [
{
url: 'https://images.unsplash.com/photo-1534081333815-ae5019106622?auto=format&fit=crop&w=400',
title: 'Snorkeling',
width: '40%',
},
{
url: 'https://images.unsplash.com/photo-1531299204812-e6d44d9a185c?auto=format&fit=crop&w=400',
title: 'Massage',
width: '20%',
},
{
url: 'https://images.unsplash.com/photo-1476480862126-209bfaa8edc8?auto=format&fit=crop&w=400',
title: 'Hiking',
width: '40%',
},
{
url: 'https://images.unsplash.com/photo-1453747063559-36695c8771bd?auto=format&fit=crop&w=400',
title: 'Tour',
width: '38%',
},
{
url: 'https://images.unsplash.com/photo-1523309996740-d5315f9cc28b?auto=format&fit=crop&w=400',
title: 'Gastronomy',
width: '38%',
},
{
url: 'https://images.unsplash.com/photo-1534452203293-494d7ddbf7e0?auto=format&fit=crop&w=400',
title: 'Shopping',
width: '24%',
},
{
url: 'https://images.unsplash.com/photo-1506941433945-99a2aa4bd50a?auto=format&fit=crop&w=400',
title: 'Walking',
width: '40%',
},
{
url: 'https://images.unsplash.com/photo-1533727937480-da3a97967e95?auto=format&fit=crop&w=400',
title: 'Fitness',
width: '20%',
},
{
url: 'https://images.unsplash.com/photo-1518136247453-74e7b5265980?auto=format&fit=crop&w=400',
title: 'Reading',
width: '40%',
},
];
export default function ProductCategories() {
return (
<Container component="section" sx={{ mt: 8, mb: 4 }}>
<Typography variant="h4" marked="center" align="center" component="h2">
For all tastes and all desires
</Typography>
<Box sx={{ mt: 8, display: 'flex', flexWrap: 'wrap' }}>
{images.map((image) => (
<ImageIconButton
key={image.title}
style={{
width: image.width,
}}
>
<Box
sx={{
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundSize: 'cover',
backgroundPosition: 'center 40%',
backgroundImage: `url(${image.url})`,
}}
/>
<ImageBackdrop className="imageBackdrop" />
<Box
sx={{
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'common.white',
}}
>
<Typography
component="h3"
variant="h6"
color="inherit"
className="imageTitle"
>
{image.title}
<div className="imageMarked" />
</Typography>
</Box>
</ImageIconButton>
))}
</Box>
</Container>
);
}

View File

@ -0,0 +1,159 @@
import * as React from 'react';
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
import ButtonBase from '@mui/material/ButtonBase';
import Container from '@mui/material/Container';
import Typography from '../components/Typography';
const ImageBackdrop = styled('div')(({ theme }) => ({
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
background: '#000',
opacity: 0.5,
transition: theme.transitions.create('opacity'),
}));
const ImageIconButton = styled(ButtonBase)(({ theme }) => ({
position: 'relative',
display: 'block',
padding: 0,
borderRadius: 0,
height: '40vh',
[theme.breakpoints.down('md')]: {
width: '100% !important',
height: 100,
},
'&:hover': {
zIndex: 1,
},
'&:hover .imageBackdrop': {
opacity: 0.15,
},
'&:hover .imageMarked': {
opacity: 0,
},
'&:hover .imageTitle': {
border: '4px solid currentColor',
},
'& .imageTitle': {
position: 'relative',
padding: `${theme.spacing(2)} ${theme.spacing(4)} 14px`,
},
'& .imageMarked': {
height: 3,
width: 18,
background: theme.palette.common.white,
position: 'absolute',
bottom: -2,
left: 'calc(50% - 9px)',
transition: theme.transitions.create('opacity'),
},
}));
const images = [
{
url: 'https://images.unsplash.com/photo-1534081333815-ae5019106622?auto=format&fit=crop&w=400',
title: 'Snorkeling',
width: '40%',
},
{
url: 'https://images.unsplash.com/photo-1531299204812-e6d44d9a185c?auto=format&fit=crop&w=400',
title: 'Massage',
width: '20%',
},
{
url: 'https://images.unsplash.com/photo-1476480862126-209bfaa8edc8?auto=format&fit=crop&w=400',
title: 'Hiking',
width: '40%',
},
{
url: 'https://images.unsplash.com/photo-1453747063559-36695c8771bd?auto=format&fit=crop&w=400',
title: 'Tour',
width: '38%',
},
{
url: 'https://images.unsplash.com/photo-1523309996740-d5315f9cc28b?auto=format&fit=crop&w=400',
title: 'Gastronomy',
width: '38%',
},
{
url: 'https://images.unsplash.com/photo-1534452203293-494d7ddbf7e0?auto=format&fit=crop&w=400',
title: 'Shopping',
width: '24%',
},
{
url: 'https://images.unsplash.com/photo-1506941433945-99a2aa4bd50a?auto=format&fit=crop&w=400',
title: 'Walking',
width: '40%',
},
{
url: 'https://images.unsplash.com/photo-1533727937480-da3a97967e95?auto=format&fit=crop&w=400',
title: 'Fitness',
width: '20%',
},
{
url: 'https://images.unsplash.com/photo-1518136247453-74e7b5265980?auto=format&fit=crop&w=400',
title: 'Reading',
width: '40%',
},
];
export default function ProductCategories() {
return (
<Container component="section" sx={{ mt: 8, mb: 4 }}>
<Typography variant="h4" marked="center" align="center" component="h2">
For all tastes and all desires
</Typography>
<Box sx={{ mt: 8, display: 'flex', flexWrap: 'wrap' }}>
{images.map((image) => (
<ImageIconButton
key={image.title}
style={{
width: image.width,
}}
>
<Box
sx={{
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundSize: 'cover',
backgroundPosition: 'center 40%',
backgroundImage: `url(${image.url})`,
}}
/>
<ImageBackdrop className="imageBackdrop" />
<Box
sx={{
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
color: 'common.white',
}}
>
<Typography
component="h3"
variant="h6"
color="inherit"
className="imageTitle"
>
{image.title}
<div className="imageMarked" />
</Typography>
</Box>
</ImageIconButton>
))}
</Box>
</Container>
);
}

View File

@ -0,0 +1,50 @@
import * as React from 'react';
import Button from '../components/Button';
import Typography from '../components/Typography';
import ProductHeroLayout from './ProductHeroLayout';
const backgroundImage =
'https://images.unsplash.com/photo-1534854638093-bada1813ca19?auto=format&fit=crop&w=1400';
export default function ProductHero() {
return (
<ProductHeroLayout
sxBackground={{
backgroundImage: `url(${backgroundImage})`,
backgroundColor: '#7fc7d9', // Average color of the background image.
backgroundPosition: 'center',
}}
>
{/* Increase the network loading priority of the background image. */}
<img
style={{ display: 'none' }}
src={backgroundImage}
alt="increase priority"
/>
<Typography color="inherit" align="center" variant="h2" marked="center">
Upgrade your Sundays
</Typography>
<Typography
color="inherit"
align="center"
variant="h5"
sx={{ mb: 4, mt: { sx: 4, sm: 10 } }}
>
Enjoy secret offers up to -70% off the best luxury hotels every Sunday.
</Typography>
<Button
color="secondary"
variant="contained"
size="large"
component="a"
href="/premium-themes/onepirate/sign-up/"
sx={{ minWidth: 200 }}
>
Register
</Button>
<Typography variant="body2" color="inherit" sx={{ mt: 2 }}>
Discover the experience
</Typography>
</ProductHeroLayout>
);
}

View File

@ -0,0 +1,50 @@
import * as React from 'react';
import Button from '../components/Button';
import Typography from '../components/Typography';
import ProductHeroLayout from './ProductHeroLayout';
const backgroundImage =
'https://images.unsplash.com/photo-1534854638093-bada1813ca19?auto=format&fit=crop&w=1400';
export default function ProductHero() {
return (
<ProductHeroLayout
sxBackground={{
backgroundImage: `url(${backgroundImage})`,
backgroundColor: '#7fc7d9', // Average color of the background image.
backgroundPosition: 'center',
}}
>
{/* Increase the network loading priority of the background image. */}
<img
style={{ display: 'none' }}
src={backgroundImage}
alt="increase priority"
/>
<Typography color="inherit" align="center" variant="h2" marked="center">
Upgrade your Sundays
</Typography>
<Typography
color="inherit"
align="center"
variant="h5"
sx={{ mb: 4, mt: { sx: 4, sm: 10 } }}
>
Enjoy secret offers up to -70% off the best luxury hotels every Sunday.
</Typography>
<Button
color="secondary"
variant="contained"
size="large"
component="a"
href="/premium-themes/onepirate/sign-up/"
sx={{ minWidth: 200 }}
>
Register
</Button>
<Typography variant="body2" color="inherit" sx={{ mt: 2 }}>
Discover the experience
</Typography>
</ProductHeroLayout>
);
}

View File

@ -0,0 +1,89 @@
import * as React from 'react';
import PropTypes from 'prop-types';
import { styled } from '@mui/material/styles';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
const ProductHeroLayoutRoot = styled('section')(({ theme }) => ({
color: theme.palette.common.white,
position: 'relative',
display: 'flex',
alignItems: 'center',
[theme.breakpoints.up('sm')]: {
height: '80vh',
minHeight: 500,
maxHeight: 1300,
},
}));
const Background = styled(Box)({
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
zIndex: -2,
});
function ProductHeroLayout(props) {
const { sxBackground, children } = props;
return (
<ProductHeroLayoutRoot>
<Container
sx={{
mt: 3,
mb: 14,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<img
src="/static/themes/onepirate/productHeroWonder.png"
alt="wonder"
width="147"
height="80"
/>
{children}
<Box
sx={{
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundColor: 'common.black',
opacity: 0.5,
zIndex: -1,
}}
/>
<Background sx={sxBackground} />
<Box
component="img"
src="/static/themes/onepirate/productHeroArrowDown.png"
height="16"
width="12"
alt="arrow down"
sx={{ position: 'absolute', bottom: 32 }}
/>
</Container>
</ProductHeroLayoutRoot>
);
}
ProductHeroLayout.propTypes = {
children: PropTypes.node,
sxBackground: PropTypes.oneOfType([
PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool]),
),
PropTypes.func,
PropTypes.object,
]),
};
export default ProductHeroLayout;

View File

@ -0,0 +1,81 @@
import * as React from 'react';
import { Theme, styled } from '@mui/material/styles';
import { SxProps } from '@mui/system';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
const ProductHeroLayoutRoot = styled('section')(({ theme }) => ({
color: theme.palette.common.white,
position: 'relative',
display: 'flex',
alignItems: 'center',
[theme.breakpoints.up('sm')]: {
height: '80vh',
minHeight: 500,
maxHeight: 1300,
},
}));
const Background = styled(Box)({
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
zIndex: -2,
});
interface ProductHeroLayoutProps {
sxBackground: SxProps<Theme>;
}
export default function ProductHeroLayout(
props: React.HTMLAttributes<HTMLDivElement> & ProductHeroLayoutProps,
) {
const { sxBackground, children } = props;
return (
<ProductHeroLayoutRoot>
<Container
sx={{
mt: 3,
mb: 14,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<img
src="/static/themes/onepirate/productHeroWonder.png"
alt="wonder"
width="147"
height="80"
/>
{children}
<Box
sx={{
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
backgroundColor: 'common.black',
opacity: 0.5,
zIndex: -1,
}}
/>
<Background sx={sxBackground} />
<Box
component="img"
src="/static/themes/onepirate/productHeroArrowDown.png"
height="16"
width="12"
alt="arrow down"
sx={{ position: 'absolute', bottom: 32 }}
/>
</Container>
</ProductHeroLayoutRoot>
);
}

View File

@ -0,0 +1,121 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Container from '@mui/material/Container';
import Button from '../components/Button';
import Typography from '../components/Typography';
const item = {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
px: 5,
};
const number = {
fontSize: 24,
fontFamily: 'default',
color: 'secondary.main',
fontWeight: 'medium',
};
const image = {
height: 55,
my: 4,
};
function ProductHowItWorks() {
return (
<Box
component="section"
sx={{ display: 'flex', bgcolor: 'secondary.light', overflow: 'hidden' }}
>
<Container
sx={{
mt: 10,
mb: 15,
position: 'relative',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Box
component="img"
src="/static/themes/onepirate/productCurvyLines.png"
alt="curvy lines"
sx={{
pointerEvents: 'none',
position: 'absolute',
top: -180,
opacity: 0.7,
}}
/>
<Typography variant="h4" marked="center" component="h2" sx={{ mb: 14 }}>
How it works
</Typography>
<div>
<Grid container spacing={5}>
<Grid item xs={12} md={4}>
<Box sx={item}>
<Box sx={number}>1.</Box>
<Box
component="img"
src="/static/themes/onepirate/productHowItWorks1.svg"
alt="suitcase"
sx={image}
/>
<Typography variant="h5" align="center">
Appointment every Wednesday 9am.
</Typography>
</Box>
</Grid>
<Grid item xs={12} md={4}>
<Box sx={item}>
<Box sx={number}>2.</Box>
<Box
component="img"
src="/static/themes/onepirate/productHowItWorks2.svg"
alt="graph"
sx={image}
/>
<Typography variant="h5" align="center">
First come, first served. Our offers are in limited quantities, so
be quick.
</Typography>
</Box>
</Grid>
<Grid item xs={12} md={4}>
<Box sx={item}>
<Box sx={number}>3.</Box>
<Box
component="img"
src="/static/themes/onepirate/productHowItWorks3.svg"
alt="clock"
sx={image}
/>
<Typography variant="h5" align="center">
{'New offers every week. New experiences, new surprises. '}
{'Your Sundays will no longer be alike.'}
</Typography>
</Box>
</Grid>
</Grid>
</div>
<Button
color="secondary"
size="large"
variant="contained"
component="a"
href="/premium-themes/onepirate/sign-up/"
sx={{ mt: 8 }}
>
Get started
</Button>
</Container>
</Box>
);
}
export default ProductHowItWorks;

View File

@ -0,0 +1,122 @@
import * as React from 'react';
import { Theme } from '@mui/material/styles';
import { SxProps } from '@mui/system';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Container from '@mui/material/Container';
import Button from '../components/Button';
import Typography from '../components/Typography';
const item: SxProps<Theme> = {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
px: 5,
};
const number = {
fontSize: 24,
fontFamily: 'default',
color: 'secondary.main',
fontWeight: 'medium',
};
const image = {
height: 55,
my: 4,
};
function ProductHowItWorks() {
return (
<Box
component="section"
sx={{ display: 'flex', bgcolor: 'secondary.light', overflow: 'hidden' }}
>
<Container
sx={{
mt: 10,
mb: 15,
position: 'relative',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
}}
>
<Box
component="img"
src="/static/themes/onepirate/productCurvyLines.png"
alt="curvy lines"
sx={{
pointerEvents: 'none',
position: 'absolute',
top: -180,
opacity: 0.7,
}}
/>
<Typography variant="h4" marked="center" component="h2" sx={{ mb: 14 }}>
How it works
</Typography>
<div>
<Grid container spacing={5}>
<Grid item xs={12} md={4}>
<Box sx={item}>
<Box sx={number}>1.</Box>
<Box
component="img"
src="/static/themes/onepirate/productHowItWorks1.svg"
alt="suitcase"
sx={image}
/>
<Typography variant="h5" align="center">
Appointment every Wednesday 9am.
</Typography>
</Box>
</Grid>
<Grid item xs={12} md={4}>
<Box sx={item}>
<Box sx={number}>2.</Box>
<Box
component="img"
src="/static/themes/onepirate/productHowItWorks2.svg"
alt="graph"
sx={image}
/>
<Typography variant="h5" align="center">
First come, first served. Our offers are in limited quantities, so
be quick.
</Typography>
</Box>
</Grid>
<Grid item xs={12} md={4}>
<Box sx={item}>
<Box sx={number}>3.</Box>
<Box
component="img"
src="/static/themes/onepirate/productHowItWorks3.svg"
alt="clock"
sx={image}
/>
<Typography variant="h5" align="center">
{'New offers every week. New experiences, new surprises. '}
{'Your Sundays will no longer be alike.'}
</Typography>
</Box>
</Grid>
</Grid>
</div>
<Button
color="secondary"
size="large"
variant="contained"
component="a"
href="/premium-themes/onepirate/sign-up/"
sx={{ mt: 8 }}
>
Get started
</Button>
</Container>
</Box>
);
}
export default ProductHowItWorks;

View File

@ -0,0 +1,39 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Container from '@mui/material/Container';
import Typography from '../components/Typography';
function ProductSmokingHero() {
return (
<Container
component="section"
sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', my: 9 }}
>
<Button
sx={{
border: '4px solid currentColor',
borderRadius: 0,
height: 'auto',
py: 2,
px: 5,
}}
>
<Typography variant="h4" component="span">
Got any questions? Need help?
</Typography>
</Button>
<Typography variant="subtitle1" sx={{ my: 3 }}>
We are here to help. Get in touch!
</Typography>
<Box
component="img"
src="/static/themes/onepirate/producBuoy.svg"
alt="buoy"
sx={{ width: 60 }}
/>
</Container>
);
}
export default ProductSmokingHero;

View File

@ -0,0 +1,39 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Container from '@mui/material/Container';
import Typography from '../components/Typography';
function ProductSmokingHero() {
return (
<Container
component="section"
sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', my: 9 }}
>
<Button
sx={{
border: '4px solid currentColor',
borderRadius: 0,
height: 'auto',
py: 2,
px: 5,
}}
>
<Typography variant="h4" component="span">
Got any questions? Need help?
</Typography>
</Button>
<Typography variant="subtitle1" sx={{ my: 3 }}>
We are here to help. Get in touch!
</Typography>
<Box
component="img"
src="/static/themes/onepirate/producBuoy.svg"
alt="buoy"
sx={{ width: 60 }}
/>
</Container>
);
}
export default ProductSmokingHero;

View File

@ -0,0 +1,94 @@
import * as React from 'react';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Container from '@mui/material/Container';
import Typography from '../components/Typography';
const item = {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
px: 5,
};
function ProductValues() {
return (
<Box
component="section"
sx={{ display: 'flex', overflow: 'hidden', bgcolor: 'secondary.light' }}
>
<Container sx={{ mt: 15, mb: 30, display: 'flex', position: 'relative' }}>
<Box
component="img"
src="/static/themes/onepirate/productCurvyLines.png"
alt="curvy lines"
sx={{ pointerEvents: 'none', position: 'absolute', top: -180 }}
/>
<Grid container spacing={5}>
<Grid item xs={12} md={4}>
<Box sx={item}>
<Box
component="img"
src="/static/themes/onepirate/productValues1.svg"
alt="suitcase"
sx={{ height: 55 }}
/>
<Typography variant="h6" sx={{ my: 5 }}>
The best luxury hotels
</Typography>
<Typography variant="h5">
{
'From the latest trendy boutique hotel to the iconic palace with XXL pool'
}
{
', go for a mini-vacation just a few subway stops away from your home.'
}
</Typography>
</Box>
</Grid>
<Grid item xs={12} md={4}>
<Box sx={item}>
<Box
component="img"
src="/static/themes/onepirate/productValues2.svg"
alt="graph"
sx={{ height: 55 }}
/>
<Typography variant="h6" sx={{ my: 5 }}>
New experiences
</Typography>
<Typography variant="h5">
{
'Privatize a pool, take a Japanese bath or wake up in 900m2 of garden… '
}
{'your Sundays will not be alike.'}
</Typography>
</Box>
</Grid>
<Grid item xs={12} md={4}>
<Box sx={item}>
<Box
component="img"
src="/static/themes/onepirate/productValues3.svg"
alt="clock"
sx={{ height: 55 }}
/>
<Typography variant="h6" sx={{ my: 5 }}>
Exclusive rates
</Typography>
<Typography variant="h5">
{'By registering, you will access specially negotiated rates '}
{'that you will not find anywhere else.'}
</Typography>
</Box>
</Grid>
</Grid>
</Container>
</Box>
);
}
export default ProductValues;

View File

@ -0,0 +1,93 @@
import * as React from 'react';
import { Theme } from '@mui/material/styles';
import { SxProps } from '@mui/system';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import Container from '@mui/material/Container';
import Typography from '../components/Typography';
const item: SxProps<Theme> = {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
px: 5,
};
function ProductValues() {
return (
<Box
component="section"
sx={{ display: 'flex', overflow: 'hidden', bgcolor: 'secondary.light' }}
>
<Container sx={{ mt: 15, mb: 30, display: 'flex', position: 'relative' }}>
<Box
component="img"
src="/static/themes/onepirate/productCurvyLines.png"
alt="curvy lines"
sx={{ pointerEvents: 'none', position: 'absolute', top: -180 }}
/>
<Grid container spacing={5}>
<Grid item xs={12} md={4}>
<Box sx={item}>
<Box
component="img"
src="/static/themes/onepirate/productValues1.svg"
alt="suitcase"
sx={{ height: 55 }}
/>
<Typography variant="h6" sx={{ my: 5 }}>
The best luxury hotels
</Typography>
<Typography variant="h5">
{
'From the latest trendy boutique hotel to the iconic palace with XXL pool'
}
{
', go for a mini-vacation just a few subway stops away from your home.'
}
</Typography>
</Box>
</Grid>
<Grid item xs={12} md={4}>
<Box sx={item}>
<Box
component="img"
src="/static/themes/onepirate/productValues2.svg"
alt="graph"
sx={{ height: 55 }}
/>
<Typography variant="h6" sx={{ my: 5 }}>
New experiences
</Typography>
<Typography variant="h5">
{
'Privatize a pool, take a Japanese bath or wake up in 900m2 of garden… '
}
{'your Sundays will not be alike.'}
</Typography>
</Box>
</Grid>
<Grid item xs={12} md={4}>
<Box sx={item}>
<Box
component="img"
src="/static/themes/onepirate/productValues3.svg"
alt="clock"
sx={{ height: 55 }}
/>
<Typography variant="h6" sx={{ my: 5 }}>
Exclusive rates
</Typography>
<Typography variant="h5">
{'By registering, you will access specially negotiated rates '}
{'that you will not find anywhere else.'}
</Typography>
</Box>
</Grid>
</Grid>
</Container>
</Box>
);
}
export default ProductValues;

View File

@ -0,0 +1,93 @@
<!-- markdownlint-capture -->
<!-- markdownlint-disable -->
Last modified: October 7th, 2018.
<!-- markdownlint-restore -->
MUI is committed to protecting and respecting your privacy. This Privacy Policy sets out how we collect and process personal information about you when you visit the website mui.com, when you use our products and services (our "Services"), or when you otherwise do business or make contact with us.
Please read this policy carefully to understand how we handle and treat your personal information.
## What information do we collect?
We may collect and process the following personal information from you:
- **Information you provide to us:** We collect personal information when you voluntarily provide us with such information in the course of using our website or Services. For example, when you register to use our Services, we will collect your name, email address and organization information. We also collect personal information from you when you subscribe to our newsletter, or respond to a survey. If you make an enquiry through our website, or contact us in any other way, we will keep a copy of your communications with us.
- **Information we collect when you do business with us:** We may process your personal information when you do business with us for example, as a customer or prospective customer, or as a vendor, supplier, consultant or other third party. For example, we may hold your business contact information and financial account information (if any) and other communications you have with us for the purposes of maintaining our business relations with you.
- **Information we automatically collect:** We may also collect certain technical information by automatic means when you visit our website, such as IP address, browser type and operating system, referring URLs, your use of our website, and other clickstream data. We collect this information automatically through the use of various technologies, such as cookies.
- **Personal information where we act as a data processor:** We also process personal information on behalf of our customers in the context of supporting our products and services. Where a customer subscribes to our Services for their website, game or app, they will be the ones who control what event data is collected and stored on our systems. For example, they may ask us to log basic user data (e.g. email address or username), device identifiers, IP addresses, event type, and related source code. In such cases, we are data processors acting in accordance with the instructions of our customers. You will need to refer to the privacy policies of our customers to find out more about how such information is handled by them.
## What do we use your information for?
The personal information we collect from you may be used in one of the following ways:
- To deal with your inquiries and requests
- To create and administer records about any online account that you register with us
- To provide you with information and access to resources that you have requested from us
- To provide you with technical support (your information helps us to better respond to your individual needs)
- To improve our website (we continually strive to improve our website offerings based on the information and feedback we receive from you), including to improve the navigation and content of our sites
- For website and system administration and security
- For general business purposes, including to improve customer service (your information helps us to more effectively respond to your customer service requests and support needs), to help us improve the content and functionality of our Services, to better understand our users, to protect against wrongdoing, to enforce our Terms of Service, and to generally manage our business
- To process transactions and to provide Services to our customers and end-users
- For recruitment purposes, where you apply for a job with us
- To administer a contest, promotion, survey, or other site features
- To send periodic emails. The email address you provide for order processing, will only be used to send you information and updates pertaining to your order. Where it is in accordance with your marketing preferences, we will send occasional marketing emails about our products and services, which you can unsubscribe from at any time using the link provided in the message.
## How do we protect your information?
We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information. We offer the use of a secure server. All supplied sensitive/credit information is transmitted via Secure Socket Layer (SSL) technology and then encrypted into our Payment gateway providers database only to be accessible by those authorized with special access rights to such systems, and are required to keep the information confidential. After a transaction, your private information (credit cards, social security numbers, financials, etc.) will not be stored on our servers.
## Do we use cookies?
Yes. Cookies are small files that a site or its service provider transfers to your computers hard drive through your Web browser (if you allow) that enables the sites or service providers systems to recognize your browser and capture and remember certain information.
We use cookies to understand and save your preferences for future visits, to advertise to you on other sites and compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future.
You may refuse to accept cookies by activating the setting on your browser which allows you to refuse the setting of cookies. You can find information on popular browsers and how to adjust your cookie preferences at the following websites:
- Microsoft Internet Explorer
- Mozilla Firefox
- Google Chrome
- Apple Safari
However, if you choose to disable cookies, you may be unable to access certain parts of our site. Unless you have adjusted your browser setting so that it will refuse cookies, our system will issue cookies when you log on to our site.
## Do we disclose any information to outside parties?
We will only share your information with third parties in certain circumstances:
- We engage certain trusted third parties to perform functions and provide services to us, including cloud hosting services, off-site backups, email service providers, and customer support providers. We will only share your personal information with third parties to the extent necessary to perform these functions, in accordance with the purposes set out in this Privacy Policy and applicable laws.
- In the event of a corporate sale, merger, reorganization, dissolution or similar event, your personal information may be sold, disposed of, transferred or otherwise disclosed as part of that transaction.
- We may also disclose information about you to third parties where we believe it necessary or appropriate under law, for example: (1) to protect or defend our rights, interests or property or that of third parties; (2) to comply with legal process, judicial orders or subpoenas; (3) to respond to requests from public or government authorities, including for national security and law enforcement purposes; (4) to prevent or investigate possible wrongdoing in connection with the Services or to enforce our Terms of Service; (5) to protect the vital interests of our users, customers and other third parties.
- We may use and share aggregated non-personal information with third parties for marketing, advertising and analytics purposes.
We do not sell or trade your personal information to third parties.
## Third Party Links
Occasionally, at our discretion, we may include or offer third party products or services on our website. If you access other websites using the links provided, the operators of these websites may collect information from you that will be used by them in accordance with their privacy policies. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.
## International Transfers
If you are visiting our website or using our Services from outside the United States (US), please be aware that you are sending personal information to the US where our servers are located. The US may not have data protection laws that are as comprehensive or protective as those in your country of residence; however, our collection, storage and use of your personal information will at all times be in accordance with this Privacy Policy.
## Your Rights
If you are from the EU, you may have the right to access a copy of the personal information we hold about you, or to request the correction, amendment or deletion of such information where it is inaccurate or processed in violation of the Privacy Shield Principles. To make such a request, please contact us at the contact details at the left.
We will consider and respond to your request in accordance with the Privacy Shield Principles and applicable laws.
Furthermore, we commit to giving you an opportunity to opt-out if your personal information is to be disclosed to any other independent third parties, or to be used for a purpose materially different from those that are set out in this Privacy Policy. Where sensitive personal information is involved, we will always obtain your express opt-in consent to do such things. If you otherwise wish to limit the use or disclosure of your personal information, please write to us at the contact details further below.
You can also unsubscribe from our marketing communications at any time by following the instructions or unsubscribe mechanism in the email message itself.
## Data Retention
We may retain your personal information as long as you continue to use the Services, have an account with us or for as long as is necessary to fulfil the purposes outlined in the policy. You can ask to close your account by contacting us at the details below and we will delete your personal information on request.
We may however retain personal information for an additional period as is permitted or required under applicable laws, for legal, tax or regulatory reasons, or for legitimate and lawful business purposes.
## Changes to our Privacy Policy
If we decide to change our privacy policy, we will post those changes on this page, and/or update the Privacy Policy modification date below.

View File

@ -0,0 +1,154 @@
<!-- markdownlint-capture -->
<!-- markdownlint-disable -->
Last modified: October 7th, 2018.
<!-- markdownlint-restore -->
## 1. Services
- 1.1 These MUI Terms of Service (these "Terms") apply to the features and functions provided by Functional Software, Inc. ("MUI," "our," or "we") via mui.com (the "Site") (collectively, the "Services"). By accessing or using the Site or the Services, you agree to be bound by these Terms. If you do not agree to these Terms, you are not allowed to use the Site or the Services. The "Effective Date" of these Terms is the date you first use the Site, or access any of the Services.
- 1.2 If you are using the Site or accessing the Services in your capacity as an employee, consultant or agent of a company or other entity, you represent that you are an employee, consultant or agent of that company or entity, and that you have the authority to bind that company or entity to these Terms. For the purpose of these Terms, you (and, if applicable, the company or entity that you represent) will be referred to as "Customer" or "you".
- 1.3 MUI reserves the right to change or modify these Terms, or any of our other policies or guidelines, at any time upon notice to you. We may provide that notice in a variety of ways, including, without limitation, sending you an email, posting a notice on the Site, or posting the revised Terms on the Site and revising the date at the top of these Terms. Any changes or modifications will be effective after we provide notice that these Terms have been modified. You acknowledge that your continued use of the Site or any of the Services following such notice constitutes your acceptance of the modified Terms.
- 1.4 MUI reserves the right at any time, and without notice or liability to you to modify the Site or the Services, or any part of them, temporarily or permanently. We may modify the Services for a variety of reasons, including, without limitation, for the purpose of providing new features, implementing new protocols, maintaining compatibility with emerging standards, or complying with regulatory requirements.
- 1.5 These Terms form a binding agreement between you and MUI. Violation of any of the Terms below will result in the termination of your account(s).
## 2. Privacy
Please see MUI' privacy policy at www.mui.com/privacy for information about how we collect, use, and disclose information about users of the Site and the Services. By using the Site and the Services, you consent to our collection, use, and disclosure of information as set forth in our privacy policy, as we may update that policy from time to time.
## 3. Registration
- 3.1 In order to use many aspects of the Services, you must first complete the MUI registration process via the Site. During the registration process, you will be asked to select a package to access the Services (each, a "Plan"), which includes: (a) the period during which you can access the Services (the "Subscription Period"); and (b) the fee you must pay to MUI in exchange for your right to access the Services (the "Subscription Fees"). All such information is incorporated into these Terms by reference. We have several different types of paid Plans, as well as a free Plan, for which there are no Subscription Fees. One person or legal entity may not sign up for more than one free Plan.
- 3.2 You agree: (a) to provide accurate, current and complete information about you as part of the registration process ("Registration Data"); (b) to maintain the security of your password(s); (c) to maintain and promptly update your Registration Data, and any other information you provide to MUI, and to keep it accurate, current and complete; (d) to accept all risks of unauthorized access to your Registration Data, and any other information you provide to MUI, via your account(s) or password(s); (e) that you are responsible for maintaining the security of your account and safeguarding your password(s), and (f) that you will be fully responsible for any activities or transactions that take place using your account(s) or password(s), even if you were not aware of them.
## 4. Access to services
Subject to your continued compliance with these Terms, MUI grants you a limited, non-transferable, non-exclusive, revocable right and license to: (i) access and use the Services and its associated documentation, solely for your own internal business purposes, for the Subscription Period for which you have paid the applicable Subscription Fees; and (ii) access and use any data or reports that we provide or make available to you as part of your access and use of the Services (collectively, "Reports"), solely in conjunction with your use of the Services. Reports are considered part of the applicable Services, for the purpose of the license granted above. You understand that MUI uses third-party vendors and hosting partners to provide the necessary hardware, software, networking, storage, and related technology required to provide the Services, and you agree that MUI is not and will not be liable or responsible for the acts or omissions of such third-party vendors or hosting partners.
## 5. Restrictions
Except as expressly authorized by these Terms, you may not: (a) modify, disclose, alter, translate or create derivative works of the Site or the Services; (b) license, sublicense, resell, distribute, lease, rent, lend, transfer, assign or otherwise dispose of the Services or any Report (or any components thereof); (c) offer any part of the Services (including, without limitation, any Report) on a timeshare or service bureau basis; (c) allow or permit any third party to access or use the Services; (d) use the Site or the Services to store or transmit any viruses, software routines, or other code designed to permit anyone to access in an unauthorized manner, disable, erase or otherwise harm software, hardware, or data, or to perform any other harmful actions; (e) build a competitive product or service, or copy any features or functions of the Site or the Services (including, without limitation, the look-and-feel of the Site or the Services); (f) interfere with or disrupt the integrity or performance of the Site or the Services;&nbsp;(g) disclose to any third party any performance information or analysis relating to the Site or the Services; (h) remove, alter or obscure any proprietary notices in or on the Site or the Services, including copyright notices; (i) use the Site or the Services or any product thereof for any illegal or unauthorized purpose, or in a manner which violates any laws or regulations in your jurisdiction; (j) reverse engineer, decompile, disassemble, or otherwise attempt to discover the source code, object code, or underlying structure, ideas, or algorithms that make up the Services or any software, documentation, or data relating to the Services, except to the limited extent that applicable law prohibits such a restriction; or (k) cause or permit any third party to do any of the foregoing.
## 6. Content
- 6.1 If you publish or upload data, images, code or content, or otherwise make (or allow any third party to make) material available by means of the Site or the Services (collectively, "Content"), you agree that you are entirely responsible for such Content, and for any harm or liability resulting from or arising out of that Content. Your responsibility applies whether or not the Content in question constitutes text, graphics, audio files, video files, computer software, or any other type of content, and whether or not you were the original creator or owner of the Content. You agree that you will be responsible for all Content on your account(s), even if placed there by third parties. By publishing or uploading Content to the Site or the Services, you represent and warrant that:
- a. the Content does not and will not infringe, violate or misappropriate the Intellectual Property Rights of any third party (where "Intellectual Property Rights" are defined as any patents, copyrights, moral rights, trademarks, trade secrets, or any other form of intellectual property rights recognized in any jurisdiction in the world, including applications and registrations for any of the foregoing);
- b. you have obtained all rights and permissions necessary to publish and/or use the Content in the manner in which you have published and/or used it;
- c. MUI's use of the Content for the purpose of providing the Services (including, without limitation, downloading, copying, processing, or creating aggregations of the Content) does not and will not (i) violate any applicable laws or regulations, or (ii) infringe, violate, or misappropriate the Intellectual Property Rights of any third party;
- d. you have fully complied with any third-party licenses relating to the Content;
- e. the Content does not contain or install any viruses, worms, malware, Trojan horses or other harmful or destructive code;
- f. the Content does not and will not include any: (i) "personal health information," as defined under the Health Insurance Portability and Accountability Act, unless you have entered into a separate agreement with us relating to the processing of such data; (ii) government issued identification numbers, including Social Security numbers, drivers' license numbers or other state-issued identification numbers; (iii) financial account information, including bank account numbers; (iv) payment card data, including credit card or debit card numbers; or (iv) "sensitive" personal data, as defined under Directive 95/46/EC of the European Parliament ("EU Directive") and any national laws adopted pursuant to the EU Directive, about residents of Switzerland and any member country of the European Union, including racial or ethnic origin, political opinions, religious beliefs, trade union membership, physical or mental health or condition, sexual life, or the commission or alleged commission any crime or offense;
- g. the Content is not spam, is not randomly-generated, and does not contain unethical or unwanted commercial content designed to drive traffic to third party sites or boost the search engine rankings of third party sites, or for any other unlawful acts (such as phishing), or for misleading recipients as to the source of the material (such as spoofing);
- h. the Content does not contain threats or incitement to violence, and does not violate the privacy or publicity rights of any third party;
- i. the Content is not being advertised via unwanted electronic messages (such as, by way of example, spam links on newsgroups, email lists, other blogs and web sites, and similar unsolicited promotional methods);
- j. the Content is not named in a manner that misleads (or could mislead) third parties into thinking that you are another person or company (by way of example, your Content's URL or name should not be confusingly similar to the name of another person or entity); and
- k. you have, in the case of Content that includes computer code, accurately categorized and/or described the type, nature, uses and effects of the materials, whether requested to do so by the Services or otherwise.
- 6.2 By submitting or uploading Content to the Services, you grant MUI a worldwide, royalty-free, and non-exclusive license (i) to use, reproduce, modify, adapt and publish that Content solely for the purpose of providing the Services to you; and (ii) to create aggregations and summaries of the Content or portions thereof and to use, disclose, and distribute such aggregations publicly to any third party in support of our business (both during the period that these Terms are in effect, and thereafter), provided that such aggregations and summaries do not directly or indirectly identify you or your Content. If you delete Content, MUI will use reasonable efforts to remove it from the Services. You acknowledge, however, that cached copies or other references to the Content may still be available.
- 6.3 Without limiting any of your representations or warranties with respect to the Content, MUI has the right (but not the obligation) to reject or remove any Content, without liability or notice to you, that MUI believes, in MUI' sole discretion: (i) violates these Terms or any MUI policy, (ii) violates or misappropriates the Intellectual Property Rights of any third party, or (iii) is in any way harmful or objectionable.
## 7. Fee and payment terms; Plan upgrade/downgrade/cancellation; Pricing changes
- 7.1 In exchange for your rights to access the Site and use the Services during the Subscription Period, you agree to pay the applicable Subscription Fees to MUI. The Subscription Fees do not include taxes; you will be responsible for, and will promptly pay, all taxes associated with your use of the Site and the Services, other than taxes based on our net income. Subscription Fees are payable in full, in advance, in accordance with your Plan, and are non-refundable and non-creditable. You agree to make all payments in U.S. Dollars.
- 7.2 You can cancel your account(s)/subscription(s) via the process set forth in the "Cancel Subscription" section of your Account Settings on the Site. An email or phone request to cancel your account is not considered cancellation. No refunds will be issued, unless expressly stated otherwise. All of your Content will be deleted from the Services within a reasonable time period from when you cancel your account/subscription. Deleted Content cannot be recovered once your account/subscription is cancelled.
- 7.3 If you upgrade from the free Plan to any paid Plan, we will immediately bill you for the applicable Subscription Fees. There will be no refunds or credits for partial months of service, upgrade/downgrade refunds, or refunds for months unused with an open account.
- 7.4 Downgrading your account(s) may cause the loss of Content, features, or capacity of your account(s). We do not accept any liability for such loss.
- 7.5 Each Subscription Period will automatically renew (and we may automatically invoice you) for additional Subscription Periods of equivalent length, unless and until one party provides written notice to the other at least thirty (30) days prior to the expiration of the then-current Subscription Period that it wishes to terminate the subscription at the end of the then-current Subscription Period. We reserve the right to modify the fees for the Services at any time upon thirty (30) days' prior notice to you, provided that the modified fees will not apply until the next Subscription Period.
- 7.6 Interest on any late payments will accrue at the rate of 1.5% per month, or the highest rate permitted by law, whichever is lower, from the date the amount is due until the date the amount is paid in full. If you are late in paying us, you also agree that, in addition to our rights to suspend your access to the Services, terminate your account(s), downgrade you to a free Plan, and/or pursue any other rights or remedies available to us at law or in equity, you are responsible to reimburse us for any costs that we incur while attempting to collect such late payments.
## 8. DISCLAIMER
YOU ACKNOWLEDGE THAT THE SITE AND THE SERVICES ARE PROVIDED ON AN "AS IS", "AS AVAILABLE" BASIS, WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, AND THAT YOUR USE OF THE SITE AND THE SERVICES IS AT YOUR SOLE RISK. ARGOS DOES NOT WARRANT: (I) THAT THE SITE OR THE SERVICES WILL MEET YOUR SPECIFIC REQUIREMENTS, (II) THAT THE SITE OR THE SERVICES WILL BE UNINTERRUPTED, TIMELY, SECURE, OR ERROR-FREE, (III) THAT THE RESULTS THAT MAY BE OBTAINED FROM THE USE OF THE SERVICES WILL BE ACCURATE OR RELIABLE, (IV) THAT THE QUALITY OF ANY PRODUCTS, SERVICES, INFORMATION, OR OTHER MATERIAL THAT YOU PURCHASE OR OBTAIN THROUGH THE SITE OR THE SERVICES WILL MEET YOUR EXPECTATIONS, OR (V) THAT ANY ERRORS IN THE SITE OR THE SERVICES WILL BE CORRECTED. ARGOS SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT.
## 9. Indemnification obligations
You agree, at your sole expense, to defend, indemnify and hold MUI (and its directors, officers, employees, consultants and agents) harmless from and against any and all actual or threatened suits, actions, proceedings (whether at law or in equity), claims, damages, payments, deficiencies, fines, judgments, settlements, liabilities, losses, costs and expenses (including, without limitation, reasonable attorneys' fees, costs, penalties, interest and disbursements) arising out of or relating to (i) your Content; (ii) your use of the Site or the Services; (iii) your failure to pay any taxes that you owe under these Terms; and (iv) any other actual or alleged breach of any of your obligations under these Terms (including, among other things, any actual or alleged breach of any of your representations or warranties as set forth herein). You will not settle any such claim in any manner that would require MUI to pay money or admit wrongdoing of any kind without our prior written consent, which we may withhold in our sole discretion.
## 10. LIMITATION OF LIABILITY
- 10.1 IN NO EVENT WILL ARGOS'S TOTAL, AGGREGATE LIABILITY TO YOU OR TO ANY THIRD PARTY ARISING OUT OF OR RELATED TO THESE TERMS OR YOUR USE OF (OR INABILITY TO USE) ANY PART OF THE SITE OR THE SERVICES EXCEED THE TOTAL AMOUNT YOU ACTUALLY PAID TO ARGOS IN SUBSCRIPTION FEES FOR THE SERVICES DURING THE TWELVE (12) MONTHS IMMEDIATELY PRIOR TO THE ACCRUAL OF THE FIRST CLAIM. MULTIPLE CLAIMS WILL NOT EXPAND THIS LIMITATION.
- 10.2 IN NO EVENT WILL ARGOS BE LIABLE TO YOU OR TO ANY THIRD PARTY FOR ANY LOSS OF PROFITS, LOSS OF USE, LOSS OF REVENUE, LOSS OF GOODWILL, INTERRUPTION OF BUSINESS, LOSS OF DATA, OR ANY INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY, PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND ARISING OUT OF, OR IN CONNECTION WITH THESE TERMS OR YOUR USE (OR INABILITY TO USE) ANY PART OF THE SITE OR THE SERVICES, WHETHER IN CONTRACT, TORT, STRICT LIABILITY OR OTHERWISE, EVEN IF WE HAVE BEEN ADVISED OR ARE OTHERWISE AWARE OF THE POSSIBILITY OF SUCH DAMAGES.
- 10.3 THIS SECTION (LIMITATION OF LIABILITY) WILL BE GIVEN FULL EFFECT EVEN IF ANY REMEDY SPECIFIED IN THESE TERMS IS DEEMED TO HAVE FAILED OF ITS ESSENTIAL PURPOSE.
## 11. Ownership; Reservation of rights
- 11.1 As between the parties: (i) you own all right, title and interest in and to your Content; and (ii) MUI owns all right, title and interest in and to the Site and the Services, and all Intellectual Property Rights therein. The look and feel of the Site and the Services, including any custom graphics, button icons, and scripts are also the property of MUI, and you may not copy, imitate, or use them, in whole or in part, without MUI' prior written consent. MUI reserves all rights not expressly granted to you in these Terms, and MUI does not grant any licenses to you or to any other party under these Terms, whether by implication, estoppel or otherwise, except as expressly set forth herein.
- 11.2 You acknowledge that any suggestions, comments, or other feedback that you provide to MUI with respect to the Site, the Services, or any other MUI product or service (collectively, "Feedback") will be owned by MUI, including all Intellectual Property Rights therein, and will be and become MUI' Confidential Information (as defined below). You acknowledge and agree that MUI will be free to use, disclose, reproduce, license, and otherwise distribute and exploit the Feedback as MUI sees fit, without obligation or restriction of any kind. At our request and expense, you agree to execute documents or take such further actions as we may reasonably request to help us acquire, perfect, and maintain our rights in the Feedback.
## 12 Term, termination, and effect of termination
- 12.1 These Terms will apply to you starting on the Effective Date, and will continue for as long as you are accessing or using the Site or the Services.
- 12.2 MUI, in its sole discretion, has the right to suspend your ability to access the Services, without liability, under the following circumstances: (i) for scheduled or emergency maintenance to the Site or the Services, or any part thereof; (ii) if MUI believes that you are using the Site or Services in violation of these Terms or applicable law; (iii) if MUI believes that your use of the Site or the Services poses a security risk to us or to any third party; (iv) if required by law enforcement or government agency, or otherwise in order to comply with applicable law or regulation; or (v) if you fail to fulfill your payment obligations hereunder. MUI also reserves the right to temporarily or permanently suspend your ability to access the Services, without liability, if MUI determines, in its sole discretion, that you are engaging in abusive or excessively frequent use of the Services.
- 12.3 Either of us can terminate these Terms upon notice to the other if the other party breaches any of these Terms and fails to cure the breach within fifteen (15) days of receiving written notice of it from the non-breaching party. We reserve the right to terminate these Terms for cause immediately upon notice to you, and without giving you a cure period, if you breach any of these Terms relating to our intellectual property (including your compliance with the access grant and any restrictions) or our Confidential Information (defined below).
- 12.4 We can terminate any free Plan that you have subscribed to, at any time and for any reason, without notice or liability to you. We can terminate any paid Plan that you have subscribed to, for any reason and without liability, by providing notice to you that we intend to terminate your Plan at the end of the then-current Subscription Period.
- 12.5 When these Terms terminate or expire: (i) you will no longer have the right to use or access the Site or the Services as of the date of termination/expiration; (ii) if you owed us any fees prior to such termination/expiration, you will pay those fees immediately; and (iii) each of us will promptly return to the other (or, if the other party requests it, destroy) all Confidential Information belonging to the other. Sections 1, 2, 4 through 10, 11, and 13 through 15 will survive the termination or expiration of these Terms for any reason.
## 13. Support
- 13.1 If you are subscribed to a paid Plan, MUI will provide you with email-based support just write to our support desk at [support@mui.com](mailto:support@mui.com). While we work hard to respond to you and resolve your issues quickly, we do not warrant that we will respond within any particular timeframe, or that we will be able to resolve your issue. If you are subscribed to a free Plan, while you are welcome to email us your questions, we encourage you to visit our community forum which can provide valuable information to help answer your questions.
## 14. Confidential information
- 14.1 For the purposes of these Terms, "Confidential Information" means any technical or business information disclosed by one party to the other that: (i) if disclosed in writing, is marked "confidential" or "proprietary" at the time of disclosure; (ii) if disclosed orally, is identified as confidential or proprietary at the time of such disclosure, and is summarized in a writing sent by the disclosing Party to the receiving Party within thirty (30) days of the disclosure. For the purposes of these Terms you agree that the Feedback, any Reports we provide to you, and any non-public elements of the Site or the Services (including, without limitation, the source code of any MUI-proprietary software), will be deemed to be MUI's Confidential Information, regardless of whether it is marked as such.
- 14.2 Neither of us will use the other party's Confidential Information, except as permitted by these Terms. Each of us agrees to maintain in confidence and protect the other party's Confidential Information using at least the same degree of care as it uses for its own information of a similar nature, but in all events at least a reasonable degree of care. Each of us agrees to take all reasonable precautions to prevent any unauthorized disclosure of the other party's Confidential Information, including, without limitation, disclosing Confidential Information only to its employees, independent contractors, consultants, and legal and financial advisors (collectively, "Representatives"): (i) with a need to know such information, (ii) who are parties to appropriate agreements sufficient to comply with this Section 13, and (iii) who are informed of the nondisclosure obligations imposed by this Section 13. Each party will be responsible for all acts and omissions of its Representatives. The foregoing obligations will not restrict either party from disclosing Confidential Information of the other party pursuant to the order or requirement of a court, administrative agency, or other governmental body, provided that the party required to make such a disclosure gives reasonable notice to the other party to enable them to contest such order or requirement.
- 14.3 The restrictions set forth in Section 13 will not apply with respect to any Confidential Information that: (i) was or becomes publicly known through no fault of the receiving party; (ii) was rightfully known or becomes rightfully known to the receiving party without confidential or proprietary restriction from a source other than the disclosing party who has a right to disclose it; (iii) is approved by the disclosing party for disclosure without restriction in a written document which is signed by a duly authorized officer of such disclosing party; or (iv) the receiving party independently develops without access to or use of the other party's Confidential Information.
## 15. Trademarks
You acknowledge and agree that any MUI names, trademarks, service marks, logos, trade dress, or other branding included on the Site or as part of the Services (collectively, the "Marks") are owned by MUI and may not be copied, imitated, or used (in whole or in part) without MUI's prior written consent. All other trademarks, names, or logos referenced on the Site or the Services (collectively, "Third-Party Trademarks") are the property of their respective owners, and the use of such Third-Party Trademarks inure to the benefit of their respective owners. The use of such Third-Party Trademarks is intended to denote interoperability, and does not constitute an affiliation by MUI or its licensors with any company or an endorsement or approval by that company of MUI, its licensors, or their respective products or services.
## 16. General provisions
- 16.1 These Terms, together with any policies incorporated into these Terms by reference, are the complete and exclusive understanding of the parties with respect to MUI's provision of, and your use of and access to, the Site and the Services, and supersede all previous or contemporaneous agreements or communications, whether written or oral, relating to the subject matter of these Terms (including, without limitation, prior versions of these Terms). Any terms or conditions that you send to MUI that are inconsistent with or in addition to these Terms are hereby rejected by MUI, and will be deemed void and of no effect.
- 16.2 These Terms will be governed by and construed in accordance with the laws of the State of California, without regard to that State's conflict of law principles. Any legal action or proceeding arising under, related to or connected with these Terms will be brought exclusively in the federal (if they have jurisdiction) or state courts located in San Francisco, California, and the parties irrevocably consent to the personal jurisdiction and venue of such court(s). The United Nations Convention on Contracts for the International Sale of Goods and the Uniform Computer Information Transactions Act will not apply to these Terms. If a party initiates any proceeding regarding these Terms, the prevailing party to such proceeding is entitled to reasonable attorneys' fees and costs.
- 16.3 You agree that MUI has the right to use your name and logo on the Site or other MUI websites or marketing materials, for the purposes of identifying you as a MUI customer and describing your use of the Services. You also agree that MUI may (but is under no obligation to): (i) issue a press release identifying you as a MUI customer; (ii) inform other potential customers that you are a user of the Services; and (iii) identify you as a customer in other forms of publicity (including, without limitation, case studies, blog posts, and the like.
- 16.4 You may not assign these Terms, in whole or in part, by operation of law or otherwise, without the prior written consent of MUI, and any attempted transfer, assignment or delegation without such consent will be void and of no effect. MUI may freely transfer, assign or delegate these Terms, or its rights and duties under these Terms, without notice to you. Subject to the foregoing, these Terms will be binding upon and will inure to the benefit of the parties and their respective representatives, heirs, administrators, successors and permitted assigns.
- 16.5 Except as expressly set forth in these Terms, the exercise by either party of any of its remedies will be without prejudice to its other remedies under these Terms or otherwise. The failure by a party to enforce any part of these Terms will not constitute a waiver of future enforcement of that or any other provision. Any waiver of any provision of these Terms will be effective only if in writing and signed by an authorized representative of the waiving party.
- 16.6 You agree that any notice that MUI is required to provide pursuant to these Terms can be given electronically, which may include an email to the email address you provide to MUI as part of your Registration Data. These notices can be about a wide variety of things, including responding to your questions, requests for additional information, and legal notices. You agree that such electronic notices satisfy any legal requirement that such communications be in writing. An electronic notice will be deemed to have been received on the day the email is sent to you, provided that the email is the same as the email address you provided as part of your Registration Data.
- 16.7 You acknowledge that you are responsible for complying with all applicable laws and regulations associated with your access and use of the Site and Services, including, without limitation, all applicable export control laws and regulations.
- 16.8 We do not develop any technical data or computer software pursuant to these Terms. The Site and the Services have been developed solely with private funds, are considered "Commercial Computer Software" and "Commercial Computer Software Documentation" as described in FAR 12.212, FAR 27.405-3, and DFARS 227.7202-3, and access is provided to U.S. Government end users as restricted computer software and limited rights data. Any use, disclosure, modification, distribution, or reproduction of the Site or the Services by the U.S. Government, its end users or contractors is subject to the restrictions set forth in these Terms.
- 16.9 If any portion of these Terms is held to be unenforceable or invalid, that portion will be enforced to the maximum extent possible, and all other provisions will remain in full force and effect.
- 16.10 Except for payments due under these Terms, neither party will be responsible for any delay or failure to perform that is attributable in whole or in part to any cause beyond its reasonable control, including, without limitation, acts of God (fire, storm, floods, earthquakes, etc.); civil disturbances; disruption of telecommunications, power or other essential services; interruption or termination of service by any service providers used by MUI to host the Services or to link its servers to the Internet; labor disturbances; vandalism; cable cut; computer viruses or other similar occurrences; or any malicious or unlawful acts of any third party.
- 16.11 We are each independent contractors with respect to the subject matter of these Terms. Nothing contained in these Terms will be deemed or construed in any manner whatsoever to create a partnership, joint venture, employment, agency, fiduciary, or other similar relationship between us, and neither of us can bind the other contractually.

View File

@ -0,0 +1,18 @@
import * as React from 'react';
import { ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import theme from './theme';
export default function withRoot(Component) {
function WithRoot(props) {
return (
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<Component {...props} />
</ThemeProvider>
);
}
return WithRoot;
}

View File

@ -0,0 +1,20 @@
import * as React from 'react';
import { ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import theme from './theme';
export default function withRoot<P extends JSX.IntrinsicAttributes>(
Component: React.ComponentType<P>,
) {
function WithRoot(props: P) {
return (
<ThemeProvider theme={theme}>
{/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
<CssBaseline />
<Component {...props} />
</ThemeProvider>
);
}
return WithRoot;
}

View File

@ -8,12 +8,14 @@
"jsx": "react",
"allowJs": true,
"moduleResolution": "node",
"baseUrl": "./src",
"baseUrl": "./",
"paths" : {
"@components": ["components"],
"@components/*": ["components/*"],
"@scss/*": ["scss/*"],
"@ts/*": ["ts/*"],
}
"@components": ["src/components"],
"@components/*": ["src/components/*"],
"@scss/*": ["src/scss/*"],
"@ts/*": ["src/ts/*"],
"@theme/*": ["themes/onepirate/*"],
}
},
"include": ["./src/**/*"]
}