Files
ostal.pl/wp-content/themes/ostal_WP/page-2101.php
Jacek Pyziak 0dd4cbe82f Add winter season promotional section and update project inquiry form
- Introduced a winter header and benefits section on page-2101.php to promote early bookings for spring-summer 2026.
- Enhanced the project inquiry form with additional fields and updated budget ranges for better user experience.
- Improved styling and layout for collaboration steps on page-2598.php, including hover effects and responsive design adjustments.
- Updated text content for clarity and consistency across both pages.
2026-02-09 22:13:49 +01:00

1008 lines
48 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php get_header(); ?>
<div class="wrapper--default">
<div id="primary" class="wrapper--inner">
<div id="content" role="main" class="span8 offset2">
<?php if ( have_posts() ) : ?>
<?php while ( have_posts() ) : the_post(); ?>
<article class="post mt-12">
<h1 class=""><?php the_title(); ?></h1>
<p class="subtitle"><?= get_field('subtitle'); ?></p>
<div class="the-content">
<?php the_content(); ?>
<?php wp_link_pages(); ?>
</div><!-- the-content -->
</article>
<?php endwhile; ?>
<?php else : ?>
<article class="post error">
<h1 class="404">Nothing posted yet</h1>
</article>
<?php endif; ?>
</div><!-- #content .site-content -->
</div><!-- #primary .content-area -->
</div>
<div id="primary" class="row-fluid mx-auto lg:container lg:mx-auto">
<div id="content" role="main" class="wrapper--inner span8 offset2">
<?php if (have_posts()) : ?>
<div id="app">
<div class="page-data post">
<!-- Zimowy nagłówek i sekcje -->
<div v-if="isWinterSeason" class="winter-header" style="text-align: center; margin-bottom: 40px; padding: 30px; background: linear-gradient(135deg, #88b14b 0%, #6a8f3a 100%); border-radius: 12px; color: white;">
<h2 style="font-size: 28px; font-weight: 700; margin-bottom: 10px; color: white;">Przyjmujemy zamówienia na realizacje wiosna-lato 2026</h2>
<p style="font-size: 18px; margin: 0; color: rgba(255,255,255,0.95);">Zarezerwuj termin i cenę już teraz.</p>
</div>
<div v-if="isWinterSeason" class="winter-benefits" style="background-color: #f8f9fa; padding: 40px 30px; border-radius: 12px; margin-bottom: 40px; border: 2px solid #88b14b;">
<h3 style="font-size: 24px; font-weight: 700; margin-bottom: 25px; color: #333; text-align: center;">Dlaczego warto zamówić zimą?</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin-bottom: 30px;">
<div style="display: flex; align-items: flex-start; gap: 12px;">
<span style="color: #88b14b; font-size: 24px; flex-shrink: 0;">✓</span>
<span style="font-size: 16px; color: #333;">Gwarancja ceny przed sezonem</span>
</div>
<div style="display: flex; align-items: flex-start; gap: 12px;">
<span style="color: #88b14b; font-size: 24px; flex-shrink: 0;">✓</span>
<span style="font-size: 16px; color: #333;">Priorytetowy termin montażu</span>
</div>
<div style="display: flex; align-items: flex-start; gap: 12px;">
<span style="color: #88b14b; font-size: 24px; flex-shrink: 0;">✓</span>
<span style="font-size: 16px; color: #333;">Spokojne przygotowanie projektu</span>
</div>
<div style="display: flex; align-items: flex-start; gap: 12px;">
<span style="color: #88b14b; font-size: 24px; flex-shrink: 0;">✓</span>
<span style="font-size: 16px; color: #333;">Brak kolejek produkcyjnych</span>
</div>
</div>
<div style="background-color: white; padding: 25px; border-radius: 8px; margin-bottom: 25px; border-left: 4px solid #88b14b;">
<p style="font-size: 16px; margin: 0; color: #666;">Podpisz umowę zimą zarezerwuj termin realizacji na wiosnę.</p>
</div>
<div style="background: linear-gradient(135deg, #88b14b 0%, #6a8f3a 100%); padding: 25px; border-radius: 8px; color: white;">
<h4 style="font-size: 18px; font-weight: 600; margin-bottom: 15px; color: white;">ZIMOWY BONUS</h4>
<ul style="list-style: none; padding: 0; margin: 0;">
<li style="margin-bottom: 10px; display: flex; align-items: flex-start; gap: 10px;">
<span style="font-size: 20px; flex-shrink: 0;">🎁</span>
<span style="font-size: 15px;">Darmowy upgrade szyby z ochroną przeciwsłoneczną gratis.</span>
</li>
<li style="margin-bottom: 10px; display: flex; align-items: flex-start; gap: 10px;">
<span style="font-size: 20px; flex-shrink: 0;">🎁</span>
<span style="font-size: 15px;">Darmowa konsultacja projektowa</span>
</li>
<li style="display: flex; align-items: flex-start; gap: 10px;">
<span style="font-size: 20px; flex-shrink: 0;">🎁</span>
<span style="font-size: 15px;">Priorytetowy termin montażu</span>
</li>
</ul>
</div>
<div style="text-align: center; margin-top: 25px; padding: 20px; background-color: #fff3cd; border-radius: 8px; border: 2px dashed #88b14b;">
<p style="font-size: 18px; font-weight: 600; margin: 0; color: #333;">
⏰ Dostępne terminy realizacji na sezon 2026: <span style="color: #88b14b; font-size: 24px;">{{ availableSlots }}</span>
</p>
</div>
</div>
<div id="calc-container">
<div class="calc-bar-fix">
<div id="calc-bar" ref="calcBar">
<div class="calc-s calc-placeholder"></div>
<!-- Step 1 -->
<div class="calc-s active" id="calc-ch-0" ref="slide1">
<div class="calc-box gray-bg box-shadow">
<div class="calc-box--data">
<div class="calc-box--title">
<h2>Krok 1</h2>
</div>
<ul class="calc-radio" v-if="isLoaded">
<li v-for="(step, index) in acfData.acf.step_1" :key="index" @click="setStep_1(step.text, index)" :class="{ selected: index === selected_option_1 }">
<div class="radio"></div>
<div class="radio-text">
<div class="radio-title">{{ step.text }}</div>
</div>
</li>
</ul>
</div>
<div class="next disabled" v-if="!configData.step_1">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
<div v-else>
<div class="next" v-if="windowWidth > 1000" @click="swipeSlide('slide2', -125, 2)">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
<div class="next" v-else @click="swipeSlide('slide2', -100, 2)">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
</div>
</div>
</div>
<!-- Step 2 -->
<div class="calc-s" id="calc-ch-1" ref="slide2">
<div class="calc-box gray-bg box-shadow">
<div class="calc-box--data">
<div class="calc-box--title">
<h2>Krok 2</h2>
</div>
<div class="slide-data">
<img :src="acfData.acf.step_2.image" alt="">
<ul class="calc-texts" v-if="isLoaded && this.selected_option_1 !== 2">
<li>
<p>{{ acfData.acf.step_2.width }} ( od 2m do 7m ):<br><span v-if="configData.step_2.width">{{ configData.step_2.width / 1000 }}m</span></p>
<input v-model="configData.step_2.width" ref="rangeWidth" type="range" name="width" id="width" value="2000" min="2000" max="7000" step="500">
</li>
<li>
<p>{{ acfData.acf.step_2.length }} ( od 2m do 7m ):<br><span v-if="configData.step_2.length">{{ configData.step_2.length / 1000 }}m</span></p>
<input v-model="configData.step_2.length" type="range" name="length" id="length" min="2000" max="7000" step="500">
</li>
<li>
<p>{{ acfData.acf.step_2.height }} ( do 2,8m lub do 3,5m ):<br><span v-if="configData.step_2.height">{{ configData.step_2.height / 1000 }}m</span></p>
<input v-model="configData.step_2.height" ref="rangeHeight" type="range" name="height" id="height" min="2800" max="3500" step="100">
</li>
</ul>
<!-- Second variant -->
<ul class="calc-texts" v-if="isLoaded && this.selected_option_1 === 2">
<li>
<p>{{ acfData.acf.step_2.width }} ( od 5m do 7,5m ):<br><span v-if="configData.step_2.width">{{ configData.step_2.width / 1000 }}m</span></p>
<input v-model="configData.step_2.width" ref="rangeWidth" type="range" name="width" id="width" min="5000" max="7500" step="500">
</li>
<li>
<p>{{ acfData.acf.step_2.length }} ( od 2,5m do 7,5m ):<br><span v-if="configData.step_2.length">{{ configData.step_2.length / 1000 }}m</span></p>
<input v-model="configData.step_2.length" type="range" name="length" id="length" min="2500" max="7500" step="500">
</li>
<li class="c-select">
<p>Wjazd standardowy lub wjazd boczny</p>
<select v-model="configData.step_2.entry">
<option value="Standardowy od niskiej albo wysokiej strony">Standardowy od niskiej albo wysokiej strony</option>
<option value="Boczny z bocznej skośnej strony dachu">Boczny z bocznej skośnej strony dachu</option>
</select>
</li>
</ul>
<p v-if="selected_option_1 === 0" class="budget-info" style="margin-top: 20px; font-size: 14px; color: #666; font-style: italic;">
Orientacyjny budżet całorocznego ogrodu zimowego zwykle mieści się w przedziale 120 000 180 000 zł netto
</p>
</div>
</div>
<div v-if="windowWidth > 1000">
<div class="prev" @click="swipeSlide('slide1', -55, 1)">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
<div class="next" @click="swipeSlide('slide3', -195, 3)">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
</div>
<div v-else>
<div class="prev" @click="swipeSlide('slide1', 0, 1)">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
<div class="next" @click="swipeSlide('slide3', -200, 3)">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
</div>
</div>
</div>
<!-- Step 3 -->
<div class="calc-s" id="calc-ch-2" ref="slide3">
<div class="calc-box gray-bg box-shadow">
<div class="calc-box--data">
<div class="calc-box--title">
<h2>Krok 3</h2>
</div>
<div v-if="selected_option_1 === 0" style="margin-top: 20px; margin-bottom: 30px;">
<h3 style="font-size: 20px; font-weight: 600; margin-bottom: 15px; color: #333;">Orientacyjny budżet realizacji</h3>
<p style="font-size: 14px; line-height: 1.6; color: #666; margin-bottom: 20px;">
Na podstawie wybranego typu konstrukcji i podanych wymiarów możemy pokazać orientacyjny budżet realizacji.<br><br>
Każdy ogród zimowy projektujemy indywidualnie, dlatego podane kwoty mają charakter przykładowy i służą jedynie do wstępnej oceny, czy inwestycja mieści się w zakładanym budżecie.
</p>
</div>
<ul class="calc-radio" v-if="isLoaded && this.selected_option_1 !== 2">
<li
v-for="(step, index) in acfData_step_3"
:key="index"
@click="setStep_3(step.text, index, step.extra_option)"
:class="{ selected: index === selected_option_3 || (step.extra_option && this.configData.step_3_extra) }"
>
<div class="radio"></div>
<div class="radio-text">
<div class="radio-title">{{ step.text }}</div>
</div>
</li>
</ul>
<!-- Second variant -->
<ul class="calc-radio" v-if="isLoaded && this.selected_option_1 === 2">
<li
v-for="(step, index) in acfData_step_3"
:key="index"
@click="setStep_3(step.text, index, step.extra_option)"
:class="{ selected: index === selected_option_3 || (step.extra_option && this.configData.step_3_extra) }"
>
<div class="radio"></div>
<div class="radio-text">
<div class="radio-title">{{ step.text }}</div>
</div>
</li>
</ul>
<ul class="calc-texts" v-if="isLoaded && this.selected_option_1 === 2">
<li class="c-select">
<p>Klient prywatny , klient biznesowy</p>
<select v-model="configData.step_3_extra">
<option value="Prywatny">Prywatny</option>
<option value="Biznesowy B2B">Biznesowy B2B</option>
</select>
</li>
</ul>
<div class="price_examples" v-if="selected_option_1 === 0" style="margin-top: 20px;">
<h3 style="font-size: 20px; font-weight: 600; margin-bottom: 20px; color: #333;">PRZYKŁADY CEN</h3>
<div class="price_example-item" style="background-color: #f8f9fa; padding: 20px; border-radius: 8px; margin-bottom: 15px; border-left: 4px solid #88b14b;">
<h4 style="font-size: 16px; font-weight: 600; margin-bottom: 10px; color: #333;">Przykład 1 Ogród zimowy 4 x 5 m (ok. 20 m2)</h4>
<ul style="list-style: none; padding: 0; margin: 10px 0; font-size: 14px; color: #666;">
<li style="margin-bottom: 5px;">- dach jednospadowy</li>
<li style="margin-bottom: 5px;">- szyby zespolone jednokomorowe Ug=1,1</li>
<li style="margin-bottom: 5px;">- markiza dachowa zewnętrzna w cenie.</li>
</ul>
<p style="font-size: 15px; font-weight: 600; color: #88b14b; margin-top: 15px;">Orientacyjny budżet realizacji 120 000 150 000 zł netto</p>
</div>
<div class="price_example-item" style="background-color: #f8f9fa; padding: 20px; border-radius: 8px; margin-bottom: 15px; border-left: 4px solid #88b14b;">
<h4 style="font-size: 16px; font-weight: 600; margin-bottom: 10px; color: #333;">Przykład 2 Ogród zimowy 3 x 4 m (ok. 12 m2)</h4>
<ul style="list-style: none; padding: 0; margin: 10px 0; font-size: 14px; color: #666;">
<li style="margin-bottom: 5px;">- dach jednospadowy</li>
<li style="margin-bottom: 5px;">- szyby zespolone jednokomorowe Ug=1,1</li>
<li style="margin-bottom: 5px;">- markiza zewnętrzna.</li>
</ul>
<p style="font-size: 15px; font-weight: 600; color: #88b14b; margin-top: 15px;">Orientacyjny budżet realizacji 80 000 100 000 zł netto</p>
</div>
<div class="price_example-item" style="background-color: #f8f9fa; padding: 20px; border-radius: 8px; margin-bottom: 15px; border-left: 4px solid #88b14b;">
<h4 style="font-size: 16px; font-weight: 600; margin-bottom: 10px; color: #333;">Przykład 3 Ogród zimowy 5 x 6 m (ok. 30 m2)</h4>
<ul style="list-style: none; padding: 0; margin: 10px 0; font-size: 14px; color: #666;">
<li style="margin-bottom: 5px;">- Dach jednospadowy lub wielospadowy.</li>
<li style="margin-bottom: 5px;">- większe przeszklenia szyby Ug=1,1</li>
<li style="margin-bottom: 5px;">- markiza zewnętrzna.</li>
</ul>
<p style="font-size: 15px; font-weight: 600; color: #88b14b; margin-top: 15px;">Orientacyjny budżet realizacji 170 000 210 000 zł netto</p>
</div>
<div style="margin-top: 30px; padding: 20px; background-color: #fff; border-radius: 8px;">
<p style="font-size: 15px; line-height: 1.6; color: #333; margin-bottom: 20px; font-weight: 500;">
Większość naszych realizacji mieści się w powyższych zakresach po dopasowaniu projektu do potrzeb i budżetu klienta.
</p>
<h4 style="font-size: 16px; font-weight: 600; margin-bottom: 15px; color: #333;">Ostateczna cena zależy m.in. od:</h4>
<ul style="list-style: none; padding: 0; margin: 0 0 25px 0; font-size: 14px; color: #666;">
<li style="margin-bottom: 8px; padding-left: 20px; position: relative;">
<span style="position: absolute; left: 0; color: #88b14b;">•</span> bryły i rodzaju dachu
</li>
<li style="margin-bottom: 8px; padding-left: 20px; position: relative;">
<span style="position: absolute; left: 0; color: #88b14b;">•</span> ilości i rodzaju przeszkleń
</li>
<li style="margin-bottom: 8px; padding-left: 20px; position: relative;">
<span style="position: absolute; left: 0; color: #88b14b;">•</span> systemów otwierania
</li>
<li style="margin-bottom: 8px; padding-left: 20px; position: relative;">
<span style="position: absolute; left: 0; color: #88b14b;">•</span> dodatkowego wyposażenia
</li>
</ul>
<a href="https://ostal.pl/kontakt/"
style="display: inline-block; background-color: #88b14b; color: white; padding: 15px 30px; border-radius: 6px; text-decoration: none; font-size: 16px; font-weight: 600; text-align: center; transition: background-color 0.3s ease; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"
onmouseover="this.style.backgroundColor='#6a8f3a'"
onmouseout="this.style.backgroundColor='#88b14b'">
Porozmawiajmy o Twoim projekcie
</a>
</div>
</div>
</div>
<div v-if="windowWidth > 1000">
<div class="prev" @click="swipeSlide('slide2', -125, 2)">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
<div class="next disabled" v-if="!configData.step_3">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
<div class="next" v-else @click="swipeSlide('slide4', -265, 4)">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
</div>
<div v-else>
<div class="prev" @click="swipeSlide('slide2', -100, 2)">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
<div class="next disabled" v-if="!configData.step_3">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
<div class="next" v-else @click="swipeSlide('slide4', -300, 4)">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
</div>
</div>
</div>
<!-- Step 4 -->
<div class="calc-s" id="calc-ch-2" ref="slide4">
<div class="calc-box gray-bg box-shadow">
<div class="calc-box--data">
<div class="calc-box--title">
<h2>Krok 4</h2>
</div>
<div style="margin-top: 20px; margin-bottom: 30px;">
<h3 style="font-size: 20px; font-weight: 600; margin-bottom: 15px; color: #333;">Wyślij zapytanie o swój projekt</h3>
<p style="font-size: 14px; line-height: 1.6; color: #666; margin-bottom: 10px;">
Na podstawie podanych informacji przygotujemy wstępne dopasowanie projektu oraz orientacyjny budżet realizacji.
</p>
<p style="font-size: 14px; line-height: 1.6; color: #666;">
Realizacje całorocznych ogrodów zimowych rozpoczynają się od około 100 000 zł netto.
</p>
</div>
<ul class="calc-texts" v-if="isLoaded">
<li>
<p>Imię i nazwisko</p>
<input v-model="configData.step_4.name" type="text" name="name" id="calc_name" class="input" require>
</li>
<li>
<p>Email</p>
<input v-model="configData.step_4.email" type="email" name="email" id="calc_email" class="input" require>
</li>
<li>
<p>Telefon</p>
<input v-model="configData.step_4.phone" type="tel" name="phone" id="calc_phone" class="input" require>
</li>
<li>
<p>Miejsce montażu i kod pocztowy</p>
<input v-model="configData.step_4.miejsce_montazu" type="text" name="miejsce_montazu" id="calc_miejsce_montazu" class="input" require>
</li>
<li>
<p>Załącznik (opcjonalnie)</p>
<div class="file-upload-area"
:class="{ 'dragover': isDragOver }"
@dragover.prevent="isDragOver = true"
@dragleave.prevent="isDragOver = false"
@drop.prevent="handleFileDrop">
<input type="file"
ref="fileInput"
name="attachment"
id="calc_attachment"
class="file-input-hidden"
multiple
accept=".jpg,.jpeg,.png,.pdf,.doc,.docx,.xls,.xlsx"
@change="handleFileSelect">
<div class="file-upload-content" @click="$refs.fileInput.click()">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#88b14b" stroke-width="2">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="17 8 12 3 7 8"></polyline>
<line x1="12" y1="3" x2="12" y2="15"></line>
</svg>
<p class="upload-text">Przeciągnij pliki tutaj lub kliknij aby wybrać</p>
<p class="upload-hint">Dozwolone: JPG, PNG, PDF, DOC, DOCX, XLS, XLSX (max 10MB każdy)</p>
</div>
</div>
<div v-if="uploadedFiles.length > 0" class="uploaded-files-list">
<div v-for="(file, index) in uploadedFiles" :key="index" class="uploaded-file-item">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#88b14b" stroke-width="2">
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path>
<polyline points="13 2 13 9 20 9"></polyline>
</svg>
<span class="file-name">{{ file.name }}</span>
<span class="file-size">({{ formatFileSize(file.size) }})</span>
<button type="button" class="remove-file-btn" @click="removeFile(index)">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#dc3545" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
</div>
<p v-if="fileError" class="file-error">{{ fileError }}</p>
</li>
</ul>
<ul class="calc-texts" style="margin-top: 30px;">
<li class="c-select">
<p>Planowany termin realizacji:</p>
<select v-model="configData.step_4.planned_season">
<option value="">-- Wybierz --</option>
<option value="Wiosna">Wiosna</option>
<option value="Lato">Lato</option>
<option value="Jesień">Jesień</option>
<option value="Zima">Zima</option>
<option value="Jeszcze nie wiem">Jeszcze nie wiem</option>
</select>
</li>
<li class="c-select">
<p>Budżet inwestycji:</p>
<select v-model="configData.step_4.budget_range">
<option value="">-- Wybierz --</option>
<option value="do 100 tyś.">do 100 tyś.</option>
<option value="100-150 tyś.">100-150 tyś.</option>
<option value="150-200 tyś.">150-200 tyś.</option>
<option value="Powyżej 200 tyś.">Powyżej 200 tyś.</option>
<option value="Chcę omówić">Chcę omówić</option>
</select>
</li>
<li class="c-select">
<p>Czy jesteś właścicielem nieruchomości?</p>
<select v-model="configData.step_4.is_owner">
<option value="">-- Wybierz --</option>
<option value="Tak">Tak</option>
<option value="Nie">Nie</option>
</select>
</li>
</ul>
<div v-if="selected_option_1 === 0" class="checkboxes-confirm" style="margin-top: 30px;">
<label style="display: flex; align-items: flex-start; gap: 10px; margin-bottom: 15px; cursor: pointer;">
<input type="checkbox" v-model="configData.step_4.confirm_budget" style="margin-top: 4px; flex-shrink: 0;">
<span style="font-size: 14px;">Rozumiem, że realizacje całorocznych ogrodów zimowych zaczynają się od około 120 000 zł netto.</span>
</label>
<label style="display: flex; align-items: flex-start; gap: 10px; margin-bottom: 15px; cursor: pointer;">
<input type="checkbox" v-model="configData.step_4.confirm_deposit" style="margin-top: 4px; flex-shrink: 0;">
<span style="font-size: 14px;">Rozumiem, że do rezerwacji terminu realizacji wymagana jest zaliczka (ok. 40%).</span>
</label>
</div>
<button class="btn_send btn btn-primary" type="submit" @click="sendEmail()" :disabled="isSended" style="margin-top: 30px;">Wyślij zapytanie i sprawdź budżet projektu</button>
</div>
<div v-if="windowWidth > 1000">
<div class="prev" @click="swipeSlide('slide3', -195, 3)">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
</div>
<div v-else>
<div class="prev" @click="swipeSlide('slide3', -200, 3)">
<svg class="svg-arrow" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M11.166 23.963L22.36 17.5C23.79 16.676 23.79 15.325 22.36 14.5L11.165 8.037C9.73502 7.211 8.56702 7.887 8.56702 9.537V22.463C8.56702 24.113 9.73702 24.789 11.165 23.963H11.166Z" fill="#F8F8F8" />
</svg>
</div>
</div>
</div>
</div>
</div>
<div id="progress-bar">
<div class="progress-bar">
<div class="progress-bar-line" :style="{ width: progressBar + '%' }"></div>
</div>
<!-- <progress id="calculate-progres" :value="progressBar" max="100"></progress> -->
</div>
</div>
</div>
<section class="section-header xl:container xl:mx-auto">
<h2 class="title">Przykładowe realizacje</h2>
</section>
<section class="section-realizacje flex flex-row items-center flex-wrap sm:justify-start xl:container xl:mx-auto">
<?php
$related = get_posts(
array(
'category__in' => wp_get_post_categories($post->ID),
'numberposts' => 4,
'post_type' => 'realizacje',
'post__not_in' => array($post->ID)
)
);
if ($related) foreach ($related as $post) {
setup_postdata($post); ?>
<a href="<?= the_permalink(); ?>" class="recent-realizations-item" style="background-image: url(<?= get_the_post_thumbnail_url(); ?>)">
<p class="absolute text-xl min-w-full min-h-full bg-opacity-90 h-full w-full flex items-center justify-center text-white text-center">
<?= get_the_title(); ?></p>
</a>
<?php } wp_reset_postdata(); ?>
</section>
</div>
</div>
<?php else : ?>
<article class="post error">
<h1 class="404">Nothing posted yet</h1>
</article>
<?php endif; ?>
</div><!-- #content .site-content -->
</div>
<script>
var app = Vue.createApp({
el: '#app',
data() {
return {
isLoaded: false,
isSended: false,
selected_option_1: null,
selected_option_3: null,
progressBar: 25,
windowWidth: 0,
uploadedFiles: [],
isDragOver: false,
fileError: '',
isWinterSeason: false,
availableSlots: 8,
acfData: {
acf: {}
},
acfData_step_3: {
},
configData: {
step_1: '',
step_2: {},
step_3: '',
step_3_extra: '',
step_4: {
name: '',
email: '',
phone: '',
miejsce_montazu: '',
planned_season: '',
budget_range: '',
is_owner: '',
confirm_budget: false,
confirm_deposit: false,
}
}
}
},
async beforeMount() {
this.checkWinterSeason();
this.getWindowWidth();
try {
const res = await fetch(`<?php echo get_home_url() ?>/wp-json/wp/v2/pages/<?php echo get_the_ID() ?>`);
const data = await res.json();
this.acfData = data;
this.isLoaded = true;
} catch (error) {
console.error('Error fetching data:', error);
this.isLoaded = true;
}
},
methods: {
checkWinterSeason() {
const now = new Date();
const month = now.getMonth(); // 0-11 (0 = styczeń, 11 = grudzień)
// Sezon zimowy: listopad (10), grudzień (11), styczeń (0), luty (1), marzec (2), kwiecień (3)
this.isWinterSeason = month >= 10 || month <= 3;
},
setStep_1(value, index) {
this.configData.step_1 = value
this.selected_option_1 = index
this.updateStep2Content();
this.updateStep3Content();
},
updateStep2Content() {
this.configData.step_2 = {};
// Tutaj umieszczasz logikę, która zmienia zawartość kroku 2 w zależności od wyboru w kroku 1.
// Przykład: zmiana obrazka w kroku 2 na podstawie wyboru
if (this.selected_option_1 === 0) { // Zakładając, że pierwsza opcja w kroku 1 zmienia obraz na 'obrazek1.webp'
this.acfData.acf.step_2.image = '/wp-content/uploads/2024/02/konfigurator-ogrod-zimowy.png';
} else if (this.selected_option_1 === 1) { // Druga opcja zmienia obraz na 'obrazek2.webp'
this.acfData.acf.step_2.image = '/wp-content/uploads/2024/02/konfigurator-ogrod-letni.png';
} else if (this.selected_option_1 === 2) { // Druga opcja zmienia obraz na 'obrazek2.webp'
this.acfData.acf.step_2.image = '/wp-content/uploads/2024/02/konfigurator-ogrod-dach-jednospadowy.png';
}
// I tak dalej dla innych opcji...
},
updateStep3Content() {
this.configData.step_3 = {};
this.configData.step_3_extra = '';
if (this.selected_option_1 === 0) {
this.acfData_step_3 = this.acfData.acf.step_3.items_1
} else if (this.selected_option_1 === 1) {
this.acfData_step_3 = this.acfData.acf.step_3.items_2
} else if (this.selected_option_1 === 2) {
this.acfData_step_3 = this.acfData.acf.step_3.items_3
}
},
setStep_3(value, index, extra_option = false) {
if (!extra_option) {
this.configData.step_3 = value
this.selected_option_3 = index
this.configData.step_3_extra = ''
} else {
this.configData.step_3_extra = value
}
},
swipeSlide(refName, percent, progress) {
const myDiv = this.$refs.calcBar;
const slide = this.$refs[refName];
const calcSElements = myDiv.querySelectorAll('.calc-s');
calcSElements.forEach((element) => {
element.classList.remove('active');
});
myDiv.style.marginLeft = `${percent}%`;
slide.classList.add('active');
this.progressBar = ((100 / 4) * progress)
},
sendEmail() {
if (this.checkFields()) {
const url = '/konfigurator-mail.php';
const formData = new FormData();
formData.append('configData', JSON.stringify(this.configData));
// Dodaj wszystkie załączone pliki
this.uploadedFiles.forEach((file, index) => {
formData.append(`attachment_${index}`, file);
});
formData.append('attachments_count', this.uploadedFiles.length);
// const data = {
// configData: this.configData,
// };
this.isSended = true
// const options = {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json',
// },
// body: JSON.stringify(data),
// };
fetch(url, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
this.isSended = false
this.resetAllFields()
this.swipeSlide('slide1', 0)
window.location.href = "/konfigurator-podziekowania/";
})
.catch(error => {
this.isSended = false
console.error(error);
});
} else {
alert('Proszę uzupełnić wszystkie pola.');
}
},
areAllFieldsFilled(configData) {
const checkFields = (data, ignoreKeys = []) => {
for (const key in data) {
if (ignoreKeys.includes(key)) continue;
const value = data[key];
if (typeof value === 'object' && value !== null) {
if (!checkFields(value, ignoreKeys)) {
return false;
}
} else if (value === '' || value === null || value === undefined) {
return false;
} else if (typeof value === 'string' && value.trim() === '') {
return false;
}
}
return true;
};
return checkFields(configData, ['step_3_extra', 'confirm_budget', 'confirm_deposit']);
},
checkFields() {
const basicFieldsFilled = this.areAllFieldsFilled(this.configData);
// Dla ogrodów zimowych (selected_option_1 === 0) wymagaj checkboxów
if (this.selected_option_1 === 0) {
if (!this.configData.step_4.confirm_budget ||
!this.configData.step_4.confirm_deposit) {
return false;
}
}
return basicFieldsFilled;
},
resetAllFields() {
const stack = [this.configData];
this.selected_option_1 = null
this.selected_option_3 = null
this.uploadedFiles = []
this.fileError = ''
while (stack.length > 0) {
const obj = stack.pop();
for (const key in obj) {
const value = obj[key];
if (typeof value === 'object') {
stack.push(value);
} else {
if (Array.isArray(value)) {
obj[key] = [];
} else if (typeof value === 'number') {
obj[key] = 0;
} else if (typeof value === 'string') {
obj[key] = '';
} else if (typeof value === 'boolean') {
obj[key] = false;
} else {
obj[key] = null;
}
}
}
}
},
handleFileDrop(e) {
this.isDragOver = false;
const files = Array.from(e.dataTransfer.files);
this.addFiles(files);
},
handleFileSelect(e) {
const files = Array.from(e.target.files);
this.addFiles(files);
},
addFiles(files) {
this.fileError = '';
for (const file of files) {
const validation = this.validateFile(file);
if (!validation.valid) {
this.fileError = validation.error;
continue;
}
// Sprawdź czy plik już nie został dodany
const exists = this.uploadedFiles.some(f => f.name === file.name && f.size === file.size);
if (!exists) {
this.uploadedFiles.push(file);
}
}
// Wyczyść input aby móc dodać ten sam plik ponownie
if (this.$refs.fileInput) {
this.$refs.fileInput.value = '';
}
},
validateFile(file) {
const maxSize = 10 * 1024 * 1024; // 10MB
const allowedTypes = [
'image/jpeg',
'image/jpg',
'image/png',
'application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
];
const allowedExtensions = ['.jpg', '.jpeg', '.png', '.pdf', '.doc', '.docx', '.xls', '.xlsx'];
// Sprawdź rozszerzenie pliku
const fileName = file.name.toLowerCase();
const hasValidExtension = allowedExtensions.some(ext => fileName.endsWith(ext));
if (!hasValidExtension && !allowedTypes.includes(file.type)) {
return {
valid: false,
error: `Nieprawidłowy typ pliku: ${file.name}. Dozwolone: JPG, PNG, PDF, DOC, DOCX, XLS, XLSX`
};
}
if (file.size > maxSize) {
return {
valid: false,
error: `Plik ${file.name} jest za duży. Maksymalny rozmiar: 10MB`
};
}
return { valid: true };
},
removeFile(index) {
this.uploadedFiles.splice(index, 1);
this.fileError = '';
},
formatFileSize(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
},
getWindowWidth() {
this.windowWidth = window.innerWidth;
},
},
mounted() {
window.addEventListener('resize', this.getWindowWidth);
this.getWindowWidth();
},
computed: {
debug() {
return this.configData
}
},
watch: {
configData: {
deep: true,
handler() {
console.log('configData changed:\n', JSON.stringify(this.configData, null, 2));
}
}
},
beforeDestroy() {
window.removeEventListener('resize', this.getWindowWidth);
},
})
app.mount('#app')
</script>
<style>
.file-upload-area {
border: 2px dashed #88b14b;
border-radius: 8px;
padding: 30px;
text-align: center;
background-color: #f8f9fa;
transition: all 0.3s ease;
cursor: pointer;
margin-top: 10px;
}
.file-upload-area:hover {
background-color: #e8f5e9;
border-color: #6a8f3a;
}
.file-upload-area.dragover {
background-color: #e8f5e9;
border-color: #6a8f3a;
transform: scale(1.02);
}
.file-input-hidden {
display: none;
}
.file-upload-content {
cursor: pointer;
}
.file-upload-content svg {
margin: 0 auto 15px;
display: block;
}
.upload-text {
font-size: 16px;
font-weight: 500;
color: #333;
margin: 10px 0 5px;
}
.upload-hint {
font-size: 12px;
color: #666;
margin: 0;
}
.uploaded-files-list {
margin-top: 20px;
}
.uploaded-file-item {
display: flex;
align-items: center;
gap: 10px;
padding: 12px;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 6px;
margin-bottom: 8px;
transition: all 0.2s ease;
}
.uploaded-file-item:hover {
background-color: #f8f9fa;
border-color: #88b14b;
}
.uploaded-file-item svg:first-child {
flex-shrink: 0;
}
.file-name {
flex: 1;
font-size: 14px;
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.file-size {
font-size: 12px;
color: #666;
flex-shrink: 0;
}
.remove-file-btn {
background: none;
border: none;
cursor: pointer;
padding: 4px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
transition: background-color 0.2s ease;
flex-shrink: 0;
}
.remove-file-btn:hover {
background-color: #fee;
}
.file-error {
color: #dc3545;
font-size: 13px;
margin-top: 10px;
padding: 8px 12px;
background-color: #fee;
border-radius: 4px;
border-left: 3px solid #dc3545;
}
</style>
<?php get_footer(); ?>