1128 lines
35 KiB
HTML
1128 lines
35 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Calendar{% endblock %}
|
|
|
|
{% block extra_head %}
|
|
<style>
|
|
.calendar-container {
|
|
margin: 20px 0;
|
|
height: calc(100vh - 250px);
|
|
min-height: 600px;
|
|
max-height: calc(100vh - 200px);
|
|
border-radius: 8px;
|
|
overflow: auto;
|
|
border: 1px solid #30363d;
|
|
background-color: #161b22;
|
|
padding: 15px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.calendar-container {
|
|
height: calc(100vh - 200px);
|
|
max-height: calc(100vh - 150px);
|
|
overflow-y: auto;
|
|
}
|
|
}
|
|
|
|
/* Make sure the container scrolls properly */
|
|
html, body, .app-container, .main-area, .content-area {
|
|
height: 100%;
|
|
overflow: auto;
|
|
}
|
|
|
|
.calendar-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 25px;
|
|
padding: 0 15px;
|
|
background-color: #21262d;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.calendar-options {
|
|
display: flex;
|
|
gap: 25px;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.calendar-filter {
|
|
margin-right: 10px;
|
|
color: #ffffff;
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* Status colors */
|
|
.status-downloaded {
|
|
background-color: #28a745 !important;
|
|
border-color: #28a745 !important;
|
|
color: white !important;
|
|
}
|
|
|
|
.status-error {
|
|
background-color: #dc3545 !important;
|
|
border-color: #dc3545 !important;
|
|
color: white !important;
|
|
}
|
|
|
|
.status-downloading {
|
|
background-color: #6f42c1 !important;
|
|
border-color: #6f42c1 !important;
|
|
color: white !important;
|
|
}
|
|
|
|
.status-unmonitored {
|
|
background-color: #999999 !important;
|
|
border-color: #999999 !important;
|
|
color: white !important;
|
|
}
|
|
|
|
.calendar-legend {
|
|
display: flex;
|
|
gap: 15px;
|
|
margin: 15px 0;
|
|
flex-wrap: wrap;
|
|
padding: 12px 15px;
|
|
background-color: #21262d;
|
|
border-radius: 8px;
|
|
justify-content: center;
|
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.legend-item {
|
|
display: flex;
|
|
align-items: center;
|
|
font-size: 14px;
|
|
color: #ffffff;
|
|
background-color: #2d333b;
|
|
padding: 6px 12px;
|
|
border-radius: 6px;
|
|
border: 1px solid #30363d;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.legend-color {
|
|
width: 18px;
|
|
height: 18px;
|
|
margin-right: 8px;
|
|
border-radius: 4px;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
/* Responsive styles */
|
|
@media (max-width: 768px) {
|
|
.calendar-container {
|
|
height: calc(100vh - 300px);
|
|
}
|
|
|
|
.calendar-options {
|
|
gap: 10px;
|
|
}
|
|
}
|
|
|
|
/* Fix for overflow issues */
|
|
.content-area {
|
|
overflow: auto !important;
|
|
}
|
|
|
|
.main-area {
|
|
overflow: auto !important;
|
|
}
|
|
|
|
/* Ensure calendar content scrolls properly */
|
|
.calendar-day-events {
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.calendar-week-days {
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.calendar-day-detail-events {
|
|
overflow-y: auto;
|
|
}
|
|
|
|
/* Form control styling to match app theme */
|
|
.form-control-sm {
|
|
height: 34px;
|
|
padding: 0 12px;
|
|
background-color: #2d333b;
|
|
border: 1px solid #444c56;
|
|
border-radius: 6px;
|
|
color: #ffffff;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
transition: all 0.2s ease;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.form-control-sm:focus {
|
|
outline: none;
|
|
border-color: #58a6ff;
|
|
box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.3);
|
|
background-color: #2d333b;
|
|
}
|
|
|
|
.form-control-sm:hover {
|
|
border-color: #58a6ff;
|
|
background-color: #2d333b;
|
|
}
|
|
|
|
/* Loading indicator */
|
|
.calendar-loading {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: rgba(13, 17, 23, 0.8);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 10;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.loading-spinner {
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 3px solid rgba(88, 166, 255, 0.3);
|
|
border-radius: 50%;
|
|
border-top-color: #58a6ff;
|
|
animation: spin 1s linear infinite;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.loading-text {
|
|
color: #f0f6fc;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
/* Animation for calendar appearance */
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(10px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/* Button group styles */
|
|
.btn-group {
|
|
display: flex;
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.btn-group .btn {
|
|
border-radius: 0;
|
|
margin: 0;
|
|
border-right: none;
|
|
height: 36px;
|
|
padding: 0 16px;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.btn-group .btn:first-child {
|
|
border-top-left-radius: 6px;
|
|
border-bottom-left-radius: 6px;
|
|
}
|
|
|
|
.btn-group .btn:last-child {
|
|
border-top-right-radius: 6px;
|
|
border-bottom-right-radius: 6px;
|
|
border-right: 1px solid #30363d;
|
|
}
|
|
|
|
.btn-group .btn:hover {
|
|
background-color: #30363d;
|
|
}
|
|
|
|
.btn-group .btn.btn-primary {
|
|
background-color: #1f6feb;
|
|
border-color: #1f6feb;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.btn-group .btn.btn-primary:hover {
|
|
background-color: #388bfd;
|
|
}
|
|
|
|
.calendar-view-toggles {
|
|
margin-left: auto;
|
|
}
|
|
|
|
/* Calendar Day Styles */
|
|
.calendar-day {
|
|
margin-bottom: 20px;
|
|
animation: fadeIn 0.5s ease-out;
|
|
}
|
|
|
|
.calendar-day-header {
|
|
background-color: #21262d;
|
|
padding: 10px 15px;
|
|
border-radius: 6px 6px 0 0;
|
|
border: 1px solid #30363d;
|
|
border-bottom: none;
|
|
}
|
|
|
|
.calendar-day-title {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #f0f6fc;
|
|
margin: 0;
|
|
}
|
|
|
|
.calendar-day-content {
|
|
border: 1px solid #30363d;
|
|
border-radius: 0 0 6px 6px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* Calendar Event Styles */
|
|
.calendar-event {
|
|
display: flex;
|
|
border-bottom: 1px solid #30363d;
|
|
position: relative;
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
.calendar-event:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.calendar-event:hover {
|
|
background-color: #1f2937;
|
|
}
|
|
|
|
.calendar-event-status {
|
|
width: 5px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.calendar-event-content {
|
|
flex: 1;
|
|
padding: 10px 15px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.calendar-event-title {
|
|
font-weight: 600;
|
|
color: #f0f6fc;
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.calendar-event-info {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
font-size: 12px;
|
|
color: #c9d1d9;
|
|
}
|
|
|
|
.calendar-event-time {
|
|
color: #7d8590;
|
|
font-size: 11px;
|
|
}
|
|
|
|
.calendar-event-episode {
|
|
color: #7d8590;
|
|
font-size: 11px;
|
|
}
|
|
|
|
/* Table-based calendar layout */
|
|
.calendar-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
border: 1px solid #30363d;
|
|
background-color: #161b22;
|
|
}
|
|
|
|
.calendar-table th {
|
|
background-color: #21262d;
|
|
color: #f0f6fc;
|
|
font-weight: 600;
|
|
padding: 10px;
|
|
text-align: center;
|
|
border: 1px solid #30363d;
|
|
}
|
|
|
|
.calendar-table td {
|
|
border: 1px solid #30363d;
|
|
padding: 0;
|
|
vertical-align: top;
|
|
}
|
|
|
|
.calendar-day-cell {
|
|
min-height: 100px;
|
|
}
|
|
|
|
.calendar-day-number {
|
|
background-color: #21262d;
|
|
color: #f0f6fc;
|
|
font-weight: 600;
|
|
padding: 5px 10px;
|
|
text-align: center;
|
|
border-bottom: 1px solid #30363d;
|
|
}
|
|
|
|
/* Sonarr-style calendar event */
|
|
.calendar-day-events {
|
|
padding: 0;
|
|
}
|
|
|
|
.calendar-event-item {
|
|
position: relative;
|
|
border-bottom: 1px solid #30363d;
|
|
}
|
|
|
|
.calendar-event-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.calendar-event-underlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
z-index: 1;
|
|
cursor: pointer;
|
|
background: transparent;
|
|
border: none;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.calendar-event-overlay {
|
|
position: relative;
|
|
z-index: 2;
|
|
padding: 12px 15px;
|
|
display: flex;
|
|
}
|
|
|
|
.calendar-event-item:hover {
|
|
background-color: rgba(31, 41, 55, 0.5);
|
|
}
|
|
|
|
.calendar-event-status-bar {
|
|
width: 8px;
|
|
margin-right: 12px;
|
|
border-radius: 3px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.calendar-event-status-bar.status-downloaded {
|
|
background-color: #28a745;
|
|
}
|
|
|
|
.calendar-event-status-bar.status-error {
|
|
background-color: #dc3545;
|
|
}
|
|
|
|
.calendar-event-status-bar.status-downloading {
|
|
background-color: #6f42c1;
|
|
}
|
|
|
|
.calendar-event-status-bar.status-unmonitored {
|
|
background-color: #999999;
|
|
}
|
|
|
|
.calendar-event-details {
|
|
flex: 1;
|
|
min-width: 0; /* Prevent overflow */
|
|
}
|
|
|
|
.calendar-event-podcast-title {
|
|
font-weight: 600;
|
|
color: #ffffff;
|
|
margin-bottom: 5px;
|
|
font-size: 15px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.calendar-event-episode-title {
|
|
color: #e6e6e6;
|
|
margin-bottom: 5px;
|
|
font-size: 14px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.calendar-event-episode-info {
|
|
color: #a0a0a0;
|
|
font-size: 12px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.calendar-event-absolute-number {
|
|
color: #a0a0a0;
|
|
margin-left: 5px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.calendar-event-time {
|
|
color: #a0a0a0;
|
|
font-size: 12px;
|
|
white-space: nowrap;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* Month view specific event styles */
|
|
.calendar-month-grid .calendar-event-item {
|
|
margin: 3px 4px;
|
|
border-bottom: none;
|
|
border-radius: 4px;
|
|
overflow: hidden;
|
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
transition: transform 0.1s ease;
|
|
}
|
|
|
|
.calendar-month-grid .calendar-event-item:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
|
|
}
|
|
|
|
.calendar-month-grid .calendar-event-overlay {
|
|
padding: 5px 8px;
|
|
}
|
|
|
|
.calendar-month-grid .calendar-event-status-bar {
|
|
width: 4px;
|
|
margin-right: 6px;
|
|
}
|
|
|
|
.calendar-month-grid .calendar-event-podcast-title {
|
|
font-size: 12px;
|
|
margin-bottom: 0;
|
|
font-weight: 700;
|
|
letter-spacing: 0.2px;
|
|
}
|
|
|
|
.calendar-month-grid .calendar-event-episode-title,
|
|
.calendar-month-grid .calendar-event-episode-info {
|
|
display: none;
|
|
}
|
|
|
|
/* Calendar Day View - Sonarr Style */
|
|
.calendar-day-view {
|
|
display: flex;
|
|
flex-direction: column;
|
|
margin-bottom: 20px;
|
|
border: 1px solid #30363d;
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
width: 100%;
|
|
}
|
|
|
|
.calendar-day-header {
|
|
background-color: #21262d;
|
|
padding: 10px 12px;
|
|
font-weight: 600;
|
|
color: #ffffff;
|
|
border-bottom: 1px solid #30363d;
|
|
margin-bottom: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.calendar-day-number {
|
|
font-size: 18px;
|
|
font-weight: 700;
|
|
margin-right: 8px;
|
|
color: #58a6ff;
|
|
}
|
|
|
|
.calendar-day-name {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
margin-right: 5px;
|
|
}
|
|
|
|
.calendar-day-events {
|
|
display: flex;
|
|
flex-direction: column;
|
|
background-color: #161b22;
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
/* Month View Styles */
|
|
.calendar-month-view {
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
border: 1px solid #30363d;
|
|
border-radius: 6px;
|
|
overflow: auto;
|
|
animation: fadeIn 0.5s ease-out;
|
|
flex: 1;
|
|
min-height: 600px; /* Ensure enough vertical space */
|
|
}
|
|
|
|
.calendar-month-header {
|
|
background-color: #21262d;
|
|
padding: 10px 15px;
|
|
border-bottom: 1px solid #30363d;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 10;
|
|
}
|
|
|
|
.calendar-month-header h3 {
|
|
margin: 0;
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
color: #f0f6fc;
|
|
text-align: center;
|
|
}
|
|
|
|
.calendar-month-grid {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
table-layout: fixed;
|
|
min-width: 800px; /* Ensure table doesn't get too small on narrow screens */
|
|
}
|
|
|
|
.calendar-month-grid th {
|
|
background-color: #21262d;
|
|
color: #f0f6fc;
|
|
font-weight: 600;
|
|
padding: 10px;
|
|
text-align: center;
|
|
border: 1px solid #30363d;
|
|
position: sticky;
|
|
top: 45px; /* Adjust based on the height of the month header */
|
|
z-index: 5;
|
|
}
|
|
|
|
.calendar-month-grid td {
|
|
border: 1px solid #30363d;
|
|
vertical-align: top;
|
|
height: 150px;
|
|
padding: 0;
|
|
position: relative;
|
|
min-width: 120px;
|
|
max-width: 14.28%; /* 100% / 7 days = 14.28% */
|
|
width: 14.28%;
|
|
}
|
|
|
|
/* Ensure the table has enough rows */
|
|
.calendar-month-grid tbody tr {
|
|
height: 150px;
|
|
display: table-row;
|
|
}
|
|
|
|
.calendar-month-grid td.empty {
|
|
background-color: #0d1117;
|
|
}
|
|
|
|
.calendar-month-grid td.today {
|
|
background-color: rgba(88, 166, 255, 0.2);
|
|
box-shadow: inset 0 0 0 2px #58a6ff;
|
|
}
|
|
|
|
.calendar-month-grid td.today .calendar-day-number {
|
|
background-color: #1f6feb;
|
|
color: #ffffff;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.calendar-day-cell .calendar-day-number {
|
|
background-color: #21262d;
|
|
color: #ffffff;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
padding: 8px;
|
|
text-align: center;
|
|
border-bottom: 1px solid #30363d;
|
|
display: block;
|
|
}
|
|
|
|
.calendar-day-cell .calendar-day-events {
|
|
max-height: calc(100% - 30px);
|
|
overflow-y: auto;
|
|
padding: 0;
|
|
height: 120px; /* Increased height for more content */
|
|
}
|
|
|
|
.calendar-no-episodes {
|
|
padding: 10px;
|
|
color: #a0a0a0;
|
|
text-align: center;
|
|
font-size: 13px;
|
|
font-style: italic;
|
|
background-color: rgba(33, 38, 45, 0.3);
|
|
margin: 5px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
/* Week View Styles */
|
|
.calendar-week-view {
|
|
display: flex;
|
|
flex-direction: column;
|
|
border: 1px solid #30363d;
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
animation: fadeIn 0.5s ease-out;
|
|
width: 100%;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.calendar-week-header {
|
|
background-color: #21262d;
|
|
padding: 12px 18px;
|
|
border-bottom: 1px solid #30363d;
|
|
}
|
|
|
|
.calendar-week-header h3 {
|
|
margin: 0;
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
color: #ffffff;
|
|
text-align: center;
|
|
}
|
|
|
|
.calendar-week-days {
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
gap: 2px;
|
|
background-color: #30363d;
|
|
padding: 2px;
|
|
}
|
|
|
|
.calendar-week-day {
|
|
display: flex;
|
|
flex-direction: column;
|
|
background-color: #161b22;
|
|
min-height: 250px;
|
|
border-radius: 4px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* Day Detail View Styles */
|
|
.calendar-day-detail-view {
|
|
display: flex;
|
|
flex-direction: column;
|
|
border: 1px solid #30363d;
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
animation: fadeIn 0.5s ease-out;
|
|
width: 100%;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.calendar-day-detail-header {
|
|
background-color: #21262d;
|
|
padding: 15px 20px;
|
|
border-bottom: 1px solid #30363d;
|
|
}
|
|
|
|
.calendar-day-detail-header h3 {
|
|
margin: 0;
|
|
font-size: 20px;
|
|
font-weight: 600;
|
|
color: #ffffff;
|
|
text-align: center;
|
|
}
|
|
|
|
.calendar-day-detail-events {
|
|
display: flex;
|
|
flex-direction: column;
|
|
background-color: #161b22;
|
|
padding: 15px;
|
|
gap: 12px;
|
|
}
|
|
|
|
.calendar-event-item-detailed {
|
|
margin-bottom: 0;
|
|
border: 1px solid #30363d;
|
|
border-radius: 6px;
|
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
|
|
transition: transform 0.1s ease, box-shadow 0.1s ease;
|
|
}
|
|
|
|
.calendar-event-item-detailed:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.calendar-no-events {
|
|
padding: 30px;
|
|
text-align: center;
|
|
color: #a0a0a0;
|
|
font-size: 16px;
|
|
font-style: italic;
|
|
background-color: rgba(33, 38, 45, 0.3);
|
|
border-radius: 6px;
|
|
margin: 20px;
|
|
}
|
|
|
|
/* Responsive styles for week view */
|
|
@media (max-width: 768px) {
|
|
.calendar-week-days {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.calendar-week-day {
|
|
margin-bottom: 1px;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<section class="calendar">
|
|
<h2>Podcast Calendar</h2>
|
|
|
|
<div class="calendar-header">
|
|
<div class="calendar-options">
|
|
<div>
|
|
<a href="{{ url_for('calendar.ical') }}" class="btn btn-sm">iCal Link</a>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="calendar-filter" class="calendar-filter">Show:</label>
|
|
<select id="calendar-filter" class="form-control-sm">
|
|
{% if settings.calendar_show_monitored_only %}
|
|
<option value="all">All Podcasts</option>
|
|
<option value="monitored" selected>Monitored Only</option>
|
|
{% else %}
|
|
<option value="all" selected>All Podcasts</option>
|
|
<option value="monitored">Monitored Only</option>
|
|
{% endif %}
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="first-day" class="calendar-filter">First day:</label>
|
|
<select id="first-day" class="form-control-sm">
|
|
{% if settings.calendar_first_day == 'Monday' %}
|
|
<option value="1" selected>Monday</option>
|
|
<option value="0">Sunday</option>
|
|
{% else %}
|
|
<option value="1">Monday</option>
|
|
<option value="0" selected>Sunday</option>
|
|
{% endif %}
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="calendar-view-toggles">
|
|
<div class="btn-group">
|
|
<a href="{{ url_for('calendar.index', view='month') }}" class="btn btn-sm {% if view_type == 'month' %}btn-primary{% endif %}">Month</a>
|
|
<a href="{{ url_for('calendar.index', view='week') }}" class="btn btn-sm {% if view_type == 'week' %}btn-primary{% endif %}">Week</a>
|
|
<a href="{{ url_for('calendar.index', view='day') }}" class="btn btn-sm {% if view_type == 'day' %}btn-primary{% endif %}">Day</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="calendar-legend">
|
|
<div class="legend-item">
|
|
<div class="legend-color status-downloaded"></div>
|
|
<span>Downloaded</span>
|
|
</div>
|
|
<div class="legend-item">
|
|
<div class="legend-color status-error"></div>
|
|
<span>Missing</span>
|
|
</div>
|
|
<div class="legend-item">
|
|
<div class="legend-color status-downloading"></div>
|
|
<span>Downloading</span>
|
|
</div>
|
|
<div class="legend-item">
|
|
<div class="legend-color status-unmonitored"></div>
|
|
<span>Unmonitored</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="calendar-container">
|
|
{% if view_type == 'month' %}
|
|
<!-- Month View -->
|
|
<div class="calendar-month-view">
|
|
<div class="calendar-month-header">
|
|
<h3>{{ selected_date.strftime('%B %Y') }}</h3>
|
|
</div>
|
|
<table class="calendar-month-grid">
|
|
<thead>
|
|
<tr>
|
|
{% if settings.calendar_first_day == 'Monday' %}
|
|
<th>Mon</th>
|
|
<th>Tue</th>
|
|
<th>Wed</th>
|
|
<th>Thu</th>
|
|
<th>Fri</th>
|
|
<th>Sat</th>
|
|
<th>Sun</th>
|
|
{% else %}
|
|
<th>Sun</th>
|
|
<th>Mon</th>
|
|
<th>Tue</th>
|
|
<th>Wed</th>
|
|
<th>Thu</th>
|
|
<th>Fri</th>
|
|
<th>Sat</th>
|
|
{% endif %}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% set first_day_of_week = 0 if settings.calendar_first_day == 'Sunday' else 1 %}
|
|
{% set month_first_day = first_day.replace(day=1) %}
|
|
{% set month_start_weekday = month_first_day.weekday() %}
|
|
{% set adjusted_weekday = (month_start_weekday - first_day_of_week) % 7 %}
|
|
|
|
{% set day_counter = 0 %}
|
|
{% set total_days = days_in_month|length %}
|
|
{% set current_row = 0 %}
|
|
|
|
{# Start with the first row #}
|
|
<tr>
|
|
{# Add empty cells for days before the first of the month #}
|
|
{% for _ in range(adjusted_weekday) %}
|
|
<td class="calendar-day-cell empty"></td>
|
|
{% set day_counter = day_counter + 1 %}
|
|
{% endfor %}
|
|
|
|
{# Add cells for each day of the month #}
|
|
{% for day in days_in_month %}
|
|
{% set day_str = day.strftime('%Y-%m-%d') %}
|
|
|
|
{# Start a new row after every 7 cells #}
|
|
{% if day_counter % 7 == 0 and day_counter > 0 %}
|
|
</tr><tr>
|
|
{% set current_row = current_row + 1 %}
|
|
{% endif %}
|
|
|
|
<td class="calendar-day-cell {% if day.date() == today.date() %}today{% endif %}">
|
|
<div class="calendar-day-number">{{ day.strftime('%d') }}</div>
|
|
<div class="calendar-day-events">
|
|
{% for episode in episodes_by_day[day_str] %}
|
|
<div class="calendar-event-item">
|
|
<a href="{{ episode.url }}" class="calendar-event-underlay"></a>
|
|
<div class="calendar-event-overlay">
|
|
<div class="calendar-event-status-bar {{ episode.status_class }}"></div>
|
|
<div class="calendar-event-details">
|
|
<div class="calendar-event-podcast-title">{{ episode.podcast_title }}</div>
|
|
<div class="calendar-event-episode-title">{{ episode.title }}</div>
|
|
<div class="calendar-event-episode-info">
|
|
<div>
|
|
{% if episode.season and episode.episode_number %}
|
|
{{ episode.season }}x{{ episode.episode_number }}
|
|
{% elif episode.episode_number %}
|
|
{{ episode.episode_number }}
|
|
{% endif %}
|
|
</div>
|
|
<div class="calendar-event-time">{{ episode.air_time }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="calendar-no-episodes">No episodes</div>
|
|
{% endfor %}
|
|
</div>
|
|
</td>
|
|
|
|
{% set day_counter = day_counter + 1 %}
|
|
{% endfor %}
|
|
|
|
{# Add empty cells for days after the last day of the month #}
|
|
{% set remaining_cells = 7 - (day_counter % 7) %}
|
|
{% if remaining_cells < 7 %}
|
|
{% for _ in range(remaining_cells) %}
|
|
<td class="calendar-day-cell empty"></td>
|
|
{% endfor %}
|
|
{% endif %}
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% elif view_type == 'week' %}
|
|
<!-- Week View -->
|
|
<div class="calendar-week-view">
|
|
<div class="calendar-week-header">
|
|
<h3>Week of {{ start_of_week.strftime('%B %d') }} - {{ end_of_week.strftime('%B %d, %Y') }}</h3>
|
|
</div>
|
|
<div class="calendar-week-days">
|
|
{% for current_day in days_to_include %}
|
|
{% set day_str = current_day.strftime('%Y-%m-%d') %}
|
|
<div class="calendar-week-day">
|
|
<div class="calendar-day-header">
|
|
<span class="calendar-day-name">{{ current_day.strftime('%A') }}</span>
|
|
<span class="calendar-day-number">{{ current_day.strftime('%d') }}</span>
|
|
</div>
|
|
<div class="calendar-day-events">
|
|
{% if day_str in episodes_by_day %}
|
|
{% for episode in episodes_by_day[day_str] %}
|
|
<div class="calendar-event-item">
|
|
<a href="{{ episode.url }}" class="calendar-event-underlay"></a>
|
|
<div class="calendar-event-overlay">
|
|
<div class="calendar-event-status-bar {{ episode.status_class }}"></div>
|
|
<div class="calendar-event-details">
|
|
<div class="calendar-event-podcast-title">{{ episode.podcast_title }}</div>
|
|
<div class="calendar-event-episode-title">{{ episode.title }}</div>
|
|
<div class="calendar-event-episode-info">
|
|
<div>
|
|
{% if episode.season and episode.episode_number %}
|
|
{{ episode.season }}x{{ episode.episode_number }}
|
|
{% elif episode.episode_number %}
|
|
{{ episode.episode_number }}
|
|
{% endif %}
|
|
</div>
|
|
<div class="calendar-event-time">{{ episode.air_time }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="calendar-event-item" style="padding: 10px; color: #7d8590; text-align: center;">
|
|
No episodes
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="calendar-event-item" style="padding: 10px; color: #7d8590; text-align: center;">
|
|
No episodes
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
{% elif view_type == 'day' %}
|
|
<!-- Day View -->
|
|
<div class="calendar-day-detail-view">
|
|
<div class="calendar-day-detail-header">
|
|
<h3>{{ selected_date.strftime('%A, %B %d, %Y') }}</h3>
|
|
</div>
|
|
<div class="calendar-day-detail-events">
|
|
{% set day_str = selected_date.strftime('%Y-%m-%d') %}
|
|
{% if day_str in episodes_by_day %}
|
|
{% for episode in episodes_by_day[day_str] %}
|
|
<div class="calendar-event-item calendar-event-item-detailed">
|
|
<a href="{{ episode.url }}" class="calendar-event-underlay"></a>
|
|
<div class="calendar-event-overlay">
|
|
<div class="calendar-event-status-bar {{ episode.status_class }}"></div>
|
|
<div class="calendar-event-details">
|
|
<div class="calendar-event-podcast-title">{{ episode.podcast_title }}</div>
|
|
<div class="calendar-event-episode-title">{{ episode.title }}</div>
|
|
<div class="calendar-event-episode-info">
|
|
<div>
|
|
{% if episode.season and episode.episode_number %}
|
|
{{ episode.season }}x{{ episode.episode_number }}
|
|
{% elif episode.episode_number %}
|
|
{{ episode.episode_number }}
|
|
{% endif %}
|
|
</div>
|
|
<div class="calendar-event-time">{{ episode.air_time }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="calendar-no-events">
|
|
<p>No episodes for this day</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</section>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Handle filter change
|
|
document.getElementById('calendar-filter').addEventListener('change', function() {
|
|
const showMonitoredOnly = this.value === 'monitored';
|
|
|
|
// Save the setting via AJAX
|
|
// Create URLSearchParams object
|
|
const params = new URLSearchParams({
|
|
'download_path': '{{ settings.download_path }}',
|
|
'naming_format': '{{ settings.naming_format }}',
|
|
'max_downloads': '{{ settings.max_downloads }}',
|
|
'delete_after_days': '{{ settings.delete_after_days }}',
|
|
'calendar_first_day': document.getElementById('first-day').value === '1' ? 'Monday' : 'Sunday'
|
|
});
|
|
|
|
// Only add calendar_show_monitored_only if it's enabled
|
|
if (showMonitoredOnly) {
|
|
params.append('calendar_show_monitored_only', 'on');
|
|
}
|
|
|
|
fetch('{{ url_for("settings.index") }}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
},
|
|
body: params
|
|
}).then(() => {
|
|
// Reload the page to refresh the calendar
|
|
window.location.reload();
|
|
});
|
|
});
|
|
|
|
// Handle first day change
|
|
document.getElementById('first-day').addEventListener('change', function() {
|
|
const firstDay = parseInt(this.value);
|
|
|
|
// Save the setting via AJAX
|
|
// Create URLSearchParams object
|
|
const params = new URLSearchParams({
|
|
'download_path': '{{ settings.download_path }}',
|
|
'naming_format': '{{ settings.naming_format }}',
|
|
'max_downloads': '{{ settings.max_downloads }}',
|
|
'delete_after_days': '{{ settings.delete_after_days }}',
|
|
'calendar_first_day': firstDay === 1 ? 'Monday' : 'Sunday'
|
|
});
|
|
|
|
// Only add calendar_show_monitored_only if it's enabled
|
|
if (document.getElementById('calendar-filter').value === 'monitored') {
|
|
params.append('calendar_show_monitored_only', 'on');
|
|
}
|
|
|
|
fetch('{{ url_for("settings.index") }}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
},
|
|
body: params
|
|
}).then(() => {
|
|
// Reload the page to refresh the calendar
|
|
window.location.reload();
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %}
|