Compare commits
7 Commits
643f9b3908
...
ef6145dfb6
| Author | SHA1 | Date | |
|---|---|---|---|
| ef6145dfb6 | |||
| d4605b952e | |||
| 007ad4581b | |||
| 8008d68c8b | |||
| 09296f077f | |||
| a76c2ec4a0 | |||
| e2ce075c96 |
55
analytics.py
55
analytics.py
@ -727,6 +727,8 @@ def file_access():
|
||||
if 'timeframe' not in session:
|
||||
session['timeframe'] = 'last24hours'
|
||||
session['timeframe'] = request.args.get('timeframe', session['timeframe'])
|
||||
if 'file_access_category' not in session:
|
||||
session['file_access_category'] = None
|
||||
|
||||
now = datetime.now()
|
||||
|
||||
@ -775,25 +777,56 @@ def file_access():
|
||||
'category': hf.extract_structure_from_string(rel_path)[0]
|
||||
})
|
||||
|
||||
# Get possible categories from the rows
|
||||
categories = sorted({r['category'] for r in rows if r['category'] is not None})
|
||||
all_categories = [None] + categories
|
||||
top20 = []
|
||||
for category in all_categories:
|
||||
label = category if category is not None else 'Keine Kategorie gefunden !'
|
||||
files = [r for r in rows if r['category'] == category][:20]
|
||||
top20.append({
|
||||
'category': label,
|
||||
'files': files
|
||||
# Build a list of selectable categories for the dropdown
|
||||
category_options = []
|
||||
has_uncategorized = any(r['category'] is None for r in rows)
|
||||
if has_uncategorized:
|
||||
category_options.append({
|
||||
'value': 'uncategorized',
|
||||
'label': 'Keine Kategorie gefunden !'
|
||||
})
|
||||
|
||||
unique_categories = sorted({r['category'] for r in rows if r['category'] is not None})
|
||||
for category in unique_categories:
|
||||
category_options.append({
|
||||
'value': category,
|
||||
'label': category
|
||||
})
|
||||
|
||||
default_category_value = category_options[0]['value'] if category_options else None
|
||||
requested_category = request.args.get('category')
|
||||
stored_category = session.get('file_access_category')
|
||||
selected_category = requested_category or stored_category or default_category_value
|
||||
|
||||
valid_values = {opt['value'] for opt in category_options}
|
||||
if selected_category not in valid_values:
|
||||
selected_category = default_category_value
|
||||
|
||||
session['file_access_category'] = selected_category
|
||||
|
||||
def matches_category(row):
|
||||
if selected_category == 'uncategorized':
|
||||
return row['category'] is None
|
||||
return row['category'] == selected_category
|
||||
|
||||
filtered_rows = [r for r in rows if matches_category(r)] if selected_category is not None else []
|
||||
top20_files = filtered_rows[:20]
|
||||
|
||||
selected_category_label = next(
|
||||
(opt['label'] for opt in category_options if opt['value'] == selected_category),
|
||||
'Keine Daten verfügbar'
|
||||
)
|
||||
|
||||
title_short = app_config.get('TITLE_SHORT', 'Default Title')
|
||||
title_long = app_config.get('TITLE_LONG' , 'Default Title')
|
||||
|
||||
return render_template(
|
||||
"file_access.html",
|
||||
timeframe=session['timeframe'],
|
||||
top20 = top20,
|
||||
categories=category_options,
|
||||
selected_category=selected_category,
|
||||
selected_category_label=selected_category_label,
|
||||
top20_files=top20_files,
|
||||
admin_enabled=auth.is_admin(),
|
||||
title_short=title_short,
|
||||
title_long=title_long
|
||||
|
||||
4
app.py
4
app.py
@ -1224,7 +1224,9 @@ def create_token(subpath):
|
||||
token_qr_code=img_base64,
|
||||
token_folder=token_item.get('folders'),
|
||||
token_url=url,
|
||||
token_valid_to=token_item.get('validity', 'Unbekannt')
|
||||
token_valid_to=token_item.get('validity', 'Unbekannt'),
|
||||
title_short=auth.return_app_config().get('TITLE_SHORT', ''),
|
||||
title_long=auth.return_app_config().get('TITLE_LONG', '')
|
||||
)
|
||||
|
||||
|
||||
|
||||
567
static/app.css
567
static/app.css
@ -1,44 +1,57 @@
|
||||
/* Ensure html and body take full height */
|
||||
html, body {
|
||||
background-color: var(--light-background);
|
||||
background: var(--light-background);
|
||||
color: var(--main-text-color);
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
/* padding: 0; */
|
||||
}
|
||||
|
||||
/* Global Styles */
|
||||
body {
|
||||
font-family: 'Helvetica Neue', Arial, sans-serif;
|
||||
padding-top: 70px; /* Adjust to your header height */
|
||||
overflow-x: hidden; /* Prevent horizontal scroll */
|
||||
font-family: 'Manrope', 'Segoe UI', sans-serif;
|
||||
padding-top: 0;
|
||||
overflow-x: hidden;
|
||||
letter-spacing: 0.01em;
|
||||
line-height: 1.6;
|
||||
background:
|
||||
radial-gradient(1200px 900px at -10% 10%, rgba(34, 211, 238, 0.12), transparent 40%),
|
||||
radial-gradient(1100px 800px at 110% -20%, rgba(59, 130, 246, 0.14), transparent 45%),
|
||||
var(--light-background);
|
||||
}
|
||||
|
||||
/* Header styles */
|
||||
.site-header {
|
||||
background: linear-gradient(120deg, var(--dark-background), #13253f);
|
||||
color: var(--header-text-color);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 2000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.site-header img.logo {
|
||||
height: 54px;
|
||||
margin-right: 6px;
|
||||
filter: drop-shadow(0 8px 10px rgba(0, 0, 0, 0.25));
|
||||
}
|
||||
|
||||
.site-header h1 {
|
||||
font-size: 1.3rem;
|
||||
margin: 0;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
font-weight: 800;
|
||||
}
|
||||
/* Header styles */
|
||||
.site-header {
|
||||
background-color: var(--dark-background);
|
||||
color: var(--header-text-color);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
.site-header img.logo {
|
||||
height: 50px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.site-header h1 {
|
||||
font-size: 1.5em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
padding-bottom: 200px;
|
||||
padding-bottom: 220px;
|
||||
}
|
||||
|
||||
.wrapper > .admin-nav,
|
||||
@ -49,79 +62,179 @@ body {
|
||||
.wrapper > main,
|
||||
.wrapper > section {
|
||||
flex: 1 1 auto;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.wrapper > footer {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
width: 90%;
|
||||
margin: 0 auto;
|
||||
padding: 20px 0;
|
||||
width: 95%;
|
||||
|
||||
}
|
||||
|
||||
.container a {
|
||||
font-size: 18px;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.container-fluid {
|
||||
/* Carded content shell */
|
||||
main.tab-content,
|
||||
section.tab-content {
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
border: 1px solid var(--border-color);
|
||||
box-shadow: none;
|
||||
padding: 20px;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
/* Search view container (custom element) */
|
||||
search {
|
||||
display: block;
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 0 0 16px 16px;
|
||||
box-shadow: none;
|
||||
margin-top: -1px;
|
||||
padding: 16px 12px 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Search view controls */
|
||||
.search-close-btn {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 14px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.input-clear-btn {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.input-clear-btn:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.search-close-btn:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Breadcrumb Styles */
|
||||
.breadcrumb a {
|
||||
font-size: 19px;
|
||||
text-decoration: none;
|
||||
color: var(--main-text-color);
|
||||
.breadcrumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
padding: 10px 16px;
|
||||
margin: 0 0 14px;
|
||||
}
|
||||
|
||||
.breadcrumb a {
|
||||
font-size: 17px;
|
||||
text-decoration: none;
|
||||
color: var(--brand-navy);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.breadcrumb span {
|
||||
font-size: 19px;
|
||||
color: #ccc;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 17px;
|
||||
color: var(--muted-text);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Admin Nav Styles */
|
||||
.admin-nav {
|
||||
color: #ccc;
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
background-color: #979797;
|
||||
color: var(--brand-ink);
|
||||
border: 1px solid var(--border-color);
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
margin-bottom: 12px;
|
||||
width: 100vw;
|
||||
margin-left: calc(50% - 50vw);
|
||||
margin-right: calc(50% - 50vw);
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
z-index: 1000;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.admin-nav-rail {
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.admin-nav-track {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: flex-start;
|
||||
padding: 0 10px; /* keep first/last items off the screen edge */
|
||||
overflow-x: auto;
|
||||
overflow-y: visible;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.admin-nav a {
|
||||
color: #ccc;
|
||||
color: var(--brand-ink);
|
||||
text-decoration: none;
|
||||
padding: 5px 0;
|
||||
padding: 8px 12px;
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
border: 1px solid transparent;
|
||||
background: linear-gradient(135deg, #fff, #f4f7fb);
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 6px 16px rgba(15, 23, 42, 0.05);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.admin-nav a:hover {
|
||||
color: var(--brand-sky);
|
||||
border-color: var(--border-color);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.admin-nav span {
|
||||
color: var(--border-color);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.admin-nav .dropdown-toggle {
|
||||
color: #ccc;
|
||||
text-decoration: none;
|
||||
padding: 5px 0;
|
||||
color: var(--brand-ink);
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.admin-nav .dropdown-toggle::after {
|
||||
border-top-color: #ccc;
|
||||
display: none; /* hide default Bootstrap caret */
|
||||
}
|
||||
|
||||
.admin-nav .dropdown-menu {
|
||||
background-color: #f2f2f2;
|
||||
border-color: #ccc;
|
||||
background-color: #fff;
|
||||
border-color: var(--border-color);
|
||||
border-radius: 10px;
|
||||
box-shadow: var(--card-shadow);
|
||||
z-index: 4000;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 100%;
|
||||
margin-top: 0;
|
||||
margin-left: 8px;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.admin-nav .dropdown-item {
|
||||
color: #333;
|
||||
color: var(--brand-ink);
|
||||
padding-left: 14px;
|
||||
padding-right: 14px;
|
||||
}
|
||||
|
||||
.admin-nav .dropdown-item:hover,
|
||||
.admin-nav .dropdown-item:focus {
|
||||
background-color: #e6e6e6;
|
||||
color: #000;
|
||||
background-color: #eef4ff;
|
||||
color: var(--brand-navy);
|
||||
}
|
||||
|
||||
/* List Styles */
|
||||
@ -138,18 +251,29 @@ div.directory-item a, li.directory-item a, li.file-item a, li.link-item a {
|
||||
|
||||
/* Directory Items (in a list) */
|
||||
.directory-item, .link-item, .directory-item, .file-item {
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 12px;
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
padding: 10px 15px;
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--card-shadow);
|
||||
padding: 12px 18px;
|
||||
border: 1px solid var(--border-color);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.directory-item:hover,
|
||||
.link-item:hover,
|
||||
.file-item:hover {
|
||||
border-color: var(--brand-sky);
|
||||
box-shadow: 0 14px 26px rgba(59, 130, 246, 0.14);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* Grid Layout for Directories */
|
||||
.directories-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 0 10px;
|
||||
column-gap: 14px;
|
||||
row-gap: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@ -159,49 +283,58 @@ div.directory-item a, li.directory-item a, li.file-item a, li.link-item a {
|
||||
grid-template-columns: 1fr auto;
|
||||
align-items: center;
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--card-shadow);
|
||||
}
|
||||
|
||||
/* Link Styles */
|
||||
.directory-link,
|
||||
.link-link,
|
||||
a.play-file {
|
||||
color: var(--player-text-color);
|
||||
color: var(--brand-ink);
|
||||
text-decoration: none;
|
||||
word-break: break-all;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
a.show-transcript {
|
||||
text-decoration: none;
|
||||
color: var(--main-text-color);
|
||||
font-size: 20px;
|
||||
color: var(--brand-navy);
|
||||
font-size: 18px;
|
||||
margin-left: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
a.show-transcript:hover {
|
||||
color: rgb(113, 146, 167);
|
||||
color: var(--brand-sky);
|
||||
}
|
||||
|
||||
.highlight {
|
||||
background-color: yellow;
|
||||
background-color: #fffbcc;
|
||||
padding: 2px 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#transcriptContent code.timecode-link {
|
||||
cursor: pointer;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
|
||||
a.create-share {
|
||||
text-decoration: none;
|
||||
color: var(--main-text-color);
|
||||
font-size: 20px;
|
||||
color: var(--brand-navy);
|
||||
font-size: 18px;
|
||||
margin-left: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
a.create-share:hover {
|
||||
color: rgb(113, 146, 167);
|
||||
color: var(--brand-sky);
|
||||
}
|
||||
|
||||
.currently-playing {
|
||||
background-color: var(--selected-background);
|
||||
border: 1px solid var(--brand-sky);
|
||||
}
|
||||
|
||||
/* Footer Player Styles */
|
||||
@ -210,14 +343,16 @@ footer {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: var(--dark-background);
|
||||
color: #fff;
|
||||
padding: 7px;
|
||||
text-align: center;
|
||||
z-index: 1000; /* Make sure it sits on top */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: none; /* avoid blocking underlying content */
|
||||
}
|
||||
|
||||
footer .audio-player-container {
|
||||
pointer-events: auto; /* keep controls clickable */
|
||||
}
|
||||
|
||||
/* Modal Styles */
|
||||
@ -235,21 +370,25 @@ footer {
|
||||
#transcriptModal .modal-content {
|
||||
background-color: #fff;
|
||||
margin: 5% auto;
|
||||
padding: 20px;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
width: 90%;
|
||||
max-width: 800px;
|
||||
position: relative;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
#transcriptModal .close {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 10px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
right: 0;
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
margin-left: auto;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
/* Basic Markdown Styles */
|
||||
@ -313,9 +452,10 @@ footer {
|
||||
.image-item {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border: 2px solid #ccc;
|
||||
border-radius: 8px;
|
||||
background: #f9f9f9;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(145deg, #ffffff, #f6f8fc);
|
||||
box-shadow: var(--card-shadow);
|
||||
}
|
||||
|
||||
/* the filename overlay, centered at bottom */
|
||||
@ -422,41 +562,60 @@ footer {
|
||||
|
||||
/* Tab Navigation Styles */
|
||||
.main-tabs {
|
||||
background-color: var(--dark-background);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
gap: 2px;
|
||||
border-bottom: 2px solid #444;
|
||||
justify-content: flex-start;
|
||||
padding: 6px 18px 0;
|
||||
gap: 0;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
flex-wrap: nowrap;
|
||||
border-radius: 0;
|
||||
width: 100vw;
|
||||
margin-left: calc(50% - 50vw);
|
||||
margin-right: calc(50% - 50vw);
|
||||
box-sizing: border-box;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
background: #333;
|
||||
border: 1px solid #444;
|
||||
background: rgba(217, 217, 217, 0.5);
|
||||
border: 1px solid var(--border-color);
|
||||
border-bottom: none;
|
||||
color: #999;
|
||||
padding: 6px 20px;
|
||||
font-size: 14px;
|
||||
color: var(--muted-text);
|
||||
padding: 10px 16px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
border-radius: 5px 5px 0 0;
|
||||
margin: 0;
|
||||
border-radius: 10px 10px 0 0;
|
||||
margin: 0 6px 0 0;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: none;
|
||||
box-shadow:
|
||||
0 -10px 18px -12px rgba(15, 23, 42, 0.18),
|
||||
10px 0 18px -12px rgba(15, 23, 42, 0.12),
|
||||
-10px 0 18px -12px rgba(15, 23, 42, 0.12);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.tab-button:hover {
|
||||
color: #fff;
|
||||
background: #3a3a3a;
|
||||
color: var(--brand-ink);
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
}
|
||||
|
||||
.tab-button.active {
|
||||
color: var(--main-text-color, #000);
|
||||
background: var(--light-background);
|
||||
border-color: #444;
|
||||
border-bottom-color: var(--light-background);
|
||||
z-index: 1;
|
||||
color: var(--brand-ink);
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
border-color: var(--border-color);
|
||||
z-index: 2;
|
||||
box-shadow:
|
||||
0 -12px 22px -12px rgba(15, 23, 42, 0.22),
|
||||
12px 0 20px -14px rgba(15, 23, 42, 0.14),
|
||||
-12px 0 20px -14px rgba(15, 23, 42, 0.14);
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
@ -471,27 +630,28 @@ footer {
|
||||
.messages-section-header {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 0 0 8px;
|
||||
padding: 0 0 10px;
|
||||
}
|
||||
|
||||
.messages-container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 0;
|
||||
padding: 10px 0 24px;
|
||||
}
|
||||
|
||||
.message-card {
|
||||
background: var(--card-background, #fff);
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
background: linear-gradient(145deg, #ffffff, #f7f9ff);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 14px;
|
||||
padding: 20px 22px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
transition: box-shadow 0.3s ease;
|
||||
box-shadow: var(--card-shadow);
|
||||
transition: box-shadow 0.3s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
.message-card:hover {
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
box-shadow: 0 20px 38px rgba(15, 23, 42, 0.16);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.message-header {
|
||||
@ -500,18 +660,18 @@ footer {
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
border-bottom: 1px dashed var(--border-color);
|
||||
}
|
||||
|
||||
.message-title {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
color: var(--main-text-color);
|
||||
color: var(--brand-ink);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.message-datetime {
|
||||
color: #666;
|
||||
color: var(--muted-text);
|
||||
font-size: 0.9em;
|
||||
font-style: italic;
|
||||
}
|
||||
@ -527,9 +687,9 @@ footer {
|
||||
gap: 6px;
|
||||
padding: 8px 16px;
|
||||
margin: 4px 0;
|
||||
background: #f0f4f8;
|
||||
color: #495057;
|
||||
border: 1px solid #d1dce5;
|
||||
background: #ecf6ff;
|
||||
color: #0f172a;
|
||||
border: 1px solid #bed8ff;
|
||||
border-radius: 6px;
|
||||
font-size: 0.95em;
|
||||
font-weight: 500;
|
||||
@ -539,9 +699,9 @@ footer {
|
||||
}
|
||||
|
||||
.message-content .folder-link-btn:hover {
|
||||
background: #e3eaf0;
|
||||
border-color: #b8c5d0;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.12);
|
||||
background: #dff0ff;
|
||||
border-color: #93c5fd;
|
||||
box-shadow: 0 6px 18px rgba(14, 165, 233, 0.24);
|
||||
}
|
||||
|
||||
.message-content .folder-link-btn:active {
|
||||
@ -554,13 +714,13 @@ footer {
|
||||
}
|
||||
|
||||
.message-content a {
|
||||
color: #007bff;
|
||||
color: var(--brand-sky);
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.message-content a:hover {
|
||||
color: #0056b3;
|
||||
color: #0ea5e9;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@ -620,10 +780,12 @@ footer {
|
||||
}
|
||||
|
||||
.message-content blockquote {
|
||||
border-left: 4px solid #ccc;
|
||||
border-left: 4px solid var(--border-color);
|
||||
padding-left: 15px;
|
||||
color: #666;
|
||||
color: var(--muted-text);
|
||||
font-style: italic;
|
||||
background: #f8fbff;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.message-image {
|
||||
@ -638,7 +800,7 @@ footer {
|
||||
.message-actions {
|
||||
margin-top: 15px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
border-top: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
@ -654,12 +816,12 @@ footer {
|
||||
}
|
||||
|
||||
.btn-edit {
|
||||
background-color: #007bff;
|
||||
background: linear-gradient(135deg, #2563eb, #22d3ee);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-edit:hover {
|
||||
background-color: #0056b3;
|
||||
background: linear-gradient(135deg, #1d4ed8, #0ea5e9);
|
||||
}
|
||||
|
||||
.btn-delete {
|
||||
@ -674,7 +836,7 @@ footer {
|
||||
.no-messages {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #999;
|
||||
color: var(--muted-text);
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
@ -690,9 +852,9 @@ footer {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||
gap: 0;
|
||||
border: 1px solid #ddd;
|
||||
border: 1px solid var(--border-color);
|
||||
border-top: 0;
|
||||
border-radius: 0 0 6px 6px;
|
||||
border-radius: 0 0 10px 10px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
@ -701,9 +863,9 @@ footer {
|
||||
}
|
||||
|
||||
.calendar-day {
|
||||
background: var(--card-background, #fff);
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 8px 10px 4px;
|
||||
background: #ffffff;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 12px 12px 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
@ -713,19 +875,19 @@ footer {
|
||||
}
|
||||
|
||||
.calendar-day-today {
|
||||
background: #f7fbff;
|
||||
box-shadow: inset 0 0 0 2px #4da3ff;
|
||||
border-color: #4da3ff;
|
||||
background: linear-gradient(135deg, #f6fbff, #ecf4ff);
|
||||
box-shadow: inset 0 0 0 2px #3b82f6;
|
||||
border-color: #3b82f6;
|
||||
}
|
||||
|
||||
.calendar-day-sunday,
|
||||
.calendar-day-holiday {
|
||||
background: #fff7f7;
|
||||
background: #fff8f5;
|
||||
}
|
||||
|
||||
.calendar-day-today.calendar-day-sunday,
|
||||
.calendar-day-today.calendar-day-holiday {
|
||||
background: linear-gradient(135deg, #f7fbff 0%, #f7fbff 60%, #fff2f2 100%);
|
||||
background: linear-gradient(135deg, #f6fbff 0%, #f6fbff 60%, #fff1ed 100%);
|
||||
}
|
||||
|
||||
.calendar-day-sunday .calendar-day-header,
|
||||
@ -757,7 +919,7 @@ footer {
|
||||
@media (min-width: 992px) {
|
||||
.calendar-grid {
|
||||
grid-template-columns: repeat(7, minmax(0, 1fr));
|
||||
border-radius: 0 0 8px 8px;
|
||||
border-radius: 0 0 12px 12px;
|
||||
}
|
||||
|
||||
.calendar-weekday-header {
|
||||
@ -765,21 +927,21 @@ footer {
|
||||
grid-template-columns: repeat(7, minmax(0, 1fr));
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
color: #999;
|
||||
border: 1px solid #ddd;
|
||||
color: var(--muted-text);
|
||||
border: 1px solid var(--border-color);
|
||||
border-bottom: 0;
|
||||
border-radius: 8px 8px 0 0;
|
||||
border-radius: 12px 12px 0 0;
|
||||
overflow: visible;
|
||||
position: sticky;
|
||||
top: 70px;
|
||||
z-index: 900;
|
||||
background: var(--card-background, #fff);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
|
||||
box-shadow: 0 10px 28px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.calendar-day {
|
||||
border-bottom: 1px solid #ddd;
|
||||
border-right: 1px solid #ddd;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
border-right: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.calendar-day:nth-child(7n) {
|
||||
@ -795,17 +957,17 @@ footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
margin-bottom: 8px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.calendar-day-name {
|
||||
font-weight: 600;
|
||||
font-weight: 700;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.calendar-day-date {
|
||||
color: #666;
|
||||
color: var(--muted-text);
|
||||
font-size: 0.95rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@ -817,9 +979,9 @@ footer {
|
||||
}
|
||||
|
||||
.calendar-day thead th {
|
||||
background: #f1f1f1;
|
||||
color: #666;
|
||||
border-color: #ddd;
|
||||
background: #f6f8fb;
|
||||
color: var(--muted-text);
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
|
||||
.calendar-day.empty thead {
|
||||
@ -851,7 +1013,7 @@ footer {
|
||||
|
||||
.details-indicator {
|
||||
font-weight: 700;
|
||||
color: #666;
|
||||
color: var(--brand-sky);
|
||||
margin-left: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
@ -869,7 +1031,7 @@ footer {
|
||||
}
|
||||
|
||||
.calendar-details-row td {
|
||||
background: #f8f9fa;
|
||||
background: #f8fbff;
|
||||
}
|
||||
|
||||
.calendar-inline-actions {
|
||||
@ -882,8 +1044,10 @@ footer {
|
||||
.file-browser-content {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 10px;
|
||||
background: #fff;
|
||||
box-shadow: var(--card-shadow);
|
||||
}
|
||||
|
||||
.file-browser-content .list-group-item {
|
||||
@ -906,15 +1070,98 @@ footer {
|
||||
}
|
||||
|
||||
.file-browser-panel {
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
padding: 15px;
|
||||
background-color: #f8fbff;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.file-browser-panel h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
color: #333;
|
||||
color: var(--brand-ink);
|
||||
}
|
||||
/* Buttons - harmonize with dark blue palette */
|
||||
.btn {
|
||||
border-radius: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, var(--brand-navy), var(--dark-background));
|
||||
border: 1px solid var(--brand-navy);
|
||||
color: #e5e7eb;
|
||||
box-shadow: 0 6px 14px rgba(15, 23, 42, 0.18);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: linear-gradient(135deg, #1b2a44, #0d1524);
|
||||
border-color: #1b2a44;
|
||||
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.2);
|
||||
}
|
||||
|
||||
.btn-primary:active,
|
||||
.btn-primary:focus {
|
||||
background: linear-gradient(135deg, #0d1524, #0a1020);
|
||||
border-color: #0d1524;
|
||||
box-shadow: 0 4px 10px rgba(15, 23, 42, 0.22);
|
||||
color: #e5e7eb;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: linear-gradient(135deg, #1f2e4a, #0f172a);
|
||||
border: 1px solid #1f2e4a;
|
||||
color: #e5e7eb;
|
||||
box-shadow: 0 6px 14px rgba(15, 23, 42, 0.18);
|
||||
}
|
||||
|
||||
.btn-secondary:hover,
|
||||
.btn-secondary:focus {
|
||||
background: linear-gradient(135deg, #223456, #111a2f);
|
||||
border-color: #223456;
|
||||
box-shadow: 0 8px 16px rgba(15, 23, 42, 0.2);
|
||||
}
|
||||
|
||||
.btn-secondary:active {
|
||||
background: linear-gradient(135deg, #0c1220, #0a0f1a);
|
||||
border-color: #0c1220;
|
||||
box-shadow: 0 4px 10px rgba(15, 23, 42, 0.22);
|
||||
}
|
||||
|
||||
.btn-outline-secondary {
|
||||
border: 1px solid var(--brand-navy);
|
||||
color: var(--brand-navy);
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
.btn-outline-secondary:hover,
|
||||
.btn-outline-secondary:focus {
|
||||
background: linear-gradient(135deg, #f7f9fd, #eef2f9);
|
||||
color: var(--brand-navy);
|
||||
border-color: var(--brand-navy);
|
||||
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.12);
|
||||
}
|
||||
|
||||
.btn-outline-secondary:active {
|
||||
background: #e5eaf5;
|
||||
color: var(--brand-navy);
|
||||
border-color: var(--brand-navy);
|
||||
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.12);
|
||||
}
|
||||
|
||||
.btn-light {
|
||||
background: linear-gradient(135deg, #e8ecf4, #dfe6f3);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--brand-ink);
|
||||
box-shadow: 0 4px 10px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
.btn-light:hover,
|
||||
.btn-light:focus {
|
||||
background: linear-gradient(135deg, #dfe5f1, #d7dfef);
|
||||
color: var(--brand-ink);
|
||||
border-color: var(--brand-navy);
|
||||
}
|
||||
|
||||
@ -252,9 +252,9 @@ function renderContent(data) {
|
||||
contentHTML += '<div class="directories-grid">';
|
||||
data.directories.forEach(dir => {
|
||||
if (admin_enabled && data.breadcrumbs.length != 1 && dir.share) {
|
||||
share_link = `<a href="#" class="create-share" data-url="${dir.path}">⚙️</a>`;
|
||||
share_link = `<a href="#" class="create-share" data-url="${dir.path}"><i class="bi bi-gear"></i></a>`;
|
||||
}
|
||||
contentHTML += `<div class="directory-item"><a href="#" class="directory-link" data-path="${dir.path}">📁 ${dir.name}</a>
|
||||
contentHTML += `<div class="directory-item"><a href="#" class="directory-link" data-path="${dir.path}"><i class="bi bi-folder"></i> ${dir.name}</a>
|
||||
${share_link}
|
||||
</div>`;
|
||||
});
|
||||
@ -262,22 +262,22 @@ function renderContent(data) {
|
||||
} else {
|
||||
contentHTML += '<ul>';
|
||||
if (data.breadcrumbs.length === 1 && Array.isArray(data.folder_today) && data.folder_today.length > 0) {
|
||||
contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="heute">📅 Heute</a></li>`;
|
||||
contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="heute"><i class="bi bi-calendar2-day"></i> Heute</a></li>`;
|
||||
} else if (data.breadcrumbs.length === 1 && Array.isArray(data.folder_yesterday) && data.folder_yesterday.length > 0) {
|
||||
contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="gestern">📅 Gestern</a></li>`;
|
||||
contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="gestern"><i class="bi bi-calendar2-day"></i> Gestern</a></li>`;
|
||||
}
|
||||
if (data.breadcrumbs.length === 1 && data.toplist_enabled) {
|
||||
contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="toplist">🔥 oft angehört</a></li>`;
|
||||
contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="toplist"><i class="bi bi-graph-up"></i> oft angehört</a></li>`;
|
||||
}
|
||||
|
||||
data.directories.forEach(dir => {
|
||||
if (admin_enabled && data.breadcrumbs.length != 1 && dir.share) {
|
||||
share_link = `<a href="#" class="create-share" data-url="${dir.path}">⚙️</a>`;
|
||||
share_link = `<a href="#" class="create-share" data-url="${dir.path}"><i class="bi bi-gear"></i></a>`;
|
||||
}
|
||||
if (dir.path.includes('toplist')) {
|
||||
link_symbol = '⭐';
|
||||
link_symbol = '<i class="bi bi-star"></i>';
|
||||
} else {
|
||||
link_symbol = '📁';
|
||||
link_symbol = '<i class="bi bi-folder"></i>';
|
||||
}
|
||||
contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="${dir.path}">${link_symbol} ${dir.name}</a>${share_link}</li>`;
|
||||
});
|
||||
@ -286,7 +286,7 @@ function renderContent(data) {
|
||||
}
|
||||
// Add search link at top level above directories/files
|
||||
if (data.breadcrumbs.length === 1) {
|
||||
contentHTML = `<ul><li class="link-item" onclick="viewSearch()"><a onclick="viewSearch()" class="link-link">🔎 Suche</a></li></ul>` + contentHTML;
|
||||
contentHTML = `<ul><li class="link-item" onclick="viewSearch()"><a onclick="viewSearch()" class="link-link"><i class="bi bi-search"></i> Suche</a></li></ul>` + contentHTML;
|
||||
}
|
||||
|
||||
// Render files (including music and non-image files)
|
||||
@ -294,18 +294,18 @@ function renderContent(data) {
|
||||
if (nonImageFiles.length > 0) {
|
||||
contentHTML += '<ul>';
|
||||
nonImageFiles.forEach((file, idx) => {
|
||||
let symbol = '📄';
|
||||
let symbol = '<i class="bi bi-file-earmark"></i>';
|
||||
if (file.file_type === 'music') {
|
||||
symbol = '🔊';
|
||||
symbol = '<i class="bi bi-volume-up"></i>';
|
||||
currentMusicFiles.push({ path: file.path, index: idx, title: file.name.replace('.mp3', '') });
|
||||
} else if (file.file_type === 'image') {
|
||||
symbol = '🖼️';
|
||||
symbol = '<i class="bi bi-image"></i>';
|
||||
}
|
||||
const indexAttr = file.file_type === 'music' ? ` data-index="${currentMusicFiles.length - 1}"` : '';
|
||||
contentHTML += `<li class="file-item">
|
||||
<a href="#" class="play-file"${indexAttr} data-url="${file.path}" data-file-type="${file.file_type}">${symbol} ${file.name.replace('.mp3', '')}</a>`;
|
||||
if (file.has_transcript) {
|
||||
contentHTML += `<a href="#" class="show-transcript" data-url="${file.transcript_url}" data-audio-url="${file.path}" title="Show Transcript">📄</a>`;
|
||||
contentHTML += `<a href="#" class="show-transcript" data-url="${file.transcript_url}" data-audio-url="${file.path}" title="Show Transcript"><i class="bi bi-journal-text"></i></a>`;
|
||||
}
|
||||
contentHTML += `</li>`;
|
||||
});
|
||||
|
||||
@ -1,23 +1,26 @@
|
||||
.audio-player-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
padding: 7px;
|
||||
width: 100%;
|
||||
background: linear-gradient(135deg, #f9fbff, #eef4ff);
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 12px 26px rgba(15, 23, 42, 0.12);
|
||||
border: 8px solid #0b1220;
|
||||
padding: 12px 12px;
|
||||
width: min(600px, 100%);
|
||||
display: none;
|
||||
color: #1f2937;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* Now Playing Info */
|
||||
.now-playing-info {
|
||||
margin-top: 5px;
|
||||
font-size: 16px;
|
||||
color: var(--main-text-color);
|
||||
font-size: 15px;
|
||||
color: #1f2937;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.audio-player {
|
||||
--player-button-width: 4em;
|
||||
--sound-button-width: 3em;
|
||||
--control-size: 3.4em;
|
||||
--space: 0.5em;
|
||||
}
|
||||
|
||||
@ -26,6 +29,7 @@
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: var(--space); /* ensures spacing between controls */
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
/* Make the slider container fill the available space and stack its children vertically */
|
||||
@ -40,10 +44,10 @@
|
||||
-webkit-appearance: none;
|
||||
width: 100%;
|
||||
height: 0.5em;
|
||||
background-color: #ccc;
|
||||
background-color: #e6edf7;
|
||||
border-radius: 5px;
|
||||
background-size: 0% 100%;
|
||||
background-image: linear-gradient(var(--dark-background), var(--dark-background));
|
||||
background-image: linear-gradient(90deg, #0b1220, #0f172a);
|
||||
background-repeat: no-repeat;
|
||||
appearance: none;
|
||||
outline: none;
|
||||
@ -56,9 +60,9 @@
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 50%;
|
||||
background-color: var(--dark-background);
|
||||
background-color: #0f172a;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border: 2px solid #0b1220;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@ -67,9 +71,9 @@
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 50%;
|
||||
background-color: var(--dark-background);
|
||||
background-color: #0f172a;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border: 2px solid #0b1220;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@ -78,9 +82,9 @@
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
border-radius: 50%;
|
||||
background-color: var(--dark-background);
|
||||
background-color: #0f172a;
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
border: 2px solid #0b1220;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@ -104,18 +108,55 @@
|
||||
|
||||
.player-button,
|
||||
.sound-button {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
width: var(--control-size);
|
||||
height: var(--control-size);
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
box-shadow: none;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background-color 0.15s ease, transform 0.1s ease;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.player-button {
|
||||
width: var(--player-button-width);
|
||||
height: var(--player-button-width);
|
||||
.player-button:hover,
|
||||
.sound-button:hover {
|
||||
background: rgba(15, 23, 42, 0.08);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.sound-button {
|
||||
width: var(--sound-button-width);
|
||||
height: var(--sound-button-width);
|
||||
.player-button:active,
|
||||
.sound-button:active {
|
||||
transform: translateY(0);
|
||||
background: rgba(15, 23, 42, 0.12);
|
||||
}
|
||||
|
||||
.player-button:focus-visible,
|
||||
.sound-button:focus-visible {
|
||||
outline: 2px solid rgba(20, 33, 61, 0.85);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.player-button svg,
|
||||
.sound-button svg {
|
||||
width: var(--control-size);
|
||||
height: var(--control-size);
|
||||
display: block;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.player-button svg path,
|
||||
.sound-button svg path {
|
||||
fill: var(--dark-background);
|
||||
}
|
||||
|
||||
.timeline::-webkit-slider-thumb,
|
||||
.timeline::-moz-range-thumb,
|
||||
.timeline::-ms-thumb {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
@ -25,4 +25,45 @@ function toClipboard(url) {
|
||||
}
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function printToken() {
|
||||
const printable = document.getElementById('tokenPrintable');
|
||||
if (!printable) {
|
||||
console.error('Token printable content not found.');
|
||||
return;
|
||||
}
|
||||
|
||||
const clone = printable.cloneNode(true);
|
||||
clone.querySelectorAll('.token-action-buttons').forEach(el => el.remove());
|
||||
|
||||
const styles = Array.from(document.querySelectorAll('link[rel="stylesheet"], style'))
|
||||
.map(node => node.outerHTML)
|
||||
.join('\n');
|
||||
|
||||
const popup = window.open('', '_blank', 'width=900,height=1200');
|
||||
if (!popup) {
|
||||
alert('Bitte Popups erlauben, um das PDF zu speichern.');
|
||||
return;
|
||||
}
|
||||
|
||||
popup.document.write(`<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Token PDF</title>
|
||||
${styles}
|
||||
<style>
|
||||
body { padding: 24px; }
|
||||
.token-action-buttons { display: none !important; }
|
||||
.card { box-shadow: none !important; }
|
||||
</style>
|
||||
</head>
|
||||
<body>${clone.outerHTML}</body>
|
||||
</html>`);
|
||||
popup.document.close();
|
||||
popup.focus();
|
||||
popup.onload = () => {
|
||||
popup.print();
|
||||
popup.close();
|
||||
};
|
||||
}
|
||||
|
||||
@ -417,7 +417,7 @@ function setAutoPlay(active) {
|
||||
const playButton = document.getElementById('gallery-play');
|
||||
isAutoPlay = active;
|
||||
if (playButton) {
|
||||
playButton.textContent = active ? '⏸' : '▶';
|
||||
playButton.textContent = active ? '||' : '>';
|
||||
playButton.setAttribute('aria-pressed', active ? 'true' : 'false');
|
||||
playButton.classList.toggle('playing', active);
|
||||
}
|
||||
|
||||
@ -1,6 +1,66 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// (No per-icon setup needed here—just ensure your CSS is in place.)
|
||||
});
|
||||
function wireAdminDropdowns() {
|
||||
const reposition = (toggle, menu) => {
|
||||
const rect = toggle.getBoundingClientRect();
|
||||
const gutter = 8;
|
||||
const menuWidth = menu.offsetWidth || 220;
|
||||
const menuHeight = menu.offsetHeight || 0;
|
||||
|
||||
// Prefer placing the menu to the right of the toggle; clamp within viewport.
|
||||
let left = rect.right + gutter;
|
||||
if (left + menuWidth > window.innerWidth - gutter) {
|
||||
left = Math.max(gutter, window.innerWidth - menuWidth - gutter);
|
||||
}
|
||||
|
||||
let top = Math.max(gutter, Math.min(rect.top, window.innerHeight - menuHeight - gutter));
|
||||
|
||||
menu.style.position = 'fixed';
|
||||
menu.style.transform = 'none';
|
||||
menu.style.top = `${top}px`;
|
||||
menu.style.left = `${left}px`;
|
||||
menu.style.right = 'auto';
|
||||
menu.style.minWidth = `${Math.max(rect.width, 160)}px`;
|
||||
menu.style.maxWidth = '260px';
|
||||
menu.style.display = 'block';
|
||||
menu.dataset.dropdownOpen = '1';
|
||||
menu.classList.add('show');
|
||||
};
|
||||
|
||||
const resetMenu = (menu) => {
|
||||
menu.style.position = '';
|
||||
menu.style.transform = '';
|
||||
menu.style.top = '';
|
||||
menu.style.left = '';
|
||||
menu.style.right = '';
|
||||
menu.style.minWidth = '';
|
||||
menu.style.maxWidth = '';
|
||||
menu.style.display = '';
|
||||
menu.classList.remove('show');
|
||||
delete menu.dataset.dropdownOpen;
|
||||
};
|
||||
|
||||
document.querySelectorAll('.admin-nav .dropdown-toggle').forEach((toggle) => {
|
||||
if (toggle.dataset.dropdownWired) return;
|
||||
toggle.dataset.dropdownWired = '1';
|
||||
const menu = toggle.nextElementSibling;
|
||||
if (!menu) return;
|
||||
|
||||
toggle.addEventListener('shown.bs.dropdown', () => reposition(toggle, menu));
|
||||
toggle.addEventListener('hide.bs.dropdown', () => resetMenu(menu));
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
if (menu.dataset.dropdownOpen) reposition(toggle, menu);
|
||||
}, { passive: true });
|
||||
window.addEventListener('resize', () => {
|
||||
if (menu.dataset.dropdownOpen) reposition(toggle, menu);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
||||
wireAdminDropdowns();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', wireAdminDropdowns);
|
||||
}
|
||||
|
||||
// 1) Delegate clicks on any .toggle-icon, now or in the future:
|
||||
document.addEventListener('click', function(e) {
|
||||
|
||||
@ -14,11 +14,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
card.className = 'card';
|
||||
card.innerHTML = `
|
||||
<div class="card-body">
|
||||
<p><button class="btn btn-light" onclick="player.loadTrack('${file.relative_path}')" style="width:100%;">🔊 ${filenameWithoutExtension}</button></p>
|
||||
<p><button onclick="window.open('/path/${file.relative_path}', '_self');" class="btn btn-light btn-sm" style="width:100%;">📁 ${parentFolder}</button></p>
|
||||
<p><button class="btn btn-light" onclick="player.loadTrack('${file.relative_path}')" style="width:100%;"><i class="bi bi-volume-up"></i> ${filenameWithoutExtension}</button></p>
|
||||
<p><button onclick="window.open('/path/${file.relative_path}', '_self');" class="btn btn-light btn-sm" style="width:100%;"><i class="bi bi-folder"></i> ${parentFolder}</button></p>
|
||||
<p class="card-text">Anzahl Downloads: ${file.hitcount}</p>
|
||||
${ file.performance_date !== undefined ? `<p class="card-text">Datum: ${file.performance_date}</p>` : ``}
|
||||
${ file.transcript_hits !== undefined ? `<p class="card-text">Treffer im Transkript: ${file.transcript_hits} <a href="#" class="show-transcript" data-url="${transcriptURL}" data-audio-url="${file.relative_path}" highlight="${file.query}">📄</a></p>` : ``}
|
||||
${ file.transcript_hits !== undefined ? `<p class="card-text">Treffer im Transkript: ${file.transcript_hits} <a href="#" class="show-transcript" data-url="${transcriptURL}" data-audio-url="${file.relative_path}" highlight="${file.query}"><i class="bi bi-journal-text"></i></a></p>` : ``}
|
||||
</div>
|
||||
`;
|
||||
resultsDiv.appendChild(card);
|
||||
@ -66,6 +66,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
e.preventDefault();
|
||||
const query = document.getElementById('query').value.trim();
|
||||
const includeTranscript = document.getElementById('includeTranscript').checked;
|
||||
const spinnerTimer = setTimeout(() => {
|
||||
if (typeof showSpinner === 'function') showSpinner();
|
||||
}, 200);
|
||||
|
||||
// Get the selected category radio button, if any
|
||||
const categoryRadio = document.querySelector('input[name="category"]:checked');
|
||||
@ -96,7 +99,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
formData.append('dateto', document.getElementById('dateto').value);
|
||||
formData.append('includeTranscript', includeTranscript);
|
||||
|
||||
fetch('/searchcommand', {
|
||||
const settleSpinner = () => {
|
||||
clearTimeout(spinnerTimer);
|
||||
if (typeof hideSpinner === 'function') hideSpinner();
|
||||
};
|
||||
|
||||
const fetchPromise = fetch('/searchcommand', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
@ -118,6 +126,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
// Always clear/hide spinner once the request settles
|
||||
if (typeof fetchPromise.finally === 'function') {
|
||||
fetchPromise.finally(settleSpinner);
|
||||
} else {
|
||||
fetchPromise.then(settleSpinner, settleSpinner);
|
||||
}
|
||||
});
|
||||
|
||||
// Clear button event handler
|
||||
|
||||
80
static/token.css
Normal file
80
static/token.css
Normal file
@ -0,0 +1,80 @@
|
||||
.token-container {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#tokenPrintable .qr-code {
|
||||
max-width: 420px;
|
||||
width: 100%;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
#tokenPrintable .token-qr-wrap {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#tokenPrintable .token-pdf-logo {
|
||||
max-width: 120px;
|
||||
width: 28%;
|
||||
min-width: 80px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
margin: 0;
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
filter: drop-shadow(0 0 8px rgba(0, 0, 0, 0.8)) drop-shadow(0 0 14px rgba(0, 0, 0, 0.6));
|
||||
}
|
||||
|
||||
#tokenPrintable .token-folder-title {
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
|
||||
#tokenPrintable .token-page-title {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
#tokenPrintable .copy-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
border: 1px solid #007bff;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
#tokenPrintable .copy-btn:active {
|
||||
background-color: #0056b3;
|
||||
border-color: #0056b3;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.token-action-buttons {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
#tokenPrintable .qr-code {
|
||||
max-width: 620px;
|
||||
width: 90% !important;
|
||||
}
|
||||
|
||||
#tokenPrintable .token-pdf-logo {
|
||||
max-width: 140px;
|
||||
width: 24%;
|
||||
}
|
||||
|
||||
#tokenPrintable .token-qr-wrap {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
@ -1,247 +1,190 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
{% extends 'base.html' %}
|
||||
|
||||
<title>{{ title_short }}</title>
|
||||
{% block title %}{{ title_short }}{% endblock %}
|
||||
|
||||
<meta property="og:title" content="{{ og_title }}" />
|
||||
<meta property="og:description" content="{{ og_description }}" />
|
||||
<meta property="og:image" content="/icon/logo-192x192.png" />
|
||||
<meta property="og:type" content="website" />
|
||||
{% block head_extra %}
|
||||
<meta property="og:title" content="{{ og_title }}">
|
||||
<meta property="og:description" content="{{ og_description }}">
|
||||
<meta property="og:image" content="/icon/logo-192x192.png">
|
||||
<meta property="og:type" content="website">
|
||||
<meta name="description" content="... uns aber, die wir gerettet werden, ist es eine Gotteskraft.">
|
||||
<meta name="author" content="{{ title_short }}">
|
||||
<link rel="icon" href="/icon/logo-192x192.png" type="image/png" sizes="192x192">
|
||||
<link rel="manifest" href="{{ url_for('static', filename='manifest.json') }}">
|
||||
<link rel="touch-icon" href="/icon/logo-192x192.png">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-status-bar-style" content="default">
|
||||
<meta name="mobile-web-app-title" content="Gottesdienste">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='gallery.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='audioplayer.css') }}">
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script>const admin_enabled = {{ admin_enabled | tojson | safe }};</script>
|
||||
{% endblock %}
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<meta name="description" content="... uns aber, die wir gerettet werden, ist es eine Gotteskraft.">
|
||||
<meta name="author" content="{{ title_short }}">
|
||||
<link rel="icon" href="/icon/logo-192x192.png" type="image/png" sizes="192x192">
|
||||
|
||||
|
||||
<!-- Web App Manifest -->
|
||||
<link rel="manifest" href="{{ url_for('static', filename='manifest.json') }}">
|
||||
|
||||
<!-- Android Theme Color -->
|
||||
<meta name="theme-color" content="#000">
|
||||
|
||||
<!-- Apple-specific tags -->
|
||||
<link rel="touch-icon" href="/icon/logo-192x192.png">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-status-bar-style" content="default">
|
||||
<meta name="mobile-web-app-title" content="Gottesdienste">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
|
||||
|
||||
<!-- Your CSS -->
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='theme.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='app.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='gallery.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='audioplayer.css') }}">
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script> const admin_enabled = {{ admin_enabled | tojson | safe }}; </script>
|
||||
</head>
|
||||
<body>
|
||||
<script src="{{ url_for('static', filename='functions.js') }}"></script>
|
||||
<header class="site-header">
|
||||
<a href="/">
|
||||
<img src="/custom_logo/logoW.png" alt="Logo" class="logo">
|
||||
</a>
|
||||
<h1>{{ title_long }}</h1>
|
||||
</header>
|
||||
<div class="wrapper">
|
||||
{% if admin_enabled %}
|
||||
<div class="admin-nav">
|
||||
<a href="{{ url_for('mylinks') }}">Meine Links</a>
|
||||
<span> | </span>
|
||||
<a href="{{ url_for('folder_secret_config_editor') }}" id="edit-folder-config">Ordnerkonfiguration</a>
|
||||
<span> | </span>
|
||||
<div class="dropdown d-inline-block">
|
||||
<a class="dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Auswertungen
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="{{ url_for('dashboard') }}">Dashbord</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('connections') }}">Verbindungen</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('file_access') }}">Dateizugriffe</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('songs_dashboard') }}">Wiederholungen</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('search_db_analyzer') }}">Dateiindex Analyse</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if features %}
|
||||
<!-- Tab Navigation -->
|
||||
{% block content %}
|
||||
<!-- Tab Navigation -->
|
||||
{% if features %}
|
||||
<nav class="main-tabs">
|
||||
{% if "files" in features %}<button class="tab-button active" data-tab="browse">Audio/Photo</button>{% endif %}
|
||||
{% if "messages" in features %}<button class="tab-button" data-tab="messages">Nachrichten</button>{% endif %}
|
||||
{% if "calendar" in features %}<button class="tab-button" data-tab="calendar">Kalender</button>{% endif %}
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
<!-- Browse Section -->
|
||||
<main id="browse-section" class="tab-content active">
|
||||
<div class="container">
|
||||
<div id="breadcrumbs" class="breadcrumb"></div>
|
||||
<div id="content"></div>
|
||||
<div id="directory-controls">
|
||||
<button id="reload-button" onclick="reloadDirectory()" title="Reload Directory">
|
||||
<svg width="70px" height="70px" viewBox="0 0 24 24"xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.2721 3.13079L17.4462 6.15342C17.6461 6.66824 17.3909 7.24768 16.8761 7.44764L13.8535 8.6217" stroke="#CCC" stroke-width="2.5" stroke-linecap="round"/>
|
||||
<path d="M7.61555 20.5111L6.93391 17.3409C6.81782 16.801 7.16142 16.2692 7.70136 16.1531L10.8715 15.4714" stroke="#CCC" stroke-width="2.5" stroke-linecap="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.9153 7.86755C17.6501 7.5052 17.8749 6.55584 17.263 6.01119C16.4869 5.32046 15.5626 4.77045 14.5169 4.41551C10.3331 2.99538 5.79017 5.23581 4.37005 9.41964C4.01511 10.4653 3.88883 11.5335 3.96442 12.5696C4.02402 13.3867 4.9141 13.7862 5.64883 13.4239V13.4239C6.17327 13.1652 6.44536 12.5884 6.44406 12.0037C6.44274 11.4136 6.53712 10.8132 6.73739 10.2232C7.71372 7.34681 10.837 5.80651 13.7134 6.78285C14.3034 6.98311 14.8371 7.27371 15.3045 7.63397C15.7677 7.99095 16.3909 8.12619 16.9153 7.86755V7.86755ZM6.97575 16.1145C7.50019 15.8558 8.12343 15.991 8.58656 16.348C9.05394 16.7083 9.58773 16.9989 10.1777 17.1992C13.0541 18.1755 16.1774 16.6352 17.1537 13.7588C17.354 13.1688 17.4483 12.5684 17.447 11.9783C17.4457 11.3936 17.7178 10.8168 18.2423 10.5581V10.5581C18.977 10.1958 19.8671 10.5953 19.9267 11.4124C20.0022 12.4485 19.876 13.5167 19.521 14.5624C18.1009 18.7462 13.558 20.9866 9.37418 19.5665C8.32849 19.2116 7.4042 18.6615 6.62812 17.9708C6.01616 17.4262 6.24102 16.4768 6.97575 16.1145V16.1145Z" fill="#CCC"/>
|
||||
</svg>
|
||||
{% endif %}
|
||||
|
||||
<!-- Browse Section -->
|
||||
<main id="browse-section" class="tab-content active">
|
||||
<div id="breadcrumbs" class="breadcrumb"></div>
|
||||
<div id="content"></div>
|
||||
<div id="directory-controls">
|
||||
<button id="reload-button" onclick="reloadDirectory()" title="Reload Directory">
|
||||
<svg width="70px" height="70px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16.2721 3.13079L17.4462 6.15342C17.6461 6.66824 17.3909 7.24768 16.8761 7.44764L13.8535 8.6217" stroke="#CCC" stroke-width="2.5" stroke-linecap="round" />
|
||||
<path d="M7.61555 20.5111L6.93391 17.3409C6.81782 16.801 7.16142 16.2692 7.70136 16.1531L10.8715 15.4714" stroke="#CCC" stroke-width="2.5" stroke-linecap="round" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.9153 7.86755C17.6501 7.5052 17.8749 6.55584 17.263 6.01119C16.4869 5.32046 15.5626 4.77045 14.5169 4.41551C10.3331 2.99538 5.79017 5.23581 4.37005 9.41964C4.01511 10.4653 3.88883 11.5335 3.96442 12.5696C4.02402 13.3867 4.9141 13.7862 5.64883 13.4239V13.4239C6.17327 13.1652 6.44536 12.5884 6.44406 12.0037C6.44274 11.4136 6.53712 10.8132 6.73739 10.2232C7.71372 7.34681 10.837 5.80651 13.7134 6.78285C14.3034 6.98311 14.8371 7.27371 15.3045 7.63397C15.7677 7.99095 16.3909 8.12619 16.9153 7.86755V7.86755ZM6.97575 16.1145C7.50019 15.8558 8.12343 15.991 8.58656 16.348C9.05394 16.7083 9.58773 16.9989 10.1777 17.1992C13.0541 18.1755 16.1774 16.6352 17.1537 13.7588C17.354 13.1688 17.4483 12.5684 17.447 11.9783C17.4457 11.3936 17.7178 10.8168 18.2423 10.5581V10.5581C18.977 10.1958 19.8671 10.5953 19.9267 11.4124C20.0022 12.4485 19.876 13.5167 19.521 14.5624C18.1009 18.7462 13.558 20.9866 9.37418 19.5665C8.32849 19.2116 7.4042 18.6615 6.62812 17.9708C6.01616 17.4262 6.24102 16.4768 6.97575 16.1145V16.1145Z" fill="#CCC" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{% include 'messages_section.html' %}
|
||||
{% include 'calendar_section.html' %}
|
||||
|
||||
<search style="display: none;">
|
||||
<button type="button" id="backBtn" class="btn-close search-close-btn" aria-label="beenden"></button>
|
||||
<div class="container">
|
||||
<form id="searchForm" method="post" class="mb-4">
|
||||
<!-- Suchwörter -->
|
||||
<div class="mb-3 search-query-wrap">
|
||||
<label for="query" class="h5 form-label">Suchwörter:</label>
|
||||
<div class="input-group">
|
||||
<input type="text" id="query" name="query" class="form-control" required>
|
||||
<button type="button" id="clearBtn" class="btn btn-outline-secondary input-clear-btn" aria-label="zurücksetzen">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toggle für Suchoptionen -->
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<h2 class="h5 mb-0">Suchoptionen</h2>
|
||||
<button class="btn btn-sm btn-link p-0"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#searchOptions"
|
||||
aria-expanded="false"
|
||||
aria-controls="searchOptions"
|
||||
aria-label="Toggle Suchoptionen">
|
||||
<i class="bi bi-plus-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Suchoptionen einklappbar -->
|
||||
<div id="searchOptions" class="collapse border rounded p-3 mb-3">
|
||||
<!-- Kategorie -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Kategorie:</label>
|
||||
<div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="category" id="none" value="" checked>
|
||||
<label class="form-check-label" for="none">Alles</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="category" id="lied" value="Lied">
|
||||
<label class="form-check-label" for="lied">Lied</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="category" id="gedicht" value="Gedicht">
|
||||
<label class="form-check-label" for="gedicht">Gedicht</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="category" id="predigt" value="Predigt">
|
||||
<label class="form-check-label" for="predigt">Predigt</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="category" id="chor" value="chor">
|
||||
<label class="form-check-label" for="chor">Chor</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="category" id="orchester" value="orchester">
|
||||
<label class="form-check-label" for="orchester">Orchester</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- In Ordner suchen -->
|
||||
<div class="mb-3">
|
||||
<label for="folder" class="form-label">In Ordner suchen:</label>
|
||||
<select id="folder" name="folder" class="form-select">
|
||||
<option value="">Alle</option>
|
||||
{% for folder in search_folders %}
|
||||
<option value="{{ folder }}">{{ folder }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Transkript durchsuchen -->
|
||||
<div class="form-check mb-3">
|
||||
<input type="checkbox" class="form-check-input" id="includeTranscript" name="includeTranscript">
|
||||
<label class="form-check-label" for="includeTranscript">Im Transkript suchen</label>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Zeitraum -->
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-12 col-md-6">
|
||||
<label for="datefrom" class="form-label">Datum von:</label>
|
||||
<input type="date" id="datefrom" name="datefrom" class="form-control">
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label for="dateto" class="form-label">Datum bis:</label>
|
||||
<input type="date" id="dateto" name="dateto" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="mb-3">
|
||||
<button type="submit" class="btn btn-primary">Suchen</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- AJAX-loaded results -->
|
||||
<div id="results"></div>
|
||||
</div>
|
||||
</search>
|
||||
|
||||
<!-- Global Audio Player in Footer -->
|
||||
<footer>
|
||||
<div class="audio-player-container" id="audioPlayerContainer">
|
||||
<div class="audio-player">
|
||||
<audio id="globalAudio" prefetch="auto">
|
||||
Your browser does not support the audio element.
|
||||
</audio>
|
||||
<div class="controls">
|
||||
<button class="player-button icon-color">
|
||||
</button>
|
||||
<div class="slider">
|
||||
<input type="range" class="timeline" max="100" value="0" step="0.1">
|
||||
<div id="timeInfo" class="now-playing-info"></div>
|
||||
</div>
|
||||
<button class="sound-button icon-color" onclick="player.fileDownload()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M12 2.25A9.75 9.75 0 1 1 2.25 12 9.75 9.75 0 0 1 12 2.25Zm0 5a.75.75 0 0 0-.75.75v4.19l-1.72-1.72a.75.75 0 1 0-1.06 1.06l3.03 3.03a.75.75 0 0 0 1.06 0l3.03-3.03a.75.75 0 1 0-1.06-1.06L12.75 12.2V8a.75.75 0 0 0-.75-.75ZM7.5 15.25a.75.75 0 0 0 0 1.5h9a.75.75 0 0 0 0-1.5Z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{% include 'messages_section.html' %}
|
||||
{% include 'calendar_section.html' %}
|
||||
|
||||
<search style="display: none;">
|
||||
<div class="container">
|
||||
<form id="searchForm" method="post" class="mb-4">
|
||||
|
||||
<!-- Suchwörter -->
|
||||
<div class="mb-3">
|
||||
<label for="query" class="h5 form-label">Suchwörter:</label>
|
||||
<input type="text" id="query" name="query" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<!-- Toggle für Suchoptionen -->
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<h2 class="h5 mb-0">Suchoptionen</h2>
|
||||
<button class="btn btn-sm btn-link p-0"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#searchOptions"
|
||||
aria-expanded="false"
|
||||
aria-controls="searchOptions"
|
||||
aria-label="Toggle Suchoptionen">
|
||||
<i class="bi bi-plus-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Suchoptionen einklappbar -->
|
||||
<div id="searchOptions" class="collapse border rounded p-3 mb-3">
|
||||
|
||||
<!-- Kategorie -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Kategorie:</label>
|
||||
<div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="category" id="none" value="" checked>
|
||||
<label class="form-check-label" for="none">Alles</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="category" id="lied" value="Lied">
|
||||
<label class="form-check-label" for="lied">Lied</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="category" id="gedicht" value="Gedicht">
|
||||
<label class="form-check-label" for="gedicht">Gedicht</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="category" id="predigt" value="Predigt">
|
||||
<label class="form-check-label" for="predigt">Predigt</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="category" id="chor" value="chor">
|
||||
<label class="form-check-label" for="chor">Chor</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="category" id="orchester" value="orchester">
|
||||
<label class="form-check-label" for="orchester">Orchester</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- In Ordner suchen -->
|
||||
<div class="mb-3">
|
||||
<label for="folder" class="form-label">In Ordner suchen:</label>
|
||||
<select id="folder" name="folder" class="form-select">
|
||||
<option value="">Alle</option>
|
||||
{% for folder in search_folders %}
|
||||
<option value="{{ folder }}">{{ folder }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Transkript durchsuchen -->
|
||||
<div class="form-check mb-3">
|
||||
<input type="checkbox" class="form-check-input" id="includeTranscript" name="includeTranscript">
|
||||
<label class="form-check-label" for="includeTranscript">Im Transkript suchen</label>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Zeitraum -->
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-12 col-md-6">
|
||||
<label for="datefrom" class="form-label">Datum von:</label>
|
||||
<input type="date" id="datefrom" name="datefrom" class="form-control">
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label for="dateto" class="form-label">Datum bis:</label>
|
||||
<input type="date" id="dateto" name="dateto" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- Ende Suchoptionen -->
|
||||
|
||||
<!-- Buttons -->
|
||||
<div class="mb-3">
|
||||
<button type="submit" class="btn btn-primary">Suchen</button>
|
||||
<button type="button" id="clearBtn" class="btn btn-secondary ms-2">zurücksetzen</button>
|
||||
<button type="button" id="backBtn" class="btn btn-secondary ms-2">beenden</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<!-- AJAX-loaded results -->
|
||||
<div id="results"></div>
|
||||
<div id="nowPlayingInfo" class="now-playing-info">
|
||||
keine Datei ausgewählt...
|
||||
</div>
|
||||
</search>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<!-- Global Audio Player in Footer -->
|
||||
<footer>
|
||||
<div class="audio-player-container" id="audioPlayerContainer">
|
||||
<div class="audio-player">
|
||||
<audio id="globalAudio" prefetch="auto">
|
||||
Your browser does not support the audio element.
|
||||
</audio>
|
||||
<div class="controls">
|
||||
<button class="player-button icon-color">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zM7 8a1 1 0 012 0v4a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v4a1 1 0 102 0V8a1 1 0 00-1-1z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="slider">
|
||||
<input type="range" class="timeline" max="100" value="0" step="0.1">
|
||||
<div id="timeInfo" class="now-playing-info"></div>
|
||||
</div>
|
||||
<button class="sound-button icon-color" onclick="player.fileDownload()">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 120.89" width="35" height="35">
|
||||
<path fill-rule="evenodd" d="M84.58,47a7.71,7.71,0,1,1,10.8,11L66.09,86.88a7.72,7.72,0,0,1-10.82,0L26.4,58.37a7.71,7.71,0,1,1,10.81-11L53.1,63.12l.16-55.47a7.72,7.72,0,0,1,15.43.13l-.15,55L84.58,47ZM0,113.48.1,83.3a7.72,7.72,0,1,1,15.43.14l-.07,22q46,.09,91.91,0l.07-22.12a7.72,7.72,0,1,1,15.44.14l-.1,30h-.09a7.71,7.71,0,0,1-7.64,7.36q-53.73.1-107.38,0A7.7,7.7,0,0,1,0,113.48Z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="nowPlayingInfo" class="now-playing-info">
|
||||
keine Datei ausgewählt...
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<!-- Transcript Modal -->
|
||||
<div id="transcriptModal">
|
||||
<div class="modal-content">
|
||||
@ -249,19 +192,19 @@
|
||||
<div id="transcriptContent"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Gallery Modal for Images -->
|
||||
<div id="gallery-modal" style="display: none;">
|
||||
<button id="gallery-close">x</button>
|
||||
<button id="gallery-info" aria-expanded="false" aria-controls="gallery-exif">i</button>
|
||||
<img id="gallery-modal-content" src="" />
|
||||
<img id="gallery-modal-content" src="">
|
||||
<button class="gallery-nav gallery-prev">‹</button>
|
||||
<button class="gallery-nav gallery-next">›</button>
|
||||
<button id="gallery-play" aria-pressed="false">▶</button>
|
||||
<button id="gallery-play" aria-pressed="false" aria-label="Start slideshow">></button>
|
||||
<div id="gallery-exif" aria-live="polite"></div>
|
||||
<div id="gallery-filename" aria-live="polite"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="loader-container" style="display: none;">
|
||||
<div id="gallery-loader"></div>
|
||||
</div>
|
||||
@ -269,10 +212,9 @@
|
||||
<div id="fullscreen-loader" style="display: none;">
|
||||
<div class="loader"></div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Load main app JS first, then gallery JS -->
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='audioplayer.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='app.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='gallery.js') }}"></script>
|
||||
@ -281,9 +223,8 @@
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
navigator.serviceWorker.register('{{ url_for("static", filename="sw.js") }}')
|
||||
navigator.serviceWorker.register('{{ url_for("static", filename="sw.js") }}');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@ -10,6 +10,10 @@
|
||||
<!-- Android Theme Color -->
|
||||
<meta name="theme-color" content="#000">
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='theme.css') }}">
|
||||
@ -29,23 +33,27 @@
|
||||
{% if admin_enabled %}
|
||||
<!-- Navigation Bar -->
|
||||
<div class="admin-nav">
|
||||
<a href="{{ url_for('index') }}">App</a>
|
||||
<span> | </span>
|
||||
<a href="{{ url_for('mylinks') }}">Meine Links</a>
|
||||
<span> | </span>
|
||||
<a href="{{ url_for('folder_secret_config_editor') }}" id="edit-folder-config">Ordnerkonfiguration</a>
|
||||
<span> | </span>
|
||||
<div class="dropdown d-inline-block">
|
||||
<a class="dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Auswertungen
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="{{ url_for('dashboard') }}">Dashbord</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('connections') }}">Verbindungen</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('file_access') }}">Dateizugriffe</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('songs_dashboard') }}">Wiederholungen</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('search_db_analyzer') }}">Dateiindex Analyse</a></li>
|
||||
</ul>
|
||||
<div class="admin-nav-rail">
|
||||
<div class="admin-nav-track">
|
||||
<a href="{{ url_for('index') }}">App</a>
|
||||
<span> | </span>
|
||||
<a href="{{ url_for('mylinks') }}">Meine Links</a>
|
||||
<span> | </span>
|
||||
<a href="{{ url_for('folder_secret_config_editor') }}" id="edit-folder-config">Ordnerkonfiguration</a>
|
||||
<span> | </span>
|
||||
<div class="dropdown dropend d-inline-block">
|
||||
<a class="dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" data-bs-display="static" aria-expanded="false">
|
||||
Auswertungen
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="dropdown-item" href="{{ url_for('dashboard') }}">Dashbord</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('connections') }}">Verbindungen</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('file_access') }}">Dateizugriffe</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('songs_dashboard') }}">Wiederholungen</a></li>
|
||||
<li><a class="dropdown-item" href="{{ url_for('search_db_analyzer') }}">Dateiindex Analyse</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -54,7 +62,7 @@
|
||||
</div>
|
||||
|
||||
{% block scripts %}{% endblock %}
|
||||
<script src="{{ url_for('static', filename='general.js') }}"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="{{ url_for('static', filename='general.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -34,47 +34,63 @@
|
||||
<ul class="dropdown-menu" aria-labelledby="timeframeDropdown">
|
||||
<li>
|
||||
<a class="dropdown-item {% if session['timeframe'] == 'last24hours' %}active{% endif %}"
|
||||
href="{{ url_for('file_access', timeframe='last24hours') }}">
|
||||
href="{{ url_for('file_access', timeframe='last24hours', category=selected_category) }}">
|
||||
Last 24 Hours
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item {% if session['timeframe'] == '7days' %}active{% endif %}"
|
||||
href="{{ url_for('file_access', timeframe='7days') }}">
|
||||
href="{{ url_for('file_access', timeframe='7days', category=selected_category) }}">
|
||||
Last 7 Days
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item {% if session['timeframe'] == '14days' %}active{% endif %}"
|
||||
href="{{ url_for('file_access', timeframe='14days') }}">
|
||||
href="{{ url_for('file_access', timeframe='14days', category=selected_category) }}">
|
||||
Last 14 Days
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item {% if session['timeframe'] == '30days' %}active{% endif %}"
|
||||
href="{{ url_for('file_access', timeframe='30days') }}">
|
||||
href="{{ url_for('file_access', timeframe='30days', category=selected_category) }}">
|
||||
Last 30 Days
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item {% if session['timeframe'] == '365days' %}active{% endif %}"
|
||||
href="{{ url_for('file_access', timeframe='365days') }}">
|
||||
href="{{ url_for('file_access', timeframe='365days', category=selected_category) }}">
|
||||
Last 365 Days
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Category Dropdown -->
|
||||
{% if categories %}
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-secondary dropdown-toggle"
|
||||
type="button" id="categoryDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
{{ selected_category_label }}
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="categoryDropdown">
|
||||
{% for option in categories %}
|
||||
<li>
|
||||
<a class="dropdown-item {% if selected_category == option.value %}active{% endif %}"
|
||||
href="{{ url_for('file_access', timeframe=session['timeframe'], category=option.value) }}">
|
||||
{{ option.label }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Detailed Table of Top File Accesses -->
|
||||
{% for top20_item in top20 %}
|
||||
<div class="card mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>{{ top20_item['category'] }}</span>
|
||||
<span class="toggle-icon" aria-label="collapse" role="button" tabindex="0">+</span>
|
||||
</div>
|
||||
<div class="card-body collapsable">
|
||||
<div class="card-header">{{ selected_category_label }}</div>
|
||||
<div class="card-body">
|
||||
{% if top20_files %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
@ -84,23 +100,20 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in top20_item['files'] %}
|
||||
{% for row in top20_files %}
|
||||
<tr>
|
||||
<td>{{ row.access_count }}</td>
|
||||
<td>{{ row.rel_path }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr>
|
||||
<td colspan="2">No data available for the selected timeframe.</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="mb-0">No data available for the selected timeframe and category.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
@ -1,53 +1,36 @@
|
||||
<style>
|
||||
.container-fluid {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.qr-code {
|
||||
max-width: 300px;
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
.copy-btn {
|
||||
margin-top: 1rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem 1rem;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
border: 1px solid #007bff;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
.copy-btn:active {
|
||||
background-color: #0056b3;
|
||||
border-color: #0056b3;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='token.css') }}">
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<img src="data:image/png;base64,{{ token_qr_code }}" class="card-img-top qr-code p-3" alt="QR Code for token">
|
||||
<div class="container-fluid token-container">
|
||||
{% set folder_names = token_folder | map(attribute='foldername') | list %}
|
||||
<div class="card h-100 shadow-sm text-center" id="tokenPrintable">
|
||||
<h2 class="token-page-title">{{ title_long }}</h2>
|
||||
{% if folder_names %}
|
||||
<h3 class="token-folder-title">{{ folder_names | join(', ') }}</h3>
|
||||
{% endif %}
|
||||
<div class="token-qr-wrap mt-3">
|
||||
<img src="/custom_logo/logoW.png" class="token-pdf-logo" alt="Logo">
|
||||
<img src="data:image/png;base64,{{ token_qr_code }}" class="card-img-top qr-code p-3" alt="QR Code for token">
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card-text mt-2">
|
||||
<h2 class="card-title">Token-Link:</h2>
|
||||
<a href="{{ token_url }}" class="copy-btn" style="text-decoration: none;" id="tokenLink">Link öffnen</a>
|
||||
<button class="copy-btn" onclick="toClipboard('{{ token_url }}')">Link kopieren</button>
|
||||
<div class="token-action-buttons d-flex flex-wrap gap-2 justify-content-center text-center">
|
||||
<a href="{{ token_url }}" class="copy-btn" style="text-decoration: none;" id="tokenLink">Link öffnen</a>
|
||||
<button class="copy-btn" onclick="toClipboard('{{ token_url }}')">Link kopieren</button>
|
||||
<button class="copy-btn" id="printTokenButton" type="button" onclick="printToken()">Drucken</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-text mt-2">
|
||||
<h2 class="mt-3">Gültig bis:</h2>
|
||||
{{ token_valid_to }}
|
||||
</div>
|
||||
<div class="card-text mt-2">
|
||||
<!-- <div class="card-text mt-2">
|
||||
<h2 class="mt-3">Ordner:</h2>
|
||||
{% for folder in token_folder %}
|
||||
{{ folder.foldername }}<br>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user