batman
This commit is contained in:
commit
acb3c7642a
23 changed files with 3940 additions and 0 deletions
42
app/templates/auth/login.html
Normal file
42
app/templates/auth/login.html
Normal file
|
@ -0,0 +1,42 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Sign In - Flask Files{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="auth-container">
|
||||
<h2>Sign In</h2>
|
||||
|
||||
<form method="POST" action="{{ url_for('auth.login') }}" class="auth-form">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="form-group">
|
||||
{{ form.username.label }}
|
||||
{{ form.username(size=32, class="form-control") }}
|
||||
{% for error in form.username.errors %}
|
||||
<span class="error">{{ error }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{{ form.password.label }}
|
||||
{{ form.password(size=32, class="form-control") }}
|
||||
{% for error in form.password.errors %}
|
||||
<span class="error">{{ error }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="form-group checkbox">
|
||||
{{ form.remember_me() }}
|
||||
{{ form.remember_me.label }}
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
{{ form.submit(class="btn primary") }}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="auth-links">
|
||||
<p>Don't have an account? <a href="{{ url_for('auth.register') }}">Register here</a></p>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
34
app/templates/auth/profile.html
Normal file
34
app/templates/auth/profile.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Profile - Flask Files{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="profile-container">
|
||||
<h2>User Profile</h2>
|
||||
|
||||
<div class="profile-card">
|
||||
<div class="profile-header">
|
||||
<div class="avatar">
|
||||
{{ current_user.username[0].upper() }}
|
||||
</div>
|
||||
<h3>{{ current_user.username }}</h3>
|
||||
</div>
|
||||
|
||||
<div class="profile-stats">
|
||||
<div class="stat-item">
|
||||
<span class="stat-value">{{ current_user.files.count() }}</span>
|
||||
<span class="stat-label">Files</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-value">{{ current_user.shares.count() }}</span>
|
||||
<span class="stat-label">Shares</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="profile-actions">
|
||||
<a href="#" class="btn">Change Password</a>
|
||||
<a href="{{ url_for('files.browser') }}" class="btn primary">Manage Files</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
45
app/templates/auth/register.html
Normal file
45
app/templates/auth/register.html
Normal file
|
@ -0,0 +1,45 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Register - Flask Files{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="auth-container">
|
||||
<h2>Create an Account</h2>
|
||||
|
||||
<form method="POST" action="{{ url_for('auth.register') }}" class="auth-form">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="form-group">
|
||||
{{ form.username.label }}
|
||||
{{ form.username(size=32, class="form-control") }}
|
||||
{% for error in form.username.errors %}
|
||||
<span class="error">{{ error }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{{ form.password.label }}
|
||||
{{ form.password(size=32, class="form-control") }}
|
||||
{% for error in form.password.errors %}
|
||||
<span class="error">{{ error }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{{ form.password2.label }}
|
||||
{{ form.password2(size=32, class="form-control") }}
|
||||
{% for error in form.password2.errors %}
|
||||
<span class="error">{{ error }}</span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
{{ form.submit(class="btn primary") }}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="auth-links">
|
||||
<p>Already have an account? <a href="{{ url_for('auth.login') }}">Sign in</a></p>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
157
app/templates/base.html
Normal file
157
app/templates/base.html
Normal file
|
@ -0,0 +1,157 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="light">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Flask Files{% endblock %}</title>
|
||||
|
||||
<!-- Classless CSS Framework -->
|
||||
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/digitallytailored/classless@latest/classless.min.css"> -->
|
||||
|
||||
<!-- Custom CSS -->
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/custom.css') }}">
|
||||
|
||||
<!-- Font Awesome Icons -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||||
|
||||
{% block extra_css %}{% endblock %}
|
||||
<!-- JavaScript -->
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Close alert buttons
|
||||
document.querySelectorAll('.alert .close').forEach(function (alert) {
|
||||
alert.addEventListener('click', function () {
|
||||
this.parentElement.style.display = 'none';
|
||||
});
|
||||
});
|
||||
|
||||
// Dark mode toggle
|
||||
const darkModeToggle = document.getElementById('darkModeToggle');
|
||||
if (darkModeToggle) {
|
||||
function setColorScheme(scheme) {
|
||||
document.documentElement.setAttribute('data-theme', scheme);
|
||||
localStorage.setItem('color-scheme', scheme);
|
||||
}
|
||||
|
||||
function getColorScheme() {
|
||||
let scheme = localStorage.getItem('color-scheme');
|
||||
if (scheme) {
|
||||
return scheme;
|
||||
}
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
setColorScheme(getColorScheme());
|
||||
|
||||
darkModeToggle.addEventListener('click', function () {
|
||||
const newScheme = getColorScheme() === 'dark' ? 'light' : 'dark';
|
||||
setColorScheme(newScheme);
|
||||
});
|
||||
|
||||
darkModeToggle.checked = getColorScheme() === 'dark';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% block extra_js %}{% endblock %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header id="main-header">
|
||||
<nav>
|
||||
<div class="logo">
|
||||
<h1><a href="{{ url_for('dashboard.index') }}">Flask Files</a></h1>
|
||||
</div>
|
||||
<ul>
|
||||
<li><a href="{{ url_for('dashboard.index') }}"><i class="fas fa-chart-pie"></i> Dashboard</a></li>
|
||||
<li><a href="{{ url_for('files.browser') }}"><i class="fas fa-folder"></i> Files</a></li>
|
||||
{% if current_user.is_authenticated %}
|
||||
<li><a href="{{ url_for('auth.profile') }}"><i class="fas fa-user"></i> {{ current_user.username }}</a>
|
||||
</li>
|
||||
<li><a href="{{ url_for('auth.logout') }}"><i class="fas fa-sign-out-alt"></i> Logout</a></li>
|
||||
{% else %}
|
||||
<li><a href="{{ url_for('auth.login') }}"><i class="fas fa-sign-in-alt"></i> Login</a></li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<button id="darkModeToggle" class="toggle-button" aria-label="Toggle dark mode">
|
||||
<i class="fas fa-moon"></i>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<section class="alerts">
|
||||
{% for category, message in messages %}
|
||||
<div class="alert {{ category }}">
|
||||
{{ message }}
|
||||
<button class="close" aria-label="Close">×</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<section class="content">
|
||||
{% block content %}{% endblock %}
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>© {{ now.year }} Flask Files - A Simple File Manager</p>
|
||||
</footer>
|
||||
|
||||
<!-- Add this right before the closing </body> tag -->
|
||||
<div id="upload-overlay" class="upload-overlay">
|
||||
<div class="upload-modal">
|
||||
<div class="upload-header">
|
||||
<h3><i class="fas fa-cloud-upload-alt"></i> Upload Files</h3>
|
||||
<button class="close-upload" id="close-upload-btn">×</button>
|
||||
</div>
|
||||
|
||||
<div class="upload-body">
|
||||
<div class="upload-dropzone" id="dropzone">
|
||||
<i class="fas fa-cloud-upload-alt upload-icon"></i>
|
||||
<p>Drag & drop files or folders here</p>
|
||||
<p>or</p>
|
||||
<div class="upload-buttons">
|
||||
<label class="btn primary">
|
||||
<i class="fas fa-file"></i> Select Files
|
||||
<input type="file" id="file-upload" multiple style="display: none">
|
||||
</label>
|
||||
<label class="btn">
|
||||
<i class="fas fa-folder"></i> Select Folder
|
||||
<input type="file" id="folder-upload" webkitdirectory directory multiple
|
||||
style="display: none">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="upload-list" id="upload-list">
|
||||
<h4>Upload Queue</h4>
|
||||
<div class="upload-items" id="upload-items"></div>
|
||||
</div>
|
||||
|
||||
<div class="upload-progress-overall">
|
||||
<div class="progress-label">
|
||||
<span>Overall Progress</span>
|
||||
<span id="upload-percentage">0%</span>
|
||||
</div>
|
||||
<div class="progress-bar-container">
|
||||
<div class="progress-bar" id="total-progress-bar" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="upload-footer">
|
||||
<button class="btn" id="cancel-upload-btn">Cancel</button>
|
||||
<button class="btn primary" id="start-upload-btn">Start Upload</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
74
app/templates/dashboard.html
Normal file
74
app/templates/dashboard.html
Normal file
|
@ -0,0 +1,74 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Dashboard - Flask Files{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="dashboard">
|
||||
<h2>Dashboard</h2>
|
||||
|
||||
<div class="dashboard-stats">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">📁</div>
|
||||
<div class="stat-info">
|
||||
<span class="stat-value">{{ total_folders }}</span>
|
||||
<span class="stat-label">Folders</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">📄</div>
|
||||
<div class="stat-info">
|
||||
<span class="stat-value">{{ total_files }}</span>
|
||||
<span class="stat-label">Files</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">🔗</div>
|
||||
<div class="stat-info">
|
||||
<span class="stat-value">{{ active_shares }}</span>
|
||||
<span class="stat-label">Active Shares</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dashboard-recent">
|
||||
<h3>Recent Files</h3>
|
||||
|
||||
{% if recent_files %}
|
||||
<div class="recent-files-list">
|
||||
{% for file in recent_files %}
|
||||
<div class="file-item">
|
||||
<div class="file-icon">
|
||||
{% if file.name.endswith('.pdf') %}📕
|
||||
{% elif file.name.endswith(('.jpg', '.jpeg', '.png', '.gif')) %}🖼️
|
||||
{% elif file.name.endswith(('.mp3', '.wav', '.flac')) %}🎵
|
||||
{% elif file.name.endswith(('.mp4', '.mov', '.avi')) %}🎬
|
||||
{% elif file.name.endswith(('.doc', '.docx')) %}📘
|
||||
{% elif file.name.endswith(('.xls', '.xlsx')) %}📊
|
||||
{% elif file.name.endswith(('.ppt', '.pptx')) %}📙
|
||||
{% elif file.name.endswith('.zip') %}📦
|
||||
{% else %}📄{% endif %}
|
||||
</div>
|
||||
<div class="file-details">
|
||||
<div class="file-name">{{ file.name }}</div>
|
||||
<div class="file-meta">
|
||||
<span class="file-size">{{ (file.size / 1024)|round(1) }} KB</span>
|
||||
<span class="file-date">{{ file.updated_at.strftime('%b %d, %Y') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="empty-state">No files uploaded yet. <a href="{{ url_for('files.browser') }}">Upload your first
|
||||
file</a>.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="dashboard-actions">
|
||||
<a href="{{ url_for('files.browser') }}" class="btn primary">Browse Files</a>
|
||||
<a href="{{ url_for('files.upload') }}" class="btn">Upload Files</a>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
181
app/templates/files/browser.html
Normal file
181
app/templates/files/browser.html
Normal file
|
@ -0,0 +1,181 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}File Browser - Flask Files{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="file-browser">
|
||||
<div class="browser-header">
|
||||
<h2>File Browser</h2>
|
||||
<div class="browser-actions">
|
||||
<a href="{{ url_for('files.upload', folder=current_folder.id if current_folder else None) }}"
|
||||
class="btn primary">
|
||||
<i class="fas fa-cloud-upload-alt"></i> Upload
|
||||
</a>
|
||||
<button class="btn" id="new-folder-btn">
|
||||
<i class="fas fa-folder-plus"></i> New Folder
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="path-nav">
|
||||
<a href="{{ url_for('files.browser') }}" class="path-item">
|
||||
<i class="fas fa-home"></i> Home
|
||||
</a>
|
||||
{% for folder in breadcrumbs %}
|
||||
<span class="path-separator">/</span>
|
||||
<a href="{{ url_for('files.browser', folder=folder.id) }}" class="path-item">{{ folder.name }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if folders or files %}
|
||||
<div class="files-container">
|
||||
{% if folders %}
|
||||
<div class="folder-section">
|
||||
<h3>Folders</h3>
|
||||
<div class="files-list">
|
||||
{% for folder in folders %}
|
||||
<div class="file-item folder">
|
||||
<a href="{{ url_for('files.browser', folder=folder.id) }}" class="file-link">
|
||||
<div class="file-icon">
|
||||
<i class="fas fa-folder fa-2x"></i>
|
||||
</div>
|
||||
<div class="file-name">{{ folder.name }}</div>
|
||||
</a>
|
||||
<div class="file-actions">
|
||||
<button class="action-btn rename" data-id="{{ folder.id }}" title="Rename">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<button class="action-btn delete" data-id="{{ folder.id }}" title="Delete">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if files %}
|
||||
<div class="file-section">
|
||||
<h3>Files</h3>
|
||||
<div class="files-list">
|
||||
{% for file in files %}
|
||||
<div class="file-item">
|
||||
<div class="file-link">
|
||||
<div class="file-icon">
|
||||
{% if file.name.endswith('.pdf') %}<i class="fas fa-file-pdf fa-2x"></i>
|
||||
{% elif file.name.endswith(('.jpg', '.jpeg', '.png', '.gif')) %}<i
|
||||
class="fas fa-file-image fa-2x"></i>
|
||||
{% elif file.name.endswith(('.mp3', '.wav', '.flac')) %}<i
|
||||
class="fas fa-file-audio fa-2x"></i>
|
||||
{% elif file.name.endswith(('.mp4', '.mov', '.avi')) %}<i
|
||||
class="fas fa-file-video fa-2x"></i>
|
||||
{% elif file.name.endswith(('.doc', '.docx')) %}<i class="fas fa-file-word fa-2x"></i>
|
||||
{% elif file.name.endswith(('.xls', '.xlsx')) %}<i class="fas fa-file-excel fa-2x"></i>
|
||||
{% elif file.name.endswith(('.ppt', '.pptx')) %}<i class="fas fa-file-powerpoint fa-2x"></i>
|
||||
{% elif file.name.endswith('.zip') %}<i class="fas fa-file-archive fa-2x"></i>
|
||||
{% else %}<i class="fas fa-file fa-2x"></i>{% endif %}
|
||||
</div>
|
||||
<div class="file-details">
|
||||
<div class="file-name">{{ file.name }}</div>
|
||||
<div class="file-meta">
|
||||
<span class="file-size">{{ (file.size / 1024)|round(1) }} KB</span>
|
||||
<span class="file-date">{{ file.updated_at.strftime('%b %d, %Y') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-actions">
|
||||
<a href="#" class="action-btn download" data-id="{{ file.id }}" title="Download">
|
||||
<i class="fas fa-download"></i>
|
||||
</a>
|
||||
<a href="#" class="action-btn share" data-id="{{ file.id }}" title="Share">
|
||||
<i class="fas fa-share-alt"></i>
|
||||
</a>
|
||||
<button class="action-btn rename" data-id="{{ file.id }}" title="Rename">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<button class="action-btn delete" data-id="{{ file.id }}" title="Delete">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">
|
||||
<i class="fas fa-folder-open fa-3x"></i>
|
||||
</div>
|
||||
<p>This folder is empty</p>
|
||||
<p>Upload files or create a new folder to get started</p>
|
||||
<div class="empty-actions">
|
||||
<button class="btn primary" data-action="upload"
|
||||
data-folder-id="{{ current_folder.id if current_folder else None }}">
|
||||
<i class="fas fa-cloud-upload-alt"></i> Upload Files
|
||||
</button>
|
||||
<button class="btn" id="empty-new-folder-btn">
|
||||
<i class="fas fa-folder-plus"></i> New Folder
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="{{ url_for('static', filename='js/upload.js') }}"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// New folder functionality
|
||||
const newFolderBtn = document.getElementById('new-folder-btn');
|
||||
const emptyNewFolderBtn = document.getElementById('empty-new-folder-btn');
|
||||
|
||||
function showNewFolderPrompt() {
|
||||
const folderName = prompt('Enter folder name:');
|
||||
if (folderName) {
|
||||
createNewFolder(folderName);
|
||||
}
|
||||
}
|
||||
|
||||
function createNewFolder(name) {
|
||||
const formData = new FormData();
|
||||
formData.append('name', name);
|
||||
|
||||
{% if current_folder %}
|
||||
formData.append('parent_id', '{{ current_folder.id }}');
|
||||
{% endif %}
|
||||
|
||||
fetch('/files/create_folder', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert(data.error || 'Failed to create folder');
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert('An error occurred while creating the folder');
|
||||
});
|
||||
}
|
||||
|
||||
if (newFolderBtn) {
|
||||
newFolderBtn.addEventListener('click', showNewFolderPrompt);
|
||||
}
|
||||
|
||||
if (emptyNewFolderBtn) {
|
||||
emptyNewFolderBtn.addEventListener('click', showNewFolderPrompt);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
140
app/templates/files/upload.html
Normal file
140
app/templates/files/upload.html
Normal file
|
@ -0,0 +1,140 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Upload Files - Flask Files{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/upload.css') }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="upload-container">
|
||||
<h2>Upload Files</h2>
|
||||
|
||||
<div class="upload-tabs">
|
||||
<button class="tab-btn active" data-tab="file-tab">Files</button>
|
||||
<button class="tab-btn" data-tab="folder-tab">Folder</button>
|
||||
</div>
|
||||
|
||||
<div class="upload-location">
|
||||
<p>
|
||||
Uploading to:
|
||||
{% if parent_folder %}
|
||||
<a href="{{ url_for('files.browser', folder_id=parent_folder.id) }}">{{ parent_folder.name }}</a>
|
||||
{% else %}
|
||||
<a href="{{ url_for('files.browser') }}">Root</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="tab-content active" id="file-tab">
|
||||
<form id="file-upload-form" enctype="multipart/form-data" class="upload-form">
|
||||
<input type="hidden" name="folder_id" value="{{ parent_folder.id if parent_folder else '' }}">
|
||||
|
||||
<div class="upload-dropzone" id="file-dropzone">
|
||||
<i class="fas fa-cloud-upload-alt upload-icon"></i>
|
||||
<p>Drag & drop files here to start uploading</p>
|
||||
<p>or</p>
|
||||
<label class="btn primary">
|
||||
<i class="fas fa-file"></i> Select Files
|
||||
<input type="file" name="files[]" multiple id="file-input" style="display: none">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="upload-progress-container">
|
||||
<h4>Upload Progress</h4>
|
||||
<div class="progress-overall">
|
||||
<div class="progress-label">
|
||||
<span>Overall Progress</span>
|
||||
<span id="progress-percentage">0%</span>
|
||||
</div>
|
||||
<div class="progress-bar-container">
|
||||
<div class="progress-bar" id="progress-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="upload-stats">
|
||||
<div class="stat">
|
||||
<span class="stat-label">Speed:</span>
|
||||
<span class="stat-value" id="upload-speed">0 KB/s</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">Uploaded:</span>
|
||||
<span class="stat-value" id="uploaded-size">0 KB / 0 KB</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">Remaining:</span>
|
||||
<span class="stat-value" id="time-remaining">calculating...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="selected-files">
|
||||
<h4>Files</h4>
|
||||
<div class="file-list" id="file-list"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="{{ url_for('files.browser', folder_id=parent_folder.id if parent_folder else None) }}"
|
||||
class="btn">Back to Browser</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tab-content" id="folder-tab">
|
||||
<form id="folder-upload-form" enctype="multipart/form-data" class="upload-form">
|
||||
<input type="hidden" name="folder_id" value="{{ parent_folder.id if parent_folder else '' }}">
|
||||
|
||||
<div class="upload-dropzone folder-dropzone" id="folder-dropzone">
|
||||
<i class="fas fa-folder-open upload-icon"></i>
|
||||
<p>Select a folder to upload</p>
|
||||
<p>(Some browsers may not fully support folder drag & drop)</p>
|
||||
<label class="btn primary">
|
||||
<i class="fas fa-folder"></i> Select Folder
|
||||
<input type="file" name="files[]" webkitdirectory directory multiple id="folder-input"
|
||||
style="display: none">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="upload-progress-container">
|
||||
<h4>Upload Progress</h4>
|
||||
<div class="progress-overall">
|
||||
<div class="progress-label">
|
||||
<span>Overall Progress</span>
|
||||
<span id="folder-progress-percentage">0%</span>
|
||||
</div>
|
||||
<div class="progress-bar-container">
|
||||
<div class="progress-bar" id="folder-progress-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="upload-stats">
|
||||
<div class="stat">
|
||||
<span class="stat-label">Speed:</span>
|
||||
<span class="stat-value" id="folder-upload-speed">0 KB/s</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">Uploaded:</span>
|
||||
<span class="stat-value" id="folder-uploaded-size">0 KB / 0 KB</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">Remaining:</span>
|
||||
<span class="stat-value" id="folder-time-remaining">calculating...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="selected-files">
|
||||
<h4>Folder Contents</h4>
|
||||
<div class="file-list" id="folder-file-list"></div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="{{ url_for('files.browser', folder_id=parent_folder.id if parent_folder else None) }}"
|
||||
class="btn">Back to Browser</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="{{ url_for('static', filename='js/upload.js') }}"></script>
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue