Applied new layout

nightly
Andrew 4 years ago
parent 38ae681a9b
commit ada4bce0eb
  1. 61
      stashr/routes.py
  2. 110
      stashr/static/css/stashr.css
  3. 12
      stashr/static/js/stashr.js
  4. 26
      stashr/templates/all_collections_page.html
  5. 26
      stashr/templates/all_publishers_page.html
  6. 74
      stashr/templates/all_volumes_page.html
  7. 256
      stashr/templates/base.html
  8. 110
      stashr/templates/first_run_page.html
  9. 89
      stashr/templates/login_page.html
  10. 42
      stashr/templates/new_releases_page.html
  11. 6
      stashr/templates/read_issue_page.html
  12. 71
      stashr/templates/reading_list_page.html
  13. 45
      stashr/templates/register_page.html
  14. 53
      stashr/templates/scrape_page.html
  15. 49
      stashr/templates/search_page.html
  16. 228
      stashr/templates/settings_page.html
  17. 16
      stashr/templates/settings_page_all_users.html
  18. 13
      stashr/templates/settings_page_app.html
  19. 13
      stashr/templates/settings_page_directories.html
  20. 13
      stashr/templates/settings_page_mail.html
  21. 86
      stashr/templates/settings_page_new_user.html
  22. 15
      stashr/templates/settings_page_plugins.html
  23. 16
      stashr/templates/settings_page_single_user.html
  24. 64
      stashr/templates/settings_page_tasks.html
  25. 153
      stashr/templates/single_collection_page.html
  26. 74
      stashr/templates/single_publisher_page.html
  27. 274
      stashr/templates/single_volume_page.html

@ -116,6 +116,10 @@ def index_page():
flash('First Run')
return redirect(url_for('first_run_page'))
if not current_user.is_authenticated:
flash('Please Log IN', 'Info')
return redirect(url_for('login_page'))
return render_template(
'index_page.html',
title='Home',
@ -156,28 +160,6 @@ def login_page():
)
# REGISTER
@app.route('/register', methods=['GET', 'POST'])
def register_page():
if not stashrconfig['APP']['open_registration']:
flash('Registration Closed', 'error')
return redirect(url_for('login_page'))
registration_form = forms.registration_form()
if registration_form.validate_on_submit() and registration_form.register_button.data:
utils.register_new_user(registration_form)
return redirect(url_for('login_page'))
return render_template(
'register_page.html',
registration_form=registration_form,
title='Register',
open_registration=stashrconfig['APP']['open_registration']
)
# FORGOT PASSWORD
@app.route('/forgot', methods=['GET', 'POST'])
def forgot_page():
@ -211,7 +193,7 @@ def logout_page():
logout_user()
# identity_changed.send(current_app._get_current_object(), identity=AnonymousIdentity())
flash('Logged Out', 'info')
return redirect(url_for('index_page'))
return redirect(url_for('login_page'))
""" --- SETTINGS --- """
@ -477,21 +459,21 @@ def settings_plugins_page():
# FIRST RUN
@app.route('/firstrun', methods=['GET', 'POST'])
def first_run_page():
if current_user.is_authenticated:
flash('App Configured', 'info')
return redirect(url_for('index_page'))
return redirect(url_for('login_page'))
if not stashrconfig['APP']['first_run']:
flash('App Configured', 'info')
return redirect(url_for('index_page'))
return redirect(url_for('login_page'))
first_run_form = forms.app_first_run_form()
if request.method == 'POST' and first_run_form.first_run_button.data:
if request.method == 'POST' and first_run_form.first_run_button.data and first_run_form.validate():
utils.complete_first_run(first_run_form)
flash('Setup Complete', 'success')
return redirect(url_for('index_page'))
return redirect(url_for('login_page'))
return render_template(
'first_run_page.html',
@ -709,6 +691,27 @@ def custom_image_static(foldername, filename):
def custom_service_worker():
return app.send_static_file('js/service-worker.js')
""" --- v TEST AREA v --- """
@app.route('/test_login')
def test_login():
login_form = forms.login_form()
return render_template(
'test_login.html',
login_form = login_form
)
@app.route('/test_base')
def test_base():
return render_template(
'test_base.html'
)
""" --- ^ TEST AREA ^ --- """
"""-------------------------------------------------------------------------------------------
-- DEVELOPMENT
-------------------------------------------------------------------------------------------"""

@ -1,72 +1,50 @@
/* START NEW STASHR CLASSES */
body { background:#444444; }
body { display: flex; flex-wrap: nowrap; height: 100vh; height: -webkit-fill-available; overflow-x: auto; overflow-y: hidden; background-color:#2c2d35 !important; }
ul { list-style-type: none; }
.center { text-align:center; }
.login { align-items: center !important; }
@media screen and (max-width: 800px) {
.stashr-cover_size {max-width:100px;min-width:20px;min-height:150px;}
@media screen and (max-width: 768px) {
.stashr-menu { margin-left:-280px; }
.stashr-item_container {max-width:100px;min-width:20px;min-height:150px;}
}
@media screen and (min-width: 800px) {
.stashr-cover_size {max-width:160px;min-width:100px;min-height:150px;}
@media screen and (min-width: 768px) {
.stashr-menu { margin-left:0px; }
.stashr-item_container {max-width:160px;min-width:100px;min-height:150px;}
}
#app {margin:0;padding:0;}
body .stashrRead { margin:0;background-color:black; }
.stashr-project_title { font-family: 'Grand Hotel', cursive;font-size:xx-large;line-height:40px;vertical-align: middle; }
.stashr-series_title { font-family: 'Oswald', sans-serif;font-weight:bold; }
.stashr-series_info {min-width:30%;max-width:60%;}
.stashr-poster_wrapper {position:relative;background-color:pink;z-index:5;}
.stashr-badge_tl {position:absolute;top:-5;left:-5;z-index:4;}
.stashr-badge_tr {position:absolute;top:-5;right:-5;z-index:4;}
.stashr-badge_bl {position:absolute;bottom:-5;left:-5;z-index:4;}
.stashr-badge_br {position:absolute;bottom:-5;right:-5;z-index:4;}
.stashr-poster_container {position:relative;overflow:hidden;}
.stashr-overlay_top {position:absolute;top:0;background-color:rgba(220,220,220,.9);z-index:5;}
.stashr-overlay_bottom {position:absolute;bottom:0;background-color:rgba(220,220,220,.9);z-index:5;}
.stashr-poster_background {z-index:0;}
.stashr-poster_link {position:absolute;top:0;left:0;width:100%;height:100%;}
.stashr-poster_image {margin: 0 auto;display: block;vertical-align: middle;}
.stashr-test_display {position:relative;display:inline-block;vertical-align:middle;}
.stashr-test-image {margin:0 auto;display:block;vertical-align:middle;width:100%;}
.swiper-container { width:100%;height:100%;padding:0px;margin:0px; }
.swiper-wrapper { padding:0px;margin:0px; }
.swiper-slide { width:100%;height:100%;background: black; }
.swiper-slide img { height:100%; }
.bg-mine { background: #3e3e44; }
.stashr-menu { transition: 0.5s; }
.stashr-footer { position:fixed; bottom:0; z-index:6; }
.stashr-item_container { overflow: hidden; }
.stashr-poster_container { position:relative; overflow: hidden; }
.stashr-poster_info { position:absolute; top:0; left:0; z-index:3; border-bottom-right-radius: 25%; }
.stashr-badge_top_left { position:absolute; top:0; left:0; z-index:3; border-bottom-right-radius: 25%; }
.stashr-badge_bottom_left { position:absolute; bottom:0; left:0; z-index:3; border-top-right-radius: 25%; }
.stashr-badge_top_right { position:absolute; top:0; right:0; z-index:3; border-bottom-left-radius: 25%; }
.stashr-badge_bottom_right { position:absolute; bottom:0; right:0; z-index:3; border-top-left-radius: 25%; }
.stashr-poster_tag { position: absolute; height:0; right:0; width:25%; padding-bottom:25%; transform: rotate(45deg); z-index:3; margin-top:-12.5%; margin-right:-12.5%; }
.stashr-poster { position:absolute; top:0; left:0; }
.stashr-overlay_top { position:absolute; top:0; background-color:rgba(220,220,220,.9); z-index:4; }
.stashr-overlay_bottom { position:absolute; bottom:0; background-color:rgba(220,220,220,.9); z-index:4; }
.stashr-progress_wrapper { position:absolute; bottom:0; z-index:3; }
.stashr-poster_wrapper { position:relative; overflow:hidden; }
.stashr-link { text-decoration: none; }
.stashr-button_container {position:fixed;bottom:10;right:10;opacity:.7;z-index:999;}
.stashr-button_container_reader {position:fixed;bottom:0;right:0;opacity:.7;z-index:999;}
.stashr-button { display:table-cell;vertical-align:middle;width:60px;height:60px; }
.stashr-check_box {position:absolute;top:0;right:0;z-index:5;}
.btn-circle.btn-sm {
width: 30px;
height: 30px;
padding: 6px 0px;
border-radius: 15px;
font-size: 8px;
text-align: center;
}
.btn-circle.btn-md {
width: 50px;
height: 50px;
padding: 7px 10px;
border-radius: 25px;
font-size: 10px;
text-align: center;
}
.btn-circle.btn-xl {
width: 70px;
height: 70px;
padding: 10px 16px;
border-radius: 35px;
font-size: 12px;
text-align: center;
}
.stashr-menu_button_container {position:fixed;bottom:10;left:10;opacity:.7;z-index:999;}
.stashr-reader { background:black; }
.new-stashr-button_container { opacity:.7; }
.btn-circle.btn-sm { width: 30px; height: 30px; padding: 6px 0px; border-radius: 15px; font-size: 8px; text-align: center; margin:2px; }
.btn-circle.btn-md { width: 50px; height: 50px; padding: 7px 10px; border-radius: 25px; font-size: 10px; text-align: center; margin:2px; }
.btn-circle.btn-xl { width: 70px; height: 70px; padding: 10px 16px; border-radius: 35px; font-size: 12px; text-align: center; margin:2px; }
.stashr-signin { width: 100%; max-width: 330px; padding: 15px; margin: auto; }
.stashr-signin .checkbox { font-weight: 400; }
.stashr-signin .form-floating:focus-within { z-index: 2; }
.stashr-signin input[type="username"] { margin-bottom: -1px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; }
.stashr-signin input[type="password"] { margin-bottom: 10px; border-top-left-radius: 0; border-top-right-radius: 0; }
.stashr-logo { position:relative; top:-85px; width:170px; height:170px; border-radius: 50%; border: 10px solid #2c2d35; margin-bottom:-70px; }
.stashr-firstrun { width: 100%; max-width: 330px; padding: 15px; margin: auto; }
.stashr-firstrun .form-floating:focus-within { z-index: 2; }
.stashr-firstrun input { margin-bottom: 5px; }

@ -2,7 +2,17 @@ function stashrToast(message, type) {
Vue.$toast.open({
message: message,
type: type,
position: 'bottom-left',
position: 'bottom-right',
dismissable: true
});
}
function toggleMenu() {
console.log('Toggle MENU')
var margin = getComputedStyle(document.querySelector('#sideMenu')).marginLeft;
if(margin == '0px') {
document.getElementById('sideMenu').style.marginLeft = "-280px";
} else {
document.getElementById('sideMenu').style.marginLeft = "0px"
}
}

@ -30,8 +30,10 @@ Vue.component('collections', {
props: ['collections'],
template: `
<div>
<div class="mb-3 px-5">
<input type="text" v-model="search" class="form-control" placeholder="Search Collections..." />
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-md-top shadow">
<div class="row w-100 m-0 p-3">
<input type="text" v-model="search" class="form-control" placeholder="Search Collections..." />
</div>
</div>
<ul class="d-flex flex-wrap w-100 m-0 p-0 py-2 justify-content-center">
<collection
@ -56,18 +58,18 @@ Vue.component('collections', {
Vue.component('collection', {
props: ['collection'],
template: `
<li class='stashr-cover_size m-2'
<li class='stashr-item_container m-2'
@mouseover="hover = true"
@mouseleave="hover = false"
>
<div class='stashr-poster_wrapper rounded'>
<div class="stashr-poster_container border rounded">
<div class="stashr-overlay_bottom w-100 center" v-if="hover">
<a href="#">[[ collection.collection_name ]]</a>
</div>
<img class="stashr-poster_background w-100" loading="eager" src="/static/assets/cover.svg" />
<a class="stashr-poster_link" v-bind:href="'/collections/'+collection.collection_slug">
<img class="w-100" loading="lazy" v-bind:src="'/images/issues/'+collection.collection_cover_image+'.jpg'" onerror="this.src='/static/assets/cover.svg'" />
>
<div class="stashr-poster_container border border-dark rounded-3">
<a class="stashr-poster_link" v-bind:href="'/collections/'+collection.collection_slug">
<img class="stashr-background w-100" loading="eager" src="/static/assets/cover.svg" />
<img class="stashr-poster w-100" loading="eager" v-bind:src="'/images/issues/'+collection.collection_cover_image+'.jpg'" />
</a>
<div class="stashr-overlay_bottom w-100 text-center shadow" v-if="hover">
<a class="stashr-link" v-bind:href="'/collections/'+collection.collection_slug">
[[ collection.collection_name ]]
</a>
</div>
</div>

@ -47,8 +47,10 @@ Vue.component('publishers', {
props: ['publishers'],
template: `
<div>
<div class="mb-3 px-5">
<input type="text" v-model="search" class="form-control" placeholder="Search Publishers..." />
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-md-top shadow">
<div class="row w-100 m-0 p-3">
<input type="text" v-model="search" class="form-control" placeholder="Search Publishers..." />
</div>
</div>
<ul class="d-flex flex-wrap w-100 m-0 p-0 py-2 justify-content-center">
<publisher
@ -77,18 +79,18 @@ Vue.component('publishers', {
Vue.component('publisher',{
props: ['publisher'],
template: `
<li class='stashr-cover_size m-2'
<li class='stashr-item_container m-2'
@mouseover="hover = true"
@mouseleave="hover = false"
>
<div class='stashr-poster_wrapper rounded'>
<div class="stashr-poster_container border rounded">
<div class="stashr-overlay_bottom w-100 center" v-if="hover">
<a href="#">[[ publisher.publisher_name ]]</a>
</div>
<img class="stashr-poster_background w-100" loading="eager" src="/static/assets/cover.svg" />
<a class="stashr-poster_link align-middle" :href="'{{ url_for('single_publisher_page', publisher_id='PUBLISHERID') }}'.replace('PUBLISHERID', publisher.publisher_id)">
<img class="w-100" loading="lazy" v-bind:src=publisher.publisher_image />
>
<div class="stashr-poster_container border border-dark rounded-3">
<a :href="'{{ url_for('single_publisher_page', publisher_id='PUBLISHERID') }}'.replace('PUBLISHERID', publisher.publisher_id)">
<img class="stashr-background w-100" loading="eager" src="/static/assets/cover.svg" />
<img class="stashr-poster w-100" loading="lazy" v-bind:src=publisher.publisher_image />
</a>
<div class="stashr-overlay_bottom w-100 text-center shadow" v-if="hover">
<a class="stashr-link" :href="'{{ url_for('single_publisher_page', publisher_id='PUBLISHERID') }}'.replace('PUBLISHERID', publisher.publisher_id)">
[[ publisher.publisher_name ]]
</a>
</div>
</div>

@ -51,40 +51,57 @@
Vue.component('volume-item', {
props: ['volume'],
template: `
<li class='stashr-cover_size m-2'
<li class='stashr-item_container m-2'
@mouseover="hover = true"
@mouseleave="hover = false"
>
<div class='stashr-poster_wrapper rounded'>
<div class="stashr-badge_tl badge rounded-pill bg-info border">[[ volume.age_rating[0].rating_short ]]</div>
<div class="stashr-badge_tr badge rounded-pill bg-primary border">[[ volume.volume_have ]]/[[ volume.volume_total ]]</div>
<div class="stashr-badge_br badge rounded-pill border" :class="statusClass">[[ statusWord ]]</div>
<div class="stashr-poster_container border rounded">
<div class="stashr-overlay_bottom w-100 center" v-if="hover">
<a :href="'/volumes/'+volume.volume_slug">[[ volume.volume_name ]]</a>
</div>
<img class="stashr-poster_background w-100" loading="eager" src="/static/assets/cover.svg" />
<a class="stashr-poster_link" :href="'/volumes/'+volume.volume_slug">
<img class="w-100" loading="lazy" v-bind:src="'/images/volumes/'+volume.volume_id+'.jpg'" @error="$event.target.src=volume.volume_image_med"/>
>
<div class="stashr-poster_container border border-dark rounded-3">
<div :class="volumeTag"></div>
<div class="stashr-poster_info bg-info text-white px-1">
[[ volume.age_rating[0].rating_short ]]
</div>
<div class="stashr-poster_wrapper">
<a :href="'/volumes/'+volume.volume_slug">
<img class="stashr-background w-100" loading="eager" src="/static/assets/cover.svg" />
<img class="stashr-poster w-100" loading="lazy" v-bind:src="'/images/volumes/'+volume.volume_id+'.jpg'" />
</a>
</div>
<div class="stashr-overlay_bottom w-100 text-center shadow" v-if="hover">
<a class="stashr-link" :href="'/volumes/'+volume.volume_slug">
[[ volume.volume_name ]]
</a>
</div>
<div class="stashr-progress_wrapper w-100">
<div class="progress bg-dark" style="height: 5px;">
<div class="progress-bar" :class="progressColor" role="progressbar" :style="progressStyle" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</div>
</li>
`,
computed: {
statusClass() {
let classname = 'bg-danger';
if(this.volume.volume_status) {
classname = 'bg-success';
};
return classname;
volumeTag() {
let cornertag = '';
if(!this.volume.volume_status) {
cornertag = "stashr-poster_tag bg-danger shadow";
}
return cornertag;
},
progressWidth() {
return (this.volume.volume_have / this.volume.volume_total)*100;
},
statusWord() {
let status = 'ENDED';
progressColor() {
let classname = 'bg-success';
if(this.volume.volume_status) {
status = 'ONGOING';
};
return status;
classname = 'bg-info';
}
if(this.progressWidth < 100) {
classname = 'bg-danger';
}
return classname
},
progressStyle() {
return "width: " + this.progressWidth + "%;"
}
},
data() { return { hover: false } },
@ -95,8 +112,10 @@ Vue.component('volumes', {
props: ['volumes'],
template: `
<div>
<div class="mb-3 px-5">
<input type="text" v-model="search" class="form-control" placeholder="Search Volumes..." />
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-top shadow">
<div class="row w-100 m-0 p-3">
<input type="text" v-model="search" class="form-control" placeholder="Search Volumes..." />
</div>
</div>
<ul class="d-flex flex-wrap w-100 m-0 p-0 py-2 justify-content-center">
<volume-item
@ -105,9 +124,6 @@ Vue.component('volumes', {
v-bind:key="volume.volume_id"
></volume-item>
</ul>
<!--
<i class="text-primary fas fa-spinner fa-spin fa-3x" v-if="loading"></i>
-->
</div>
`,
data() { return { loading: true, search: '', } },

@ -1,5 +1,6 @@
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, shrink-to-fit=no">
@ -26,186 +27,161 @@
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<script src="{{ url_for('static', filename='js/stashr.js') }}"></script>
<!-- VueJS -->
<script src="{{ url_for('static', filename='js/vue.dev.js') }}"></script>
<!-- VueJS Toasts -->
<script src="https://cdn.jsdelivr.net/npm/vue-toast-notification"></script>
<!-- START HEADER SCRIPT INCLUDES -->
{% block header_script_files %}{% endblock %}
{{ emit_tep('base_page_header_script_files') }}
<!-- END HEADER SCRIPT INCLUDES -->
</head>
<body>
<!-- START NAVBAR -->
<nav class="navbar navbar-expand-lg navbar-dark bg-primary sticky-top">
<div class="container-fluid">
<a class="navbar-brand" href="{{ url_for('index_page') }}">
<img class="border rounded-circle" src="{{ url_for('static', filename='assets/stashr.svg') }}" width="40" height="40" />
<span class="stashr-project_title">Stashr</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarStashr" aria-cointrols="navbarStashr" aria-expanded="false" aria-label="Toggle Navigation">
<span class="navbar-toggler-icon"></span>
</button>
{% if not current_user.is_authenticated %}
{% if not request.endpoint == 'first_run_page' %}
<div class="collapse navbar-collapse" id="navbarStashr">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link" href="{{ url_for('login_page') }}">
LOGIN
</a>
</li>
{% if open_registration %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('register_page') }}">
REGISTER
</a>
</li>
{% endif %}
</ul>
<div class="container-fluid d-flex min-vh-100 m-0 p-0">
<div class="stashr-menu d-flex flex-column p-3 text-white bg-dark text-center min-vh-100 sticky-top overflow-auto" id="sideMenu" style="width:280px;">
<div class="d-flex flex-row">
<a class="navbar-brand" href="{{ url_for('index_page') }}">
<img class="border rounded-circle" src="{{ url_for('static', filename='assets/stashr.svg') }}" width="40" height="40" />
<span class="stashr-project_title">Stashr</span>
</a>
</div>
{% endif %}
{% endif %}
{% if current_user.is_authenticated %}
<div class="collapse navbar-collapse" id="navbarStashr">
<ul class="navbar-nav me-auto">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="libraryDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
LIBRARY
</a>
<ul class="dropdown-menu" aria-labelledby="libraryDropdown">
<li><a class="dropdown-item" href="{{ url_for('all_volumes_page') }}">VOLUMES</a></li>
<li><a class="dropdown-item" href="{{ url_for('all_publishers_page') }}">PUBLISHERS</a></li>
<hr/>
<ul class="nav nav-pills flex-column mb-auto text-start">
<li>
<a data-bs-toggle="collapse" data-bs-target="#collapseLibrary" class="nav-link text-white collapsed">
<i class="fas fa-chevron-right"></i>
Library
</a>
<div class="collapse{% if (request.path == url_for('all_volumes_page')) or
(request.path == url_for('all_publishers_page')) or
(request.path == url_for('scrape_folders_page')) %} show{% endif %}" id="collapseLibrary">
<ul class="ps-3 nav nav-pills flex-column">
<li>
<a href="{{ url_for('all_volumes_page') }}" class="nav-link text-white{% if request.path == url_for('all_volumes_page') %} active{% endif %}">
Volumes
</a>
</li>
<li>
<a href="{{ url_for('all_publishers_page') }}" class="nav-link text-white{% if request.path == url_for('all_publishers_page') %} active{% endif %}">
Publishers
</a>
</li>
{% if current_user.role == 'admin' %}
<li><a class="dropdown-item" href="{{ url_for('scrape_folders_page') }}">SCRAPE</a></li>
<li>
<a href="{{ url_for('scrape_folders_page') }}" class="nav-link text-white{% if request.path == url_for('scrape_folders_page') %} active{% endif %}">
Scrape
</a>
</li>
{% endif %}
</ul>
</li>
{% if current_user.role == 'admin' or current_user.role == 'librarian' %}
<li class="nav-item">
<a class="nav-link{% if request.path == url_for('new_releases_page') %} active{% endif %}" href="{{ url_for('new_releases_page') }}">
NEW RELEASES
</a>
</li>
{% endif %}
{% if current_user.role == 'admin' or current_user.role == 'librarian' or current_user.role == 'reader' %}
<li class="nav-item">
<a class="nav-link{% if request.path == url_for('reading_list_page') %} active{% endif %}" href="{{ url_for('reading_list_page') }}">
READING LIST
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.path == url_for('all_collections_page') %} active{% endif %}" href="{{ url_for('all_collections_page') }}">
COLLECTIONS
</a>
</li>
{% endif %}
{{ emit_tep("base_page_main_menu") }}
</ul>
<ul class="navbar-nav ms-auto">
{% if current_user.role == 'admin' %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('settings_page') }}">
<i class="fa fa-cogs"></i>
Settings
</a>
</li>
{% endif %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="libraryDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<i class="fa fa-user"></i>
{{ current_user.username }}
</a>
<ul class="dropdown-menu" aria-labelledby="libraryDropdown">
<li><a class="dropdown-item" href="{{ url_for('settings_single_user_page', user_id=current_user.id) }}">
<i class="fa fa-user"></i>
{{ current_user.username }}
</a></li>
<li><a class="dropdown-item" href="{{ url_for('logout_page') }}">
<i class="fas fa-sign-out-alt"></i>
Logout
</a></li>
</ul>
</li>
</ul>
</div>
{% endif %}
</div>
</li>
{% if current_user.role == 'admin' or current_user.role == 'librarian' %}
<li>
<a href="{{ url_for('new_releases_page') }}" class="nav-link text-white{% if request.path == url_for('new_releases_page') %} active{% endif %}">
New Releases
</a>
</li>
{% endif %}
{% if current_user.role == 'admin' or current_user.role == 'librarian' or current_user.role == 'reader' %}
<li>
<a href="{{ url_for('reading_list_page') }}" class="nav-link text-white{% if request.path == url_for('reading_list_page') %} active{% endif %}">
Reading List
</a>
</li>
<li>
<a href="{{ url_for('all_collections_page') }}" class="nav-link text-white{% if request.path == url_for('all_collections_page') %} active{% endif %}">
Collections
</a>
</li>
{% endif %}
{{ emit_tep("base_page_main_menu") }}
</ul>
<hr />
<ul class="nav nav-pills d-flex flex-column text-start">
<li>
<a href="{{ url_for('settings_single_user_page', user_id=current_user.id) }}" class="nav-link text-white">
<i class="fa fa-user"></i>
{{ current_user.username }}
</a>
</li>
{% if current_user.role == 'admin' %}
<li>
<a href="{{ url_for('settings_page') }}" class="nav-link text-white">
<i class="fas fa-cogs"></i>
Settings
</a>
</li>
{% endif %}
<li>
<a href="{{ url_for('logout_page') }}" class="nav-link text-white">
<i class="fas fa-sign-out-alt"></i>
Logout
</a>
</li>
</ul>
</div>
</nav>
<div class="d-flex flex-column w-100 m-0 px-0 min-vh-100 overflow-auto">
<!-- END NAVBAR -->
<!-- v HEADER v -->
<!-- ^ HEADER ^ -->
<!-- START HEADER -->
<div class="py-2" id="stashr_header">
{% block header %}{% endblock %}
</div>
<!-- END HEADER -->
<!-- v CONTENT v -->
{% block content %}{% endblock %}
<!-- ^ CONTENT ^ -->
<!-- v BUTTON CONTAINER v -->
<div class="stashr-button_container p-2">
</div>
<!-- START CONTENT -->
<div class="py-2" id="stashr_content">
{% block content %}{% endblock %}
<div class="stashr-menu_button_container p-2">
</div>
<div class="d-flex w-100 stashr-footer p-2 new-stashr-button_container">
<button type="button" class="btn btn-info btn-circle btn-md" data-bs-toggle="tooltip" data-bs-placement="top" title="Add Volume from Comicvine" onclick="toggleMenu()">
<i class="fas fa-ellipsis-v fa-2x"></i>
</button>
{% block button_container %}{% endblock %}
</div>
<!-- ^ BUTTON CONTAINER ^ -->
</div>
</div>
<!-- END CONTENT -->
<!-- START MODALS -->
<!-- v MODALS v -->
{% block modals %}{% endblock %}
{{ emit_tep('base_page_modals') }}
<!-- END MODALS -->
<!-- ^ MODALS ^ -->
<!-- START BUTTON CONTAINER -->
<div class="stashr-button_container p-2">
{% block button_container %}{% endblock %}
</div>
<!-- END BUTTON CONTAINER -->
<!-- START FOOTER SCRIPT INCLUDES -->
<!-- v FOOTER SCRIPT INCLUDES v -->
<script src="{{ url_for('static', filename='js/bootstrap.bundle.js') }}"></script>
{% block footer_script_files %}{% endblock %}
{{ emit_tep('base_page_footer_script_files') }}
<!-- END FOOTER SCRIPT INCLUDES -->
<!-- ^ FOOTER SCRIPT INCLUDES ^ -->
<!-- START FOOTER SCRIPT -->
<!-- v FOOTER SCRIPT v -->
<script type="text/javascript">
/* ----- CODE FOR TOOLTIPS WHEN WORKING
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl)
})
*/
Vue.use(VueToast);
// Flashes
{% with flashes = get_flashed_messages(with_categories=true) %}
{% if flashes %}
{% for category, message in flashes %}
{% if flashes %}
{% for category, message in flashes %}
stashrToast('{{ message }}', '{{ category }}');
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}
{% endwith %}
{% block script %}{% endblock %}
{{ emit_tep('base_page_script') }}
// SERVICE WORKER
// Check that service workers are supported
window.addEventListener("load", () => {
if ("serviceWorker" in navigator) {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("service-worker.js");
}
}
});
{% block script %}{% endblock %}
</script>
<!-- END FOOTER SCRIPT -->
<!-- ^ FOOTER SCRIPT ^ -->
</body>
</html>

@ -1,52 +1,92 @@
{% extends "base.html" %}
{% block content %}
<div class="d-flex justify-content-center flex-wrap w-80">
<div class='position-relative my-2'>
<img class="border rounded-circle my-3" src="{{ url_for('static', filename='assets/stashr.svg') }}" width="200" height="200" />
</div>
<div class="bg-light m-2 px-2 rounded stashr-series_info text-center text-lg-start py-3 px-5">
<h5 class="text-center">First Run</h5>
<hr />
<form method="POST">
<html>
<head>
<title>Stashr - {{ title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, shrink-to-fit=no">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.css') }}">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/stashr.css') }}">
<link href="https://cdn.jsdelivr.net/npm/vue-toast-notification/dist/theme-sugar.css" rel="stylesheet">
<link rel="manifest" href="{{ url_for('static', filename='manifest/manifest.json') }}">
<link rel="apple-touch-icon" href="touch-icon-iphone.png">
<link rel="apple-touch-icon" sizes="152x152" href="{{ url_for('static', filename='assets/stashr-152.png') }}">
<link rel="apple-touch-icon" sizes="180x180" href="{{ url_for('static', filename='assets/stashr-180.png') }}">
<link rel="apple-touch-icon" sizes="167x167" href="{{ url_for('static', filename='assets/stashr-167.png') }}">
<meta name="apple-mobile-web-app-title" content="Stashr">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<script src="{{ url_for('static', filename='js/stashr.js') }}"></script>
<script src="{{ url_for('static', filename='js/vue.dev.js') }}"></script>
</head>
<body class="text-center login">
<main class="stashr-firstrun">
<div class="card bg-primary px-3 shadow">
<form action="{{ url_for('first_run_page') }}" method="post" id="first_run_form">
{{ first_run_form.csrf_token }}
<img class="stashr-logo" src="{{ url_for('static', filename='assets/stashr.svg') }}" w="170" h="170">
<div class="mb-3">
{{ first_run_form.username.label }}
{% if first_run_form.errors %}
<div class="notification bg-danger rounded m-3 text-white" role="alert" id='error_container'>
{% for field_name, field_errors in first_run_form.errors|dictsort if field_errors %}
<ul>
{% for error in field_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endfor %}
</div>
{% endif %}
<div class="form-floating">
{{ first_run_form.username(class_='form-control', placeholder=first_run_form.username.label.text) }}
{{ first_run_form.username.label }}
</div>
<div class="mb-3">
{{ first_run_form.email.label }}
<div class="form-floating">
{{ first_run_form.email(class_='form-control', placeholder=first_run_form.email.label.text) }}
{{ first_run_form.email.label }}
</div>
<div class="mb-3">
{{ first_run_form.password.label }}
<div class="form-floating">
{{ first_run_form.password(type='password', class_='form-control', placeholder=first_run_form.password.label.text) }}
{{ first_run_form.password.label }}
</div>
<div class="mb-3">
{{ first_run_form.confirm_password.label }}
<div class="form-floating">
{{ first_run_form.confirm_password(type='password', class_='form-control', placeholder=first_run_form.confirm_password.label.text) }}
{{ first_run_form.confirm_password.label }}
</div>
<hr />
<div class="mb-3">
{{ first_run_form.comicvine_api_key.label }}
<div class="form-floating">
{{ first_run_form.comicvine_api_key(class_='form-control', placeholder=first_run_form.comicvine_api_key.label.text) }}
{{ first_run_form.comicvine_api_key.label }}
</div>
<hr />
<div class="mb-3">
{{ first_run_form.open_registration }}
{{ first_run_form.open_registration.label }}
</div>
<div class="mb-3">
{{ first_run_form.logging_level.label }}
<div class="form-floating">
{{ first_run_form.logging_level(class_='form-control') }}
{{ first_run_form.logging_level.label }}
</div>
<hr />
<div class="mb-3 text-end">
{{ first_run_form.first_run_button(class_='btn btn-success') }}
</div>
{{ first_run_form.first_run_button(class_='btn btn-success') }}
</form>
</div>
</div>
{% endblock %}
</main>
<script type="text/javascript">
Vue.use(VueToast);
{% with flashes = get_flashed_messages(with_categories=true) %}
{% if flashes %}
{% for category, message in flashes %}
stashrToast('{{ message }}', '{{ category }}');
{% endfor %}
{% endif %}
{% endwith %}
</script>
</body>
</html>

@ -1,15 +1,37 @@
{% extends "base.html" %}
{% block content %}
<div class="d-flex justify-content-center flex-wrap w-80">
<div class='position-relative my-2'>
<img class="border rounded-circle my-3" src="{{ url_for('static', filename='assets/stashr.svg') }}" width="200" height="200" />
</div>
<div class="bg-light m-2 px-2 rounded stashr-series_info text-center text-lg-start py-3 px-5">
<h5 class="text-center">Login</h5>
<hr />
<form action="{{ url_for('login_page') }}" method="post" id="registration_form">
<html>
<head>
<title>Stashr - {{ title }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, shrink-to-fit=no">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.css') }}">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/stashr.css') }}">
<link href="https://cdn.jsdelivr.net/npm/vue-toast-notification/dist/theme-sugar.css" rel="stylesheet">
<link rel="manifest" href="{{ url_for('static', filename='manifest/manifest.json') }}">
<link rel="apple-touch-icon" href="touch-icon-iphone.png">
<link rel="apple-touch-icon" sizes="152x152" href="{{ url_for('static', filename='assets/stashr-152.png') }}">
<link rel="apple-touch-icon" sizes="180x180" href="{{ url_for('static', filename='assets/stashr-180.png') }}">
<link rel="apple-touch-icon" sizes="167x167" href="{{ url_for('static', filename='assets/stashr-167.png') }}">
<meta name="apple-mobile-web-app-title" content="Stashr">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<script src="{{ url_for('static', filename='js/stashr.js') }}"></script>
<script src="{{ url_for('static', filename='js/vue.dev.js') }}"></script>
</head>
<body class="text-center login">
<main class="stashr-signin">
<div class="card bg-primary px-3 shadow">
<form action="{{ url_for('login_page') }}" method="post" id="login_form">
{{ login_form.csrf_token }}
<img class="stashr-logo" src="{{ url_for('static', filename='assets/stashr.svg') }}" w="170" h="170">
{% if login_form.errors %}
<div class="notification bg-danger rounded m-3 text-white" role="alert" id='error_container'>
{% for field_name, field_errors in login_form.errors|dictsort if field_errors %}
@ -20,24 +42,41 @@
</ul>
{% endfor %}
</div>
{% else %}
{% endif %}
{{ login_form.csrf_token }}
<div class="mb-3">
{{ login_form.username(class_='input form-control', placeholder='Username') }}
<div class="form-floating">
{{ login_form.username(class_='input form-control', type='username', placeholder='Username') }}
{{ login_form.username.label }}
</div>
<div class="mb-3">
<div class="form-floating">
{{ login_form.password(class_='input form-control', type='password', placeholder='Password') }}
{{ login_form.password.label }}
</div>
<div class="mb-3">
{{ login_form.remember_me() }} Remember Me
</div>
<div class="mb-3">
{{ login_form.login_button(class_='btn btn-outline-success') }}
<a href="{{ url_for('forgot_page') }}" class="btn btn-outline-danger">Forgot Password</a>
<div class="checkbox mb-3">
<label class="text-white">
{{ login_form.remember_me() }} Remember Me
</label>
</div>
{{ login_form.login_button(class_='w-100 btn btn-lg btn-success shadow') }}
<a class="text-white" href="#">Forgot your password?</a>
</form>
</div>
</div>
</main>
<script type="text/javascript">
Vue.use(VueToast);
{% with flashes = get_flashed_messages(with_categories=true) %}
{% if flashes %}
{% for category, message in flashes %}
stashrToast('{{ message }}', '{{ category }}');
{% endfor %}
{% endif %}
{% endwith %}
</script>
{% endblock %}
</body>
</html>

@ -73,33 +73,34 @@ Vue.component('modals', {
Vue.component('release-item', {
props: ['release'],
template: `
<li class='stashr-cover_size m-2'
<li class='stashr-item_container m-2'
@mouseover="hover = true"
@mouseleave="hover = false"
>
<div class='stashr-poster_wrapper rounded'>
<div class="stashr-badge_tl badge rounded-pill bg-info border">#[[ release.new_release_issue_number ]]</div>
<div class="stashr-badge_br badge rounded-pill border px-2" :class="className" >[[ subText ]]</div>
<div class="stashr-poster_container border rounded">
<div class="stashr-overlay_bottom w-100 center" v-if="hover">
<a data-bs-toggle="modal" data-bs-target="#modalSubscription" v-on:click="this.changeModal">[[ release.new_release_comic_name ]]</a>
>
<div class="stashr-poster_container border border-dark rounded-3" data-bs-toggle="modal" data-bs-target="#modalSubscription" v-on:click="this.changeModal">
<div class="stashr-badge_top_left bg-info text-white px-1">
#[[ release.new_release_issue_number ]]
</div>
<div class="stashr-badge_bottom_right bg-dark">
<span class="fa-stack p-0">
<i class="fas fa-university fa-stack-1x" :class="className"></i>
<i class="fas fa-slash fa-stack-1x text-danger" v-if="!this.release.status"></i>
</span>
</div>
<img class="stashr-background w-100" loading="eager" src="/static/assets/cover.svg" />
<img class="stashr-poster w-100" loading="lazy" :src="release.new_release_image_url" />
<div class="stashr-overlay_bottom w-100 text-center shadow" v-if="hover">
[[ release.new_release_comic_name ]]
</div>
<img class="stashr-poster_background w-100" loading="eager" src="/static/assets/cover.svg" />
<a class="stashr-poster_link" data-bs-toggle="modal" data-bs-target="#modalSubscription" v-on:click="this.changeModal">
<img class="w-100" loading="lazy" :src="release.new_release_image_url" />
</a>
</div>
</div>
</li>
`,
computed: {
className() {
let classname = 'bg-danger';
let classname = 'text-danger';
try {
if (this.release.status) {
classname = 'bg-success';
classname = 'text-success';
}
} finally {
return classname;
@ -152,9 +153,16 @@ Vue.component('releases', {
props: ['releases'],
template: `
<div>
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-top shadow">
<div class="row w-100 m-0 p-3">
<input type="text" v-model="search" class="form-control" placeholder="Search New Releases..." />
</div>
</div>
<!--
<div class="mb-3 px-5">
<input type="text" v-model="search" class="form-control" placeholder="Search New Releases..." />
</div>
-->
<ul class="d-flex flex-wrap w-100 m-0 p-0 py-2 justify-content-center">
<release-item
v-for="release in filteredList"

@ -3,7 +3,7 @@
<title>Stashr - Reading - {{ comic_name }}</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.css') }}">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/stashr.css') }}">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/stashr-read.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/all.css') }}">
@ -18,14 +18,14 @@
</head>
<body class="stashrRead">
<body class="stashr-reader">
<div id="app">
<swiper v-bind:slides='slides' v-swiper='$options.swiperOptions'></swiper>
<modal></modal>
</div>
<div class="stashr-button_container_reader p-2">
<div class="stashr-button_container p-2">
<button type="button" class="btn btn-link" data-bs-toggle="modal" data-bs-target="#modalRead">
<i class="text-white fas fa-bars fa-2x"></i>
</button>

@ -53,7 +53,7 @@ Vue.component('modals', {
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body center">
<div class="modal-body text-center">
<a :href="'/read/'+issue.issue_id" id="readRead" class="btn btn-success">
<i class="fas fa-book-open"></i>
Read
@ -99,14 +99,29 @@ Vue.component('modals', {
Vue.component('issues', {
props: ['issues'],
template: `
<ul class="d-flex flex-wrap w-100 m-0 p-0 justify-content-center" v-sortable="$options.sortOptions" @sorted='handleSorted'>
<issue
v-for="issue in issues"
v-bind:issue="issue"
v-bind:key="issue.issue_id"
></issue>
</ul>
<div>
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-top shadow">
<div class="row w-100 m-0 p-3">
<input type="text" v-model="search" class="form-control" placeholder="Search Reading List..." />
</div>
</div>
<ul class="d-flex flex-wrap w-100 m-0 p-0 justify-content-center" v-sortable="$options.sortOptions" @sorted='handleSorted'>
<issue
v-for="issue in filteredList"
v-bind:issue="issue"
v-bind:key="issue.issue_id"
></issue>
</ul>
</div>
`,
data() { return { loading: true, search: '', } },
computed: {
filteredList() {
return this.issues.filter(issue => {
return issue.volume.volume_name.toLowerCase().includes(this.search.toLowerCase())
})
},
},
methods: {
handleSorted(event) {
app.handleSorted(event)
@ -124,30 +139,30 @@ Vue.component('issues', {
Vue.component('issue', {
props: ['issue'],
template: `
<li class='stashr-cover_size m-2 js-sortable-block'
<li class='stashr-item_container m-2 js-sortable-block'
@mouseover="hover = true"
@mouseleave="hover = false"
>
<div class='stashr-poster_wrapper rounded'>
<div class="stashr-badge_tl badge rounded-pill bg-dark border js-drag-handle">
<i class="fas fa-arrows-alt"></i>
</div>
<div class="stashr-badge_br badge rounded-pill bg-dark border">
<i class="fas fa-eye" v-bind:class="statusRead" v-on:click="toggleRead"></i>
</div>
<!--
<div class="stashr-badge_tr badge badge-pill badge-info border">[[ issue.reading_list_position ]]</div>
-->
<div class="stashr-poster_container border rounded">
<div class="stashr-overlay_bottom w-100 center" v-if="hover">
<a href="#">[[ issue.volume.volume_name ]] #[[ issue.issue_number ]]</a>
>
<div class="stashr-poster_container border border-dark rounded-3 js-drag-handle">
<div class="stashr-badge_top_left bg-dark">
<span class="fa-stack p-0">
<i class="fas fa-arrows-alt fa-stack-1x text-white"></i>
</span>
</div>
<div class="stashr-badge_bottom_right bg-dark">
<span class="fa-stack p-0" v-on:click="toggleRead">
<i class="fas fa-eye fa-stack-1x" :class="statusRead"></i>
<i class="fas fa-slash fa-stack-1x text-danger" v-if="!this.issue.read_status[0].read_status"></i>
</span>
</div>
<div data-bs-toggle="modal" data-bs-target="#modalRead" v-on:click="this.changeModal">
<img class="stashr-background w-100" loading="eager" src="/static/assets/cover.svg" />
<img class="stashr-poster w-100" loading="lazy" v-bind:src="'/images/issues/'+issue.issue_id+'.jpg'" />
</div>
<div class="stashr-overlay_bottom w-100 text-center shadow" v-if="hover" data-bs-toggle="modal" data-bs-target="#modalRead" v-on:click="this.changeModal">
[[ issue.volume.volume_name ]] #[[ issue.issue_number ]]
</div>
<img class="stashr-poster_background w-100" loading="eager" src="/static/assets/cover.svg" />
<a class="stashr-poster_link" data-bs-toggle="modal" data-bs-target="#modalRead" v-on:click="this.changeModal">
<img class="w-100" loading="lazy" v-bind:src="'/images/issues/'+issue.issue_id+'.jpg'"/>
</a>
</div>
</div>
</li>
`,
computed: {

@ -1,45 +0,0 @@
{% extends "base.html" %}
{% block content %}
<div class="d-flex justify-content-center flex-wrap w-80">
<div class='position-relative my-2'>
<img class="border rounded-circle my-3" src="{{ url_for('static', filename='assets/stashr.svg') }}" width="200" height="200" />
</div>
<div class="bg-light m-2 px-2 rounded stashr-series_info text-center text-lg-start py-3 px-5">
<h5 class="text-center">Register</h5>
<hr />
<form action="{{ url_for('register_page') }}" method="post" id="registration_form">
{% if registration_form.errors %}
<div class="notification bg-danger rounded m-3 text-white" role="alert" id='error_container'>
{% for field_name, field_errors in registration_form.errors|dictsort if field_errors %}
<ul>
{% for error in field_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endfor %}
</div>
{% else %}
{% endif %}
{{ registration_form.csrf_token }}
<div class="mb-3">
{{ registration_form.username(class_='input form-control', placeholder='Username') }}
</div>
<div class="mb-3">
{{ registration_form.email(class_='input form-control', placeholder='E-Mail') }}
</div>
<div class="mb-3">
{{ registration_form.reg_password(class_='input form-control', placeholder='Password', type='password') }}
</div>
<div class="mb-3">
{{ registration_form.confirm_reg_password(class_='input form-control', placeholder='Confirm Password', type='password') }}
</div>
<div class="mb-3">
{{ registration_form.register_button(class_='btn btn-outline-success') }}
</div>
</form>
</div>
</div>
{% endblock %}

@ -59,8 +59,7 @@ Vue.component('modal', {
<div class="modal-body">
<div class="row">
<div class="col-3">
<div class="stashr-poster_container border rounded">
<img class="stashr-poster_background w-100" loading="eager" src="/static/assets/folder.svg" />
<div class="stashr-item_container border rounded">
<a class="stashr-poster_link">
<img class="w-100" :src="[[ match['image']['small_url'] ]]" loading="lazy"/>
</a>
@ -81,14 +80,6 @@ Vue.component('modal', {
</div>
</div>
</div>
<!--
<select class="form-select" aria-label="Default select example">
<option selected>Open this select menu</option>
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
-->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
@ -131,34 +122,24 @@ Vue.component('modal', {
Vue.component('directory', {
props: ['directory'],
template: `
<li class='stashr-cover_size m-2'
<li class='stashr-item_container m-2'
@mouseover="hover = true"
@mouseleave="hover = false"
>
<div class='stashr-poster_wrapper rounded' v-on:click="changeModal()">
<div class="stashr-badge_tl badge badge-pill badge-info border"></div>
<div class="stashr-badge_br badge badge-pill border"></div>
<div class="stashr-poster_container border rounded">
<div class="stashr-check_box p-1">
<a @click='changeChecked'>
<i :class='checkedStatus'></i>
<!--
<i class="text-info fa-2x fas fa-circle"></i>
<i class="text-success fa-2x fas fa-check-circle"></i>
-->
</a>
</div>
<div class="stashr-overlay_bottom w-100 center">
<span>[[ directory.scrape_directory ]]</span>
<div class="stashr-poster_container border border-dark rounded-3">
<div class="stashr-badge_top_right bg-dark" @click='changeChecked'>
<span class="fa-stack p-0">
<i class="fa-stack-1x" :class='checkedStatus'></i>
</span>
</div>
<div data-bs-toggle="modal" data-bs-target="#modalScrape">
<img class="stashr-poster_background w-100" loading="eager" src="/static/assets/folder.svg" />
<a class="stashr-poster_link">
<img class="w-100" :src="imageURL" loading="lazy"/>
</a>
<img class="stashr-background w-100" loading="eager" src="/static/assets/folder.svg" />
<img class="stashr-poster w-100" loading="lazy" :src="imageURL" />
</div>
<div class="stashr-overlay_bottom w-100 text-center shadow">
[[ directory.scrape_directory ]]
</div>
</div>
</div>
</li>
`,
computed: {
@ -168,9 +149,9 @@ Vue.component('directory', {
})[0]['image']['small_url']
},
checkedStatus() {
string = `text-info fa-2x fas fa-circle`
string = `text-info fas fa-circle`
if(this.directory.scrape_add) {
string = `text-success fa-2x fas fa-check-circle`
string = `text-success fas fa-check-circle`
}
return string
},
@ -204,8 +185,10 @@ Vue.component('directories', {
props: ['directories'],
template: `
<div>
<div class="mb-3 px-5">
<input type="text" v-model="search" class="form-control" placeholder="Search Folders..." />
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-md-top shadow">
<div class="row w-100 m-0 p-3">
<input type="text" v-model="search" class="form-control" placeholder="Search Folders..." />
</div>
</div>
<ul class="d-flex flex-wrap w-100 m-0 p-0 justify-content-center">
<directory

@ -66,19 +66,17 @@ Vue.component('modals', {
Vue.component('volume-item', {
props: ['volume'],
template: `
<li class='stashr-cover_size m-2'
<li class='stashr-item_container m-2'
@mouseover="hover = true"
@mouseleave="hover = false"
>
<div class='stashr-poster_wrapper rounded'>
<div class="stashr-poster_container border rounded">
<div class="stashr-overlay_bottom w-100 center" v-if="hover">
<a href="#">[[ volume.name ]]</a>
</div>
<img class="stashr-poster_background w-100" loading="eager" src="/static/assets/cover.svg" />
<a class="stashr-poster_link" data-bs-toggle="modal" data-bs-target="#modalVolume" :data-volume_id=volume.id v-on:click="this.changeModal">
<img class="w-100" loading="lazy" :src="volume.image.medium_url"/>
</a>
>
<div class="stashr-poster_container border border-dark rounded-3" data-bs-toggle="modal" data-bs-target="#modalVolume" :data-volume_id=volume.id v-on:click="this.changeModal">
<div class="stashr-poster_wrapper">
<img class="stashr-background w-100" loading="eager" src="/static/assets/cover.svg" />
<img class="stashr-poster w-100" loading="lazy" :src="volume.image.medium_url" />
</div>
<div class="stashr-overlay_bottom w-100 text-center shadow" v-if="hover">
[[ volume.name ]]
</div>
</div>
</li>
@ -109,28 +107,17 @@ Vue.component('volumes', {
props: ['volumes'],
template: `
<div>
<!--
<div class="form-group">
<input type="text" v-model="search" class="form-control" placeholder="Search Volumes..." />
</div>
<button @click="$emit('do-search', search)">Search</button>
<ul class="d-flex flex-wrap w-100 m-0 p-0 justify-content-center">
<volume-item
v-for="volume in filteredList"
v-bind:volume="volume"
v-bind:key="volume.id"
></volume-item>
</ul>
-->
<div class="input-group mb-3 px-5">
<input type="text" class="form-control" placeholder="Search Volumes..." aria-label="Recipient's username" aria-describedby="basic-addon2" v-model="search">
<div class="input-group-append">
<button class="btn btn-success" type="button" @click="$emit('do-search', search)">
Search
</button>
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-top shadow">
<div class="m-0 p-3 input-group">
<input type="text" v-model="search" class="form-control" placeholder="Search Volumes..." />
<div class="input-group-append">
<button class="btn btn-success" type="button" @click="$emit('do-search', search)">
Search
</button>
</div>
</div>
</div>
<ul class="d-flex flex-wrap w-100 m-0 p-0 justify-content-center">
<ul class="d-flex flex-wrap w-100 m-0 p-0 py-2 justify-content-center">
<volume-item
v-for="volume in filteredList"
v-bind:volume="volume"

@ -1,61 +1,177 @@
{% extends "base.html" %}
{% block content %}
<div class="row w-100 m-0">
<div class="col-12 col-md-10 offset-md-1 bg-light rounded p-2">
<div class="row">
{% if current_user.role == 'admin' %}
<div class="col-12 col-md-3 col-lg-2 p-3">
<ul class="nav flex-column nav-pills">
<li class="nav-item">
<a class="nav-link{% if request.path == url_for('settings_app_page') %} active{% endif %}" href="{{ url_for('settings_app_page') }}">
<i class="fa fa-cogs"></i>
Application
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.path == url_for('settings_directories_page') %} active{% endif %}" href="{{ url_for('settings_directories_page') }}">
<i class="fas fa-folder"></i>
Directories
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.path == url_for('settings_mail_page') %} active{% endif %}" href="{{ url_for('settings_mail_page') }}">
<i class="fa fa-envelope"></i>
Mail
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.path == url_for('settings_tasks_page') %} active{% endif %}" href="{{ url_for('settings_tasks_page') }}">
<i class="fa fa-tasks"></i>
Tasks
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.path == url_for('settings_all_users_page') %} active{% endif %}" href="{{ url_for('settings_all_users_page') }}">
<i class="fa fa-users"></i>
Users
</a>
</li>
<li class="nav-item">
<a class="nav-link{% if request.path == url_for('settings_plugins_page') %} active{% endif %}" href="{{ url_for('settings_plugins_page') }}">
<i class="fa fa-plug"></i>
Plugins
</a>
</li>
{{ emit_tep('settings_menu') }}
</ul>
</div>
{% endif %}
<div class="col-12{% if current_user.role=='admin' %} col-md-9 col-lg-10{% endif %} my-2">
{% block settings_pane %}{% endblock %}
</div>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, shrink-to-fit=no">
<title>Stashr - {{ title }}</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/bootstrap.css') }}">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/new_base.css') }}">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/stashr.css') }}">
<link href="https://cdn.jsdelivr.net/npm/vue-toast-notification/dist/theme-sugar.css" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='css/all.css') }}">
<link href="https://fonts.googleapis.com/css?family=Grand+Hotel" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Oswald" rel="stylesheet">
<link rel="manifest" href="{{ url_for('static', filename='manifest/manifest.json') }}">
<link rel="apple-touch-icon" href="touch-icon-iphone.png">
<link rel="apple-touch-icon" sizes="152x152" href="{{ url_for('static', filename='assets/stashr-152.png') }}">
<link rel="apple-touch-icon" sizes="180x180" href="{{ url_for('static', filename='assets/stashr-180.png') }}">
<link rel="apple-touch-icon" sizes="167x167" href="{{ url_for('static', filename='assets/stashr-167.png') }}">
<meta name="apple-mobile-web-app-title" content="Stashr">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<script src="{{ url_for('static', filename='js/stashr.js') }}"></script>
<script src="{{ url_for('static', filename='js/vue.dev.js') }}"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-toast-notification"></script>
{% block header_script_files %}{% endblock %}
{{ emit_tep('base_page_header_script_files') }}
</head>
<body>
<div class="container-fluid d-flex min-vh-100 m-0 p-0">
<div class="stashr-menu d-flex flex-column p-3 text-white bg-dark text-center min-vh-100 sticky-top overflow-auto" id="sideMenu" style="width:280px;">
<div class="d-flex flex-row">
<a class="navbar-brand" href="{{ url_for('index_page') }}">
<img class="border rounded-circle" src="{{ url_for('static', filename='assets/stashr.svg') }}" width="40" height="40" />
<span class="stashr-project_title">Stashr</span>
</a>
</div>
<hr/>
<ul class="nav nav-pills flex-column mb-auto text-start">
{% if current_user.role == 'admin' %}
<li>
<a class="nav-link text-white{% if request.path == url_for('settings_app_page') %} active{% endif %}" href="{{ url_for('settings_app_page') }}">
<i class="fa fa-cogs"></i>
Application
</a>
</li>
<li>
<a class="nav-link text-white{% if request.path == url_for('settings_directories_page') %} active{% endif %}" href="{{ url_for('settings_directories_page') }}">
<i class="fas fa-folder"></i>
Directories
</a>
</li>
<li class="nav-item">
<a class="nav-link text-white{% if request.path == url_for('settings_mail_page') %} active{% endif %}" href="{{ url_for('settings_mail_page') }}">
<i class="fa fa-envelope"></i>
Mail
</a>
</li>
<li class="nav-item">
<a class="nav-link text-white{% if request.path == url_for('settings_tasks_page') %} active{% endif %}" href="{{ url_for('settings_tasks_page') }}">
<i class="fa fa-tasks"></i>
Tasks
</a>
</li>
<li class="nav-item">
<a class="nav-link text-white{% if request.path == url_for('settings_all_users_page') %} active{% endif %}" href="{{ url_for('settings_all_users_page') }}">
<i class="fa fa-users"></i>
Users
</a>
</li>
<li class="nav-item">
<a class="nav-link text-white{% if request.path == url_for('settings_plugins_page') %} active{% endif %}" href="{{ url_for('settings_plugins_page') }}">
<i class="fa fa-plug"></i>
Plugins
</a>
</li>
{{ emit_tep('settings_menu') }}
{% endif %}
</ul>
<hr />
<ul class="nav nav-pills d-flex flex-column text-start">
<li>
<a href="{{ url_for('settings_single_user_page', user_id=current_user.id) }}" class="nav-link text-white">
<i class="fa fa-user"></i>
{{ current_user.username }}
</a>
</li>
{% if current_user.role == 'admin' %}
<li>
<a href="{{ url_for('settings_page') }}" class="nav-link text-white">
<i class="fas fa-cogs"></i>
Settings
</a>
</li>
{% endif %}
<li>
<a href="{{ url_for('logout_page') }}" class="nav-link text-white">
<i class="fas fa-sign-out-alt"></i>
Logout
</a>
</li>
</ul>
</div>
<div class="d-flex flex-column w-100 m-0 px-0 min-vh-100 overflow-auto bg-light">
<!-- v HEADER v -->
<!-- ^ HEADER ^ -->
<!-- v CONTENT v -->
{% block settings_pane %}{% endblock %}
<!-- ^ CONTENT ^ -->
<!-- v BUTTON CONTAINER v -->
<div class="stashr-button_container p-2">
</div>
<div class="stashr-menu_button_container p-2">
</div>
<div class="d-flex w-100 stashr-footer p-2 new-stashr-button_container">
<button type="button" class="btn btn-info btn-circle btn-md d-md-none" data-bs-toggle="tooltip" data-bs-placement="top" title="Add Volume from Comicvine" onclick="toggleMenu()">
<i class="fas fa-ellipsis-v fa-2x"></i>
</button>
{% block button_container %}{% endblock %}
</div>
<!-- ^ BUTTON CONTAINER ^ -->
</div>
</div>
<!-- v MODALS v -->
{% block modals %}{% endblock %}
{{ emit_tep('base_page_modals') }}
<!-- ^ MODALS ^ -->
<!-- v FOOTER SCRIPT INCLUDES v -->
<script src="{{ url_for('static', filename='js/bootstrap.bundle.js') }}"></script>
{% block footer_script_files %}{% endblock %}
{{ emit_tep('base_page_footer_script_files') }}
<!-- ^ FOOTER SCRIPT INCLUDES ^ -->
<!-- v FOOTER SCRIPT v -->
<script type="text/javascript">
Vue.use(VueToast);
// Flashes
{% with flashes = get_flashed_messages(with_categories=true) %}
{% if flashes %}
{% for category, message in flashes %}
stashrToast('{{ message }}', '{{ category }}');
{% endfor %}
{% endif %}
{% endwith %}
window.addEventListener("load", () => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("service-worker.js");
}
});
{% block script %}{% endblock %}
</script>
<!-- ^ FOOTER SCRIPT ^ -->
{% endblock %}
</body>
</html>

@ -18,9 +18,6 @@ Vue.component('user',{
props:['user'],
template: `
<tr>
<!--
:href="'{{ url_for('single_publisher_page', publisher_id='PUBLISHERID') }}'.replace('PUBLISHERID', publisher.publisher_id)"
-->
<td><a :href="'{{ url_for('settings_single_user_page', user_id='USERID') }}'.replace('USERID', user.id)">[[ user.id ]]</a></td>
<td><a :href="'{{ url_for('settings_single_user_page', user_id='USERID') }}'.replace('USERID', user.id)">[[ user.username ]]</a></td>
<td>[[ user.role ]]</td>
@ -33,18 +30,19 @@ Vue.component('user',{
Vue.component('users', {
props:['users'],
template: `
<div class="row r-10 m-2">
<div class="col col-12">
<div class="row">
<div class="col-sm-12 col-md-6 text-center text-md-start">
<div>
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-top shadow">
<div class="row w-100 m-0 p-3">
<div class="col-sm-12 col-md-6 text-center text-md-start text-white ">
<h2>Users</h2>
</div>
<div class="col-sm-12 col-md-6 text-center text-md-end">
<a type="button" class="btn btn-info" href="{{ url_for('settings_new_user_page') }}">New User</a>
</div>
</div>
<hr />
<div class="row">
</div>
<div class="d-grid w-100 m-0 p-0">
<div class="row w-100 m-0 p-3">
<table class="table">
<thead>
<tr>

@ -18,18 +18,19 @@
Vue.component('settings', {
props: ['settings'],
template: `
<div class="row r-10 m-2">
<div class="col col-12">
<div class="row">
<div class="col-sm-12 col-md-6 text-center text-md-start">
<div>
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-top shadow">
<div class="row w-100 m-0 p-3">
<div class="col-sm-12 col-md-6 text-center text-md-start text-white ">
<h2>Application</h2>
</div>
<div class="col-sm-12 col-md-6 text-center text-md-end">
<button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#settingsModal">Modify Settings</button>
</div>
</div>
<hr />
<div class="row">
</div>
<div class="d-grid w-100 m-0 p-0">
<div class="row w-100 m-0 p-3">
<table class="table table-striped">
<tbody>
<tr>

@ -18,18 +18,19 @@
Vue.component('settings', {
props: ['settings'],
template: `
<div class="row r-10 m-2">
<div class="col col-12">
<div class="row">
<div class="col-sm-12 col-md-6 text-center text-md-start">
<div>
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-top shadow">
<div class="row w-100 m-0 p-3">
<div class="col-sm-12 col-md-6 text-center text-md-start text-white ">
<h2>Directories</h2>
</div>
<div class="col-sm-12 col-md-6 text-center text-md-end">
<button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#settingsModal">Modify Settings</button>
</div>
</div>
<hr />
<div class="row">
</div>
<div class="d-grid w-100 m-0 p-0">
<div class="row w-100 m-0 p-3">
<table class="table table-striped">
<tbody>
<tr>

@ -18,18 +18,19 @@
Vue.component('settings', {
props: ['settings'],
template: `
<div class="row r-10 m-2">
<div class="col col-12">
<div class="row">
<div class="col-sm-12 col-md-6 text-center text-md-start">
<div>
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-top shadow">
<div class="row w-100 m-0 p-3">
<div class="col-sm-12 col-md-6 text-center text-md-start text-white ">
<h2>Mail Settings</h2>
</div>
<div class="col-sm-12 col-md-6 text-center text-md-end">
<button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#settingsModal">Modify Settings</button>
</div>
</div>
<hr />
<div class="row">
</div>
<div class="d-grid w-100 m-0 p-0">
<div class="row w-100 m-0 p-3">
<table class="table table-striped">
<tbody>
<tr>

@ -2,55 +2,53 @@
{% block settings_pane %}
<div id="app">
<div class="row r-10 m-2">
<div class="col col-12">
<div class="row">
<div class="col-sm-12 col-md-6 text-center text-md-start">
<h2>New User</h2>
</div>
</div>
<hr />
<div class="row">
<form method="POST">
{{ new_user_form.csrf_token }}
<div class="mb-3">
{{ new_user_form.username.label }}
{{ new_user_form.username(class_='form-control', placeholder=new_user_form.username.label.text) }}
</div>
<div class="mb-3">
{{ new_user_form.email.label }}
{{ new_user_form.email(class_='form-control', placeholder=new_user_form.email.label.text) }}
</div>
<div class="mb-3">
{{ new_user_form.password.label }}
{{ new_user_form.password(type='password', class_='form-control', placeholder=new_user_form.password.label.text) }}
</div>
<div class="mb-3">
{{ new_user_form.confirm_password.label }}
{{ new_user_form.confirm_password(type='password', class_='form-control', placeholder=new_user_form.confirm_password.label.text) }}
</div>
<div class="mb-3">
{{ new_user_form.role.label }}
{{ new_user_form.role(class_='form-control') }}
</div>
<div class="mb-3">
{{ new_user_form.age_rating.label }}
{{ new_user_form.age_rating(class_='form-control') }}
</div>
<div class="modal-footer">
{{ new_user_form.new_user_button(class_='btn btn-success') }}
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
</div>
</form>
</div>
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-top shadow">
<div class="row w-100 m-0 p-3">
<div class="col-sm-12 col-md-6 text-center text-md-start text-white ">
<h2>New User</h2>
</div>
<div class="col-sm-12 col-md-6 text-center text-md-end">
<button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#settingsModal">Modify Settings</button>
</div>
</div>
</div>
<div class="d-grid w-100 m-0 p-0">
<div class="row w-100 m-0 p-3">
<form method="POST">
{{ new_user_form.csrf_token }}
<div class="mb-3">
{{ new_user_form.username.label }}
{{ new_user_form.username(class_='form-control', placeholder=new_user_form.username.label.text) }}
</div>
<div class="mb-3">
{{ new_user_form.email.label }}
{{ new_user_form.email(class_='form-control', placeholder=new_user_form.email.label.text) }}
</div>
<div class="mb-3">
{{ new_user_form.password.label }}
{{ new_user_form.password(type='password', class_='form-control', placeholder=new_user_form.password.label.text) }}
</div>
<div class="mb-3">
{{ new_user_form.confirm_password.label }}
{{ new_user_form.confirm_password(type='password', class_='form-control', placeholder=new_user_form.confirm_password.label.text) }}
</div>
<div class="mb-3">
{{ new_user_form.role.label }}
{{ new_user_form.role(class_='form-control') }}
</div>
<div class="mb-3">
{{ new_user_form.age_rating.label }}
{{ new_user_form.age_rating(class_='form-control') }}
</div>
<div class="modal-footer">
{{ new_user_form.new_user_button(class_='btn btn-success') }}
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
</div>
</form>
</div>
</div>
{% endblock %}

@ -182,10 +182,10 @@ Vue.component('plugin',{
Vue.component('plugins',{
props: ['plugins'],
template: `
<div class="row w-100">
<div class="col col-12">
<div class="row w-100">
<div class="col-sm-12 col-md-6 text-center text-md-start">
<div>
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-top shadow">
<div class="row w-100 m-0 p-3">
<div class="col-sm-12 col-md-6 text-center text-md-start text-white ">
<h2>Plugins</h2>
</div>
<div class="col-sm-12 col-md-6 text-center text-md-end">
@ -193,8 +193,9 @@ Vue.component('plugins',{
<button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#modalUpload">Install Plugin</button>
</div>
</div>
<hr />
<div class="row">
</div>
<div class="d-grid w-100 m-0 p-0">
<div class="row w-100 m-0 p-3">
<ul class="d-flex flex-wrap w-100 m-0 p-0 justify-content-center">
<plugin
v-for="plugin in plugins"
@ -204,7 +205,7 @@ Vue.component('plugins',{
</ul>
</div>
</div>
</div>
</div>
`,
methods: {
restartServer() {

@ -18,11 +18,11 @@
Vue.component('user', {
props: ['user'],
template: `
<div class="row r-10 m-2">
<div class="col col-12">
<div class="row">
<div class="col-sm-12 col-md-6 text-center text-md-start">
<h2>User - [[ user.username ]]</h2>
<div>
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-top shadow">
<div class="row w-100 m-0 p-3">
<div class="col-sm-12 col-md-6 text-center text-md-start text-white ">
<h2>[[ user.username ]]</h2>
</div>
<div class="col-sm-12 col-md-6 text-center text-md-end">
{% if current_user.id|int == user_id|int %}
@ -36,12 +36,12 @@ Vue.component('user', {
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteUserModal">Delete User</button>
{% endif %}
{% endif %}
<br />
<button type="button" class="btn btn-info my-1" data-bs-toggle="modal" data-bs-target="#editUserModal">Edit Account</button>
</div>
</div>
<hr />
<div class="row">
</div>
<div class="d-grid w-100 m-0 p-0">
<div class="row w-100 m-0 p-3">
<table class="table table-striped">
<tbody>
<tr>

@ -5,41 +5,43 @@
{% endblock %}
{% block settings_pane %}
<div class="row r-10 m-2">
<div class="col col-12">
<div class="row">
<div class="col-sm-12 col-md-6 text-center text-md-start">
<h2>Tasks</h2>
</div>
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-top shadow">
<div class="row w-100 m-0 p-3">
<div class="col-sm-12 col-md-6 text-center text-md-start text-white ">
<h2>Tasks</h2>
</div>
<hr />
<div class="row">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">TASK</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Update New Releases</th><td><i class="fas fa-play"></i></td>
</tr>
<tr>
<th scope="row">Update New Release Subscriptions</th><td><i class="fas fa-play"></i></td>
</tr>
<tr>
<th scope="row">Clean Database</th><td><i class="fas fa-play"></i></td>
</tr>
<tr>
<th scope="row">Restart Server</th><td><a onclick="restartServer()"><i class="fas fa-play"></i></a></td>
</tr>
{{ emit_tep('settings_page_tasks_task') }}
</tbody>
</table>
<div class="col-sm-12 col-md-6 text-center text-md-end">
<button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#settingsModal">Modify Settings</button>
</div>
</div>
</div>
<div class="d-grid w-100 m-0 p-0">
<div class="row w-100 m-0 p-3">
<table class="table table-striped">
<thead>
<tr>
<th scope="col">TASK</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Update New Releases</th><td><i class="fas fa-play"></i></td>
</tr>
<tr>
<th scope="row">Update New Release Subscriptions</th><td><i class="fas fa-play"></i></td>
</tr>
<tr>
<th scope="row">Clean Database</th><td><i class="fas fa-play"></i></td>
</tr>
<tr>
<th scope="row">Restart Server</th><td><a onclick="restartServer()"><i class="fas fa-play"></i></a></td>
</tr>
{{ emit_tep('settings_page_tasks_task') }}
</tbody>
</table>
</div>
</div>
{% endblock %}
{% block script %}

@ -51,7 +51,7 @@ Vue.component('issue-modals', {
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body center">
<div class="modal-body text-center">
<a :href="'/read/'+issue.issue_id" id="actionRead" class="btn btn-success my-1">
<i class="fas fa-book-open"></i>
Read
@ -201,87 +201,51 @@ Vue.component('collection-modals', {
Vue.component('collection-jumbo', {
props: ['collection'],
template: `
<div class="row w-100 m-0">
<div class='col-12 col-md-10 offset-md-1 p-2'>
<div class='row'>
<div class='col-12 col-md-2 p-2'>
<div class='row'>
<div class='col-10 offest-1'>
<!-- START POSTER -->
<div class="new-stashr_poster-wrapper border rounded">
<div class="stashr-poster_container rounded">
<img class="stashr-poster_background w-100" src="/static/assets/cover.svg" id="poster-bg">
<a class="rounded stashr-poster_link">
<img class="rounded stashr-poster_image w-100" id="lazy-img" v-bind:src="'/images/issues/'+collection.collection_cover_image+'.jpg'" onerror="this.src='/static/assets/cover.svg'">
</a>
</div>
</div>
<!-- END POSTER -->
</div>
</div>
</div>
<div class="col-12 col-md-10 p-2">
<div class="row w-100 bg-light rounded m-0 p-4">
<div class="col-12 col-md-10">
<div class="row">
<div class="col text-center text-md-start">
<h1 class="stashr-series_title">[[ collection.collection_name ]]</h1>
</div>
</div>
<div class="row text-center text-md-start">
<div class="col-12">
<!-- START BADGES -->
<span class="badge mx-1" :class="statusClass">[[ statusText ]]</span>
<span class="badge bg-info mx-1">[[ collection.user.username ]]</span>
{{ emit_tep('single_collection_page_badge_row', collection=collection) }}
<!-- END BADGES -->
</div>
</div>
<div class="row">
<div class="col text-center text-md-start">
<!-- START BUTTON -->
<button class="btn btn-primary m-1" type="button" data-bs-toggle="modal" data-bs-target="#modalInfo" :data-collection_name="collection.collection_name" :data-collection_description="collection.collection_description">
DESCRIPTION
</button>
{{ emit_tep('single_collection_page_button_row', collection=collection) }}
<!-- END BUTTON -->
</div>
</div>
</div>
<div class="col-12 col-md-2 text-center text-md-right">
<!-- START ACTIONS -->
{% if collection.collection_user_id == current_user.id %}
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Actions
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#modalEditCollection">
<i class="fas fa-edit"></i>
Edit Collection
</a>
<a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#modalDeleteCollection">
<i class="fas fa-trash-alt"></i>
Delete Collection
</a>
{{ emit_tep('single_collection_page_action_dropdown', collection=collection) }}
</div>
</div>
{% endif %}
<!-- END ACTIONS -->
</div>
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-md-top shadow">
<div class="row w-100 m-0 p-3">
<div class="col-12 col-md-10 text-center text-md-start">
<span class="fs-1 text-white stashr-series_title">[[ collection.collection_name ]]</span>
<span role="button" data-bs-toggle="modal" data-bs-target="#modalInfo">
<i class="fas fa-info-circle text-secondary"></i>
</span>
<h5>
<span class="badge bg-dark">
<i class="fas fa-circle" v-bind:class="statusClass"></i>
[[ statusText ]]
</span>
<span class="badge bg-dark">
<i class="fas fa-user"></i>
[[ collection.user.username ]]
</span>
</h5>
</div>
<div class="col-12 col-md-2 text-center text-md-end">
{% if collection.collection_user_id == current_user.id %}
<div class="dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
Actions
</button>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuButton">
<a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#modalEditCollection">
<i class="fas fa-edit"></i>
Edit Collection
</a>
<a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#modalDeleteCollection">
<i class="fas fa-trash-alt"></i>
Delete Collection
</a>
</div>
</div>
{% endif %}
</div>
</div>
</div>`,
</div>
`,
computed: {
statusClass() {
let classname = 'bg-danger';
let classname = 'text-danger';
if (this.collection.collection_public) {
classname = 'bg-success';
classname = 'text-success';
};
return classname;
},
@ -325,29 +289,32 @@ Vue.component('issues', {
Vue.component('issue', {
props: ['issue'],
template: `
<li class='stashr-cover_size m-2 js-sortable-block'
<li class='stashr-item_container m-2 js-sortable-block'
@mouseover="hover = true"
@mouseleave="hover = false"
>
<div class='stashr-poster_wrapper rounded'>
{% if collection.collection_user_id == current_user.id %}
<div class="stashr-badge_tl badge rounded-pill bg-dark border js-drag-handle">
<i class="fas fa-arrows-alt"></i>
</div>
{% endif %}
<div class="stashr-badge_br badge rounded-pill bg-dark border">
<i class="fas fa-eye" :class="statusClass"></i>
</div>
<div class="stashr-poster_container border rounded">
<div class="stashr-overlay_bottom w-100 center" v-if="hover">
<a href="#">[[ issue.volume.volume_name ]] #[[ issue.issue_number ]]</a>
<div class="stashr-poster_container border border-dark rounded-3">
{% if collection.collection_user_id == current_user.id %}
<div class="stashr-badge_top_left bg-dark js-drag-handle">
<span class="fa-stack p-0">
<i class="fas fa-arrows-alt fa-stack-1x text-white"></i>
</span>
</div>
{% endif %}
<div class="stashr-badge_bottom_right bg-dark">
<span class="fa-stack p-0">
<i class="fas fa-eye fa-stack-1x" :class="statusClass"></i>
<i class="fas fa-slash fa-stack-1x text-danger" v-if="!this.issue.read_status[0].read_status"></i>
</span>
</div>
<div data-bs-toggle="modal" data-bs-target="#modalAction" @click="changeModal">
<img class="stashr-background w-100" loading="eager" src="/static/assets/cover.svg" />
<img class="stashr-poster w-100" loading="lazy" v-bind:src="'/images/issues/'+ issue.issue_id +'.jpg'" />
</div>
<div class="stashr-overlay_bottom w-100 text-center shadow" v-if="hover" data-bs-toggle="modal" data-bs-target="#modalAction" @click="changeModal">
[[ issue.volume.volume_name ]] #[[ issue.issue_number ]]
</div>
<img class="stashr-poster_background w-100" loading="eager" src="/static/assets/cover.svg" />
<a class="stashr-poster_link" data-bs-toggle="modal" data-bs-target="#modalAction" @click="changeModal">
<img class="w-100" loading="lazy" v-bind:src="'/images/issues/'+ issue.issue_id +'.jpg'"/>
</a>
</div>
</div>
</li>
`,
computed: {

@ -11,7 +11,6 @@
{% block content %}
<div id="app">
<publisher-jumbo v-bind:publisher="publisher"></publisher-jumbo>
<volumes v-bind:volumes='volumes'></volumes>
</div>
@ -31,8 +30,10 @@ Vue.component('volumes', {
props: ['volumes'],
template: `
<div>
<div class="mb-3 px-5">
<input type="text" v-model="search" class="form-control" placeholder="Search Publisher..." />
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-md-top shadow">
<div class="row w-100 m-0 p-3">
<input type="text" v-model="search" class="form-control" placeholder="Search Publisher..." />
</div>
</div>
<ul class="d-flex flex-wrap w-100 m-0 p-0 py-2 justify-content-center">
<volume-item
@ -41,9 +42,6 @@ Vue.component('volumes', {
v-bind:key="volume.volume_id"
></volume-item>
</ul>
<!--
<i class="text-primary fas fa-spinner fa-spin fa-3x" v-if="loading"></i>
-->
</div>
`,
data() { return { loading: true, search: '', } },
@ -60,40 +58,54 @@ Vue.component('volumes', {
Vue.component('volume-item', {
props: ['volume'],
template: `
<li class='stashr-cover_size m-2'
<li class='stashr-item_container m-2'
@mouseover="hover = true"
@mouseleave="hover = false"
>
<div class='stashr-poster_wrapper rounded'>
<div class="stashr-badge_tl badge rounded-pill bg-info border">[[ volume.age_rating[0].rating_short ]]</div>
<div class="stashr-badge_tr badge rounded-pill bg-primary border">[[ volume.volume_have ]]/[[ volume.volume_total ]]</div>
<div class="stashr-badge_br badge rounded-pill border" :class="statusClass">[[ statusWord ]]</div>
<div class="stashr-poster_container border rounded">
<div class="stashr-overlay_bottom w-100 center" v-if="hover">
<a href="#">[[ volume.volume_name ]]</a>
</div>
<img class="stashr-poster_background w-100" loading="eager" src="/static/assets/cover.svg" />
<a class="stashr-poster_link" :href="'/volumes/'+volume.volume_slug">
<img class="w-100" loading="lazy" v-bind:src="'/images/volumes/'+volume.volume_id+'.jpg'" @error="$event.target.src=volume.volume_image_med"/>
>
<div class="stashr-poster_container border border-dark rounded-3">
<div :class="volumeTag"></div>
<div class="stashr-poster_info bg-info text-white px-1">
[[ volume.age_rating[0].rating_short ]]
</div>
<div class="stashr-poster_wrapper">
<a :href="'/volumes/'+volume.volume_slug">
<img class="stashr-background w-100" loading="eager" src="/static/assets/cover.svg" />
<img class="stashr-poster w-100" loading="lazy" v-bind:src="'/images/volumes/'+volume.volume_id+'.jpg'" />
</a>
</div>
<div class="stashr-overlay_bottom w-100 text-center shadow" v-if="hover">
<a class="stashr-link" :href="'/volumes/'+volume.volume_slug">
[[ volume.volume_name ]]
</a>
</div>
<div class="stashr-progress_wrapper w-100">
<div class="progress bg-dark" style="height: 5px;">
<div class="progress-bar" :class="progressColor" role="progressbar" :style="progressStyle" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</div>
</li>
`,
computed: {
statusClass() {
let classname = 'bg-danger';
if(this.volume.volume_status) {
classname = 'bg-success';
};
return classname;
volumeTag() {
let cornertag = '';
if(!this.volume.volume_status) {
cornertag = "stashr-poster_tag bg-danger shadow";
}
return cornertag;
},
progressWidth() {
return (this.volume.volume_have / this.volume.volume_total)*100;
},
progressColor() {
let classname = 'bg-success';
if(this.progressWidth < 100) {
classname = 'bg-warning';
}
return classname
},
statusWord() {
let status = 'ENDED';
if(this.volume.volume_status) {
status = 'ONGOING';
};
return status;
progressStyle() {
return "width: " + this.progressWidth + "%;"
}
},
data() { return { hover: false } },

@ -143,7 +143,7 @@ Vue.component('modals-issue', {
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body center">
<div class="modal-body text-center">
<span v-if="issue.issue_file_status">
<a id="actionHaveRead" class="btn btn-success my-1" :href="'/read/'+issue.issue_id">
<i class="fas fa-book-open"></i>
@ -418,103 +418,75 @@ Vue.component('modals-volume', {
Vue.component('volume-jumbo', {
props: ['volume'],
template: `
<div class="row w-100 m-0">
<div class='col-12 col-md-10 offset-md-1 p-2'>
<div class='row'>
<div class='col-12 col-md-2 p-2'>
<div class='row'>
<div class='col-10 offset-1'>
<!-- START POSTER -->
<div class="new-stashr_poster-wrapper border rounded">
<div class="stashr-poster_container rounded">
<img class="stashr-poster_background w-100" src="/static/assets/cover.svg" id="poster-bg">
<a class="rounded stashr-poster_link">
<img class="rounded stashr-poster_image w-100" id="lazy-img" v-bind:src="'/images/volumes/'+volume.volume_id+'.jpg'" @error="$event.target.src=volume.volume_image_med">
</a>
</div>
</div>
<!-- END POSTER -->
</div>
</div>
</div>
<div class="col-12 col-md-10 p-2">
<div class="row w-100 bg-light rounded m-0 p-4">
<div class="col-12 col-md-10">
<div class="row">
<div class="col text-center text-md-start">
<h1 class="stashr-series_title">[[ volume.volume_name ]]</h1>
</div>
</div>
<div class="row text-center text-md-start">
<div class="col-12">
<!-- START BADGES -->
<span class="badge mx-1" v-bind:class="volumeStatus">[[ statusWord ]]</span>
<span class="badge bg-info mx-1">[[ volume.age_rating[0].rating_long ]]</span>
<span class="badge bg-info mx-1">[[ publisherName ]]</span>
<span class="badge bg-info mx-1">[[ volume.volume_year ]]</span>
<br>
<span class="badge bg-primary mx-1">Digital: [[ volume.volume_have ]]/[[ volume.volume_total ]]</span>
<span class="badge bg-primary mx-1">Physical: [[ ownedNumber ]]/[[ volume.volume_total ]]</span>
<span class="badge bg-primary mx-1">Read: [[ readNumber ]]/[[ volume.volume_total ]]</span>
<a class="badge bg-primary mx-1" :href="volume.volume_url" target="new">ComicVine</a>
<br>
{{ emit_tep('single_volume_page_badge_row', volume_id=volume_id) }}
<!-- END BADGES -->
</div>
</div>
<div class="row">
<div class="col text-center text-md-start">
<!-- START BUTTON -->
<button class="btn btn-primary m-1" type="button" data-bs-toggle="modal" data-bs-target="#modalInfo">
INFORMATION
</button>
{{ emit_tep('single_volume_page_button_row', volume_id=volume_id) }}
<!-- END BUTTON -->
</div>
</div>
</div>
<div class="col-12 col-md-2 text-center text-md-right">
<!-- START NEW ACTIONS -->
<div class="dropdown">
<a class="btn btn-secondary dropdown-toggle" href="#" role="button" id="actionMenu" data-bs-toggle="dropdown" aria-expanded="false">
Actions
</a>
<ul class="dropdown-menu" aria-labelledby="actionMenu">
{% if (current_user.role.lower() == 'admin') or
(current_user.role.lower() == 'librarian') %}
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#modalRatingEdit">
<i class="far fa-sticky-note mx-1"></i>
Edit Rating
</a></li>
<li><a class="dropdown-item" v-on:click="refreshVolume">
<i class="fas fa-sync-alt mx-1"></i>
Refresh
</a></li>
<li><a class="dropdown-item" v-on:click="toggleStatus">
<span v-if="volume.volume_status">
<i class="fas fa-stop mx-1"></i>
Mark Ended
</span>
<span v-else>
<i class="fas fa-play mx-1"></i>
Mark Ongoing
</span>
</a></li>
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#modalRemove">
<i class="fas fa-trash-alt mx-1"></i>
Remove From Library
</a></li>
{% endif %}
{{ emit_tep("single_volume_page_action_dropdown", volume_id=volume_id) }}
</ul>
</div>
<!-- END NEW ACTIONS -->
</div>
</div>
<div class="d-grid w-100 bg-mine m-0 p-0 sticky-md-top shadow">
<div class="row w-100 m-0 p-3">
<div class="col-12 col-md-10 text-center text-md-start">
<span class="fs-1 text-white stashr-series_title">[[ volume.volume_name ]]</span>
<span role="button" data-bs-toggle="modal" data-bs-target="#modalInfo">
<i class="fas fa-info-circle text-secondary"></i>
</span>
<h5>
<span class="badge bg-dark">
<i class="fas fa-circle" v-bind:class="volumeStatus"></i>
[[ statusWord ]]
</span>
<span class="badge bg-dark">[[ volume.age_rating[0].rating_long ]]</span>
<span class="badge bg-dark">
<i class="far fa-calendar"></i>
[[ volume.volume_year ]]
</span>
<br/>
<span class="badge bg-dark">
<i class="fas fa-book"></i>
[[ publisherName ]]
</span>
<span class="badge bg-dark">
<i class="fas fa-external-link-alt"></i>
ComicVine
</span>
<br/>
<span class="badge bg-dark">Digital: [[ volume.volume_have ]]/[[ volume.volume_total ]]</span>
<span class="badge bg-dark">Physical: [[ ownedNumber ]]/[[ volume.volume_total ]]</span>
<span class="badge bg-dark">Read: [[ readNumber ]]/[[ volume.volume_total ]]</span>
<br/>
{{ emit_tep('single_volume_page_badge_row', volume_id=volume_id) }}
</h5>
</div>
<div class="col-12 col-md-2 text-center text-md-end">
<div class="dropdown">
<a class="btn btn-dark dropdown-toggle" href="#" role="button" id="actionMenu" data-bs-toggle="dropdown" aria-expanded="false">
Actions
</a>
<ul class="dropdown-menu" aria-labelledby="actionMenu">
{% if (current_user.role.lower() == 'admin') or
(current_user.role.lower() == 'librarian') %}
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#modalRatingEdit">
<i class="far fa-sticky-note mx-1"></i>
Edit Rating
</a></li>
<li><a class="dropdown-item" v-on:click="refreshVolume">
<i class="fas fa-sync-alt mx-1"></i>
Refresh
</a></li>
<li><a class="dropdown-item" v-on:click="toggleStatus">
<span v-if="volume.volume_status">
<i class="fas fa-stop mx-1"></i>
Mark Ended
</span>
<span v-else>
<i class="fas fa-play mx-1"></i>
Mark Ongoing
</span>
</a></li>
<li><a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#modalRemove">
<i class="fas fa-trash-alt mx-1"></i>
Remove From Library
</a></li>
{% endif %}
{{ emit_tep("single_volume_page_action_dropdown", volume_id=volume_id) }}
</ul>
</div>
</div>
</div>
</div>
@ -545,9 +517,9 @@ Vue.component('volume-jumbo', {
};
},
volumeStatus() {
let classname = 'bg-danger';
let classname = 'text-danger';
if (this.volume.volume_status) {
classname = 'bg-success'
classname = 'text-success'
};
return classname;
},
@ -593,68 +565,66 @@ Vue.component('volume-jumbo', {
Vue.component('issue-item', {
props: ['issue'],
template: `
<li class='stashr-cover_size m-2'
<li class='stashr-item_container m-2'
@mouseover="hover = true"
@mouseleave="hover = false"
>
<div class='stashr-poster_wrapper rounded'>
<div class="stashr-badge_tl badge rounded-pill bg-info border">[[ issue.issue_number ]]</div>
<div class="stashr-badge_bl badge rounded-pill bg-dark border">
<i class="fas fa-check" v-bind:class="statusFile"></i>
<i class="fas fa-book" v-bind:class="statusOwned" v-on:click="toggleOwned"></i>
</div>
<div class="stashr-badge_br badge rounded-pill bg-dark border">
<i class="fas fa-eye" v-bind:class="statusRead" v-on:click="toggleRead"></i>
</div>
<div class="stashr-poster_container border rounded">
<div class="stashr-overlay_top w-100 center" v-if="hover" >
<!-- START BUTTONS -->
<span v-if="issue.issue_file_status">
<!-- HAVE -->
{% if (current_user.role.lower() == 'admin') or
(current_user.role.lower() == 'librarian') or
(current_user.role.lower() == 'patron') %}
<a :href="'/api/downloads/'+issue.issue_id" data-toggle='tooltip' data-placement="top" title="Download"><i class="text-primary px-1 fas fa-download"></i></a>
{% endif %}
<a :href="'/read/'+issue.issue_id" data-toggle="tooltip" title="Read"><i class="text-primary px-1 fas fa-book"></i></a>
<a v-on:click="addToReadingList" data-toggle="tooltip" title="Add to Reading List"><i class="text-primary px-1 fas fa-plus"></i></a>
<!--
<span data-toggle="tooltip" title="Delete">
<a data-bs-toggle="modal" data-bs-target="#modalDelete" :data-id="issue.issue_id" v-bind:data-number="issue.issue_number" >
<i class="text-primary px-1 fas fa-trash-alt"></i>
</a>
</span>
-->
{{ emit_tep("single_volume_page_top_overlay_have", volume_id=volume_id) }}
</span>
<span v-else>
<!-- MISSING -->
{% if (current_user.role.lower() == 'admin') or
(current_user.role.lower() == 'librarian') %}
<span data-toggle="tooltip" title="Upload to Server">
<a data-bs-toggle="modal" data-bs-target="#modalUpload" v-bind:data-issue_id=issue.issue_id v-bind:data-number=issue.issue_number >
<i class="text-primary px-1 fas fa-cloud-upload-alt"></i>
</a>
</span>
{% endif %}
{{ emit_tep("single_volume_page_top_overlay_missing", volume_id=volume_id) }}
</span>
<!-- END BUTTONS -->
</div>
<img class="stashr-poster_background w-100" loading="eager" src="/static/assets/cover.svg" />
<a class="stashr-poster_link" data-bs-toggle="modal" data-bs-target="#modalAction" :data-volume_name=issue.volume.volume_name :data-issue_number=issue.issue_number :data-issue_id=issue.issue_id :data-issue_status=issue.issue_file_status v-on:click="this.changeModal">
<img class="w-100" loading="lazy" :src="'/images/issues/' + this.issue.issue_id + '.jpg'" @error="$event.target.src=issue.issue_cover_url" />
</a>
</div>
<div class="stashr-poster_container border border-dark rounded-3">
<div class="stashr-poster_info bg-info text-white px-1">
#[[ issue.issue_number ]]
</div>
<div class="stashr-badge_bottom_left bg-dark">
<span class="fa-stack p-0">
<i class="fas fa-stack-1x" v-bind:class="statusFile"></i>
</span>
<!--
<span class="fa-stack p-0" v-on:click="toggleOwned">
<i class="fas fa-book fa-stack-1x" v-bind:class="statusOwned"></i>
</span>
-->
</div>
<div class="stashr-badge_bottom_right bg-dark">
<span class="fa-stack p-0" v-on:click="toggleRead">
<i class="fas fa-eye fa-stack-1x" v-bind:class="statusRead"></i>
<i class="fas fa-slash fa-stack-1x text-danger" v-if="!issue.read_status[0].read_status"></i>
</span>
</div>
<a class="stashr-poster_link" data-bs-toggle="modal" data-bs-target="#modalAction" :data-volume_name=issue.volume.volume_name :data-issue_number=issue.issue_number :data-issue_id=issue.issue_id :data-issue_status=issue.issue_file_status v-on:click="this.changeModal">
<img class="stashr-background w-100" loading="eager" src="/static/assets/cover.svg" />
<img class="stashr-poster w-100" loading="lazy" :src="'/images/issues/' + this.issue.issue_id + '.jpg'" @error="$event.target.src=issue.issue_cover_url"/>
</a>
<div class="stashr-overlay_top w-100 text-center p-1" v-if="hover">
<span v-if="issue.issue_file_status">
{% if (current_user.role.lower() == 'admin') or
(current_user.role.lower() == 'librarian') or
(current_user.role.lower() == 'patron') %}
<a :href="'/api/downloads/'+issue.issue_id" data-toggle='tooltip' data-placement="top" title="Download"><i class="text-primary px-1 fas fa-download"></i></a>
{% endif %}
<a :href="'/read/'+issue.issue_id" data-toggle="tooltip" title="Read"><i class="text-primary px-1 fas fa-book"></i></a>
<a v-on:click="addToReadingList" data-toggle="tooltip" title="Add to Reading List"><i class="text-primary px-1 fas fa-plus"></i></a>
{{ emit_tep("single_volume_page_top_overlay_have", volume_id=volume_id) }}
</span>
<span v-else>
{% if (current_user.role.lower() == 'admin') or
(current_user.role.lower() == 'librarian') %}
<span data-toggle="tooltip" title="Upload to Server">
<a data-bs-toggle="modal" data-bs-target="#modalUpload" v-bind:data-issue_id=issue.issue_id v-bind:data-number=issue.issue_number >
<i class="text-primary px-1 fas fa-cloud-upload-alt"></i>
</a>
</span>
{% endif %}
{{ emit_tep("single_volume_page_top_overlay_missing", volume_id=volume_id) }}
</span>
</div>
</div>
</li>
`,
data() { return { hover: false, have: false, } },
computed: {
statusFile() {
let classname = 'text-danger';
let classname = 'text-danger fa-times';
if(this.issue.issue_file_status) {
classname = 'text-success';
classname = 'text-success fa-check';
};
return classname;
},
@ -786,7 +756,7 @@ Vue.component('issue-item', {
Vue.component('issues', {
props: ['issues'],
template: `
<div>
<div class="py-3">
<ul class="d-flex flex-wrap w-100 m-0 p-0 justify-content-center">
<issue-item
v-for="issue in issues"

Loading…
Cancel
Save