090500
This commit is contained in:
@@ -9,3 +9,4 @@ docs/_book
|
|||||||
# TODO: where does this rule come from?
|
# TODO: where does this rule come from?
|
||||||
test/
|
test/
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
|||||||
@@ -1,2 +1,65 @@
|
|||||||
# vikileo.shop
|
# vikileo.shop
|
||||||
|
|
||||||
|
## Команды для настройки
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://git.fipi.pro/krasi/vikileo-shop.git
|
||||||
|
|
||||||
|
cd project_directory
|
||||||
|
python3 -m venv venv
|
||||||
|
pip3 install -r req.pip
|
||||||
|
|
||||||
|
python .\manage.py makemigrations accounts media comments main
|
||||||
|
python .\manage.py migrate
|
||||||
|
python .\manage.py createsuperuser
|
||||||
|
python .\manage.py runserver
|
||||||
|
```
|
||||||
|
|
||||||
|
## Для запуска tailwindcss
|
||||||
|
|
||||||
|
```
|
||||||
|
npx tailwindcss -i .\staticfiles\src\input.css -o .\staticfiles\src\output.css --watch
|
||||||
|
```
|
||||||
|
|
||||||
|
Отменить миграции:
|
||||||
|
> python manage.py showmigrations
|
||||||
|
|
||||||
|
Отмените миграцию, которая была применена раньше своей зависимости:
|
||||||
|
> python manage.py migrate app_name zero
|
||||||
|
|
||||||
|
Откатите миграции до той, которая вызывает проблему, используя команду:
|
||||||
|
> python manage.py migrate app_name migration_name
|
||||||
|
|
||||||
|
## Ошибки при миграции в apps properties
|
||||||
|
Если модель никак не хочеть бигрировать то нужно удалить из всех моделей
|
||||||
|
|
||||||
|
> history = HistoricalRecords()
|
||||||
|
|
||||||
|
В моделе есть строка после всех from просто перекоменть её
|
||||||
|
|
||||||
|
> History = 1
|
||||||
|
|
||||||
|
После создания БД вернуть обратно
|
||||||
|
|
||||||
|
## Ошибки при добавлении в таблицу
|
||||||
|
|
||||||
|
При добавлении нового поля в таблицу БД можно использовать число 1, если это не время для времени есть следующий параметр.
|
||||||
|
|
||||||
|
> default='2024-04-01T12:00:00Z'
|
||||||
|
# Документация по API
|
||||||
|
|
||||||
|
Проект предоставляет документацию по API с использованием Swagger. Чтобы получить доступ к документации API, выполните следующие действия:
|
||||||
|
|
||||||
|
Откройте веб-браузер и перейдите к следующему адресу:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/swagger
|
||||||
|
```
|
||||||
|
|
||||||
|
## Состав проекта
|
||||||
|
|
||||||
|
```
|
||||||
|
static
|
||||||
|
└── src
|
||||||
|
└── input.css
|
||||||
|
```
|
||||||
@@ -1,3 +1,62 @@
|
|||||||
|
import logging
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth.admin import UserAdmin
|
||||||
|
|
||||||
# Register your models here.
|
from config.accounts.models import User
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileInline(admin.StackedInline):
|
||||||
|
model = User
|
||||||
|
can_delete = False
|
||||||
|
max_num = 1
|
||||||
|
verbose_name = 'Profile'
|
||||||
|
|
||||||
|
verbose_name_plural = 'Profile'
|
||||||
|
fk_name = 'user'
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(User)
|
||||||
|
class UserAdmin(UserAdmin):
|
||||||
|
ordering = ('email', )
|
||||||
|
list_display = (
|
||||||
|
'id', 'email', 'is_staff', 'is_superuser', 'is_active', 'date_joined', 'last_updated', 'last_login'
|
||||||
|
)
|
||||||
|
search_fields = ('email',)
|
||||||
|
list_filter = (
|
||||||
|
'is_staff', 'is_superuser', 'is_active',
|
||||||
|
)
|
||||||
|
readonly_fields = (
|
||||||
|
'last_login', 'last_updated', 'date_joined'
|
||||||
|
)
|
||||||
|
list_display_links = ('id', 'email')
|
||||||
|
fieldsets = (
|
||||||
|
(None, {'fields': ('email', 'password')}),
|
||||||
|
('Permissions', {'fields': ('groups', 'user_permissions')}),
|
||||||
|
('Roles', {'fields': ('is_staff', 'is_superuser', 'is_active')}),
|
||||||
|
('Profile', {'fields': (
|
||||||
|
'username', 'first_name', 'last_name', 'gender', 'bio', 'address', 'followers'
|
||||||
|
)}),
|
||||||
|
('Dates', {'fields': ('last_login', 'last_updated', 'date_joined')})
|
||||||
|
)
|
||||||
|
add_fieldsets = (
|
||||||
|
(None, {
|
||||||
|
'classes': ('wide', ),
|
||||||
|
'fields': (
|
||||||
|
'email', 'password1', 'password2'
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
"""код что отображает все модели всех apps
|
||||||
|
|
||||||
|
|
||||||
|
models = apps.get_models()
|
||||||
|
for model in models:
|
||||||
|
try:
|
||||||
|
admin.site.register(model)
|
||||||
|
except admin.sites.AlreadyRegistered:
|
||||||
|
pass
|
||||||
|
"""
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ from django.apps import AppConfig
|
|||||||
|
|
||||||
class AccountsConfig(AppConfig):
|
class AccountsConfig(AppConfig):
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
name = 'accounts'
|
name = 'config.accounts'
|
||||||
|
verbose_name = 'Управление пользователями'
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# Generated by Django 4.2.13 on 2024-05-09 15:55
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0012_alter_user_first_name_max_length'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='User',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||||
|
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||||
|
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||||
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
|
('first_name', models.CharField(blank=True, max_length=60, null=True, verbose_name='Имя')),
|
||||||
|
('last_name', models.CharField(blank=True, max_length=60, null=True, verbose_name='Фамилия')),
|
||||||
|
('gender', models.CharField(choices=[('n', 'Не указано'), ('m', 'Мужчина'), ('f', 'Женщина')], default='n', max_length=10, verbose_name='Пол')),
|
||||||
|
('email', models.EmailField(max_length=254, unique=True, verbose_name='Email Address')),
|
||||||
|
('username', models.CharField(max_length=60, verbose_name='Ник')),
|
||||||
|
('bio', models.TextField(blank=True, verbose_name='О себе')),
|
||||||
|
('image', models.URLField(blank=True, null=True, verbose_name='Аватар')),
|
||||||
|
('address', models.CharField(blank=True, max_length=255, null=True, verbose_name='Адрес')),
|
||||||
|
('date_joined', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Дата регистрации')),
|
||||||
|
('last_updated', models.DateTimeField(auto_now=True, null=True, verbose_name='Последнее обновление')),
|
||||||
|
('last_login', models.DateTimeField(auto_now=True, null=True, verbose_name='Последняя авторизация')),
|
||||||
|
('status', models.BooleanField(default=False, verbose_name='Статус')),
|
||||||
|
('followers', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Подписчики')),
|
||||||
|
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
||||||
|
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'user',
|
||||||
|
'verbose_name_plural': 'users',
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -9,6 +9,7 @@ STATICFILES_DIR = os.path.join(BASE_DIR, "staticfiles")
|
|||||||
STATIC_DIR = os.path.join(BASE_DIR, "static")
|
STATIC_DIR = os.path.join(BASE_DIR, "static")
|
||||||
MEDIA_DIR = os.path.join(BASE_DIR, "mediafiles")
|
MEDIA_DIR = os.path.join(BASE_DIR, "mediafiles")
|
||||||
LOGS_DIR = os.path.join(BASE_DIR, "logs")
|
LOGS_DIR = os.path.join(BASE_DIR, "logs")
|
||||||
|
COMPRESS_ROOT = os.path.join(BASE_DIR, "staticfiles")
|
||||||
SECRET_KEY = 'django-insecure-xvn9xig-%a4=4xt14#yo6on@g(l$tc!r^8i#ard1nio(4i_b+@'
|
SECRET_KEY = 'django-insecure-xvn9xig-%a4=4xt14#yo6on@g(l$tc!r^8i#ard1nio(4i_b+@'
|
||||||
DADATA_API_KEY = 'a6370792e9cfc9afdf5074c604eadf093ce9521f'
|
DADATA_API_KEY = 'a6370792e9cfc9afdf5074c604eadf093ce9521f'
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|||||||
+8
-3
@@ -14,6 +14,7 @@ from .local_settings import (
|
|||||||
STATIC_DIR,
|
STATIC_DIR,
|
||||||
MEDIA_DIR,
|
MEDIA_DIR,
|
||||||
LOGS_DIR,
|
LOGS_DIR,
|
||||||
|
COMPRESS_ROOT,
|
||||||
CORS_ORIGIN_WHITELIST,
|
CORS_ORIGIN_WHITELIST,
|
||||||
CSRF_TRUSTED_ORIGINS
|
CSRF_TRUSTED_ORIGINS
|
||||||
)
|
)
|
||||||
@@ -57,13 +58,14 @@ THIRD_PARTY_APPS = [
|
|||||||
'autoslug',
|
'autoslug',
|
||||||
'localflavor',
|
'localflavor',
|
||||||
'simple_history',
|
'simple_history',
|
||||||
|
'compressor',
|
||||||
]
|
]
|
||||||
# Внутренние приложения
|
# Внутренние приложения
|
||||||
LOCAL_APPS = [
|
LOCAL_APPS = [
|
||||||
'config.accounts.apps.AccountsConfig',
|
'config.accounts.apps.AccountsConfig',
|
||||||
#'system.media.apps.MediaConfig',
|
'system.media.apps.MediaConfig',
|
||||||
#'system.comments.apps.CommentsConfig',
|
'system.comments.apps.CommentsConfig',
|
||||||
#'main.apps.MainConfig',
|
'main.apps.MainConfig',
|
||||||
#'articles.apps.ArticlesConfig',
|
#'articles.apps.ArticlesConfig',
|
||||||
#'todos.apps.TodosConfig',
|
#'todos.apps.TodosConfig',
|
||||||
]
|
]
|
||||||
@@ -168,3 +170,6 @@ CSRF_COOKIE_SECURE = True
|
|||||||
CSRF_COOKIE_HTTPONLY = True
|
CSRF_COOKIE_HTTPONLY = True
|
||||||
# При выходе из учётной записи вас направит на данный url
|
# При выходе из учётной записи вас направит на данный url
|
||||||
LOGOUT_REDIRECT_URL = "main"
|
LOGOUT_REDIRECT_URL = "main"
|
||||||
|
COMPRESS_ROOT = COMPRESS_ROOT
|
||||||
|
COMPRESS_ENABLED = True
|
||||||
|
STATICFILES_FINDERS = ('compressor.finders.CompressorFinder',)
|
||||||
+25
-19
@@ -5,9 +5,11 @@ from django.conf.urls.static import static
|
|||||||
from rest_framework import permissions
|
from rest_framework import permissions
|
||||||
from drf_yasg.views import get_schema_view
|
from drf_yasg.views import get_schema_view
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
#from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView, TokenVerifyView
|
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView, TokenVerifyView
|
||||||
# Вставки из апсов приложения
|
"""
|
||||||
# Машиночитаемая [схема] описывает, какие ресурсы доступны через API
|
Вставки из апсов приложения
|
||||||
|
Машиночитаемая [схема] описывает, какие ресурсы доступны через API
|
||||||
|
"""
|
||||||
schema_view = get_schema_view(
|
schema_view = get_schema_view(
|
||||||
openapi.Info(
|
openapi.Info(
|
||||||
title="vikileo.shop API",
|
title="vikileo.shop API",
|
||||||
@@ -17,35 +19,39 @@ schema_view = get_schema_view(
|
|||||||
public=True,
|
public=True,
|
||||||
permission_classes=(permissions.AllowAny,),
|
permission_classes=(permissions.AllowAny,),
|
||||||
)
|
)
|
||||||
# Префикс для понтов, можно без него но сним проще ориентироваться что должно иди на фронт, а что для тестов.
|
""" Префикс для понтов, можно без него но сним проще ориентироваться что должно иди на фронт, а что для тестов. """
|
||||||
api_prefix = 'api'
|
api_prefix = 'api'
|
||||||
# Кортежи адресов для системных приложений
|
""" Кортежи адресов для системных приложений """
|
||||||
urlpatterns_system = [
|
urlpatterns_system = [
|
||||||
path('admin/', admin.site.urls), # Адрес админ панели
|
path('admin/', admin.site.urls), # Адрес админ панели
|
||||||
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-redoc'), # API сборник адресов
|
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-redoc'), # API сборник адресов
|
||||||
|
path('api-rest/', include('rest_framework.urls')), # API rest, допилить виюшку
|
||||||
|
path(f'{api_prefix}/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), # Присвоение токена
|
||||||
|
path(f'{api_prefix}/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), # Перевыдача токена
|
||||||
|
path(f'{api_prefix}/token/verify/', TokenVerifyView.as_view(), name='token_verify'), # Верифекация токена
|
||||||
]
|
]
|
||||||
# Кортежи адресов для приложений
|
""" Кортежи адресов для приложений """
|
||||||
urlpatterns_apps = [
|
urlpatterns_apps = [
|
||||||
path('', include('main.urls')), # Адрес главной странице (заглушка)
|
|
||||||
path(f'{api_prefix}/', include('config.accounts.urls')), # Адрес управление учётными данными
|
path(f'{api_prefix}/', include('config.accounts.urls')), # Адрес управление учётными данными
|
||||||
#path(f'{api_prefix}/', include('system.comments.urls')), # Адрес комментариев от пользователей
|
path(f'{api_prefix}/', include('system.comments.urls')), # Адрес комментариев от пользователей
|
||||||
#path(f'{api_prefix}/', include('todos.urls')), # Адрес статей
|
path('', include('main.urls')), # Адрес главной странице (заглушка)
|
||||||
#path(f'{api_prefix}/', include('articles.urls')), # Адрес стадей для пользователей
|
path('', include('config.accounts.urls')), # Адрес управление учётными данными
|
||||||
|
|
||||||
]
|
]
|
||||||
# Кортежи адресов для тестовых приложений
|
""" Кортежи адресов для тестовых приложений """
|
||||||
urlpatterns_test = [
|
urlpatterns_test = [
|
||||||
#path('/accounts/', include('system.accounts.urls')), # Адрес управление учётными данными
|
#path(f'{api_prefix}/', include('system.media.urls')), # Адрес файлаобменника
|
||||||
|
#path(f'{api_prefix}/', include('todos.urls')), # Адрес задач админов
|
||||||
|
#path(f'{api_prefix}/', include('articles.urls')), # Адрес стадей для пользователей
|
||||||
#path(f'{api_prefix}/token/', obtain_token, name='token_obtain_pair'),
|
#path(f'{api_prefix}/token/', obtain_token, name='token_obtain_pair'),
|
||||||
#path(f'{api_prefix}/protected/', protected_view, name='protected_view'),
|
#path(f'{api_prefix}/protected/', protected_view, name='protected_view'),
|
||||||
path('api-rest/', include('rest_framework.urls')), # API rest, допилить виюшку
|
|
||||||
#path(f'{api_prefix}/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), # Присвоение токена
|
|
||||||
#path(f'{api_prefix}/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), # Перевыдача токена
|
|
||||||
#path(f'{api_prefix}/token/verify/', TokenVerifyView.as_view(), name='token_verify'), # Верифекация токена
|
|
||||||
#path(f'{api_prefix}/', include('system.media.urls')), # Адрес файлаобменника
|
|
||||||
]
|
]
|
||||||
# Объединение кортежа адресов
|
""" Объединение кортежа адресов """
|
||||||
urlpatterns = urlpatterns_system + urlpatterns_apps + urlpatterns_test
|
urlpatterns = urlpatterns_system + urlpatterns_apps + urlpatterns_test
|
||||||
# Обслуживание файлов, загруженных пользователем во время разработки
|
""" Обслуживание файлов, загруженных пользователем во время разработки """
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class MainConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'main'
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 4.2.13 on 2024-05-09 19:08
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ThemeConfiguration',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('theme', models.BooleanField(default=True, verbose_name='theme')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='themeconfiguration',
|
||||||
|
constraint=models.UniqueConstraint(fields=('user',), name='One Entry Per User'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Generated by Django 4.2.13 on 2024-05-09 19:13
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('main', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='ThemeConfiguration',
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
from django.db import models
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
from django.urls import path, include
|
||||||
|
from rest_framework.routers import DefaultRouter
|
||||||
|
#
|
||||||
|
from main import views
|
||||||
|
from .views import MainView
|
||||||
|
#
|
||||||
|
#main_router = DefaultRouter(trailing_slash=False)
|
||||||
|
#main_router.register('main', views.MainView)
|
||||||
|
#
|
||||||
|
urlpatterns = [
|
||||||
|
path('', MainView.as_view(), name='main'),
|
||||||
|
#path('', include(main_router.urls)),
|
||||||
|
]
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
from django.views.generic import View, TemplateView
|
||||||
|
|
||||||
|
|
||||||
|
# Простое отображение страницы заглушки.
|
||||||
|
class MainView(TemplateView):
|
||||||
|
template_name = 'index.html'
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
context = {
|
||||||
|
'title': 'Объявления',
|
||||||
|
"message_hello": "Тут какой-то мессадж."
|
||||||
|
}
|
||||||
|
return render(request, 'index.html', context)
|
||||||
Generated
+1358
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"dev": "npx tailwindcss -i .\\staticfiles\\src\\input.css -o .\\staticfiles\\src\\output.css --watch\n",
|
||||||
|
"build": "npx tailwindcss -i ./static/src/input.css -o ./static/src/styles.css -m"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"tailwindcss": "^3.4.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||||
|
"@tailwindcss/forms": "^0.5.7",
|
||||||
|
"@tailwindcss/typography": "^0.5.13",
|
||||||
|
"flowbite": "^2.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"paths": {}, "version": "1.1", "hash": "d75171398898"}
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
Binary file not shown.
@@ -0,0 +1,3 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from simple_history.admin import SimpleHistoryAdmin
|
||||||
|
from .models import *
|
||||||
|
|
||||||
|
admin.site.register(Comment, SimpleHistoryAdmin)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class CommentsConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'system.comments'
|
||||||
|
verbose_name = 'Комментарии'
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Generated by Django 4.2.13 on 2024-05-09 16:30
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Comment',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('content', models.TextField(help_text='Введите комментарий', verbose_name='Комментарий')),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Комментарий',
|
||||||
|
'verbose_name_plural': 'Комментарии',
|
||||||
|
'ordering': ['-created'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
from django.db import models
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
""" Комментарии основная модель """
|
||||||
|
|
||||||
|
|
||||||
|
class Comment(models.Model):
|
||||||
|
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||||
|
content = models.TextField(verbose_name='Комментарий', help_text='Введите комментарий')
|
||||||
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'Комментарий'
|
||||||
|
verbose_name_plural = 'Комментарии'
|
||||||
|
ordering = ['-created']
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
from system.comments.models import Comment
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorSerializer(serializers.ModelSerializer):
|
||||||
|
following = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ('username', 'bio', 'image', 'following')
|
||||||
|
ref_name = 'GoodsAuthorSerializer' # Указываем уникальное имя для этого сериализатора
|
||||||
|
|
||||||
|
def get_following(self, obj):
|
||||||
|
user = self.context.get('request').user
|
||||||
|
if user.is_authenticated:
|
||||||
|
return obj.followers.filter(pk=user.id).exists()
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class CommentSerializer(serializers.ModelSerializer):
|
||||||
|
author = serializers.SerializerMethodField()
|
||||||
|
createdAt = serializers.DateTimeField(source='created', format='%Y-%m-%dT%H:%M:%S.%fZ', required=False)
|
||||||
|
updatedAt = serializers.DateTimeField(source='updated', format='%Y-%m-%dT%H:%M:%S.%fZ', required=False)
|
||||||
|
body = serializers.CharField(source='content', required=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Comment
|
||||||
|
fields = ['id', 'createdAt', 'updatedAt', 'body', 'author']
|
||||||
|
|
||||||
|
|
||||||
|
def get_author(self, obj):
|
||||||
|
request = self.context.get('request')
|
||||||
|
serializer = AuthorSerializer(obj.author, context={'request': request})
|
||||||
|
return serializer.data
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
comment = Comment(
|
||||||
|
**validated_data,
|
||||||
|
author=self.context['request'].user,
|
||||||
|
article=self.context['article']
|
||||||
|
)
|
||||||
|
comment.save()
|
||||||
|
return comment
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from system.comments import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
#path('articles/<str:slug>/comments', views.CommentView.as_view(), name='comment-article'),
|
||||||
|
#path('articles/<str:slug>/comments/<int:id>', views.DeleteCommentView.as_view(), name='comment-delete'),
|
||||||
|
]
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
from rest_framework import generics , status
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from system.comments.models import Comment
|
||||||
|
from system.comments.serializers import CommentSerializer
|
||||||
|
|
||||||
|
|
||||||
|
"""class CommentView(generics.ListCreateAPIView):
|
||||||
|
queryset = Comment.objects.all()
|
||||||
|
serializer_class = CommentSerializer
|
||||||
|
permission_classes=[IsAuthenticated,]
|
||||||
|
|
||||||
|
def get_permissions(self):
|
||||||
|
if self.request.method == 'GET':
|
||||||
|
return [IsAuthenticatedOrReadOnly()]
|
||||||
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
|
def post(self, request, slug, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
|
||||||
|
article = Article.objects.get(slug=slug)
|
||||||
|
comment_data = request.data.get('comment')
|
||||||
|
|
||||||
|
serializer_context = self.get_serializer_context()
|
||||||
|
serializer_context['article'] = article
|
||||||
|
|
||||||
|
serializer = self.get_serializer(data=comment_data, context=serializer_context)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
self.perform_create(serializer)
|
||||||
|
|
||||||
|
return Response({"comment": serializer.data}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
return Response({"errors": {
|
||||||
|
"body": [
|
||||||
|
"Bad Request"
|
||||||
|
]
|
||||||
|
}}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def list(self, request, slug, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
|
||||||
|
article = Article.objects.get(slug=slug)
|
||||||
|
comments = Comment.objects.filter(article=article).order_by('-created')
|
||||||
|
|
||||||
|
serializer = self.get_serializer(comments, many=True)
|
||||||
|
|
||||||
|
response = {
|
||||||
|
'comments': serializer.data,
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response(response)
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
return Response({"errors": {
|
||||||
|
"body": [
|
||||||
|
"Bad Request"
|
||||||
|
]
|
||||||
|
}}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
|
||||||
|
class DeleteCommentView(generics.DestroyAPIView):
|
||||||
|
|
||||||
|
def destroy(self, request, slug, id, *args, **kwargs):
|
||||||
|
try:
|
||||||
|
|
||||||
|
Article.objects.get(slug=slug)
|
||||||
|
comment = Comment.objects.get(id=id)
|
||||||
|
self.perform_destroy(comment)
|
||||||
|
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
return Response({"errors": {
|
||||||
|
"body": [
|
||||||
|
"Bad Request"
|
||||||
|
]
|
||||||
|
}}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from simple_history.admin import SimpleHistoryAdmin
|
||||||
|
from .models import *
|
||||||
|
|
||||||
|
admin.site.register(Images, SimpleHistoryAdmin)
|
||||||
|
admin.site.register(Videos, SimpleHistoryAdmin)
|
||||||
|
admin.site.register(OtherFiles, SimpleHistoryAdmin)
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class MediaConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'system.media'
|
||||||
|
verbose_name = 'Файловое хранилище'
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
# Generated by Django 4.2.13 on 2024-05-09 16:30
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import simple_history.models
|
||||||
|
import system.media.validators
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Videos',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')),
|
||||||
|
('file_name', models.CharField(help_text='Введите имя файла', max_length=255, verbose_name='Имя файла')),
|
||||||
|
('file', models.FileField(help_text='Загрузите видео', upload_to='videos/%Y/%m/%d/', validators=[system.media.validators.validate_max_file_size], verbose_name='Видео')),
|
||||||
|
('author', models.ForeignKey(help_text='Выберите автора объекта', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Автор')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Видео',
|
||||||
|
'verbose_name_plural': 'Видео',
|
||||||
|
'ordering': ['-created_at'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OtherFiles',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')),
|
||||||
|
('file_name', models.CharField(help_text='Введите имя файла', max_length=255, verbose_name='Имя файла')),
|
||||||
|
('file', models.FileField(help_text='Загрузите документ', upload_to='documents/%Y/%m/%d/', validators=[system.media.validators.validate_max_file_size], verbose_name='Документ')),
|
||||||
|
('author', models.ForeignKey(help_text='Выберите автора объекта', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Автор')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Документ',
|
||||||
|
'verbose_name_plural': 'Документы',
|
||||||
|
'ordering': ['-created_at'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Images',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')),
|
||||||
|
('file_name', models.CharField(help_text='Введите имя файла', max_length=255, verbose_name='Имя файла')),
|
||||||
|
('file', models.ImageField(help_text='Загрузите изображение', upload_to='images/%Y/%m/%d/', validators=[system.media.validators.validate_max_file_size], verbose_name='Изображение')),
|
||||||
|
('author', models.ForeignKey(help_text='Выберите автора объекта', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Автор')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Изображение',
|
||||||
|
'verbose_name_plural': 'Изображения',
|
||||||
|
'ordering': ['-created_at'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='HistoricalVideos',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||||
|
('created_at', models.DateTimeField(blank=True, editable=False, verbose_name='Дата создания')),
|
||||||
|
('updated_at', models.DateTimeField(blank=True, editable=False, verbose_name='Дата обновления')),
|
||||||
|
('file_name', models.CharField(help_text='Введите имя файла', max_length=255, verbose_name='Имя файла')),
|
||||||
|
('file', models.TextField(help_text='Загрузите видео', max_length=100, validators=[system.media.validators.validate_max_file_size], verbose_name='Видео')),
|
||||||
|
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||||
|
('history_date', models.DateTimeField(db_index=True)),
|
||||||
|
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||||
|
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||||
|
('author', models.ForeignKey(blank=True, db_constraint=False, help_text='Выберите автора объекта', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Автор')),
|
||||||
|
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'historical Видео',
|
||||||
|
'verbose_name_plural': 'historical Видео',
|
||||||
|
'ordering': ('-history_date', '-history_id'),
|
||||||
|
'get_latest_by': ('history_date', 'history_id'),
|
||||||
|
},
|
||||||
|
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='HistoricalOtherFiles',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||||
|
('created_at', models.DateTimeField(blank=True, editable=False, verbose_name='Дата создания')),
|
||||||
|
('updated_at', models.DateTimeField(blank=True, editable=False, verbose_name='Дата обновления')),
|
||||||
|
('file_name', models.CharField(help_text='Введите имя файла', max_length=255, verbose_name='Имя файла')),
|
||||||
|
('file', models.TextField(help_text='Загрузите документ', max_length=100, validators=[system.media.validators.validate_max_file_size], verbose_name='Документ')),
|
||||||
|
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||||
|
('history_date', models.DateTimeField(db_index=True)),
|
||||||
|
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||||
|
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||||
|
('author', models.ForeignKey(blank=True, db_constraint=False, help_text='Выберите автора объекта', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Автор')),
|
||||||
|
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'historical Документ',
|
||||||
|
'verbose_name_plural': 'historical Документы',
|
||||||
|
'ordering': ('-history_date', '-history_id'),
|
||||||
|
'get_latest_by': ('history_date', 'history_id'),
|
||||||
|
},
|
||||||
|
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='HistoricalImages',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||||
|
('created_at', models.DateTimeField(blank=True, editable=False, verbose_name='Дата создания')),
|
||||||
|
('updated_at', models.DateTimeField(blank=True, editable=False, verbose_name='Дата обновления')),
|
||||||
|
('file_name', models.CharField(help_text='Введите имя файла', max_length=255, verbose_name='Имя файла')),
|
||||||
|
('file', models.TextField(help_text='Загрузите изображение', max_length=100, validators=[system.media.validators.validate_max_file_size], verbose_name='Изображение')),
|
||||||
|
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||||
|
('history_date', models.DateTimeField(db_index=True)),
|
||||||
|
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||||
|
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||||
|
('author', models.ForeignKey(blank=True, db_constraint=False, help_text='Выберите автора объекта', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Автор')),
|
||||||
|
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'historical Изображение',
|
||||||
|
'verbose_name_plural': 'historical Изображения',
|
||||||
|
'ordering': ('-history_date', '-history_id'),
|
||||||
|
'get_latest_by': ('history_date', 'history_id'),
|
||||||
|
},
|
||||||
|
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import os
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from simple_history.models import HistoricalRecords
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
#
|
||||||
|
from .validators import validate_max_file_size
|
||||||
|
#
|
||||||
|
User = get_user_model()
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
""" Абстрактная базовая модель для добавления автора полей времени создания и изменения."""
|
||||||
|
|
||||||
|
|
||||||
|
class TimeStampedModel(models.Model):
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')
|
||||||
|
updated_at = models.DateTimeField(auto_now=True, verbose_name='Дата обновления')
|
||||||
|
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, verbose_name='Автор',
|
||||||
|
help_text='Выберите автора объекта')
|
||||||
|
file_name = models.CharField(max_length=255, verbose_name='Имя файла', help_text='Введите имя файла')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.pk: # Проверяем, что это новый объект (не обновление)
|
||||||
|
self.created_at = timezone.now() # Автоматически устанавливаем дату создания
|
||||||
|
self.updated_at = timezone.now() # Всегда обновляем дату обновления
|
||||||
|
super().save(*args, **kwargs) # Вызываем оригинальный метод сохранения
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.__class__.__name__}: {self.file_name}'
|
||||||
|
|
||||||
|
def generate_file_url(self):
|
||||||
|
if self.file_name:
|
||||||
|
return os.path.join('media', str(self.file_name))
|
||||||
|
|
||||||
|
|
||||||
|
""" Модель файлов изображения """
|
||||||
|
|
||||||
|
|
||||||
|
class Images(TimeStampedModel):
|
||||||
|
file = models.ImageField(upload_to='images/%Y/%m/%d/', verbose_name='Изображение',
|
||||||
|
help_text='Загрузите изображение', validators=[validate_max_file_size])
|
||||||
|
|
||||||
|
history = HistoricalRecords()
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
self.name = f'Image_{self.file_name}' # Префикс "Image_" для изображений
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'Изображение'
|
||||||
|
verbose_name_plural = 'Изображения'
|
||||||
|
ordering = ['-created_at']
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
super().clean()
|
||||||
|
if self.file:
|
||||||
|
# Проверяем расширение файла
|
||||||
|
ext = self.file.name.split('.')[-1]
|
||||||
|
if ext.lower() not in ['jpg', 'jpeg', 'png', 'gif']:
|
||||||
|
raise ValidationError(_(
|
||||||
|
'Недопустимый формат изображения. Поддерживаемые форматы: JPG, JPEG, PNG, GIF.'
|
||||||
|
))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.__class__.__name__}: {self.file_name}'
|
||||||
|
|
||||||
|
|
||||||
|
""" Модель видео файлов """
|
||||||
|
|
||||||
|
|
||||||
|
class Videos(TimeStampedModel):
|
||||||
|
file = models.FileField(upload_to='videos/%Y/%m/%d/', verbose_name='Видео',
|
||||||
|
help_text='Загрузите видео', validators=[validate_max_file_size])
|
||||||
|
|
||||||
|
history = HistoricalRecords()
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
self.file_name = f'Video_{self.file_name}' # Префикс "Video_" для видео
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'Видео'
|
||||||
|
verbose_name_plural = 'Видео'
|
||||||
|
ordering = ['-created_at']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.__class__.__name__}: {self.file_name}'
|
||||||
|
|
||||||
|
|
||||||
|
""" Модель файлов всех типов документов """
|
||||||
|
|
||||||
|
|
||||||
|
class OtherFiles(TimeStampedModel):
|
||||||
|
file = models.FileField(upload_to='documents/%Y/%m/%d/', verbose_name='Документ',
|
||||||
|
help_text='Загрузите документ', validators=[validate_max_file_size])
|
||||||
|
|
||||||
|
history = HistoricalRecords()
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
self.file_name = f'Document_{self.file_name}' # Префикс "Document_" для документов
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'Документ'
|
||||||
|
verbose_name_plural = 'Документы'
|
||||||
|
ordering = ['-created_at']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.__class__.__name__}: {self.file_name}'
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
def validate_max_file_size(value):
|
||||||
|
# Максимальный размер файла в байтах (например, 200 МБ = 200 * 1024 * 1024 байт)
|
||||||
|
max_size = 200 * 1024 * 1024
|
||||||
|
if value.size > max_size:
|
||||||
|
raise ValidationError(_('Файл слишком большой. Максимальный размер файла - 200 МБ.'))
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
# Create your views here.
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
darkMode: 'media',
|
||||||
|
content: [
|
||||||
|
'./templates/**/*.html',
|
||||||
|
'./node_modules/flowbite/**/*.js'
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
spacing: {
|
||||||
|
'icon-size': '1.5rem', // размер иконки
|
||||||
|
},
|
||||||
|
colors: {
|
||||||
|
'icon-color': '#333', // цвет иконки
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
icon: ['currentColor'], // автоматическое заполнение цветом иконки
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
icon: ['Heroicons'], // название шрифта для иконок
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
require('flowbite/plugin'),
|
||||||
|
require('@tailwindcss/forms'),
|
||||||
|
require('@tailwindcss/typography'),
|
||||||
|
require('@tailwindcss/aspect-ratio')
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{% load compress %}
|
||||||
|
{% load static %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
{% include 'components/seo.html' %}
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
<!-- styles -->
|
||||||
|
{% compress css %}
|
||||||
|
<link rel="stylesheet" href="{% static 'src/output.css' %}">
|
||||||
|
{% endcompress %}
|
||||||
|
{% include 'components/css.html' %}
|
||||||
|
<!-- script -->
|
||||||
|
{% include 'components/script.html' %}
|
||||||
|
</head>
|
||||||
|
<body class="antialiased bg-gray-50 dark:bg-gray-900">
|
||||||
|
{% include 'components/navbars/nav.html' %}
|
||||||
|
{% include 'components/navbars/sidebar.html' %}
|
||||||
|
<div class="container mx-auto mt-4">
|
||||||
|
{% block content %}
|
||||||
|
{% endblock content %}
|
||||||
|
</div>
|
||||||
|
<!-- script -->
|
||||||
|
{% include 'components/footer/script.html' %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
{% load static %}
|
||||||
|
<link rel="icon" type="image/x-icon" href="{% static 'assets/favicon.ico' %}" />
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!-- Дополнительный скрипт в вашем шаблоне -->
|
||||||
|
<script>
|
||||||
|
var themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
|
||||||
|
var themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
|
||||||
|
|
||||||
|
// Change the icons inside the button based on previous settings
|
||||||
|
if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||||
|
themeToggleLightIcon.classList.remove('hidden');
|
||||||
|
} else {
|
||||||
|
themeToggleDarkIcon.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
var themeToggleBtn = document.getElementById('theme-toggle');
|
||||||
|
|
||||||
|
themeToggleBtn.addEventListener('click', function() {
|
||||||
|
|
||||||
|
// toggle icons inside button
|
||||||
|
themeToggleDarkIcon.classList.toggle('hidden');
|
||||||
|
themeToggleLightIcon.classList.toggle('hidden');
|
||||||
|
|
||||||
|
// if set via local storage previously
|
||||||
|
if (localStorage.getItem('color-theme')) {
|
||||||
|
if (localStorage.getItem('color-theme') === 'light') {
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
localStorage.setItem('color-theme', 'dark');
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove('dark');
|
||||||
|
localStorage.setItem('color-theme', 'light');
|
||||||
|
}
|
||||||
|
|
||||||
|
// if NOT set via local storage previously
|
||||||
|
} else {
|
||||||
|
if (document.documentElement.classList.contains('dark')) {
|
||||||
|
document.documentElement.classList.remove('dark');
|
||||||
|
localStorage.setItem('color-theme', 'light');
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
localStorage.setItem('color-theme', 'dark');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-dropdown-toggle="apps-dropdown"
|
||||||
|
class="p-2 text-gray-500 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-white dark:hover:bg-gray-700 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
|
||||||
|
>
|
||||||
|
<span class="sr-only">View notifications</span>
|
||||||
|
<!-- Icon -->
|
||||||
|
<svg
|
||||||
|
class="w-6 h-6"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M5 3a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2V5a2 2 0 00-2-2H5zM5 11a2 2 0 00-2 2v2a2 2 0 002 2h2a2 2 0 002-2v-2a2 2 0 00-2-2H5zM11 5a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V5zM11 13a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
class="hidden overflow-hidden z-50 my-4 max-w-sm text-base list-none bg-white rounded divide-y divide-gray-100 shadow-lg dark:bg-gray-700 dark:divide-gray-600 rounded-xl"
|
||||||
|
id="apps-dropdown"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="block py-2 px-4 text-base font-medium text-center text-gray-700 bg-gray-50 dark:bg-gray-600 dark:text-gray-300"
|
||||||
|
>
|
||||||
|
Apps
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-3 gap-4 p-4">
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600 group"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mx-auto mb-1 w-7 h-7 text-gray-400 group-hover:text-gray-500 dark:text-gray-400 dark:group-hover:text-gray-400"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M10 2a4 4 0 00-4 4v1H5a1 1 0 00-.994.89l-1 9A1 1 0 004 18h12a1 1 0 00.994-1.11l-1-9A1 1 0 0015 7h-1V6a4 4 0 00-4-4zm2 5V6a2 2 0 10-4 0v1h4zm-6 3a1 1 0 112 0 1 1 0 01-2 0zm7-1a1 1 0 100 2 1 1 0 000-2z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<div class="text-sm text-gray-900 dark:text-white">Sales</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600 group"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mx-auto mb-1 w-7 h-7 text-gray-400 group-hover:text-gray-500 dark:text-gray-400 dark:group-hover:text-gray-400"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3zM6 8a2 2 0 11-4 0 2 2 0 014 0zM16 18v-3a5.972 5.972 0 00-.75-2.906A3.005 3.005 0 0119 15v3h-3zM4.75 12.094A5.973 5.973 0 004 15v3H1v-3a3 3 0 013.75-2.906z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<div class="text-sm text-gray-900 dark:text-white">Users</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600 group"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mx-auto mb-1 w-7 h-7 text-gray-400 group-hover:text-gray-500 dark:text-gray-400 dark:group-hover:text-gray-400"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M5 3a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2V5a2 2 0 00-2-2H5zm0 2h10v7h-2l-1 2H8l-1-2H5V5z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<div class="text-sm text-gray-900 dark:text-white">Inbox</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600 group"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mx-auto mb-1 w-7 h-7 text-gray-400 group-hover:text-gray-500 dark:text-gray-400 dark:group-hover:text-gray-400"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-6-3a2 2 0 11-4 0 2 2 0 014 0zm-2 4a5 5 0 00-4.546 2.916A5.986 5.986 0 0010 16a5.986 5.986 0 004.546-2.084A5 5 0 0010 11z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<div class="text-sm text-gray-900 dark:text-white">
|
||||||
|
Profile
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600 group"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mx-auto mb-1 w-7 h-7 text-gray-400 group-hover:text-gray-500 dark:text-gray-400 dark:group-hover:text-gray-400"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<div class="text-sm text-gray-900 dark:text-white">
|
||||||
|
Settings
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600 group"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mx-auto mb-1 w-7 h-7 text-gray-400 group-hover:text-gray-500 dark:text-gray-400 dark:group-hover:text-gray-400"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path d="M4 3a2 2 0 100 4h12a2 2 0 100-4H4z"></path>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M3 8h14v7a2 2 0 01-2 2H5a2 2 0 01-2-2V8zm5 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<div class="text-sm text-gray-900 dark:text-white">
|
||||||
|
Products
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600 group"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mx-auto mb-1 w-7 h-7 text-gray-400 group-hover:text-gray-500 dark:text-gray-400 dark:group-hover:text-gray-400"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M8.433 7.418c.155-.103.346-.196.567-.267v1.698a2.305 2.305 0 01-.567-.267C8.07 8.34 8 8.114 8 8c0-.114.07-.34.433-.582zM11 12.849v-1.698c.22.071.412.164.567.267.364.243.433.468.433.582 0 .114-.07.34-.433.582a2.305 2.305 0 01-.567.267z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-13a1 1 0 10-2 0v.092a4.535 4.535 0 00-1.676.662C6.602 6.234 6 7.009 6 8c0 .99.602 1.765 1.324 2.246.48.32 1.054.545 1.676.662v1.941c-.391-.127-.68-.317-.843-.504a1 1 0 10-1.51 1.31c.562.649 1.413 1.076 2.353 1.253V15a1 1 0 102 0v-.092a4.535 4.535 0 001.676-.662C13.398 13.766 14 12.991 14 12c0-.99-.602-1.765-1.324-2.246A4.535 4.535 0 0011 9.092V7.151c.391.127.68.317.843.504a1 1 0 101.511-1.31c-.563-.649-1.413-1.076-2.354-1.253V5z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<div class="text-sm text-gray-900 dark:text-white">
|
||||||
|
Pricing
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600 group"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mx-auto mb-1 w-7 h-7 text-gray-400 group-hover:text-gray-500 dark:text-gray-400 dark:group-hover:text-gray-400"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M5 2a2 2 0 00-2 2v14l3.5-2 3.5 2 3.5-2 3.5 2V4a2 2 0 00-2-2H5zm2.5 3a1.5 1.5 0 100 3 1.5 1.5 0 000-3zm6.207.293a1 1 0 00-1.414 0l-6 6a1 1 0 101.414 1.414l6-6a1 1 0 000-1.414zM12.5 10a1.5 1.5 0 100 3 1.5 1.5 0 000-3z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<div class="text-sm text-gray-900 dark:text-white">
|
||||||
|
Billing
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block p-4 text-center rounded-lg hover:bg-gray-100 dark:hover:bg-gray-600 group"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mx-auto mb-1 w-7 h-7 text-gray-400 group-hover:text-gray-500 dark:text-gray-400 dark:group-hover:text-gray-400"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<div class="text-sm text-gray-900 dark:text-white">
|
||||||
|
Logout
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
<button data-drawer-target="drawer-navigation" data-drawer-toggle="drawer-navigation" aria-controls="drawer-navigation" class="p-2 mr-2 text-gray-600 rounded-lg cursor-pointer md:hidden hover:text-gray-900 hover:bg-gray-100 focus:bg-gray-100 dark:focus:bg-gray-700 focus:ring-2 focus:ring-gray-100 dark:focus:ring-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
|
||||||
|
{% include 'icons/burger.html' %}
|
||||||
|
<span class="sr-only">Toggle sidebar</span>
|
||||||
|
</button>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!-- Кнопка переключения темы -->
|
||||||
|
<button id="theme-toggle" type="button" class="text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5">
|
||||||
|
<svg id="theme-toggle-dark-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path>
|
||||||
|
</svg>
|
||||||
|
<svg id="theme-toggle-light-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" fill-rule="evenodd" clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{% load static %}
|
||||||
|
<a href="{% url 'main' %}" class="flex items-center justify-between mr-4">
|
||||||
|
<img src="{% static 'img/logo.png' %}" class="mr-3 h-8" alt="VikiLeo.shop Logo" />
|
||||||
|
</a>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{% load static %}
|
||||||
|
<nav class="bg-white border-b border-gray-200 px-4 py-2.5 dark:bg-gray-800 dark:border-gray-700 fixed left-0 right-0 top-0 z-50">
|
||||||
|
<div class="flex flex-wrap justify-between items-center">
|
||||||
|
<div class="flex justify-start items-center">
|
||||||
|
{% include 'components/navbars/burger.html' %}
|
||||||
|
{% include 'components/navbars/logo.html' %}
|
||||||
|
{% include 'components/navbars/search.html' %}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center lg:order-2">
|
||||||
|
<!-- Search -->
|
||||||
|
{% include 'components/navbars/search_mini.html' %}
|
||||||
|
<!-- Dark mode -->
|
||||||
|
{% include 'components/navbars/dark_mode.html' %}
|
||||||
|
<!-- Notifications -->
|
||||||
|
{% include 'components/navbars/notifications.html' %}
|
||||||
|
<!-- Apps -->
|
||||||
|
{% include 'components/navbars/apps.html' %}
|
||||||
|
<!-- Profiles -->
|
||||||
|
{% include 'components/navbars/profiles.html' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
<button type="button" data-dropdown-toggle="notification-dropdown" class="p-2 mr-1 text-gray-500 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-white dark:hover:bg-gray-700 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600">
|
||||||
|
<span class="sr-only">View notifications</span>
|
||||||
|
{% include 'icons/notifications.html' %}
|
||||||
|
</button>
|
||||||
|
<div class="hidden overflow-hidden z-50 my-4 max-w-sm text-base list-none bg-white rounded divide-y divide-gray-100 shadow-lg dark:divide-gray-600 dark:bg-gray-700 rounded-xl" id="notification-dropdown">
|
||||||
|
<div class="block py-2 px-4 text-base font-medium text-center text-gray-700 bg-gray-50 dark:bg-gray-600 dark:text-gray-300">
|
||||||
|
Notifications
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a href="#" class="flex py-3 px-4 border-b hover:bg-gray-100 dark:hover:bg-gray-600 dark:border-gray-600">
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<img
|
||||||
|
class="w-11 h-11 rounded-full"
|
||||||
|
src="https://flowbite.s3.amazonaws.com/blocks/marketing-ui/avatars/bonnie-green.png"
|
||||||
|
alt="Bonnie Green avatar"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="flex absolute justify-center items-center ml-6 -mt-5 w-5 h-5 rounded-full border border-white bg-primary-700 dark:border-gray-700"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="w-3 h-3 text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M8.707 7.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l2-2a1 1 0 00-1.414-1.414L11 7.586V3a1 1 0 10-2 0v4.586l-.293-.293z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
d="M3 5a2 2 0 012-2h1a1 1 0 010 2H5v7h2l1 2h4l1-2h2V5h-1a1 1 0 110-2h1a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2V5z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pl-3 w-full">
|
||||||
|
<div
|
||||||
|
class="text-gray-500 font-normal text-sm mb-1.5 dark:text-gray-400"
|
||||||
|
>
|
||||||
|
New message from
|
||||||
|
<span class="font-semibold text-gray-900 dark:text-white"
|
||||||
|
>Bonnie Green</span
|
||||||
|
>: "Hey, what's up? All set for the presentation?"
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-xs font-medium text-primary-600 dark:text-primary-500"
|
||||||
|
>
|
||||||
|
a few moments ago
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex py-3 px-4 border-b hover:bg-gray-100 dark:hover:bg-gray-600 dark:border-gray-600"
|
||||||
|
>
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<img
|
||||||
|
class="w-11 h-11 rounded-full"
|
||||||
|
src="https://flowbite.s3.amazonaws.com/blocks/marketing-ui/avatars/jese-leos.png"
|
||||||
|
alt="Jese Leos avatar"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="flex absolute justify-center items-center ml-6 -mt-5 w-5 h-5 bg-gray-900 rounded-full border border-white dark:border-gray-700"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="w-3 h-3 text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M8 9a3 3 0 100-6 3 3 0 000 6zM8 11a6 6 0 016 6H2a6 6 0 016-6zM16 7a1 1 0 10-2 0v1h-1a1 1 0 100 2h1v1a1 1 0 102 0v-1h1a1 1 0 100-2h-1V7z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pl-3 w-full">
|
||||||
|
<div
|
||||||
|
class="text-gray-500 font-normal text-sm mb-1.5 dark:text-gray-400"
|
||||||
|
>
|
||||||
|
<span class="font-semibold text-gray-900 dark:text-white"
|
||||||
|
>Jese leos</span
|
||||||
|
>
|
||||||
|
and
|
||||||
|
<span class="font-medium text-gray-900 dark:text-white"
|
||||||
|
>5 others</span
|
||||||
|
>
|
||||||
|
started following you.
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-xs font-medium text-primary-600 dark:text-primary-500"
|
||||||
|
>
|
||||||
|
10 minutes ago
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex py-3 px-4 border-b hover:bg-gray-100 dark:hover:bg-gray-600 dark:border-gray-600"
|
||||||
|
>
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<img
|
||||||
|
class="w-11 h-11 rounded-full"
|
||||||
|
src="https://flowbite.s3.amazonaws.com/blocks/marketing-ui/avatars/joseph-mcfall.png"
|
||||||
|
alt="Joseph McFall avatar"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="flex absolute justify-center items-center ml-6 -mt-5 w-5 h-5 bg-red-600 rounded-full border border-white dark:border-gray-700"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="w-3 h-3 text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pl-3 w-full">
|
||||||
|
<div
|
||||||
|
class="text-gray-500 font-normal text-sm mb-1.5 dark:text-gray-400"
|
||||||
|
>
|
||||||
|
<span class="font-semibold text-gray-900 dark:text-white"
|
||||||
|
>Joseph Mcfall</span
|
||||||
|
>
|
||||||
|
and
|
||||||
|
<span class="font-medium text-gray-900 dark:text-white"
|
||||||
|
>141 others</span
|
||||||
|
>
|
||||||
|
love your story. See it and view more stories.
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-xs font-medium text-primary-600 dark:text-primary-500"
|
||||||
|
>
|
||||||
|
44 minutes ago
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex py-3 px-4 border-b hover:bg-gray-100 dark:hover:bg-gray-600 dark:border-gray-600"
|
||||||
|
>
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<img
|
||||||
|
class="w-11 h-11 rounded-full"
|
||||||
|
src="https://flowbite.s3.amazonaws.com/blocks/marketing-ui/avatars/roberta-casas.png"
|
||||||
|
alt="Roberta Casas image"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="flex absolute justify-center items-center ml-6 -mt-5 w-5 h-5 bg-green-400 rounded-full border border-white dark:border-gray-700"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="w-3 h-3 text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M18 13V5a2 2 0 00-2-2H4a2 2 0 00-2 2v8a2 2 0 002 2h3l3 3 3-3h3a2 2 0 002-2zM5 7a1 1 0 011-1h8a1 1 0 110 2H6a1 1 0 01-1-1zm1 3a1 1 0 100 2h3a1 1 0 100-2H6z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pl-3 w-full">
|
||||||
|
<div
|
||||||
|
class="text-gray-500 font-normal text-sm mb-1.5 dark:text-gray-400"
|
||||||
|
>
|
||||||
|
<span class="font-semibold text-gray-900 dark:text-white"
|
||||||
|
>Leslie Livingston</span
|
||||||
|
>
|
||||||
|
mentioned you in a comment:
|
||||||
|
<span
|
||||||
|
class="font-medium text-primary-600 dark:text-primary-500"
|
||||||
|
>@bonnie.green</span
|
||||||
|
>
|
||||||
|
what do you say?
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-xs font-medium text-primary-600 dark:text-primary-500"
|
||||||
|
>
|
||||||
|
1 hour ago
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex py-3 px-4 hover:bg-gray-100 dark:hover:bg-gray-600"
|
||||||
|
>
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
<img
|
||||||
|
class="w-11 h-11 rounded-full"
|
||||||
|
src="https://flowbite.s3.amazonaws.com/blocks/marketing-ui/avatars/robert-brown.png"
|
||||||
|
alt="Robert image"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="flex absolute justify-center items-center ml-6 -mt-5 w-5 h-5 bg-purple-500 rounded-full border border-white dark:border-gray-700"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="w-3 h-3 text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M2 6a2 2 0 012-2h6a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V6zM14.553 7.106A1 1 0 0014 8v4a1 1 0 00.553.894l2 1A1 1 0 0018 13V7a1 1 0 00-1.447-.894l-2 1z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pl-3 w-full">
|
||||||
|
<div
|
||||||
|
class="text-gray-500 font-normal text-sm mb-1.5 dark:text-gray-400"
|
||||||
|
>
|
||||||
|
<span class="font-semibold text-gray-900 dark:text-white"
|
||||||
|
>Robert Brown</span
|
||||||
|
>
|
||||||
|
posted a new video: Glassmorphism - learn how to implement
|
||||||
|
the new design trend.
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="text-xs font-medium text-primary-600 dark:text-primary-500"
|
||||||
|
>
|
||||||
|
3 hours ago
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block py-2 text-md font-medium text-center text-gray-900 bg-gray-50 hover:bg-gray-100 dark:bg-gray-600 dark:text-white dark:hover:underline"
|
||||||
|
>
|
||||||
|
<div class="inline-flex items-center">
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mr-2 w-4 h-4 text-gray-500 dark:text-gray-400"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z"></path>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
View all
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
<div class="text-end">
|
||||||
|
{% if not request.user.is_authenticated %}
|
||||||
|
<a class="btn btn-outline-light me-2" href="{% url 'account-login' %}">Авторизация</a>
|
||||||
|
<a class="btn btn-outline-warning" href="{% url 'account-registration' %}">Регистрация</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="navbar-text text-white">Войти в профиль: {% if request.user.is_authenticated %}
|
||||||
|
<a href="#" class="btn btn-white">
|
||||||
|
{{ request.accounts.username }}
|
||||||
|
</a>
|
||||||
|
{% else %} гоcть!{% endif %}
|
||||||
|
<a href="{% url 'logout' %}" class="btn btn-outline-light me-2">Выйти</a>
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="flex mx-3 text-sm bg-gray-800 rounded-full md:mr-0 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
|
||||||
|
id="user-menu-button"
|
||||||
|
aria-expanded="false"
|
||||||
|
data-dropdown-toggle="dropdown"
|
||||||
|
>
|
||||||
|
<span class="sr-only">Open user menu</span>
|
||||||
|
<img
|
||||||
|
class="w-8 h-8 rounded-full"
|
||||||
|
src="https://flowbite.s3.amazonaws.com/blocks/marketing-ui/avatars/michael-gough.png"
|
||||||
|
alt="user photo"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<!-- Dropdown menu -->
|
||||||
|
<div
|
||||||
|
class="hidden z-50 my-4 w-56 text-base list-none bg-white rounded divide-y divide-gray-100 shadow dark:bg-gray-700 dark:divide-gray-600 rounded-xl"
|
||||||
|
id="dropdown"
|
||||||
|
>
|
||||||
|
<div class="py-3 px-4">
|
||||||
|
<span
|
||||||
|
class="block text-sm font-semibold text-gray-900 dark:text-white"
|
||||||
|
>Neil Sims</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="block text-sm text-gray-900 truncate dark:text-white"
|
||||||
|
>name@flowbite.com</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<ul
|
||||||
|
class="py-1 text-gray-700 dark:text-gray-300"
|
||||||
|
aria-labelledby="dropdown"
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block py-2 px-4 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-400 dark:hover:text-white"
|
||||||
|
>My profile</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block py-2 px-4 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-400 dark:hover:text-white"
|
||||||
|
>Account settings</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul
|
||||||
|
class="py-1 text-gray-700 dark:text-gray-300"
|
||||||
|
aria-labelledby="dropdown"
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center py-2 px-4 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
|
||||||
|
><svg
|
||||||
|
class="mr-2 w-5 h-5 text-gray-400"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
My likes</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center py-2 px-4 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
|
||||||
|
><svg
|
||||||
|
class="mr-2 w-5 h-5 text-gray-400"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M7 3a1 1 0 000 2h6a1 1 0 100-2H7zM4 7a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1zM2 11a2 2 0 012-2h12a2 2 0 012 2v4a2 2 0 01-2 2H4a2 2 0 01-2-2v-4z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
Collections</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex justify-between items-center py-2 px-4 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
|
||||||
|
>
|
||||||
|
<span class="flex items-center">
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="mr-2 w-5 h-5 text-primary-600 dark:text-primary-500"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M12.395 2.553a1 1 0 00-1.45-.385c-.345.23-.614.558-.822.88-.214.33-.403.713-.57 1.116-.334.804-.614 1.768-.84 2.734a31.365 31.365 0 00-.613 3.58 2.64 2.64 0 01-.945-1.067c-.328-.68-.398-1.534-.398-2.654A1 1 0 005.05 6.05 6.981 6.981 0 003 11a7 7 0 1011.95-4.95c-.592-.591-.98-.985-1.348-1.467-.363-.476-.724-1.063-1.207-2.03zM12.12 15.12A3 3 0 017 13s.879.5 2.5.5c0-1 .5-4 1.25-4.5.5 1 .786 1.293 1.371 1.879A2.99 2.99 0 0113 13a2.99 2.99 0 01-.879 2.121z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
Pro version
|
||||||
|
</span>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="w-5 h-5 text-gray-400"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul
|
||||||
|
class="py-1 text-gray-700 dark:text-gray-300"
|
||||||
|
aria-labelledby="dropdown"
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block py-2 px-4 text-sm hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white"
|
||||||
|
>Sign out</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
<form action="#" method="GET" class="hidden md:block md:pl-2">
|
||||||
|
<label for="topbar-search" class="sr-only">Search</label>
|
||||||
|
<div class="relative md:w-64 md:w-96">
|
||||||
|
<div
|
||||||
|
class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none"
|
||||||
|
>
|
||||||
|
{% include 'icons/search.html' %}
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="email"
|
||||||
|
id="topbar-search"
|
||||||
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full pl-10 p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
|
||||||
|
placeholder="Search"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<button type="button" data-drawer-toggle="drawer-navigation" aria-controls="drawer-navigation" class="p-2 mr-1 text-gray-500 rounded-lg md:hidden hover:text-gray-900 hover:bg-gray-100 dark:text-gray-400 dark:hover:text-white dark:hover:bg-gray-700 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600">
|
||||||
|
<span class="sr-only">Toggle search</span>
|
||||||
|
<svg aria-hidden="true" class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
||||||
|
<path clip-rule="evenodd" fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
@@ -0,0 +1,578 @@
|
|||||||
|
<aside
|
||||||
|
class="fixed top-0 left-0 z-40 w-64 h-screen pt-14 transition-transform -translate-x-full bg-white border-r border-gray-200 md:translate-x-0 dark:bg-gray-800 dark:border-gray-700"
|
||||||
|
aria-label="Sidenav"
|
||||||
|
id="drawer-navigation"
|
||||||
|
>
|
||||||
|
<div class="overflow-y-auto py-5 px-3 h-full bg-white dark:bg-gray-800">
|
||||||
|
<form action="#" method="GET" class="md:hidden mb-2">
|
||||||
|
<label for="sidebar-search" class="sr-only">Search</label>
|
||||||
|
<div class="relative">
|
||||||
|
<div
|
||||||
|
class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="w-5 h-5 text-gray-500 dark:text-gray-400"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="search"
|
||||||
|
id="sidebar-search"
|
||||||
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full pl-10 p-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-primary-500 dark:focus:border-primary-500"
|
||||||
|
placeholder="Search"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<ul class="space-y-2">
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center p-2 text-base font-medium text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path d="M2 10a8 8 0 018-8v8h8a8 8 0 11-16 0z"></path>
|
||||||
|
<path d="M12 2.252A8.014 8.014 0 0117.748 8H12V2.252z"></path>
|
||||||
|
</svg>
|
||||||
|
<span class="ml-3">Overview</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="flex items-center p-2 w-full text-base font-medium text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||||
|
aria-controls="dropdown-pages"
|
||||||
|
data-collapse-toggle="dropdown-pages"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 group-hover:text-gray-900 dark:text-gray-400 dark:group-hover:text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<span class="flex-1 ml-3 text-left whitespace-nowrap"
|
||||||
|
>Pages</span
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="w-6 h-6"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<ul id="dropdown-pages" class="hidden py-2 space-y-2">
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center p-2 pl-11 w-full text-base font-medium text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||||
|
>Settings</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center p-2 pl-11 w-full text-base font-medium text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||||
|
>Kanban</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center p-2 pl-11 w-full text-base font-medium text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||||
|
>Calendar</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="flex items-center p-2 w-full text-base font-medium text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||||
|
aria-controls="dropdown-sales"
|
||||||
|
data-collapse-toggle="dropdown-sales"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 group-hover:text-gray-900 dark:text-gray-400 dark:group-hover:text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M10 2a4 4 0 00-4 4v1H5a1 1 0 00-.994.89l-1 9A1 1 0 004 18h12a1 1 0 00.994-1.11l-1-9A1 1 0 0015 7h-1V6a4 4 0 00-4-4zm2 5V6a2 2 0 10-4 0v1h4zm-6 3a1 1 0 112 0 1 1 0 01-2 0zm7-1a1 1 0 100 2 1 1 0 000-2z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<span class="flex-1 ml-3 text-left whitespace-nowrap"
|
||||||
|
>Sales</span
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="w-6 h-6"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<ul id="dropdown-sales" class="hidden py-2 space-y-2">
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center p-2 pl-11 w-full text-base font-medium text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||||
|
>Products</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center p-2 pl-11 w-full text-base font-medium text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||||
|
>Billing</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center p-2 pl-11 w-full text-base font-medium text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||||
|
>Invoice</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center p-2 text-base font-medium text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M8.707 7.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l2-2a1 1 0 00-1.414-1.414L11 7.586V3a1 1 0 10-2 0v4.586l-.293-.293z"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
d="M3 5a2 2 0 012-2h1a1 1 0 010 2H5v7h2l1 2h4l1-2h2V5h-1a1 1 0 110-2h1a2 2 0 012 2v10a2 2 0 01-2 2H5a2 2 0 01-2-2V5z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<span class="flex-1 ml-3 whitespace-nowrap">Messages</span>
|
||||||
|
<span
|
||||||
|
class="inline-flex justify-center items-center w-5 h-5 text-xs font-semibold rounded-full text-primary-800 bg-primary-100 dark:bg-primary-200 dark:text-primary-800"
|
||||||
|
>
|
||||||
|
4
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="flex items-center p-2 w-full text-base font-medium text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||||
|
aria-controls="dropdown-authentication"
|
||||||
|
data-collapse-toggle="dropdown-authentication"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 group-hover:text-gray-900 dark:text-gray-400 dark:group-hover:text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<span class="flex-1 ml-3 text-left whitespace-nowrap"
|
||||||
|
>Authentication</span
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="w-6 h-6"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<ul id="dropdown-authentication" class="hidden py-2 space-y-2">
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center p-2 pl-11 w-full text-base font-medium text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||||
|
>Sign In</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center p-2 pl-11 w-full text-base font-medium text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||||
|
>Sign Up</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center p-2 pl-11 w-full text-base font-medium text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
|
||||||
|
>Forgot Password</a
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul
|
||||||
|
class="pt-5 mt-5 space-y-2 border-t border-gray-200 dark:border-gray-700"
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center p-2 text-base font-medium text-gray-900 rounded-lg transition duration-75 hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-white group"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path d="M9 2a1 1 0 000 2h2a1 1 0 100-2H9z"></path>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M4 5a2 2 0 012-2 3 3 0 003 3h2a3 3 0 003-3 2 2 0 012 2v11a2 2 0 01-2 2H6a2 2 0 01-2-2V5zm3 4a1 1 0 000 2h.01a1 1 0 100-2H7zm3 0a1 1 0 000 2h3a1 1 0 100-2h-3zm-3 4a1 1 0 100 2h.01a1 1 0 100-2H7zm3 0a1 1 0 100 2h3a1 1 0 100-2h-3z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<span class="ml-3">Docs</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center p-2 text-base font-medium text-gray-900 rounded-lg transition duration-75 hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-white group"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M7 3a1 1 0 000 2h6a1 1 0 100-2H7zM4 7a1 1 0 011-1h10a1 1 0 110 2H5a1 1 0 01-1-1zM2 11a2 2 0 012-2h12a2 2 0 012 2v4a2 2 0 01-2 2H4a2 2 0 01-2-2v-4z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<span class="ml-3">Components</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="flex items-center p-2 text-base font-medium text-gray-900 rounded-lg transition duration-75 hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-white group"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="flex-shrink-0 w-6 h-6 text-gray-500 transition duration-75 dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-2 0c0 .993-.241 1.929-.668 2.754l-1.524-1.525a3.997 3.997 0 00.078-2.183l1.562-1.562C15.802 8.249 16 9.1 16 10zm-5.165 3.913l1.58 1.58A5.98 5.98 0 0110 16a5.976 5.976 0 01-2.516-.552l1.562-1.562a4.006 4.006 0 001.789.027zm-4.677-2.796a4.002 4.002 0 01-.041-2.08l-.08.08-1.53-1.533A5.98 5.98 0 004 10c0 .954.223 1.856.619 2.657l1.54-1.54zm1.088-6.45A5.974 5.974 0 0110 4c.954 0 1.856.223 2.657.619l-1.54 1.54a4.002 4.002 0 00-2.346.033L7.246 4.668zM12 10a2 2 0 11-4 0 2 2 0 014 0z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<span class="ml-3">Help</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="hidden absolute bottom-0 left-0 justify-center p-4 space-x-4 w-full lg:flex bg-white dark:bg-gray-800 z-20"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="inline-flex justify-center p-2 text-gray-500 rounded cursor-pointer dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-600"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="w-6 h-6"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M5 4a1 1 0 00-2 0v7.268a2 2 0 000 3.464V16a1 1 0 102 0v-1.268a2 2 0 000-3.464V4zM11 4a1 1 0 10-2 0v1.268a2 2 0 000 3.464V16a1 1 0 102 0V8.732a2 2 0 000-3.464V4zM16 3a1 1 0 011 1v7.268a2 2 0 010 3.464V16a1 1 0 11-2 0v-1.268a2 2 0 010-3.464V4a1 1 0 011-1z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
data-tooltip-target="tooltip-settings"
|
||||||
|
class="inline-flex justify-center p-2 text-gray-500 rounded cursor-pointer dark:text-gray-400 dark:hover:text-white hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="w-6 h-6"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
<div
|
||||||
|
id="tooltip-settings"
|
||||||
|
role="tooltip"
|
||||||
|
class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-gray-900 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip"
|
||||||
|
>
|
||||||
|
Settings page
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
data-dropdown-toggle="language-dropdown"
|
||||||
|
class="inline-flex justify-center p-2 text-gray-500 rounded cursor-pointer dark:hover:text-white dark:text-gray-400 hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="h-5 w-5 rounded-full mt-0.5"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
viewBox="0 0 3900 3900"
|
||||||
|
>
|
||||||
|
<path fill="#b22234" d="M0 0h7410v3900H0z" />
|
||||||
|
<path
|
||||||
|
d="M0 450h7410m0 600H0m0 600h7410m0 600H0m0 600h7410m0 600H0"
|
||||||
|
stroke="#fff"
|
||||||
|
stroke-width="300"
|
||||||
|
/>
|
||||||
|
<path fill="#3c3b6e" d="M0 0h2964v2100H0z" />
|
||||||
|
<g fill="#fff">
|
||||||
|
<g id="d">
|
||||||
|
<g id="c">
|
||||||
|
<g id="e">
|
||||||
|
<g id="b">
|
||||||
|
<path
|
||||||
|
id="a"
|
||||||
|
d="M247 90l70.534 217.082-184.66-134.164h228.253L176.466 307.082z"
|
||||||
|
/>
|
||||||
|
<use xlink:href="#a" y="420" />
|
||||||
|
<use xlink:href="#a" y="840" />
|
||||||
|
<use xlink:href="#a" y="1260" />
|
||||||
|
</g>
|
||||||
|
<use xlink:href="#a" y="1680" />
|
||||||
|
</g>
|
||||||
|
<use xlink:href="#b" x="247" y="210" />
|
||||||
|
</g>
|
||||||
|
<use xlink:href="#c" x="494" />
|
||||||
|
</g>
|
||||||
|
<use xlink:href="#d" x="988" />
|
||||||
|
<use xlink:href="#c" x="1976" />
|
||||||
|
<use xlink:href="#e" x="2470" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<!-- Dropdown -->
|
||||||
|
<div
|
||||||
|
class="hidden z-50 my-4 text-base list-none bg-white rounded divide-y divide-gray-100 shadow dark:bg-gray-700"
|
||||||
|
id="language-dropdown"
|
||||||
|
>
|
||||||
|
<ul class="py-1" role="none">
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600"
|
||||||
|
role="menuitem"
|
||||||
|
>
|
||||||
|
<div class="inline-flex items-center">
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="h-3.5 w-3.5 rounded-full mr-2"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
id="flag-icon-css-us"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
>
|
||||||
|
<g fill-rule="evenodd">
|
||||||
|
<g stroke-width="1pt">
|
||||||
|
<path
|
||||||
|
fill="#bd3d44"
|
||||||
|
d="M0 0h247v10H0zm0 20h247v10H0zm0 20h247v10H0zm0 20h247v10H0zm0 20h247v10H0zm0 20h247v10H0zm0 20h247v10H0z"
|
||||||
|
transform="scale(3.9385)"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#fff"
|
||||||
|
d="M0 10h247v10H0zm0 20h247v10H0zm0 20h247v10H0zm0 20h247v10H0zm0 20h247v10H0zm0 20h247v10H0z"
|
||||||
|
transform="scale(3.9385)"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
fill="#192f5d"
|
||||||
|
d="M0 0h98.8v70H0z"
|
||||||
|
transform="scale(3.9385)"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#fff"
|
||||||
|
d="M8.2 3l1 2.8H12L9.7 7.5l.9 2.7-2.4-1.7L6 10.2l.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7L74 8.5l-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 7.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm-74.1 7l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7H65zm16.4 0l1 2.8H86l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm-74 7l.8 2.8h3l-2.4 1.7.9 2.7-2.4-1.7L6 24.2l.9-2.7-2.4-1.7h3zm16.4 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 21.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm-74.1 7l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7H65zm16.4 0l1 2.8H86l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm-74 7l.8 2.8h3l-2.4 1.7.9 2.7-2.4-1.7L6 38.2l.9-2.7-2.4-1.7h3zm16.4 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 35.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm-74.1 7l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7H65zm16.4 0l1 2.8H86l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm-74 7l.8 2.8h3l-2.4 1.7.9 2.7-2.4-1.7L6 52.2l.9-2.7-2.4-1.7h3zm16.4 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 49.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm-74.1 7l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7H65zm16.4 0l1 2.8H86l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm-74 7l.8 2.8h3l-2.4 1.7.9 2.7-2.4-1.7L6 66.2l.9-2.7-2.4-1.7h3zm16.4 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8H45l-2.4 1.7 1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9zm16.4 0l1 2.8h2.8l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h3zm16.5 0l.9 2.8h2.9l-2.3 1.7.9 2.7-2.4-1.7-2.3 1.7.9-2.7-2.4-1.7h2.9zm16.5 0l.9 2.8h2.9L92 63.5l1 2.7-2.4-1.7-2.4 1.7 1-2.7-2.4-1.7h2.9z"
|
||||||
|
transform="scale(3.9385)"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
English (US)
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:text-white dark:hover:bg-gray-600"
|
||||||
|
role="menuitem"
|
||||||
|
>
|
||||||
|
<div class="inline-flex items-center">
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="h-3.5 w-3.5 rounded-full mr-2"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
id="flag-icon-css-de"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
>
|
||||||
|
<path fill="#ffce00" d="M0 341.3h512V512H0z" />
|
||||||
|
<path d="M0 0h512v170.7H0z" />
|
||||||
|
<path fill="#d00" d="M0 170.7h512v170.6H0z" />
|
||||||
|
</svg>
|
||||||
|
Deutsch
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:text-white dark:hover:bg-gray-600"
|
||||||
|
role="menuitem"
|
||||||
|
>
|
||||||
|
<div class="inline-flex items-center">
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="h-3.5 w-3.5 rounded-full mr-2"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
id="flag-icon-css-it"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
>
|
||||||
|
<g fill-rule="evenodd" stroke-width="1pt">
|
||||||
|
<path fill="#fff" d="M0 0h512v512H0z" />
|
||||||
|
<path fill="#009246" d="M0 0h170.7v512H0z" />
|
||||||
|
<path fill="#ce2b37" d="M341.3 0H512v512H341.3z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Italiano
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="block py-2 px-4 text-sm text-gray-700 hover:bg-gray-100 dark:hover:text-white dark:text-gray-300 dark:hover:bg-gray-600"
|
||||||
|
role="menuitem"
|
||||||
|
>
|
||||||
|
<div class="inline-flex items-center">
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="h-3.5 w-3.5 rounded-full mr-2"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
id="flag-icon-css-cn"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
>
|
||||||
|
<defs>
|
||||||
|
<path
|
||||||
|
id="a"
|
||||||
|
fill="#ffde00"
|
||||||
|
d="M1-.3L-.7.8 0-1 .6.8-1-.3z"
|
||||||
|
/>
|
||||||
|
</defs>
|
||||||
|
<path fill="#de2910" d="M0 0h512v512H0z" />
|
||||||
|
<use
|
||||||
|
width="30"
|
||||||
|
height="20"
|
||||||
|
transform="matrix(76.8 0 0 76.8 128 128)"
|
||||||
|
xlink:href="#a"
|
||||||
|
/>
|
||||||
|
<use
|
||||||
|
width="30"
|
||||||
|
height="20"
|
||||||
|
transform="rotate(-121 142.6 -47) scale(25.5827)"
|
||||||
|
xlink:href="#a"
|
||||||
|
/>
|
||||||
|
<use
|
||||||
|
width="30"
|
||||||
|
height="20"
|
||||||
|
transform="rotate(-98.1 198 -82) scale(25.6)"
|
||||||
|
xlink:href="#a"
|
||||||
|
/>
|
||||||
|
<use
|
||||||
|
width="30"
|
||||||
|
height="20"
|
||||||
|
transform="rotate(-74 272.4 -114) scale(25.6137)"
|
||||||
|
xlink:href="#a"
|
||||||
|
/>
|
||||||
|
<use
|
||||||
|
width="30"
|
||||||
|
height="20"
|
||||||
|
transform="matrix(16 -19.968 19.968 16 256 230.4)"
|
||||||
|
xlink:href="#a"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
中文 (繁體)
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{% load static %}
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowbite/2.3.0/flowbite.min.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
|
||||||
|
if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove('dark')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{% load static %}
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
<meta name="description" content="">
|
||||||
|
<meta name="author" content="@iTKeyS">
|
||||||
|
<meta name="generator" content="">
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<section>
|
||||||
|
</section>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<svg aria-hidden="true" class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h6a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
<svg aria-hidden="true" class="hidden w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" >
|
||||||
|
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" ></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 716 B |
@@ -0,0 +1,3 @@
|
|||||||
|
<svg aria-hidden="true" class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M10 2a6 6 0 00-6 6v3.586l-.707.707A1 1 0 004 14h12a1 1 0 00.707-1.707L16 11.586V8a6 6 0 00-6-6zM10 18a3 3 0 01-3-3h6a3 3 0 01-3 3z"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 275 B |
@@ -0,0 +1 @@
|
|||||||
|
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20"> <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/> </svg>
|
||||||
|
After Width: | Height: | Size: 290 B |
@@ -0,0 +1,58 @@
|
|||||||
|
{% extends "components/_base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<main class="p-4 md:ml-64 h-auto pt-20">
|
||||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-4">
|
||||||
|
<div
|
||||||
|
class="border-2 border-dashed border-gray-300 rounded-lg dark:border-gray-600 h-32 md:h-64"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-32 md:h-64"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-96 mb-4"
|
||||||
|
></div>
|
||||||
|
<div class="grid grid-cols-2 gap-4 mb-4">
|
||||||
|
<div
|
||||||
|
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-96 mb-4"
|
||||||
|
></div>
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<div
|
||||||
|
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="border-2 border-dashed rounded-lg border-gray-300 dark:border-gray-600 h-48 md:h-72"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock content %}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{% load static %}
|
||||||
|
<section>
|
||||||
|
</section>
|
||||||
Reference in New Issue
Block a user