Spaces:
Running
Running
File size: 9,313 Bytes
c001f24 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | {% extends "base.html" %}
{% block title %}Drive Manager{% endblock %}
{% block styles %}
<style>
.source-card {
cursor: pointer;
transition: all 0.3s ease;
}
.source-card:hover {
transform: translateY(-5px);
border-color: #0d6efd !important;
box-shadow: 0 8px 16px rgba(13, 110, 253, 0.3);
}
.recent-pdf-card:hover {
transform: translateY(-3px) scale(1.02);
box-shadow: 0 6px 20px rgba(13, 110, 253, 0.4);
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid mt-4" style="width: 90%; margin: auto;">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1>Drive Sync Manager</h1>
<div>
{% if not drive_connected %}
<a href="/drive/connect" class="btn btn-outline-warning me-2">
<i class="bi bi-google me-1"></i> Connect Drive
</a>
{% endif %}
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addSourceModal">
<i class="bi bi-plus-lg me-1"></i> Add Drive Source
</button>
</div>
</div>
{% if recent_pdfs %}
<div class="card border-secondary mb-4" style="background: linear-gradient(135deg, #1a1d20 0%, #212529 100%);">
<div class="card-header border-secondary" style="background: linear-gradient(90deg, rgba(13,110,253,0.15) 0%, rgba(13,110,253,0.05) 100%);">
<h5 class="mb-0 text-primary"><i class="bi bi-clock-history me-2"></i>Recently Opened PDFs</h5>
</div>
<div class="card-body">
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-4 g-3">
{% for pdf in recent_pdfs %}
<div class="col">
<div class="card h-100 border-primary source-card" onclick="location.href='/drive/api/open/{{ pdf.file_id }}'" style="background: linear-gradient(135deg, #212529 0%, #2b3035 100%); transition: all 0.3s ease;">
<div class="card-body d-flex flex-column">
<div class="mb-2 flex-grow-1">
<h6 class="card-title text-primary mb-2" style="word-wrap: break-word; line-height: 1.4;">
<i class="bi bi-file-pdf-fill me-2"></i>{{ pdf.filename }}
</h6>
</div>
<div class="mt-auto pt-2 border-top border-secondary">
<small class="text-muted">
<i class="bi bi-clock me-1"></i>{{ pdf.opened_at }}
</small>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endif %}
{% if drive_connected %}
<div class="card bg-dark text-white border-warning mb-4 source-card" onclick="location.href='/drive/api/browse/root'">
<div class="card-body d-flex justify-content-between align-items-center">
<div>
<h5 class="card-title text-warning mb-0"><i class="bi bi-google me-2"></i>My Drive</h5>
<small class="text-muted">Browse your personal Google Drive (API)</small>
</div>
<i class="bi bi-chevron-right text-muted"></i>
</div>
</div>
{% endif %}
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4" id="sources-grid">
{% for source in sources %}
<div class="col">
<div class="card h-100 bg-dark text-white border-secondary source-card">
<div class="card-body" onclick="location.href='/drive/browse/{{ source.id }}'">
<div class="d-flex justify-content-between align-items-start mb-2">
<h5 class="card-title text-primary">
{% if source.source_type == 'file' %}
<i class="bi bi-file-earmark-arrow-down me-2"></i>
{% else %}
<i class="bi bi-hdd-network me-2"></i>
{% endif %}
{{ source.name }}
</h5>
<div class="dropdown" onclick="event.stopPropagation()">
<button class="btn btn-link text-muted p-0" data-bs-toggle="dropdown">
<i class="bi bi-three-dots-vertical"></i>
</button>
<ul class="dropdown-menu dropdown-menu-dark">
<li><button class="dropdown-item sync-btn" data-id="{{ source.id }}"><i class="bi bi-arrow-repeat me-2"></i>Sync Now</button></li>
<li><hr class="dropdown-divider"></li>
<li><button class="dropdown-item text-danger delete-btn" data-id="{{ source.id }}"><i class="bi bi-trash me-2"></i>Delete</button></li>
</ul>
</div>
</div>
<p class="card-text text-muted small text-truncate">{{ source.url }}</p>
<div class="mt-3">
<small class="text-secondary">
Last Synced: {{ source.last_synced or 'Never' }}
</small>
</div>
</div>
</div>
</div>
{% else %}
<div class="col-12 text-center text-muted py-5">
<i class="bi bi-cloud-slash display-4 mb-3 d-block"></i>
<p>No drive sources added yet.</p>
</div>
{% endfor %}
</div>
</div>
<!-- Add Modal -->
<div class="modal fade" id="addSourceModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content bg-dark text-white">
<div class="modal-header">
<h5 class="modal-title">Add Public Drive Folder</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="add-source-form">
<div class="mb-3">
<label class="form-label">Name (Local Folder Name)</label>
<input type="text" class="form-control" name="name" required placeholder="e.g. Question Papers 2024">
</div>
<div class="mb-3">
<label class="form-label">Drive Folder URL</label>
<input type="url" class="form-control" name="url" required placeholder="https://drive.google.com/drive/folders/...">
<div class="form-text text-muted">Must be a publicly accessible Google Drive folder link.</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="save-source-btn">Add Source</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
document.getElementById('save-source-btn').addEventListener('click', async () => {
const form = document.getElementById('add-source-form');
if (!form.checkValidity()) {
form.reportValidity();
return;
}
const formData = new FormData(form);
try {
const res = await fetch('/drive/add', {
method: 'POST',
body: formData
});
const data = await res.json();
if (data.success) {
location.reload();
} else {
alert('Error: ' + data.error);
}
} catch (e) {
alert('Error adding source');
}
});
document.querySelectorAll('.sync-btn').forEach(btn => {
btn.addEventListener('click', async () => {
const id = btn.dataset.id;
if(!confirm('Start sync in background? Large folders may take time.')) return;
try {
const res = await fetch(`/drive/sync/${id}`, { method: 'POST' });
const data = await res.json();
if (data.success) alert('Sync started!');
else alert('Error: ' + data.error);
} catch (e) {
alert('Request failed');
}
});
});
document.querySelectorAll('.delete-btn').forEach(btn => {
btn.addEventListener('click', async () => {
const id = btn.dataset.id;
if(!confirm('Delete this source and all downloaded files locally?')) return;
try {
const res = await fetch(`/drive/delete/${id}`, { method: 'POST' });
const data = await res.json();
if (data.success) location.reload();
else alert('Error: ' + data.error);
} catch (e) {
alert('Request failed');
}
});
});
</script>
{% endblock %}
|