This commit is contained in:
2024-05-10 09:38:31 +03:00
parent 9a5f895b93
commit 49cc44027d
81 changed files with 6093 additions and 28 deletions

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@ docs/_book
# TODO: where does this rule come from?
test/
node_modules/

View File

@@ -1,2 +1,65 @@
# 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
```

View File

@@ -1,3 +1,62 @@
import logging
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
"""

View File

@@ -3,4 +3,5 @@ from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'accounts'
name = 'config.accounts'
verbose_name = 'Управление пользователями'

View File

@@ -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,
},
),
]

View File

@@ -9,6 +9,7 @@ STATICFILES_DIR = os.path.join(BASE_DIR, "staticfiles")
STATIC_DIR = os.path.join(BASE_DIR, "static")
MEDIA_DIR = os.path.join(BASE_DIR, "mediafiles")
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+@'
DADATA_API_KEY = 'a6370792e9cfc9afdf5074c604eadf093ce9521f'
DEBUG = True

View File

@@ -14,6 +14,7 @@ from .local_settings import (
STATIC_DIR,
MEDIA_DIR,
LOGS_DIR,
COMPRESS_ROOT,
CORS_ORIGIN_WHITELIST,
CSRF_TRUSTED_ORIGINS
)
@@ -57,13 +58,14 @@ THIRD_PARTY_APPS = [
'autoslug',
'localflavor',
'simple_history',
'compressor',
]
# Внутренние приложения
LOCAL_APPS = [
'config.accounts.apps.AccountsConfig',
#'system.media.apps.MediaConfig',
#'system.comments.apps.CommentsConfig',
#'main.apps.MainConfig',
'system.media.apps.MediaConfig',
'system.comments.apps.CommentsConfig',
'main.apps.MainConfig',
#'articles.apps.ArticlesConfig',
#'todos.apps.TodosConfig',
]
@@ -167,4 +169,7 @@ CSRF_TRUSTED_ORIGINS = CSRF_TRUSTED_ORIGINS
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
# При выходе из учётной записи вас направит на данный url
LOGOUT_REDIRECT_URL = "main"
LOGOUT_REDIRECT_URL = "main"
COMPRESS_ROOT = COMPRESS_ROOT
COMPRESS_ENABLED = True
STATICFILES_FINDERS = ('compressor.finders.CompressorFinder',)

View File

@@ -5,9 +5,11 @@ from django.conf.urls.static import static
from rest_framework import permissions
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
#from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView, TokenVerifyView
# Вставки из апсов приложения
# Машиночитаемая [схема] описывает, какие ресурсы доступны через API
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView, TokenVerifyView
"""
Вставки из апсов приложения
Машиночитаемая [схема] описывает, какие ресурсы доступны через API
"""
schema_view = get_schema_view(
openapi.Info(
title="vikileo.shop API",
@@ -17,35 +19,39 @@ schema_view = get_schema_view(
public=True,
permission_classes=(permissions.AllowAny,),
)
# Префикс для понтов, можно без него но сним проще ориентироваться что должно иди на фронт, а что для тестов.
""" Префикс для понтов, можно без него но сним проще ориентироваться что должно иди на фронт, а что для тестов. """
api_prefix = 'api'
# Кортежи адресов для системных приложений
""" Кортежи адресов для системных приложений """
urlpatterns_system = [
path('admin/', admin.site.urls),# Адрес админ панели
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-redoc'), # API сборник адресов
path('admin/', admin.site.urls), # Адрес админ панели
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 = [
path('', include('main.urls')), # Адрес главной странице (заглушка)
path(f'{api_prefix}/', include('config.accounts.urls')), # Адрес управление учётными данными
#path(f'{api_prefix}/', include('system.comments.urls')), # Адрес комментариев от пользователей
#path(f'{api_prefix}/', include('todos.urls')), # Адрес статей
#path(f'{api_prefix}/', include('articles.urls')), # Адрес стадей для пользователей
path(f'{api_prefix}/', include('system.comments.urls')), # Адрес комментариев от пользователей
path('', include('main.urls')), # Адрес главной странице (заглушка)
path('', include('config.accounts.urls')), # Адрес управление учётными данными
]
# Кортежи адресов для тестовых приложений
""" Кортежи адресов для тестовых приложений """
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}/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
# Обслуживание файлов, загруженных пользователем во время разработки
""" Обслуживание файлов, загруженных пользователем во время разработки """
if settings.DEBUG:
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
db.sqlite3 Normal file

Binary file not shown.

0
main/__init__.py Normal file
View File

3
main/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
main/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class MainConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'main'

View File

View File

@@ -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'),
),
]

View File

@@ -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',
),
]

View File

1
main/models.py Normal file
View File

@@ -0,0 +1 @@
from django.db import models

3
main/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

13
main/urls.py Normal file
View File

@@ -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)),
]

14
main/views.py Normal file
View File

@@ -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)

1358
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

15
package.json Normal file
View File

@@ -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"
}
}

1
static/staticfiles.json Normal file
View File

@@ -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

BIN
staticfiles/img/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
staticfiles/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

View File

@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

2461
staticfiles/src/output.css Normal file

File diff suppressed because it is too large Load Diff

0
system/__init__.py Normal file
View File

View File

5
system/comments/admin.py Normal file
View File

@@ -0,0 +1,5 @@
from django.contrib import admin
from simple_history.admin import SimpleHistoryAdmin
from .models import *
admin.site.register(Comment, SimpleHistoryAdmin)

7
system/comments/apps.py Normal file
View File

@@ -0,0 +1,7 @@
from django.apps import AppConfig
class CommentsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'system.comments'
verbose_name = 'Комментарии'

View File

@@ -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'],
},
),
]

View File

17
system/comments/models.py Normal file
View File

@@ -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']

View File

@@ -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

8
system/comments/urls.py Normal file
View File

@@ -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'),
]

85
system/comments/views.py Normal file
View File

@@ -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
system/media/__init__.py Normal file
View File

7
system/media/admin.py Normal file
View File

@@ -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)

7
system/media/apps.py Normal file
View File

@@ -0,0 +1,7 @@
from django.apps import AppConfig
class MediaConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'system.media'
verbose_name = 'Файловое хранилище'

View File

@@ -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),
),
]

View File

115
system/media/models.py Normal file
View File

@@ -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}'

3
system/media/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -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 МБ.'))

3
system/media/views.py Normal file
View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

31
tailwind.config.js Normal file
View File

@@ -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')
]
}

View File

@@ -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>

View File

@@ -0,0 +1,2 @@
{% load static %}
<link rel="icon" type="image/x-icon" href="{% static 'assets/favicon.ico' %}" />

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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="">

2
templates/headers.html Normal file
View File

@@ -0,0 +1,2 @@
<section>
</section>

View File

@@ -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

View File

@@ -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

View File

@@ -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

58
templates/index.html Normal file
View File

@@ -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 %}

3
templates/welcome.html Normal file
View File

@@ -0,0 +1,3 @@
{% load static %}
<section>
</section>