382 lines
13 KiB
HTML
382 lines
13 KiB
HTML
{% extends "layouts/dashboard.html" %}
|
|
|
|
{% block title %}Manage Shares - Retoor's Cloud Solutions{% endblock %}
|
|
|
|
{% block dashboard_head %}
|
|
<style>
|
|
.shares-container {
|
|
padding: 1.5rem;
|
|
max-width: 1400px;
|
|
margin: 0 auto;
|
|
}
|
|
.shares-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
|
|
gap: 1.5rem;
|
|
margin-top: 1.5rem;
|
|
}
|
|
.share-card {
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 1.5rem;
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
border-left: 4px solid #0066cc;
|
|
transition: transform 0.2s, box-shadow 0.2s;
|
|
}
|
|
.share-card:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
}
|
|
.share-card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
margin-bottom: 1rem;
|
|
padding-bottom: 1rem;
|
|
border-bottom: 1px solid #eee;
|
|
}
|
|
.share-item-path {
|
|
font-weight: 600;
|
|
font-size: 1.1rem;
|
|
color: #333;
|
|
word-break: break-all;
|
|
flex: 1;
|
|
margin-right: 1rem;
|
|
}
|
|
.share-status {
|
|
padding: 0.4rem 0.8rem;
|
|
border-radius: 16px;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
}
|
|
.share-status.active {
|
|
background: #d4edda;
|
|
color: #155724;
|
|
}
|
|
.share-status.inactive {
|
|
background: #f8d7da;
|
|
color: #721c24;
|
|
}
|
|
.share-status.expired {
|
|
background: #fff3cd;
|
|
color: #856404;
|
|
}
|
|
.share-card-body {
|
|
margin-bottom: 1rem;
|
|
}
|
|
.share-info-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 0.5rem 0;
|
|
font-size: 0.9rem;
|
|
}
|
|
.share-info-label {
|
|
color: #666;
|
|
font-weight: 500;
|
|
}
|
|
.share-info-value {
|
|
color: #333;
|
|
font-weight: 600;
|
|
}
|
|
.share-link {
|
|
font-family: monospace;
|
|
font-size: 0.85rem;
|
|
color: #0066cc;
|
|
background: #f0f7ff;
|
|
padding: 0.5rem;
|
|
border-radius: 4px;
|
|
margin: 0.75rem 0;
|
|
display: block;
|
|
word-break: break-all;
|
|
}
|
|
.share-actions {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
flex-wrap: wrap;
|
|
margin-top: 1rem;
|
|
padding-top: 1rem;
|
|
border-top: 1px solid #eee;
|
|
}
|
|
.share-actions button {
|
|
flex: 1;
|
|
min-width: 80px;
|
|
padding: 0.6rem 0.8rem;
|
|
font-size: 0.85rem;
|
|
}
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: 4rem 2rem;
|
|
background: white;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
.empty-state-icon {
|
|
font-size: 4rem;
|
|
margin-bottom: 1rem;
|
|
opacity: 0.5;
|
|
}
|
|
.empty-state h3 {
|
|
margin-bottom: 0.5rem;
|
|
color: #333;
|
|
}
|
|
.empty-state p {
|
|
color: #666;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
.modal {
|
|
display: none;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(0,0,0,0.5);
|
|
z-index: 1000;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.modal.active {
|
|
display: flex;
|
|
}
|
|
.modal-content {
|
|
background: white;
|
|
padding: 2rem;
|
|
border-radius: 8px;
|
|
max-width: 600px;
|
|
width: 90%;
|
|
max-height: 80vh;
|
|
overflow-y: auto;
|
|
}
|
|
.modal-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
.modal-close {
|
|
background: none;
|
|
border: none;
|
|
font-size: 1.5rem;
|
|
cursor: pointer;
|
|
}
|
|
.recipient-list {
|
|
margin-top: 1rem;
|
|
}
|
|
.recipient-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 0.75rem;
|
|
background: #f5f5f5;
|
|
margin-bottom: 0.5rem;
|
|
border-radius: 4px;
|
|
}
|
|
@media (max-width: 768px) {
|
|
.shares-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block page_title %}Manage Shares{% endblock %}
|
|
|
|
{% block dashboard_content %}
|
|
<div class="shares-container">
|
|
{% if shares %}
|
|
<div class="shares-grid" id="shares-list">
|
|
{% for share in shares %}
|
|
<div class="share-card" data-share-id="{{ share.share_id }}">
|
|
<div class="share-card-header">
|
|
<div class="share-item-path">{{ share.item_path }}</div>
|
|
{% if not share.active %}
|
|
<span class="share-status inactive">Inactive</span>
|
|
{% elif share.expires_at and share.expires_at < now %}
|
|
<span class="share-status expired">Expired</span>
|
|
{% else %}
|
|
<span class="share-status active">Active</span>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="share-card-body">
|
|
<div class="share-link">/share/{{ share.share_id }}</div>
|
|
|
|
<div class="share-info-row">
|
|
<span class="share-info-label">Permission</span>
|
|
<span class="share-info-value">{{ share.permission }}</span>
|
|
</div>
|
|
|
|
<div class="share-info-row">
|
|
<span class="share-info-label">Scope</span>
|
|
<span class="share-info-value">{{ share.scope }}</span>
|
|
</div>
|
|
|
|
<div class="share-info-row">
|
|
<span class="share-info-label">Access Count</span>
|
|
<span class="share-info-value">{{ share.access_count or 0 }} views</span>
|
|
</div>
|
|
|
|
<div class="share-info-row">
|
|
<span class="share-info-label">Created</span>
|
|
<span class="share-info-value">{{ share.created_at[:10] }}</span>
|
|
</div>
|
|
|
|
<div class="share-info-row">
|
|
<span class="share-info-label">Expires</span>
|
|
<span class="share-info-value">{{ share.expires_at[:10] if share.expires_at else 'Never' }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="share-actions">
|
|
<button class="btn-small copy-link-btn" data-share-id="{{ share.share_id }}">Copy Link</button>
|
|
<button class="btn-small view-details-btn" data-share-id="{{ share.share_id }}">Details</button>
|
|
{% if share.active %}
|
|
<button class="btn-small deactivate-btn" data-share-id="{{ share.share_id }}">Deactivate</button>
|
|
{% else %}
|
|
<button class="btn-small activate-btn" data-share-id="{{ share.share_id }}">Activate</button>
|
|
{% endif %}
|
|
<button class="btn-small delete-btn btn-danger" data-share-id="{{ share.share_id }}">Delete</button>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="empty-state">
|
|
<div class="empty-state-icon">🔗</div>
|
|
<h3>No Shares Yet</h3>
|
|
<p>You have not created any shares. Start sharing your files and folders with others.</p>
|
|
<a href="/files" class="btn-primary">Go to My Files</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="modal" id="share-details-modal">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h2>Share Details</h2>
|
|
<button class="modal-close">×</button>
|
|
</div>
|
|
<div id="share-details-content">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script type="module">
|
|
const sharesList = document.getElementById('shares-list');
|
|
const detailsModal = document.getElementById('share-details-modal');
|
|
const detailsContent = document.getElementById('share-details-content');
|
|
const modalClose = document.querySelector('.modal-close');
|
|
|
|
if (sharesList) {
|
|
sharesList.addEventListener('click', async (e) => {
|
|
const shareId = e.target.dataset.shareId;
|
|
if (!shareId) return;
|
|
|
|
if (e.target.classList.contains('view-details-btn')) {
|
|
await viewShareDetails(shareId);
|
|
} else if (e.target.classList.contains('copy-link-btn')) {
|
|
copyShareLink(shareId);
|
|
} else if (e.target.classList.contains('deactivate-btn')) {
|
|
await updateShare(shareId, 'deactivate');
|
|
} else if (e.target.classList.contains('activate-btn')) {
|
|
await updateShare(shareId, 'reactivate');
|
|
} else if (e.target.classList.contains('delete-btn')) {
|
|
if (confirm('Are you sure you want to delete this share?')) {
|
|
await updateShare(shareId, 'delete');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
async function viewShareDetails(shareId) {
|
|
try {
|
|
const response = await fetch(`/api/sharing/${shareId}`);
|
|
const data = await response.json();
|
|
|
|
let html = `
|
|
<div class="form-group">
|
|
<label>Item Path</label>
|
|
<p>${data.share.item_path}</p>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Permission</label>
|
|
<p>${data.share.permission}</p>
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Scope</label>
|
|
<p>${data.share.scope}</p>
|
|
</div>
|
|
`;
|
|
|
|
if (data.recipients && data.recipients.length > 0) {
|
|
html += `
|
|
<div class="form-group">
|
|
<label>Recipients</label>
|
|
<div class="recipient-list">
|
|
${data.recipients.map(r => `
|
|
<div class="recipient-item">
|
|
<div>
|
|
<div>${r.email}</div>
|
|
<small>Permission: ${r.permission}</small>
|
|
</div>
|
|
<div>
|
|
${r.accessed ? 'Accessed' : 'Not accessed'}
|
|
</div>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
detailsContent.innerHTML = html;
|
|
detailsModal.classList.add('active');
|
|
} catch (error) {
|
|
alert('Error loading share details: ' + error.message);
|
|
}
|
|
}
|
|
|
|
function copyShareLink(shareId) {
|
|
const url = window.location.origin + '/share/' + shareId;
|
|
navigator.clipboard.writeText(url).then(() => {
|
|
alert('Share link copied to clipboard!');
|
|
});
|
|
}
|
|
|
|
async function updateShare(shareId, action) {
|
|
try {
|
|
const response = await fetch(`/api/sharing/${shareId}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ action })
|
|
});
|
|
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
location.reload();
|
|
} else {
|
|
alert('Error: ' + result.error);
|
|
}
|
|
} catch (error) {
|
|
alert('Error updating share: ' + error.message);
|
|
}
|
|
}
|
|
|
|
modalClose.addEventListener('click', () => {
|
|
detailsModal.classList.remove('active');
|
|
});
|
|
|
|
detailsModal.addEventListener('click', (e) => {
|
|
if (e.target === detailsModal) {
|
|
detailsModal.classList.remove('active');
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|