Add: theme switcher

This commit is contained in:
Stepan Zhukovsky 2023-08-01 15:47:27 +09:00
parent 1964e29197
commit 9015a7daa5
12 changed files with 314 additions and 90 deletions

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

View File

@ -0,0 +1,90 @@
/*!
* Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
* Copyright 2011-2023 The Bootstrap Authors
* Licensed under the Creative Commons Attribution 3.0 Unported License.
*/
;(() => {
"use strict"
const getStoredTheme = () => localStorage.getItem("theme")
const setStoredTheme = (theme) => localStorage.setItem("theme", theme)
const getPreferredTheme = () => {
const storedTheme = getStoredTheme()
if (storedTheme) {
return storedTheme
}
return window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light"
}
const setTheme = (theme) => {
if (
theme === "auto" &&
window.matchMedia("(prefers-color-scheme: dark)").matches
) {
document.documentElement.setAttribute("data-bs-theme", "dark")
} else {
document.documentElement.setAttribute("data-bs-theme", theme)
}
}
setTheme(getPreferredTheme())
const showActiveTheme = (theme, focus = false) => {
const themeSwitcher = document.querySelector("#bd-theme")
if (!themeSwitcher) {
return
}
const themeSwitcherText = document.querySelector("#bd-theme-text")
const activeThemeIcon = document.querySelector(".theme-icon-active use")
const btnToActive = document.querySelector(
`[data-bs-theme-value="${theme}"]`
)
const svgOfActiveBtn = btnToActive
.querySelector("svg use")
.getAttribute("href")
document.querySelectorAll("[data-bs-theme-value]").forEach((element) => {
element.classList.remove("active")
element.setAttribute("aria-pressed", "false")
})
btnToActive.classList.add("active")
btnToActive.setAttribute("aria-pressed", "true")
activeThemeIcon.setAttribute("href", svgOfActiveBtn)
const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`
themeSwitcher.setAttribute("aria-label", themeSwitcherLabel)
if (focus) {
themeSwitcher.focus()
}
}
window
.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", () => {
const storedTheme = getStoredTheme()
if (storedTheme !== "light" && storedTheme !== "dark") {
setTheme(getPreferredTheme())
}
})
window.addEventListener("DOMContentLoaded", () => {
showActiveTheme(getPreferredTheme())
document.querySelectorAll("[data-bs-theme-value]").forEach((toggle) => {
toggle.addEventListener("click", () => {
const theme = toggle.getAttribute("data-bs-theme-value")
setStoredTheme(theme)
setTheme(theme)
showActiveTheme(theme, true)
})
})
})
})()

View File

@ -0,0 +1,31 @@
$(function () {
function deleteArchiveListElement(id) {
const archiveList = `#li-archive-${id}`
$(archiveList).remove()
}
$(".btn-archive-eraser").click(function (e) {
e.preventDefault();
const csrf = $("input[name=csrfmiddlewaretoken]").val()
console.log(csrf)
const archiveListElement = $(this).attr("data-jq-archive-target");
$.ajax({
type: "delete",
url: $(this).attr("href"),
headers: {
'X-CSRFToken':csrf,
'Content-Type':'application/json'
},
// beforeSend: function(xhr) {
// xhr.setRequestHeader("X-CSRFToken", csrf);
// },
success: function (response) {
console.log(response)
deleteArchiveListElement(archiveListElement);
},
error: function (response) {
console.log(response)
}
});
});
console.log("JQ is ready to work");
});

View File

@ -1,6 +1,6 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<html lang="en" data-bs-theme="auto">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
@ -27,6 +27,7 @@
href="{% static 'collector/img/favicon-16x16.png' %}"
/>
<link rel="manifest" href="{% static 'collector/img/site.webmanifest' %}">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
<title>Document</title>
</head>
<body>
@ -36,6 +37,7 @@
</section>
</main>
<script src="{% static 'collector/js/bootstrap.bundle.min.js' %}"></script>
<script src="{% static 'collector/js/bs.theme.mode.js' %}"></script>
<script src="{% static 'collector/js/jquery-3.7.0.min.js' %}"></script>
{% block bs %}{% endblock bs %}
{% block jquery %}{% endblock jquery %}

View File

@ -1,8 +1,23 @@
<header>
<section>
<svg xmlns="http://www.w3.org/2000/svg" class="d-none">
<symbol id="check2" viewBox="0 0 16 16">
<path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/>
</symbol>
<symbol id="circle-half" fill="currentColor" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z"/>
</symbol>
<symbol id="moon-stars-fill" fill="currentColor" viewBox="0 0 16 16">
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"/>
<path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z"/>
</symbol>
<symbol id="sun-fill" fill="currentColor" viewBox="0 0 16 16">
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"/>
</symbol>
</svg>
<nav class="navbar navbar-expand-lg bg-body-tertiary">
<div class="container">
<a class="navbar-brand" href="{% url 'index' %}">Logs Collector</a>
<a class="navbar-brand" href="{% url 'index' %}">Logs Collector <i class="bi bi-file-earmark-zip-fill"></i></a>
<button
class="navbar-toggler"
type="button"
@ -14,17 +29,15 @@
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav ml-auto mb-2 mb-lg-0">
<li class="nav-item dropdown">
<a
<button
class="nav-link dropdown-toggle"
href="#"
role="button"
data-bs-toggle="dropdown"
aria-expanded="false"
>Tickets</a
>
>Tickets</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" href="{% url 'index' %}">All</a>
@ -39,22 +52,84 @@
</li>
{% endfor %}
<li><hr class="dropdown-divider" /></li>
<li><a class="dropdown-item" href="#">Create ticket</a></li>
<li><a class="dropdown-item" href="#"><i class="bi bi-pencil-square"></i> Create ticket</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link disabled">Отключенная</a>
</ul>
<!-- Search -->
<ul class="navbar-nav flex-row flex-wrap ms-md-auto">
<li class="nav-item py-2 py-lg-1 col-12 col-lg-auto">
<form class="d-flex" role="search">
<input
class="form-control me-2"
type="search"
placeholder="Search"
aria-label="Search"
/>
<button class="btn btn-outline-success" type="submit"><i class="bi bi-search"></i></button>
</form>
</li>
</ul>
<!-- User settings -->
<ul class="navbar-nav flex-row flex-wrap ms-md-auto">
<li class="nav-item py-2 py-lg-1 col-12 col-lg-auto">
<div class="btn-group">
<button type="button" class="btn btn-outline-secondary">
<i class="bi bi-person-square"></i> {{ request.user }}
</button>
<button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><button class="dropdown-item" type="button">Settings</button></li>
<li><hr class="dropdown-divider" /></li>
<li><button class="dropdown-item" type="button">Logout</button></li>
</ul>
</div>
</li>
<!-- Separator -->
<li class="nav-item py-2 py-lg-1 col-12 col-lg-auto">
<div class="vr d-none d-lg-flex h-100 mx-lg-2 text-white"></div>
<hr class="d-lg-none my-2 text-white-50">
</li>
<!-- Theme switcher-->
<li class="nav-item dropdown">
<div class="dropdown bd-mode-toggle">
<button class="btn btn-bd-primary py-2 dropdown-toggle d-flex align-items-center"
id="bd-theme"
type="button"
aria-expanded="false"
data-bs-toggle="dropdown"
aria-label="Toggle theme (auto)">
<svg class="bi my-1 theme-icon-active" width="1em" height="1em"><use href="#circle-half"></use></svg>
<span class="visually-hidden" id="bd-theme-text">Toggle theme</span>
</button>
<ul class="dropdown-menu dropdown-menu-end shadow" aria-labelledby="bd-theme-text">
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="light" aria-pressed="false">
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#sun-fill"></use></svg>
Light
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark" aria-pressed="false">
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#moon-stars-fill"></use></svg>
Dark
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="auto" aria-pressed="true">
<svg class="bi me-2 opacity-50 theme-icon" width="1em" height="1em"><use href="#circle-half"></use></svg>
Auto
<svg class="bi ms-auto d-none" width="1em" height="1em"><use href="#check2"></use></svg>
</button>
</li>
</ul>
</div>
</li>
</ul>
<form class="d-flex" role="search">
<input
class="form-control me-2"
type="search"
placeholder="Поиск"
aria-label="Поиск"
/>
<button class="btn btn-outline-success" type="submit">Поиск</button>
</form>
</div>
</div>
</nav>

View File

@ -1,70 +1,83 @@
{% extends 'collector/base.html' %}
{% extends 'collector/base.html' %}
{% load static %}
{% block content %}
{% include 'collector/navigation.html' %}
<main>
<section>
<div class="container mt-3">
<div class="row">
<div class="list-group mb-2">
<div class="list-group-item list-group-item-action disable" aria-current="true">
{% csrf_token %}
<div class="card">
<div class="card-body" aria-current="true">
<div class="d-flex w-100 justify-content-between mb-2">
<h5 class="mb-1">Ticket: {{ ticket.number }}</h5>
<h4 class="card-title mb-1">Ticket: {{ ticket.number }}</h4>
<small>{{ ticket.time_create }}</small>
</div>
<div class="col-xl-6 mb-2">
<h6 class="mb-1">Platform: {{ ticket.platform.pretty_name }}</h6>
<h6 class="mb-1">Owner: {{ ticket.user.username }}</h6>
<h6 class="card-title mb-1">Platform: {{ ticket.platform.pretty_name }}</h6>
<h6 class="card-title mb-1">Owner: {{ ticket.user.username }}</h6>
</div>
<div class="col-xl-6 mt-1 mb-2">
{% if ticket.note %}
<small><b>Note:</b></small>
<hr>
<p>{{ ticket.note }}</p>
<hr>
<div class="card">
<div class="card-header">
Note:
</div>
<div class="card-body">
<p class="card-text">{{ ticket.note }}</p>
</div>
</div>
{% endif %}
</div>
<!-- Logs -->
{% if ticket.archive_set.all %}
<small><b>Logs:</b></small>
<ul class="list-group mb-2 mt-2">
{% for archive in ticket.archive_set.all %}
<li class="list-group-item list-group-item-action">
<p style="word-wrap: break-word" ><b>File:</b> {{ archive.file }}</p>
<small>
<b>SHA1:</b>
<span style="word-wrap: break-word">{{ archive.sha1 }}</span>
</small>
<br>
<small>
<b>Size:</b>
<span style="word-wrap: break-word">{{ archive.size }}</span>
</small>
<div class="row">
<div class="col" >
<a
class="btn btn-outline-success btn-sm mt-2"
href="{{ archive.get_absolute_url }}"
>GET</a>
<button
class="btn btn-outline-danger btn-sm ms-2 mt-2"
button type="button" data-bs-toggle="modal" data-bs-target="#modal-archive-del-{{ archive.id }}"
>DEL</button>
</div>
</div>
</li>
{% endfor %}
</ul>
<ul class="list-group col-xl-6 mb-2 mt-2">
{% for archive in ticket.archive_set.all %}
<li
id="li-archive-{{ archive.id }}"
class="list-group-item list-group-item-action">
<smal>
<b>File:</b>
<span style="word-wrap: break-word">{{ archive.file }}</span>
</small>
<small>
<br>
<b>SHA1:</b>
<span style="word-wrap: break-word">{{ archive.sha1 }}</span>
</small>
<br>
<small>
<b>Size:</b>
<span style="word-wrap: break-word">{{ archive.size }}</span>
</small>
<div class="row">
<div class="col-xl-8" >
<a
class="btn btn-outline-success btn-sm mt-2"
href="{{ archive.get_absolute_url }}"
><i class="bi bi-download"></i> GET</a>
<button
button type="button"
class="btn btn-outline-danger btn-sm ms-2 mt-2"
data-bs-toggle="modal"
data-bs-target="#modal-archive-del-{{ archive.id }}"
><i class="bi bi-trash"></i> DEL</button>
</div>
</div>
</li>
{% endfor %}
</ul>
{% endif %}
<!-- Card buttons -->
<div class="d-flex w-100 justify-content-between">
<a
href="{{ ticket.get_absolute_url }}"
class="btn btn-outline-warning mb-1 mt-1"
>Edit</a>
><i class="bi bi-pencil-square"></i> Edit</a>
<a
href="{{ ticket.get_absolute_url }}"
class="btn btn-outline-danger mb-1 mt-1"
>Delete</a>
><i class="bi bi-trash"></i> Delete</a>
</div>
</div>
</div>
@ -82,8 +95,20 @@
<p style="word-wrap: break-word">{{ archive.file }}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger">Delete</button>
<button
type="button"
class="btn btn-secondary"
data-bs-dismiss="modal"
>Cancel
</button>
<a
href="{% url 'download' archive.file %}"
type="button"
class="btn btn-danger btn-archive-eraser"
data-bs-dismiss="modal"
data-jq-archive-target="{{ archive.id }}"
>Delete
</a>
</div>
</div>
</div>
@ -93,3 +118,6 @@
</section>
</main>
{% endblock content %}
{% block jquery %}
<script src="{% static 'collector/js/jq.delete.archive.js' %}"></script>
{% endblock jquery %}

View File

@ -93,5 +93,5 @@
</div>
{% endblock content %}
{% block bs %}
<script src="{% static 'collector/js/bs-tooltip.js' %}"></script>
<script src="{% static 'collector/js/bs.tooltip.js' %}"></script>
{% endblock bs %}

View File

@ -1,25 +1,13 @@
# from django.shortcuts import render
from django.contrib.auth.decorators import login_required
from django.http import FileResponse, Http404
from django.http import FileResponse, JsonResponse, Http404
from django.views import generic
from rest_framework import status
from rest_framework.response import Response
# from rest_framework.response import Response
from .models import Archive, Ticket, Platform
# handles the url "/archives/{PATH}"".
@login_required
def download(request, path):
try:
file = Archive.objects.get(file=path)
except Archive.DoesNotExist:
return Http404
return FileResponse(file.file)
class ArchiveHandlerView(generic.View):
def get(self, request, path):
try:
@ -29,13 +17,24 @@ class ArchiveHandlerView(generic.View):
return FileResponse(file.file)
def delete(self, request, path):
content = {'file': path}
try:
file = Archive.objects.get(file=path)
file.delete()
return Response(content, status=status.HTTP_200_OK)
except Archive.DoesNot.Exist:
return Response(content, status=status.HTTP_204_NO_CONTENT)
return JsonResponse(
{
'file': path,
'status': status.HTTP_200_OK
},
status=status.HTTP_200_OK
)
except Archive.DoesNotExist:
return JsonResponse(
{
'file': path,
'status': status.HTTP_204_NO_CONTENT
},
status=status.HTTP_204_NO_CONTENT
)
class ListAllTickets(generic.ListView):