Initial commit

This commit is contained in:
2025-10-11 17:02:49 +02:00
commit 92056f073f
243 changed files with 27536 additions and 0 deletions

View File

@@ -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;
}

View 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>
);
};

View 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;
}

View 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>
);
}

View 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;
}
}

View 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>
);
}