Files
ostal.pl/wp-content/themes/ostal_WP/page-2101.php

992 lines
44 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">
<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>
<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="tabs_examples" v-if="selected_option_1 === 0" style="margin-top: 50px;">
<h3>Przykłady cen ogrodu zimowego 4x5m</h3>
<div class="tabs_examples-nav">
<button :class="{ active: activeExampleTab === 'standard' }" @click="activeExampleTab = 'standard'">Standard</button>
<button :class="{ active: activeExampleTab === 'comfort' }" @click="activeExampleTab = 'comfort'">Comfort</button>
<button :class="{ active: activeExampleTab === 'premium' }" @click="activeExampleTab = 'premium'">Premium</button>
</div>
<div class="tabs_examples-content">
<!-- Standard -->
<div class="tabs_examples-panel" v-if="activeExampleTab === 'standard'">
<div class="tabs_examples-img">
<img src="/wp-content/uploads/2026/02/image-1.png" alt="Ogród zimowy Standard 4x5m">
</div>
<div class="tabs_examples-desc">
<h4>Ogród zimowy Classic 1 Standard.<br>Cena brutto 114.390,00 zł (z VAT 8%)</h4>
<p>- Szyby izolacyjne Ug=1,1 na dachu i ścianach.<br>
- 1 szt. drzwi tarasowe uchylno-przesuwnymi PSK na frontowej ścianie.<br>
- Bez osłon przeciwsłonecznych. (w zacienione miejsca)</p>
</div>
</div>
<!-- Comfort -->
<div class="tabs_examples-panel" v-if="activeExampleTab === 'comfort'">
<div class="tabs_examples-img">
<img src="/wp-content/uploads/2026/02/image-2.png" alt="Ogród zimowy Comfort 4x5m">
</div>
<div class="tabs_examples-desc">
<h4>Ogród zimowy Classic 2 Comfort.<br>Cena brutto 142.819,00 zł (z VAT 8%)</h4>
<p>- Szyby izolacyjne Ug=1,1 na dachu i ścianach.<br>
- 2 szt. drzwi tarasowych uchylno-przesuwnymi PSK na ścianie frontowej i ścianie bocznej.<br>
- Komplet osłon przeciwsłonecznych na dachu. 2 osłony Veranda ster. Elektr + pilot.</p>
</div>
</div>
<!-- Premium -->
<div class="tabs_examples-panel" v-if="activeExampleTab === 'premium'">
<div class="tabs_examples-img">
<img src="/wp-content/uploads/2026/02/image-3.png" alt="Ogród zimowy Premium 4x5m">
</div>
<div class="tabs_examples-desc">
<h4>Ogród zimowy Classic 3 Premium.<br>Cena brutto 203.550,00 zł (z VAT 8%)</h4>
<p>- Szyby Energooszczędne Ug=0,5 na dachu i ścianach.<br>
- 1 szt. drzwi tarasowych uchylno-przesuwnymi PSK na frontowej ścianie + 1 szt. drzwi unoszono przesuwne HS na bocznej ścianie.<br>
- Komplet osłon przeciwsłonecznych na dachu. 2 osłony Veranda ster. Elektr. + pilot + czujnik słoneczno-wiatrowy.<br>
- Osłony przeciwsłoneczne ścian żaluzje C80 sterowane ele. + pilot.<br>
- Okno HS w dachu przesuwane, sterowane ele.<br>
- oświetlenie punktowe LED z pilotem.</p>
</div>
</div>
</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>
<ul class="calc-texts" v-if="isLoaded">
<li>
<p>{{acfData.acf.step_4.miejsce_montazu}}</p>
<input v-model="configData.step_4.miejsce_montazu" type="text" name="miejsce_montazu" id="calc_miejsce_montazu" class="input" require>
</li>
<li>
<p>{{acfData.acf.step_4.name}}</p>
<input v-model="configData.step_4.name" type="text" name="name" id="calc_name" class="input" require>
</li>
<li>
<p>{{acfData.acf.step_4.email}}</p>
<input v-model="configData.step_4.email" type="email" name="email" id="calc_email" class="input" require>
</li>
<li>
<p>{{acfData.acf.step_4.phone}}</p>
<input v-model="configData.step_4.phone" type="tel" name="phone" id="calc_phone" class="input" require>
</li>
<li>
<p>{{acfData.acf.step_4.attachment}}</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 v-if="selected_option_1 === 0" class="calc-texts" style="margin-top: 20px;">
<li class="c-datepicker">
<p>Planowany termin montażu</p>
<div class="datepicker-wrapper">
<div class="datepicker-input" @click="showDatepicker = !showDatepicker">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#88b14b" stroke-width="2">
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
<line x1="16" y1="2" x2="16" y2="6"></line>
<line x1="8" y1="2" x2="8" y2="6"></line>
<line x1="3" y1="10" x2="21" y2="10"></line>
</svg>
<span :class="{ placeholder: !configData.step_4.planned_date }">
{{ configData.step_4.planned_date || 'Wybierz datę' }}
</span>
<svg class="datepicker-chevron" :class="{ open: showDatepicker }" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#999" stroke-width="2">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</div>
<div class="datepicker-calendar" v-if="showDatepicker">
<div class="datepicker-header">
<button type="button" @click.stop="dpPrevMonth">&lsaquo;</button>
<span>{{ dpMonthName }} {{ dpYear }}</span>
<button type="button" @click.stop="dpNextMonth">&rsaquo;</button>
</div>
<div class="datepicker-weekdays">
<span>Pn</span><span>Wt</span><span>Śr</span><span>Cz</span><span>Pt</span><span>Sb</span><span>Nd</span>
</div>
<div class="datepicker-days">
<span v-for="blank in dpFirstDayOffset" class="datepicker-day empty"></span>
<span
v-for="day in dpDaysInMonth"
:key="day"
class="datepicker-day"
:class="{
selected: dpIsSelected(day),
disabled: dpIsDisabled(day),
today: dpIsToday(day)
}"
@click.stop="dpSelectDay(day)"
>{{ day }}</span>
</div>
</div>
</div>
</li>
<li class="c-select">
<p>Orientacyjny budżet</p>
<select v-model="configData.step_4.budget_range">
<option value="">-- Wybierz --</option>
<option value="100.000 zł - 150.000 zł">100.000 zł - 150.000 zł</option>
<option value="150.000 zł - 200.000 zł">150.000 zł - 200.000 zł</option>
<option value="250.000 zł - 300.000 zł">250.000 zł - 300.000 zł</option>
<option value="Pow. 300.000 zł">Pow. 300.000 zł</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: 20px;">
<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">Wyślij</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,
activeExampleTab: 'standard',
showDatepicker: false,
dpMonth: new Date().getMonth(),
dpYear: new Date().getFullYear(),
uploadedFiles: [],
isDragOver: false,
fileError: '',
acfData: {
acf: {}
},
acfData_step_3: {
},
configData: {
step_1: '',
step_2: {},
step_3: '',
step_3_extra: '',
step_4: {
name: '',
email: '',
phone: '',
planned_date: '',
budget_range: '',
is_owner: '',
confirm_budget: false,
confirm_deposit: false,
}
}
}
},
async beforeMount() {
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: {
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', 'planned_date', 'budget_range', 'is_owner', 'confirm_budget', 'confirm_deposit']);
},
checkFields() {
const basicFieldsFilled = this.areAllFieldsFilled(this.configData);
// Dla ogrodów zimowych (selected_option_1 === 0) wymagaj dodatkowych pól i checkboxów
if (this.selected_option_1 === 0) {
if (!this.configData.step_4.planned_date ||
!this.configData.step_4.budget_range ||
!this.configData.step_4.is_owner ||
!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;
}
}
}
}
},
dpPrevMonth() {
if (this.dpMonth === 0) { this.dpMonth = 11; this.dpYear--; }
else { this.dpMonth--; }
},
dpNextMonth() {
if (this.dpMonth === 11) { this.dpMonth = 0; this.dpYear++; }
else { this.dpMonth++; }
},
dpSelectDay(day) {
if (this.dpIsDisabled(day)) return;
const d = String(day).padStart(2, '0');
const m = String(this.dpMonth + 1).padStart(2, '0');
this.configData.step_4.planned_date = `${d}.${m}.${this.dpYear}`;
this.showDatepicker = false;
},
dpIsSelected(day) {
const d = String(day).padStart(2, '0');
const m = String(this.dpMonth + 1).padStart(2, '0');
return this.configData.step_4.planned_date === `${d}.${m}.${this.dpYear}`;
},
dpIsDisabled(day) {
const date = new Date(this.dpYear, this.dpMonth, day);
const today = new Date();
today.setHours(0,0,0,0);
return date < today;
},
dpIsToday(day) {
const now = new Date();
return day === now.getDate() && this.dpMonth === now.getMonth() && this.dpYear === now.getFullYear();
},
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();
document.addEventListener('click', (e) => {
if (this.showDatepicker && !e.target.closest('.datepicker-wrapper')) {
this.showDatepicker = false;
}
});
},
computed: {
debug() {
return this.configData
},
dpMonthName() {
const names = ['Styczeń','Luty','Marzec','Kwiecień','Maj','Czerwiec','Lipiec','Sierpień','Wrzesień','Październik','Listopad','Grudzień'];
return names[this.dpMonth];
},
dpDaysInMonth() {
return new Date(this.dpYear, this.dpMonth + 1, 0).getDate();
},
dpFirstDayOffset() {
const d = new Date(this.dpYear, this.dpMonth, 1).getDay();
return d === 0 ? 6 : d - 1;
}
},
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(); ?>