feat: Implement Inertia.js with React for a new frontend architecture, including home, posts, and profile pages, along with theme customizations.
This commit is contained in:
parent
b4d2ae52f0
commit
4faaebdc59
14
app/Http/Controllers/Home/HomepageController.php
Normal file
14
app/Http/Controllers/Home/HomepageController.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Home;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class HomepageController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return Inertia::render('HomePage');
|
||||
}
|
||||
}
|
||||
43
app/Http/Middleware/HandleInertiaRequests.php
Normal file
43
app/Http/Middleware/HandleInertiaRequests.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Middleware;
|
||||
|
||||
class HandleInertiaRequests extends Middleware
|
||||
{
|
||||
/**
|
||||
* The root template that's loaded on the first page visit.
|
||||
*
|
||||
* @see https://inertiajs.com/server-side-setup#root-template
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $rootView = 'app';
|
||||
|
||||
/**
|
||||
* Determines the current asset version.
|
||||
*
|
||||
* @see https://inertiajs.com/asset-versioning
|
||||
*/
|
||||
public function version(Request $request): ?string
|
||||
{
|
||||
return parent::version($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the props that are shared by default.
|
||||
*
|
||||
* @see https://inertiajs.com/shared-data
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function share(Request $request): array
|
||||
{
|
||||
return [
|
||||
...parent::share($request),
|
||||
//
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Middleware\HandleInertiaRequests;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
@ -11,7 +12,9 @@
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
//
|
||||
$middleware->web(append: [
|
||||
HandleInertiaRequests::class,
|
||||
]);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions): void {
|
||||
//
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
"bezhansalleh/filament-shield": "^4.0",
|
||||
"filament/filament": "^4.0",
|
||||
"filament/spatie-laravel-media-library-plugin": "^4.0",
|
||||
"inertiajs/inertia-laravel": "^2.0",
|
||||
"joaopaulolndev/filament-pdf-viewer": "^2.0",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/tinker": "^2.10.1",
|
||||
|
||||
72
composer.lock
generated
72
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "c2c5addb8f0b597b5135fc66bf225efd",
|
||||
"content-hash": "a1f1369a3464148f2f165f2d8a1aeadc",
|
||||
"packages": [
|
||||
{
|
||||
"name": "anourvalar/eloquent-serialize",
|
||||
@ -2305,6 +2305,76 @@
|
||||
],
|
||||
"time": "2025-08-22T14:27:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "inertiajs/inertia-laravel",
|
||||
"version": "v2.0.16",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/inertiajs/inertia-laravel.git",
|
||||
"reference": "c060177e1b5120e70d1c26fe7c1007a8c8238f0d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/c060177e1b5120e70d1c26fe7c1007a8c8238f0d",
|
||||
"reference": "c060177e1b5120e70d1c26fe7c1007a8c8238f0d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"laravel/framework": "^10.0|^11.0|^12.0",
|
||||
"php": "^8.1.0",
|
||||
"symfony/console": "^6.2|^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"larastan/larastan": "^3.0",
|
||||
"laravel/pint": "^1.16",
|
||||
"mockery/mockery": "^1.3.3",
|
||||
"orchestra/testbench": "^8.0|^9.2|^10.0",
|
||||
"phpunit/phpunit": "^10.4|^11.5",
|
||||
"roave/security-advisories": "dev-master"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-pcntl": "Recommended when running the Inertia SSR server via the `inertia:start-ssr` artisan command."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Inertia\\ServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"./helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Inertia\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jonathan Reinink",
|
||||
"email": "jonathan@reinink.ca",
|
||||
"homepage": "https://reinink.ca"
|
||||
}
|
||||
],
|
||||
"description": "The Laravel adapter for Inertia.js.",
|
||||
"keywords": [
|
||||
"inertia",
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/inertiajs/inertia-laravel/issues",
|
||||
"source": "https://github.com/inertiajs/inertia-laravel/tree/v2.0.16"
|
||||
},
|
||||
"time": "2025-12-17T21:57:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "joaopaulolndev/filament-pdf-viewer",
|
||||
"version": "v2.0.0",
|
||||
|
||||
1582
package-lock.json
generated
1582
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -8,10 +8,20 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@vitejs/plugin-react": "^5.1.2",
|
||||
"axios": "^1.11.0",
|
||||
"concurrently": "^9.0.1",
|
||||
"laravel-vite-plugin": "^2.0.0",
|
||||
"tailwindcss": "^4.1.18",
|
||||
"vite": "^7.0.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@inertiajs/react": "^2.3.4",
|
||||
"@mui/icons-material": "^7.3.6",
|
||||
"@mui/material": "^7.3.6",
|
||||
"react": "^19.2.3",
|
||||
"react-dom": "^19.2.3"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
public/assets/img/logo/icon-with-text.png
Normal file
BIN
public/assets/img/logo/icon-with-text.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@ -4,8 +4,9 @@
|
||||
@source '../../storage/framework/views/*.php';
|
||||
@source '../**/*.blade.php';
|
||||
@source '../**/*.js';
|
||||
@source '../**/*.jsx';
|
||||
|
||||
@theme {
|
||||
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
}
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
import './bootstrap';
|
||||
31
resources/js/app.jsx
Normal file
31
resources/js/app.jsx
Normal file
@ -0,0 +1,31 @@
|
||||
import '../css/app.css'
|
||||
import React from 'react'
|
||||
import { createInertiaApp } from '@inertiajs/react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
|
||||
createInertiaApp({
|
||||
resolve: name => {
|
||||
const pages = import.meta.glob('./pages/**/*.jsx', { eager: true })
|
||||
return pages[`./pages/${name}.jsx`]
|
||||
},
|
||||
setup({ el, App, props }) {
|
||||
createRoot(el).render(<App {...props} />)
|
||||
},
|
||||
defaults: {
|
||||
form: {
|
||||
recentlySuccessfulDuration: 5000,
|
||||
},
|
||||
prefetch: {
|
||||
cacheFor: "1m",
|
||||
hoverDelay: 150,
|
||||
},
|
||||
visitOptions: (href, options) => {
|
||||
return {
|
||||
headers: {
|
||||
...options.headers,
|
||||
"X-Custom-Header": "value",
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
})
|
||||
9
resources/js/pages/DashboardPage.jsx
Normal file
9
resources/js/pages/DashboardPage.jsx
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function DashboardPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
37
resources/js/pages/HomePage.jsx
Normal file
37
resources/js/pages/HomePage.jsx
Normal file
@ -0,0 +1,37 @@
|
||||
import CssBaseline from '@mui/material/CssBaseline';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import AppTheme from './theme/AppTheme';
|
||||
import Hero from './home/components/Hero';
|
||||
import LogoCollection from './home/components/LogoCollection';
|
||||
import Highlights from './home/components/Highlights';
|
||||
import Pricing from './home/components/Pricing';
|
||||
import Features from './home/components/Features';
|
||||
import Testimonials from './home/components/Testimonials';
|
||||
import FAQ from './home/components/FAQ';
|
||||
import Footer from './home/components/Footer';
|
||||
import Navbar from './home/components/Navbar';
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<AppTheme>
|
||||
<CssBaseline enableColorScheme />
|
||||
|
||||
<Navbar />
|
||||
<Hero />
|
||||
<div>
|
||||
<LogoCollection />
|
||||
<Features />
|
||||
<Divider />
|
||||
<Testimonials />
|
||||
<Divider />
|
||||
<Highlights />
|
||||
<Divider />
|
||||
<Pricing />
|
||||
<Divider />
|
||||
<FAQ />
|
||||
<Divider />
|
||||
<Footer />
|
||||
</div>
|
||||
</AppTheme>
|
||||
);
|
||||
}
|
||||
153
resources/js/pages/home/components/FAQ.jsx
Normal file
153
resources/js/pages/home/components/FAQ.jsx
Normal file
@ -0,0 +1,153 @@
|
||||
import * as React from 'react';
|
||||
import Accordion from '@mui/material/Accordion';
|
||||
import AccordionDetails from '@mui/material/AccordionDetails';
|
||||
import AccordionSummary from '@mui/material/AccordionSummary';
|
||||
import Box from '@mui/material/Box';
|
||||
import Container from '@mui/material/Container';
|
||||
import Link from '@mui/material/Link';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
|
||||
export default function FAQ() {
|
||||
const [expanded, setExpanded] = React.useState([]);
|
||||
|
||||
const handleChange =
|
||||
(panel) => (event, isExpanded) => {
|
||||
setExpanded(
|
||||
isExpanded
|
||||
? [...expanded, panel]
|
||||
: expanded.filter((item) => item !== panel),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Container
|
||||
id="faq"
|
||||
sx={{
|
||||
pt: { xs: 4, sm: 12 },
|
||||
pb: { xs: 8, sm: 16 },
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: { xs: 3, sm: 6 },
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h4"
|
||||
sx={{
|
||||
color: 'text.primary',
|
||||
width: { sm: '100%', md: '60%' },
|
||||
textAlign: { sm: 'left', md: 'center' },
|
||||
}}
|
||||
>
|
||||
Frequently asked questions
|
||||
</Typography>
|
||||
<Box sx={{ width: '100%' }}>
|
||||
<Accordion
|
||||
expanded={expanded.includes('panel1')}
|
||||
onChange={handleChange('panel1')}
|
||||
>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
aria-controls="panel1d-content"
|
||||
id="panel1d-header"
|
||||
>
|
||||
<Typography component="span" variant="subtitle2">
|
||||
How do I contact customer support if I have a question or issue?
|
||||
</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography
|
||||
variant="body2"
|
||||
gutterBottom
|
||||
sx={{ maxWidth: { sm: '100%', md: '70%' } }}
|
||||
>
|
||||
You can reach our customer support team by emailing
|
||||
<Link href="mailto:support@email.com">support@email.com</Link>
|
||||
or calling our toll-free number. We're here to assist you
|
||||
promptly.
|
||||
</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
<Accordion
|
||||
expanded={expanded.includes('panel2')}
|
||||
onChange={handleChange('panel2')}
|
||||
>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
aria-controls="panel2d-content"
|
||||
id="panel2d-header"
|
||||
>
|
||||
<Typography component="span" variant="subtitle2">
|
||||
Can I return the product if it doesn't meet my expectations?
|
||||
</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography
|
||||
variant="body2"
|
||||
gutterBottom
|
||||
sx={{ maxWidth: { sm: '100%', md: '70%' } }}
|
||||
>
|
||||
Absolutely! We offer a hassle-free return policy. If you're not
|
||||
completely satisfied, you can return the product within [number of
|
||||
days] days for a full refund or exchange.
|
||||
</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
<Accordion
|
||||
expanded={expanded.includes('panel3')}
|
||||
onChange={handleChange('panel3')}
|
||||
>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
aria-controls="panel3d-content"
|
||||
id="panel3d-header"
|
||||
>
|
||||
<Typography component="span" variant="subtitle2">
|
||||
What makes your product stand out from others in the market?
|
||||
</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography
|
||||
variant="body2"
|
||||
gutterBottom
|
||||
sx={{ maxWidth: { sm: '100%', md: '70%' } }}
|
||||
>
|
||||
Our product distinguishes itself through its adaptability, durability,
|
||||
and innovative features. We prioritize user satisfaction and
|
||||
continually strive to exceed expectations in every aspect.
|
||||
</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
<Accordion
|
||||
expanded={expanded.includes('panel4')}
|
||||
onChange={handleChange('panel4')}
|
||||
>
|
||||
<AccordionSummary
|
||||
expandIcon={<ExpandMoreIcon />}
|
||||
aria-controls="panel4d-content"
|
||||
id="panel4d-header"
|
||||
>
|
||||
<Typography component="span" variant="subtitle2">
|
||||
Is there a warranty on the product, and what does it cover?
|
||||
</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography
|
||||
variant="body2"
|
||||
gutterBottom
|
||||
sx={{ maxWidth: { sm: '100%', md: '70%' } }}
|
||||
>
|
||||
Yes, our product comes with a [length of warranty] warranty. It covers
|
||||
defects in materials and workmanship. If you encounter any issues
|
||||
covered by the warranty, please contact our customer support for
|
||||
assistance.
|
||||
</Typography>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
262
resources/js/pages/home/components/Features.jsx
Normal file
262
resources/js/pages/home/components/Features.jsx
Normal file
@ -0,0 +1,262 @@
|
||||
import * as React from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
import Card from '@mui/material/Card';
|
||||
import MuiChip from '@mui/material/Chip';
|
||||
import Container from '@mui/material/Container';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
import DevicesRoundedIcon from '@mui/icons-material/DevicesRounded';
|
||||
import EdgesensorHighRoundedIcon from '@mui/icons-material/EdgesensorHighRounded';
|
||||
import ViewQuiltRoundedIcon from '@mui/icons-material/ViewQuiltRounded';
|
||||
|
||||
const items = [
|
||||
{
|
||||
icon: <ViewQuiltRoundedIcon />,
|
||||
title: 'Dashboard',
|
||||
description:
|
||||
'This item could provide a snapshot of the most important metrics or data points related to the product.',
|
||||
imageLight: `url("${import.meta.env.TEMPLATE_IMAGE_URL || 'https://mui.com'}/static/images/templates/templates-images/dash-light.png")`,
|
||||
imageDark: `url("${import.meta.env.TEMPLATE_IMAGE_URL || 'https://mui.com'}/static/images/templates/templates-images/dash-dark.png")`,
|
||||
},
|
||||
{
|
||||
icon: <EdgesensorHighRoundedIcon />,
|
||||
title: 'Mobile integration',
|
||||
description:
|
||||
'This item could provide information about the mobile app version of the product.',
|
||||
imageLight: `url("${import.meta.env.TEMPLATE_IMAGE_URL || 'https://mui.com'}/static/images/templates/templates-images/mobile-light.png")`,
|
||||
imageDark: `url("${import.meta.env.TEMPLATE_IMAGE_URL || 'https://mui.com'}/static/images/templates/templates-images/mobile-dark.png")`,
|
||||
},
|
||||
{
|
||||
icon: <DevicesRoundedIcon />,
|
||||
title: 'Available on all platforms',
|
||||
description:
|
||||
'This item could let users know the product is available on all platforms, such as web, mobile, and desktop.',
|
||||
imageLight: `url("${import.meta.env.TEMPLATE_IMAGE_URL || 'https://mui.com'}/static/images/templates/templates-images/devices-light.png")`,
|
||||
imageDark: `url("${import.meta.env.TEMPLATE_IMAGE_URL || 'https://mui.com'}/static/images/templates/templates-images/devices-dark.png")`,
|
||||
},
|
||||
];
|
||||
|
||||
const Chip = styled(MuiChip)(({ theme }) => ({
|
||||
variants: [
|
||||
{
|
||||
props: ({ selected }) => !!selected,
|
||||
style: {
|
||||
background:
|
||||
'linear-gradient(to bottom right, hsl(210, 98%, 48%), hsl(210, 98%, 35%))',
|
||||
color: 'hsl(0, 0%, 100%)',
|
||||
borderColor: (theme.vars || theme).palette.primary.light,
|
||||
'& .MuiChip-label': {
|
||||
color: 'hsl(0, 0%, 100%)',
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
borderColor: (theme.vars || theme).palette.primary.dark,
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
export function MobileLayout({
|
||||
selectedItemIndex,
|
||||
handleItemClick,
|
||||
selectedFeature,
|
||||
}) {
|
||||
if (!items[selectedItemIndex]) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: { xs: 'flex', sm: 'none' },
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: 'flex', gap: 2, overflow: 'auto' }}>
|
||||
{items.map(({ title }, index) => (
|
||||
<Chip
|
||||
size="medium"
|
||||
key={index}
|
||||
label={title}
|
||||
onClick={() => handleItemClick(index)}
|
||||
selected={selectedItemIndex === index}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
<Card variant="outlined">
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
mb: 2,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
minHeight: 280,
|
||||
backgroundImage: 'var(--items-imageLight)',
|
||||
...theme.applyStyles('dark', {
|
||||
backgroundImage: 'var(--items-imageDark)',
|
||||
}),
|
||||
})}
|
||||
style={
|
||||
items[selectedItemIndex]
|
||||
? ({
|
||||
'--items-imageLight': items[selectedItemIndex].imageLight,
|
||||
'--items-imageDark': items[selectedItemIndex].imageDark,
|
||||
})
|
||||
: {}
|
||||
}
|
||||
/>
|
||||
<Box sx={{ px: 2, pb: 2 }}>
|
||||
<Typography
|
||||
gutterBottom
|
||||
sx={{ color: 'text.primary', fontWeight: 'medium' }}
|
||||
>
|
||||
{selectedFeature.title}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 1.5 }}>
|
||||
{selectedFeature.description}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Card>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Features() {
|
||||
const [selectedItemIndex, setSelectedItemIndex] = React.useState(0);
|
||||
|
||||
const handleItemClick = (index) => {
|
||||
setSelectedItemIndex(index);
|
||||
};
|
||||
|
||||
const selectedFeature = items[selectedItemIndex];
|
||||
|
||||
return (
|
||||
<Container id="features" sx={{ py: { xs: 8, sm: 16 } }}>
|
||||
<Box sx={{ width: { sm: '100%', md: '60%' } }}>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h4"
|
||||
gutterBottom
|
||||
sx={{ color: 'text.primary' }}
|
||||
>
|
||||
Product features
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body1"
|
||||
sx={{ color: 'text.secondary', mb: { xs: 2, sm: 4 } }}
|
||||
>
|
||||
Provide a brief overview of the key features of the product. For example,
|
||||
you could list the number of features, their types or benefits, and
|
||||
add-ons.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: { xs: 'column', md: 'row-reverse' },
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<Box
|
||||
sx={{
|
||||
display: { xs: 'none', sm: 'flex' },
|
||||
flexDirection: 'column',
|
||||
gap: 2,
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
{items.map(({ icon, title, description }, index) => (
|
||||
<Box
|
||||
key={index}
|
||||
component={Button}
|
||||
onClick={() => handleItemClick(index)}
|
||||
sx={[
|
||||
(theme) => ({
|
||||
p: 2,
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
'&:hover': {
|
||||
backgroundColor: (theme.vars || theme).palette.action.hover,
|
||||
},
|
||||
}),
|
||||
selectedItemIndex === index && {
|
||||
backgroundColor: 'action.selected',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Box
|
||||
sx={[
|
||||
{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'left',
|
||||
gap: 1,
|
||||
textAlign: 'left',
|
||||
textTransform: 'none',
|
||||
color: 'text.secondary',
|
||||
},
|
||||
selectedItemIndex === index && {
|
||||
color: 'text.primary',
|
||||
},
|
||||
]}
|
||||
>
|
||||
{icon}
|
||||
|
||||
<Typography variant="h6">{title}</Typography>
|
||||
<Typography variant="body2">{description}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
<MobileLayout
|
||||
selectedItemIndex={selectedItemIndex}
|
||||
handleItemClick={handleItemClick}
|
||||
selectedFeature={selectedFeature}
|
||||
/>
|
||||
</div>
|
||||
<Box
|
||||
sx={{
|
||||
display: { xs: 'none', sm: 'flex' },
|
||||
width: { xs: '100%', md: '70%' },
|
||||
height: 'var(--items-image-height)',
|
||||
}}
|
||||
>
|
||||
<Card
|
||||
variant="outlined"
|
||||
sx={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
display: { xs: 'none', sm: 'flex' },
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
m: 'auto',
|
||||
width: 420,
|
||||
height: 500,
|
||||
backgroundSize: 'contain',
|
||||
backgroundImage: 'var(--items-imageLight)',
|
||||
...theme.applyStyles('dark', {
|
||||
backgroundImage: 'var(--items-imageDark)',
|
||||
}),
|
||||
})}
|
||||
style={
|
||||
items[selectedItemIndex]
|
||||
? ({
|
||||
'--items-imageLight': items[selectedItemIndex].imageLight,
|
||||
'--items-imageDark': items[selectedItemIndex].imageDark,
|
||||
})
|
||||
: {}
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
</Box>
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
219
resources/js/pages/home/components/Footer.jsx
Normal file
219
resources/js/pages/home/components/Footer.jsx
Normal file
@ -0,0 +1,219 @@
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
import Container from '@mui/material/Container';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import InputLabel from '@mui/material/InputLabel';
|
||||
import Link from '@mui/material/Link';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import GitHubIcon from '@mui/icons-material/GitHub';
|
||||
import LinkedInIcon from '@mui/icons-material/LinkedIn';
|
||||
import TwitterIcon from '@mui/icons-material/X';
|
||||
import Logo from './Logo';
|
||||
|
||||
function Copyright() {
|
||||
return (
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary', mt: 1 }}>
|
||||
{'Copyright © '}
|
||||
<Link color="text.secondary" href="https://mui.com/">
|
||||
Logo
|
||||
</Link>
|
||||
|
||||
{new Date().getFullYear()}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<Container
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: { xs: 4, sm: 8 },
|
||||
py: { xs: 8, sm: 10 },
|
||||
textAlign: { sm: 'center', md: 'left' },
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: { xs: 'column', sm: 'row' },
|
||||
width: '100%',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 4,
|
||||
minWidth: { xs: '100%', sm: '60%' },
|
||||
}}
|
||||
>
|
||||
<Box sx={{ width: { xs: '100%', sm: '60%' } }}>
|
||||
<Logo />
|
||||
<Typography variant="body2" gutterBottom sx={{ fontWeight: 600, mt: 2 }}>
|
||||
Join the newsletter
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary', mb: 2 }}>
|
||||
Subscribe for weekly updates. No spams ever!
|
||||
</Typography>
|
||||
<InputLabel htmlFor="email-newsletter">Email</InputLabel>
|
||||
<Stack direction="row" spacing={1} useFlexGap>
|
||||
<TextField
|
||||
id="email-newsletter"
|
||||
hiddenLabel
|
||||
size="small"
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
aria-label="Enter your email address"
|
||||
placeholder="Your email address"
|
||||
slotProps={{
|
||||
htmlInput: {
|
||||
autoComplete: 'off',
|
||||
'aria-label': 'Enter your email address',
|
||||
},
|
||||
}}
|
||||
sx={{ width: '250px' }}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
size="small"
|
||||
sx={{ flexShrink: 0 }}
|
||||
>
|
||||
Subscribe
|
||||
</Button>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: { xs: 'none', sm: 'flex' },
|
||||
flexDirection: 'column',
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2" sx={{ fontWeight: 'medium' }}>
|
||||
Product
|
||||
</Typography>
|
||||
<Link color="text.secondary" variant="body2" href="#">
|
||||
Features
|
||||
</Link>
|
||||
<Link color="text.secondary" variant="body2" href="#">
|
||||
Testimonials
|
||||
</Link>
|
||||
<Link color="text.secondary" variant="body2" href="#">
|
||||
Highlights
|
||||
</Link>
|
||||
<Link color="text.secondary" variant="body2" href="#">
|
||||
Pricing
|
||||
</Link>
|
||||
<Link color="text.secondary" variant="body2" href="#">
|
||||
FAQs
|
||||
</Link>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: { xs: 'none', sm: 'flex' },
|
||||
flexDirection: 'column',
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2" sx={{ fontWeight: 'medium' }}>
|
||||
Company
|
||||
</Typography>
|
||||
<Link color="text.secondary" variant="body2" href="#">
|
||||
About us
|
||||
</Link>
|
||||
<Link color="text.secondary" variant="body2" href="#">
|
||||
Careers
|
||||
</Link>
|
||||
<Link color="text.secondary" variant="body2" href="#">
|
||||
Press
|
||||
</Link>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: { xs: 'none', sm: 'flex' },
|
||||
flexDirection: 'column',
|
||||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2" sx={{ fontWeight: 'medium' }}>
|
||||
Legal
|
||||
</Typography>
|
||||
<Link color="text.secondary" variant="body2" href="#">
|
||||
Terms
|
||||
</Link>
|
||||
<Link color="text.secondary" variant="body2" href="#">
|
||||
Privacy
|
||||
</Link>
|
||||
<Link color="text.secondary" variant="body2" href="#">
|
||||
Contact
|
||||
</Link>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
pt: { xs: 4, sm: 8 },
|
||||
width: '100%',
|
||||
borderTop: '1px solid',
|
||||
borderColor: 'divider',
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<Link color="text.secondary" variant="body2" href="#">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
<Typography sx={{ display: 'inline', mx: 0.5, opacity: 0.5 }}>
|
||||
•
|
||||
</Typography>
|
||||
<Link color="text.secondary" variant="body2" href="#">
|
||||
Terms of Service
|
||||
</Link>
|
||||
<Copyright />
|
||||
</div>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={1}
|
||||
useFlexGap
|
||||
sx={{ justifyContent: 'left', color: 'text.secondary' }}
|
||||
>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
size="small"
|
||||
href="https://github.com/mui"
|
||||
aria-label="GitHub"
|
||||
sx={{ alignSelf: 'center' }}
|
||||
>
|
||||
<GitHubIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
size="small"
|
||||
href="https://x.com/MaterialUI"
|
||||
aria-label="X"
|
||||
sx={{ alignSelf: 'center' }}
|
||||
>
|
||||
<TwitterIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
size="small"
|
||||
href="https://www.linkedin.com/company/mui/"
|
||||
aria-label="LinkedIn"
|
||||
sx={{ alignSelf: 'center' }}
|
||||
>
|
||||
<LinkedInIcon />
|
||||
</IconButton>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
151
resources/js/pages/home/components/Hero.jsx
Normal file
151
resources/js/pages/home/components/Hero.jsx
Normal file
@ -0,0 +1,151 @@
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
import Container from '@mui/material/Container';
|
||||
import InputLabel from '@mui/material/InputLabel';
|
||||
import Link from '@mui/material/Link';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import visuallyHidden from '@mui/utils/visuallyHidden';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
const StyledBox = styled('div')(({ theme }) => ({
|
||||
alignSelf: 'center',
|
||||
width: '100%',
|
||||
height: 400,
|
||||
marginTop: theme.spacing(8),
|
||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||
outline: '6px solid',
|
||||
outlineColor: 'hsla(220, 25%, 80%, 0.2)',
|
||||
border: '1px solid',
|
||||
borderColor: (theme.vars || theme).palette.grey[200],
|
||||
boxShadow: '0 0 12px 8px hsla(220, 25%, 80%, 0.2)',
|
||||
backgroundImage: `url(${import.meta.env.TEMPLATE_IMAGE_URL || 'https://mui.com'}/static/screenshots/material-ui/getting-started/templates/dashboard.jpg)`,
|
||||
backgroundSize: 'cover',
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
marginTop: theme.spacing(10),
|
||||
height: 700,
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
boxShadow: '0 0 24px 12px hsla(210, 100%, 25%, 0.2)',
|
||||
backgroundImage: `url(${import.meta.env.TEMPLATE_IMAGE_URL || 'https://mui.com'}/static/screenshots/material-ui/getting-started/templates/dashboard-dark.jpg)`,
|
||||
outlineColor: 'hsla(220, 20%, 42%, 0.1)',
|
||||
borderColor: (theme.vars || theme).palette.grey[700],
|
||||
}),
|
||||
}));
|
||||
|
||||
export default function Hero() {
|
||||
return (
|
||||
<Box
|
||||
id="hero"
|
||||
sx={(theme) => ({
|
||||
width: '100%',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
|
||||
backgroundImage:
|
||||
'radial-gradient(ellipse 80% 50% at 50% -20%, hsl(210, 100%, 90%), transparent)',
|
||||
...theme.applyStyles('dark', {
|
||||
backgroundImage:
|
||||
'radial-gradient(ellipse 80% 50% at 50% -20%, hsl(210, 100%, 16%), transparent)',
|
||||
}),
|
||||
})}
|
||||
>
|
||||
<Container
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
pt: { xs: 14, sm: 20 },
|
||||
pb: { xs: 8, sm: 12 },
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
spacing={2}
|
||||
useFlexGap
|
||||
sx={{ alignItems: 'center', width: { xs: '100%', sm: '70%' } }}
|
||||
>
|
||||
<Typography
|
||||
variant="h1"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: { xs: 'column', sm: 'row' },
|
||||
alignItems: 'center',
|
||||
fontSize: 'clamp(3rem, 10vw, 3.5rem)',
|
||||
}}
|
||||
>
|
||||
Our latest
|
||||
<Typography
|
||||
component="span"
|
||||
variant="h1"
|
||||
sx={(theme) => ({
|
||||
fontSize: 'inherit',
|
||||
color: 'primary.main',
|
||||
...theme.applyStyles('dark', {
|
||||
color: 'primary.light',
|
||||
}),
|
||||
})}
|
||||
>
|
||||
products
|
||||
</Typography>
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
textAlign: 'center',
|
||||
color: 'text.secondary',
|
||||
width: { sm: '100%', md: '80%' },
|
||||
}}
|
||||
>
|
||||
Explore our cutting-edge dashboard, delivering high-quality solutions
|
||||
tailored to your needs. Elevate your experience with top-tier features
|
||||
and services.
|
||||
</Typography>
|
||||
<Stack
|
||||
direction={{ xs: 'column', sm: 'row' }}
|
||||
spacing={1}
|
||||
useFlexGap
|
||||
sx={{ pt: 2, width: { xs: '100%', sm: '350px' } }}
|
||||
>
|
||||
<InputLabel htmlFor="email-hero" sx={visuallyHidden}>
|
||||
Email
|
||||
</InputLabel>
|
||||
<TextField
|
||||
id="email-hero"
|
||||
hiddenLabel
|
||||
size="small"
|
||||
variant="outlined"
|
||||
aria-label="Enter your email address"
|
||||
placeholder="Your email address"
|
||||
fullWidth
|
||||
slotProps={{
|
||||
htmlInput: {
|
||||
autoComplete: 'off',
|
||||
'aria-label': 'Enter your email address',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
size="small"
|
||||
sx={{ minWidth: 'fit-content' }}
|
||||
>
|
||||
Start now
|
||||
</Button>
|
||||
</Stack>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="text.secondary"
|
||||
sx={{ textAlign: 'center' }}
|
||||
>
|
||||
By clicking "Start now" you agree to our
|
||||
<Link href="#" color="primary">
|
||||
Terms & Conditions
|
||||
</Link>
|
||||
.
|
||||
</Typography>
|
||||
</Stack>
|
||||
<StyledBox id="image" />
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
120
resources/js/pages/home/components/Highlights.jsx
Normal file
120
resources/js/pages/home/components/Highlights.jsx
Normal file
@ -0,0 +1,120 @@
|
||||
import Box from '@mui/material/Box';
|
||||
import Card from '@mui/material/Card';
|
||||
import Container from '@mui/material/Container';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Stack from '@mui/material/Stack';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import AutoFixHighRoundedIcon from '@mui/icons-material/AutoFixHighRounded';
|
||||
import ConstructionRoundedIcon from '@mui/icons-material/ConstructionRounded';
|
||||
import QueryStatsRoundedIcon from '@mui/icons-material/QueryStatsRounded';
|
||||
import SettingsSuggestRoundedIcon from '@mui/icons-material/SettingsSuggestRounded';
|
||||
import SupportAgentRoundedIcon from '@mui/icons-material/SupportAgentRounded';
|
||||
import ThumbUpAltRoundedIcon from '@mui/icons-material/ThumbUpAltRounded';
|
||||
|
||||
const items = [
|
||||
{
|
||||
icon: <SettingsSuggestRoundedIcon />,
|
||||
title: 'Adaptable performance',
|
||||
description:
|
||||
'Our product effortlessly adjusts to your needs, boosting efficiency and simplifying your tasks.',
|
||||
},
|
||||
{
|
||||
icon: <ConstructionRoundedIcon />,
|
||||
title: 'Built to last',
|
||||
description:
|
||||
'Experience unmatched durability that goes above and beyond with lasting investment.',
|
||||
},
|
||||
{
|
||||
icon: <ThumbUpAltRoundedIcon />,
|
||||
title: 'Great user experience',
|
||||
description:
|
||||
'Integrate our product into your routine with an intuitive and easy-to-use interface.',
|
||||
},
|
||||
{
|
||||
icon: <AutoFixHighRoundedIcon />,
|
||||
title: 'Innovative functionality',
|
||||
description:
|
||||
'Stay ahead with features that set new standards, addressing your evolving needs better than the rest.',
|
||||
},
|
||||
{
|
||||
icon: <SupportAgentRoundedIcon />,
|
||||
title: 'Reliable support',
|
||||
description:
|
||||
'Count on our responsive customer support, offering assistance that goes beyond the purchase.',
|
||||
},
|
||||
{
|
||||
icon: <QueryStatsRoundedIcon />,
|
||||
title: 'Precision in every detail',
|
||||
description:
|
||||
'Enjoy a meticulously crafted product where small touches make a significant impact on your overall experience.',
|
||||
},
|
||||
];
|
||||
|
||||
export default function Highlights() {
|
||||
return (
|
||||
<Box
|
||||
id="highlights"
|
||||
sx={{
|
||||
pt: { xs: 4, sm: 12 },
|
||||
pb: { xs: 8, sm: 16 },
|
||||
color: 'white',
|
||||
bgcolor: 'grey.900',
|
||||
}}
|
||||
>
|
||||
<Container
|
||||
sx={{
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: { xs: 3, sm: 6 },
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: { sm: '100%', md: '60%' },
|
||||
textAlign: { sm: 'left', md: 'center' },
|
||||
}}
|
||||
>
|
||||
<Typography component="h2" variant="h4" gutterBottom>
|
||||
Highlights
|
||||
</Typography>
|
||||
<Typography variant="body1" sx={{ color: 'grey.400' }}>
|
||||
Explore why our product stands out: adaptability, durability,
|
||||
user-friendly design, and innovation. Enjoy reliable customer support and
|
||||
precision in every detail.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Grid container spacing={2}>
|
||||
{items.map((item, index) => (
|
||||
<Grid size={{ xs: 12, sm: 6, md: 4 }} key={index}>
|
||||
<Stack
|
||||
direction="column"
|
||||
component={Card}
|
||||
spacing={1}
|
||||
useFlexGap
|
||||
sx={{
|
||||
color: 'inherit',
|
||||
p: 3,
|
||||
height: '100%',
|
||||
borderColor: 'hsla(220, 25%, 25%, 0.3)',
|
||||
backgroundColor: 'grey.800',
|
||||
}}
|
||||
>
|
||||
<Box sx={{ opacity: '50%' }}>{item.icon}</Box>
|
||||
<div>
|
||||
<Typography gutterBottom sx={{ fontWeight: 'medium' }}>
|
||||
{item.title}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ color: 'grey.400' }}>
|
||||
{item.description}
|
||||
</Typography>
|
||||
</div>
|
||||
</Stack>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Container>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
9
resources/js/pages/home/components/Logo.jsx
Normal file
9
resources/js/pages/home/components/Logo.jsx
Normal file
@ -0,0 +1,9 @@
|
||||
export default function Logo() {
|
||||
return (
|
||||
<img
|
||||
src="/assets/img/logo/icon-with-text.png"
|
||||
alt="Logo"
|
||||
className="h-[32px] w-auto mr-4 block object-contain"
|
||||
/>
|
||||
);
|
||||
}
|
||||
69
resources/js/pages/home/components/LogoCollection.jsx
Normal file
69
resources/js/pages/home/components/LogoCollection.jsx
Normal file
@ -0,0 +1,69 @@
|
||||
import Box from '@mui/material/Box';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import { useColorScheme } from '@mui/material/styles';
|
||||
|
||||
const darkModeLogos = [
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/6560628e8573c43893fe0ace_Sydney-white.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f4d520d0517ae8e8ddf13_Bern-white.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f46794c159024c1af6d44_Montreal-white.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/61f12e891fa22f89efd7477a_TerraLight.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/6560a09d1f6337b1dfed14ab_colorado-white.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f5caa77bf7d69fb78792e_Ankara-white.svg',
|
||||
];
|
||||
|
||||
const lightModeLogos = [
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/6560628889c3bdf1129952dc_Sydney-black.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f4d4d8b829a89976a419c_Bern-black.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f467502f091ccb929529d_Montreal-black.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/61f12e911fa22f2203d7514c_TerraDark.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/6560a0990f3717787fd49245_colorado-black.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f5ca4e548b0deb1041c33_Ankara-black.svg',
|
||||
];
|
||||
|
||||
const logoStyle = {
|
||||
width: '100px',
|
||||
height: '80px',
|
||||
margin: '0 32px',
|
||||
opacity: 0.7,
|
||||
};
|
||||
|
||||
export default function LogoCollection() {
|
||||
const { mode, systemMode } = useColorScheme();
|
||||
let logos;
|
||||
if (mode === 'system') {
|
||||
if (systemMode === 'light') {
|
||||
logos = lightModeLogos;
|
||||
} else {
|
||||
logos = darkModeLogos;
|
||||
}
|
||||
} else if (mode === 'light') {
|
||||
logos = lightModeLogos;
|
||||
} else {
|
||||
logos = darkModeLogos;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box id="logoCollection" sx={{ py: 4 }}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="subtitle2"
|
||||
align="center"
|
||||
sx={{ color: 'text.secondary' }}
|
||||
>
|
||||
Trusted by the best companies
|
||||
</Typography>
|
||||
<Grid container sx={{ justifyContent: 'center', mt: 0.5, opacity: 0.6 }}>
|
||||
{logos.map((logo, index) => (
|
||||
<Grid key={index}>
|
||||
<img
|
||||
src={logo}
|
||||
alt={`Fake company number ${index + 1}`}
|
||||
style={logoStyle}
|
||||
/>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
142
resources/js/pages/home/components/Navbar.jsx
Normal file
142
resources/js/pages/home/components/Navbar.jsx
Normal file
@ -0,0 +1,142 @@
|
||||
import * as React from 'react';
|
||||
import { styled, alpha } from '@mui/material/styles';
|
||||
import Box from '@mui/material/Box';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import Button from '@mui/material/Button';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Container from '@mui/material/Container';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import Drawer from '@mui/material/Drawer';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import CloseRoundedIcon from '@mui/icons-material/CloseRounded';
|
||||
import ColorModeIconDropdown from '../../theme/ColorModeIconDropdown';
|
||||
import Logo from './Logo';
|
||||
|
||||
const StyledToolbar = styled(Toolbar)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
flexShrink: 0,
|
||||
borderRadius: `calc(${theme.shape.borderRadius}px + 8px)`,
|
||||
backdropFilter: 'blur(24px)',
|
||||
border: '1px solid',
|
||||
borderColor: (theme.vars || theme).palette.divider,
|
||||
backgroundColor: theme.vars
|
||||
? `rgba(${theme.vars.palette.background.defaultChannel} / 0.4)`
|
||||
: alpha(theme.palette.background.default, 0.4),
|
||||
boxShadow: (theme.vars || theme).shadows[1],
|
||||
padding: '8px 12px',
|
||||
}));
|
||||
|
||||
export default function Navbar() {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
|
||||
const toggleDrawer = (newOpen) => () => {
|
||||
setOpen(newOpen);
|
||||
};
|
||||
|
||||
return (
|
||||
<AppBar
|
||||
position="fixed"
|
||||
enableColorOnDark
|
||||
sx={{
|
||||
boxShadow: 0,
|
||||
bgcolor: 'transparent',
|
||||
backgroundImage: 'none',
|
||||
mt: 'calc(var(--template-frame-height, 0px) + 28px)',
|
||||
}}
|
||||
>
|
||||
<Container maxWidth="lg">
|
||||
<StyledToolbar variant="dense" disableGutters>
|
||||
<Box sx={{ flexGrow: 1, display: 'flex', alignItems: 'center', px: 0 }}>
|
||||
<Logo />
|
||||
<Box sx={{ display: { xs: 'none', md: 'flex' } }}>
|
||||
<Button variant="text" color="info" size="small">
|
||||
Features
|
||||
</Button>
|
||||
<Button variant="text" color="info" size="small">
|
||||
Testimonials
|
||||
</Button>
|
||||
<Button variant="text" color="info" size="small">
|
||||
Highlights
|
||||
</Button>
|
||||
<Button variant="text" color="info" size="small">
|
||||
Pricing
|
||||
</Button>
|
||||
<Button variant="text" color="info" size="small" sx={{ minWidth: 0 }}>
|
||||
FAQ
|
||||
</Button>
|
||||
<Button variant="text" color="info" size="small" sx={{ minWidth: 0 }}>
|
||||
Blog
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: { xs: 'none', md: 'flex' },
|
||||
gap: 1,
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Button color="primary" variant="text" size="small">
|
||||
Sign in
|
||||
</Button>
|
||||
<Button color="primary" variant="contained" size="small">
|
||||
Sign up
|
||||
</Button>
|
||||
<ColorModeIconDropdown />
|
||||
</Box>
|
||||
<Box sx={{ display: { xs: 'flex', md: 'none' }, gap: 1 }}>
|
||||
<ColorModeIconDropdown size="medium" />
|
||||
<IconButton aria-label="Menu button" onClick={toggleDrawer(true)}>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Drawer
|
||||
anchor="top"
|
||||
open={open}
|
||||
onClose={toggleDrawer(false)}
|
||||
PaperProps={{
|
||||
sx: {
|
||||
top: 'var(--template-frame-height, 0px)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box sx={{ p: 2, backgroundColor: 'background.default' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<IconButton onClick={toggleDrawer(false)}>
|
||||
<CloseRoundedIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
<MenuItem>Features</MenuItem>
|
||||
<MenuItem>Testimonials</MenuItem>
|
||||
<MenuItem>Highlights</MenuItem>
|
||||
<MenuItem>Pricing</MenuItem>
|
||||
<MenuItem>FAQ</MenuItem>
|
||||
<MenuItem>Blog</MenuItem>
|
||||
<Divider sx={{ my: 3 }} />
|
||||
<MenuItem>
|
||||
<Button color="primary" variant="contained" fullWidth>
|
||||
Sign up
|
||||
</Button>
|
||||
</MenuItem>
|
||||
<MenuItem>
|
||||
<Button color="primary" variant="outlined" fullWidth>
|
||||
Sign in
|
||||
</Button>
|
||||
</MenuItem>
|
||||
</Box>
|
||||
</Drawer>
|
||||
</Box>
|
||||
</StyledToolbar>
|
||||
</Container>
|
||||
</AppBar>
|
||||
);
|
||||
}
|
||||
211
resources/js/pages/home/components/Pricing.jsx
Normal file
211
resources/js/pages/home/components/Pricing.jsx
Normal file
@ -0,0 +1,211 @@
|
||||
import Box from '@mui/material/Box';
|
||||
import Button from '@mui/material/Button';
|
||||
import Card from '@mui/material/Card';
|
||||
import Chip from '@mui/material/Chip';
|
||||
import CardActions from '@mui/material/CardActions';
|
||||
import CardContent from '@mui/material/CardContent';
|
||||
import Container from '@mui/material/Container';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
|
||||
import CheckCircleRoundedIcon from '@mui/icons-material/CheckCircleRounded';
|
||||
|
||||
const tiers = [
|
||||
{
|
||||
title: 'Free',
|
||||
price: '0',
|
||||
description: [
|
||||
'10 users included',
|
||||
'2 GB of storage',
|
||||
'Help center access',
|
||||
'Email support',
|
||||
],
|
||||
buttonText: 'Sign up for free',
|
||||
buttonVariant: 'outlined',
|
||||
buttonColor: 'primary',
|
||||
},
|
||||
{
|
||||
title: 'Professional',
|
||||
subheader: 'Recommended',
|
||||
price: '15',
|
||||
description: [
|
||||
'20 users included',
|
||||
'10 GB of storage',
|
||||
'Help center access',
|
||||
'Priority email support',
|
||||
'Dedicated team',
|
||||
'Best deals',
|
||||
],
|
||||
buttonText: 'Start now',
|
||||
buttonVariant: 'contained',
|
||||
buttonColor: 'secondary',
|
||||
},
|
||||
{
|
||||
title: 'Enterprise',
|
||||
price: '30',
|
||||
description: [
|
||||
'50 users included',
|
||||
'30 GB of storage',
|
||||
'Help center access',
|
||||
'Phone & email support',
|
||||
],
|
||||
buttonText: 'Contact us',
|
||||
buttonVariant: 'outlined',
|
||||
buttonColor: 'primary',
|
||||
},
|
||||
];
|
||||
|
||||
export default function Pricing() {
|
||||
return (
|
||||
<Container
|
||||
id="pricing"
|
||||
sx={{
|
||||
pt: { xs: 4, sm: 12 },
|
||||
pb: { xs: 8, sm: 16 },
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: { xs: 3, sm: 6 },
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: { sm: '100%', md: '60%' },
|
||||
textAlign: { sm: 'left', md: 'center' },
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h4"
|
||||
gutterBottom
|
||||
sx={{ color: 'text.primary' }}
|
||||
>
|
||||
Pricing
|
||||
</Typography>
|
||||
<Typography variant="body1" sx={{ color: 'text.secondary' }}>
|
||||
Quickly build an effective pricing table for your potential customers with
|
||||
this layout. <br />
|
||||
It's built with default Material UI components with little
|
||||
customization.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Grid
|
||||
container
|
||||
spacing={3}
|
||||
sx={{ alignItems: 'center', justifyContent: 'center', width: '100%' }}
|
||||
>
|
||||
{tiers.map((tier) => (
|
||||
<Grid
|
||||
size={{ xs: 12, sm: tier.title === 'Enterprise' ? 12 : 6, md: 4 }}
|
||||
key={tier.title}
|
||||
>
|
||||
<Card
|
||||
sx={[
|
||||
{
|
||||
p: 2,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 4,
|
||||
},
|
||||
tier.title === 'Professional' &&
|
||||
((theme) => ({
|
||||
border: 'none',
|
||||
background:
|
||||
'radial-gradient(circle at 50% 0%, hsl(220, 20%, 35%), hsl(220, 30%, 6%))',
|
||||
boxShadow: `0 8px 12px hsla(220, 20%, 42%, 0.2)`,
|
||||
...theme.applyStyles('dark', {
|
||||
background:
|
||||
'radial-gradient(circle at 50% 0%, hsl(220, 20%, 20%), hsl(220, 30%, 16%))',
|
||||
boxShadow: `0 8px 12px hsla(0, 0%, 0%, 0.8)`,
|
||||
}),
|
||||
})),
|
||||
]}
|
||||
>
|
||||
<CardContent>
|
||||
<Box
|
||||
sx={[
|
||||
{
|
||||
mb: 1,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
gap: 2,
|
||||
},
|
||||
tier.title === 'Professional'
|
||||
? { color: 'grey.100' }
|
||||
: { color: '' },
|
||||
]}
|
||||
>
|
||||
<Typography component="h3" variant="h6">
|
||||
{tier.title}
|
||||
</Typography>
|
||||
{tier.title === 'Professional' && (
|
||||
<Chip icon={<AutoAwesomeIcon />} label={tier.subheader} />
|
||||
)}
|
||||
</Box>
|
||||
<Box
|
||||
sx={[
|
||||
{
|
||||
display: 'flex',
|
||||
alignItems: 'baseline',
|
||||
},
|
||||
tier.title === 'Professional'
|
||||
? { color: 'grey.50' }
|
||||
: { color: null },
|
||||
]}
|
||||
>
|
||||
<Typography component="h3" variant="h2">
|
||||
${tier.price}
|
||||
</Typography>
|
||||
<Typography component="h3" variant="h6">
|
||||
per month
|
||||
</Typography>
|
||||
</Box>
|
||||
<Divider sx={{ my: 2, opacity: 0.8, borderColor: 'divider' }} />
|
||||
{tier.description.map((line) => (
|
||||
<Box
|
||||
key={line}
|
||||
sx={{ py: 1, display: 'flex', gap: 1.5, alignItems: 'center' }}
|
||||
>
|
||||
<CheckCircleRoundedIcon
|
||||
sx={[
|
||||
{
|
||||
width: 20,
|
||||
},
|
||||
tier.title === 'Professional'
|
||||
? { color: 'primary.light' }
|
||||
: { color: 'primary.main' },
|
||||
]}
|
||||
/>
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
component={'span'}
|
||||
sx={[
|
||||
tier.title === 'Professional'
|
||||
? { color: 'grey.50' }
|
||||
: { color: null },
|
||||
]}
|
||||
>
|
||||
{line}
|
||||
</Typography>
|
||||
</Box>
|
||||
))}
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
<Button
|
||||
fullWidth
|
||||
variant={tier.buttonVariant}
|
||||
color={tier.buttonColor}
|
||||
>
|
||||
{tier.buttonText}
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
173
resources/js/pages/home/components/Testimonials.jsx
Normal file
173
resources/js/pages/home/components/Testimonials.jsx
Normal file
@ -0,0 +1,173 @@
|
||||
import Card from '@mui/material/Card';
|
||||
import CardHeader from '@mui/material/CardHeader';
|
||||
import CardContent from '@mui/material/CardContent';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Box from '@mui/material/Box';
|
||||
import Container from '@mui/material/Container';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import { useColorScheme } from '@mui/material/styles';
|
||||
|
||||
const userTestimonials = [
|
||||
{
|
||||
avatar: <Avatar alt="Remy Sharp" src="/static/images/avatar/1.jpg" />,
|
||||
name: 'Remy Sharp',
|
||||
occupation: 'Senior Engineer',
|
||||
testimonial:
|
||||
"I absolutely love how versatile this product is! Whether I'm tackling work projects or indulging in my favorite hobbies, it seamlessly adapts to my changing needs. Its intuitive design has truly enhanced my daily routine, making tasks more efficient and enjoyable.",
|
||||
},
|
||||
{
|
||||
avatar: <Avatar alt="Travis Howard" src="/static/images/avatar/2.jpg" />,
|
||||
name: 'Travis Howard',
|
||||
occupation: 'Lead Product Designer',
|
||||
testimonial:
|
||||
"One of the standout features of this product is the exceptional customer support. In my experience, the team behind this product has been quick to respond and incredibly helpful. It's reassuring to know that they stand firmly behind their product.",
|
||||
},
|
||||
{
|
||||
avatar: <Avatar alt="Cindy Baker" src="/static/images/avatar/3.jpg" />,
|
||||
name: 'Cindy Baker',
|
||||
occupation: 'CTO',
|
||||
testimonial:
|
||||
'The level of simplicity and user-friendliness in this product has significantly simplified my life. I appreciate the creators for delivering a solution that not only meets but exceeds user expectations.',
|
||||
},
|
||||
{
|
||||
avatar: <Avatar alt="Remy Sharp" src="/static/images/avatar/4.jpg" />,
|
||||
name: 'Julia Stewart',
|
||||
occupation: 'Senior Engineer',
|
||||
testimonial:
|
||||
"I appreciate the attention to detail in the design of this product. The small touches make a big difference, and it's evident that the creators focused on delivering a premium experience.",
|
||||
},
|
||||
{
|
||||
avatar: <Avatar alt="Travis Howard" src="/static/images/avatar/5.jpg" />,
|
||||
name: 'John Smith',
|
||||
occupation: 'Product Designer',
|
||||
testimonial:
|
||||
"I've tried other similar products, but this one stands out for its innovative features. It's clear that the makers put a lot of thought into creating a solution that truly addresses user needs.",
|
||||
},
|
||||
{
|
||||
avatar: <Avatar alt="Cindy Baker" src="/static/images/avatar/6.jpg" />,
|
||||
name: 'Daniel Wolf',
|
||||
occupation: 'CDO',
|
||||
testimonial:
|
||||
"The quality of this product exceeded my expectations. It's durable, well-designed, and built to last. Definitely worth the investment!",
|
||||
},
|
||||
];
|
||||
|
||||
const darkModeLogos = [
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/6560628e8573c43893fe0ace_Sydney-white.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f4d520d0517ae8e8ddf13_Bern-white.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f46794c159024c1af6d44_Montreal-white.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/61f12e891fa22f89efd7477a_TerraLight.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/6560a09d1f6337b1dfed14ab_colorado-white.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f5caa77bf7d69fb78792e_Ankara-white.svg',
|
||||
];
|
||||
|
||||
const lightModeLogos = [
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/6560628889c3bdf1129952dc_Sydney-black.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f4d4d8b829a89976a419c_Bern-black.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f467502f091ccb929529d_Montreal-black.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/61f12e911fa22f2203d7514c_TerraDark.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/6560a0990f3717787fd49245_colorado-black.svg',
|
||||
'https://assets-global.website-files.com/61ed56ae9da9fd7e0ef0a967/655f5ca4e548b0deb1041c33_Ankara-black.svg',
|
||||
];
|
||||
|
||||
const logoStyle = {
|
||||
width: '64px',
|
||||
opacity: 0.3,
|
||||
};
|
||||
|
||||
export default function Testimonials() {
|
||||
const { mode, systemMode } = useColorScheme();
|
||||
|
||||
let logos;
|
||||
if (mode === 'system') {
|
||||
if (systemMode === 'light') {
|
||||
logos = lightModeLogos;
|
||||
} else {
|
||||
logos = darkModeLogos;
|
||||
}
|
||||
} else if (mode === 'light') {
|
||||
logos = lightModeLogos;
|
||||
} else {
|
||||
logos = darkModeLogos;
|
||||
}
|
||||
|
||||
return (
|
||||
<Container
|
||||
id="testimonials"
|
||||
sx={{
|
||||
pt: { xs: 4, sm: 12 },
|
||||
pb: { xs: 8, sm: 16 },
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: { xs: 3, sm: 6 },
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: { sm: '100%', md: '60%' },
|
||||
textAlign: { sm: 'left', md: 'center' },
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h4"
|
||||
gutterBottom
|
||||
sx={{ color: 'text.primary' }}
|
||||
>
|
||||
Testimonials
|
||||
</Typography>
|
||||
<Typography variant="body1" sx={{ color: 'text.secondary' }}>
|
||||
See what our customers love about our products. Discover how we excel in
|
||||
efficiency, durability, and satisfaction. Join us for quality, innovation,
|
||||
and reliable support.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Grid container spacing={2}>
|
||||
{userTestimonials.map((testimonial, index) => (
|
||||
<Grid size={{ xs: 12, sm: 6, md: 4 }} key={index} sx={{ display: 'flex' }}>
|
||||
<Card
|
||||
variant="outlined"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
flexGrow: 1,
|
||||
}}
|
||||
>
|
||||
<CardContent>
|
||||
<Typography
|
||||
variant="body1"
|
||||
gutterBottom
|
||||
sx={{ color: 'text.secondary' }}
|
||||
>
|
||||
{testimonial.testimonial}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<CardHeader
|
||||
avatar={testimonial.avatar}
|
||||
title={testimonial.name}
|
||||
subheader={testimonial.occupation}
|
||||
/>
|
||||
<img
|
||||
src={logos[index]}
|
||||
alt={`Logo ${index + 1}`}
|
||||
style={logoStyle}
|
||||
/>
|
||||
</Box>
|
||||
</Card>
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
14
resources/js/pages/layouts/Layout.jsx
Normal file
14
resources/js/pages/layouts/Layout.jsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { Link } from '@inertiajs/react'
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return (
|
||||
<main>
|
||||
<header>
|
||||
<Link href="/">Home</Link>
|
||||
<Link href="/about">About</Link>
|
||||
<Link href="/contact">Contact</Link>
|
||||
</header>
|
||||
<article>{children}</article>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
9
resources/js/pages/posts/CreatePostPage.jsx
Normal file
9
resources/js/pages/posts/CreatePostPage.jsx
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function CreatePostPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Create Post</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
9
resources/js/pages/posts/EditPostPage.jsx
Normal file
9
resources/js/pages/posts/EditPostPage.jsx
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function EditPostPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Edit Post</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
9
resources/js/pages/posts/PostsIndexPage.jsx
Normal file
9
resources/js/pages/posts/PostsIndexPage.jsx
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function PostsIndexPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Posts Index</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
5
resources/js/pages/posts/components/PublishStatus.jsx
Normal file
5
resources/js/pages/posts/components/PublishStatus.jsx
Normal file
@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function PublishStatus() {
|
||||
return <span>Published</span>;
|
||||
}
|
||||
3
resources/js/pages/posts/helpers/generateSlug.js
Normal file
3
resources/js/pages/posts/helpers/generateSlug.js
Normal file
@ -0,0 +1,3 @@
|
||||
export const generateSlug = (title: string) => {
|
||||
return title.toLowerCase().replace(/ /g, '-').replace(/[^\w-]+/g, '');
|
||||
};
|
||||
9
resources/js/pages/profile/ProfilePage.jsx
Normal file
9
resources/js/pages/profile/ProfilePage.jsx
Normal file
@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function ProfilePage() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Profile</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
10
resources/js/pages/profile/layouts/ProfileLayout.jsx
Normal file
10
resources/js/pages/profile/layouts/ProfileLayout.jsx
Normal file
@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function ProfileLayout({ children }) {
|
||||
return (
|
||||
<div className="profile-layout">
|
||||
<aside>Profile Sidebar</aside>
|
||||
<main>{children}</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
43
resources/js/pages/theme/AppTheme.jsx
Normal file
43
resources/js/pages/theme/AppTheme.jsx
Normal file
@ -0,0 +1,43 @@
|
||||
import * as React from 'react';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import { inputsCustomizations } from './customizations/inputs';
|
||||
import { dataDisplayCustomizations } from './customizations/dataDisplay';
|
||||
import { feedbackCustomizations } from './customizations/feedback';
|
||||
import { navigationCustomizations } from './customizations/navigation';
|
||||
import { surfacesCustomizations } from './customizations/surfaces';
|
||||
import { colorSchemes, typography, shadows, shape } from './themePrimitives';
|
||||
|
||||
export default function AppTheme(props) {
|
||||
const { children, disableCustomTheme, themeComponents } = props;
|
||||
const theme = React.useMemo(() => {
|
||||
return disableCustomTheme
|
||||
? {}
|
||||
: createTheme({
|
||||
// For more details about CSS variables configuration, see https://mui.com/material-ui/customization/css-theme-variables/configuration/
|
||||
cssVariables: {
|
||||
colorSchemeSelector: 'data-mui-color-scheme',
|
||||
cssVarPrefix: 'template',
|
||||
},
|
||||
colorSchemes, // Recently added in v6 for building light & dark mode app, see https://mui.com/material-ui/customization/palette/#color-schemes
|
||||
typography,
|
||||
shadows,
|
||||
shape,
|
||||
components: {
|
||||
...inputsCustomizations,
|
||||
...dataDisplayCustomizations,
|
||||
...feedbackCustomizations,
|
||||
...navigationCustomizations,
|
||||
...surfacesCustomizations,
|
||||
...themeComponents,
|
||||
},
|
||||
});
|
||||
}, [disableCustomTheme, themeComponents]);
|
||||
if (disableCustomTheme) {
|
||||
return <React.Fragment>{children}</React.Fragment>;
|
||||
}
|
||||
return (
|
||||
<ThemeProvider theme={theme} disableTransitionOnChange>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
89
resources/js/pages/theme/ColorModeIconDropdown.jsx
Normal file
89
resources/js/pages/theme/ColorModeIconDropdown.jsx
Normal file
@ -0,0 +1,89 @@
|
||||
import * as React from 'react';
|
||||
import DarkModeIcon from '@mui/icons-material/DarkModeRounded';
|
||||
import LightModeIcon from '@mui/icons-material/LightModeRounded';
|
||||
import Box from '@mui/material/Box';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Menu from '@mui/material/Menu';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import { useColorScheme } from '@mui/material/styles';
|
||||
|
||||
export default function ColorModeIconDropdown(props) {
|
||||
const { mode, systemMode, setMode } = useColorScheme();
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
const open = Boolean(anchorEl);
|
||||
const handleClick = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
const handleClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
const handleMode = (targetMode) => () => {
|
||||
setMode(targetMode);
|
||||
handleClose();
|
||||
};
|
||||
if (!mode) {
|
||||
return (
|
||||
<Box
|
||||
data-screenshot="toggle-mode"
|
||||
sx={(theme) => ({
|
||||
verticalAlign: 'bottom',
|
||||
display: 'inline-flex',
|
||||
width: '2.25rem',
|
||||
height: '2.25rem',
|
||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||
border: '1px solid',
|
||||
borderColor: (theme.vars || theme).palette.divider,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
const resolvedMode = (systemMode || mode);
|
||||
const icon = {
|
||||
light: <LightModeIcon />,
|
||||
dark: <DarkModeIcon />,
|
||||
}[resolvedMode];
|
||||
return (
|
||||
<React.Fragment>
|
||||
<IconButton
|
||||
data-screenshot="toggle-mode"
|
||||
onClick={handleClick}
|
||||
disableRipple
|
||||
size="small"
|
||||
aria-controls={open ? 'color-scheme-menu' : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={open ? 'true' : undefined}
|
||||
{...props}
|
||||
>
|
||||
{icon}
|
||||
</IconButton>
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
id="account-menu"
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
onClick={handleClose}
|
||||
slotProps={{
|
||||
paper: {
|
||||
variant: 'outlined',
|
||||
elevation: 0,
|
||||
sx: {
|
||||
my: '4px',
|
||||
},
|
||||
},
|
||||
}}
|
||||
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
|
||||
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
||||
>
|
||||
<MenuItem selected={mode === 'system'} onClick={handleMode('system')}>
|
||||
System
|
||||
</MenuItem>
|
||||
<MenuItem selected={mode === 'light'} onClick={handleMode('light')}>
|
||||
Light
|
||||
</MenuItem>
|
||||
<MenuItem selected={mode === 'dark'} onClick={handleMode('dark')}>
|
||||
Dark
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
27
resources/js/pages/theme/ColorModeSelect.jsx
Normal file
27
resources/js/pages/theme/ColorModeSelect.jsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { useColorScheme } from '@mui/material/styles';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import Select from '@mui/material/Select';
|
||||
|
||||
export default function ColorModeSelect(props) {
|
||||
const { mode, setMode } = useColorScheme();
|
||||
if (!mode) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Select
|
||||
value={mode}
|
||||
onChange={(event) =>
|
||||
setMode(event.target.value)
|
||||
}
|
||||
SelectDisplayProps={{
|
||||
// @ts-ignore
|
||||
'data-screenshot': 'toggle-mode',
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<MenuItem value="system">System</MenuItem>
|
||||
<MenuItem value="light">Light</MenuItem>
|
||||
<MenuItem value="dark">Dark</MenuItem>
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
233
resources/js/pages/theme/customizations/dataDisplay.jsx
Normal file
233
resources/js/pages/theme/customizations/dataDisplay.jsx
Normal file
@ -0,0 +1,233 @@
|
||||
import { alpha } from '@mui/material/styles';
|
||||
import { svgIconClasses } from '@mui/material/SvgIcon';
|
||||
import { typographyClasses } from '@mui/material/Typography';
|
||||
import { buttonBaseClasses } from '@mui/material/ButtonBase';
|
||||
import { chipClasses } from '@mui/material/Chip';
|
||||
import { iconButtonClasses } from '@mui/material/IconButton';
|
||||
import { gray, red, green } from '../themePrimitives';
|
||||
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export const dataDisplayCustomizations = {
|
||||
MuiList: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
padding: '8px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiListItem: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
[`& .${svgIconClasses.root}`]: {
|
||||
width: '1rem',
|
||||
height: '1rem',
|
||||
color: (theme.vars || theme).palette.text.secondary,
|
||||
},
|
||||
[`& .${typographyClasses.root}`]: {
|
||||
fontWeight: 500,
|
||||
},
|
||||
[`& .${buttonBaseClasses.root}`]: {
|
||||
display: 'flex',
|
||||
gap: 8,
|
||||
padding: '2px 8px',
|
||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||
opacity: 0.7,
|
||||
'&.Mui-selected': {
|
||||
opacity: 1,
|
||||
backgroundColor: alpha(theme.palette.action.selected, 0.3),
|
||||
[`& .${svgIconClasses.root}`]: {
|
||||
color: (theme.vars || theme).palette.text.primary,
|
||||
},
|
||||
'&:focus-visible': {
|
||||
backgroundColor: alpha(theme.palette.action.selected, 0.3),
|
||||
},
|
||||
'&:hover': {
|
||||
backgroundColor: alpha(theme.palette.action.selected, 0.5),
|
||||
},
|
||||
},
|
||||
'&:focus-visible': {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiListItemText: {
|
||||
styleOverrides: {
|
||||
primary: ({ theme }) => ({
|
||||
fontSize: theme.typography.body2.fontSize,
|
||||
fontWeight: 500,
|
||||
lineHeight: theme.typography.body2.lineHeight,
|
||||
}),
|
||||
secondary: ({ theme }) => ({
|
||||
fontSize: theme.typography.caption.fontSize,
|
||||
lineHeight: theme.typography.caption.lineHeight,
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiListSubheader: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
backgroundColor: 'transparent',
|
||||
padding: '4px 8px',
|
||||
fontSize: theme.typography.caption.fontSize,
|
||||
fontWeight: 500,
|
||||
lineHeight: theme.typography.caption.lineHeight,
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiListItemIcon: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
minWidth: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiChip: {
|
||||
defaultProps: {
|
||||
size: 'small',
|
||||
},
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
border: '1px solid',
|
||||
borderRadius: '999px',
|
||||
[`& .${chipClasses.label}`]: {
|
||||
fontWeight: 600,
|
||||
},
|
||||
variants: [
|
||||
{
|
||||
props: {
|
||||
color: 'default',
|
||||
},
|
||||
style: {
|
||||
borderColor: gray[200],
|
||||
backgroundColor: gray[100],
|
||||
[`& .${chipClasses.label}`]: {
|
||||
color: gray[500],
|
||||
},
|
||||
[`& .${chipClasses.icon}`]: {
|
||||
color: gray[500],
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
borderColor: gray[700],
|
||||
backgroundColor: gray[800],
|
||||
[`& .${chipClasses.label}`]: {
|
||||
color: gray[300],
|
||||
},
|
||||
[`& .${chipClasses.icon}`]: {
|
||||
color: gray[300],
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
props: {
|
||||
color: 'success',
|
||||
},
|
||||
style: {
|
||||
borderColor: green[200],
|
||||
backgroundColor: green[50],
|
||||
[`& .${chipClasses.label}`]: {
|
||||
color: green[500],
|
||||
},
|
||||
[`& .${chipClasses.icon}`]: {
|
||||
color: green[500],
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
borderColor: green[800],
|
||||
backgroundColor: green[900],
|
||||
[`& .${chipClasses.label}`]: {
|
||||
color: green[300],
|
||||
},
|
||||
[`& .${chipClasses.icon}`]: {
|
||||
color: green[300],
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
props: {
|
||||
color: 'error',
|
||||
},
|
||||
style: {
|
||||
borderColor: red[100],
|
||||
backgroundColor: red[50],
|
||||
[`& .${chipClasses.label}`]: {
|
||||
color: red[500],
|
||||
},
|
||||
[`& .${chipClasses.icon}`]: {
|
||||
color: red[500],
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
borderColor: red[800],
|
||||
backgroundColor: red[900],
|
||||
[`& .${chipClasses.label}`]: {
|
||||
color: red[200],
|
||||
},
|
||||
[`& .${chipClasses.icon}`]: {
|
||||
color: red[300],
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
props: { size: 'small' },
|
||||
style: {
|
||||
maxHeight: 20,
|
||||
[`& .${chipClasses.label}`]: {
|
||||
fontSize: theme.typography.caption.fontSize,
|
||||
},
|
||||
[`& .${svgIconClasses.root}`]: {
|
||||
fontSize: theme.typography.caption.fontSize,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
props: { size: 'medium' },
|
||||
style: {
|
||||
[`& .${chipClasses.label}`]: {
|
||||
fontSize: theme.typography.caption.fontSize,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiTablePagination: {
|
||||
styleOverrides: {
|
||||
actions: {
|
||||
display: 'flex',
|
||||
gap: 8,
|
||||
marginRight: 6,
|
||||
[`& .${iconButtonClasses.root}`]: {
|
||||
minWidth: 0,
|
||||
width: 36,
|
||||
height: 36,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiIcon: {
|
||||
defaultProps: {
|
||||
fontSize: 'small',
|
||||
},
|
||||
styleOverrides: {
|
||||
root: {
|
||||
variants: [
|
||||
{
|
||||
props: {
|
||||
fontSize: 'small',
|
||||
},
|
||||
style: {
|
||||
fontSize: '1rem',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
46
resources/js/pages/theme/customizations/feedback.jsx
Normal file
46
resources/js/pages/theme/customizations/feedback.jsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { alpha } from '@mui/material/styles';
|
||||
import { gray, orange } from '../themePrimitives';
|
||||
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export const feedbackCustomizations = {
|
||||
MuiAlert: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
borderRadius: 10,
|
||||
backgroundColor: orange[100],
|
||||
color: (theme.vars || theme).palette.text.primary,
|
||||
border: `1px solid ${alpha(orange[300], 0.5)}`,
|
||||
'& .MuiAlert-icon': {
|
||||
color: orange[500],
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
backgroundColor: `${alpha(orange[900], 0.5)}`,
|
||||
border: `1px solid ${alpha(orange[800], 0.5)}`,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiDialog: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
'& .MuiDialog-paper': {
|
||||
borderRadius: '10px',
|
||||
border: '1px solid',
|
||||
borderColor: (theme.vars || theme).palette.divider,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiLinearProgress: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
height: 8,
|
||||
borderRadius: 8,
|
||||
backgroundColor: gray[200],
|
||||
...theme.applyStyles('dark', {
|
||||
backgroundColor: gray[800],
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
444
resources/js/pages/theme/customizations/inputs.jsx
Normal file
444
resources/js/pages/theme/customizations/inputs.jsx
Normal file
@ -0,0 +1,444 @@
|
||||
import { alpha } from '@mui/material/styles';
|
||||
import { outlinedInputClasses } from '@mui/material/OutlinedInput';
|
||||
import { svgIconClasses } from '@mui/material/SvgIcon';
|
||||
import { toggleButtonGroupClasses } from '@mui/material/ToggleButtonGroup';
|
||||
import { toggleButtonClasses } from '@mui/material/ToggleButton';
|
||||
import CheckBoxOutlineBlankRoundedIcon from '@mui/icons-material/CheckBoxOutlineBlankRounded';
|
||||
import CheckRoundedIcon from '@mui/icons-material/CheckRounded';
|
||||
import RemoveRoundedIcon from '@mui/icons-material/RemoveRounded';
|
||||
import { gray, brand } from '../themePrimitives';
|
||||
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export const inputsCustomizations = {
|
||||
MuiButtonBase: {
|
||||
defaultProps: {
|
||||
disableTouchRipple: true,
|
||||
disableRipple: true,
|
||||
},
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
boxSizing: 'border-box',
|
||||
transition: 'all 100ms ease-in',
|
||||
'&:focus-visible': {
|
||||
outline: `3px solid ${alpha(theme.palette.primary.main, 0.5)}`,
|
||||
outlineOffset: '2px',
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiButton: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
boxShadow: 'none',
|
||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||
textTransform: 'none',
|
||||
variants: [
|
||||
{
|
||||
props: {
|
||||
size: 'small',
|
||||
},
|
||||
style: {
|
||||
height: '2.25rem',
|
||||
padding: '8px 12px',
|
||||
},
|
||||
},
|
||||
{
|
||||
props: {
|
||||
size: 'medium',
|
||||
},
|
||||
style: {
|
||||
height: '2.5rem', // 40px
|
||||
},
|
||||
},
|
||||
{
|
||||
props: {
|
||||
color: 'primary',
|
||||
variant: 'contained',
|
||||
},
|
||||
style: {
|
||||
color: 'white',
|
||||
backgroundColor: gray[900],
|
||||
backgroundImage: `linear-gradient(to bottom, ${gray[700]}, ${gray[800]})`,
|
||||
boxShadow: `inset 0 1px 0 ${gray[600]}, inset 0 -1px 0 1px hsl(220, 0%, 0%)`,
|
||||
border: `1px solid ${gray[700]}`,
|
||||
'&:hover': {
|
||||
backgroundImage: 'none',
|
||||
backgroundColor: gray[700],
|
||||
boxShadow: 'none',
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: gray[800],
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
color: 'black',
|
||||
backgroundColor: gray[50],
|
||||
backgroundImage: `linear-gradient(to bottom, ${gray[100]}, ${gray[50]})`,
|
||||
boxShadow: 'inset 0 -1px 0 hsl(220, 30%, 80%)',
|
||||
border: `1px solid ${gray[50]}`,
|
||||
'&:hover': {
|
||||
backgroundImage: 'none',
|
||||
backgroundColor: gray[300],
|
||||
boxShadow: 'none',
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: gray[400],
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
props: {
|
||||
color: 'secondary',
|
||||
variant: 'contained',
|
||||
},
|
||||
style: {
|
||||
color: 'white',
|
||||
backgroundColor: brand[300],
|
||||
backgroundImage: `linear-gradient(to bottom, ${alpha(brand[400], 0.8)}, ${brand[500]})`,
|
||||
boxShadow: `inset 0 2px 0 ${alpha(brand[200], 0.2)}, inset 0 -2px 0 ${alpha(brand[700], 0.4)}`,
|
||||
border: `1px solid ${brand[500]}`,
|
||||
'&:hover': {
|
||||
backgroundColor: brand[700],
|
||||
boxShadow: 'none',
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: brand[700],
|
||||
backgroundImage: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
props: {
|
||||
variant: 'outlined',
|
||||
},
|
||||
style: {
|
||||
color: (theme.vars || theme).palette.text.primary,
|
||||
border: '1px solid',
|
||||
borderColor: gray[200],
|
||||
backgroundColor: alpha(gray[50], 0.3),
|
||||
'&:hover': {
|
||||
backgroundColor: gray[100],
|
||||
borderColor: gray[300],
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: gray[200],
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
backgroundColor: gray[800],
|
||||
borderColor: gray[700],
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: gray[900],
|
||||
borderColor: gray[600],
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: gray[900],
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
props: {
|
||||
color: 'secondary',
|
||||
variant: 'outlined',
|
||||
},
|
||||
style: {
|
||||
color: brand[700],
|
||||
border: '1px solid',
|
||||
borderColor: brand[200],
|
||||
backgroundColor: brand[50],
|
||||
'&:hover': {
|
||||
backgroundColor: brand[100],
|
||||
borderColor: brand[400],
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: alpha(brand[200], 0.7),
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
color: brand[50],
|
||||
border: '1px solid',
|
||||
borderColor: brand[900],
|
||||
backgroundColor: alpha(brand[900], 0.3),
|
||||
'&:hover': {
|
||||
borderColor: brand[700],
|
||||
backgroundColor: alpha(brand[900], 0.6),
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: alpha(brand[900], 0.5),
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
props: {
|
||||
variant: 'text',
|
||||
},
|
||||
style: {
|
||||
color: gray[600],
|
||||
'&:hover': {
|
||||
backgroundColor: gray[100],
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: gray[200],
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
color: gray[50],
|
||||
'&:hover': {
|
||||
backgroundColor: gray[700],
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: alpha(gray[700], 0.7),
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
props: {
|
||||
color: 'secondary',
|
||||
variant: 'text',
|
||||
},
|
||||
style: {
|
||||
color: brand[700],
|
||||
'&:hover': {
|
||||
backgroundColor: alpha(brand[100], 0.5),
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: alpha(brand[200], 0.7),
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
color: brand[100],
|
||||
'&:hover': {
|
||||
backgroundColor: alpha(brand[900], 0.5),
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: alpha(brand[900], 0.3),
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiIconButton: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
boxShadow: 'none',
|
||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||
textTransform: 'none',
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
letterSpacing: 0,
|
||||
color: (theme.vars || theme).palette.text.primary,
|
||||
border: '1px solid ',
|
||||
borderColor: gray[200],
|
||||
backgroundColor: alpha(gray[50], 0.3),
|
||||
'&:hover': {
|
||||
backgroundColor: gray[100],
|
||||
borderColor: gray[300],
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: gray[200],
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
backgroundColor: gray[800],
|
||||
borderColor: gray[700],
|
||||
'&:hover': {
|
||||
backgroundColor: gray[900],
|
||||
borderColor: gray[600],
|
||||
},
|
||||
'&:active': {
|
||||
backgroundColor: gray[900],
|
||||
},
|
||||
}),
|
||||
variants: [
|
||||
{
|
||||
props: {
|
||||
size: 'small',
|
||||
},
|
||||
style: {
|
||||
width: '2.25rem',
|
||||
height: '2.25rem',
|
||||
padding: '0.25rem',
|
||||
[`& .${svgIconClasses.root}`]: { fontSize: '1rem' },
|
||||
},
|
||||
},
|
||||
{
|
||||
props: {
|
||||
size: 'medium',
|
||||
},
|
||||
style: {
|
||||
width: '2.5rem',
|
||||
height: '2.5rem',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiToggleButtonGroup: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
borderRadius: '10px',
|
||||
boxShadow: `0 4px 16px ${alpha(gray[400], 0.2)}`,
|
||||
[`& .${toggleButtonGroupClasses.selected}`]: {
|
||||
color: brand[500],
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
[`& .${toggleButtonGroupClasses.selected}`]: {
|
||||
color: '#fff',
|
||||
},
|
||||
boxShadow: `0 4px 16px ${alpha(brand[700], 0.5)}`,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiToggleButton: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
padding: '12px 16px',
|
||||
textTransform: 'none',
|
||||
borderRadius: '10px',
|
||||
fontWeight: 500,
|
||||
...theme.applyStyles('dark', {
|
||||
color: gray[400],
|
||||
boxShadow: '0 4px 16px rgba(0, 0, 0, 0.5)',
|
||||
[`&.${toggleButtonClasses.selected}`]: {
|
||||
color: brand[300],
|
||||
},
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiCheckbox: {
|
||||
defaultProps: {
|
||||
disableRipple: true,
|
||||
icon: (
|
||||
<CheckBoxOutlineBlankRoundedIcon sx={{ color: 'hsla(210, 0%, 0%, 0.0)' }} />
|
||||
),
|
||||
checkedIcon: <CheckRoundedIcon sx={{ height: 14, width: 14 }} />,
|
||||
indeterminateIcon: <RemoveRoundedIcon sx={{ height: 14, width: 14 }} />,
|
||||
},
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
margin: 10,
|
||||
height: 16,
|
||||
width: 16,
|
||||
borderRadius: 5,
|
||||
border: '1px solid ',
|
||||
borderColor: alpha(gray[300], 0.8),
|
||||
boxShadow: '0 0 0 1.5px hsla(210, 0%, 0%, 0.04) inset',
|
||||
backgroundColor: alpha(gray[100], 0.4),
|
||||
transition: 'border-color, background-color, 120ms ease-in',
|
||||
'&:hover': {
|
||||
borderColor: brand[300],
|
||||
},
|
||||
'&.Mui-focusVisible': {
|
||||
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||
outlineOffset: '2px',
|
||||
borderColor: brand[400],
|
||||
},
|
||||
'&.Mui-checked': {
|
||||
color: 'white',
|
||||
backgroundColor: brand[500],
|
||||
borderColor: brand[500],
|
||||
boxShadow: `none`,
|
||||
'&:hover': {
|
||||
backgroundColor: brand[600],
|
||||
},
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
borderColor: alpha(gray[700], 0.8),
|
||||
boxShadow: '0 0 0 1.5px hsl(210, 0%, 0%) inset',
|
||||
backgroundColor: alpha(gray[900], 0.8),
|
||||
'&:hover': {
|
||||
borderColor: brand[300],
|
||||
},
|
||||
'&.Mui-focusVisible': {
|
||||
borderColor: brand[400],
|
||||
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||
outlineOffset: '2px',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiInputBase: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
border: 'none',
|
||||
},
|
||||
input: {
|
||||
'&::placeholder': {
|
||||
opacity: 0.7,
|
||||
color: gray[500],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiOutlinedInput: {
|
||||
styleOverrides: {
|
||||
input: {
|
||||
padding: 0,
|
||||
},
|
||||
root: ({ theme }) => ({
|
||||
padding: '8px 12px',
|
||||
color: (theme.vars || theme).palette.text.primary,
|
||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||
backgroundColor: (theme.vars || theme).palette.background.default,
|
||||
transition: 'border 120ms ease-in',
|
||||
'&:hover': {
|
||||
borderColor: gray[400],
|
||||
},
|
||||
[`&.${outlinedInputClasses.focused}`]: {
|
||||
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||
borderColor: brand[400],
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
'&:hover': {
|
||||
borderColor: gray[500],
|
||||
},
|
||||
}),
|
||||
variants: [
|
||||
{
|
||||
props: {
|
||||
size: 'small',
|
||||
},
|
||||
style: {
|
||||
height: '2.25rem',
|
||||
},
|
||||
},
|
||||
{
|
||||
props: {
|
||||
size: 'medium',
|
||||
},
|
||||
style: {
|
||||
height: '2.5rem',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
notchedOutline: {
|
||||
border: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiInputAdornment: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
color: (theme.vars || theme).palette.grey[500],
|
||||
...theme.applyStyles('dark', {
|
||||
color: (theme.vars || theme).palette.grey[400],
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiFormLabel: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
typography: theme.typography.caption,
|
||||
marginBottom: 8,
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
279
resources/js/pages/theme/customizations/navigation.jsx
Normal file
279
resources/js/pages/theme/customizations/navigation.jsx
Normal file
@ -0,0 +1,279 @@
|
||||
import * as React from 'react';
|
||||
import { alpha } from '@mui/material/styles';
|
||||
|
||||
import { buttonBaseClasses } from '@mui/material/ButtonBase';
|
||||
import { dividerClasses } from '@mui/material/Divider';
|
||||
import { menuItemClasses } from '@mui/material/MenuItem';
|
||||
import { selectClasses } from '@mui/material/Select';
|
||||
import { tabClasses } from '@mui/material/Tab';
|
||||
import UnfoldMoreRoundedIcon from '@mui/icons-material/UnfoldMoreRounded';
|
||||
import { gray, brand } from '../themePrimitives';
|
||||
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export const navigationCustomizations = {
|
||||
MuiMenuItem: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||
padding: '6px 8px',
|
||||
[`&.${menuItemClasses.focusVisible}`]: {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
[`&.${menuItemClasses.selected}`]: {
|
||||
[`&.${menuItemClasses.focusVisible}`]: {
|
||||
backgroundColor: alpha(theme.palette.action.selected, 0.3),
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiMenu: {
|
||||
styleOverrides: {
|
||||
list: {
|
||||
gap: '0px',
|
||||
[`&.${dividerClasses.root}`]: {
|
||||
margin: '0 -8px',
|
||||
},
|
||||
},
|
||||
paper: ({ theme }) => ({
|
||||
marginTop: '4px',
|
||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||
backgroundImage: 'none',
|
||||
background: 'hsl(0, 0%, 100%)',
|
||||
boxShadow:
|
||||
'hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px',
|
||||
[`& .${buttonBaseClasses.root}`]: {
|
||||
'&.Mui-selected': {
|
||||
backgroundColor: alpha(theme.palette.action.selected, 0.3),
|
||||
},
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
background: gray[900],
|
||||
boxShadow:
|
||||
'hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px',
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiSelect: {
|
||||
defaultProps: {
|
||||
IconComponent: React.forwardRef((props, ref) => (
|
||||
<UnfoldMoreRoundedIcon fontSize="small" {...props} ref={ref} />
|
||||
)),
|
||||
},
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||
border: '1px solid',
|
||||
borderColor: gray[200],
|
||||
backgroundColor: (theme.vars || theme).palette.background.paper,
|
||||
boxShadow: `inset 0 1px 0 1px hsla(220, 0%, 100%, 0.6), inset 0 -1px 0 1px hsla(220, 35%, 90%, 0.5)`,
|
||||
'&:hover': {
|
||||
borderColor: gray[300],
|
||||
backgroundColor: (theme.vars || theme).palette.background.paper,
|
||||
boxShadow: 'none',
|
||||
},
|
||||
[`&.${selectClasses.focused}`]: {
|
||||
outlineOffset: 0,
|
||||
borderColor: gray[400],
|
||||
},
|
||||
'&:before, &:after': {
|
||||
display: 'none',
|
||||
},
|
||||
|
||||
...theme.applyStyles('dark', {
|
||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||
borderColor: gray[700],
|
||||
backgroundColor: (theme.vars || theme).palette.background.paper,
|
||||
boxShadow: `inset 0 1px 0 1px ${alpha(gray[700], 0.15)}, inset 0 -1px 0 1px hsla(220, 0%, 0%, 0.7)`,
|
||||
'&:hover': {
|
||||
borderColor: alpha(gray[700], 0.7),
|
||||
backgroundColor: (theme.vars || theme).palette.background.paper,
|
||||
boxShadow: 'none',
|
||||
},
|
||||
[`&.${selectClasses.focused}`]: {
|
||||
outlineOffset: 0,
|
||||
borderColor: gray[900],
|
||||
},
|
||||
'&:before, &:after': {
|
||||
display: 'none',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
select: ({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
...theme.applyStyles('dark', {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
'&:focus-visible': {
|
||||
backgroundColor: gray[900],
|
||||
},
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiLink: {
|
||||
defaultProps: {
|
||||
underline: 'none',
|
||||
},
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
color: (theme.vars || theme).palette.text.primary,
|
||||
fontWeight: 500,
|
||||
position: 'relative',
|
||||
textDecoration: 'none',
|
||||
width: 'fit-content',
|
||||
'&::before': {
|
||||
content: '""',
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '1px',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
backgroundColor: (theme.vars || theme).palette.text.secondary,
|
||||
opacity: 0.3,
|
||||
transition: 'width 0.3s ease, opacity 0.3s ease',
|
||||
},
|
||||
'&:hover::before': {
|
||||
width: 0,
|
||||
},
|
||||
'&:focus-visible': {
|
||||
outline: `3px solid ${alpha(brand[500], 0.5)}`,
|
||||
outlineOffset: '4px',
|
||||
borderRadius: '2px',
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiDrawer: {
|
||||
styleOverrides: {
|
||||
paper: ({ theme }) => ({
|
||||
backgroundColor: (theme.vars || theme).palette.background.default,
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiPaginationItem: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
'&.Mui-selected': {
|
||||
color: 'white',
|
||||
backgroundColor: (theme.vars || theme).palette.grey[900],
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
'&.Mui-selected': {
|
||||
color: 'black',
|
||||
backgroundColor: (theme.vars || theme).palette.grey[50],
|
||||
},
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiTabs: {
|
||||
styleOverrides: {
|
||||
root: { minHeight: 'fit-content' },
|
||||
indicator: ({ theme }) => ({
|
||||
backgroundColor: (theme.vars || theme).palette.grey[800],
|
||||
...theme.applyStyles('dark', {
|
||||
backgroundColor: (theme.vars || theme).palette.grey[200],
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiTab: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
padding: '6px 8px',
|
||||
marginBottom: '8px',
|
||||
textTransform: 'none',
|
||||
minWidth: 'fit-content',
|
||||
minHeight: 'fit-content',
|
||||
color: (theme.vars || theme).palette.text.secondary,
|
||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||
border: '1px solid',
|
||||
borderColor: 'transparent',
|
||||
':hover': {
|
||||
color: (theme.vars || theme).palette.text.primary,
|
||||
backgroundColor: gray[100],
|
||||
borderColor: gray[200],
|
||||
},
|
||||
[`&.${tabClasses.selected}`]: {
|
||||
color: gray[900],
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
':hover': {
|
||||
color: (theme.vars || theme).palette.text.primary,
|
||||
backgroundColor: gray[800],
|
||||
borderColor: gray[700],
|
||||
},
|
||||
[`&.${tabClasses.selected}`]: {
|
||||
color: '#fff',
|
||||
},
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiStepConnector: {
|
||||
styleOverrides: {
|
||||
line: ({ theme }) => ({
|
||||
borderTop: '1px solid',
|
||||
borderColor: (theme.vars || theme).palette.divider,
|
||||
flex: 1,
|
||||
borderRadius: '99px',
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiStepIcon: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
color: 'transparent',
|
||||
border: `1px solid ${gray[400]}`,
|
||||
width: 12,
|
||||
height: 12,
|
||||
borderRadius: '50%',
|
||||
'& text': {
|
||||
display: 'none',
|
||||
},
|
||||
'&.Mui-active': {
|
||||
border: 'none',
|
||||
color: (theme.vars || theme).palette.primary.main,
|
||||
},
|
||||
'&.Mui-completed': {
|
||||
border: 'none',
|
||||
color: (theme.vars || theme).palette.success.main,
|
||||
},
|
||||
...theme.applyStyles('dark', {
|
||||
border: `1px solid ${gray[700]}`,
|
||||
'&.Mui-active': {
|
||||
border: 'none',
|
||||
color: (theme.vars || theme).palette.primary.light,
|
||||
},
|
||||
'&.Mui-completed': {
|
||||
border: 'none',
|
||||
color: (theme.vars || theme).palette.success.light,
|
||||
},
|
||||
}),
|
||||
variants: [
|
||||
{
|
||||
props: { completed: true },
|
||||
style: {
|
||||
width: 12,
|
||||
height: 12,
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiStepLabel: {
|
||||
styleOverrides: {
|
||||
label: ({ theme }) => ({
|
||||
'&.Mui-completed': {
|
||||
opacity: 0.6,
|
||||
...theme.applyStyles('dark', { opacity: 0.5 }),
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
114
resources/js/pages/theme/customizations/surfaces.ts
Normal file
114
resources/js/pages/theme/customizations/surfaces.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import { alpha } from '@mui/material/styles';
|
||||
import type { Theme, Components } from '@mui/material/styles';
|
||||
import { gray } from '../themePrimitives';
|
||||
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export const surfacesCustomizations: Components<Theme> = {
|
||||
MuiAccordion: {
|
||||
defaultProps: {
|
||||
elevation: 0,
|
||||
disableGutters: true,
|
||||
},
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
padding: 4,
|
||||
overflow: 'clip',
|
||||
backgroundColor: (theme.vars || theme).palette.background.default,
|
||||
border: '1px solid',
|
||||
borderColor: (theme.vars || theme).palette.divider,
|
||||
':before': {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
'&:not(:last-of-type)': {
|
||||
borderBottom: 'none',
|
||||
},
|
||||
'&:first-of-type': {
|
||||
borderTopLeftRadius: (theme.vars || theme).shape.borderRadius,
|
||||
borderTopRightRadius: (theme.vars || theme).shape.borderRadius,
|
||||
},
|
||||
'&:last-of-type': {
|
||||
borderBottomLeftRadius: (theme.vars || theme).shape.borderRadius,
|
||||
borderBottomRightRadius: (theme.vars || theme).shape.borderRadius,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiAccordionSummary: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
border: 'none',
|
||||
borderRadius: 8,
|
||||
'&:hover': { backgroundColor: gray[50] },
|
||||
'&:focus-visible': { backgroundColor: 'transparent' },
|
||||
...theme.applyStyles('dark', {
|
||||
'&:hover': { backgroundColor: gray[800] },
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiAccordionDetails: {
|
||||
styleOverrides: {
|
||||
root: { mb: 20, border: 'none' },
|
||||
},
|
||||
},
|
||||
MuiPaper: {
|
||||
defaultProps: {
|
||||
elevation: 0,
|
||||
},
|
||||
},
|
||||
MuiCard: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => {
|
||||
return {
|
||||
padding: 16,
|
||||
gap: 16,
|
||||
transition: 'all 100ms ease',
|
||||
backgroundColor: gray[50],
|
||||
borderRadius: (theme.vars || theme).shape.borderRadius,
|
||||
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||
boxShadow: 'none',
|
||||
...theme.applyStyles('dark', {
|
||||
backgroundColor: gray[800],
|
||||
}),
|
||||
variants: [
|
||||
{
|
||||
props: {
|
||||
variant: 'outlined',
|
||||
},
|
||||
style: {
|
||||
border: `1px solid ${(theme.vars || theme).palette.divider}`,
|
||||
boxShadow: 'none',
|
||||
background: 'hsl(0, 0%, 100%)',
|
||||
...theme.applyStyles('dark', {
|
||||
background: alpha(gray[900], 0.4),
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiCardContent: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
padding: 0,
|
||||
'&:last-child': { paddingBottom: 0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiCardHeader: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
padding: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiCardActions: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
padding: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
403
resources/js/pages/theme/themePrimitives.ts
Normal file
403
resources/js/pages/theme/themePrimitives.ts
Normal file
@ -0,0 +1,403 @@
|
||||
import { createTheme, alpha, PaletteMode, Shadows } from '@mui/material/styles';
|
||||
|
||||
declare module '@mui/material/Paper' {
|
||||
interface PaperPropsVariantOverrides {
|
||||
highlighted: true;
|
||||
}
|
||||
}
|
||||
declare module '@mui/material/styles' {
|
||||
interface ColorRange {
|
||||
50: string;
|
||||
100: string;
|
||||
200: string;
|
||||
300: string;
|
||||
400: string;
|
||||
500: string;
|
||||
600: string;
|
||||
700: string;
|
||||
800: string;
|
||||
900: string;
|
||||
}
|
||||
|
||||
interface PaletteColor extends ColorRange { }
|
||||
|
||||
interface Palette {
|
||||
baseShadow: string;
|
||||
}
|
||||
}
|
||||
|
||||
const defaultTheme = createTheme();
|
||||
|
||||
const customShadows: Shadows = [...defaultTheme.shadows];
|
||||
|
||||
export const brand = {
|
||||
50: 'hsl(210, 100%, 95%)',
|
||||
100: 'hsl(210, 100%, 92%)',
|
||||
200: 'hsl(210, 100%, 80%)',
|
||||
300: 'hsl(210, 100%, 65%)',
|
||||
400: 'hsl(210, 98%, 48%)',
|
||||
500: 'hsl(210, 98%, 42%)',
|
||||
600: 'hsl(210, 98%, 55%)',
|
||||
700: 'hsl(210, 100%, 35%)',
|
||||
800: 'hsl(210, 100%, 16%)',
|
||||
900: 'hsl(210, 100%, 21%)',
|
||||
};
|
||||
|
||||
export const gray = {
|
||||
50: 'hsl(220, 35%, 97%)',
|
||||
100: 'hsl(220, 30%, 94%)',
|
||||
200: 'hsl(220, 20%, 88%)',
|
||||
300: 'hsl(220, 20%, 80%)',
|
||||
400: 'hsl(220, 20%, 65%)',
|
||||
500: 'hsl(220, 20%, 42%)',
|
||||
600: 'hsl(220, 20%, 35%)',
|
||||
700: 'hsl(220, 20%, 25%)',
|
||||
800: 'hsl(220, 30%, 6%)',
|
||||
900: 'hsl(220, 35%, 3%)',
|
||||
};
|
||||
|
||||
export const green = {
|
||||
50: 'hsl(120, 80%, 98%)',
|
||||
100: 'hsl(120, 75%, 94%)',
|
||||
200: 'hsl(120, 75%, 87%)',
|
||||
300: 'hsl(120, 61%, 77%)',
|
||||
400: 'hsl(120, 44%, 53%)',
|
||||
500: 'hsl(120, 59%, 30%)',
|
||||
600: 'hsl(120, 70%, 25%)',
|
||||
700: 'hsl(120, 75%, 16%)',
|
||||
800: 'hsl(120, 84%, 10%)',
|
||||
900: 'hsl(120, 87%, 6%)',
|
||||
};
|
||||
|
||||
export const orange = {
|
||||
50: 'hsl(45, 100%, 97%)',
|
||||
100: 'hsl(45, 92%, 90%)',
|
||||
200: 'hsl(45, 94%, 80%)',
|
||||
300: 'hsl(45, 90%, 65%)',
|
||||
400: 'hsl(45, 90%, 40%)',
|
||||
500: 'hsl(45, 90%, 35%)',
|
||||
600: 'hsl(45, 91%, 25%)',
|
||||
700: 'hsl(45, 94%, 20%)',
|
||||
800: 'hsl(45, 95%, 16%)',
|
||||
900: 'hsl(45, 93%, 12%)',
|
||||
};
|
||||
|
||||
export const red = {
|
||||
50: 'hsl(0, 100%, 97%)',
|
||||
100: 'hsl(0, 92%, 90%)',
|
||||
200: 'hsl(0, 94%, 80%)',
|
||||
300: 'hsl(0, 90%, 65%)',
|
||||
400: 'hsl(0, 90%, 40%)',
|
||||
500: 'hsl(0, 90%, 30%)',
|
||||
600: 'hsl(0, 91%, 25%)',
|
||||
700: 'hsl(0, 94%, 18%)',
|
||||
800: 'hsl(0, 95%, 12%)',
|
||||
900: 'hsl(0, 93%, 6%)',
|
||||
};
|
||||
|
||||
export const getDesignTokens = (mode: PaletteMode) => {
|
||||
customShadows[1] =
|
||||
mode === 'dark'
|
||||
? 'hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px'
|
||||
: 'hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px';
|
||||
|
||||
return {
|
||||
palette: {
|
||||
mode,
|
||||
primary: {
|
||||
light: brand[200],
|
||||
main: brand[400],
|
||||
dark: brand[700],
|
||||
contrastText: brand[50],
|
||||
...(mode === 'dark' && {
|
||||
contrastText: brand[50],
|
||||
light: brand[300],
|
||||
main: brand[400],
|
||||
dark: brand[700],
|
||||
}),
|
||||
},
|
||||
info: {
|
||||
light: brand[100],
|
||||
main: brand[300],
|
||||
dark: brand[600],
|
||||
contrastText: gray[50],
|
||||
...(mode === 'dark' && {
|
||||
contrastText: brand[300],
|
||||
light: brand[500],
|
||||
main: brand[700],
|
||||
dark: brand[900],
|
||||
}),
|
||||
},
|
||||
warning: {
|
||||
light: orange[300],
|
||||
main: orange[400],
|
||||
dark: orange[800],
|
||||
...(mode === 'dark' && {
|
||||
light: orange[400],
|
||||
main: orange[500],
|
||||
dark: orange[700],
|
||||
}),
|
||||
},
|
||||
error: {
|
||||
light: red[300],
|
||||
main: red[400],
|
||||
dark: red[800],
|
||||
...(mode === 'dark' && {
|
||||
light: red[400],
|
||||
main: red[500],
|
||||
dark: red[700],
|
||||
}),
|
||||
},
|
||||
success: {
|
||||
light: green[300],
|
||||
main: green[400],
|
||||
dark: green[800],
|
||||
...(mode === 'dark' && {
|
||||
light: green[400],
|
||||
main: green[500],
|
||||
dark: green[700],
|
||||
}),
|
||||
},
|
||||
grey: {
|
||||
...gray,
|
||||
},
|
||||
divider: mode === 'dark' ? alpha(gray[700], 0.6) : alpha(gray[300], 0.4),
|
||||
background: {
|
||||
default: 'hsl(0, 0%, 99%)',
|
||||
paper: 'hsl(220, 35%, 97%)',
|
||||
...(mode === 'dark' && { default: gray[900], paper: 'hsl(220, 30%, 7%)' }),
|
||||
},
|
||||
text: {
|
||||
primary: gray[800],
|
||||
secondary: gray[600],
|
||||
warning: orange[400],
|
||||
...(mode === 'dark' && { primary: 'hsl(0, 0%, 100%)', secondary: gray[400] }),
|
||||
},
|
||||
action: {
|
||||
hover: alpha(gray[200], 0.2),
|
||||
selected: `${alpha(gray[200], 0.3)}`,
|
||||
...(mode === 'dark' && {
|
||||
hover: alpha(gray[600], 0.2),
|
||||
selected: alpha(gray[600], 0.3),
|
||||
}),
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
h1: {
|
||||
fontSize: defaultTheme.typography.pxToRem(48),
|
||||
fontWeight: 600,
|
||||
lineHeight: 1.2,
|
||||
letterSpacing: -0.5,
|
||||
},
|
||||
h2: {
|
||||
fontSize: defaultTheme.typography.pxToRem(36),
|
||||
fontWeight: 600,
|
||||
lineHeight: 1.2,
|
||||
},
|
||||
h3: {
|
||||
fontSize: defaultTheme.typography.pxToRem(30),
|
||||
lineHeight: 1.2,
|
||||
},
|
||||
h4: {
|
||||
fontSize: defaultTheme.typography.pxToRem(24),
|
||||
fontWeight: 600,
|
||||
lineHeight: 1.5,
|
||||
},
|
||||
h5: {
|
||||
fontSize: defaultTheme.typography.pxToRem(20),
|
||||
fontWeight: 600,
|
||||
},
|
||||
h6: {
|
||||
fontSize: defaultTheme.typography.pxToRem(18),
|
||||
fontWeight: 600,
|
||||
},
|
||||
subtitle1: {
|
||||
fontSize: defaultTheme.typography.pxToRem(18),
|
||||
},
|
||||
subtitle2: {
|
||||
fontSize: defaultTheme.typography.pxToRem(14),
|
||||
fontWeight: 500,
|
||||
},
|
||||
body1: {
|
||||
fontSize: defaultTheme.typography.pxToRem(14),
|
||||
},
|
||||
body2: {
|
||||
fontSize: defaultTheme.typography.pxToRem(14),
|
||||
fontWeight: 400,
|
||||
},
|
||||
caption: {
|
||||
fontSize: defaultTheme.typography.pxToRem(12),
|
||||
fontWeight: 400,
|
||||
},
|
||||
},
|
||||
shape: {
|
||||
borderRadius: 8,
|
||||
},
|
||||
shadows: customShadows,
|
||||
};
|
||||
};
|
||||
|
||||
export const colorSchemes = {
|
||||
light: {
|
||||
palette: {
|
||||
primary: {
|
||||
light: brand[200],
|
||||
main: brand[400],
|
||||
dark: brand[700],
|
||||
contrastText: brand[50],
|
||||
},
|
||||
info: {
|
||||
light: brand[100],
|
||||
main: brand[300],
|
||||
dark: brand[600],
|
||||
contrastText: gray[50],
|
||||
},
|
||||
warning: {
|
||||
light: orange[300],
|
||||
main: orange[400],
|
||||
dark: orange[800],
|
||||
},
|
||||
error: {
|
||||
light: red[300],
|
||||
main: red[400],
|
||||
dark: red[800],
|
||||
},
|
||||
success: {
|
||||
light: green[300],
|
||||
main: green[400],
|
||||
dark: green[800],
|
||||
},
|
||||
grey: {
|
||||
...gray,
|
||||
},
|
||||
divider: alpha(gray[300], 0.4),
|
||||
background: {
|
||||
default: 'hsl(0, 0%, 99%)',
|
||||
paper: 'hsl(220, 35%, 97%)',
|
||||
},
|
||||
text: {
|
||||
primary: gray[800],
|
||||
secondary: gray[600],
|
||||
warning: orange[400],
|
||||
},
|
||||
action: {
|
||||
hover: alpha(gray[200], 0.2),
|
||||
selected: `${alpha(gray[200], 0.3)}`,
|
||||
},
|
||||
baseShadow:
|
||||
'hsla(220, 30%, 5%, 0.07) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.07) 0px 8px 16px -5px',
|
||||
},
|
||||
},
|
||||
dark: {
|
||||
palette: {
|
||||
primary: {
|
||||
contrastText: brand[50],
|
||||
light: brand[300],
|
||||
main: brand[400],
|
||||
dark: brand[700],
|
||||
},
|
||||
info: {
|
||||
contrastText: brand[300],
|
||||
light: brand[500],
|
||||
main: brand[700],
|
||||
dark: brand[900],
|
||||
},
|
||||
warning: {
|
||||
light: orange[400],
|
||||
main: orange[500],
|
||||
dark: orange[700],
|
||||
},
|
||||
error: {
|
||||
light: red[400],
|
||||
main: red[500],
|
||||
dark: red[700],
|
||||
},
|
||||
success: {
|
||||
light: green[400],
|
||||
main: green[500],
|
||||
dark: green[700],
|
||||
},
|
||||
grey: {
|
||||
...gray,
|
||||
},
|
||||
divider: alpha(gray[700], 0.6),
|
||||
background: {
|
||||
default: gray[900],
|
||||
paper: 'hsl(220, 30%, 7%)',
|
||||
},
|
||||
text: {
|
||||
primary: 'hsl(0, 0%, 100%)',
|
||||
secondary: gray[400],
|
||||
},
|
||||
action: {
|
||||
hover: alpha(gray[600], 0.2),
|
||||
selected: alpha(gray[600], 0.3),
|
||||
},
|
||||
baseShadow:
|
||||
'hsla(220, 30%, 5%, 0.7) 0px 4px 16px 0px, hsla(220, 25%, 10%, 0.8) 0px 8px 16px -5px',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const typography = {
|
||||
fontFamily: 'Inter, sans-serif',
|
||||
h1: {
|
||||
fontSize: defaultTheme.typography.pxToRem(48),
|
||||
fontWeight: 600,
|
||||
lineHeight: 1.2,
|
||||
letterSpacing: -0.5,
|
||||
},
|
||||
h2: {
|
||||
fontSize: defaultTheme.typography.pxToRem(36),
|
||||
fontWeight: 600,
|
||||
lineHeight: 1.2,
|
||||
},
|
||||
h3: {
|
||||
fontSize: defaultTheme.typography.pxToRem(30),
|
||||
lineHeight: 1.2,
|
||||
},
|
||||
h4: {
|
||||
fontSize: defaultTheme.typography.pxToRem(24),
|
||||
fontWeight: 600,
|
||||
lineHeight: 1.5,
|
||||
},
|
||||
h5: {
|
||||
fontSize: defaultTheme.typography.pxToRem(20),
|
||||
fontWeight: 600,
|
||||
},
|
||||
h6: {
|
||||
fontSize: defaultTheme.typography.pxToRem(18),
|
||||
fontWeight: 600,
|
||||
},
|
||||
subtitle1: {
|
||||
fontSize: defaultTheme.typography.pxToRem(18),
|
||||
},
|
||||
subtitle2: {
|
||||
fontSize: defaultTheme.typography.pxToRem(14),
|
||||
fontWeight: 500,
|
||||
},
|
||||
body1: {
|
||||
fontSize: defaultTheme.typography.pxToRem(14),
|
||||
},
|
||||
body2: {
|
||||
fontSize: defaultTheme.typography.pxToRem(14),
|
||||
fontWeight: 400,
|
||||
},
|
||||
caption: {
|
||||
fontSize: defaultTheme.typography.pxToRem(12),
|
||||
fontWeight: 400,
|
||||
},
|
||||
};
|
||||
|
||||
export const shape = {
|
||||
borderRadius: 8,
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
const defaultShadows: Shadows = [
|
||||
'none',
|
||||
'var(--template-palette-baseShadow)',
|
||||
...defaultTheme.shadows.slice(2),
|
||||
];
|
||||
export const shadows = defaultShadows;
|
||||
15
resources/views/app.blade.php
Normal file
15
resources/views/app.blade.php
Normal file
@ -0,0 +1,15 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
@viteReactRefresh
|
||||
@vite('resources/js/app.jsx')
|
||||
@inertiaHead
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@inertia
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -1,7 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\Home\HomepageController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('/', function () {
|
||||
return view('welcome');
|
||||
});
|
||||
Route::get('/', [HomepageController::class, 'index'])->name('homepage');
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import laravel from 'laravel-vite-plugin';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
laravel({
|
||||
input: [
|
||||
'resources/css/app.css',
|
||||
'resources/js/app.js',
|
||||
'resources/js/app.jsx',
|
||||
'resources/css/filament/dashboard/theme.css'
|
||||
],
|
||||
refresh: true,
|
||||
}),
|
||||
tailwindcss(),
|
||||
react(),
|
||||
],
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user