add code
This commit is contained in:
parent
f1da01193b
commit
b0a4567169
20 changed files with 1734 additions and 0 deletions
157
templates/add_asset.html
Normal file
157
templates/add_asset.html
Normal file
|
@ -0,0 +1,157 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<div class="page-header">
|
||||
<h1>Add New Digital Asset</h1>
|
||||
</div>
|
||||
|
||||
<div class="form-container">
|
||||
<form method="POST" enctype="multipart/form-data" class="form">
|
||||
<div class="form-group">
|
||||
<label for="title" class="form-label">Title</label>
|
||||
<input
|
||||
type="text"
|
||||
id="title"
|
||||
name="title"
|
||||
class="form-input"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description" class="form-label">Description</label>
|
||||
<textarea
|
||||
id="description"
|
||||
name="description"
|
||||
class="form-input"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="license_key">License Key</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="license_key"
|
||||
name="license_key"
|
||||
placeholder="Enter the asset's license key"
|
||||
/>
|
||||
<small class="form-text text-muted"
|
||||
>Enter the license key that came with your purchased
|
||||
asset</small
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="featured_image" class="form-label"
|
||||
>Featured Image</label
|
||||
>
|
||||
<div class="file-input-wrapper">
|
||||
<input
|
||||
type="file"
|
||||
id="featured_image"
|
||||
name="featured_image"
|
||||
class="file-input"
|
||||
required
|
||||
accept="image/*"
|
||||
/>
|
||||
<label for="featured_image" class="file-input-label">
|
||||
<i class="fas fa-cloud-upload-alt"></i>
|
||||
<span>Choose a file...</span>
|
||||
</label>
|
||||
<div class="file-input-preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="additional_files" class="form-label"
|
||||
>Additional Files</label
|
||||
>
|
||||
<div class="file-input-wrapper">
|
||||
<input
|
||||
type="file"
|
||||
id="additional_files"
|
||||
name="additional_files"
|
||||
class="file-input"
|
||||
multiple
|
||||
/>
|
||||
<label for="additional_files" class="file-input-label">
|
||||
<i class="fas fa-cloud-upload-alt"></i>
|
||||
<span>Choose files...</span>
|
||||
</label>
|
||||
<div class="selected-files"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="button button-primary">
|
||||
<i class="fas fa-save"></i> Save Asset
|
||||
</button>
|
||||
<a href="{{ url_for('index') }}" class="button button-secondary">
|
||||
<i class="fas fa-times"></i> Cancel
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script>
|
||||
// Initialize Trumbowyg
|
||||
$("#description").trumbowyg({
|
||||
btns: [
|
||||
["viewHTML"],
|
||||
["undo", "redo"],
|
||||
["formatting"],
|
||||
["strong", "em", "del"],
|
||||
["link"],
|
||||
["insertImage"],
|
||||
["justifyLeft", "justifyCenter", "justifyRight"],
|
||||
["unorderedList", "orderedList"],
|
||||
["horizontalRule"],
|
||||
["removeformat"],
|
||||
["fullscreen"],
|
||||
],
|
||||
autogrow: true,
|
||||
resetCss: true,
|
||||
removeformatPasted: true,
|
||||
svgPath:
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/Trumbowyg/2.27.3/ui/icons.svg",
|
||||
});
|
||||
|
||||
// File input preview handling
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// Featured image preview
|
||||
const featuredInput = document.getElementById("featured_image");
|
||||
const featuredPreview = document.querySelector(".file-input-preview");
|
||||
|
||||
featuredInput.addEventListener("change", function () {
|
||||
const file = this.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
featuredPreview.innerHTML = `
|
||||
<div class="preview-image">
|
||||
<img src="${e.target.result}" alt="Preview">
|
||||
<span class="filename">${file.name}</span>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
|
||||
// Additional files list
|
||||
const additionalInput = document.getElementById("additional_files");
|
||||
const selectedFiles = document.querySelector(".selected-files");
|
||||
|
||||
additionalInput.addEventListener("change", function () {
|
||||
selectedFiles.innerHTML = "";
|
||||
Array.from(this.files).forEach((file) => {
|
||||
selectedFiles.innerHTML += `
|
||||
<div class="selected-file">
|
||||
<i class="fas fa-file"></i>
|
||||
<span>${file.name}</span>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
130
templates/asset_detail.html
Normal file
130
templates/asset_detail.html
Normal file
|
@ -0,0 +1,130 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<div class="asset-detail">
|
||||
<div class="page-header">
|
||||
<h1>{{ asset.title }}</h1>
|
||||
<div class="page-actions">
|
||||
<a
|
||||
href="{{ url_for('edit_asset', id=asset.id) }}"
|
||||
class="button button-secondary"
|
||||
>
|
||||
<i class="fas fa-edit"></i> Edit
|
||||
</a>
|
||||
<form
|
||||
method="POST"
|
||||
action="{{ url_for('delete_asset', id=asset.id) }}"
|
||||
class="inline-form"
|
||||
>
|
||||
<button
|
||||
type="submit"
|
||||
class="button button-danger"
|
||||
onclick="return confirm('Are you sure you want to delete this asset?')"
|
||||
>
|
||||
<i class="fas fa-trash"></i> Delete
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="asset-content">
|
||||
<div class="asset-main-image">
|
||||
<img
|
||||
src="{{ url_for('static', filename='uploads/' + asset.featured_image) }}"
|
||||
alt="{{ asset.title }}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="asset-info">
|
||||
<div class="description content-box">
|
||||
<h2>Description</h2>
|
||||
{{ asset.safe_description|safe }}
|
||||
</div>
|
||||
|
||||
{% if asset.license_key %}
|
||||
<div class="content-box">
|
||||
<h2>License Key</h2>
|
||||
<div class="license-key-container">
|
||||
<div class="license-key-wrapper">
|
||||
<input
|
||||
type="text"
|
||||
class="form-input license-key-input"
|
||||
value="{{ asset.license_key }}"
|
||||
readonly
|
||||
aria-label="License Key"
|
||||
/>
|
||||
<button
|
||||
class="button button-secondary license-copy-btn"
|
||||
type="button"
|
||||
aria-label="Copy License Key"
|
||||
>
|
||||
<i class="fas fa-copy"></i>
|
||||
<span>Copy</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="attached-files content-box">
|
||||
<h2>Attached Files</h2>
|
||||
{% if asset.files %}
|
||||
<ul class="files-list">
|
||||
{% for file in asset.files %}
|
||||
<li class="file-item">
|
||||
<a
|
||||
href="{{ url_for('static', filename='uploads/' + file.filename) }}"
|
||||
target="_blank"
|
||||
class="file-link"
|
||||
>
|
||||
<i class="fas fa-file"></i>
|
||||
{{ file.original_filename or file.filename }}
|
||||
</a>
|
||||
<form
|
||||
method="POST"
|
||||
action="{{ url_for('delete_asset_file', id=file.id) }}"
|
||||
class="inline-form"
|
||||
>
|
||||
<button
|
||||
type="submit"
|
||||
class="button button-small button-danger"
|
||||
onclick="return confirm('Are you sure you want to delete this file?')"
|
||||
>
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-muted">No files attached</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const copyButton = document.querySelector(".license-copy-btn");
|
||||
if (copyButton) {
|
||||
copyButton.addEventListener("click", async function () {
|
||||
const input = this.previousElementSibling;
|
||||
try {
|
||||
await navigator.clipboard.writeText(input.value);
|
||||
const buttonText = this.querySelector("span");
|
||||
const buttonIcon = this.querySelector("i");
|
||||
|
||||
buttonText.textContent = "Copied!";
|
||||
buttonIcon.classList.replace("fa-copy", "fa-check");
|
||||
|
||||
setTimeout(() => {
|
||||
buttonText.textContent = "Copy";
|
||||
buttonIcon.classList.replace("fa-check", "fa-copy");
|
||||
}, 2000);
|
||||
} catch (err) {
|
||||
console.error("Failed to copy text: ", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
73
templates/base.html
Normal file
73
templates/base.html
Normal file
|
@ -0,0 +1,73 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Digital Assets Manager - Organize and manage your digital assets"
|
||||
/>
|
||||
<title>Digital Assets Manager</title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="{{ url_for('static', filename='css/style.css') }}"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/Trumbowyg/2.27.3/ui/trumbowyg.min.css"
|
||||
integrity="sha512-Fm8kRNVGCBZn0sPmwJbVXlqfJmPC13zRsMElZenX6v721g/H7OukJd8XzDEBRQ2FSATK8xNF9UYvzsCtUpfeJg=="
|
||||
crossorigin="anonymous"
|
||||
referrerpolicy="no-referrer"
|
||||
/>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Trumbowyg/2.27.3/trumbowyg.min.js"></script>
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<nav class="main-nav">
|
||||
<div class="nav-container">
|
||||
<div class="nav-brand">Digital Assets Manager</div>
|
||||
<div class="nav-links">
|
||||
<a href="{{ url_for('index') }}" class="nav-link"
|
||||
><i class="fas fa-home"></i> Home</a
|
||||
>
|
||||
<a href="{{ url_for('add_asset') }}" class="nav-link"
|
||||
><i class="fas fa-plus"></i> Add Asset</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="main-content">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %} {%
|
||||
if messages %} {% for category, message in messages %}
|
||||
<div class="alert alert-{{ category }}">
|
||||
{{ message }}
|
||||
<button class="alert-close">×</button>
|
||||
</div>
|
||||
{% endfor %} {% endif %} {% endwith %} {% block content %}{%
|
||||
endblock %}
|
||||
</main>
|
||||
|
||||
{% block scripts %}{% endblock %}
|
||||
<script>
|
||||
// Close alert messages
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
document.querySelectorAll(".alert-close").forEach((button) => {
|
||||
button.addEventListener("click", () => {
|
||||
button.parentElement.style.display = "none";
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
||||
</body>
|
||||
</html>
|
201
templates/edit_asset.html
Normal file
201
templates/edit_asset.html
Normal file
|
@ -0,0 +1,201 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<div class="page-header">
|
||||
<h1>Edit Digital Asset</h1>
|
||||
</div>
|
||||
|
||||
<div class="form-container">
|
||||
<form method="POST" enctype="multipart/form-data" class="form">
|
||||
<div class="form-group">
|
||||
<label for="title" class="form-label">Title</label>
|
||||
<input
|
||||
type="text"
|
||||
id="title"
|
||||
name="title"
|
||||
class="form-input"
|
||||
value="{{ asset.title }}"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="description" class="form-label">Description</label>
|
||||
<textarea id="description" name="description" class="form-input">
|
||||
{{ asset.description }}</textarea
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Current Featured Image</label>
|
||||
<div class="current-image">
|
||||
<img
|
||||
src="{{ url_for('static', filename='uploads/' + asset.featured_image) }}"
|
||||
alt="{{ asset.title }}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="license_key">License Key</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-input"
|
||||
id="license_key"
|
||||
name="license_key"
|
||||
value="{{ asset.license_key or '' }}"
|
||||
placeholder="Enter the asset's license key"
|
||||
/>
|
||||
<small class="form-text text-muted"
|
||||
>The license key that came with your purchased asset</small
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="featured_image" class="form-label"
|
||||
>Update Featured Image</label
|
||||
>
|
||||
<div class="file-input-wrapper">
|
||||
<input
|
||||
type="file"
|
||||
id="featured_image"
|
||||
name="featured_image"
|
||||
class="file-input"
|
||||
accept="image/*"
|
||||
/>
|
||||
<label for="featured_image" class="file-input-label">
|
||||
<i class="fas fa-cloud-upload-alt"></i>
|
||||
<span>Choose a new image...</span>
|
||||
</label>
|
||||
<div class="file-input-preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="additional_files" class="form-label"
|
||||
>Add More Files</label
|
||||
>
|
||||
<div class="file-input-wrapper">
|
||||
<input
|
||||
type="file"
|
||||
id="additional_files"
|
||||
name="additional_files"
|
||||
class="file-input"
|
||||
multiple
|
||||
/>
|
||||
<label for="additional_files" class="file-input-label">
|
||||
<i class="fas fa-cloud-upload-alt"></i>
|
||||
<span>Choose files...</span>
|
||||
</label>
|
||||
<div class="selected-files"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="button button-primary">
|
||||
<i class="fas fa-save"></i> Update Asset
|
||||
</button>
|
||||
<a
|
||||
href="{{ url_for('asset_detail', id=asset.id) }}"
|
||||
class="button button-secondary"
|
||||
>
|
||||
<i class="fas fa-times"></i> Cancel
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% if asset.files %}
|
||||
<div class="current-files">
|
||||
<h3>Current Files</h3>
|
||||
<ul class="files-list">
|
||||
{% for file in asset.files %}
|
||||
<li class="file-item">
|
||||
<a
|
||||
href="{{ url_for('static', filename='uploads/' + file.filename) }}"
|
||||
target="_blank"
|
||||
class="file-link"
|
||||
>
|
||||
<i class="fas fa-file"></i>
|
||||
{{ file.original_filename or file.filename }}
|
||||
</a>
|
||||
<form
|
||||
method="POST"
|
||||
action="{{ url_for('delete_asset_file', id=file.id) }}"
|
||||
class="inline-form"
|
||||
>
|
||||
<button
|
||||
type="submit"
|
||||
class="button button-small button-danger"
|
||||
onclick="return confirm('Are you sure you want to delete this file?')"
|
||||
>
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script>
|
||||
// Initialize Trumbowyg
|
||||
$("#description").trumbowyg({
|
||||
btns: [
|
||||
["viewHTML"],
|
||||
["undo", "redo"],
|
||||
["formatting"],
|
||||
["strong", "em", "del"],
|
||||
["link"],
|
||||
["insertImage"],
|
||||
["justifyLeft", "justifyCenter", "justifyRight"],
|
||||
["unorderedList", "orderedList"],
|
||||
["horizontalRule"],
|
||||
["removeformat"],
|
||||
["fullscreen"],
|
||||
],
|
||||
autogrow: true,
|
||||
resetCss: true,
|
||||
removeformatPasted: true,
|
||||
svgPath:
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/Trumbowyg/2.27.3/ui/icons.svg",
|
||||
});
|
||||
|
||||
// File input preview handling (keeping your existing code)
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// Featured image preview
|
||||
const featuredInput = document.getElementById("featured_image");
|
||||
const featuredPreview = document.querySelector(".file-input-preview");
|
||||
|
||||
featuredInput.addEventListener("change", function () {
|
||||
const file = this.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
featuredPreview.innerHTML = `
|
||||
<div class="preview-image">
|
||||
<img src="${e.target.result}" alt="Preview">
|
||||
<span class="filename">${file.name}</span>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
});
|
||||
|
||||
// Additional files list
|
||||
const additionalInput = document.getElementById("additional_files");
|
||||
const selectedFiles = document.querySelector(".selected-files");
|
||||
|
||||
additionalInput.addEventListener("change", function () {
|
||||
selectedFiles.innerHTML = "";
|
||||
Array.from(this.files).forEach((file) => {
|
||||
selectedFiles.innerHTML += `
|
||||
<div class="selected-file">
|
||||
<i class="fas fa-file"></i>
|
||||
<span>${file.name}</span>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
39
templates/index.html
Normal file
39
templates/index.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<div class="page-header">
|
||||
<h1>My Digital Assets</h1>
|
||||
</div>
|
||||
|
||||
<div class="gallery">
|
||||
{% for asset in assets %}
|
||||
<div class="asset-card">
|
||||
<div class="asset-card-image">
|
||||
<img
|
||||
src="{{ url_for('static', filename='uploads/' + asset.featured_image) }}"
|
||||
alt="{{ asset.title }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
<div class="asset-card-content">
|
||||
<h3>{{ asset.title }}</h3>
|
||||
<div class="asset-card-actions">
|
||||
<a
|
||||
href="{{ url_for('asset_detail', id=asset.id) }}"
|
||||
class="button button-primary"
|
||||
>
|
||||
<i class="fas fa-eye"></i> View Details
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-box-open"></i>
|
||||
<h2>No assets yet</h2>
|
||||
<p>Start by adding your first digital asset!</p>
|
||||
<a href="{{ url_for('add_asset') }}" class="button button-primary">
|
||||
<i class="fas fa-plus"></i> Add Asset
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue