updated navbar, reformed project structure

This commit is contained in:
Alexander Karpov 2023-02-16 14:00:16 +03:00
parent 727f6a6f9e
commit 095a0d02f8
26 changed files with 230 additions and 169 deletions

View File

@ -38,13 +38,14 @@ def __str__(self):
class ProviderBlock(BaseBlock):
TYPE = "Provider"
parent = None
children: list[BaseBlock]
class Meta:
abstract = True
class BaseStorage(PolymorphicModel):
id: uuid.uuid4 = models.UUIDField(
id: uuid.UUID = models.UUIDField(
primary_key=True, default=uuid.uuid4, editable=False
)

View File

@ -1,7 +1,11 @@
from django.db import models
from akarpov.pipeliner.models import BaseBlock
class Workspace(models.Model):
blocks: list[BaseBlock]
name = models.CharField(max_length=50, blank=True)
slug = models.SlugField(max_length=8)

View File

@ -1,10 +0,0 @@
from django.urls import path
from akarpov.shortener.views import link_detail_view, short_link_create_view
app_name = "shortener"
urlpatterns = [
path("", short_link_create_view, name="create"),
path("<str:slug>", link_detail_view, name="view"),
]

View File

@ -31,6 +31,20 @@ p {
margin: 0px;
}
.profile {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 100%;
}
.profile-wrapper {
position: relative;
width: 100%;
height: 100vh;
}
.profile-card {
background: #E0E0E0;
width: 56px;
@ -115,10 +129,9 @@ p {
width: 218px;
display: inline-block;
float: right;
margin: 0px;
padding: 15px 20px;
background: #FFFFFF;
margin-top: 50px;
margin: 50px 0 0;
text-align: center;
opacity: 0;
-webkit-box-sizing: border-box;
@ -492,3 +505,29 @@ p {
font-weight: bolder;
margin-right: 5px;
}
.sidebar {
max-height: 15%;
}
@media (min-width: 576px) {
.h-sm-100 {
height: 100%;
}
.sidebar {
max-height: 100%;
}
}
.blog-card {
margin-bottom: -99999px;
padding: 10px 10px 99999px;
}
.nav-active::before {
content: '→';
}
.nav-active {
color: white;
}

View File

@ -12,48 +12,25 @@
{% get_providers as socialaccount_providers %}
{% if socialaccount_providers %}
<p>
{% translate "Please sign in with one of your existing third party accounts:" %}
{% if ACCOUNT_ALLOW_REGISTRATION %}
{% blocktranslate trimmed %}
Or, <a href="{{ signup_url }}">sign up</a>
for a {{ site_name }} account and sign in below:
{% endblocktranslate %}
{% endif %}
</p>
<div class="socialaccount_ballot">
<ul class="socialaccount_providers">
{% include "socialaccount/snippets/provider_list.html" with process="login" %}
</ul>
<div class="login-or">{% translate "or" %}</div>
</div>
{% include "socialaccount/snippets/login_extra.html" %}
{% else %}
{% if ACCOUNT_ALLOW_REGISTRATION %}
<p>
{% blocktranslate trimmed %}
If you have not created an account yet, then please
<a href="{{ signup_url }}">sign up</a> first.
{% endblocktranslate %}
</p>
{% endif %}
{% endif %}
<form class="login" method="POST" action="{% url 'account_login' %}">
{% csrf_token %}
{{ form|crispy }}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
{% endif %}
<button class="primaryAction btn btn-primary me-3" type="submit">{% translate "Sign In" %}</button>
<a class="button secondaryAction" href="{% url 'account_reset_password' %}">{% translate "Forgot Password?" %}</a>
<button class="primaryAction btn btn-primary" type="submit">{% translate "Sign In" %}</button>
</form>
<p class="mt-2 mb-1">Alternative: </p>
{% if socialaccount_providers %}
<div class="socialaccount_ballot">
<ul class="socialaccount_providers">
{% include "socialaccount/snippets/provider_list.html" with process="login" %}
</ul>
</div>
{% include "socialaccount/snippets/login_extra.html" %}
{% endif %}
{% endblock %}

View File

@ -11,7 +11,6 @@
<link rel="icon" href="{% static 'images/favicons/favicon.ico' %}">
{% block css %}
<!-- Latest compiled and minified Bootstrap CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.2/font/bootstrap-icons.css">
@ -20,6 +19,7 @@
<!-- This file stores project-specific CSS -->
<link href="{% static 'css/project.css' %}" rel="stylesheet">
{% block css %}
{% endblock %}
<!-- Le javascript
================================================== -->
@ -37,56 +37,87 @@
</head>
<body>
<div class="container-fluid">
<div class="row vh-100 overflow-auto">
<div class="sidebar col-12 col-sm-3 col-xl-1 px-sm-2 px-0 bg-dark d-flex sticky-top">
<div class="d-flex flex-sm-column flex-row flex-grow-1 align-items-center align-items-sm-start px-3 pt-2 text-white">
<a href="/" class="d-flex align-items-center pb-sm-3 mb-md-0 me-md-auto text-white text-wrap text-decoration-none">
<span class="fs-5">A<span class="d-none d-sm-inline">karpov</span></span>
</a>
<ul class="nav nav-pills flex-sm-column flex-row flex-nowrap flex-shrink-1 flex-sm-grow-0 flex-grow-1 mb-sm-auto mb-0 justify-content-center align-items-center align-items-sm-start" id="menu">
{% if request.user.is_superuser %}
<li>
<a href="/admin/" class="text-muted nav-link px-sm-0 px-2">
<i class="fs-5 bi-speedometer2"></i><span class="ms-1 d-none d-sm-inline">Admin</span> </a>
</li>
{% endif %}
<li>
<a href="#" class="text-muted nav-link px-sm-0 px-2">
<i class="fs-5 bi-table"></i><span class="ms-1 d-none d-sm-inline">Orders</span></a>
</li>
<li class="dropdown">
<a href="#" class="text-muted nav-link dropdown-toggle px-sm-0 px-1" id="dropdown" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fs-5 bi-terminal-fill"></i><span class="ms-1 d-none d-sm-inline">Apps</span>
</a>
<ul class="dropdown-menu dropdown-menu-dark text-small shadow" aria-labelledby="dropdown">
<li><a class="dropdown-item {% active_link 'tools:qr:create' %}" href="{% url 'tools:qr:create' %}">QR generator</a></li>
<li><a class="dropdown-item {% active_link 'tools:shortener:create' %}" href="{% url 'tools:shortener:create' %}">URL shortcuter</a></li>
</ul>
</li>
</ul>
<div class="mb-1">
<nav class="navbar navbar-expand-md navbar-light bg-light">
<div class="container-fluid">
<button class="navbar-toggler navbar-toggler-right" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand" href="{% url 'blog:post_list' %}">akarpov</a>
<div class="dropdown py-sm-4 mt-sm-auto ms-auto ms-sm-0 flex-shrink-1">
{% if request.user.is_authenticated %}
<a href="#" class="d-flex align-items-center text-white text-decoration-none dropdown-toggle" id="dropdownUser1" data-bs-toggle="dropdown" aria-expanded="false">
{% if request.user.image_cropped %}<img src="{{ request.user.image_cropped.url }}" alt="hugenerd" width="28" height="28" class="rounded-circle">{% endif %}
<span class="d-none d-sm-inline mx-1">{{ request.user.username }}</span>
</a>
<ul class="dropdown-menu dropdown-menu-dark text-small shadow" aria-labelledby="dropdownUser1">
<li><a class="dropdown-item {% active_link 'users:update' %}" href="{% url 'users:update' %}">Settings</a></li>
<li><a class="dropdown-item {% active_link 'users:detail' request.user.username %}" href="{% url 'users:detail' request.user.username %}">Profile</a></li>
<li>
<hr class="dropdown-divider">
</li>
<li><a class="dropdown-item" href="{% url 'account_logout' %}">Sign out</a></li>
</ul>
{% else %}
<ul class="nav nav-pills flex-sm-column flex-row flex-nowrap flex-shrink-1 flex-sm-grow-0 flex-grow-1 py-sm-4 mt-sm-auto ms-auto ms-sm-0" id="menu">
{% if ACCOUNT_ALLOW_REGISTRATION %}
<li>
<a href="{% url 'account_signup' %}" class="text-muted nav-link px-sm-0 px-2 {% active_link 'account_signup' %}">
<i class="fs-5 bi-person"></i><span class="ms-1 d-none d-sm-inline">Sign up</span> </a>
</li>
{% endif %}
<li>
<a href="{% url 'account_login' %}" class="nav-link px-sm-0 px-2 {% active_link 'account_login' %} text-muted">
<i class="fs-5 bi-box-arrow-in-right"></i><span class="ms-2 d-none d-sm-inline"> Log in</span> </a>
</li>
</ul>
{% endif %}
</div>
</div>
</div>
<div class="col d-flex flex-column h-sm-100">
<main class="row overflow-aut px-lg-4">
<div class="col pt-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-dismissible {% if message.tags %}alert-{{ message.tags }}{% endif %}">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
{% if request.user.is_authenticated %}
<li class="nav-item">
{# URL provided by django-allauth/account/urls.py #}
<a class="nav-link {% active_link 'users:detail' %}" href="{% url 'users:detail' request.user.username %}">{% translate "My Profile" %}</a>
</li>
<li class="nav-item">
{# URL provided by django-allauth/account/urls.py #}
<a class="nav-link {% active_link 'account_logout' %}" href="{% url 'account_logout' %}">{% if request.user.image_cropped %}<img class="rounded" width="20" src="{{ request.user.image_cropped.url }}" alt=""> {% endif %}{% translate "Sign Out" %}</a>
</li>
{% else %}
{% if ACCOUNT_ALLOW_REGISTRATION %}
<li class="nav-item">
{# URL provided by django-allauth/account/urls.py #}
<a id="sign-up-link" class="nav-link {% active_link 'account_signup' %}" href="{% url 'account_signup' %}">{% translate "Sign Up" %}</a>
</li>
{% endif %}
<li class="nav-item">
{# URL provided by django-allauth/account/urls.py #}
<a id="log-in-link" class="nav-link {% active_link 'account_login' %}" href="{% url 'account_login' %}">{% translate "Sign In" %}</a>
</li>
{% endif %}
</ul>
</div>
{% block content %}
{% endblock content %}
</div>
</main>
<footer class="row bg-light py-1 mt-auto text-center">
<div class="col"> Writen by sanspie, find source code <a href="https://github.com/Alexander-D-Karpov/akarpov">here</a> </div>
</footer>
</div>
</div>
</nav>
</div>
<div class="container">
{% if messages %}
{% for message in messages %}
<div class="alert alert-dismissible {% if message.tags %}alert-{{ message.tags }}{% endif %}">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
{% block content %}
{% endblock content %}
</div>
{% block modal %}{% endblock modal %}

View File

@ -9,8 +9,8 @@
{% endif %}
<div class="row mb-2">
{% for post in post_list %}
<div class="col-md-6">
<div class="row g-0 border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">
<div class="col-md-6 col-xl-12">
<div class="row g-0 border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative blog-cards">
<div class="col p-4 d-flex flex-column position-static">
<strong style="color: {{ post.h_tag.color }}" class="d-inline-block mb-2">{{ post.h_tag.name }}</strong>
<h3 class="mb-0">{{ post.title }}</h3>
@ -22,7 +22,7 @@
<i class="bi bi-chat ms-3"></i> {{ post.comment_count }}</p>
</div>
{% if post.image_cropped %}
<img class="col-auto d-none d-lg-block img-fluid" style="object-fit: cover" src="{{ post.image_cropped.url }}" alt="">
<img class="col-auto d-none d-lg-block img-fluid col-xl-3" style="object-fit: cover" src="{{ post.image_cropped.url }}" alt="">
{% endif %}
</div>
</div>

View File

@ -4,54 +4,58 @@
{% block title %}User: {{ object.username }}{% endblock %}
{% block content %}
<aside class="profile-card">
<header>
<!-- heres the avatar -->
<a target="_blank" href="#">
{% if object.image_cropped %}<img style="object-fit: cover; width: 125px; height: 125px" src="{{ object.image_cropped.url }}" class="hoverZoomLink">{% endif %}
</a>
<div class="profile-wrapper">
<div class="profile">
<aside class="profile-card">
<header>
<!-- heres the avatar -->
<a target="_blank" href="#">
{% if object.image_cropped %}<img style="object-fit: cover; width: 125px; height: 125px" src="{{ object.image_cropped.url }}" class="hoverZoomLink">{% endif %}
</a>
<!-- the username -->
<h1>{{ object.username }}</h1>
<!-- the username -->
<h1>{{ object.username }}</h1>
<!-- and role or location -->
<h2>{% if object.name %}{{ object.name }}{% endif %}</h2>
<!-- and role or location -->
<h2>{% if object.name %}{{ object.name }}{% endif %}</h2>
</header>
</header>
<!-- bit of a bio; who are you? -->
<div class="profile-bio">
<!-- bit of a bio; who are you? -->
<div class="profile-bio">
<p>
{{ object.about }}
</p>
<p>
{{ object.about }}
</p>
</div>
<ul class="profile-social-links">
{% if object == request.user %}
<li>
<a href="{% url 'users:update' %}">
<i class="bi bi-sliders"></i>
</a>
</li>
<li>
<a href="{% url 'account_email' %}">
<i class="bi bi-envelope"></i>
</a>
</li>
<li>
<a href="{% url 'account_reset_password' %}">
<i class="bi bi-key"></i>
</a>
</li>
<li>
<a href="{% url 'socialaccount_connections' %}">
<i class="bi bi-person-add"></i>
</a>
</li>
{% endif %}
</ul>
<!-- End Action buttons -->
</aside>
</div>
</div>
<ul class="profile-social-links">
{% if object == request.user %}
<li>
<a href="{% url 'users:update' %}">
<i class="bi bi-sliders"></i>
</a>
</li>
<li>
<a href="{% url 'account_email' %}">
<i class="bi bi-envelope"></i>
</a>
</li>
<li>
<a href="{% url 'account_reset_password' %}">
<i class="bi bi-key"></i>
</a>
</li>
<li>
<a href="{% url 'socialaccount_connections' %}">
<i class="bi bi-person-add"></i>
</a>
</li>
{% endif %}
</ul>
<!-- End Action buttons -->
</aside>
{% endblock content %}

View File

@ -1,5 +1,5 @@
from django.contrib import admin
from akarpov.shortener.models import Link
from akarpov.tools.shortener.models import Link
admin.site.register(Link)

View File

@ -3,7 +3,7 @@
class ShortenerConfig(AppConfig):
name = "akarpov.shortener"
name = "akarpov.tools.shortener"
verbose_name = _("Link shortener")
def ready(self):

View File

@ -1,7 +1,7 @@
from django import forms
from django.core.validators import URLValidator
from akarpov.shortener.models import Link
from akarpov.tools.shortener.models import Link
class LinkForm(forms.ModelForm):

View File

@ -15,7 +15,7 @@ class Link(TimeStampedModel):
viewed = models.IntegerField(default=0)
def get_absolute_url(self):
return reverse("shortener:view", kwargs={"slug": self.slug})
return reverse("short_url", kwargs={"slug": self.slug})
def __str__(self):
return f"link to {self.source}"

View File

@ -1,21 +1,21 @@
from django.conf import settings
from akarpov.shortener.models import Link
from akarpov.tools.shortener.models import Link
from akarpov.utils.generators import generate_charset, get_pk_from_uuid, get_str_uuid
lenght = settings.SHORTENER_SLUG_LENGTH
length = settings.SHORTENER_SLUG_LENGTH
def generate_slug(pk: int) -> str:
if settings.SHORTENER_ADD_SLUG:
slug = generate_charset(lenght)
slug = generate_charset(length)
return slug + get_str_uuid(pk)
return get_str_uuid(pk)
def get_link_from_slug(slug: str, check_whole=False) -> Link | bool:
if settings.SHORTENER_ADD_SLUG and not check_whole:
payload = slug[lenght:]
payload = slug[length:]
pk = get_pk_from_uuid(payload)
try:
return Link.objects.get(pk=pk)

View File

@ -1,8 +1,8 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from akarpov.shortener.models import Link
from akarpov.shortener.services import generate_slug
from akarpov.tools.shortener.models import Link
from akarpov.tools.shortener.services import generate_slug
@receiver(post_save, sender=Link)

View File

@ -0,0 +1,9 @@
from django.urls import path
from akarpov.tools.shortener.views import short_link_create_view
app_name = "shortener"
urlpatterns = [
path("", short_link_create_view, name="create"),
]

View File

@ -1,9 +1,9 @@
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.views.generic import CreateView, DetailView
from akarpov.shortener.forms import LinkForm
from akarpov.shortener.models import Link
from akarpov.shortener.services import get_link_from_slug
from akarpov.tools.shortener.forms import LinkForm
from akarpov.tools.shortener.models import Link
from akarpov.tools.shortener.services import get_link_from_slug
class ShortLinkCreateView(CreateView):

View File

@ -1,4 +1,7 @@
from django.urls import include, path
app_name = "tools"
urlpatterns = [path("qr/", include("akarpov.tools.qr.urls", namespace="qr"))]
urlpatterns = [
path("qr/", include("akarpov.tools.qr.urls", namespace="qr")),
path("shortener/", include("akarpov.tools.shortener.urls", namespace="shortener")),
]

View File

@ -141,8 +141,8 @@
"akarpov.users",
"akarpov.blog",
"akarpov.files",
"akarpov.shortener",
"akarpov.pipeliner",
"akarpov.tools.shortener",
"akarpov.tools.qr",
]
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
@ -519,4 +519,4 @@
# ACTIVE_LINK
# ------------------------------------------------------------------------------
ACTIVE_LINK_CSS_CLASS = "active"
ACTIVE_LINK_CSS_CLASS = "nav-active"

View File

@ -10,6 +10,8 @@
SpectacularSwaggerView,
)
from akarpov.tools.shortener.views import link_detail_view
urlpatterns = [
path("home", TemplateView.as_view(template_name="pages/home.html"), name="home"),
path(
@ -22,10 +24,10 @@
# User management
path("users/", include("akarpov.users.urls", namespace="users")),
path("tools/", include("akarpov.tools.urls", namespace="tools")),
path("shortener/", include("akarpov.shortener.urls", namespace="shortener")),
path("ckeditor/", include("ckeditor_uploader.urls")),
path("accounts/", include("allauth.urls")),
path("", include("akarpov.blog.urls", namespace="blog")),
path("s/<str:slug>", link_detail_view, name="short_url"),
# Your stuff: custom urls includes go here
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

17
poetry.lock generated
View File

@ -1275,27 +1275,30 @@ celery = ["celery (>=5.1)"]
[[package]]
name = "django-stubs"
version = "1.13.1"
version = "1.14.0"
description = "Mypy stubs for Django"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "django-stubs-1.13.1.tar.gz", hash = "sha256:bcc618ba353dabc540d982b9dac1d5a1921652f8fc2a13653d545a57d5e3cc0f"},
{file = "django_stubs-1.13.1-py3-none-any.whl", hash = "sha256:fbf2ee6a4bce76c3eb5f6707ccadb4cf1c2f1ec485e8c44701ca8de2d0a5df18"},
{file = "django-stubs-1.14.0.tar.gz", hash = "sha256:d53bcd4975a54ca5c9abbbd33b61f40d44191971018f2ea54f73b0a6a99e1a8b"},
{file = "django_stubs-1.14.0-py3-none-any.whl", hash = "sha256:b081d64d923171f79d4e57899b0980da847e4046b91166e3658a6151645a36c5"},
]
[package.dependencies]
django = "*"
django-stubs-ext = ">=0.7.0"
mypy = ">=0.980"
mypy = [
{version = ">=0.980"},
{version = ">=0.991,<1.0", optional = true, markers = "extra == \"compatible-mypy\""},
]
tomli = "*"
types-pytz = "*"
types-PyYAML = "*"
typing-extensions = "*"
[package.extras]
compatible-mypy = ["mypy (>=0.980,<0.990)"]
compatible-mypy = ["mypy (>=0.991,<1.0)"]
[[package]]
name = "django-stubs-ext"
@ -2046,7 +2049,6 @@ category = "main"
optional = false
python-versions = "*"
files = [
{file = "livereload-2.6.3-py2.py3-none-any.whl", hash = "sha256:ad4ac6f53b2d62bb6ce1a5e6e96f1f00976a32348afedcb4b6d68df2a1d346e4"},
{file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"},
]
@ -3663,6 +3665,7 @@ category = "main"
optional = false
python-versions = "*"
files = [
{file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"},
{file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"},
]
@ -3777,4 +3780,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = "^3.11"
content-hash = "392fed32dc5e397e6fd56683185d571a8bd93faaf71d54bba6479d1dce4dab84"
content-hash = "fe807601a32feb33caa293b0a8639c33ccf436b02efac27a56783dafb95f1942"

View File

@ -37,7 +37,7 @@ werkzeug = {extras = ["watchdog"], version = "^2.2.2"}
ipdb = "^0.13.11"
watchfiles = "^0.18.1"
mypy = "^0.991"
django-stubs = "^1.13.1"
django-stubs = {extras = ["compatible-mypy"], version = "^1.14.0"}
pytest = "^7.2.0"
pytest-sugar = "^0.9.6"
djangorestframework-stubs = {extras = ["compatible-mypy"], version = "^1.8.0"}

View File

@ -28,8 +28,6 @@ plugins = mypy_django_plugin.main, mypy_drf_plugin.main
[mypy.plugins.django-stubs]
django_settings_module = config.settings.test
# https://github.com/typeddjango/django-stubs/issues/1158
django-manager-missing = False
[mypy-*.migrations.*]
# Django migrations should not produce any errors: