First Commit
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
'use client';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
// @mui
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import List from '@mui/material/List';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
// @project
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
// Helper functions for scrollspy
|
||||
const clamp = (value) => Math.max(0, value);
|
||||
const isBetween = (value, floor, ceil) => value >= floor && value <= ceil;
|
||||
|
||||
/*************************** HOOKS - SCROLLSPY ***************************/
|
||||
|
||||
function useScrollspy(ids, offset = 0) {
|
||||
const [activeId, setActiveId] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const listener = () => {
|
||||
const scroll = window.scrollY;
|
||||
|
||||
const position = ids
|
||||
.map((id) => {
|
||||
const element = document.getElementById(id);
|
||||
if (!element) return { id, top: -1, bottom: -1 };
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
const top = clamp(rect.top + scroll - offset);
|
||||
const bottom = clamp(rect.bottom + scroll - offset);
|
||||
|
||||
return { id, top, bottom };
|
||||
})
|
||||
.find(({ top, bottom }) => isBetween(scroll, top, bottom));
|
||||
|
||||
setActiveId(position?.id || '');
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', listener);
|
||||
window.addEventListener('resize', listener);
|
||||
listener(); // Initial call to set the activeId
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', listener);
|
||||
window.removeEventListener('resize', listener);
|
||||
};
|
||||
}, [ids, offset]);
|
||||
|
||||
return activeId;
|
||||
}
|
||||
|
||||
/*************************** TERMS CONDITION - DATA ***************************/
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
id: 'acceptance-of-terms',
|
||||
heading: 'Acceptance of terms',
|
||||
caption:
|
||||
'By accessing and using this website, you agree to be bound by these Terms and Conditions of Use. If you do not agree with any part of these terms, you must not use the website. shares information about you when you use our website or services. By accessing or using our website, you consent to the practices described in this policy.'
|
||||
},
|
||||
{
|
||||
id: 'changes-to-terms',
|
||||
heading: 'Changes to terms',
|
||||
caption:
|
||||
'We reserve the right to modify or replace these terms at our sole discretion. It is your responsibility to check these terms periodically for changes. Your continued use of the website after the posting of any changes constitutes acceptance of those changes.'
|
||||
},
|
||||
{
|
||||
id: 'user-conduct',
|
||||
heading: 'User conduct',
|
||||
caption:
|
||||
'You agree to use this website only for lawful purposes and in a manner consistent with all applicable local, national, and international laws and regulations.'
|
||||
},
|
||||
{
|
||||
id: 'intellectual-property',
|
||||
heading: 'Intellectual property',
|
||||
caption:
|
||||
'All content on this website, including but not limited to text, graphics, logos, images, audio clips, video clips, digital downloads, and data compilations, is the property of [Your Company Name] or its content suppliers and protected by international copyright laws.'
|
||||
},
|
||||
{
|
||||
id: 'privacy-policy',
|
||||
heading: 'Privacy policy',
|
||||
caption:
|
||||
'We do not sell, trade, or otherwise transfer your personal information to third parties. We may share information with trusted service providers who assist us in operating our website, conducting our business, or servicing you.'
|
||||
},
|
||||
{
|
||||
id: 'user-generated-content',
|
||||
heading: 'User-generated content',
|
||||
caption:
|
||||
'If you submit any material to this website, you grant [Your Company Name] a perpetual, royalty-free, worldwide license to use, reproduce, modify, adapt, publish, translate, create derivative works from, distribute, and display such material.'
|
||||
},
|
||||
{
|
||||
id: 'limitation-of-liability',
|
||||
heading: 'Limitation of liability',
|
||||
caption:
|
||||
'In no event shall [Your Company Name] or its affiliates be liable for any direct, indirect, incidental, special, or consequential damages resulting from the use or inability to use this website.'
|
||||
},
|
||||
{
|
||||
id: 'indemnity',
|
||||
heading: 'Indemnity',
|
||||
caption:
|
||||
"You agree to indemnify and hold harmless [Your Company Name] and its affiliates from any claims, actions, demands, damages, liabilities, costs, or expenses, including reasonable attorneys' fees, arising out of or related to your use of the website or any violation of these terms."
|
||||
},
|
||||
{
|
||||
id: 'governing-law',
|
||||
heading: 'Governing law',
|
||||
caption:
|
||||
'These terms are governed by and construed in accordance with the laws of [Your Country/State], without regard to its conflict of law principles.'
|
||||
}
|
||||
];
|
||||
|
||||
/*************************** SECTIONS - TERMS CONDITION ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Terms Condition](https://www.saasable.io/sections/terms-condition)
|
||||
*/
|
||||
|
||||
export default function TermsConditionPage() {
|
||||
const ids = menuItems.map((item) => item.id);
|
||||
|
||||
// Adjust offset as per header height
|
||||
const activeId = useScrollspy(ids, 60);
|
||||
const [selectedID, setSelectedID] = useState(activeId);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeId) {
|
||||
setSelectedID(activeId);
|
||||
}
|
||||
}, [activeId]);
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ pb: SECTION_COMMON_PY }}>
|
||||
<Grid container spacing={{ xs: 2, md: 3 }}>
|
||||
<Grid size={{ xs: 12, sm: 4, md: 3 }}>
|
||||
<List component="div" sx={{ position: 'sticky', top: 20 }} disablePadding>
|
||||
{menuItems.map((item, index) => (
|
||||
<ListItemButton
|
||||
key={index}
|
||||
href={`#${item.id}`}
|
||||
sx={{
|
||||
py: 1.25,
|
||||
px: 1.5,
|
||||
borderRadius: 3,
|
||||
mb: 0.75,
|
||||
...(selectedID === item.id && { color: 'primary.main', bgcolor: 'grey.100' }),
|
||||
'&:hover': { bgcolor: 'grey.50' }
|
||||
}}
|
||||
onClick={() => setSelectedID(item.id)}
|
||||
>
|
||||
<ListItemText primary={item.heading} sx={{ my: 0 }} slotProps={{ primary: { variant: 'subtitle1' } }} />
|
||||
</ListItemButton>
|
||||
))}
|
||||
</List>
|
||||
<Divider sx={{ display: { xs: 'block', sm: 'none' } }} />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 8, md: 9 }}>
|
||||
{menuItems.map((item, index) => (
|
||||
<Stack
|
||||
key={index}
|
||||
id={item.id}
|
||||
sx={{ py: { xs: 1, sm: 1.5, md: 3 }, px: { md: 3 }, gap: 1, '&:first-of-type': { pt: { sm: 0 } } }}
|
||||
>
|
||||
<Typography variant="h4">{item.heading}</Typography>
|
||||
<Typography variant="body1" sx={{ color: 'text.secondary' }}>
|
||||
{item.caption}
|
||||
</Typography>
|
||||
</Stack>
|
||||
))}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
import PropTypes from 'prop-types';
|
||||
// @mui
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @project
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import Typeset from '@/components/Typeset';
|
||||
|
||||
/*************************** TYPOGRAPHY - DATA ***************************/
|
||||
|
||||
const typographyData = [
|
||||
{
|
||||
heading: 'Headings',
|
||||
items: [
|
||||
{
|
||||
title: 'Heading 01',
|
||||
sizeValue: { desktop: '57px', tablet: '45px', mobile: '36px' },
|
||||
lineHeight: { desktop: '64px', tablet: '52px', mobile: '44px' },
|
||||
letterSpacing: { desktop: '-0.25px', tablet: '0px', mobile: '0px' },
|
||||
variant: 'h1',
|
||||
label: 'h1 - Heading Large',
|
||||
fontWeight: 'Regular (400)'
|
||||
},
|
||||
{
|
||||
title: 'Heading 02',
|
||||
sizeValue: { desktop: '45px', tablet: '36px', mobile: '24px' },
|
||||
lineHeight: { desktop: '52px', tablet: '44px', mobile: '32px' },
|
||||
letterSpacing: { desktop: '0px', tablet: '0px', mobile: '0px' },
|
||||
variant: 'h2',
|
||||
label: 'h2 - Heading Medium',
|
||||
fontWeight: 'Regular (400)'
|
||||
},
|
||||
{
|
||||
title: 'Heading 03',
|
||||
sizeValue: { desktop: '28px', tablet: '24px', mobile: '20px' },
|
||||
lineHeight: { desktop: '36px', tablet: '32px', mobile: '28px' },
|
||||
letterSpacing: { desktop: '0px', tablet: '0px', mobile: '0px' },
|
||||
variant: 'h3',
|
||||
label: 'h3 - Display Medium',
|
||||
fontWeight: 'Regular (400)'
|
||||
},
|
||||
{
|
||||
title: 'Heading 04',
|
||||
sizeValue: { desktop: '24px', tablet: '20px', mobile: '16px' },
|
||||
lineHeight: { desktop: '32px', tablet: '28px', mobile: '24px' },
|
||||
letterSpacing: { desktop: '0px', tablet: '0px', mobile: '0px' },
|
||||
variant: 'h4',
|
||||
label: 'h4 - Display Small',
|
||||
fontWeight: 'Regular (400)'
|
||||
},
|
||||
{
|
||||
title: 'Heading 05',
|
||||
sizeValue: { desktop: '22px', tablet: '16px', mobile: '16px' },
|
||||
lineHeight: { desktop: '28px', tablet: '24px', mobile: '24px' },
|
||||
letterSpacing: { desktop: '0px', tablet: '0.15px', mobile: '0.15px' },
|
||||
variant: 'h5',
|
||||
label: 'h5 - Label Large',
|
||||
fontWeight: 'Medium (500)'
|
||||
},
|
||||
{
|
||||
title: 'Heading 06',
|
||||
sizeValue: { desktop: '22px', tablet: '16px', mobile: '16px' },
|
||||
lineHeight: { desktop: '30px', tablet: '24px', mobile: '24px' },
|
||||
letterSpacing: { desktop: '0px', tablet: '0.5px', mobile: '0.5px' },
|
||||
variant: 'h6',
|
||||
label: 'h6 - Paragraph Large',
|
||||
fontWeight: 'Regular (400)'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Body / Paragraph',
|
||||
items: [
|
||||
{
|
||||
title: 'Body 01',
|
||||
sizeValue: { desktop: '16px', tablet: '14px', mobile: '14px' },
|
||||
lineHeight: { desktop: '24px', tablet: '20px', mobile: '20px' },
|
||||
letterSpacing: { desktop: '0.5px', tablet: '0.25px', mobile: '0.25px' },
|
||||
variant: 'body1',
|
||||
label: 'body1 - Paragraph Medium',
|
||||
fontWeight: 'Regular (400)'
|
||||
},
|
||||
{
|
||||
title: 'Body 02',
|
||||
sizeValue: { desktop: '14px', tablet: '12px', mobile: '12px' },
|
||||
lineHeight: { desktop: '20px', tablet: '18px', mobile: '18px' },
|
||||
letterSpacing: { desktop: '0.25px', tablet: '0.25px', mobile: '0.25px' },
|
||||
variant: 'body2',
|
||||
label: 'body2 - Paragraph Small',
|
||||
fontWeight: 'Regular (400)'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Subtitle',
|
||||
items: [
|
||||
{
|
||||
title: 'Subtitle 01',
|
||||
sizeValue: { desktop: '16px', tablet: '14px', mobile: '14px' },
|
||||
lineHeight: { desktop: '24px', tablet: '20px', mobile: '20px' },
|
||||
letterSpacing: { desktop: '0.15px', tablet: '0.1px', mobile: '0.1px' },
|
||||
variant: 'subtitle1',
|
||||
label: 'subtitle1 - Label Medium',
|
||||
fontWeight: 'Medium (500)'
|
||||
},
|
||||
{
|
||||
title: 'Subtitle 02',
|
||||
sizeValue: { desktop: '14px', tablet: '12px', mobile: '12px' },
|
||||
lineHeight: { desktop: '20px', tablet: '18px', mobile: '18px' },
|
||||
letterSpacing: { desktop: '0.1px', tablet: '0.1px', mobile: '0.1 px' },
|
||||
variant: 'subtitle2',
|
||||
label: 'subtitle2 - Label Small',
|
||||
fontWeight: 'Semibold (600)'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Caption',
|
||||
items: [
|
||||
{
|
||||
title: 'Caption',
|
||||
sizeValue: { desktop: '12px', tablet: '12px', mobile: '12px' },
|
||||
lineHeight: { desktop: '16px', tablet: '16px', mobile: '16px' },
|
||||
letterSpacing: { desktop: '0px', tablet: '0px', mobile: '0 px' },
|
||||
variant: 'caption',
|
||||
label: 'caption - Hyperlink Small',
|
||||
fontWeight: 'Semibold (600)'
|
||||
},
|
||||
{
|
||||
title: 'Caption 01',
|
||||
sizeValue: { desktop: '16px', tablet: '16px', mobile: '16px' },
|
||||
lineHeight: { desktop: '24px', tablet: '24px', mobile: '24px' },
|
||||
letterSpacing: { desktop: '0.5px', tablet: '0.5px', mobile: '0.5px' },
|
||||
variant: 'caption1',
|
||||
label: 'caption1 - Hyperlink Large',
|
||||
fontWeight: 'Regular (400)'
|
||||
},
|
||||
{
|
||||
title: 'Caption 02',
|
||||
sizeValue: { desktop: '14px', tablet: '14px', mobile: '14px' },
|
||||
lineHeight: { desktop: '20px', tablet: '20px', mobile: '20px' },
|
||||
letterSpacing: { desktop: '0.1px', tablet: '0.1 px', mobile: '0.1px' },
|
||||
variant: 'caption2',
|
||||
label: 'caption2 - Hyperlink Medium',
|
||||
fontWeight: 'Medium (500)'
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
/*************************** TYPOGRAPHY - BLOCK ***************************/
|
||||
|
||||
function FontSizeBlock({ desktop, tablet, mobile, type }) {
|
||||
let label1 = 'Desktop';
|
||||
let label2 = 'Tablet';
|
||||
let label3 = 'Mobile';
|
||||
|
||||
if (type === 'letter-spacing') {
|
||||
label1 = label2 = label3 = 'Letter Spacing';
|
||||
}
|
||||
|
||||
if (type === 'line-height') {
|
||||
label1 = label2 = label3 = 'Line Height';
|
||||
}
|
||||
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
<Grid size={4}>
|
||||
<Stack sx={{ gap: 0.5 }}>
|
||||
<Typography variant="body2" sx={{ color: 'grey.700' }}>
|
||||
{label1}
|
||||
</Typography>
|
||||
<Typography variant="subtitle1">{desktop}</Typography>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid size={4}>
|
||||
<Stack sx={{ gap: 0.5 }}>
|
||||
<Typography variant="body2" sx={{ color: 'grey.700' }}>
|
||||
{label2}
|
||||
</Typography>
|
||||
<Typography variant="subtitle1">{tablet}</Typography>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid size={4}>
|
||||
<Stack sx={{ gap: 0.5 }}>
|
||||
<Typography variant="body2" sx={{ color: 'grey.700' }}>
|
||||
{label3}
|
||||
</Typography>
|
||||
<Typography variant="subtitle1">{mobile}</Typography>
|
||||
</Stack>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
/*************************** SECTIONS - TYPOGRAPHY ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Typography](https://www.saasable.io/sections/typography)
|
||||
*/
|
||||
|
||||
export default function TypographyPage() {
|
||||
return (
|
||||
<Grid container spacing={{ xs: 2.5, sm: 3, md: 4 }}>
|
||||
{typographyData.map((item, index) => (
|
||||
<Grid key={index} size={12}>
|
||||
<Stack sx={{ gap: { xs: 1.5 } }}>
|
||||
<Typeset {...{ heading: item.heading, stackProps: { sx: { mb: 1 } } }} />
|
||||
{item.items.map((block, index) => (
|
||||
<GraphicsCard key={index}>
|
||||
<Box sx={{ p: { xs: 3, sm: 4, md: 5 } }}>
|
||||
<Grid container spacing={2.5}>
|
||||
<Grid size={{ xs: 12, sm: 3, md: 2 }}>
|
||||
<Typography variant="h4">{block.title}</Typography>
|
||||
<Typography variant="body2" sx={{ color: 'grey.700' }}>
|
||||
{block.label}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 11, sm: 6, md: 4 }}>
|
||||
<Stack sx={{ gap: 2.5 }}>
|
||||
<Typography variant="subtitle1">{block.fontWeight}</Typography>
|
||||
<FontSizeBlock {...block.sizeValue} type="font-size" />
|
||||
<FontSizeBlock {...block.lineHeight} type="line-height" />
|
||||
<FontSizeBlock {...block.letterSpacing} type="letter-spacing" />
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<Typography variant={block.variant}>Quick brown fox jumps!</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</GraphicsCard>
|
||||
))}
|
||||
</Stack>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
FontSizeBlock.propTypes = { desktop: PropTypes.string, tablet: PropTypes.string, mobile: PropTypes.string, type: PropTypes.string };
|
||||
@@ -0,0 +1,125 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
import Slider from 'react-slick';
|
||||
|
||||
// @project
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import GraphicsImage from '@/components/GraphicsImage';
|
||||
|
||||
import { withAlpha } from '@/utils/colorUtils';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
/*************************** CLIENTELE - 3 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Clientele3](https://www.saasable.io/blocks/clientele/clientele3)
|
||||
*
|
||||
* API:
|
||||
* - [Clientele3 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/clientele/clientele3#props-details)
|
||||
*/
|
||||
|
||||
export default function Clientele3({ title, clienteleList }) {
|
||||
const theme = useTheme();
|
||||
|
||||
const settings = {
|
||||
autoplay: true,
|
||||
arrows: false,
|
||||
dots: false,
|
||||
infinite: true,
|
||||
speed: 500,
|
||||
slidesToShow: 5,
|
||||
swipeToSlide: true,
|
||||
initialSlide: 0,
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: theme.breakpoints.values.md,
|
||||
settings: { slidesToShow: 4 }
|
||||
},
|
||||
{
|
||||
breakpoint: theme.breakpoints.values.sm,
|
||||
settings: { slidesToShow: 2, centerMode: true }
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const shade = {
|
||||
content: `' '`,
|
||||
zIndex: 1,
|
||||
position: 'absolute',
|
||||
width: { sm: 60, xs: 40 },
|
||||
height: 1,
|
||||
top: 0,
|
||||
background: `linear-gradient(90deg, ${theme.vars.palette.background.default} -8.54%, ${withAlpha(theme.vars.palette.background.default, 0)} 100%)`,
|
||||
transform: null
|
||||
};
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ gap: 2.5 }}>
|
||||
{title && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.3 }}
|
||||
>
|
||||
<Typography variant="subtitle2" align="center" sx={{ color: 'text.secondary' }}>
|
||||
{title}
|
||||
</Typography>
|
||||
</motion.div>
|
||||
)}
|
||||
<Box sx={{ position: 'relative', '&:before': { ...shade, left: 0 }, '&:after': { ...shade, right: 0, rotate: '180deg' } }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.4
|
||||
}}
|
||||
>
|
||||
<Slider {...settings}>
|
||||
{clienteleList.map((item, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, scale: 0.5 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, ease: 'easeOut', delay: index * 0.2 }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
px: { xs: 0.25, sm: 0.5, md: 0.75 },
|
||||
'& svg': { opacity: 0.4, transition: ' all 0.5s ease-in-out' },
|
||||
'&:hover svg': { opacity: 1, transition: ' all 0.5s ease-in-out' }
|
||||
}}
|
||||
>
|
||||
<Chip
|
||||
label={<GraphicsImage {...item} />}
|
||||
slotProps={{ label: { sx: { p: 0 } } }}
|
||||
sx={{ bgcolor: 'grey.100', height: { xs: 40, sm: 46, md: 60 }, width: 1 }}
|
||||
/>
|
||||
</Box>
|
||||
</motion.div>
|
||||
))}
|
||||
</Slider>
|
||||
</motion.div>
|
||||
</Box>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Clientele3.propTypes = { title: PropTypes.string, clienteleList: PropTypes.array };
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Clientele3 } from './Clientele3';
|
||||
@@ -0,0 +1,131 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import Button from '@mui/material/Button';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
// @project
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import { ContactUsForm2 } from '@/components/contact-us';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import Typeset from '@/components/Typeset';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
/*************************** CONTACT US - CARD ***************************/
|
||||
|
||||
function ContactCard({ icon, title, content, link }) {
|
||||
return (
|
||||
<GraphicsCard sx={{ height: 1 }}>
|
||||
<Stack direction={{ xs: 'row', sm: 'column' }} sx={{ gap: { xs: 2, sm: 4, md: 5 }, height: 1, p: { xs: 2, sm: 3, md: 4 } }}>
|
||||
<Avatar sx={{ width: 60, height: 60, bgcolor: 'grey.300' }}>
|
||||
<SvgIcon {...(typeof icon === 'string' ? { name: icon } : { ...icon })} />
|
||||
</Avatar>
|
||||
<Stack sx={{ gap: { xs: 2, md: 3 }, height: 1, alignItems: 'flex-start', justifyContent: 'space-between' }}>
|
||||
<Typeset
|
||||
{...{
|
||||
heading: title,
|
||||
caption: content,
|
||||
stackProps: { sx: { gap: 1 } },
|
||||
headingProps: { variant: 'h4' },
|
||||
captionProps: { variant: 'body1' }
|
||||
}}
|
||||
/>
|
||||
{link && (
|
||||
<ButtonAnimationWrapper>
|
||||
<Button color="primary" variant="outlined" {...link} />
|
||||
</ButtonAnimationWrapper>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</GraphicsCard>
|
||||
);
|
||||
}
|
||||
|
||||
/*************************** CONTACT US - 4 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [ContactUs4](https://www.saasable.io/blocks/contact-us/contact-us4)
|
||||
*
|
||||
* API:
|
||||
* - [ContactUs4 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/contact-us/contactus4#props-details)
|
||||
*/
|
||||
|
||||
export default function ContactUs4({ heading, caption, list, showForm = true }) {
|
||||
const sectionPadding = { xs: 2, sm: 3, md: 5 };
|
||||
const cardRadius = { xs: 6, sm: 8 };
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ gap: { xs: 3, sm: 4 } }}>
|
||||
<Grid container spacing={1.5}>
|
||||
{showForm && (
|
||||
<Grid size={12}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.4,
|
||||
delay: 0.4
|
||||
}}
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
<GraphicsCard sx={{ height: 1, borderRadius: cardRadius }}>
|
||||
<GraphicsCard sx={{ bgcolor: 'grey.200', borderRadius: cardRadius }}>
|
||||
<Box sx={{ p: { xs: 2, sm: 4, md: 5 } }}>
|
||||
{heading && (
|
||||
<Typeset
|
||||
{...{
|
||||
heading,
|
||||
caption,
|
||||
stackProps: { sx: { alignItems: 'center', textAlign: 'center' } },
|
||||
headingProps: { sx: { maxWidth: { xs: '85%', sm: '80%' } } },
|
||||
captionProps: { sx: { maxWidth: { sm: '60%' } } }
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</GraphicsCard>
|
||||
<Box sx={{ p: sectionPadding, px: { md: 24 } }}>
|
||||
<ContactUsForm2 />
|
||||
</Box>
|
||||
</GraphicsCard>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
)}
|
||||
{list?.map((item, index) => (
|
||||
<Grid key={index} size={{ xs: 12, sm: 4 }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.4,
|
||||
delay: item.animationDelay
|
||||
}}
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
<ContactCard {...{ ...item }} />
|
||||
</motion.div>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
ContactCard.propTypes = { icon: PropTypes.any, title: PropTypes.any, content: PropTypes.any, link: PropTypes.any };
|
||||
|
||||
ContactUs4.propTypes = { heading: PropTypes.any, caption: PropTypes.any, list: PropTypes.any, showForm: PropTypes.bool };
|
||||
@@ -0,0 +1 @@
|
||||
export { default as ContactUs4 } from './ContactUs4';
|
||||
@@ -0,0 +1,122 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import Button from '@mui/material/Button';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import { ProfileGroup } from '@/components/cards/profile-card';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
// @assets
|
||||
import Arrow from '@/images/graphics/Arrow';
|
||||
|
||||
/*************************** CALL TO ACTION - 4 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [CTA4](https://www.saasable.io/blocks/cta/cta4)
|
||||
*
|
||||
* API:
|
||||
* - [CTA4 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/cta/cta4#props-details)
|
||||
*/
|
||||
|
||||
export default function Cta4({ headLine, primaryBtn, profileGroups, list, clientContent }) {
|
||||
const transformValues = { xs: 'rotate(45deg)', sm: 'rotate(320deg)', md: 'unset' };
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<GraphicsCard>
|
||||
<Box sx={{ p: { xs: 3, sm: 4, md: 5 } }}>
|
||||
<Grid container spacing={{ xs: 5, sm: 0, md: 3 }} sx={{ alignItems: 'flex-end' }}>
|
||||
<Grid size={{ xs: 12, sm: 9, md: 8 }}>
|
||||
<Stack sx={{ gap: 5 }}>
|
||||
<ProfileGroup {...profileGroups} />
|
||||
<Stack sx={{ gap: { xs: 2, sm: 5 } }}>
|
||||
{typeof headLine === 'string' ? <Typography variant="h2">{headLine}</Typography> : headLine}
|
||||
{list && (
|
||||
<Stack direction={{ sm: 'row' }} sx={{ columnGap: { xs: 1, sm: 3 }, rowGap: 1, flexWrap: 'wrap' }}>
|
||||
{list.map((item, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.2, ease: 'easeInOut', delay: index * 0.6 }}
|
||||
>
|
||||
<Stack direction="row" sx={{ gap: 1, alignItems: 'center' }}>
|
||||
<SvgIcon name="tabler-rosette-discount-check" color="text.secondary" stroke={1} />
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
{item.primary}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</motion.div>
|
||||
))}
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid sx={{ position: 'relative', pl: { md: 3 }, pt: { md: 3 } }} size={{ sm: 3, md: 4 }}>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: { xs: -36, sm: -98, md: -68 },
|
||||
right: { xs: -70, sm: 40, md: 100 },
|
||||
transform: transformValues
|
||||
}}
|
||||
>
|
||||
<Arrow />
|
||||
</Box>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
sx={{
|
||||
color: 'primary.main',
|
||||
width: 94,
|
||||
position: 'absolute',
|
||||
top: { xs: 6, sm: -160, md: -82 },
|
||||
right: { xs: -160, sm: 0 }
|
||||
}}
|
||||
>
|
||||
{clientContent}
|
||||
</Typography>
|
||||
<Box sx={{ textAlign: 'right' }}>
|
||||
<motion.div
|
||||
initial={{ scale: 0.9 }}
|
||||
animate={{ opacity: 1, y: 0, scale: [1, 1.05, 1] }}
|
||||
transition={{ duration: 1.1, delay: 0.1, ease: 'easeInOut', repeat: Infinity }}
|
||||
whileHover={{ scale: 1, transition: { duration: 0.3 } }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button color="primary" size="large" variant="contained" sx={{ minWidth: { md: 263 } }} {...primaryBtn} />
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</GraphicsCard>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Cta4.propTypes = {
|
||||
headLine: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
primaryBtn: PropTypes.any,
|
||||
profileGroups: PropTypes.object,
|
||||
list: PropTypes.array,
|
||||
clientContent: PropTypes.string
|
||||
};
|
||||
@@ -0,0 +1,200 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import Button from '@mui/material/Button';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import OutlinedInput from '@mui/material/OutlinedInput';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import { ProfileGroup } from '@/components/cards/profile-card';
|
||||
import LogoWatermark from '@/components/logo/LogoWatermark';
|
||||
import Typeset from '@/components/Typeset';
|
||||
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
// @assets
|
||||
import Wave from '@/images/graphics/Wave';
|
||||
|
||||
/*************************** CALL TO ACTION - 5 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [CTA5](https://www.saasable.io/blocks/cta/cta5)
|
||||
*
|
||||
* API:
|
||||
* - [CTA5 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/cta/cta5#props-details)
|
||||
*/
|
||||
|
||||
export default function Cta5({ heading, caption, label, input = false, primaryBtn, secondaryBtn, description, saleData, profileGroups }) {
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.4
|
||||
}}
|
||||
>
|
||||
<Grid container spacing={1.5}>
|
||||
<Grid size={{ xs: 12, sm: 8, md: 9 }}>
|
||||
<motion.div initial={{ opacity: 0, y: -100 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.8, delay: 0.6 }}>
|
||||
<GraphicsCard sx={{ position: 'relative' }}>
|
||||
<Stack
|
||||
sx={{ alignItems: 'flex-start', gap: { xs: 5.75, sm: 10 }, p: { xs: 3, sm: 4, md: 8 }, position: 'relative', zIndex: 1 }}
|
||||
>
|
||||
<Stack sx={{ gap: 5 }}>
|
||||
<Stack direction="row" sx={{ alignItems: 'center', gap: 1 }}>
|
||||
<Chip
|
||||
label={label}
|
||||
variant="outlined"
|
||||
slotProps={{ label: { sx: { py: 0.75, px: 2, typography: 'caption', color: 'secondary.main' } } }}
|
||||
sx={{ borderColor: 'grey.600' }}
|
||||
/>
|
||||
<Divider sx={{ width: 63, borderBottomWidth: 2 }} />
|
||||
</Stack>
|
||||
<Typeset {...{ heading, caption, captionProps: { sx: { maxWidth: 478 } } }} />
|
||||
</Stack>
|
||||
{input && typeof input === 'object' && (
|
||||
<Stack sx={{ gap: 0.75, width: { sm: '100%', md: 'unset' } }}>
|
||||
<OutlinedInput
|
||||
placeholder={input.placeholder || 'Enter your email address'}
|
||||
endAdornment={
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
sx={{ px: 4, minWidth: { xs: 110, md: 120 } }}
|
||||
{...input.adornmentBtn}
|
||||
/>
|
||||
}
|
||||
slotProps={{
|
||||
input: { 'aria-label': 'Email address', sx: { px: 2.5, py: 0.75 } },
|
||||
notchedOutline: { sx: { borderRadius: 25 } }
|
||||
}}
|
||||
sx={{ typography: 'caption2', color: 'secondary.main', p: 0.5, whiteSpace: 'nowrap' }}
|
||||
/>
|
||||
{input.helpertext && (
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
{input.helpertext}
|
||||
</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
{(primaryBtn || secondaryBtn || description) && (
|
||||
<Stack sx={{ alignItems: 'flex-start', gap: 1.5, width: { sm: '100%', md: '60%' }, ...(input && { mt: -6 }) }}>
|
||||
{(primaryBtn || secondaryBtn) && (
|
||||
<Stack direction="row" spacing={1.5} sx={{ alignItems: 'flex-start', justifyContent: 'center' }}>
|
||||
{secondaryBtn && (
|
||||
<ButtonAnimationWrapper>
|
||||
<Button variant="outlined" sx={{ minWidth: { sm: 170 } }} {...secondaryBtn} />
|
||||
</ButtonAnimationWrapper>
|
||||
)}
|
||||
{primaryBtn && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button variant="contained" sx={{ minWidth: { sm: 170 } }} {...primaryBtn} />
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
{description && typeof description === 'string' ? (
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
{description}
|
||||
</Typography>
|
||||
) : (
|
||||
description
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
<Box
|
||||
sx={{ position: 'absolute', right: -160, bottom: -160, display: { xs: 'none', md: 'block' }, transform: 'scaleX(-1)' }}
|
||||
>
|
||||
<LogoWatermark />
|
||||
</Box>
|
||||
</GraphicsCard>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 4, md: 3 }}>
|
||||
<Grid container sx={{ height: 1, position: 'relative' }}>
|
||||
<Grid size={{ xs: 6, sm: 12 }} sx={{ minHeight: { sm: '50%' } }}>
|
||||
<GraphicsCard sx={{ height: 1 }}>
|
||||
<Stack sx={{ alignItems: 'center', gap: 1, py: { xs: 2, sm: 6, md: 7.5 }, px: { xs: 2, sm: 3.5 }, textAlign: 'center' }}>
|
||||
<Typography component="div" variant="h1">
|
||||
{saleData.count}
|
||||
<Typography variant="h2" component="span" sx={{ color: 'text.secondary' }}>
|
||||
{saleData.defaultUnit}
|
||||
</Typography>
|
||||
</Typography>
|
||||
<Typography sx={{ color: 'text.secondary' }}>{saleData.caption}</Typography>
|
||||
</Stack>
|
||||
</GraphicsCard>
|
||||
</Grid>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
transform: { xs: 'translate(-50%,-50%) rotate(90deg)', sm: 'translate(-50%,-50%)' },
|
||||
'& .wave svg': { width: { xs: 70, sm: 122 } }
|
||||
}}
|
||||
>
|
||||
<Wave />
|
||||
</Box>
|
||||
<Grid size={{ xs: 6, sm: 12 }} sx={{ minHeight: { sm: '50%' } }}>
|
||||
<GraphicsCard sx={{ height: 1 }}>
|
||||
<ProfileGroup
|
||||
{...profileGroups}
|
||||
sx={{
|
||||
py: { xs: 2, sm: 4, md: 6.75 },
|
||||
px: { xs: 2, sm: 1.5 },
|
||||
height: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
textAlign: 'center',
|
||||
'& .MuiAvatarGroup-root': { mb: 0.5 },
|
||||
'& .MuiAvatar-root': { width: { xs: 40, sm: 58 }, height: { xs: 40, sm: 58 } },
|
||||
'& .wave': { display: 'none' }
|
||||
}}
|
||||
/>
|
||||
</GraphicsCard>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</motion.div>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Cta5.propTypes = {
|
||||
heading: PropTypes.string,
|
||||
caption: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
input: PropTypes.oneOfType([PropTypes.bool, PropTypes.any]),
|
||||
primaryBtn: PropTypes.any,
|
||||
secondaryBtn: PropTypes.any,
|
||||
description: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
||||
saleData: PropTypes.any,
|
||||
profileGroups: PropTypes.object
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as Cta4 } from './Cta4';
|
||||
export { default as Cta5 } from './Cta5';
|
||||
@@ -0,0 +1,211 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
// @next
|
||||
import NextLink from 'next/link';
|
||||
|
||||
// @mui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Accordion from '@mui/material/Accordion';
|
||||
import AccordionDetails from '@mui/material/AccordionDetails';
|
||||
import AccordionSummary from '@mui/material/AccordionSummary';
|
||||
import Button from '@mui/material/Button';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
import Slider from 'react-slick';
|
||||
|
||||
// @project
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import FaqDetails from '@/components/faq/FaqDetails';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import Typeset from '@/components/Typeset';
|
||||
|
||||
import useFocusWithin from '@/hooks/useFocusWithin';
|
||||
import { generateFocusVisibleStyles } from '@/utils/CommonFocusStyle';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
/*************************** FAQ - 6 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [FAQ6](https://www.saasable.io/blocks/faq/faq6)
|
||||
*
|
||||
* API:
|
||||
* - [FAQ6 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/faq/faq6#props-details)
|
||||
*/
|
||||
|
||||
export default function Faq6({ heading, caption, defaultExpanded, faqList, getInTouch, categories, activeCategory }) {
|
||||
const theme = useTheme();
|
||||
const isFocusWithin = useFocusWithin();
|
||||
const [expanded, setExpanded] = useState(defaultExpanded || false);
|
||||
const [activeTopic, setActiveTopic] = useState(activeCategory || '');
|
||||
const [filterFaqList, setFilterFaqList] = useState(activeCategory ? faqList.filter((item) => item.category === activeCategory) : faqList);
|
||||
|
||||
const cardRadius = { xs: 4, sm: 6 };
|
||||
const accordionRadius = { xs: cardRadius.xs * 4, sm: cardRadius.sm * 4 };
|
||||
const accordionPX = { xs: 2, sm: 3 };
|
||||
const iconProps = { color: 'text.primary' };
|
||||
|
||||
// Handles the expansion of accordion panels
|
||||
const handleChange = (panel) => (event, isExpanded) => setExpanded(isExpanded ? panel : false);
|
||||
|
||||
const slickStyle = { '& .slick-slide': { ' > div': { px: { xs: 0.5, md: 0.75 } } } };
|
||||
|
||||
const settings = {
|
||||
arrows: false,
|
||||
dots: false,
|
||||
infinite: false,
|
||||
speed: 500,
|
||||
swipeToSlide: true,
|
||||
initialSlide: 0,
|
||||
variableWidth: true
|
||||
};
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ gap: { xs: 3, sm: 4 } }}>
|
||||
{heading && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
>
|
||||
<Stack direction={{ sm: 'row' }} sx={{ gap: 4, justifyContent: 'space-between', alignItems: { xs: 'flex-start', sm: 'end' } }}>
|
||||
<Typeset {...{ heading, caption }} />
|
||||
{getInTouch?.link && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
{...getInTouch.link}
|
||||
{...(getInTouch.link && getInTouch.link.href && { component: NextLink })}
|
||||
sx={{ minWidth: 215, ...getInTouch.link.sx }}
|
||||
/>
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
</Stack>
|
||||
</motion.div>
|
||||
)}
|
||||
<Stack sx={{ gap: 2 }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.4
|
||||
}}
|
||||
>
|
||||
<Stack sx={slickStyle}>
|
||||
<Slider {...settings}>
|
||||
<Button
|
||||
sx={{
|
||||
minHeight: { xs: 40, sm: 48 },
|
||||
color: 'text.primary',
|
||||
borderColor: 'divider',
|
||||
bgcolor: activeTopic === '' ? 'grey.100' : 'inherit',
|
||||
'&.MuiButton-root:hover': { bgcolor: 'grey.100', borderColor: 'divider' }
|
||||
}}
|
||||
variant="outlined"
|
||||
onClick={() => {
|
||||
setActiveTopic('');
|
||||
setFilterFaqList(faqList);
|
||||
}}
|
||||
>
|
||||
All
|
||||
</Button>
|
||||
{categories.map((item, index) => (
|
||||
<Button
|
||||
key={index}
|
||||
sx={{
|
||||
minHeight: { xs: 40, sm: 48 },
|
||||
color: 'text.primary',
|
||||
borderColor: 'divider',
|
||||
bgcolor: activeTopic === item ? 'grey.100' : 'inherit',
|
||||
'&.MuiButton-root:hover': { bgcolor: 'grey.100', borderColor: 'divider' }
|
||||
}}
|
||||
variant="outlined"
|
||||
onClick={() => {
|
||||
setActiveTopic(item);
|
||||
setFilterFaqList(faqList.filter((list) => list.category === item));
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</Button>
|
||||
))}
|
||||
</Slider>
|
||||
</Stack>
|
||||
</motion.div>
|
||||
<Stack
|
||||
sx={{
|
||||
gap: 1.5,
|
||||
'& .MuiAccordion-root:first-of-type': { borderTopLeftRadius: accordionRadius, borderTopRightRadius: accordionRadius },
|
||||
'& .MuiAccordion-root:last-of-type': { borderBottomLeftRadius: accordionRadius, borderBottomRightRadius: accordionRadius }
|
||||
}}
|
||||
>
|
||||
{filterFaqList.map((item, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ y: 20, opacity: 0 }}
|
||||
whileInView={{ y: 0, opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.2, delay: index * 0.2 }}
|
||||
>
|
||||
<Accordion
|
||||
key={index}
|
||||
expanded={expanded === `panel${index}`}
|
||||
onChange={handleChange(`panel${index}`)}
|
||||
sx={{
|
||||
borderRadius: cardRadius,
|
||||
backgroundColor: 'grey.100',
|
||||
...(isFocusWithin && { '&:focus-within': generateFocusVisibleStyles(theme.vars.palette.primary.main) })
|
||||
}}
|
||||
>
|
||||
<AccordionSummary
|
||||
expandIcon={<SvgIcon name={expanded === `panel${index}` ? 'tabler-minus' : 'tabler-plus'} {...iconProps} size={20} />}
|
||||
sx={{
|
||||
p: accordionPX,
|
||||
'&.Mui-focusVisible': { bgcolor: 'transparent' },
|
||||
'&:hover, &:hover svg': { color: 'primary.dark' }
|
||||
}}
|
||||
slotProps={{ content: { sx: { my: 0 } } }}
|
||||
>
|
||||
<Typography variant="h4">{item.question}</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails sx={{ px: accordionPX, pt: 0, pb: accordionPX }} key={index}>
|
||||
<FaqDetails answer={item.answer} />
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
</motion.div>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Faq6.propTypes = {
|
||||
heading: PropTypes.any,
|
||||
caption: PropTypes.any,
|
||||
defaultExpanded: PropTypes.any,
|
||||
faqList: PropTypes.any,
|
||||
getInTouch: PropTypes.any,
|
||||
categories: PropTypes.array,
|
||||
activeCategory: PropTypes.string
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Faq6 } from './Faq6';
|
||||
@@ -0,0 +1,268 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useState } from 'react';
|
||||
|
||||
// @mui
|
||||
import Button from '@mui/material/Button';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Tab from '@mui/material/Tab';
|
||||
import TabContext from '@mui/lab/TabContext';
|
||||
import TabList from '@mui/lab/TabList';
|
||||
import TabPanel from '@mui/lab/TabPanel';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import Typeset from '@/components/Typeset';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
// @assets
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import GraphicsImage from '@/components/GraphicsImage';
|
||||
|
||||
/*************************** FEATURE - 18 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Feature18](https://www.saasable.io/blocks/feature/feature18)
|
||||
*
|
||||
* API
|
||||
* - [Feature18 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/feature/feature18#props-details)
|
||||
*/
|
||||
|
||||
export default function Feature18({ heading, caption, topics }) {
|
||||
const boxPadding = { xs: 3, md: 5 };
|
||||
const imagePadding = { xs: 3, sm: 4, md: 5 };
|
||||
|
||||
const [value, setValue] = useState('1');
|
||||
|
||||
// Handle tab change
|
||||
const handleChange = (event, newValue) => {
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ gap: { xs: 3, sm: 4 } }}>
|
||||
<Typeset
|
||||
{...{
|
||||
heading,
|
||||
caption,
|
||||
stackProps: { sx: { alignItems: 'center', textAlign: 'center', maxWidth: { sm: 470, md: 615 }, mx: 'auto' } }
|
||||
}}
|
||||
/>
|
||||
<Stack sx={{ gap: 1.5, alignItems: 'center' }}>
|
||||
<TabContext value={value}>
|
||||
<GraphicsCard sx={{ width: { xs: 1, sm: 'unset' } }}>
|
||||
<Box sx={{ p: 0.25 }}>
|
||||
<TabList
|
||||
onChange={handleChange}
|
||||
sx={{ minHeight: 'unset', p: 0.25 }}
|
||||
slotProps={{ indicator: { sx: { display: 'none' } } }}
|
||||
variant="scrollable"
|
||||
>
|
||||
{topics.map((item, index) => (
|
||||
<Tab
|
||||
label={item.title}
|
||||
disableFocusRipple
|
||||
{...(item.icon && {
|
||||
icon: (
|
||||
<SvgIcon
|
||||
{...(typeof item.icon === 'string' ? { name: item.icon } : { ...item.icon })}
|
||||
size={16}
|
||||
stroke={2}
|
||||
color="text.secondary"
|
||||
/>
|
||||
)
|
||||
})}
|
||||
value={String(index + 1)}
|
||||
key={index}
|
||||
iconPosition="start"
|
||||
tabIndex={0}
|
||||
sx={{
|
||||
minHeight: 44,
|
||||
minWidth: { xs: 112, md: 160, sm: 156 },
|
||||
borderRadius: 10,
|
||||
borderWidth: 1,
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'transparent',
|
||||
'& svg ': { mr: 1 },
|
||||
'&.Mui-selected': {
|
||||
bgcolor: 'grey.200',
|
||||
borderColor: 'grey.400',
|
||||
minWidth: { xs: 112, md: 160, sm: 156 },
|
||||
color: 'text.primary',
|
||||
'& svg': { stroke: 'text.primary' }
|
||||
},
|
||||
'&.Mui-focusVisible': { bgcolor: 'grey.300' },
|
||||
'&:hover': { bgcolor: 'grey.200' }
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</TabList>
|
||||
</Box>
|
||||
</GraphicsCard>
|
||||
{topics.map((item, index) => (
|
||||
<TabPanel value={String(index + 1)} key={index} sx={{ p: 0, width: 1 }}>
|
||||
<Grid container spacing={1.5}>
|
||||
<Grid size={{ xs: 12, sm: 5 }}>
|
||||
<GraphicsCard>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: 100, y: 100 }}
|
||||
whileInView={{ opacity: 1, x: 0, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.1 }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
pl: item.isCoverImage ? 0 : imagePadding,
|
||||
pt: item.isCoverImage ? 0 : imagePadding,
|
||||
height: { xs: 260, sm: 396, md: 434 }
|
||||
}}
|
||||
>
|
||||
<GraphicsImage
|
||||
sx={{
|
||||
height: 1,
|
||||
backgroundPositionX: 'left',
|
||||
backgroundPositionY: 'top',
|
||||
...(item.isImageBorder && { borderTop: '5px solid', borderLeft: '5px solid', borderColor: 'grey.200' }),
|
||||
...(item.isCoverImage && { backgroundSize: 'cover', border: 'none' }),
|
||||
borderTopLeftRadius: { xs: 12 },
|
||||
borderBottomRightRadius: { xs: 20, sm: 32, md: 40 }
|
||||
}}
|
||||
image={item.image}
|
||||
/>
|
||||
</Box>
|
||||
</motion.div>
|
||||
</GraphicsCard>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 7 }} sx={{ display: 'flex' }}>
|
||||
<GraphicsCard>
|
||||
<Stack
|
||||
sx={{
|
||||
justifyContent: 'space-between',
|
||||
gap: 5,
|
||||
height: item.actionBtn || item.actionBtn2 ? { sm: 'calc(100% - 98px)', md: 'calc(100% - 114px)' } : 1,
|
||||
pt: boxPadding,
|
||||
px: boxPadding
|
||||
}}
|
||||
>
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: -30 }}
|
||||
transition={{ duration: 0.2, ease: 'linear', delay: index * 0.1 }}
|
||||
>
|
||||
<Stack direction="row" sx={{ gap: 1 }}>
|
||||
{item.icon && (
|
||||
<SvgIcon
|
||||
{...(typeof item.icon === 'string' ? { name: item.icon } : { ...item.icon })}
|
||||
size={16}
|
||||
stroke={2}
|
||||
color="text.primary"
|
||||
/>
|
||||
)}
|
||||
<Typography variant="subtitle2" sx={{ color: 'text.secondary' }}>
|
||||
{item.title}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</motion.div>
|
||||
<Stack sx={{ gap: { xs: 2, md: 3 }, pb: boxPadding }}>
|
||||
<Stack sx={{ gap: 0.5 }}>
|
||||
<Typography variant="h4">{item.title2}</Typography>
|
||||
{item.description && <Typography sx={{ color: 'text.secondary' }}>{item.description}</Typography>}
|
||||
</Stack>
|
||||
{item.list && (
|
||||
<Grid container spacing={{ xs: 0.75, md: 1 }}>
|
||||
{item.list.map((list, index) => (
|
||||
<Grid key={index} size={{ xs: 12, md: 6 }}>
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, x: -30 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: -30 }}
|
||||
transition={{ duration: 0.2, ease: 'linear', delay: index * 0.1 }}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
sx={{
|
||||
gap: 0.5,
|
||||
alignItems: 'center',
|
||||
'& svg.tabler-rosette-discount-check': { width: { xs: 16, md: 24 }, height: { xs: 16, md: 24 } }
|
||||
}}
|
||||
>
|
||||
<SvgIcon name="tabler-rosette-discount-check" stroke={1} color="text.secondary" />
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
{list.primary}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
{(item.actionBtn || item.actionBtn2) && (
|
||||
<GraphicsCard sx={{ bgcolor: 'grey.200' }}>
|
||||
<Stack direction="row" sx={{ alignItems: 'flex-start', gap: 1.5, p: { xs: 2, sm: 3, md: 4 } }}>
|
||||
{item.actionBtn2 && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
startIcon={<SvgIcon name="tabler-help" size={16} stroke={3} />}
|
||||
{...item.actionBtn2}
|
||||
/>
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
{item.actionBtn && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<SvgIcon name="tabler-link" size={16} stroke={3} color="background.default" />}
|
||||
{...item.actionBtn}
|
||||
/>
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
</Stack>
|
||||
</GraphicsCard>
|
||||
)}
|
||||
</GraphicsCard>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</TabPanel>
|
||||
))}
|
||||
</TabContext>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Feature18.propTypes = { heading: PropTypes.string, caption: PropTypes.string, topics: PropTypes.array };
|
||||
@@ -0,0 +1,248 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import Button from '@mui/material/Button';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import GraphicsImage from '@/components/GraphicsImage';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import Typeset from '@/components/Typeset';
|
||||
import { withAlpha } from '@/utils/colorUtils';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
// @assets
|
||||
import Star from '@/images/graphics/Star';
|
||||
|
||||
/*************************** FEATURE - 20 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Feature20](https://www.saasable.io/blocks/feature/feature20)
|
||||
*
|
||||
* API
|
||||
* - [Feature20 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/feature/feature20#props-details)
|
||||
*/
|
||||
|
||||
export default function Feature20({ heading, caption, image, features, actionBtn, secondaryBtn }) {
|
||||
const theme = useTheme();
|
||||
const downSM = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
const downMD = useMediaQuery(theme.breakpoints.down('md'));
|
||||
|
||||
const partitionInExtraSmall = 1;
|
||||
const partitionInSmall = 2;
|
||||
const partitionInLarge = 3;
|
||||
|
||||
const columns = downSM ? partitionInExtraSmall : downMD ? partitionInSmall : partitionInLarge;
|
||||
|
||||
const calculateElementsInLastRow = (dataArray, columns) => {
|
||||
const totalItems = dataArray.length;
|
||||
const elementsInLastRow = totalItems % columns || columns;
|
||||
return elementsInLastRow;
|
||||
};
|
||||
|
||||
const calculateIndexOfFirstElementInLastRow = (dataArray, elementsInLastRow) => {
|
||||
const totalItems = dataArray.length;
|
||||
const indexOfFirstElementInLastRow = totalItems - elementsInLastRow;
|
||||
return indexOfFirstElementInLastRow;
|
||||
};
|
||||
|
||||
const elementsInLastRow = calculateElementsInLastRow(features, columns);
|
||||
const indexOfFirstElementInLastRow = calculateIndexOfFirstElementInLastRow(features, elementsInLastRow);
|
||||
|
||||
const calculateIndexOfLastElementOfEachRow = (dataArray, columns) => {
|
||||
const indices = [];
|
||||
const totalItems = dataArray.length;
|
||||
const rows = Math.ceil(totalItems / columns);
|
||||
|
||||
for (let i = 1; i <= rows; i++) {
|
||||
const lastIndexInRow = i * columns - 1;
|
||||
indices.push(lastIndexInRow < totalItems ? lastIndexInRow : totalItems - 1);
|
||||
}
|
||||
|
||||
return indices;
|
||||
};
|
||||
|
||||
const indicesOfLastElements = calculateIndexOfLastElementOfEachRow(features, columns);
|
||||
const gc = theme.vars.palette.background.default;
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ gap: { xs: 3, sm: 4, md: 5 } }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.3
|
||||
}}
|
||||
>
|
||||
<Typeset {...{ heading, stackProps: { sx: { maxWidth: { md: 500 }, ...(!image && { maxWidth: 1, textAlign: 'center' }) } } }} />
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 25 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.4
|
||||
}}
|
||||
>
|
||||
<GraphicsCard sx={{ position: 'relative', overflow: 'visible' }}>
|
||||
{image && (
|
||||
<GraphicsCard
|
||||
sx={{
|
||||
height: { md: 267 },
|
||||
width: { md: 456 },
|
||||
bgcolor: 'transparent',
|
||||
position: 'absolute',
|
||||
top: -190,
|
||||
right: 45,
|
||||
zIndex: -1,
|
||||
display: { xs: 'none', md: 'block' }
|
||||
}}
|
||||
>
|
||||
<GraphicsImage sx={{ height: 1, backgroundPositionX: 'right', backgroundPositionY: 'top' }} image={image}>
|
||||
<Box
|
||||
sx={{ width: 1, height: 1, background: `linear-gradient(180deg, ${withAlpha(gc, 0)} 0%, ${withAlpha(gc, 0.6)} 100%)` }}
|
||||
/>
|
||||
</GraphicsImage>
|
||||
</GraphicsCard>
|
||||
)}
|
||||
<Box sx={{ p: 3 }}>
|
||||
<Grid container>
|
||||
{features.map((item, index) => (
|
||||
<Grid
|
||||
key={index}
|
||||
size={{ xs: 12 / partitionInExtraSmall, sm: 12 / partitionInSmall, md: 12 / partitionInLarge }}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
...(index < indexOfFirstElementInLastRow && { borderBottom: `1px solid ${theme.vars.palette.grey[300]}` }),
|
||||
...(!indicesOfLastElements.includes(index) && { borderRight: `1px solid ${theme.vars.palette.grey[300]}` })
|
||||
}}
|
||||
>
|
||||
<Stack sx={{ gap: { xs: 3, sm: 4 }, height: 1, py: { xs: 1.5, sm: 3, md: 4 }, px: { xs: 0, sm: 3, md: 4 } }}>
|
||||
<Avatar sx={{ width: 60, height: 60, bgcolor: 'grey.300' }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.6 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 2, delay: index * 0.1 }}
|
||||
>
|
||||
<SvgIcon {...(typeof item.icon === 'string' ? { name: item.icon } : { ...item.icon })} />
|
||||
</motion.div>
|
||||
</Avatar>
|
||||
<Stack sx={{ gap: { xs: 0.5, md: 1 } }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 25 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: index * 0.2 }}
|
||||
>
|
||||
{item.title && <Typography variant="h4">{item.title}</Typography>}
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 25 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: index * 0.3 }}
|
||||
>
|
||||
{item.content && <Typography sx={{ color: 'text.secondary' }}>{item.content}</Typography>}
|
||||
</motion.div>
|
||||
</Stack>
|
||||
</Stack>
|
||||
{index < indexOfFirstElementInLastRow && !indicesOfLastElements.includes(index) && (
|
||||
<Stack sx={{ position: 'absolute', bottom: -9, right: -9 }}>
|
||||
<Star />
|
||||
</Stack>
|
||||
)}
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
</GraphicsCard>
|
||||
</motion.div>
|
||||
<Stack sx={{ alignItems: 'center', gap: 3 }}>
|
||||
<Typography variant="h6" sx={{ color: 'text.secondary', maxWidth: { xs: '75%', sm: '45%' }, textAlign: 'center' }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 15 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.4
|
||||
}}
|
||||
>
|
||||
{caption}
|
||||
</motion.div>
|
||||
</Typography>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.5
|
||||
}}
|
||||
>
|
||||
<Stack direction="row" sx={{ alignItems: 'center', justifyContent: 'center', gap: 1.5 }}>
|
||||
{secondaryBtn && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button variant="outlined" {...secondaryBtn} />
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
{actionBtn && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={<SvgIcon name="tabler-sparkles" size={16} stroke={3} color="background.default" />}
|
||||
{...actionBtn}
|
||||
/>
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
</Stack>
|
||||
</motion.div>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Feature20.propTypes = {
|
||||
heading: PropTypes.string,
|
||||
caption: PropTypes.string,
|
||||
image: PropTypes.any,
|
||||
features: PropTypes.array,
|
||||
actionBtn: PropTypes.any,
|
||||
secondaryBtn: PropTypes.any
|
||||
};
|
||||
@@ -0,0 +1,153 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import Button from '@mui/material/Button';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import { GraphicsCard, IconCard } from '@/components/cards';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import GraphicsImage from '@/components/GraphicsImage';
|
||||
import Typeset from '@/components/Typeset';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
/*************************** FEATURE - 21 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Feature21](https://www.saasable.io/blocks/feature/feature21)
|
||||
*
|
||||
* API
|
||||
* - [Feature21 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/feature/feature21#props-details)
|
||||
*/
|
||||
|
||||
export default function Feature21({ heading, caption, image, features, primaryBtn, secondaryBtn }) {
|
||||
const imagePadding = { xs: 3, sm: 4, md: 5 };
|
||||
const iconProps = { color: 'text.primary', stroke: 1 };
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ gap: { xs: 3, sm: 4 } }}>
|
||||
{(heading || caption) && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 5 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.3, delay: 0.3 }}
|
||||
>
|
||||
<Typeset {...{ heading, caption, stackProps: { sx: { textAlign: 'center' } } }} />
|
||||
</motion.div>
|
||||
)}
|
||||
<Stack sx={{ gap: 1.5 }}>
|
||||
{image && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.3,
|
||||
delay: 0.3
|
||||
}}
|
||||
>
|
||||
<GraphicsCard>
|
||||
<Box sx={{ pl: imagePadding, pt: imagePadding, height: { xs: 204, sm: 300, md: 360 } }}>
|
||||
<GraphicsImage
|
||||
image={image}
|
||||
sx={{
|
||||
height: 1,
|
||||
backgroundPositionX: 'left',
|
||||
backgroundPositionY: 'top',
|
||||
borderTopLeftRadius: { xs: 12 },
|
||||
borderBottomRightRadius: { xs: 20, sm: 32, md: 40 },
|
||||
border: '5px solid',
|
||||
borderColor: 'grey.200',
|
||||
borderBottom: 'none',
|
||||
borderRight: 'none'
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</GraphicsCard>
|
||||
</motion.div>
|
||||
)}
|
||||
<Grid container spacing={1.5}>
|
||||
{features.map((item, index) => (
|
||||
<Grid key={index} size={{ xs: 12, sm: 6, md: 3 }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.3, delay: index * 0.2 }}
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
<IconCard
|
||||
icon={{ ...(typeof item.icon === 'string' ? { name: item.icon, ...iconProps } : { ...iconProps, ...item.icon }) }}
|
||||
title={item.title}
|
||||
stackProps={{ sx: { gap: 2, height: 1 } }}
|
||||
cardPadding={{ xs: 2, sm: 3 }}
|
||||
/>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Stack>
|
||||
{(primaryBtn || secondaryBtn) && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.3, delay: 0.4 }}
|
||||
>
|
||||
<Stack direction="row" sx={{ alignItems: 'center', justifyContent: 'center', gap: 1.5 }}>
|
||||
{secondaryBtn && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button variant="outlined" startIcon={<SvgIcon name="tabler-eye" size={16} stroke={3} />} {...secondaryBtn} />
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
{primaryBtn && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<SvgIcon name="tabler-download" size={16} stroke={3} color="background.default" />}
|
||||
{...primaryBtn}
|
||||
/>
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
</Stack>
|
||||
</motion.div>
|
||||
)}
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Feature21.propTypes = {
|
||||
heading: PropTypes.string,
|
||||
caption: PropTypes.string,
|
||||
image: PropTypes.any,
|
||||
features: PropTypes.array,
|
||||
primaryBtn: PropTypes.any,
|
||||
secondaryBtn: PropTypes.any
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as Feature18 } from './Feature18';
|
||||
export { default as Feature20 } from './Feature20';
|
||||
export { default as Feature21 } from './Feature21';
|
||||
@@ -0,0 +1,205 @@
|
||||
'use client';
|
||||
|
||||
// @next
|
||||
import NextLink from 'next/link';
|
||||
|
||||
// @mui
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Link from '@mui/material/Link';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'framer-motion';
|
||||
|
||||
// @project
|
||||
import branding from '@/branding.json';
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import { Copyright, FollowUS, Sitemap } from '@/components/footer';
|
||||
import LogoSection from '@/components/logo';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
|
||||
import { CopyrightType } from '@/enum';
|
||||
import { DOCS_URL, FREEBIES_URL } from '@/path';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
/*************************** FOOTER - 7 DATA ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Footer7](https://www.saasable.io/blocks/footer/footer7)
|
||||
*/
|
||||
|
||||
const linkProps = { target: '_blank', rel: 'noopener noreferrer' };
|
||||
const data = [
|
||||
{
|
||||
id: 'resources',
|
||||
grid: { size: { xs: 12, sm: 'auto' } },
|
||||
title: 'Resources',
|
||||
menu: [
|
||||
{
|
||||
label: 'Freebies',
|
||||
link: { href: FREEBIES_URL, ...linkProps }
|
||||
},
|
||||
{
|
||||
label: 'Documentation',
|
||||
link: { href: DOCS_URL, ...linkProps }
|
||||
},
|
||||
{
|
||||
label: 'Blog',
|
||||
link: { href: 'https://blog.saasable.io/', ...linkProps }
|
||||
},
|
||||
{
|
||||
label: 'Refund Policy',
|
||||
link: { href: 'https://mui.com/store/customer-refund-policy/', ...linkProps }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'support',
|
||||
grid: { size: { xs: 6, sm: 'auto' } },
|
||||
title: 'Support',
|
||||
menu: [
|
||||
{
|
||||
label: 'Pricing',
|
||||
link: { href: 'https://mui.com/store/items/saasable-multipurpose-ui-kit-and-dashboard/', ...linkProps }
|
||||
},
|
||||
{
|
||||
label: 'FAQs',
|
||||
link: { href: 'https://phoenixcoded.gitbook.io/saasable/faq', ...linkProps }
|
||||
},
|
||||
{
|
||||
label: 'Support',
|
||||
link: { href: branding.company.socialLink.support, ...linkProps }
|
||||
},
|
||||
{
|
||||
label: 'License Terms',
|
||||
link: { href: 'https://mui.com/store/license/', ...linkProps }
|
||||
},
|
||||
{
|
||||
label: 'Discord',
|
||||
link: { href: branding.company.socialLink.discord, ...linkProps }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'company',
|
||||
grid: { size: { xs: 6, sm: 'auto' } },
|
||||
title: 'Company',
|
||||
menu: [
|
||||
{
|
||||
label: 'Why Phoenixcoded?',
|
||||
link: {
|
||||
href: 'https://blog.saasable.io/a-decade-of-expertise-the-phoenixcoded-story-and-why-you-should-trust-us',
|
||||
...linkProps
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'About',
|
||||
link: { href: 'https://saasable.io/about', ...linkProps }
|
||||
},
|
||||
{
|
||||
label: 'Contact Us',
|
||||
link: { href: '/contact', ...linkProps }
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const iconProps = { color: 'text.secondary' };
|
||||
|
||||
const usefullLinks = [
|
||||
{
|
||||
icon: <SvgIcon name="tabler-brand-figma" {...iconProps} />,
|
||||
title: 'Figma Version 1.1.0',
|
||||
href: 'https://www.figma.com/community/file/1425095061180549847'
|
||||
},
|
||||
{
|
||||
icon: <SvgIcon name="tabler-route" {...iconProps} />,
|
||||
title: 'React Material UI v7',
|
||||
href: 'https://mui.com/material-ui/getting-started'
|
||||
},
|
||||
{
|
||||
icon: <SvgIcon name="tabler-sparkles" {...iconProps} />,
|
||||
title: 'Documentation',
|
||||
href: DOCS_URL
|
||||
}
|
||||
];
|
||||
|
||||
/*************************** FOOTER - 7 ***************************/
|
||||
|
||||
export default function Footer7() {
|
||||
const logoFollowContent = (
|
||||
<Stack sx={{ alignItems: 'flex-start', gap: { xs: 1.5, sm: 3 } }}>
|
||||
<LogoSection />
|
||||
<Typography variant="h6" sx={{ maxWidth: { sm: 280 }, mb: { xs: -1, sm: -2.5 } }}>
|
||||
{process.env.NEXT_PUBLIC_VERSION}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ maxWidth: { sm: 280 } }}>
|
||||
Explore the different versions of our {branding.brandName} template.
|
||||
</Typography>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.4
|
||||
}}
|
||||
>
|
||||
<Stack id="footer-7" role="contentinfo" rel="noopener noreferrer" aria-label="Footer 7" sx={{ gap: { xs: 3, sm: 4, md: 5 } }}>
|
||||
<Grid container spacing={{ xs: 4, md: 3 }}>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<Stack direction={{ sm: 'row', md: 'column' }} sx={{ gap: 3, justifyContent: 'space-between', height: 1 }}>
|
||||
{logoFollowContent}
|
||||
<Stack sx={{ gap: { xs: 2, sm: 2.5, md: 3 } }}>
|
||||
{usefullLinks.map((item, index) => (
|
||||
<Stack direction="row" sx={{ gap: 1, alignItems: 'center' }} key={index}>
|
||||
{item.icon}
|
||||
<Link
|
||||
component={NextLink}
|
||||
variant="body2"
|
||||
color="text.secondary"
|
||||
href={item.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Usefull Links"
|
||||
>
|
||||
{item.title}
|
||||
</Link>
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<Sitemap list={data} isMenuDesign />
|
||||
</Grid>
|
||||
</Grid>
|
||||
<GraphicsCard sx={{ borderRadius: { xs: 6, sm: 8 } }}>
|
||||
<Stack
|
||||
direction={{ sm: 'row' }}
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
justifyContent: { xs: 'center', sm: 'space-between' },
|
||||
gap: 1.5,
|
||||
py: { xs: 2, sm: 1.5 },
|
||||
px: { xs: 2, sm: 3 }
|
||||
}}
|
||||
>
|
||||
<Copyright type={CopyrightType.TYPE3} />
|
||||
<FollowUS heading={false} color="grey.100" />
|
||||
</Stack>
|
||||
</GraphicsCard>
|
||||
</Stack>
|
||||
</motion.div>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Footer7 } from './Footer7';
|
||||
@@ -0,0 +1,250 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
// @mui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Button from '@mui/material/Button';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion, useScroll, useTransform } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import GraphicsImage from '@/components/GraphicsImage';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
import { getBackgroundDots } from '@/utils/getBackgroundDots';
|
||||
import { withAlpha } from '@/utils/colorUtils';
|
||||
|
||||
// @assets
|
||||
import Wave from '@/images/graphics/Wave';
|
||||
|
||||
// threshold - adjust threshold as needed
|
||||
const options = { root: null, rootMargin: '0px', threshold: 0.6 };
|
||||
|
||||
/*************************** HERO - 17 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Hero17](https://www.saasable.io/blocks/hero/hero17)
|
||||
*
|
||||
* API:
|
||||
* - [Hero17 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/hero/hero17#props-details)
|
||||
*/
|
||||
|
||||
export default function Hero17({ chip, headLine, captionLine, primaryBtn, videoSrc, videoThumbnail, listData }) {
|
||||
const theme = useTheme();
|
||||
const boxRadius = { xs: 24, sm: 32, md: 40 };
|
||||
|
||||
const containerRef = useRef(null);
|
||||
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: containerRef,
|
||||
offset: ['start start', 'end start']
|
||||
});
|
||||
|
||||
const scale = useTransform(scrollYProgress, [0, 0.1, 0.2, 0.4, 0.6], [0.9, 0.92, 0.94, 0.96, 1]);
|
||||
|
||||
const videoRef = useRef(null);
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
|
||||
// Handle video play/pause based on intersection with the viewport
|
||||
useEffect(() => {
|
||||
const handleIntersection = (entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
if (videoRef.current && !isPlaying) {
|
||||
videoRef.current
|
||||
.play()
|
||||
.then(() => {
|
||||
setIsPlaying(true);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Autoplay was prevented:', error);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (videoRef.current && isPlaying) {
|
||||
videoRef.current.pause();
|
||||
setIsPlaying(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver(handleIntersection, options);
|
||||
const videoElement = videoRef.current;
|
||||
|
||||
if (videoElement) {
|
||||
observer.observe(videoElement);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (videoElement) {
|
||||
observer.unobserve(videoElement);
|
||||
}
|
||||
};
|
||||
}, [isPlaying]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
height: { xs: 592, sm: 738, md: 878 },
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: 1,
|
||||
zIndex: -1,
|
||||
borderBottomLeftRadius: boxRadius,
|
||||
borderBottomRightRadius: boxRadius,
|
||||
...getBackgroundDots(theme.vars.palette.grey[300], 2, 35),
|
||||
bgcolor: 'grey.100'
|
||||
}}
|
||||
/>
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Box ref={containerRef}>
|
||||
<Box sx={{ pb: { xs: 3, sm: 4, md: 5 } }}>
|
||||
<Stack sx={{ alignItems: 'center', gap: 1.5 }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.6 }}
|
||||
whileInView={{ opacity: 1, scale: [0.6, 1.15, 0.95, 1] }}
|
||||
animate={{
|
||||
boxShadow: [
|
||||
`0 0 0px ${withAlpha(theme.vars.palette.primary.dark, 0)}`,
|
||||
`0 0 20px ${withAlpha(theme.vars.palette.primary.main, 0.8)}`,
|
||||
`0 0 0px ${withAlpha(theme.vars.palette.primary.dark, 0)}`
|
||||
],
|
||||
borderRadius: '74px'
|
||||
}}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.8, ease: 'linear' }}
|
||||
>
|
||||
<Chip
|
||||
variant="outlined"
|
||||
label={chip.label}
|
||||
slotProps={{
|
||||
label: {
|
||||
sx: { py: 0.5, px: 1.5, ...(typeof chip.label === 'string' && { typography: 'caption', color: 'text.secondary' }) }
|
||||
}
|
||||
}}
|
||||
sx={{ bgcolor: 'grey.100' }}
|
||||
/>
|
||||
</motion.div>
|
||||
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.6 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, delay: 0.2, ease: 'linear' }}
|
||||
>
|
||||
<Typography variant="h1" align="center" sx={{ maxWidth: 800 }}>
|
||||
{headLine}
|
||||
</Typography>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 1, delay: 0.2, ease: [0.215, 0.61, 0.355, 1] }}
|
||||
>
|
||||
<Box sx={{ pt: 0.5, pb: 0.75 }}>
|
||||
<Wave />
|
||||
</Box>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 1, delay: 0.3, ease: [0.215, 0.61, 0.355, 1] }}
|
||||
>
|
||||
<Typography variant="h6" align="center" sx={{ color: 'text.secondary', maxWidth: 650 }}>
|
||||
{captionLine}
|
||||
</Typography>
|
||||
</motion.div>
|
||||
</Stack>
|
||||
<Stack sx={{ alignItems: 'center', gap: 2, mt: { xs: 3, sm: 4, md: 5 } }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button
|
||||
color="primary"
|
||||
variant="contained"
|
||||
startIcon={<SvgIcon name="tabler-sparkles" size={16} stroke={3} color="background.default" />}
|
||||
{...primaryBtn}
|
||||
/>
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
<Stack direction="row" sx={{ gap: 1, flexWrap: 'wrap', justifyContent: 'center' }}>
|
||||
{listData.map((item, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, scale: 0.6 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.9, delay: index * 0.08, ease: 'linear' }}
|
||||
>
|
||||
<Chip
|
||||
label={item.title}
|
||||
variant="outlined"
|
||||
icon={<GraphicsImage image={item.image} sx={{ width: 16, height: 16 }} />}
|
||||
slotProps={{ label: { sx: { py: 0.75, px: 1, typography: 'caption2' } } }}
|
||||
sx={{ height: 32, px: 1, bgcolor: 'grey.100' }}
|
||||
/>
|
||||
</motion.div>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.6 }}
|
||||
whileInView={{ opacity: 1, scale: 0.9 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.9, delay: 0.3 }}
|
||||
style={{ scale }}
|
||||
>
|
||||
<GraphicsCard sx={{ border: '5px solid', borderColor: 'grey.300' }}>
|
||||
<video
|
||||
playsInline
|
||||
ref={videoRef}
|
||||
width="100%"
|
||||
height="100%"
|
||||
style={{ display: 'flex', objectFit: 'cover' }}
|
||||
preload="metadata"
|
||||
autoPlay={false}
|
||||
loop={true}
|
||||
muted={true}
|
||||
poster={videoThumbnail}
|
||||
>
|
||||
<source src={videoSrc} type="video/mp4" />
|
||||
</video>
|
||||
</GraphicsCard>
|
||||
</motion.div>
|
||||
</Box>
|
||||
</ContainerWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Hero17.propTypes = {
|
||||
chip: PropTypes.object,
|
||||
headLine: PropTypes.string,
|
||||
captionLine: PropTypes.string,
|
||||
primaryBtn: PropTypes.any,
|
||||
videoSrc: PropTypes.string,
|
||||
videoThumbnail: PropTypes.string,
|
||||
listData: PropTypes.array
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Hero17 } from './Hero17';
|
||||
@@ -0,0 +1,119 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import Button from '@mui/material/Button';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
/*************************** INTEGRATION - TAG ***************************/
|
||||
|
||||
function IntegrationTag({ label, icon }) {
|
||||
const iconSize = { xs: 32, md: 40 };
|
||||
|
||||
return (
|
||||
<Chip
|
||||
label={label}
|
||||
{...(icon && { icon: <SvgIcon {...(typeof icon === 'string' ? { name: icon } : { ...icon })} stroke={1} color="inherit" /> })}
|
||||
slotProps={{ label: { sx: { p: 0, ...(icon && { pl: 1.25 }), typography: 'h5' } } }}
|
||||
sx={{
|
||||
px: { xs: 2.25, md: 3 },
|
||||
py: icon ? { xs: 1.25, md: 1.75 } : { xs: 1.875, md: 2.5 },
|
||||
m: 0.5,
|
||||
bgcolor: 'background.default',
|
||||
'& svg': { width: iconSize, height: iconSize }
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/*************************** INTEGRATION - 2 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Integration2](https://www.saasable.io/blocks/integration/integration2)
|
||||
*
|
||||
* API:
|
||||
* - [Integration2 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/integration/integration2#props-details)
|
||||
*/
|
||||
|
||||
export default function Integration2({ headLine, captionLine, primaryBtn, tagList }) {
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.4
|
||||
}}
|
||||
>
|
||||
<GraphicsCard>
|
||||
<Stack sx={{ alignItems: 'center', p: { xs: 3, sm: 4, md: 5 } }}>
|
||||
<Stack sx={{ alignItems: 'center', gap: { xs: 1, sm: 1.5 } }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 25 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.4, ease: [0.215, 0.61, 0.355, 1] }}
|
||||
>
|
||||
{headLine && (
|
||||
<Typography align="center" variant="h2">
|
||||
{headLine}
|
||||
</Typography>
|
||||
)}
|
||||
{captionLine && (
|
||||
<Typography align="center" variant="h6" sx={{ color: 'text.secondary' }}>
|
||||
{captionLine}
|
||||
</Typography>
|
||||
)}
|
||||
</motion.div>
|
||||
</Stack>
|
||||
<Stack direction="row" sx={{ py: { xs: 4, sm: 5, md: 6 }, justifyContent: 'center', alignItems: 'center', flexWrap: 'wrap' }}>
|
||||
{tagList.map((integration, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, scale: 0.7 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.6, ease: 'easeOut', delay: Math.random() * 0.6 }}
|
||||
>
|
||||
<IntegrationTag {...integration} />
|
||||
</motion.div>
|
||||
))}
|
||||
</Stack>
|
||||
{primaryBtn && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button variant="contained" size="large" {...primaryBtn} />
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
)}
|
||||
</Stack>
|
||||
</GraphicsCard>
|
||||
</motion.div>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
IntegrationTag.propTypes = { label: PropTypes.any, icon: PropTypes.any };
|
||||
|
||||
Integration2.propTypes = { headLine: PropTypes.string, captionLine: PropTypes.string, primaryBtn: PropTypes.any, tagList: PropTypes.array };
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Integration2 } from './Integration2';
|
||||
@@ -0,0 +1,55 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import Button from '@mui/material/Button';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
// @project
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
// @assets
|
||||
import Error404 from '@/images/maintenance/Error404';
|
||||
|
||||
/*************************** ERROR 404 - PAGES ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Error404](https://www.saasable.io/blocks/error404)
|
||||
*
|
||||
* API
|
||||
* - [Error404 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/maintenance/error404#props-details)
|
||||
*/
|
||||
|
||||
export default function Error404Page({ primaryBtn, heading }) {
|
||||
return (
|
||||
<ContainerWrapper>
|
||||
<Stack
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: 1,
|
||||
height: '100vh',
|
||||
py: SECTION_COMMON_PY,
|
||||
minHeight: { xs: 450, sm: 600, md: 800 }
|
||||
}}
|
||||
>
|
||||
<GraphicsCard sx={{ width: 1, height: 1, py: { xs: 3, sm: 4, md: 6 } }}>
|
||||
<Stack sx={{ justifyContent: 'center', height: 1, gap: { xs: 4, sm: 1 } }}>
|
||||
<Error404 />
|
||||
<Stack sx={{ gap: 2.25, alignItems: 'center', mt: { sm: -5, lg: -6.25 } }}>
|
||||
<Typography sx={{ width: { xs: 210, sm: 300 }, textAlign: 'center' }}>{heading}</Typography>
|
||||
{primaryBtn && <Button variant="contained" size="medium" {...primaryBtn} />}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</GraphicsCard>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Error404Page.propTypes = { primaryBtn: PropTypes.any, heading: PropTypes.string };
|
||||
@@ -0,0 +1,48 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import Button from '@mui/material/Button';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @project
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
// @assets
|
||||
import Error500 from '@/images/maintenance/Error500';
|
||||
import Error500Server from '@/images/maintenance/Error500Server';
|
||||
|
||||
/*************************** ERROR 500 - PAGES ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Error500](https://www.saasable.io/blocks/error500)
|
||||
*/
|
||||
|
||||
export default function Error500Page({ primaryBtn, heading }) {
|
||||
return (
|
||||
<ContainerWrapper>
|
||||
<Stack sx={{ width: 1, height: '100vh', py: SECTION_COMMON_PY, minHeight: { xs: 450, sm: 600, md: 800 } }}>
|
||||
<GraphicsCard sx={{ width: 1, height: 1, position: 'relative' }}>
|
||||
<Stack sx={{ alignItems: 'center', justifyContent: 'center', gap: 2.25, height: '70%' }}>
|
||||
<Box sx={{ width: 1, maxWidth: { xs: 340, sm: 486, md: 728 }, p: 2 }}>
|
||||
<Error500 />
|
||||
</Box>
|
||||
<Typography sx={{ textAlign: 'center', width: { xs: 248, sm: 340, md: 448 } }}>{heading}</Typography>
|
||||
{primaryBtn && <Button variant="contained" size="medium" {...primaryBtn} sx={{ zIndex: 1 }} />}
|
||||
</Stack>
|
||||
<Box sx={{ width: { xs: '95%', md: '90%' }, position: 'absolute', left: -2, bottom: -6 }}>
|
||||
<Error500Server />
|
||||
</Box>
|
||||
</GraphicsCard>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Error500Page.propTypes = { primaryBtn: PropTypes.any, heading: PropTypes.string };
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as Error404Page } from './Error404';
|
||||
export { default as Error500Page } from './Error500';
|
||||
@@ -0,0 +1,51 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @next
|
||||
import NextLink from 'next/link';
|
||||
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Link from '@mui/material/Link';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import PreviewCard from '@/components/cards/PreviewCard';
|
||||
|
||||
/*************************** MEGA MENU - 4 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [MegaMenu4](https://www.saasable.io/blocks/megamenu/megamenu4)
|
||||
*
|
||||
* API
|
||||
* - [MegaMenu4 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/megamenu/megamenu4#props-details)
|
||||
*/
|
||||
|
||||
export default function MegaMenu4({ menuItems, footerData, popperWidth = 936, footerSX }) {
|
||||
return (
|
||||
<Box sx={{ maxWidth: { xs: 1, md: popperWidth } }}>
|
||||
<Grid container spacing={1} sx={{ p: 1, bgcolor: 'background.paper' }}>
|
||||
{menuItems.map((item, index) => (
|
||||
<Grid key={index} size={{ xs: 6, sm: 4, md: 3 }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.5, delay: index * 0.2 }}
|
||||
>
|
||||
<Link {...(item.link && { component: NextLink, ...item.link, sx: { ...item.link?.sx } })} aria-label={item.title}>
|
||||
<PreviewCard {...item} />
|
||||
</Link>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
{footerData && <Box sx={{ p: 2.5, bgcolor: 'grey.100', ...footerSX }}>{footerData}</Box>}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
MegaMenu4.propTypes = { menuItems: PropTypes.array, footerData: PropTypes.node, popperWidth: PropTypes.number, footerSX: PropTypes.any };
|
||||
@@ -0,0 +1,123 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import List from '@mui/material/List';
|
||||
import ListSubheader from '@mui/material/ListSubheader';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import { withAlpha } from '@/utils/colorUtils';
|
||||
|
||||
/*************************** MEGA MENU - 5 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [MegaMenu5](https://www.saasable.io/blocks/megamenu/megamenu5)
|
||||
*
|
||||
* API
|
||||
* - [MegaMenu5 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/megamenu/megamenu5#props-details)
|
||||
*/
|
||||
|
||||
export default function MegaMenu5({ menuItems, bannerData, popperWidth = 750 }) {
|
||||
const theme = useTheme();
|
||||
let gridItem = menuItems.length === 1 ? 12 : 6;
|
||||
|
||||
return (
|
||||
<Grid container>
|
||||
{bannerData && (
|
||||
<Grid sx={{ py: 1, pl: { xs: 2, md: 1 }, pr: { xs: 2, sm: 1 } }} size={{ xs: 12, sm: 4.5 }}>
|
||||
<Box sx={{ p: 2.5, bgcolor: 'grey.100', height: 1, borderRadius: 2 }}>{bannerData}</Box>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid size={{ xs: 12, sm: bannerData ? 7.5 : 12 }} sx={{ px: 1 }}>
|
||||
<Grid container spacing={1}>
|
||||
{menuItems.map((items, index) => (
|
||||
<Grid key={index} size={{ xs: 12, sm: menuItems.length > 2 ? 4 : gridItem }}>
|
||||
<List
|
||||
component="nav"
|
||||
sx={{ p: 1, width: '100%', maxWidth: { xs: 1, md: popperWidth }, display: 'flex', flexDirection: 'column' }}
|
||||
{...(items.title && {
|
||||
subheader: (
|
||||
<ListSubheader component="div" sx={{ typography: 'subtitle1', p: 1, color: 'text.primary', bgcolor: 'transparent' }}>
|
||||
{items.title}
|
||||
</ListSubheader>
|
||||
)
|
||||
})}
|
||||
>
|
||||
{items?.itemsList &&
|
||||
items?.itemsList.map((item, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.5, delay: index * 0.3 }}
|
||||
>
|
||||
{/* @ts-ignore */}
|
||||
<ListItemButton
|
||||
key={index}
|
||||
{...(item && item?.link && item?.link !== undefined && { ...item.link })}
|
||||
sx={{
|
||||
py: 0.5,
|
||||
px: 1.25,
|
||||
my: 0.25,
|
||||
borderRadius: 2,
|
||||
'&:hover': { bgcolor: 'grey.50' },
|
||||
'&:focus-visible': { bgcolor: 'grey.200' }
|
||||
}}
|
||||
TouchRippleProps={{
|
||||
style: {
|
||||
color: withAlpha(theme.vars.palette.primary.main, 0.3)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ListItemText
|
||||
primary={item.title}
|
||||
secondary={item.content}
|
||||
slotProps={{
|
||||
primary: {
|
||||
variant: 'body1',
|
||||
sx: { whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', mr: 0.5, color: 'text.primary' }
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{item && item?.link && item?.link !== undefined && item?.link?.target === '_blank' ? (
|
||||
<SvgIcon name="tabler-arrow-up-right" size={16} stroke={2} color={theme.vars.palette.grey[800]} />
|
||||
) : (
|
||||
<Chip
|
||||
label={
|
||||
<Typography variant="caption" sx={{ color: 'primary.main' }}>
|
||||
{item.status}
|
||||
</Typography>
|
||||
}
|
||||
size="small"
|
||||
sx={{
|
||||
bgcolor: 'primary.lighter',
|
||||
'& .MuiChip-label': { px: 1, py: 0.25, minWidth: 20 }
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ListItemButton>
|
||||
</motion.div>
|
||||
))}
|
||||
</List>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
MegaMenu5.propTypes = { menuItems: PropTypes.array, bannerData: PropTypes.node, popperWidth: PropTypes.number };
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as MegaMenu4 } from './MegaMenu4';
|
||||
export { default as MegaMenu5 } from './MegaMenu5';
|
||||
@@ -0,0 +1,99 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
|
||||
// @mui
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
// @third-party
|
||||
import { animate, motion, useMotionValue, useTransform } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import Typeset from '@/components/Typeset';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
function AnimatedCounter({ startCount, endCount }) {
|
||||
const countValue = useMotionValue(startCount);
|
||||
const rounded = useTransform(countValue, (value) => Math.round(value));
|
||||
|
||||
useEffect(() => {
|
||||
const controls = animate(countValue, endCount, { duration: 5, ease: 'linear' });
|
||||
|
||||
return () => controls.stop();
|
||||
}, [countValue, endCount]);
|
||||
|
||||
return <motion.pre style={{ margin: 0 }}>{rounded}</motion.pre>;
|
||||
}
|
||||
|
||||
/*************************** METRICS - 5 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Metrics5](https://www.saasable.io/blocks/metrics/metrics5)
|
||||
*
|
||||
* API:
|
||||
* - [Metrics5 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/metrics/metrics5#props-details)
|
||||
*/
|
||||
|
||||
export default function Metrics5({ heading, caption, blockDetail }) {
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ gap: { xs: 3, sm: 4 } }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.3 }}
|
||||
>
|
||||
<Typeset
|
||||
{...{
|
||||
heading,
|
||||
caption,
|
||||
stackProps: { sx: { alignItems: 'center', textAlign: 'center' } },
|
||||
captionProps: { sx: { width: { xs: 1, sm: '80%', md: '65%' } } }
|
||||
}}
|
||||
/>
|
||||
</motion.div>
|
||||
<Grid container spacing={1.5}>
|
||||
{blockDetail.map((item, index) => (
|
||||
<Grid key={index} size={{ xs: 6, md: 3 }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.9, delay: index * 0.4, ease: [0.215, 0.61, 0.355, 1] }}
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
<GraphicsCard sx={{ p: { xs: 2, sm: 2.25, md: 3 }, height: 1 }}>
|
||||
<Stack sx={{ gap: 0.5, alignItems: 'center' }}>
|
||||
<Stack direction="row" sx={{ alignItems: 'flex-end' }}>
|
||||
<Typography component="div" variant="h1">
|
||||
<AnimatedCounter startCount={0} endCount={item.counter} />
|
||||
</Typography>
|
||||
<Typography component="div" variant="h3" sx={{ color: 'text.secondary', mb: { xs: 0.25, md: 0.625 } }}>
|
||||
{item.defaultUnit}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Typography align="center" sx={{ color: 'text.secondary' }}>
|
||||
{item.caption}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</GraphicsCard>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
AnimatedCounter.propTypes = { startCount: PropTypes.number, endCount: PropTypes.number };
|
||||
|
||||
Metrics5.propTypes = { heading: PropTypes.any, caption: PropTypes.any, blockDetail: PropTypes.any };
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Metrics5 } from './Metrics5';
|
||||
@@ -0,0 +1,39 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { cloneElement } from 'react';
|
||||
|
||||
// @mui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import { useScrollTrigger } from '@mui/material';
|
||||
|
||||
// @project
|
||||
import { withAlpha } from '@/utils/colorUtils';
|
||||
|
||||
/*************************** NAVBAR - ELEVATION SCROLL ***************************/
|
||||
|
||||
export default function ElevationScroll({ children, window, isFixed, triggerSX }) {
|
||||
const theme = useTheme();
|
||||
|
||||
const trigger = useScrollTrigger({
|
||||
disableHysteresis: true,
|
||||
threshold: 0,
|
||||
target: window ? window() : undefined
|
||||
});
|
||||
|
||||
if (!isFixed) {
|
||||
return children;
|
||||
}
|
||||
|
||||
const triggerStyles = {
|
||||
boxShadow: `${withAlpha(theme.vars.palette.text.primary, 0.08)} 0px 12px 16px -4px, ${withAlpha(theme.vars.palette.text.primary, 0.03)} 0px 4px 6px -2px;`,
|
||||
bgcolor: 'background.paper',
|
||||
...triggerSX
|
||||
};
|
||||
|
||||
return children
|
||||
? cloneElement(children, {
|
||||
sx: { boxShadow: 'none', bgcolor: 'transparent', backgroundImage: 'none', ...(trigger && { ...triggerStyles }) }
|
||||
})
|
||||
: null;
|
||||
}
|
||||
|
||||
ElevationScroll.propTypes = { children: PropTypes.node, window: PropTypes.func, isFixed: PropTypes.bool, triggerSX: PropTypes.any };
|
||||
@@ -0,0 +1,60 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import { styled } from '@mui/material/styles';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
|
||||
// @project
|
||||
import ElevationScroll from './ElevationScroll';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
|
||||
export const navbar10Height = { xs: 64, sm: 72, md: 84 };
|
||||
|
||||
// override media queries injected by theme.mixins.toolbar
|
||||
const StyledToolbar = styled(Toolbar)(({ theme }) => ({
|
||||
'@media all': {
|
||||
minHeight: navbar10Height.md,
|
||||
paddingLeft: 0,
|
||||
paddingRight: 0
|
||||
},
|
||||
paddingTop: theme.spacing(2),
|
||||
paddingBottom: theme.spacing(2),
|
||||
[theme.breakpoints.down('md')]: {
|
||||
'@media all': { minHeight: navbar10Height.sm }
|
||||
},
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
'@media all': { minHeight: navbar10Height.xs },
|
||||
paddingTop: theme.spacing(1.5),
|
||||
paddingBottom: theme.spacing(1.5)
|
||||
}
|
||||
}));
|
||||
|
||||
/*************************** NAVBAR - 10 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Navbar10](https://www.saasable.io/blocks/navbar/navbar10)
|
||||
*
|
||||
* API:
|
||||
* - [Navbar10 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/navbar/navbar10#props-details)
|
||||
*/
|
||||
|
||||
export default function Navbar10({ children, isFixed = true, ...props }) {
|
||||
return (
|
||||
<>
|
||||
<ElevationScroll isFixed={isFixed} {...props}>
|
||||
<AppBar {...(!isFixed && { position: 'static', elevation: 0 })} component="nav" color="inherit" sx={{ background: 'transparent' }}>
|
||||
<StyledToolbar>
|
||||
<ContainerWrapper>{children}</ContainerWrapper>
|
||||
</StyledToolbar>
|
||||
</AppBar>
|
||||
</ElevationScroll>
|
||||
{isFixed && <StyledToolbar />}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Navbar10.propTypes = { children: PropTypes.any, isFixed: PropTypes.bool, props: PropTypes.any };
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Navbar10 } from './Navbar10';
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import { navbar10Height } from '../Navbar10';
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import Logo from '@/components/logo';
|
||||
import { MenuPopper, NavMenu, NavMenuDrawer, NavPrimaryButton, NavSecondaryButton } from '@/components/navbar';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import { withAlpha } from '@/utils/colorUtils';
|
||||
|
||||
/*************************** NAVBAR - CONTENT 10 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [NavbarContent10](https://www.saasable.io/blocks/navbar/navbar10)
|
||||
*
|
||||
* API:
|
||||
* - [NavbarContent10 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/navbar/navbar-content/navbarcontent10#props-details)
|
||||
*/
|
||||
|
||||
export default function NavbarContent10({ landingBaseUrl, navItems, primaryBtn, secondaryBtn, animated }) {
|
||||
const theme = useTheme();
|
||||
|
||||
const downMD = useMediaQuery(theme.breakpoints.down('md'));
|
||||
const downSM = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
|
||||
return (
|
||||
<Stack direction="row" sx={{ alignItems: 'center', justifyContent: 'space-between', width: 1 }}>
|
||||
<Logo to={landingBaseUrl} />
|
||||
{!downMD && navItems && (
|
||||
<Box sx={{ bgcolor: 'grey.200', borderRadius: 10 }}>
|
||||
<NavMenu {...{ navItems }} />
|
||||
</Box>
|
||||
)}
|
||||
<Stack direction="row" sx={{ gap: { xs: 1, md: 1.5 } }}>
|
||||
{!downSM && (
|
||||
<>
|
||||
<NavSecondaryButton {...secondaryBtn} />
|
||||
<ButtonAnimationWrapper>
|
||||
{animated ? (
|
||||
<motion.div
|
||||
initial={{ borderRadius: '50px' }}
|
||||
animate={{
|
||||
boxShadow: [
|
||||
`0px 0px 0px 0px ${withAlpha(theme.vars.palette.primary.main, 0.7)}`,
|
||||
`0px 0px 0px 8px ${withAlpha(theme.vars.palette.primary.main, 0)}`,
|
||||
`0px 0px 0px 0px ${withAlpha(theme.vars.palette.primary.main, 0)}`
|
||||
],
|
||||
borderRadius: '50px'
|
||||
}}
|
||||
transition={{ duration: 1.2, repeat: Infinity, ease: 'linear' }}
|
||||
>
|
||||
<NavPrimaryButton {...primaryBtn} />
|
||||
</motion.div>
|
||||
) : (
|
||||
<NavPrimaryButton {...primaryBtn} />
|
||||
)}
|
||||
</ButtonAnimationWrapper>
|
||||
</>
|
||||
)}
|
||||
{downMD && (
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<MenuPopper
|
||||
offset={downSM ? 12 : 16}
|
||||
toggleProps={{
|
||||
children: <SvgIcon name="tabler-menu-2" color="text.primary" />,
|
||||
color: 'inherit',
|
||||
sx: { minWidth: 40, width: 40, height: 40, p: 0 }
|
||||
}}
|
||||
>
|
||||
<ContainerWrapper
|
||||
sx={{
|
||||
height: 'auto',
|
||||
maxHeight: { xs: `calc(100vh - ${navbar10Height.xs}px)`, sm: `calc(100vh - ${navbar10Height.sm}px)` },
|
||||
overflowY: 'auto'
|
||||
}}
|
||||
>
|
||||
{navItems && (
|
||||
<Box sx={{ mx: -2 }}>
|
||||
<NavMenuDrawer {...{ navItems }} />
|
||||
</Box>
|
||||
)}
|
||||
{downSM && (
|
||||
<Stack direction="row" sx={{ justifyContent: 'space-between', gap: 1, px: 2, py: 2.5, mx: -2, bgcolor: 'grey.100' }}>
|
||||
<NavSecondaryButton {...secondaryBtn} />
|
||||
<ButtonAnimationWrapper>
|
||||
<NavPrimaryButton {...primaryBtn} />
|
||||
</ButtonAnimationWrapper>
|
||||
</Stack>
|
||||
)}
|
||||
</ContainerWrapper>
|
||||
</MenuPopper>
|
||||
</Box>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
NavbarContent10.propTypes = {
|
||||
landingBaseUrl: PropTypes.any,
|
||||
navItems: PropTypes.any,
|
||||
primaryBtn: PropTypes.any,
|
||||
secondaryBtn: PropTypes.any,
|
||||
selectedTheme: PropTypes.any,
|
||||
animated: PropTypes.any
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { default as NavbarContent10 } from './NavbarContent10';
|
||||
@@ -0,0 +1,181 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @next
|
||||
import NextLink from 'next/link';
|
||||
|
||||
// @mui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Button from '@mui/material/Button';
|
||||
import CardMedia from '@mui/material/CardMedia';
|
||||
import Link from '@mui/material/Link';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import Typeset from '@/components/Typeset';
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
|
||||
import useFocusWithin from '@/hooks/useFocusWithin';
|
||||
import { withAlpha } from '@/utils/colorUtils';
|
||||
import { generateFocusVisibleStyles } from '@/utils/CommonFocusStyle';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
import GetImagePath from '@/utils/GetImagePath';
|
||||
|
||||
// @assets
|
||||
import Background from '@/images/graphics/Background';
|
||||
import Wave from '@/images/graphics/Wave';
|
||||
|
||||
/*************************** OTHER - 1 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Other1](https://www.saasable.io/blocks/other/other1)
|
||||
*
|
||||
* API
|
||||
* - [Other1 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/other/other1#props-details)
|
||||
*/
|
||||
|
||||
export default function Other1({ heading, description, primaryBtn, sections }) {
|
||||
const theme = useTheme();
|
||||
const isFocusWithin = useFocusWithin();
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ gap: { xs: 3, sm: 4 } }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.3
|
||||
}}
|
||||
>
|
||||
<Typeset {...{ heading, stackProps: { sx: { textAlign: 'center' } } }} />
|
||||
</motion.div>
|
||||
<Grid container spacing={1.5}>
|
||||
{sections.map((item, index) => (
|
||||
<Grid key={index} size={{ xs: 6, sm: 4, md: 4 }}>
|
||||
<GraphicsCard sx={{ overflow: 'hidden' }}>
|
||||
<motion.div
|
||||
whileHover={{ scale: 1.02 }}
|
||||
initial={{ opacity: 0, y: 25 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: item.animationDelay
|
||||
}}
|
||||
>
|
||||
<GraphicsCard
|
||||
sx={{
|
||||
height: { xs: 240, sm: 324, md: 380 },
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
...(isFocusWithin && { '&:focus-within': generateFocusVisibleStyles(theme.vars.palette.primary.main) })
|
||||
}}
|
||||
>
|
||||
<Link
|
||||
href={item.link}
|
||||
component={NextLink}
|
||||
aria-label={item.title}
|
||||
sx={{ position: 'absolute', top: 0, height: 1, width: 1, borderRadius: { xs: 6, sm: 8, md: 10 }, zIndex: 1 }}
|
||||
/>
|
||||
<Background />
|
||||
<Box sx={{ position: 'absolute', top: 0, width: 1, height: 1, textAlign: 'center' }}>
|
||||
<CardMedia
|
||||
component="img"
|
||||
image={GetImagePath(item.image)}
|
||||
sx={{
|
||||
px: '14.5%',
|
||||
pt: '16%',
|
||||
pb: { xs: 2, md: 1 },
|
||||
objectFit: 'contain'
|
||||
}}
|
||||
alt="other sections"
|
||||
loading="lazy"
|
||||
/>
|
||||
<Box sx={{ '& div': { alignItems: 'center', pt: 0.875 } }}>
|
||||
<Wave />
|
||||
</Box>
|
||||
</Box>
|
||||
<Stack
|
||||
sx={{
|
||||
height: 177,
|
||||
bottom: 0,
|
||||
width: 1,
|
||||
position: 'absolute',
|
||||
justifyContent: 'end',
|
||||
textAlign: 'center',
|
||||
gap: { xs: 0.25, md: 0.5, sm: 1 },
|
||||
p: 3,
|
||||
background: `linear-gradient(180deg, ${withAlpha(theme.vars.palette.grey[100], 0)} 0%, ${theme.vars.palette.grey[100]} 100%)`
|
||||
}}
|
||||
>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 25 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.4 }}
|
||||
>
|
||||
<Typography variant="h4" sx={{ color: 'primary.main' }}>
|
||||
{item.title}
|
||||
</Typography>
|
||||
</motion.div>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 25 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.6 }}
|
||||
>
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
{item.subTitle}
|
||||
</Typography>
|
||||
</motion.div>
|
||||
</Stack>
|
||||
</GraphicsCard>
|
||||
</motion.div>
|
||||
</GraphicsCard>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
<Stack sx={{ gap: 2, alignItems: 'center' }}>
|
||||
<Typography variant="h6" align="center" sx={{ color: 'text.secondary', width: { xs: 1, sm: '80%', md: '65%' } }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 15 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: 0.3
|
||||
}}
|
||||
>
|
||||
{description}
|
||||
</motion.div>
|
||||
</Typography>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
whileHover={{ scale: 1.06 }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button variant="outlined" {...primaryBtn} />
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Other1.propTypes = { heading: PropTypes.string, description: PropTypes.string, primaryBtn: PropTypes.any, sections: PropTypes.array };
|
||||
@@ -0,0 +1,153 @@
|
||||
'use client';
|
||||
|
||||
// @mui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Skeleton from '@mui/material/Skeleton';
|
||||
import Stack from '@mui/material/Stack';
|
||||
|
||||
// @project
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import { withAlpha } from '@/utils/colorUtils';
|
||||
|
||||
/*************************** OTHER - 2 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Other2](https://www.saasable.io/blocks/other/other2)
|
||||
*
|
||||
* API
|
||||
* - [Other2 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/other/other2#props-details)
|
||||
*/
|
||||
|
||||
export default function Other2() {
|
||||
const theme = useTheme();
|
||||
const lightColor = theme.vars.palette.secondary.light;
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ gap: { xs: 3, sm: 4, md: 5 } }}>
|
||||
<Stack sx={{ alignItems: 'center', gap: { xs: 2.5, sm: 3 } }}>
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{ borderRadius: 5, bgcolor: `${withAlpha(theme.vars.palette.secondary.lighter, 0.4)}` }}
|
||||
width="30%"
|
||||
height={32}
|
||||
animation={false}
|
||||
/>
|
||||
<Stack sx={{ alignItems: 'center', gap: 1.5, width: 1, height: 1 }}>
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{ borderRadius: 2.5, bgcolor: `${withAlpha(lightColor, 0.4)}` }}
|
||||
width="70%"
|
||||
height={57}
|
||||
animation={false}
|
||||
/>
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{ borderRadius: 2.5, bgcolor: `${withAlpha(lightColor, 0.4)}` }}
|
||||
width="60%"
|
||||
height={57}
|
||||
animation={false}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack sx={{ alignItems: 'center', gap: 1, width: 1, height: 1 }}>
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{ borderRadius: 1.5, bgcolor: `${withAlpha(lightColor, 0.3)}` }}
|
||||
width="36%"
|
||||
height={24}
|
||||
animation={false}
|
||||
/>
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{ borderRadius: 1.5, bgcolor: `${withAlpha(lightColor, 0.3)}` }}
|
||||
width="30%"
|
||||
height={24}
|
||||
animation={false}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack direction="row" sx={{ padding: 1.25, gap: 1.5, width: 1, justifyContent: 'center' }}>
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{ borderRadius: 5, bgcolor: `${withAlpha(lightColor, 0.3)}` }}
|
||||
width={183}
|
||||
height={52}
|
||||
animation={false}
|
||||
/>
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{ borderRadius: 5, bgcolor: `${withAlpha(lightColor, 0.6)}` }}
|
||||
width={183}
|
||||
height={52}
|
||||
animation={false}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<GraphicsCard sx={{ py: { xs: 5, sm: 7.5 }, px: { xs: 3, sm: 6.25 }, position: 'relative' }}>
|
||||
<Grid container sx={{ mb: -10 }}>
|
||||
<Grid size={8}>
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{ borderRadius: 5, height: { xs: 280, sm: 380 }, bgcolor: `${withAlpha(lightColor, 0.4)}` }}
|
||||
animation={false}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid size={8}>
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{
|
||||
fontSize: '1rem',
|
||||
borderRadius: 5,
|
||||
bgcolor: `${withAlpha(theme.vars.palette.background.default, 0.4)}`,
|
||||
position: 'absolute',
|
||||
top: '40%',
|
||||
left: '35%'
|
||||
}}
|
||||
width="61%"
|
||||
height="80%"
|
||||
animation={false}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</GraphicsCard>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Grid container spacing={7.5}>
|
||||
{[0, 1, 2, 3].map((item) => (
|
||||
<Grid key={item} size={{ xs: 6, sm: 6, md: 3 }}>
|
||||
<Stack sx={{ gap: 1.5 }}>
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{ borderRadius: 3, bgcolor: `${withAlpha(lightColor, 0.4)}` }}
|
||||
width="80%"
|
||||
height={36}
|
||||
animation={false}
|
||||
/>
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{ borderRadius: 1.5, bgcolor: `${withAlpha(lightColor, 0.25)}` }}
|
||||
width="100%"
|
||||
height={16}
|
||||
animation={false}
|
||||
/>
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
sx={{ borderRadius: 1.5, bgcolor: `${withAlpha(lightColor, 0.25)}` }}
|
||||
width="32%"
|
||||
height={16}
|
||||
animation={false}
|
||||
/>
|
||||
</Stack>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as Other1 } from './Other1';
|
||||
export { default as Other2 } from './Other2';
|
||||
@@ -0,0 +1,181 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @next
|
||||
import NextLink from 'next/link';
|
||||
|
||||
// @mui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import Button from '@mui/material/Button';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Link from '@mui/material/Link';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import Typeset from '@/components/Typeset';
|
||||
|
||||
import { IconType } from '@/enum';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
/*************************** PRICING - 9 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Pricing9](https://www.saasable.io/blocks/pricing/pricing9)
|
||||
*
|
||||
* API
|
||||
* - [Pricing9 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/pricing/pricing9#props-details)
|
||||
*/
|
||||
|
||||
export default function Pricing9({ heading, caption, features, plans }) {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ gap: { xs: 6 } }}>
|
||||
{heading && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: 0.4 }}
|
||||
>
|
||||
<Typeset {...{ heading, caption, stackProps: { sx: { textAlign: 'center' } } }} />
|
||||
</motion.div>
|
||||
)}
|
||||
<Grid container spacing={1.5} sx={{ height: 1, justifyContent: 'center' }}>
|
||||
{plans.map((plan, index) => (
|
||||
<Grid key={index} size={{ xs: 12, sm: 6, md: 4 }}>
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, x: index === 0 ? -60 : index === 2 ? 60 : 0, y: 0, scale: index === 1 ? 0.9 : 1 }}
|
||||
whileInView={{ opacity: 1, x: 0, y: 0, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.4, ease: 'easeInOut', delay: plan.animationDelay }}
|
||||
style={{ height: '100%' }}
|
||||
>
|
||||
<GraphicsCard sx={{ height: 1, ...(plan.active && { border: '1px solid', borderColor: 'primary.main' }) }}>
|
||||
<Box sx={{ pt: { xs: 4, sm: 5, md: 8 }, px: { xs: 2, md: 5 }, pb: { xs: 2, sm: 3, md: 5.25 }, height: 1 }}>
|
||||
<Stack sx={{ gap: 5, height: 1 }}>
|
||||
<Stack sx={{ gap: { xs: 2, md: 3 } }}>
|
||||
<Stack sx={{ gap: { xs: 1, sm: 1.5 }, textAlign: 'center' }}>
|
||||
<Typography variant="subtitle1" sx={{ color: 'text.secondary' }}>
|
||||
{plan.title}
|
||||
</Typography>
|
||||
<Stack>
|
||||
<Typography component="div" variant="h1">
|
||||
${plan.offerPrice && plan.offerPrice > 0 ? plan.offerPrice : plan.price}
|
||||
</Typography>
|
||||
{!!(plan.offerPrice && plan.offerPrice > 0) && (
|
||||
<Typography variant="h3" sx={{ color: 'grey.700', textDecoration: 'line-through' }}>
|
||||
${plan.price}
|
||||
</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack sx={{ height: 1, justifyContent: 'space-between', gap: { xs: 3, sm: 4, md: 5 } }}>
|
||||
<Stack sx={{ gap: 5 }}>
|
||||
<Divider>
|
||||
<Chip
|
||||
label={plan.featureTitle}
|
||||
size="small"
|
||||
slotProps={{ label: { sx: { py: 0.5, px: 1.5, typography: 'caption', color: 'text.secondary' } } }}
|
||||
sx={{ bgcolor: 'grey.200' }}
|
||||
/>
|
||||
</Divider>
|
||||
<Stack sx={{ gap: { xs: 0.75, md: 1 } }}>
|
||||
{features.map((item, index) => {
|
||||
const active = plan.featuresID.includes(item.id);
|
||||
return (
|
||||
<Stack key={index} direction="row" sx={{ gap: 1.25, alignItems: 'center' }}>
|
||||
<Avatar sx={{ bgcolor: 'grey.100', width: 24, height: 24 }}>
|
||||
<SvgIcon
|
||||
name={active ? 'tabler-check' : 'tabler-x'}
|
||||
type={IconType.STROKE}
|
||||
size={16}
|
||||
twoToneColor={theme.vars.palette.grey[100]}
|
||||
color={active ? 'secondary.darker' : 'text.secondary'}
|
||||
stroke={2}
|
||||
/>
|
||||
</Avatar>
|
||||
<Typography
|
||||
variant={active ? 'subtitle1' : 'body1'}
|
||||
sx={{ color: active ? 'secondary.darker' : 'text.secondary' }}
|
||||
>
|
||||
{item.label}
|
||||
</Typography>
|
||||
</Stack>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack sx={{ gap: 0.75 }}>
|
||||
{plan.contentLink && (
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary', textAlign: 'center', mb: 1.25 }}>
|
||||
{plan.content}
|
||||
<Link component={NextLink} color="primary.main" sx={{ textDecoration: 'underline' }} {...plan.contentLink} />
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<motion.div
|
||||
initial={{ scale: 0.9 }}
|
||||
animate={{ scale: plan.active ? [1, 1.04, 1] : 1 }}
|
||||
transition={{
|
||||
duration: plan.active ? 0.9 : 1,
|
||||
delay: 0.1,
|
||||
ease: 'easeInOut',
|
||||
...(plan.active && { repeat: Infinity, repeatType: 'loop' })
|
||||
}}
|
||||
whileHover={{ scale: 1.01, transition: { duration: 0.3 } }}
|
||||
>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button
|
||||
variant={plan.active ? 'contained' : 'outlined'}
|
||||
sx={{ ...(!plan.link && { mb: { sm: 3.25, md: 3.75 } }) }}
|
||||
fullWidth
|
||||
{...plan.exploreLink}
|
||||
/>
|
||||
</ButtonAnimationWrapper>
|
||||
</motion.div>
|
||||
{plan.link && (
|
||||
<Typography variant="subtitle1" sx={{ textAlign: 'center', color: 'text.secondary' }}>
|
||||
or
|
||||
<Link
|
||||
component={NextLink}
|
||||
variant="subtitle1"
|
||||
color="primary.main"
|
||||
{...plan.link}
|
||||
underline="hover"
|
||||
sx={{ marginLeft: 0.5 }}
|
||||
/>
|
||||
</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
</GraphicsCard>
|
||||
</motion.div>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Pricing9.propTypes = { heading: PropTypes.string, caption: PropTypes.string, features: PropTypes.array, plans: PropTypes.array };
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Pricing9 } from './Pricing9';
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
'use client';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
// @mui
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import List from '@mui/material/List';
|
||||
import ListItemButton from '@mui/material/ListItemButton';
|
||||
import ListItemText from '@mui/material/ListItemText';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
// @project
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
// Helper functions for scrollspy
|
||||
const clamp = (value) => Math.max(0, value);
|
||||
const isBetween = (value, floor, ceil) => value >= floor && value <= ceil;
|
||||
|
||||
/*************************** HOOKS - SCROLLSPY ***************************/
|
||||
|
||||
function useScrollspy(ids, offset = 0) {
|
||||
const [activeId, setActiveId] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const listener = () => {
|
||||
const scroll = window.scrollY;
|
||||
|
||||
const position = ids
|
||||
.map((id) => {
|
||||
const element = document.getElementById(id);
|
||||
if (!element) return { id, top: -1, bottom: -1 };
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
const top = clamp(rect.top + scroll - offset);
|
||||
const bottom = clamp(rect.bottom + scroll - offset);
|
||||
|
||||
return { id, top, bottom };
|
||||
})
|
||||
.find(({ top, bottom }) => isBetween(scroll, top, bottom));
|
||||
|
||||
setActiveId(position?.id || '');
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', listener);
|
||||
window.addEventListener('resize', listener);
|
||||
listener(); // Initial call to set the activeId
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', listener);
|
||||
window.removeEventListener('resize', listener);
|
||||
};
|
||||
}, [ids, offset]);
|
||||
|
||||
return activeId;
|
||||
}
|
||||
|
||||
/*************************** PRIVACY POLICY - DATA ***************************/
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
id: 'acceptance-of-terms',
|
||||
heading: 'Acceptance of terms',
|
||||
caption:
|
||||
'By accessing and using this website, you agree to be bound by these Terms and Conditions of Use. If you do not agree with any part of these terms, you must not use the website. shares information about you when you use our website or services. By accessing or using our website, you consent to the practices described in this policy.'
|
||||
},
|
||||
{
|
||||
id: 'changes-to-terms',
|
||||
heading: 'Changes to terms',
|
||||
caption:
|
||||
'We reserve the right to modify or replace these terms at our sole discretion. It is your responsibility to check these terms periodically for changes. Your continued use of the website after the posting of any changes constitutes acceptance of those changes.'
|
||||
},
|
||||
{
|
||||
id: 'user-conduct',
|
||||
heading: 'User conduct',
|
||||
caption:
|
||||
'You agree to use this website only for lawful purposes and in a manner consistent with all applicable local, national, and international laws and regulations.'
|
||||
},
|
||||
{
|
||||
id: 'intellectual-property',
|
||||
heading: 'Intellectual property',
|
||||
caption:
|
||||
'All content on this website, including but not limited to text, graphics, logos, images, audio clips, video clips, digital downloads, and data compilations, is the property of [Your Company Name] or its content suppliers and protected by international copyright laws.'
|
||||
},
|
||||
{
|
||||
id: 'privacy-policy',
|
||||
heading: 'Privacy policy',
|
||||
caption:
|
||||
'We do not sell, trade, or otherwise transfer your personal information to third parties. We may share information with trusted service providers who assist us in operating our website, conducting our business, or servicing you.'
|
||||
},
|
||||
{
|
||||
id: 'user-generated-content',
|
||||
heading: 'User-generated content',
|
||||
caption:
|
||||
'If you submit any material to this website, you grant [Your Company Name] a perpetual, royalty-free, worldwide license to use, reproduce, modify, adapt, publish, translate, create derivative works from, distribute, and display such material.'
|
||||
},
|
||||
{
|
||||
id: 'limitation-of-liability',
|
||||
heading: 'Limitation of liability',
|
||||
caption:
|
||||
'In no event shall [Your Company Name] or its affiliates be liable for any direct, indirect, incidental, special, or consequential damages resulting from the use or inability to use this website.'
|
||||
},
|
||||
{
|
||||
id: 'indemnity',
|
||||
heading: 'Indemnity',
|
||||
caption:
|
||||
"You agree to indemnify and hold harmless [Your Company Name] and its affiliates from any claims, actions, demands, damages, liabilities, costs, or expenses, including reasonable attorneys' fees, arising out of or related to your use of the website or any violation of these terms."
|
||||
},
|
||||
{
|
||||
id: 'governing-law',
|
||||
heading: 'Governing law',
|
||||
caption:
|
||||
'These terms are governed by and construed in accordance with the laws of [Your Country/State], without regard to its conflict of law principles.'
|
||||
}
|
||||
];
|
||||
|
||||
/*************************** PRIVACY POLICY - 1 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [PrivacyPolicy1](https://www.saasable.io/blocks/privacy-policy/privacy-policy1)
|
||||
*/
|
||||
|
||||
export default function PrivacyPolicy1() {
|
||||
const ids = menuItems.map((item) => item.id);
|
||||
|
||||
// Adjust offset as per header height
|
||||
const activeId = useScrollspy(ids, 60);
|
||||
const [selectedID, setSelectedID] = useState(activeId);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeId) {
|
||||
setSelectedID(activeId);
|
||||
}
|
||||
}, [activeId]);
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Grid container spacing={{ xs: 2, md: 3 }}>
|
||||
<Grid size={{ xs: 12, sm: 4, md: 3 }}>
|
||||
<List component="div" sx={{ position: 'sticky', top: 20 }} disablePadding>
|
||||
{menuItems.map((item, index) => (
|
||||
<ListItemButton
|
||||
key={index}
|
||||
href={`#${item.id}`}
|
||||
sx={{
|
||||
py: 1.25,
|
||||
px: 1.5,
|
||||
borderRadius: 3,
|
||||
mb: 0.75,
|
||||
...(selectedID === item.id && { color: 'primary.main', bgcolor: 'grey.100' }),
|
||||
'&:hover': { bgcolor: 'grey.50' }
|
||||
}}
|
||||
onClick={() => setSelectedID(item.id)}
|
||||
>
|
||||
<ListItemText primary={item.heading} slotProps={{ primary: { variant: 'subtitle1' } }} sx={{ my: 0 }} />
|
||||
</ListItemButton>
|
||||
))}
|
||||
</List>
|
||||
<Divider sx={{ display: { xs: 'block', sm: 'none' } }} />
|
||||
</Grid>
|
||||
<Grid size={{ xs: 12, sm: 8, md: 9 }}>
|
||||
{menuItems.map((item, index) => (
|
||||
<Stack
|
||||
key={index}
|
||||
id={item.id}
|
||||
sx={{ py: { xs: 1, sm: 1.5, md: 3 }, px: { md: 3 }, gap: 1, '&:first-of-type': { pt: { sm: 0 } } }}
|
||||
>
|
||||
<Typography variant="h4">{item.heading}</Typography>
|
||||
<Typography variant="body1" sx={{ color: 'text.secondary' }}>
|
||||
{item.caption}
|
||||
</Typography>
|
||||
</Stack>
|
||||
))}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default as PrivacyPolicy1 } from './PrivacyPolicy1';
|
||||
@@ -0,0 +1,83 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import Button from '@mui/material/Button';
|
||||
import CardMedia from '@mui/material/CardMedia';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Link from '@mui/material/Link';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
|
||||
// @project
|
||||
import ButtonAnimationWrapper from '@/components/ButtonAnimationWrapper';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import GraphicsImage from '@/components/GraphicsImage';
|
||||
import SvgIcon from '@/components/SvgIcon';
|
||||
import { NextLink } from '@/components/routes';
|
||||
|
||||
export default function ProPage({ image }) {
|
||||
return (
|
||||
<>
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Box
|
||||
sx={{
|
||||
bgcolor: 'background.default',
|
||||
borderRadius: 7.5
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center' }}>
|
||||
<GraphicsImage
|
||||
image={image}
|
||||
sx={{
|
||||
width: { xs: '75px', md: '150px' },
|
||||
height: { xs: '75px', md: '150px' },
|
||||
borderRadius: 0
|
||||
}}
|
||||
/>
|
||||
<Typography variant="h4" sx={{ fontWeight: 'bold', mb: 1, mt: { xs: 4, md: 7 }, textAlign: 'center' }}>
|
||||
Discover the Components Not Available in the Free Version
|
||||
</Typography>
|
||||
<Typography variant="body1" color="textSecondary" sx={{ mb: 3, color: 'grey.700', textAlign: 'center' }}>
|
||||
<Link href="https://www.saasable.io/" target="_blank" underline="hover" component={NextLink}>
|
||||
Check out SaasAble PRO
|
||||
</Link>{' '}
|
||||
which offers updated components, auto layout, and Figma new variables, complete with dark mode!
|
||||
</Typography>
|
||||
|
||||
<Grid container spacing={2} justifyContent="center">
|
||||
<Grid>
|
||||
<Button
|
||||
variant="outlined"
|
||||
component={NextLink}
|
||||
href="https://www.figma.com/design/mlkXfeqxUKqIo0GQhPBqPb/SaasAble---UI-Kit---Preview-only?node-id=11-1833&t=JBHOIIEuYZpmN6v8-1"
|
||||
target="_blank"
|
||||
sx={{ minWidth: 215 }}
|
||||
startIcon={<CardMedia component="img" src="/assets/images/shared/figma.svg" sx={{ width: 16, height: 16 }} alt="figma" />}
|
||||
>
|
||||
Preview Pro
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid>
|
||||
<ButtonAnimationWrapper>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
component={NextLink}
|
||||
href={'https://www.saasable.io/sections'}
|
||||
target="_blank"
|
||||
startIcon={<SvgIcon name="tabler-sparkles" size={16} stroke={3} color="background.default" />}
|
||||
>
|
||||
View Pro Component
|
||||
</Button>
|
||||
</ButtonAnimationWrapper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</Box>
|
||||
</ContainerWrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
ProPage.propTypes = { image: PropTypes.any };
|
||||
@@ -0,0 +1 @@
|
||||
export { default as ProPage } from './ProPage';
|
||||
@@ -0,0 +1,74 @@
|
||||
import PropTypes from 'prop-types';
|
||||
// @mui
|
||||
import Button from '@mui/material/Button';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import Link from '@mui/material/Link';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
|
||||
// @project
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import { NextLink } from '@/components/routes';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
/*************************** SMALL HERO - 3 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [SmallHero3](https://www.saasable.io/blocks/small-hero/small-hero3)
|
||||
*
|
||||
* API
|
||||
* - [SmallHero3 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/smallhero/smallhero3#props-details)
|
||||
*/
|
||||
|
||||
export default function SmallHero3({ chip, headLine, captionLine, exploreBtn }) {
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ alignItems: 'start', gap: { xs: 1.5, md: 4 } }}>
|
||||
{chip && (
|
||||
<Chip
|
||||
label={
|
||||
typeof chip.label === 'string' ? (
|
||||
<Typography variant="caption" sx={{ color: 'text.secondary' }}>
|
||||
{chip.label}
|
||||
{chip.link && (
|
||||
<Link
|
||||
component={NextLink}
|
||||
variant="caption"
|
||||
color="primary.main"
|
||||
{...chip.link}
|
||||
underline="hover"
|
||||
sx={{ '&:hover': { color: 'primary.dark' } }}
|
||||
/>
|
||||
)}
|
||||
</Typography>
|
||||
) : (
|
||||
chip.label
|
||||
)
|
||||
}
|
||||
sx={{ bgcolor: 'grey.100' }}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Stack direction="row" sx={{ justifyContent: 'space-between', flexWrap: { xs: 'wrap', md: 'unset' }, width: 1, gap: 3 }}>
|
||||
<Stack sx={{ gap: 1.5 }}>
|
||||
{typeof headLine === 'string' ? <Typography variant="h1">{headLine}</Typography> : headLine}
|
||||
{captionLine && (
|
||||
<Typography variant="h6" sx={{ color: 'text.secondary', width: { md: '85%' } }}>
|
||||
{captionLine}
|
||||
</Typography>
|
||||
)}
|
||||
</Stack>
|
||||
{exploreBtn && (
|
||||
<Stack direction="row" sx={{ alignItems: 'end', justifyContent: 'center', minWidth: 'fit-content' }}>
|
||||
<Button size="large" variant="outlined" {...exploreBtn} />
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
SmallHero3.propTypes = { chip: PropTypes.any, headLine: PropTypes.any, captionLine: PropTypes.any, exploreBtn: PropTypes.any };
|
||||
@@ -0,0 +1 @@
|
||||
export { default as SmallHero3 } from './SmallHero3';
|
||||
@@ -0,0 +1,82 @@
|
||||
'use client';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// @mui
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Masonry from '@mui/lab/Masonry';
|
||||
|
||||
// @third-party
|
||||
import { motion } from 'motion/react';
|
||||
|
||||
// @project
|
||||
import { GraphicsCard } from '@/components/cards';
|
||||
import ContainerWrapper from '@/components/ContainerWrapper';
|
||||
import Rating from '@/components/Rating';
|
||||
import Typeset from '@/components/Typeset';
|
||||
|
||||
import { withAlpha } from '@/utils/colorUtils';
|
||||
import { SECTION_COMMON_PY } from '@/utils/constant';
|
||||
|
||||
/*************************** TESTIMONIAL - 10 ***************************/
|
||||
|
||||
/**
|
||||
*
|
||||
* Demos:
|
||||
* - [Testimonial10](https://www.saasable.io/blocks/testimonial/testimonial10)
|
||||
*
|
||||
* API:
|
||||
* - [Testimonial10 API](https://phoenixcoded.gitbook.io/saasable/ui-kit/development/components/testimonial/testimonial10#props-details)
|
||||
*/
|
||||
|
||||
export default function Testimonial10({ heading, caption, testimonials }) {
|
||||
const theme = useTheme();
|
||||
|
||||
const gc = theme.vars.palette.background.default;
|
||||
const gradient = `radial-gradient(146.46% 68.12% at 50% 29.86%, ${withAlpha(gc, 0)} 0%, ${gc} 100%)`;
|
||||
|
||||
return (
|
||||
<ContainerWrapper sx={{ py: SECTION_COMMON_PY }}>
|
||||
<Stack sx={{ gap: { xs: 3, sm: 4 } }}>
|
||||
<Typeset {...{ heading, caption, stackProps: { sx: { maxWidth: { xs: 550, md: 700 }, textAlign: 'center', mx: 'auto' } } }} />
|
||||
<Masonry
|
||||
columns={{ xs: 2, sm: 3 }}
|
||||
spacing={{ xs: 1, sm: 1.5 }}
|
||||
sx={{
|
||||
position: 'relative',
|
||||
'&:before': { position: 'absolute', content: `' '`, left: 0, bottom: 0, width: 1, height: 1, background: gradient }
|
||||
}}
|
||||
>
|
||||
{testimonials.map((testimonial, index) => (
|
||||
<motion.div
|
||||
key={index}
|
||||
initial={{ opacity: 0, scale: 0.5 }}
|
||||
whileInView={{ opacity: 1, scale: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.4, ease: 'easeOut', delay: index * 0.1 }}
|
||||
>
|
||||
<GraphicsCard key={index} sx={{ borderRadius: { xs: 4, md: 6 } }}>
|
||||
<Stack sx={{ justifyContent: 'space-between', height: 1, gap: 3, p: { xs: 1.5, md: 2 } }}>
|
||||
<Rating {...{ rate: testimonial.ratings, starSize: 16 }} />
|
||||
<Stack sx={{ gap: 1 }}>
|
||||
<Typography variant="subtitle1">{testimonial.title}</Typography>
|
||||
<Typography sx={{ color: 'text.secondary' }}>{testimonial.review}</Typography>
|
||||
</Stack>
|
||||
<Stack sx={{ gap: 0.5 }}>
|
||||
<Typography variant="subtitle1">{testimonial.profile.name}</Typography>
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
{testimonial.profile.role}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</GraphicsCard>
|
||||
</motion.div>
|
||||
))}
|
||||
</Masonry>
|
||||
</Stack>
|
||||
</ContainerWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
Testimonial10.propTypes = { heading: PropTypes.any, caption: PropTypes.any, testimonials: PropTypes.any };
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Testimonial10 } from './Testimonial10';
|
||||
Reference in New Issue
Block a user