Update login template.
@@ -1,128 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
* Specific elements. *
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
html {
|
|
||||||
height:100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
height: 100%;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#id_username {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#id_password {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:link {
|
|
||||||
color: #ffff00;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:visited {
|
|
||||||
color: #009CD9;
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************
|
|
||||||
* Containers. *
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
.centered-container {
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
margin-bottom: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background {
|
|
||||||
background-color: darkslategray;
|
|
||||||
color: lightgray;
|
|
||||||
}
|
|
||||||
|
|
||||||
.image-container {
|
|
||||||
max-width: 900px;
|
|
||||||
max-height: 600px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fc {
|
|
||||||
width: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.h90 {
|
|
||||||
height: 90%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.full-width {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fixed-width {
|
|
||||||
width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************
|
|
||||||
* Content. *
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
.image {
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 600px;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mauto {
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mauto-top {
|
|
||||||
margin: 200px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navigation-icon {
|
|
||||||
width: 100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.small-nav-icon {
|
|
||||||
width: 3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mb-2 {
|
|
||||||
margin-bottom: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mr-2 {
|
|
||||||
margin-right: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ml-4 {
|
|
||||||
margin-left: 4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ml-2 {
|
|
||||||
margin-left: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clear-btn {
|
|
||||||
border: none;
|
|
||||||
background-color: #00000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box {
|
|
||||||
height: 2.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.float-right {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************
|
|
||||||
* Grid. *
|
|
||||||
****************************************************************************/
|
|
||||||
|
|
||||||
.column {
|
|
||||||
text-align: center;
|
|
||||||
border: black 1px solid;
|
|
||||||
}
|
|
||||||
@@ -378,7 +378,16 @@ body.theme-dark .small.text-muted {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 991.98px) {
|
.login-form {
|
||||||
|
background: var(--panel-strong);
|
||||||
|
border-radius: 0 1rem 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: var(--bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
media (max-width: 991.98px) {
|
||||||
.app-shell {
|
.app-shell {
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
@@ -405,6 +414,12 @@ body.theme-dark .small.text-muted {
|
|||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.login-form {
|
||||||
|
background: var(--panel-strong);
|
||||||
|
border-radius: 1rem 1rem 1rem 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 575.98px) {
|
@media (max-width: 575.98px) {
|
||||||
/* On narrow viewports keep the fixed 128px thumbnails but allow the
|
/* On narrow viewports keep the fixed 128px thumbnails but allow the
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 22 KiB |
BIN
viewer/static/imgs/login.jpg
Normal file
|
After Width: | Height: | Size: 604 KiB |
BIN
viewer/static/imgs/login.png
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
viewer/static/imgs/logo.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
|
Before Width: | Height: | Size: 10 KiB |
@@ -1,43 +1,74 @@
|
|||||||
{% load static %}
|
{% load static form_tags %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, height=device-height" />
|
<meta charset="utf-8">
|
||||||
<title>
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
NibasaViewer login
|
<title>NibasaViewer — Login</title>
|
||||||
</title>
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
<link href="{% static 'css/old_styles.css' %}" rel="stylesheet">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/css/all.min.css" crossorigin="anonymous" referrerpolicy="no-referrer">
|
||||||
|
<link href="{% static 'css/styles.css' %}" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body class="background">
|
<body class="theme-{{ theme|default:'dark' }}">
|
||||||
<div class="fixed-width mauto-top">
|
|
||||||
<form method="post" action="{% url 'login' %}" class="full-width">
|
<section class="vh-100">
|
||||||
{% csrf_token %}
|
<div class="container py-5 h-100">
|
||||||
<table class="full-width">
|
<div class="row d-flex justify-content-center align-items-center h-100">
|
||||||
<tr>
|
<div class="col col-xl-10">
|
||||||
<td>
|
<div class="card shadow" style="border-radius: 1rem;">
|
||||||
{{ form.username.label_tag }}
|
<div class="row g-0">
|
||||||
</td>
|
<div class="col-md-6 col-lg-5 d-none d-md-block">
|
||||||
<td>
|
<img src="{% static 'imgs/login.jpg' %}"
|
||||||
{{ form.username }}
|
alt="login image" class="img-fluid" style="height:100%; object-fit:cover; border-radius: 1rem 0 0 1rem;" />
|
||||||
</td>
|
</div>
|
||||||
</tr>
|
<div class="col-md-6 col-lg-7 d-flex align-items-center">
|
||||||
<tr>
|
<div class="card-body p-4 p-lg-5 text-black h-100 login-form">
|
||||||
<td>
|
|
||||||
{{ form.password.label_tag }}
|
<form method="post" action="{% url 'login' %}" class="w-100">
|
||||||
</td>
|
{% csrf_token %}
|
||||||
<td>
|
{% if form.non_field_errors %}
|
||||||
{{ form.password }}
|
<div class="alert alert-danger">{{ form.non_field_errors }}</div>
|
||||||
</td>
|
{% endif %}
|
||||||
</tr>
|
|
||||||
<tr>
|
<div class="d-flex align-items-center mb-3 pb-1">
|
||||||
<td colspan="2">
|
<img src="{% static 'imgs/logo.png' %}" alt="Logo" style="width:44px; height:44px; object-fit:contain;" class="me-3">
|
||||||
<input type="submit" value="login" class="float-right">
|
<span class="h1 fw-bold mb-0 text-light">Nibasa Viewer</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h5 class="fw-normal mb-1 mt-4 pb-3" style="letter-spacing: 1px; color:var(--muted);">Sign in</h5>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
{% render_field form.username class="form-control form-control-lg" placeholder="User name" autocomplete="username" %}
|
||||||
|
{% if form.username.errors %}
|
||||||
|
<div class="text-danger small mt-1">{{ form.username.errors|striptags }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
{% render_field form.password class="form-control form-control-lg" placeholder="Password" autocomplete="current-password" %}
|
||||||
|
{% if form.password.errors %}
|
||||||
|
<div class="text-danger small mt-1">{{ form.password.errors|striptags }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pt-1 mb-4">
|
||||||
|
<button class="btn btn-dark btn-lg btn-block w-100" type="submit">LOGIN</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if next %}
|
||||||
<input type="hidden" name="next" value="{{ next }}">
|
<input type="hidden" name="next" value="{{ next }}">
|
||||||
</td>
|
{% endif %}
|
||||||
</tr>
|
</form>
|
||||||
</table>
|
|
||||||
</form>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|||||||
0
viewer/templatetags/__init__.py
Normal file
33
viewer/templatetags/form_tags.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
from django import template
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def render_field(field, **attrs):
|
||||||
|
"""Render a bound field with temporary widget attributes.
|
||||||
|
|
||||||
|
Usage in template:
|
||||||
|
{% render_field form.username class="form-control" placeholder="Email" %}
|
||||||
|
|
||||||
|
This updates the widget attrs just for the duration of rendering and
|
||||||
|
restores the original attrs afterwards to avoid side effects.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
widget = field.field.widget
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
# preserve original attrs and update temporarily
|
||||||
|
original_attrs = widget.attrs.copy()
|
||||||
|
# Convert all attr values to strings (template passes them as strings)
|
||||||
|
for k, v in attrs.items():
|
||||||
|
widget.attrs[str(k)] = str(v)
|
||||||
|
|
||||||
|
rendered = field.as_widget()
|
||||||
|
|
||||||
|
# restore original attrs
|
||||||
|
widget.attrs = original_attrs
|
||||||
|
|
||||||
|
return mark_safe(rendered)
|
||||||