Initial commit
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
.phone {
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 3rem;
|
||||
background-color: #f7941f;
|
||||
border-radius: 20px;
|
||||
padding: 5px 10px;
|
||||
width: fit-content;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.phone-fixed {
|
||||
position: fixed;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 3rem;
|
||||
background-color: #f7941f;
|
||||
border-radius: 20px;
|
||||
padding: 5px 10px;
|
||||
width: fit-content;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
|
||||
.onlineorder {
|
||||
margin: 5px auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 3rem;
|
||||
background-color: #ED561A;
|
||||
border-radius: 20px;
|
||||
padding: 5px 10px;
|
||||
width: fit-content;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.onlineorder-fixed {
|
||||
position: fixed;
|
||||
right: 20px;
|
||||
bottom: 84px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 3rem;
|
||||
background-color: #ED561A;
|
||||
border-radius: 20px;
|
||||
padding: 5px 10px;
|
||||
width: fit-content;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.onlineorder:hover, .onlineorder-fixed:hover {
|
||||
background-color: #d12600;
|
||||
}
|
||||
|
||||
.phone-icon {
|
||||
margin-right: 7px;
|
||||
}
|
||||
|
||||
.cart-icon {
|
||||
margin-right: 7px;
|
||||
}
|
||||
|
||||
.phone-link:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
83
resources/js/components/ActionButtons/ActionButtons.tsx
Normal file
83
resources/js/components/ActionButtons/ActionButtons.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
import React from 'react';
|
||||
import type { IconDefinition } from '@fortawesome/fontawesome-svg-core';
|
||||
import { faCartShopping, faPhone } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import styles from './ActionButtons.module.css';
|
||||
import { Link } from '@inertiajs/react';
|
||||
|
||||
const SolidIcons = {
|
||||
faCartShopping,
|
||||
faPhone,
|
||||
}
|
||||
|
||||
interface ButtonData {
|
||||
type: 'link';
|
||||
data: {
|
||||
name: string;
|
||||
icon: keyof typeof SolidIcons;
|
||||
btn_style: 'primary' | 'secondary';
|
||||
link: string;
|
||||
external: boolean;
|
||||
new_tab: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface ActionButtonsProps {
|
||||
variant?: 'default' | 'floating';
|
||||
}
|
||||
|
||||
export const ActionButtons: React.FC<ActionButtonsProps> = ({ variant = 'default' }) => {
|
||||
const [buttons, setButtons] = React.useState<ButtonData[]>([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
fetch('/api/buttons')
|
||||
.then(res => res.json())
|
||||
.then(response => {
|
||||
if (response.status === 'success') {
|
||||
setButtons(response.data);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching buttons:', error);
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
{buttons && buttons.length > 0 && buttons.map((button, index) => {
|
||||
const icon: IconDefinition | null = button.data.icon ? (SolidIcons[button.data.icon] as IconDefinition) : null;
|
||||
const isPrimary = button.data.btn_style === 'primary';
|
||||
|
||||
const buttonClass = variant === 'floating'
|
||||
? isPrimary ? styles['onlineorder-fixed'] : styles['phone-fixed']
|
||||
: isPrimary ? styles.onlineorder : styles.phone;
|
||||
|
||||
return button.data.external ? (
|
||||
<div className={buttonClass} key={index}>
|
||||
<a
|
||||
href={button.data.link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles['phone-link']}
|
||||
>
|
||||
{icon && <FontAwesomeIcon className={isPrimary ? styles['cart-icon'] : styles['phone-icon']} icon={icon} />}
|
||||
<span>{button.data.name}</span>
|
||||
</a>
|
||||
</div>
|
||||
) : (
|
||||
<div className={buttonClass} key={index}>
|
||||
<Link
|
||||
href={button.data.link}
|
||||
className={styles['phone-link']}
|
||||
>
|
||||
{icon && <FontAwesomeIcon className={isPrimary ? styles['cart-icon'] : styles['phone-icon']} icon={icon} />}
|
||||
<span>{button.data.name}</span>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
40
resources/js/components/Footer/Footer.module.css
Normal file
40
resources/js/components/Footer/Footer.module.css
Normal file
@@ -0,0 +1,40 @@
|
||||
.footer {
|
||||
font-size: 2rem;
|
||||
text-align: center;
|
||||
font-weight: 300;
|
||||
position: relative;
|
||||
color: white;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #242422;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #edb265;
|
||||
}
|
||||
|
||||
.footer-icons {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.links {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.links a {
|
||||
margin-right: 20px;
|
||||
color: white;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.links a > div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
76
resources/js/components/Footer/Footer.tsx
Normal file
76
resources/js/components/Footer/Footer.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import type { IconDefinition } from '@fortawesome/fontawesome-svg-core';
|
||||
import { faFacebook, faGoogle } from '@fortawesome/free-brands-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { Link } from '@inertiajs/react';
|
||||
import styles from './Footer.module.css';
|
||||
|
||||
const BrandsIcons = {
|
||||
faFacebook,
|
||||
faGoogle,
|
||||
}
|
||||
|
||||
interface FooterLink {
|
||||
type: 'link';
|
||||
data: {
|
||||
name: string;
|
||||
icon?: keyof typeof BrandsIcons;
|
||||
link: string;
|
||||
external: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export default function Footer() {
|
||||
const [links, setLinks] = React.useState<FooterLink[]>([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
fetch('/api/footer')
|
||||
.then(res => res.json())
|
||||
.then(response => {
|
||||
if (response.status === 'success') {
|
||||
setLinks(response.data);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching footer links:', error);
|
||||
});
|
||||
}, []);
|
||||
return (
|
||||
<footer className={styles.footer}>
|
||||
<div className={styles.links}>
|
||||
{links && links.length > 0 && links.map((link, index) => {
|
||||
const icon: IconDefinition | null = link.data.icon ? BrandsIcons[link.data.icon] : null;
|
||||
|
||||
const linkContent = (
|
||||
<>
|
||||
{icon && <FontAwesomeIcon icon={icon} className={styles['footer-icons']} />}
|
||||
{link.data.name}
|
||||
</>
|
||||
);
|
||||
|
||||
return link.data.external ? (
|
||||
<a
|
||||
key={index}
|
||||
href={link.data.link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center text-white/80 hover:text-accent transition-colors"
|
||||
>
|
||||
{linkContent}
|
||||
</a>
|
||||
) : (
|
||||
<Link
|
||||
key={index}
|
||||
href={link.data.link}
|
||||
className="flex items-center text-white/80 hover:text-accent transition-colors"
|
||||
>
|
||||
{linkContent}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div>© {new Date().getFullYear()} GHOST PIZZA Krzysztof Szymański. Wszelkie prawa zastrzeżone.</div>
|
||||
<div>Wykonane przez <a target="_blank" rel="noreferrer" href='https://bwitek.dev'>BWitek.dev</a></div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
64
resources/js/components/Header/Header.module.css
Normal file
64
resources/js/components/Header/Header.module.css
Normal file
@@ -0,0 +1,64 @@
|
||||
.header ul {
|
||||
list-style-type: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: black;
|
||||
margin-bottom: 45px;
|
||||
font-weight: 400;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.header ul a {
|
||||
color: white;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
padding: 10px 20px;
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
.header ul a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 100%;
|
||||
max-width: 2000px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.logo-small {
|
||||
margin-top: 20px;
|
||||
width: 90%;
|
||||
max-width: 650px;
|
||||
}
|
||||
|
||||
.img-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.header ul a {
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
.logo {
|
||||
width: 100%;
|
||||
height: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 280px) {
|
||||
.header ul a {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
}
|
||||
132
resources/js/components/Header/Header.tsx
Normal file
132
resources/js/components/Header/Header.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Link } from '@inertiajs/react';
|
||||
import styles from './Header.module.css';
|
||||
|
||||
interface HeaderData {
|
||||
status: string;
|
||||
data: {
|
||||
photo: string | null;
|
||||
photo_mobile: string | null;
|
||||
photo_menu: string | null;
|
||||
title: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
interface NavigationLink {
|
||||
id: number;
|
||||
sort_order: number;
|
||||
name: string;
|
||||
link: string;
|
||||
external: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export default function Header({isSmall=false}) {
|
||||
const [innerWidth, setInnerWidth] = useState(0);
|
||||
const [links, setLinks] = React.useState<NavigationLink[]>([]);
|
||||
const [data, setData] = useState<HeaderData['data'] | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([
|
||||
fetch('/api/homepage').then(res => res.json()),
|
||||
fetch('/api/navigation').then(res => res.json())
|
||||
])
|
||||
.then(([headerResponse, navigationResponse]) => {
|
||||
setData(headerResponse.data);
|
||||
setLinks(navigationResponse.data);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching data:', error);
|
||||
setLoading(false);
|
||||
});
|
||||
|
||||
|
||||
setInnerWidth(prevState => prevState = window.innerWidth);
|
||||
|
||||
function handleResize() {
|
||||
setInnerWidth(prevState => prevState = window.innerWidth);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<></>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<header className={styles.header}>
|
||||
<nav>
|
||||
<ul>
|
||||
{links.map((link) => {
|
||||
return link.external ? (
|
||||
<li key={link.id}>
|
||||
<a
|
||||
href={link.link}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{link.name}
|
||||
</a>
|
||||
</li>
|
||||
) : (
|
||||
<li key={link.id}>
|
||||
<Link
|
||||
href={link.link}
|
||||
>
|
||||
{link.name}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</nav>
|
||||
<div className="image-column">
|
||||
{isSmall ? (
|
||||
<>
|
||||
{data && data.photo && data.photo_menu ? (
|
||||
<div className="image-container">
|
||||
<img className={styles['logo-small']} fetchPriority="high" src={data.photo_menu} alt="Logo pizzeri GhostPizza" />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
(data && data.photo && data.photo_mobile) ? (
|
||||
<div className="image-container">
|
||||
{innerWidth > 1450 ? (
|
||||
<div className={styles['img-container']}>
|
||||
<img
|
||||
style={{ objectFit: "contain" }}
|
||||
className={styles.logo}
|
||||
src={data.photo}
|
||||
alt={data.title || 'Ghost Pizza'}
|
||||
fetchPriority="high"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles['img-container']}>
|
||||
<img
|
||||
style={{ objectFit: "contain" }}
|
||||
className={styles.logo}
|
||||
src={data.photo_mobile}
|
||||
alt={data.title || 'Ghost Pizza'}
|
||||
fetchPriority="high"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : null
|
||||
)}
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user