feat: add FAQ question form with email notifications and validations
All checks were successful
Build and Push Docker Image / build (push) Successful in 43s
All checks were successful
Build and Push Docker Image / build (push) Successful in 43s
- Created `FaqQuestionForm` in `forms.py` with custom field validations - Added FAQ form to the FAQ section with CSRF protection and error handling - Integrated email notifications to administrator upon FAQ submission - Updated styles and scripts for form inputs, validation messages, and messages UI - Adjusted FAQ section layout in `faq.html` and related CSS for responsiveness - Implemented tests for FAQ form submission and email notifications
This commit is contained in:
32
home/forms.py
Normal file
32
home/forms.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
|
class FaqQuestionForm(forms.Form):
|
||||||
|
name = forms.CharField(
|
||||||
|
label="Имя",
|
||||||
|
max_length=150,
|
||||||
|
widget=forms.TextInput(attrs={"placeholder": "Имя", "autocomplete": "name"}),
|
||||||
|
)
|
||||||
|
phone = forms.CharField(
|
||||||
|
label="Номер телефона",
|
||||||
|
max_length=64,
|
||||||
|
widget=forms.TextInput(attrs={"placeholder": "Номер телефона", "autocomplete": "tel"}),
|
||||||
|
)
|
||||||
|
question = forms.CharField(
|
||||||
|
label="Ваш вопрос",
|
||||||
|
max_length=1000,
|
||||||
|
widget=forms.Textarea(attrs={"placeholder": "Ваш вопрос", "rows": 4, "autocomplete": "off"}),
|
||||||
|
)
|
||||||
|
accept = forms.BooleanField(
|
||||||
|
label="Я согласен с политикой обработки персональных данных",
|
||||||
|
error_messages={"required": "Необходимо согласие на обработку персональных данных."},
|
||||||
|
)
|
||||||
|
|
||||||
|
def clean_name(self):
|
||||||
|
return self.cleaned_data["name"].strip()
|
||||||
|
|
||||||
|
def clean_phone(self):
|
||||||
|
return self.cleaned_data["phone"].strip()
|
||||||
|
|
||||||
|
def clean_question(self):
|
||||||
|
return self.cleaned_data["question"].strip()
|
||||||
@@ -1,3 +1,38 @@
|
|||||||
from django.test import TestCase
|
from django.core import mail
|
||||||
|
from django.test import TestCase, override_settings
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
|
@override_settings(EMAIL_BACKEND="django.core.mail.backends.locmem.EmailBackend", ADMINISTRATOR_EMAIL="admin@example.com")
|
||||||
|
class FaqQuestionTests(TestCase):
|
||||||
|
def test_faq_question_form_sends_email_to_administrator(self):
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("home"),
|
||||||
|
data={
|
||||||
|
"name": "Иван Петров",
|
||||||
|
"phone": "+375291112233",
|
||||||
|
"question": "Сколько стоит доставка?",
|
||||||
|
"accept": "on",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertEqual(response["Location"], reverse("home") + "#faq-section")
|
||||||
|
self.assertEqual(len(mail.outbox), 1)
|
||||||
|
self.assertEqual(mail.outbox[0].to, ["admin@example.com"])
|
||||||
|
self.assertIn("Иван Петров", mail.outbox[0].body)
|
||||||
|
self.assertIn("Сколько стоит доставка?", mail.outbox[0].body)
|
||||||
|
|
||||||
|
def test_faq_question_form_requires_consent(self):
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("home"),
|
||||||
|
data={
|
||||||
|
"name": "Иван Петров",
|
||||||
|
"phone": "+375291112233",
|
||||||
|
"question": "Сколько стоит доставка?",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(len(mail.outbox), 0)
|
||||||
|
self.assertContains(response, "Необходимо согласие на обработку персональных данных.")
|
||||||
|
|||||||
@@ -1,8 +1,48 @@
|
|||||||
from django.shortcuts import render
|
import logging
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.core.mail import send_mail
|
||||||
|
from django.shortcuts import redirect, render
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.contrib import messages
|
||||||
|
|
||||||
|
from home.forms import FaqQuestionForm
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
return render(request=request, template_name='home/index.html', context={})
|
form = FaqQuestionForm(request.POST or None)
|
||||||
|
|
||||||
|
if request.method == "POST" and form.is_valid():
|
||||||
|
subject = f"Новый вопрос с сайта {settings.SITE_NAME}"
|
||||||
|
message = "\n".join(
|
||||||
|
[
|
||||||
|
f"Имя: {form.cleaned_data['name']}",
|
||||||
|
f"Телефон: {form.cleaned_data['phone']}",
|
||||||
|
f"Вопрос: {form.cleaned_data['question']}",
|
||||||
|
"",
|
||||||
|
f"Страница: {request.build_absolute_uri('/')}",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
send_mail(
|
||||||
|
subject=subject,
|
||||||
|
message=message,
|
||||||
|
from_email=settings.DEFAULT_FROM_EMAIL,
|
||||||
|
recipient_list=[settings.ADMINISTRATOR_EMAIL],
|
||||||
|
fail_silently=False,
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Failed to send FAQ question email")
|
||||||
|
form.add_error(None, "Не удалось отправить вопрос. Попробуйте позже.")
|
||||||
|
else:
|
||||||
|
messages.success(request, "Спасибо. Ваш вопрос отправлен, мы свяжемся с вами в ближайшее время.")
|
||||||
|
return redirect(f"{reverse('home')}#faq-section")
|
||||||
|
|
||||||
|
return render(request=request, template_name='home/index.html', context={"faq_form": form})
|
||||||
|
|
||||||
|
|
||||||
def services(request):
|
def services(request):
|
||||||
|
|||||||
@@ -183,3 +183,4 @@ EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '')
|
|||||||
EMAIL_USE_TLS = os.getenv('EMAIL_USE_TLS', 'false').lower() in {'1', 'true', 'yes', 'on'}
|
EMAIL_USE_TLS = os.getenv('EMAIL_USE_TLS', 'false').lower() in {'1', 'true', 'yes', 'on'}
|
||||||
EMAIL_USE_SSL = os.getenv('EMAIL_USE_SSL', 'false').lower() in {'1', 'true', 'yes', 'on'}
|
EMAIL_USE_SSL = os.getenv('EMAIL_USE_SSL', 'false').lower() in {'1', 'true', 'yes', 'on'}
|
||||||
DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'no-reply@santehray.local')
|
DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'no-reply@santehray.local')
|
||||||
|
ADMINISTRATOR_EMAIL = os.getenv('ADMINISTRATOR_EMAIL', 'info@santehray.by')
|
||||||
|
|||||||
@@ -560,6 +560,33 @@
|
|||||||
padding-bottom: 90px;
|
padding-bottom: 90px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.faq-messages {
|
||||||
|
margin-top: 28px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-message {
|
||||||
|
padding: 14px 18px;
|
||||||
|
border: 1px solid #63768d;
|
||||||
|
background: rgba(99, 118, 141, 0.08);
|
||||||
|
color: #36213e;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-message--success {
|
||||||
|
border-color: #4f7d4f;
|
||||||
|
background: rgba(79, 125, 79, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.faq-message--error {
|
||||||
|
border-color: #a14b4b;
|
||||||
|
background: rgba(161, 75, 75, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.faq-section h2 {
|
.faq-section h2 {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 44px;
|
font-size: 44px;
|
||||||
@@ -696,7 +723,8 @@
|
|||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
||||||
& > input[type=text] {
|
& > input[type=text],
|
||||||
|
& > textarea {
|
||||||
width: 540px;
|
width: 540px;
|
||||||
height: 43px;
|
height: 43px;
|
||||||
padding: 1px 5px;
|
padding: 1px 5px;
|
||||||
@@ -706,6 +734,12 @@
|
|||||||
|
|
||||||
border: 1px solid #554971;
|
border: 1px solid #554971;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& > textarea {
|
||||||
|
min-height: 140px;
|
||||||
|
resize: vertical;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-form-accept-checkbox {
|
.back-form-accept-checkbox {
|
||||||
@@ -729,6 +763,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.back-form .errorlist {
|
||||||
|
margin: 10px 0 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
color: #a14b4b;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.35;
|
||||||
|
max-width: 540px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-form .errorlist li {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-form .errorlist.nonfield {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.back-form-submit {
|
.back-form-submit {
|
||||||
margin-top: 69px;
|
margin-top: 69px;
|
||||||
background: #63768d;
|
background: #63768d;
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
|
|
||||||
.auth-section {
|
.auth-section {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 60px 0 100px;
|
height: 100%;
|
||||||
|
padding-top: 60px;
|
||||||
|
padding-bottom: 60px;
|
||||||
background:
|
background:
|
||||||
radial-gradient(circle at top right, rgba(184, 243, 255, 0.45) 0, rgba(184, 243, 255, 0) 26%),
|
radial-gradient(circle at top right, rgba(184, 243, 255, 0.45) 0, rgba(184, 243, 255, 0) 26%),
|
||||||
linear-gradient(180deg, rgba(99, 118, 141, 0.06) 0%, rgba(99, 118, 141, 0.12) 100%);
|
linear-gradient(180deg, rgba(99, 118, 141, 0.06) 0%, rgba(99, 118, 141, 0.12) 100%);
|
||||||
@@ -27,14 +29,12 @@
|
|||||||
flex: 1 1 480px;
|
flex: 1 1 480px;
|
||||||
min-height: 540px;
|
min-height: 540px;
|
||||||
padding: 56px 48px;
|
padding: 56px 48px;
|
||||||
background: #63768d;
|
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: center;
|
background: #63768d no-repeat center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-intro--login {
|
.auth-intro--login {
|
||||||
@@ -180,6 +180,10 @@
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.register-auth-actions {
|
||||||
|
margin-top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
.auth-buttons {
|
.auth-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|||||||
@@ -75,10 +75,84 @@ body {
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.site-toast-stack {
|
||||||
|
position: fixed;
|
||||||
|
top: 24px;
|
||||||
|
right: 24px;
|
||||||
|
z-index: 1200;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
width: min(420px, calc(100vw - 32px));
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-toast {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
padding: 16px 18px;
|
||||||
|
border: 1px solid rgba(54, 33, 62, 0.14);
|
||||||
|
border-radius: 14px;
|
||||||
|
background: rgba(255, 255, 255, 0.96);
|
||||||
|
box-shadow: 0 18px 40px rgba(54, 33, 62, 0.18);
|
||||||
|
color: var(--dark);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
transform: translateY(0);
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.22s ease, transform 0.22s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-toast.is-hiding {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-toast--success {
|
||||||
|
border-color: rgba(79, 125, 79, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-toast--error {
|
||||||
|
border-color: rgba(161, 75, 75, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-toast--warning {
|
||||||
|
border-color: rgba(176, 133, 56, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-toast__content {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.45;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-toast__close {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--mid);
|
||||||
|
font-size: 26px;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-toast__close:hover {
|
||||||
|
color: var(--dark);
|
||||||
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
font-family: 'BerlinType', serif;
|
font-family: 'BerlinType', serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.site-toast-stack {
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
left: 12px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Container to maintain consistent width */
|
/* Container to maintain consistent width */
|
||||||
.container {
|
.container {
|
||||||
max-width: 1160px;
|
max-width: 1160px;
|
||||||
|
|||||||
34
static/css/warranty.css
Normal file
34
static/css/warranty.css
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
.warranty {
|
||||||
|
padding-bottom: 40px;
|
||||||
|
|
||||||
|
& > h1 {
|
||||||
|
margin: 0;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 50px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
color: #36213e;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > h2 {
|
||||||
|
margin-top: 30px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 30px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
color: #36213e;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > p {
|
||||||
|
display: block;
|
||||||
|
margin-top: 30px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 21px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
color: #36213e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,36 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
{% if messages %}
|
||||||
|
<div class="site-toast-stack" aria-live="polite" aria-atomic="true">
|
||||||
|
{% for message in messages %}
|
||||||
|
<div class="site-toast site-toast--{{ message.tags }}" data-toast>
|
||||||
|
<div class="site-toast__content">{{ message }}</div>
|
||||||
|
<button class="site-toast__close" type="button" aria-label="Закрыть уведомление" data-toast-close>×</button>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
document.querySelectorAll('[data-toast]').forEach(function (toast) {
|
||||||
|
const closeButton = toast.querySelector('[data-toast-close]')
|
||||||
|
const hideToast = function () {
|
||||||
|
toast.classList.add('is-hiding')
|
||||||
|
window.setTimeout(function () {
|
||||||
|
toast.remove()
|
||||||
|
}, 220)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closeButton) {
|
||||||
|
closeButton.addEventListener('click', hideToast)
|
||||||
|
}
|
||||||
|
|
||||||
|
window.setTimeout(hideToast, 4000)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<section class="faq-section container">
|
<section class="faq-section container" id="faq-section">
|
||||||
<h2>ОТВЕТЫ НА ВОПРОСЫ</h2>
|
<h2>ОТВЕТЫ НА ВОПРОСЫ</h2>
|
||||||
<div class="faq-grid">
|
<div class="faq-grid">
|
||||||
|
|
||||||
@@ -44,23 +44,30 @@
|
|||||||
|
|
||||||
<div id="back-form" style="display: none;" class="back-form">
|
<div id="back-form" style="display: none;" class="back-form">
|
||||||
<h2>НЕ НАШЛИ ОТВЕТ НА ВАШ ВОПРОС?</h2>
|
<h2>НЕ НАШЛИ ОТВЕТ НА ВАШ ВОПРОС?</h2>
|
||||||
<form action="">
|
<form action="{% url 'home' %}" method="post">
|
||||||
<label class="q-label" for="name" style="margin-top: 69px;">
|
{% csrf_token %}
|
||||||
<input type="text" placeholder="Имя" id="name">
|
{{ faq_form.non_field_errors }}
|
||||||
</label>
|
|
||||||
|
|
||||||
<label class="q-label" for="number">
|
<label class="q-label" for="{{ faq_form.name.id_for_label }}" style="margin-top: 69px;">
|
||||||
<input type="text" placeholder="Номер телефона" id="number">
|
{{ faq_form.name }}
|
||||||
</label>
|
</label>
|
||||||
|
{{ faq_form.name.errors }}
|
||||||
|
|
||||||
<label class="q-label" for="question">
|
<label class="q-label" for="{{ faq_form.phone.id_for_label }}">
|
||||||
<input type="text" placeholder="Ваш вопрос" id="question">
|
{{ faq_form.phone }}
|
||||||
</label>
|
</label>
|
||||||
|
{{ faq_form.phone.errors }}
|
||||||
|
|
||||||
|
<label class="q-label" for="{{ faq_form.question.id_for_label }}">
|
||||||
|
{{ faq_form.question }}
|
||||||
|
</label>
|
||||||
|
{{ faq_form.question.errors }}
|
||||||
|
|
||||||
<div class="back-form-accept-checkbox">
|
<div class="back-form-accept-checkbox">
|
||||||
<input type="checkbox" id="accept" name="accept">
|
{{ faq_form.accept }}
|
||||||
<label for="accept">Я согласен с политикой обработки персональных данных</label>
|
<label for="{{ faq_form.accept.id_for_label }}">{{ faq_form.accept.label }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
{{ faq_form.accept.errors }}
|
||||||
|
|
||||||
<button class="back-form-submit" type="submit">ОТПРАВИТЬ ВОПРОС</button>
|
<button class="back-form-submit" type="submit">ОТПРАВИТЬ ВОПРОС</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -78,5 +85,12 @@
|
|||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
Fancybox.bind('[data-fancybox]')
|
Fancybox.bind('[data-fancybox]')
|
||||||
|
|
||||||
|
{% if faq_form.is_bound and faq_form.errors %}
|
||||||
|
const openFaqButton = document.querySelector('.faq-cta a[data-fancybox]')
|
||||||
|
if (openFaqButton) {
|
||||||
|
openFaqButton.click()
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
@@ -4,13 +4,44 @@
|
|||||||
{% block title %}Главная{% endblock %}
|
{% block title %}Главная{% endblock %}
|
||||||
|
|
||||||
{% block add_css %}
|
{% block add_css %}
|
||||||
|
<link rel="stylesheet" href="{% static 'css/warranty.css' %}">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include "_fragments/header.html" %}
|
<div class="full-page">
|
||||||
|
<div>
|
||||||
|
{% include "_fragments/header.html" with active="warranty" breadcrumb=1 %}
|
||||||
|
{% include '_fragments/breadcrumbs.html' with url_1='/' text_1='Главная' url_2='/warranty' text_2='Гарантия' %}
|
||||||
|
</div>
|
||||||
|
|
||||||
{# TODO #}
|
<main class="warranty container">
|
||||||
В разработке
|
<h1>ГАРАНТИЯ НА ТОВАР</h1>
|
||||||
{% include "_fragments/footer.html" %}
|
<p>
|
||||||
|
В каталоге нашего интернет-магазина представлены только качественные товары от лучших производителей. Мы
|
||||||
|
закупаем только сертифицированный товар и только у официальных импортеров и производителей. Покупая
|
||||||
|
сантехнику в нашем магазине, Вы приобретаете товары только с гарантией! Все гарантийное и
|
||||||
|
постгарантийное обслуживание товаров производится специализированными авторизированными сервисными
|
||||||
|
центрами фирм производителей, имеющих на то соответствующие полномочия. Данные условия гарантируют
|
||||||
|
отсутствие в изделии дефектов и удовлетворяют законным требованиям Потребителя в течение гарантийного
|
||||||
|
срока с момента передачи товара Потребителю.
|
||||||
|
</p>
|
||||||
|
<h2>Условия гарантии:</h2>
|
||||||
|
<p>
|
||||||
|
Гарантия не распространяется на неисправности, вызванные неправильным монтажом, чисткой абразивными или
|
||||||
|
едкими веществами, неправильными эксплуатацией и ремонтом. Внимательно изучите прилагаемую инструкцию по
|
||||||
|
эксплуатации.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Гарантийные обязательства не распространяются на расходные элементы и материалы (батареи, картриджи,
|
||||||
|
комплектующие и т.п.), а также на ремонт или замену естественно изнашиваемых в процессе эксплуатации
|
||||||
|
запчастей.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
В гарантийном обслуживании может быть отказано, если установка и ремонт изделий производились лицами, не
|
||||||
|
имеющими соответствующих полномочий и квалификации.
|
||||||
|
</p>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{% include "_fragments/footer.html" %}
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="auth-page">
|
<div class="full-page">
|
||||||
{% include "_fragments/header.html" %}
|
{% include "_fragments/header.html" %}
|
||||||
|
|
||||||
<main class="auth-section">
|
<main class="auth-section">
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="auth-actions">
|
<div class="register-auth-actions">
|
||||||
<button class="auth-submit" type="submit">Создать аккаунт</button>
|
<button class="auth-submit" type="submit">Создать аккаунт</button>
|
||||||
<p class="auth-switch">
|
<p class="auth-switch">
|
||||||
Уже есть аккаунт?
|
Уже есть аккаунт?
|
||||||
|
|||||||
Reference in New Issue
Block a user