main
Lowlights 2024-03-05 17:23:18 +06:00
parent 3f86d8d822
commit 1a9f3afec2
44 changed files with 6182 additions and 349 deletions

View File

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

@ -9,24 +9,19 @@ class Cart:
Initialize the cart. Initialize the cart.
""" """
self.session = request.session self.session = request.session
user_id = self.session.get('keycloak_user_id') # Используйте идентификатор пользователя из сессии cart = self.session.get(settings.CART_SESSION_ID)
if user_id:
cart_session_id = f"{settings.CART_SESSION_ID}_{user_id}" # Создайте уникальный ключ сессии для корзины
else:
cart_session_id = settings.CART_SESSION_ID
cart = self.session.get(cart_session_id)
if not cart: if not cart:
# save an empty cart in the session # save an empty cart in the session
cart = self.session[cart_session_id] = {} cart = self.session[settings.CART_SESSION_ID] = {}
self.cart = cart self.cart = cart
self.cart_session_id = cart_session_id
def __iter__(self): def __iter__(self):
""" """
Iterate over the items in the cart and get the products Iterate over the items in the cart and get the products
from the database. from the database.
""" """
product_ids = self.cart.keys() # get the product objects and add them to the cart product_ids = self.cart.keys()
# get the product objects and add them to the cart
products = Product.objects.filter(id__in=product_ids) products = Product.objects.filter(id__in=product_ids)
cart = self.cart.copy() cart = self.cart.copy()
for product in products: for product in products:
@ -60,8 +55,6 @@ class Cart:
# mark the session as "modified" to make sure it gets saved # mark the session as "modified" to make sure it gets saved
self.session.modified = True self.session.modified = True
self.session[self.cart_session_id] = self.cart
def remove(self, product): def remove(self, product):
""" """
Remove a product from the cart. Remove a product from the cart.

View File

@ -5,10 +5,19 @@
{% block content %} {% block content %}
<link rel="stylesheet" href="{% static "/deps/css/cart.css" %}"> <link rel="stylesheet" href="{% static "/deps/css/cart.css" %}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css">
<div class="cart-container position-relative">
<div class="text-center my-4">
<h1 class="display-4" style="color:black;">
<i class="fa fa-shopping-cart" aria-hidden="true"></i> Корзина
</h1>
<p class="lead" style="color: whitesmoke">Проверьте свои товары перед оформлением заказа.</p>
</div>
</div>
<table class="cart-table" style="position: relative; background-color: #1d02269e;">
<div class="cart-container">
<h1>Корзина</h1>
<table class="cart-table">
<thead > <thead >
<tr> <tr>
<th>Изображение</th> <th>Изображение</th>
@ -34,13 +43,13 @@
{% csrf_token %} {% csrf_token %}
{{ item.update_quantity_form.quantity }} {{ item.update_quantity_form.quantity }}
{{ item.update_quantity_form.override }} {{ item.update_quantity_form.override }}
<input type="submit" value="Update" class="btn-update"> <input type="submit" value="Обновить" class="btn-update">
</form> </form>
</td> </td>
<td> <td>
<form action="{% url 'cart:cart_remove' product.id %}" method="post"> <form action="{% url 'cart:cart_remove' product.id %}" method="post">
{% csrf_token %} {% csrf_token %}
<input type="submit" value="Remove" class="btn-remove"> <input type="submit" value="Удалить" class="btn-remove">
</form> </form>
</td> </td>
<td class="num">₸{{ item.price }}</td> <td class="num">₸{{ item.price }}</td>
@ -48,17 +57,17 @@
</tr> </tr>
{% endwith %} {% endwith %}
{% endfor %} {% endfor %}
<tr class="total"> <tr class="total position-relative">
<td>Итого</td> <td>Итого</td>
<td colspan="4"></td> <td colspan="4"></td>
<td class="num">₸{{ cart.get_total_price }}</td> <td class="num">₸{{ cart.get_total_price }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<div class="cart-action-buttons"> <div class="d-flex justify-content-between my-3 position-relative">
<a href="{% url 'main:product_list' %}" class="button light">Продолжить покупки</a> <a href="{% url 'main:product_list' %}" class="btn btn-light">Продолжить покупки</a>
<!-- Обновите 'your_checkout_url' на реальный URL оформления заказа --> <!-- Обновите 'your_checkout_url' на реальный URL оформления заказа -->
<a href="{% url "orders:order_create"%}" class="button checkout">Оформить заказ</a> <a href="{% url "orders:order_create"%}" class="btn btn-light">Оформить заказ</a>
</div> </div>
</div> </div>

Binary file not shown.

5669
django.log

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,11 @@ CART_SESSION_ID = 'cart'
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'django.contrib.sites',
'allauth',
'allauth.account',
'allauth.socialaccount',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django_extensions', 'django_extensions',
@ -45,6 +50,9 @@ INSTALLED_APPS = [
] ]
SESSION_COOKIE_AGE = 86400 # 24 часа в секундах
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
SITE_ID = 1
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
@ -54,6 +62,8 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'allauth.account.middleware.AccountMiddleware',
] ]
ROOT_URLCONF = 'djangoProject1.urls' ROOT_URLCONF = 'djangoProject1.urls'
@ -70,6 +80,7 @@ TEMPLATES = [
'django.template.context_processors.request', 'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages', 'django.contrib.messages.context_processors.messages',
], ],
}, },
}, },
@ -152,9 +163,10 @@ EMAIL_USE_TLS = True
KEYCLOAK_CONFIG = { KEYCLOAK_CONFIG = {
'SERVER_URL': 'https://auth.myterior.kz', 'SERVER_URL': 'https://auth.myterior.kz',
'REALM': 'Harmony', 'REALM': 'Harmony',
'CLIENT_ID': 'lowlight', 'CLIENT_ID': 'admin-cli',
'CLIENT_SECRET': 'u31Kvkj9V2npxdwJUODReO3YJ2w2iMul', 'CLIENT_SECRET': 'wOVphEiLVBS1AlNKRpaQpD4yQh5Wm3TJ',
'CALLBACK_URL': 'http://127.0.0.1:8000/products/', 'CALLBACK_URL': 'http://127.0.0.1:8000/products/',
} }
@ -186,3 +198,21 @@ LOGGING = {
}, },
}, },
} }
# settings.py
# ... ваш предыдущий код ...
SOCIALACCOUNT_PROVIDERS = {
'openid': {
'SERVERS': [
{
'id': 'keycloak',
'name': 'Keycloak',
'openid_config_url': 'https://auth.myterior.kz/realms/Harmony/.well-known/openid-configuration',
'client_id': 'admin-cli',
'client_secret': 'wOVphEiLVBS1AlNKRpaQpD4yQh5Wm3TJ',
}
]
}
}

View File

@ -2,13 +2,15 @@ from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from main import views
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('cart/', include('cart.urls', namespace='cart')), path('cart/', include('cart.urls', namespace='cart')),
path('orders/', include('orders.urls', namespace='orders')), path('orders/', include('orders.urls', namespace='orders')),
path('auth/', include('users.urls')),
path('', include('main.urls', namespace='main')), path('', include('main.urls', namespace='main')),
path('auth/', include('users.urls')), # Подключение URL приложения users
] ]
if settings.DEBUG: if settings.DEBUG:

View File

@ -1,68 +0,0 @@
%PDF-1.3
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
1 0 obj
<<
/F1 2 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/Contents 7 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 6 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
4 0 obj
<<
/PageMode /UseNone /Pages 6 0 R /Type /Catalog
>>
endobj
5 0 obj
<<
/Author (anonymous) /CreationDate (D:20240218225917-06'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20240218225917-06'00') /Producer (ReportLab PDF Library - www.reportlab.com)
/Subject (unspecified) /Title (untitled) /Trapped /False
>>
endobj
6 0 obj
<<
/Count 1 /Kids [ 3 0 R ] /Type /Pages
>>
endobj
7 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 102
>>
stream
GapQh0E=F,0U\H3T\pNYT^QKk?tc>IP,;W#U1^23ihPEM_?CW4KISi90MntRifICK%KE/ee05&l(FoYV9neZ[Kb,ht@Ke@a%%S##~>endstream
endobj
xref
0 8
0000000000 65535 f
0000000073 00000 n
0000000104 00000 n
0000000211 00000 n
0000000414 00000 n
0000000482 00000 n
0000000778 00000 n
0000000837 00000 n
trailer
<<
/ID
[<dfa685a70af9cd44f8a81cfb0b67bc98><dfa685a70af9cd44f8a81cfb0b67bc98>]
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
/Info 5 0 R
/Root 4 0 R
/Size 8
>>
startxref
1029
%%EOF

View File

@ -1,68 +0,0 @@
%PDF-1.3
%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com
1 0 obj
<<
/F1 2 0 R
>>
endobj
2 0 obj
<<
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
>>
endobj
3 0 obj
<<
/Contents 7 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 6 0 R /Resources <<
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
>> /Rotate 0 /Trans <<
>>
/Type /Page
>>
endobj
4 0 obj
<<
/PageMode /UseNone /Pages 6 0 R /Type /Catalog
>>
endobj
5 0 obj
<<
/Author (anonymous) /CreationDate (D:20240218230410-06'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20240218230410-06'00') /Producer (ReportLab PDF Library - www.reportlab.com)
/Subject (unspecified) /Title (untitled) /Trapped /False
>>
endobj
6 0 obj
<<
/Count 1 /Kids [ 3 0 R ] /Type /Pages
>>
endobj
7 0 obj
<<
/Filter [ /ASCII85Decode /FlateDecode ] /Length 102
>>
stream
GapQh0E=F,0U\H3T\pNYT^QKk?tc>IP,;W#U1^23ihPEM_?CW4KISi90MntRifICK%KE/ee05&l(FoY69neZ[Kb,ht@Ke@a%'("2~>endstream
endobj
xref
0 8
0000000000 65535 f
0000000073 00000 n
0000000104 00000 n
0000000211 00000 n
0000000414 00000 n
0000000482 00000 n
0000000778 00000 n
0000000837 00000 n
trailer
<<
/ID
[<ec49df0e43ea243c0ea148f44a3a082b><ec49df0e43ea243c0ea148f44a3a082b>]
% ReportLab generated PDF document -- digest (http://www.reportlab.com)
/Info 5 0 R
/Root 4 0 R
/Size 8
>>
startxref
1029
%%EOF

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 494 KiB

BIN
main/static/deps/cart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 887 KiB

View File

@ -0,0 +1,60 @@
.footer-buttons {
display: flex;
justify-content: center;
gap: 20px; /* Space between buttons */
}
.button {
background: #fafaff; /* Background color of the button */
color: white;
border-radius: 50%; /* Makes the button circular */
width: 120px; /* Diameter of the button */
height: 120px; /* Diameter of the button */
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* Shadow for depth */
cursor: pointer;
transition: background 0.3s ease, box-shadow 0.3s ease;
position: relative; /* For absolute positioning of the icon */
overflow: hidden; /* Prevents overflow of the background image */
margin: 10px; /* Creates space between buttons */
/* Border around the button */
border: 3px solid #ffffff; /* White border */
}
.button:hover {
background: #ffffff; /* Lighter color on hover */
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3); /* Larger shadow on hover */
}
.button span {
font-size: 16px; /* Size of the text */
position: absolute;
bottom: 10px; /* Positioning the text below the icon */
text-transform: uppercase; /* Capitalize the text */
}
/* Icon styles */
.icon-shop, .icon-about, .icon-cart {
background-size: cover; /* Ensures the icon covers the area */
background-position: center; /* Center the icon */
background-repeat: no-repeat;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%); /* Center the icon */
width: 100%; /* Full width of the button */
height: 100%; /* Full height of the button */
}
.icon-shop {
background-image: url(" ../shop.png");
}
.icon-cart {
background-image: url('../cart.png');
}
.icon-about {
background-image: url('../aboutus.png');
}

View File

@ -1,6 +1,6 @@
/* Основные стили страницы корзины с улучшенной видимостью картинок */ /* Основные стили страницы корзины с улучшенной видимостью картинок */
.cart-page { .cart-page {
background: linear-gradient(135deg, #483D8B 0%, #6A5ACD 100%); /* Эзотерический градиент фона */ background: linear-gradient(135deg, #ffffff 0%, #f5f5f5 100%); /* Эзотерический градиент фона */
color: #FFFFFF; color: #FFFFFF;
padding: 20px; padding: 20px;
border-radius: 12px; border-radius: 12px;
@ -27,7 +27,7 @@
.cart-table th { .cart-table th {
font-size: 1.1rem; font-size: 1.1rem;
letter-spacing: 1px; letter-spacing: 1px;
background-color: rgba(255, 255, 255, 0.2); /* Слегка прозрачный фон для заголовков */ background-color: rgb(187, 123, 255); /* Слегка прозрачный фон для заголовков */
backdrop-filter: blur(4px); /* Размытие под заголовками */ backdrop-filter: blur(4px); /* Размытие под заголовками */
} }
@ -45,7 +45,7 @@
border-radius: 25px; border-radius: 25px;
border: 2px solid #FFFFFF; border: 2px solid #FFFFFF;
background: transparent; background: transparent;
color: #00ffc4; color: #db71ff;
letter-spacing: 0.5px; letter-spacing: 0.5px;
font-weight: bold; font-weight: bold;
text-transform: uppercase; /* Буквы в верхнем регистре для акцента */ text-transform: uppercase; /* Буквы в верхнем регистре для акцента */
@ -57,23 +57,23 @@
} }
.btn-remove:hover { .btn-remove:hover {
background-color: #FF6347; /* Терракот при наведении */ background-color: #cd00ff; /* Терракот при наведении */
} }
.btn-update:hover { .btn-update:hover {
background-color: #00bb85; /* Лайм при наведении */ background-color: rgba(184, 98, 255, 0.73); /* Лайм при наведении */
} }
.button.light:hover, .button.checkout:hover { .button.light:hover, .button.checkout:hover {
opacity: 0.85; opacity: 0.85;
background-color: #FFFFFF; background-color: #FFFFFF;
color: #235e73; color: #bb7bff;
} }
.cart-container{ .cart-container{
background-color: rgba(0, 82, 74, 0.72); /* Прозрачный серый фон */ background-color: rgba(203, 92, 255, 0.62); /* Прозрачный серый фон */
padding: 30px; /* Отступ для создания пространства вокруг .cart-page */ padding: 30px; /* Отступ для создания пространства вокруг .cart-page */
border-radius: 15px; /* Скруглённые углы для согласованности с .cart-page */ border-radius: 15px; /* Скруглённые углы для согласованности с .cart-page */
box-shadow: 0 6px 12px rgba(0, 107, 85, 0.47); /* Небольшое тень для дополнительной глубины */ box-shadow: 0 6px 12px rgb(136, 5, 255); /* Небольшое тень для дополнительной глубины */
margin: 20px auto; /* Автоматические отступы для центрирования и добавления пространства сверху и снизу */ margin: 20px auto; /* Автоматические отступы для центрирования и добавления пространства сверху и снизу */
max-width: calc(100% - 40px); /* Ограничение максимальной ширины с учетом отступов */ max-width: calc(100% - 40px); /* Ограничение максимальной ширины с учетом отступов */
} }
@ -81,19 +81,19 @@
.total { .total {
font-weight: bold; font-weight: bold;
color: #FFF; color: #FFF;
background: linear-gradient(145deg, #5acdae, #1ba99c); background: linear-gradient(145deg, #9d27ff, #e2b1ff);
font-size: 1.2rem; font-size: 1.2rem;
text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.5); text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.5);
} }
.total td { .total td {
padding: 15px; padding: 15px;
border-top: 3px solid rgba(36, 239, 146, 0.41); /* Добавляем золотую границу для акцента */ border-top: 3px solid rgba(186, 142, 255, 0.9); /* Добавляем золотую границу для акцента */
} }
/* Дополнительный стиль для общей цены, чтобы выделить её ещё сильнее */ /* Дополнительный стиль для общей цены, чтобы выделить её ещё сильнее */
.total .num { .total .num {
background-color: rgba(255, 215, 0, 0.2); /* Слегка прозрачный золотой фон */ background-color: rgb(203, 92, 255); /* Слегка прозрачный золотой фон */
border-radius: 5px; border-radius: 5px;
padding: 5px 10px; padding: 5px 10px;
margin-right: 5px; margin-right: 5px;
@ -102,7 +102,7 @@
/* Стиль для кнопок действий с использованием эзотерических цветов и эффектов */ /* Стиль для кнопок действий с использованием эзотерических цветов и эффектов */
.button.light, .button.checkout { .button.light, .button.checkout {
background: linear-gradient(145deg, #2ac296, #00ffb2); background: linear-gradient(145deg, #8a00a9, #71008f);
color: white; color: white;
padding: 10px 20px; padding: 10px 20px;
border-radius: 30px; border-radius: 30px;
@ -118,3 +118,7 @@
transform: scale(1.05); transform: scale(1.05);
box-shadow: 0px 6px 8px rgba(0, 0, 0, 0.3); box-shadow: 0px 6px 8px rgba(0, 0, 0, 0.3);
} }
h1{
color: #1b003d;
}

View File

@ -1,8 +1,8 @@
body, html { body {
height: 100%; height: 100%;
margin: 0; margin: 0;
font-family: 'Papyrus', fantasy; font-family: 'Playfair Display', fantasy;
background-image: url("../images/bg-image.png");
background-size: cover; background-size: cover;
} }
@ -15,7 +15,7 @@ font-family: 'Papyrus', fantasy;
color: #E0FFFF; /* Цвет текста, напоминающий свет луны */ color: #E0FFFF; /* Цвет текста, напоминающий свет луны */
border-radius: 15px; border-radius: 15px;
padding: 30px; padding: 30px;
box-shadow: 0 0 15px 5px rgba(0, 255, 255, 0.2); box-shadow: 0 0 15px 5px rgba(169, 0, 255, 0.62);
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
} }
@ -79,6 +79,3 @@ font-family: 'Papyrus', fantasy;
} }
} }
.place-order-btn:focus:not(:hover) {
animation: pulse 2s infinite;
}

View File

@ -24,19 +24,19 @@
/* Стили для заголовков и описания */ /* Стили для заголовков и описания */
.product-detail h1 { .product-detail h1 {
font-size: 2.5rem; font-size: 2.5rem;
color: #24ffc5; /* Золотой цвет для заголовков */ color: #daa3ff; /* Золотой цвет для заголовков */
text-shadow: 0px 0px 10px rgba(255, 255, 255, 0.7); /* Свечение вокруг текста */ text-shadow: 0px 0px 10px rgba(255, 255, 255, 0.7); /* Свечение вокруг текста */
} }
.product-detail h2 { .product-detail h2 {
font-size: 1.8rem; font-size: 1.8rem;
color: #7FFFD4; /* Аквамариновый цвет для подзаголовков */ color: #cc7fff; /* Аквамариновый цвет для подзаголовков */
margin-bottom: 20px; margin-bottom: 20px;
} }
.product-detail .price { .product-detail .price {
font-size: 2rem; font-size: 2rem;
color: #00FA9A; /* Мягкий зеленый цвет для цены */ color: #4f0088; /* Мягкий зеленый цвет для цены */
font-weight: bold; font-weight: bold;
margin-bottom: 20px; margin-bottom: 20px;
} }
@ -67,7 +67,7 @@
} }
.btn-secondary { .btn-secondary {
background-color: #4dd0a9; /* Сине-фиолетовый цвет */ background-color: #bf4dd0; /* Сине-фиолетовый цвет */
} }
.btn-primary:hover, .btn-secondary:hover { .btn-primary:hover, .btn-secondary:hover {
@ -88,7 +88,7 @@
align-items: center; align-items: center;
overflow: hidden; overflow: hidden;
border-radius: 15px; /* Скругляем углы контейнера */ border-radius: 15px; /* Скругляем углы контейнера */
box-shadow: 0px 10px 20px rgba(35, 182, 138, 0.2); /* Добавляем тень для эффекта "глубины" */ box-shadow: 0px 10px 20px rgba(203, 92, 255, 0.62); /* Добавляем тень для эффекта "глубины" */
margin-right: 20px; /* Отступ справа */ margin-right: 20px; /* Отступ справа */
background: rgba(255, 255, 255, 0.1); /* Немного прозрачности для контейнера */ background: rgba(255, 255, 255, 0.1); /* Немного прозрачности для контейнера */
backdrop-filter: blur(5px); /* Размытие фона за контейнером */ backdrop-filter: blur(5px); /* Размытие фона за контейнером */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -1,3 +1,8 @@
[LocalizedFileNames] [LocalizedFileNames]
Снимок экрана 2024-02-08 161455.png=@Снимок экрана 2024-02-08 161455,0 Снимок экрана 2024-02-08 161455.png=@Снимок экрана 2024-02-08 161455,0
Снимок экрана 2024-02-08 161541.png=@Снимок экрана 2024-02-08 161541,0 Снимок экрана 2024-02-08 161541.png=@Снимок экрана 2024-02-08 161541,0
Снимок экрана 2024-03-02 000101.png=@Снимок экрана 2024-03-02 000101,0
Снимок экрана 2024-03-03 223840.png=@Снимок экрана 2024-03-03 223840,0
Снимок экрана 2024-03-03 224408.png=@Снимок экрана 2024-03-03 224408,0
Снимок экрана 2024-03-03 225609.png=@Снимок экрана 2024-03-03 225609,0
Снимок экрана 2024-03-03 225802.png=@Снимок экрана 2024-03-03 225802,0

BIN
main/static/deps/shop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 KiB

View File

@ -0,0 +1,33 @@
{% extends "main/base.html" %} <!-- Предполагается, что у вас есть базовый шаблон -->
{% load static %}
{% block title %}О магазине{% endblock %}
{% block content %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
<div class="container py-5">
<h2 class="mb-4">О магазине "Гармония Эзотерики"</h2>
<p>
"Гармония Эзотерики" - это ваш уникальный магазин товаров для практикующих эзотерику и всех, кто интересуется мистическими аспектами жизни. Мы предлагаем широкий ассортимент товаров, включая кристаллы, таро карты, эзотерические книги, амулеты и многое другое.
</p>
<p>
Наш магазин создан для тех, кто ищет гармонию в себе и вокруг. Мы верим, что правильные инструменты и знания могут открыть дверь в мир энергий, интуиции и самопознания.
</p>
<h3 class="mt-5 mb-3">Контакты</h3>
<ul>
<li>Email: info@harmony-esoterics.com</li>
<li>Телефон: +7 (999) 999-99-99</li>
<li>Адрес: Город N, Улица Мистическая, д. 42</li>
</ul>
<h3 class="mt-5 mb-3">Следите за нами в социальных сетях</h3>
<p>Узнавайте о новинках, акциях и мероприятиях первыми!</p>
<div>
<a href="https://instagram.com/yourinstagram" class="btn btn-outline-dark btn-sm mb-2" target="_blank">Instagram <i class="fa fa-instagram"></i></a>
<a href="https://facebook.com/yourfacebookpage" class="btn btn-outline-dark btn-sm mb-2" target="_blank">Facebook <i class="fa fa-facebook"></i></a>
<!-- Добавьте другие социальные сети по аналогии -->
</div>
</div>
{% endblock %}

View File

@ -1,4 +1,3 @@
{% load static %} {% load static %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="ru"> <html lang="ru">
@ -9,9 +8,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static "/deps/css/bootstrap/bootstrap.min.css"%}"> <link rel="stylesheet" href="{% static "/deps/css/bootstrap/bootstrap.min.css"%}">
<link rel="stylesheet" href="{% static "/deps/css/my_css.css"%}"> <link rel="stylesheet" href="{% static "/deps/css/my_css.css"%}">
<!-- Стиль footer-a для главной страницы --> <link href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{% static "/deps/css/my_footer_css.css"%}"> <link rel="stylesheet" href="{% static "/deps/css/my_footer_css.css"%}">
<link rel="stylesheet" href="{% static "/deps/css/catal.css"%}"> <link rel="stylesheet" href="{% static "/deps/css/catal.css"%}">
<link rel="stylesheet" href="{% static "/deps/css/button_icon.css"%}">
<!-- Favicons for different platforms --> <!-- Favicons for different platforms -->
<link rel="apple-touch-icon" sizes="180x180" href="{% static "/deps/favicon/apple-touch-icon.png"%}"> <link rel="apple-touch-icon" sizes="180x180" href="{% static "/deps/favicon/apple-touch-icon.png"%}">
<link rel="icon" type="image/png" sizes="32x32" href="{% static "/deps/favicon/favicon-32x32.png"%}"> <link rel="icon" type="image/png" sizes="32x32" href="{% static "/deps/favicon/favicon-32x32.png"%}">
@ -21,7 +22,7 @@
</head> </head>
<body> <body>
<header> <header class="sticky-top">
<nav class="navbar navbar-expand-lg bg-dark " data-bs-theme="dark"> <nav class="navbar navbar-expand-lg bg-dark " data-bs-theme="dark">
<div class="container"> <div class="container">
<a class="navbar-brand" href="{% url "main:product_list"%}">Harmony of Esotericism</a> <a class="navbar-brand" href="{% url "main:product_list"%}">Harmony of Esotericism</a>
@ -49,79 +50,65 @@
<a class="nav-link text-white" href="{% url 'cart:cart_detail' %}">Корзина</a> <a class="nav-link text-white" href="{% url 'cart:cart_detail' %}">Корзина</a>
</li> </li>
<li class="nav-item">
<a class="nav-link text-white" href="{% url 'keycloak_login' %}">Войти</a>
<li><a class="dropdown-item text-white" href="{% url 'profile' %}">Личный кабинет</a></li>
</li>
{% if request.session.keycloak_user_id %}
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link dropdown-toggle text-white" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false"> <a class="nav-link dropdown-toggle text-white" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Мой профиль Пользователь
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
<li><hr class="dropdown-divider"></li> <li><a class="dropdown-item text-white" href="#">Личный кабинет</a></li>
<li><a class="dropdown-item text-white" href="{% url 'keycloak_logout' %}">Выйти</a></li> <li><a class="dropdown-item text-white" href="{% url 'keycloak_login' %}">Войти</a></li>
<li><a class="dropdown-item text-white" href="#">Выйти</a></li>
</ul> </ul>
</li> </li>
{% endif %}
</ul> </ul>
</div> </div>
</div> </div>
</nav> </nav>
</header> </header>
<main class="d-flex">
<img alt="backimg" src="{% static '/deps/images/bg-image2.png'%}" class="fixed-top z-0 vw-100 vh-100 object-fit-cover"/>
<div class="container"> <div class="button-container vh-100 d-flex flex-column gap-5 justify-content-center">
<div class="row mt-1"> <a href="/" class="button" id="shop-button">
<div class="col-lg-2"> <div class="icon-shop"></div>
<!-- Пустой блок на Ваше усмотрение -->
</div>
<div id="header">
<h1 style="color: white; text-align: center; position: relative;">
<a class="navbar-brand" href="{% url 'main:product_list' %}" style="text-decoration: none; color: white; font-family: 'Papyrus', fantasy;">
<!-- Добавляем иконку или символ перед названием магазина -->
Гармония Эзотерики
</a> </a>
</h1> <a href="{% url 'main:about' %}" class="button" id="about-button">
<div class="icon-about"></div>
</a>
</div> <a href="/cart" class="button" id="cart-button">
<div class="icon-cart"></div>
</a>
</div> </div>
<!-- Добавление кнопок категорий и формы для фильтрации --> <div class="container">
{% block content %} {% block content %}
<div class="row mb-3"> <div class="row mb-3">
<div class="col-12"> <div class="col-lg-2">
<form method="get" action="{% url 'main:product_list' %}"> <div class="list-group">
<button type="submit" class="btn btn-primary">Искать по Категориям</button> <a href="#" class="list-group-item list-group-item-action active" aria-current="true">Категории</a>
</form> {% for category in categories %}
<a href="{% url 'main:product_list_by_category' category.slug %}" class="list-group-item list-group-item-action">{{ category.name }}</a>
{% endfor %}
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}
</div> </div>
</main>
{# <footer class="py-2 bg-dark">#}
{# <div class="container">#}
{# <div class="row">#}
{# <div class="col-lg-14">#}
{# <p class="m-0 text-center text-white footer-text" style="font-size: 18px;">Harmony of Esotericism 2024</p>#}
{# </div>#}
{# <div class="col-lg-14">#}
{# <div class="text-center mt-3">#}
{# <h5 class="text-white" style="font-size: 14px;">Посетите нашего телеграм бота!</h5>#}
{##}
{# </div>#}
{# </div>#}
{# </div>#}
{# </div>#}
{# </footer>#}
<script src="{% static "/deps/js/jquery/jquery-3.7.0.min.js"%}"></script> <script src="{% static "/deps/js/jquery/jquery-3.7.0.min.js"%}"></script>
<script src="{% static "/deps/js/jquery-events.js"%}"></script> <script src="{% static "/deps/js/jquery-events.js"%}"></script>

View File

@ -1,7 +1,119 @@
{% extends "main/base.html" %} {% extends "main/base.html" %}
{% load static %}
{% block content %} {% block content %}
<h1>Личный кабинет</h1> <div class="row">
<p>Привет, {{ username }}!</p> <div class="container mt-5">
<div class="row justify-content-center">
<!-- Профиль с данными пользователя -->
<div class="col-md-5">
<div class=" bg-white p-4 mb-4 mx-2 rounded custom-shadow">
<h3 class="text-center mb-4">Профиль пользователя</h3>
<form action="#" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="row">
<div class="col-md-12 mb-3 text-center">
<img src="{% static "deps/img/no_image.jpg" %}"
alt="Аватар пользователя" class="img-fluid rounded-circle"
style="max-width: 150px;">
<input type="file" class="form-control mt-3" id="id_image"
name='image'
accept="image/*">
</div>
<div class="col-md-12 mb-3">
<label for="id_first_name" class="form-label">Имя*</label>
<input type="text" class="form-control" id="id_first_name"
name="first_name"
placeholder="Введите ваше имя"
value="{{ userinfo.firstName }}"
required>
</div>
<div class="col-md-12 mb-3">
<label for="id_last_name" class="form-label">Фамилия*</label>
<input type="text" class="form-control" id="id_last_name"
name="last_name"
placeholder="Введите вашу фамилию"
value="{{ userinfo.lastName }}"
required>
</div>
<div class="col-md-12 mb-3">
<label for="id_username" class="form-label">Имя пользователя*</label>
<input type="text" class="form-control" id="id_username"
name="username"
placeholder="Введите ваше имя пользователя"
value="{{ userinfo.username }}"
required>
</div>
<div class="col-md-12 mb-3">
<label for="id_email" class="form-label">Email*</label>
<input type="email" class="form-control" id="id_email"
name="email"
placeholder="Введите ваш email *youremail@example.com"
value="{{ userinfo.email }}"
required>
</div>
</div>
<button type="submit" class="btn btn-dark">Сохранить</button>
</form>
</div>
</div>
<!-- Оформленные заказы -->
<div class="col-md-12">
<div class=" bg-white p-4 mb-4 mx-2 rounded custom-shadow">
<h3 class="text-center mb-4">Мои заказы</h3>
<!-- Разметка заказов -->
<div class="container">
<div class="accordion" id="accordionExample">
{% for order in orders %}
<div class="accordion-item">
<h2 class="accordion-header" id="heading{{ order.id }}">
<button class="accordion-button {% if order != orders.0 %}collapsed{% endif %}" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{ order.id }}" aria-expanded="false" aria-controls="collapse{{ order.id }}">
Заказ № {{ order.id }} - {{ order.created_timestamp }} | Статус: <strong class="mx-2">{{order.requires_delivery}}</strong>
</button>
</h2>
<div id="collapse{{ order.id }}" class="accordion-collapse collapse {% if order == orders.0 %}show{% endif %}" aria-labelledby="heading{{ order.id }}" data-bs-parent="#accordionExample">
<div class="accordion-body">
<table class="table table-dark table-hover">
<thead>
<tr>
<th>Товар</th>
<th>Количество</th>
<th>Цена</th>
<th>Общая стоимость</th>
</tr>
</thead>
<tbody>
{% for item in order.orderitem_set.all %}
<tr>
<td><a class="text-white" href="{% url 'goods:product' item.product.slug %}">{{ item.product.name }}</a></td>
<td>{{ item.quantity }}</td>
<td>{{ item.price }}</td>
<td>{{ item.products_price }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %} {% endblock %}

View File

@ -9,7 +9,7 @@ path('', views.product_list, name='product_list'),
path('<slug:category_slug>/', views.product_list, path('<slug:category_slug>/', views.product_list,
name='product_list_by_category'), name='product_list_by_category'),
path('<int:id>/<slug:slug>/', views.product_detail, path('<int:id>/<slug:slug>/', views.product_detail,
name='product_detail'), name='product_detail'), path('about/', views.about, name='about'),
] ]

View File

@ -31,3 +31,8 @@ def product_detail(request, id, slug):
'main/product/detail.html', 'main/product/detail.html',
{'product': product, {'product': product,
'cart_product_form': cart_product_form}) 'cart_product_form': cart_product_form})
# Функция для страницы "О нас"
def about(request):
return render(request, 'main/about.html') # предполагается, что у вас есть шаблон about.html в папке main

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

View File

@ -0,0 +1,35 @@
.profile-container {
background-color: #f4f0ec;
color: #3a3a3c;
padding: 20px;
border-radius: 10px;
max-width: 600px;
margin: auto;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.profile-heading {
color: #8a2be2;
text-align: center;
font-family: 'Merriweather', serif;
}
.user-greeting, .user-info {
text-align: center;
margin-top: 20px;
}
.user-name, .username {
color: #4a47a3;
font-weight: bold;
}
.mystic-decor {
text-align: center;
margin-top: 20px;
}
.mystic-symbol {
width: 100px;
height: auto;
}

View File

@ -5,7 +5,7 @@
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<link rel="stylesheet" href="{% static 'thx.css' %}"> <link rel="stylesheet" href="{% static 'thx.css' %}">
<div class="thank-you-page"> <div class="thank-you-page position-relative">
<h1 class="thank-you-title">Спасибо!</h1> <h1 class="thank-you-title">Спасибо!</h1>
<p class="thank-you-text">Ваш заказ успешно оформлен. Номер вашего заказа: <strong>{{ order.id }}</strong>.</p> <p class="thank-you-text">Ваш заказ успешно оформлен. Номер вашего заказа: <strong>{{ order.id }}</strong>.</p>
<div class="mystical-decor"> <div class="mystical-decor">

View File

@ -32,4 +32,3 @@ def order_created(request, order_id):

View File

@ -0,0 +1,86 @@
import requests
from djangoProject1 import settings
#Получение access_token
def get_access_token():
keycloack_server = settings.KEYCLOAK_CONFIG['SERVER_URL']
realm = settings.KEYCLOAK_CONFIG['REALM']
url = f'{keycloack_server}/realms/{realm}/protocol/openid-connect/token'
client_id = 'lowlight'
client_secret = settings.KEYCLOAK_CONFIG['CLIENT_SECRET']
username = 'lowlight'
password = ''
payload = {
'grant_type':'password',
'client_id':client_id,
'client_secret':client_secret,
'username':username,
'password':password,
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
response = requests.post(url,data=payload,headers=headers)
if response.ok:
data = response.json()
print('Acces Token:' ,data['access_token'])
print('Refresh Token:', data['refresh_token'])
return data['access_token']
else:
print('Error :',response.text)
def refresh_token():
keycloack_server = settings.KEYCLOAK_CONFIG['SERVER_URL']
realm = settings.KEYCLOAK_CONFIG['REALM']
url = f'{keycloack_server}/realms/{realm}/protocol/openid-connect/token'
client_id = 'lowlight'
refresh_token = 'eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIyMjRmYTgzYy00ZmMzLTQyMTgtYTEyMS00ZTQxZTdiYTVhNzcifQ.eyJleHAiOjE3MDg5NjYxOTcsImlhdCI6MTcwODk2NDM5NywianRpIjoiYWE5N2IwOTMtZjIxMS00N2VhLWFmNGItNjc5OTJiOTdkOGI5IiwiaXNzIjoiaHR0cHM6Ly9hdXRoLm15dGVyaW9yLmt6L3JlYWxtcy9IYXJtb255IiwiYXVkIjoiaHR0cHM6Ly9hdXRoLm15dGVyaW9yLmt6L3JlYWxtcy9IYXJtb255Iiwic3ViIjoiOWE3ZDNiNmUtZTExZi00OGQzLThmYzctMWMyOTZmZWE5MjYyIiwidHlwIjoiUmVmcmVzaCIsImF6cCI6Imxvd2xpZ2h0Iiwic2Vzc2lvbl9zdGF0ZSI6IjExNTJmYTkwLTM0ZmUtNGU1OS1iOTYyLTY5NDA0OWRiMGU2YiIsInNjb3BlIjoiZW1haWwgcHJvZmlsZSIsInNpZCI6IjExNTJmYTkwLTM0ZmUtNGU1OS1iOTYyLTY5NDA0OWRiMGU2YiJ9.-NifrZ0Y-d9-D5l6bxOn6RoTvqm9Ekvp9sTwP4oQoRw'
client_secret = settings.KEYCLOAK_CONFIG['CLIENT_SECRET']
payload = {
'client_id': client_id,
'grant_type': 'refresh_token',
'refresh_token': refresh_token,
'client_secret': client_secret,
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
response = requests.post(url, data=payload, headers=headers)
if response.ok:
data = response.json()
print('Access Token:', data.get('access_token'))
print('Refresh Token:', data.get('refresh_token'))
print('Data:', data)
else:
print('Error:', response.text)
def get_refresh_token():
keycloack_server = settings.KEYCLOAK_CONFIG['SERVER_URL']
realm = settings.KEYCLOAK_CONFIG['REALM']
url = f'{keycloack_server}/realms/{realm}/protocol/openid-connect/token'
client_id = 'lowlight'
client_secret = settings.KEYCLOAK_CONFIG['CLIENT_SECRET']
username = 'lowlight'
password = ''
payload = {
'grant_type': 'password',
'client_id': client_id,
'client_secret': client_secret,
'username': username,
'password': password,
}
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
}
response = requests.get(url,data=payload,headers = headers)
if response.ok:
data = response.json()
return ('Refresh Token',data.get('refresh_token'))
else:
print('Error :',response.text)

View File

@ -1,10 +1,9 @@
from django.urls import path # urls.py
from .views import keycloak_login, keycloak_callback, keycloak_logout
from .views import profile
urlpatterns = [
path('login/', keycloak_login, name='keycloak_login'),
path('callback/', keycloak_callback, name='keycloak_callback'),
path('logout/', keycloak_logout, name='keycloak_logout'),
path('profile/', profile, name='profile'),
from django.urls import path
from . import views
urlpatterns = [
path('test/', views.test_payment, name='test-payment'),
path('login/', views.keycloak_login, name='keycloak_login'),
] ]

View File

@ -1,15 +1,22 @@
from django.shortcuts import redirect,render # views.py
from django.shortcuts import redirect
from django.urls import reverse
from keycloak import KeycloakOpenID
from django.http import HttpResponse from django.http import HttpResponse
from keycloak import KeycloakOpenID, KeycloakGetError from django.contrib.auth.decorators import login_required
from django.conf import settings
import logging from djangoProject1 import settings
@login_required
def test_payment(request):
# Assuming the user's username is what you want to return
return HttpResponse(request.user.username)
# Inside your_app/views.py
# Настройка логгера для текущего модуля
logger = logging.getLogger(__name__)
# Функция для начала процесса аутентификации с Keycloak
def keycloak_login(request): def keycloak_login(request):
try:
keycloak_openid = KeycloakOpenID( keycloak_openid = KeycloakOpenID(
server_url=settings.KEYCLOAK_CONFIG['SERVER_URL'], server_url=settings.KEYCLOAK_CONFIG['SERVER_URL'],
client_id=settings.KEYCLOAK_CONFIG['CLIENT_ID'], client_id=settings.KEYCLOAK_CONFIG['CLIENT_ID'],
@ -17,73 +24,5 @@ def keycloak_login(request):
client_secret_key=settings.KEYCLOAK_CONFIG['CLIENT_SECRET'] client_secret_key=settings.KEYCLOAK_CONFIG['CLIENT_SECRET']
) )
auth_url = keycloak_openid.auth_url(redirect_uri=settings.KEYCLOAK_CONFIG['CALLBACK_URL']) auth_url = keycloak_openid.auth_url(redirect_uri=settings.KEYCLOAK_CONFIG['CALLBACK_URL'])
logger.info("Redirecting to Keycloak for authentication.")
return redirect(auth_url) return redirect(auth_url)
except Exception as e:
logger.error(f"Error during Keycloak login: {e}")
return HttpResponse("Ошибка при попытке аутентификации через Keycloak.")
# Функция обратного вызова для обработки ответа от Keycloak
def keycloak_callback(request):
code = request.GET.get('code')
if code:
try:
keycloak_openid = KeycloakOpenID(
server_url=settings.KEYCLOAK_CONFIG['SERVER_URL'],
client_id=settings.KEYCLOAK_CONFIG['CLIENT_ID'],
realm_name=settings.KEYCLOAK_CONFIG['REALM'],
client_secret_key=settings.KEYCLOAK_CONFIG['CLIENT_SECRET']
)
token = keycloak_openid.token(
grant_type=['authorization_code'],
code=code,
redirect_uri=settings.KEYCLOAK_CONFIG['CALLBACK_URL']
)
userinfo = keycloak_openid.userinfo(token['access_token'])
logger.info(f"User authenticated with Keycloak: {userinfo}")
request.session['keycloak_user_id'] = userinfo['sub']
request.session['username'] = userinfo.get('preferred_username', 'Guest')
return redirect('http://127.0.0.1:8000/products/')
except KeycloakGetError as e:
logger.error(f"Keycloak authentication error: {e}")
return HttpResponse("Ошибка аутентификации.")
except Exception as e:
logger.error(f"Unexpected error during Keycloak callback: {e}")
return HttpResponse("Неожиданная ошибка.")
else:
logger.warning("Authentication code not provided.")
return HttpResponse("Код аутентификации не предоставлен.")
# Функция для выхода из системы
def keycloak_logout(request):
try:
request.session.flush()
logger.info("User logged out.")
return redirect('http://127.0.0.1:8000/')
except Exception as e:
logger.error(f"Error during logout: {e}")
return HttpResponse("Ошибка при выходе из системы.")
def profile(request):
# Проверяем, аутентифицирован ли пользователь
if 'keycloak_user_id' in request.session:
# Извлекаем данные пользователя из сессии
user_id = request.session['keycloak_user_id']
username = request.session.get('username', 'Гость')
logger.info(f"Keycloak user ID: {request.session['keycloak_user_id']}, Username: {request.session['username']},{username}")
# Отображаем шаблон, передавая в него данные пользователя
return render(request, 'main/product/profile.html', {'username': username})
else:
# Если пользователь не аутентифицирован, перенаправляем на страницу входа
return redirect('keycloak_login')