Ready for production deployment with Dockerfile and i18n support
This commit is contained in:
31
app/[lang]/layout.tsx
Normal file
31
app/[lang]/layout.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Oswald } from "next/font/google";
|
||||
import "../globals.css";
|
||||
import { getDictionary } from "../dictionaries";
|
||||
|
||||
const oswald = Oswald({
|
||||
subsets: ["latin", "latin-ext"],
|
||||
variable: "--font-oswald",
|
||||
});
|
||||
|
||||
export async function generateMetadata({ params }: { params: Promise<{ lang: string }> }) {
|
||||
const resolvedParams = await params;
|
||||
return {
|
||||
title: "Salmakis Group | Luxury Resort, Villas & Yachting",
|
||||
description: "Salmakis Group Gateway",
|
||||
};
|
||||
}
|
||||
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
params,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
params: Promise<{ lang: string }>;
|
||||
}>) {
|
||||
const resolvedParams = await params;
|
||||
return (
|
||||
<html lang={resolvedParams.lang} className={`${oswald.variable}`}>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
18
app/[lang]/page.tsx
Normal file
18
app/[lang]/page.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import HeroSplit from "../components/HeroSplit";
|
||||
import AboutLegend from "../components/AboutLegend";
|
||||
import Footer from "../components/Footer";
|
||||
import { getDictionary } from "../dictionaries";
|
||||
|
||||
export default async function Home({ params }: { params: Promise<{ lang: string }> }) {
|
||||
const resolvedParams = await params;
|
||||
const lang = resolvedParams.lang as "en" | "tr";
|
||||
const dict = await getDictionary(lang);
|
||||
|
||||
return (
|
||||
<main>
|
||||
<HeroSplit dict={dict} currentLang={lang} />
|
||||
<AboutLegend dict={dict} />
|
||||
<Footer dict={dict} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
130
app/components/AboutLegend.module.css
Normal file
130
app/components/AboutLegend.module.css
Normal file
@@ -0,0 +1,130 @@
|
||||
.aboutSection {
|
||||
padding: 8rem 2rem;
|
||||
background-color: var(--primary-white);
|
||||
color: var(--text-dark);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.sinceBadge {
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: var(--gold);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-size: 2.5rem;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 3rem;
|
||||
color: var(--navy);
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.paragraph {
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.8;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.bottomBadge {
|
||||
margin-top: 3rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.yachtingTitle {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.8rem;
|
||||
color: var(--navy);
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
/* Legend Section */
|
||||
.legendSection {
|
||||
position: relative;
|
||||
height: 80vh;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.parallaxBg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url('/2.jpg');
|
||||
background-attachment: fixed;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.glassCard {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
padding: 4rem;
|
||||
max-width: 600px;
|
||||
text-align: center;
|
||||
color: var(--primary-white);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.legendTitle {
|
||||
font-size: 2.2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
letter-spacing: 0.05em;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.legendText {
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.8;
|
||||
margin-bottom: 2rem;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.goldLine {
|
||||
width: 60px;
|
||||
height: 2px;
|
||||
background-color: var(--gold);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.aboutSection {
|
||||
padding: 5rem 1.5rem;
|
||||
}
|
||||
|
||||
.glassCard {
|
||||
padding: 2rem;
|
||||
margin: 1rem;
|
||||
}
|
||||
|
||||
.parallaxBg {
|
||||
background-attachment: scroll;
|
||||
}
|
||||
}
|
||||
38
app/components/AboutLegend.tsx
Normal file
38
app/components/AboutLegend.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
import styles from "./AboutLegend.module.css";
|
||||
|
||||
export default function AboutLegend({ dict }: { dict: any }) {
|
||||
return (
|
||||
<>
|
||||
<section className={styles.aboutSection} id="about">
|
||||
<div className={styles.container}>
|
||||
<h2 className={styles.heading}>{dict.about.title}</h2>
|
||||
<div className={styles.content}>
|
||||
<p className={styles.paragraph}>{dict.about.p1}</p>
|
||||
<p className={styles.paragraph}>{dict.about.p2}</p>
|
||||
</div>
|
||||
<div className={styles.bottomBadge}>
|
||||
<h3 className={styles.yachtingTitle}>SALMAKIS</h3>
|
||||
<p className={styles.sinceBadge}>{dict.about.since}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className={styles.legendSection} id="legend">
|
||||
{/* Parallax / minimal illustration container */}
|
||||
<div className={styles.parallaxBg}>
|
||||
<div className={styles.glassCard}>
|
||||
<h3 className={styles.legendTitle}>{dict.about.company}</h3>
|
||||
<div className={styles.legendText}>
|
||||
<p><strong>SALMAKIS TURIZM YATIRIM VE TICARET ANONIM SIRKETI</strong></p>
|
||||
<p>Adres: Kumbahce Mahallesi Icmeler Caddesi No: 28/1 Bodrum / MUGLA / TURKEY</p>
|
||||
<p>Vergi Dairesi: BODRUM</p>
|
||||
<p>Vergi No: 741 003 6900</p>
|
||||
<p>Mersis No: 0741 0036 9000 0011</p>
|
||||
</div>
|
||||
<div className={styles.goldLine}></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
124
app/components/Footer.module.css
Normal file
124
app/components/Footer.module.css
Normal file
@@ -0,0 +1,124 @@
|
||||
.footer {
|
||||
background-color: var(--text-dark);
|
||||
color: var(--primary-white);
|
||||
padding: 6rem 2rem 2rem 2rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4rem;
|
||||
}
|
||||
|
||||
.brandCol {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 2rem;
|
||||
letter-spacing: 0.2em;
|
||||
margin-bottom: 0.5rem;
|
||||
color: var(--gold);
|
||||
}
|
||||
|
||||
.tagline {
|
||||
font-size: 0.9rem;
|
||||
letter-spacing: 0.1em;
|
||||
opacity: 0.6;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.generalContact {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.contactText {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.gridContainer {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.colTitle {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.25rem;
|
||||
color: var(--sand-beige);
|
||||
margin-bottom: 1rem;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.address {
|
||||
font-size: 0.95rem;
|
||||
opacity: 0.8;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.contactLink {
|
||||
display: block;
|
||||
font-size: 0.95rem;
|
||||
opacity: 0.8;
|
||||
margin-bottom: 0.25rem;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.contactLink:hover {
|
||||
opacity: 1;
|
||||
color: var(--gold);
|
||||
}
|
||||
|
||||
.bottomBar {
|
||||
max-width: 1200px;
|
||||
margin: 4rem auto 0 auto;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 0.8rem;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.copyright {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.signature {
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.ayrisLink {
|
||||
font-weight: 500;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
transition: color 0.3s ease;
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 4px;
|
||||
}
|
||||
|
||||
.ayrisLink:hover {
|
||||
color: var(--gold);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.gridContainer {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 3rem;
|
||||
}
|
||||
|
||||
.bottomBar {
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
69
app/components/Footer.tsx
Normal file
69
app/components/Footer.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import styles from "./Footer.module.css";
|
||||
|
||||
export default function Footer({ dict }: { dict: any }) {
|
||||
return (
|
||||
<footer className={styles.footer} id="contact">
|
||||
<div className={styles.container}>
|
||||
{/* Brand Column */}
|
||||
<div className={styles.brandCol}>
|
||||
<h2 className={styles.logo}>SALMAKIS</h2>
|
||||
<p className={styles.tagline}>{dict.footer.tagline}</p>
|
||||
<div className={styles.generalContact}>
|
||||
<a href="tel:+902523166506" className={styles.contactLink}>+90 252 316 65 06</a>
|
||||
<span className={styles.contactText}>08:00 − 20:00</span>
|
||||
<a href="mailto:salmakis@salmakis.com.tr" className={styles.contactLink}>salmakis@salmakis.com.tr</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Divisions Column */}
|
||||
<div className={styles.gridContainer}>
|
||||
<div className={styles.col}>
|
||||
<h4 className={styles.colTitle}>Salmakis Resort</h4>
|
||||
<p className={styles.address}>
|
||||
Bardakci Koyu<br/>
|
||||
BODRUM/MUĞLA/TURKEY
|
||||
</p>
|
||||
<a href="tel:+902523166506" className={styles.contactLink}>+90 252 316 65 06</a>
|
||||
<a href="tel:+902523166507" className={styles.contactLink}>+90 252 316 65 07</a>
|
||||
<a href="tel:+902523166511" className={styles.contactLink}>+90 252 316 65 11</a>
|
||||
<a href="mailto:salmakis@salmakis.com.tr" className={styles.contactLink}>salmakis@salmakis.com.tr</a>
|
||||
</div>
|
||||
|
||||
<div className={styles.col}>
|
||||
<h4 className={styles.colTitle}>Salmakis Villas</h4>
|
||||
<p className={styles.address}>
|
||||
Bademlik Mevkii<br/>
|
||||
Kume Evleri No 24<br/>
|
||||
Bodrum/MUGLA/TURKEY
|
||||
</p>
|
||||
<a href="tel:+902523162738" className={styles.contactLink}>+90 252 316 27 38</a>
|
||||
<a href="tel:+902523162877" className={styles.contactLink}>+90 252 316 28 77</a>
|
||||
<a href="tel:+905327317804" className={styles.contactLink}>+90 532 731 78 04</a>
|
||||
<a href="tel:+902523162737" className={styles.contactLink}>+90 252 316 27 37</a>
|
||||
<a href="mailto:info@salmakisvillas.com" className={styles.contactLink}>info@salmakisvillas.com</a>
|
||||
</div>
|
||||
|
||||
<div className={styles.col}>
|
||||
<h4 className={styles.colTitle}>Salmakis Yachting</h4>
|
||||
<p className={styles.address}>
|
||||
Kumbahce Mah. Icmeler Cad.<br/>
|
||||
No 28/1<br/>
|
||||
BODRUM/MUGLA/TURKEY
|
||||
</p>
|
||||
<a href="tel:+902523162738" className={styles.contactLink}>+90 252 316 27 38</a>
|
||||
<a href="tel:+902523162877" className={styles.contactLink}>+90 252 316 28 77</a>
|
||||
<a href="tel:+902523162737" className={styles.contactLink}>+90 252 316 27 37</a>
|
||||
<a href="mailto:info@salmakisyachting.com" className={styles.contactLink}>info@salmakisyachting.com</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.bottomBar}>
|
||||
<p className={styles.copyright}>© {new Date().getFullYear()} {dict.footer.copyright}</p>
|
||||
<p className={styles.signature}>
|
||||
{dict.footer.created_by} <a href="https://ayris.tech" target="_blank" rel="noreferrer" className={styles.ayrisLink}>AYRISTECH</a>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
255
app/components/HeroSplit.module.css
Normal file
255
app/components/HeroSplit.module.css
Normal file
@@ -0,0 +1,255 @@
|
||||
.heroContainer {
|
||||
position: relative;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background-color: var(--text-dark);
|
||||
}
|
||||
|
||||
.navOverlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
padding: 2rem 4rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
z-index: 100;
|
||||
color: var(--primary-white);
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: var(--font-heading);
|
||||
font-size: 1.5rem;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.navLinks {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.langBtn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--primary-white);
|
||||
font-family: var(--font-text);
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.langBtn:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: 1px;
|
||||
height: 16px;
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.contactIcon {
|
||||
margin-left: 1rem;
|
||||
opacity: 0.8;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.contactIcon:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Split Pane Layout */
|
||||
.splitWrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.splitPane {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
transition: flex 0.6s cubic-bezier(0.25, 1, 0.5, 1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.paneBg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
transform: scale(1.05);
|
||||
transition: transform 10s ease;
|
||||
filter: grayscale(30%);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.videoIfrm {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 100vw;
|
||||
height: 56.25vw; /* 16:9 aspect ratio target */
|
||||
min-height: 100vh;
|
||||
min-width: 177.77vh; /* 16:9 aspect ratio target */
|
||||
pointer-events: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.videoPlaceholder {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
z-index: 2;
|
||||
transition: opacity 0.4s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to bottom, rgba(0,0,0,0.2) 0%, rgba(0,0,0,0.6) 100%);
|
||||
transition: background 0.4s ease;
|
||||
}
|
||||
|
||||
.paneContent {
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
text-align: center;
|
||||
color: var(--primary-white);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transform: translateY(20px);
|
||||
transition: transform 0.6s cubic-bezier(0.25, 1, 0.5, 1);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 3rem;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0.1em;
|
||||
margin-bottom: 0.5rem;
|
||||
text-shadow: 0 4px 10px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1rem;
|
||||
font-weight: 300;
|
||||
letter-spacing: 0.15em;
|
||||
text-transform: uppercase;
|
||||
opacity: 0.8;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.exploreBtn {
|
||||
background: transparent;
|
||||
color: var(--primary-white);
|
||||
border: 1px solid var(--primary-white);
|
||||
padding: 0.75rem 2rem;
|
||||
font-size: 0.9rem;
|
||||
font-family: var(--font-text);
|
||||
letter-spacing: 0.1em;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
transition: all 0.4s ease;
|
||||
}
|
||||
|
||||
.socialsWrapper {
|
||||
display: flex;
|
||||
gap: 1.25rem;
|
||||
margin-top: 1.5rem;
|
||||
opacity: 0;
|
||||
transform: translateY(10px);
|
||||
transition: all 0.4s ease;
|
||||
transition-delay: 0.1s; /* appears slightly after the button */
|
||||
}
|
||||
|
||||
.socialLink {
|
||||
color: var(--primary-white);
|
||||
transition: color 0.3s ease, transform 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.socialLink:hover {
|
||||
color: var(--gold);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Hover States */
|
||||
.splitWrapper:hover .splitPane {
|
||||
flex: 0.5;
|
||||
}
|
||||
|
||||
.splitWrapper .splitPane:hover {
|
||||
flex: 3;
|
||||
}
|
||||
|
||||
.splitPane:hover .videoPlaceholder {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.splitPane:hover .paneBg {
|
||||
transform: scale(1.0);
|
||||
filter: grayscale(0%);
|
||||
}
|
||||
|
||||
.splitPane:hover .overlay {
|
||||
background: linear-gradient(to bottom, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.4) 100%);
|
||||
}
|
||||
|
||||
.splitPane:hover .paneContent {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.splitPane:hover .exploreBtn {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
background: var(--primary-white);
|
||||
color: var(--text-dark);
|
||||
}
|
||||
|
||||
.splitPane:hover .socialsWrapper {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.splitWrapper {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.splitPane:hover {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.navOverlay {
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
125
app/components/HeroSplit.tsx
Normal file
125
app/components/HeroSplit.tsx
Normal file
@@ -0,0 +1,125 @@
|
||||
"use client";
|
||||
|
||||
import styles from "./HeroSplit.module.css";
|
||||
import Link from "next/link";
|
||||
import { heroSectionsData } from "../data/hero-sections";
|
||||
|
||||
export default function HeroSplit({ dict, currentLang }: { dict: any; currentLang: string }) {
|
||||
const handleMouseEnter = (e: React.MouseEvent<HTMLDivElement>, videoId?: string) => {
|
||||
if (videoId) {
|
||||
const iframe = e.currentTarget.querySelector('iframe');
|
||||
if (iframe?.contentWindow) {
|
||||
iframe.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseLeave = (e: React.MouseEvent<HTMLDivElement>, videoId?: string) => {
|
||||
if (videoId) {
|
||||
const iframe = e.currentTarget.querySelector('iframe');
|
||||
if (iframe?.contentWindow) {
|
||||
iframe.contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}', '*');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const mapDataToDict = heroSectionsData.map(item => {
|
||||
// @ts-ignore
|
||||
const translation = dict.sections[item.key] || {};
|
||||
return {
|
||||
...item,
|
||||
title: translation.title || item.title,
|
||||
subtitle: translation.subtitle || item.subtitle
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<section className={styles.heroContainer}>
|
||||
<nav className={styles.navOverlay}>
|
||||
<div className={styles.logo}>SALMAKIS</div>
|
||||
<div className={styles.navLinks}>
|
||||
<Link href="/tr" className={styles.langBtn} style={{ opacity: currentLang === 'tr' ? 1 : 0.5 }}>TR</Link>
|
||||
<span className={styles.divider}></span>
|
||||
<Link href="/en" className={styles.langBtn} style={{ opacity: currentLang === 'en' ? 1 : 0.5 }}>EN</Link>
|
||||
<Link href="#contact" className={styles.contactIcon}>
|
||||
<svg width="24" height="24" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
|
||||
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
|
||||
<polyline points="22,6 12,13 2,6"></polyline>
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div className={styles.splitWrapper}>
|
||||
{mapDataToDict.map((section, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={styles.splitPane}
|
||||
onMouseEnter={(e) => handleMouseEnter(e, section.videoId)}
|
||||
onMouseLeave={(e) => handleMouseLeave(e, section.videoId)}
|
||||
>
|
||||
{section.videoId ? (
|
||||
<div className={styles.paneBg}>
|
||||
{/* The background overlay thumbnail ensuring video loads smoothly behind */}
|
||||
<div style={{ backgroundImage: `url(${section.bgUrl})`, position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', backgroundSize: 'cover', backgroundPosition: 'center', zIndex: 0 }}></div>
|
||||
|
||||
<iframe
|
||||
className={styles.videoIfrm}
|
||||
src={`https://www.youtube.com/embed/${section.videoId}?autoplay=0&mute=1&controls=0&loop=1&playlist=${section.videoId}&showinfo=0&rel=0&modestbranding=1&playsinline=1&enablejsapi=1&vq=hd1080`}
|
||||
allow="autoplay; encrypted-media"
|
||||
frameBorder="0"
|
||||
></iframe>
|
||||
{/* The background overlay thumbnail ensuring YouTube UI is hidden until hover */}
|
||||
<div
|
||||
className={styles.videoPlaceholder}
|
||||
style={{ backgroundImage: `url(${section.bgUrl})` }}
|
||||
></div>
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={styles.paneBg}
|
||||
style={{ backgroundImage: `url(${section.bgUrl})` }}
|
||||
></div>
|
||||
)}
|
||||
<div className={styles.overlay}></div>
|
||||
|
||||
<div className={styles.paneContent}>
|
||||
<h2 className={styles.title}>{section.title}</h2>
|
||||
<p className={styles.subtitle}>{section.subtitle}</p>
|
||||
|
||||
<a href={section.link} target={section.link.startsWith('#') ? '_self' : '_blank'} rel="noreferrer" className={styles.exploreBtn}>
|
||||
{dict.nav.discover}
|
||||
</a>
|
||||
|
||||
<div className={styles.socialsWrapper}>
|
||||
{section.socials.instagram && (
|
||||
<a href={section.socials.instagram} target="_blank" rel="noreferrer" className={styles.socialLink}>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<rect x="2" y="2" width="20" height="20" rx="5" ry="5"></rect>
|
||||
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z"></path>
|
||||
<line x1="17.5" y1="6.5" x2="17.51" y2="6.5"></line>
|
||||
</svg>
|
||||
</a>
|
||||
)}
|
||||
{section.socials.twitter && (
|
||||
<a href={section.socials.twitter} target="_blank" rel="noreferrer" className={styles.socialLink}>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6 2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4-.9-4.2 4-6.6 7-3.8 1.1 0 3-1.2 3-1.2z"></path>
|
||||
</svg>
|
||||
</a>
|
||||
)}
|
||||
{section.socials.facebook && (
|
||||
<a href={section.socials.facebook} target="_blank" rel="noreferrer" className={styles.socialLink}>
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"></path>
|
||||
</svg>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
36
app/data/hero-sections.ts
Normal file
36
app/data/hero-sections.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
export const heroSectionsData = [
|
||||
{
|
||||
title: "Resort",
|
||||
subtitle: "A Timeless Escape",
|
||||
bgUrl: "/1.jpg",
|
||||
link: "https://www.salmakishotel.com/",
|
||||
socials: {
|
||||
instagram: "https://www.facebook.com/SalmakisResortSpa",
|
||||
twitter: "https://x.com/SalmakisRS",
|
||||
facebook: "https://www.instagram.com/salmakisspafitness/",
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Villas",
|
||||
subtitle: "Private Luxury Reflections",
|
||||
bgUrl: "/SU-3.jpg",
|
||||
link: "https://www.salmakisvillas.com/",
|
||||
socials: {
|
||||
instagram: "https://www.facebook.com/salmakisvillas/",
|
||||
twitter: "https://x.com/salmakisvillas",
|
||||
facebook: "https://facebook.com/salmakisvillas",
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "Yachting",
|
||||
subtitle: "Aegean Elegance",
|
||||
bgUrl: "/MEIRA-2000x1333.jpg",
|
||||
videoId: "0k4s7X8EgYI",
|
||||
link: "https://www.salmakisyachting.com/",
|
||||
socials: {
|
||||
instagram: "https://www.instagram.com/salmakisyachting/",
|
||||
twitter: "https://x.com/SalmakisYacht",
|
||||
facebook: "https://www.facebook.com/salmakis.yachting/",
|
||||
}
|
||||
},
|
||||
];
|
||||
31
app/dictionaries/en.json
Normal file
31
app/dictionaries/en.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"nav": {
|
||||
"discover": "Discover"
|
||||
},
|
||||
"about": {
|
||||
"since": "Since 1980",
|
||||
"title": "THE SALMAKIS LEGEND",
|
||||
"p1": "In ancient times, in the bay known today as Bardakçı, there were a pristine lake and a beautiful nymph both named Salmakis. One day, while bathing, she saw Hermaphroditus, the son of Hermes and Aphrodite, and fell deeply in love with him.",
|
||||
"p2": "However, Hermaphroditus rejected her. Devastated, Salmakis prayed to the gods to unite them forever. Taking pity on her, the gods merged their bodies into one. From that day on, Salmakis and Hermaphroditus lived eternally in a single body possessing dual nature.",
|
||||
"company": "COMPANY INFORMATION"
|
||||
},
|
||||
"footer": {
|
||||
"tagline": "A Heritage of Excellence Since 1980.",
|
||||
"copyright": "Salmakis Group. All rights reserved.",
|
||||
"created_by": "CREATED BY"
|
||||
},
|
||||
"sections": {
|
||||
"resort": {
|
||||
"title": "Resort",
|
||||
"subtitle": "A Timeless Escape"
|
||||
},
|
||||
"villas": {
|
||||
"title": "Villas",
|
||||
"subtitle": "Private Luxury Reflections"
|
||||
},
|
||||
"yachting": {
|
||||
"title": "Yachting",
|
||||
"subtitle": "Aegean Elegance"
|
||||
}
|
||||
}
|
||||
}
|
||||
6
app/dictionaries/index.ts
Normal file
6
app/dictionaries/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export const getDictionary = async (locale: 'en' | 'tr') => {
|
||||
if (locale === 'en') {
|
||||
return import('./en.json').then((module) => module.default);
|
||||
}
|
||||
return import('./tr.json').then((module) => module.default);
|
||||
};
|
||||
31
app/dictionaries/tr.json
Normal file
31
app/dictionaries/tr.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"nav": {
|
||||
"discover": "Keşfet"
|
||||
},
|
||||
"about": {
|
||||
"since": "Since 1980",
|
||||
"title": "SALMAKİS EFSANESİ",
|
||||
"p1": "Eski zamanlarda, bugün Bardakçı olarak bilinen koyda, adı Salmakis olan bir göl varmış. Bu gölde kendisiyle aynı ismi taşıyan Salmakis adından güzel bir peri yaşarmış. Bir gün, Salmakis gölde yıkanırken Hermes ve Afrodit’in oğlu Hermafrodit’i görmüş ve ona aşık olmuş.",
|
||||
"p2": "Ne var ki, Hermafrodit onu reddetmiş. Salmakis çok üzülmüş ve onları birleştirmeleri için tanrılara yalvarmış. Salmakis’e acıyan tanrılar, onları tek bir vücutta birleştirmiş. O günden sonra Salmakis ve Hermafrodit çift cinsiyete sahip tek bir vücutta yaşayıp gitmişler.",
|
||||
"company": "ŞİRKET BİLGİLERİ"
|
||||
},
|
||||
"footer": {
|
||||
"tagline": "A Heritage of Excellence Since 1980.",
|
||||
"copyright": "Salmakis Group. Tüm hakları saklıdır.",
|
||||
"created_by": "CREATED BY"
|
||||
},
|
||||
"sections": {
|
||||
"resort": {
|
||||
"title": "Resort",
|
||||
"subtitle": "Zamansız Bir Kaçış"
|
||||
},
|
||||
"villas": {
|
||||
"title": "Villas",
|
||||
"subtitle": "Özel Lüks Yansımalar"
|
||||
},
|
||||
"yachting": {
|
||||
"title": "Yachting",
|
||||
"subtitle": "Ege Zarafeti"
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
app/favicon.ico
BIN
app/favicon.ico
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 15 KiB |
@@ -1,26 +1,46 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
--primary-white: #ffffff;
|
||||
--turquoise: #008080;
|
||||
--sand-beige: #d7c4a3;
|
||||
--gold: #d4af37;
|
||||
--navy: #000080;
|
||||
--text-dark: #1a1a1a;
|
||||
--text-light: #fdfdfd;
|
||||
|
||||
--font-heading: var(--font-oswald), sans-serif;
|
||||
--font-text: var(--font-oswald), sans-serif;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-family: var(--font-text);
|
||||
color: var(--text-dark);
|
||||
background-color: var(--primary-white);
|
||||
overflow-x: hidden;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
animation: fadeIn 1s ease-out forwards;
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html
|
||||
lang="en"
|
||||
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
|
||||
>
|
||||
<body className="min-h-full flex flex-col">{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
65
app/page.tsx
65
app/page.tsx
@@ -1,65 +0,0 @@
|
||||
import Image from "next/image";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
|
||||
<main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={100}
|
||||
height={20}
|
||||
priority
|
||||
/>
|
||||
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
|
||||
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
|
||||
To get started, edit the page.tsx file.
|
||||
</h1>
|
||||
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
|
||||
Looking for a starting point or more instructions? Head over to{" "}
|
||||
<a
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
||||
>
|
||||
Templates
|
||||
</a>{" "}
|
||||
or the{" "}
|
||||
<a
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
className="font-medium text-zinc-950 dark:text-zinc-50"
|
||||
>
|
||||
Learning
|
||||
</a>{" "}
|
||||
center.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
|
||||
<a
|
||||
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Deploy Now
|
||||
</a>
|
||||
<a
|
||||
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Documentation
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user