Improved UI
This commit is contained in:
@@ -654,3 +654,60 @@ pre code {
|
|||||||
width: 0%;
|
width: 0%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Tabs layout styles */
|
||||||
|
.tab-layout {
|
||||||
|
display: block;
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 8px;
|
||||||
|
margin: 10px 0 20px 0;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 5;
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-btn {
|
||||||
|
appearance: none;
|
||||||
|
background: #f1f5f9;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
color: #0f172a;
|
||||||
|
padding: 10px 14px;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-btn:hover {
|
||||||
|
background: #e2e8f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-btn.active {
|
||||||
|
background: #3b82f6;
|
||||||
|
border-color: #3b82f6;
|
||||||
|
color: #ffffff;
|
||||||
|
box-shadow: 0 4px 10px rgba(59, 130, 246, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-panels { margin-top: 10px; }
|
||||||
|
|
||||||
|
.tab-panel { display: block; }
|
||||||
|
|
||||||
|
.tab-panel[hidden] { display: none; }
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.tab-layout { padding: 12px; }
|
||||||
|
.tab-nav { position: static; }
|
||||||
|
}
|
||||||
|
|||||||
221
views/index.html
221
views/index.html
@@ -1,111 +1,128 @@
|
|||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<div class="three-column-layout">
|
<div class="tab-layout">
|
||||||
<!-- Main content -->
|
<!-- Top header area -->
|
||||||
<div class="content-container">
|
<div class="content-container">
|
||||||
{{template "header" .}}
|
{{template "header" .}}
|
||||||
{{template "storage-info" .}}
|
{{template "storage-info" .}}
|
||||||
{{template "api-endpoints" .}}
|
|
||||||
{{template "usage-examples" .}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Changelog column -->
|
|
||||||
<div class="content-container">
|
|
||||||
{{template "changelog" .}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- About column -->
|
|
||||||
<div class="content-container">
|
|
||||||
<h2>About</h2>
|
|
||||||
<div class="about-content">
|
|
||||||
<h3>What is FITRA?</h3>
|
|
||||||
<p>FITRA (File Transfer API) is a lightweight, developer-friendly file sharing service designed for CLI usage and automation.</p>
|
|
||||||
|
|
||||||
<h3>🔒 Security</h3>
|
|
||||||
<p>Files are automatically deleted after 24 hours. This service is designed for temporary file sharing and should not be used for permanent storage.</p>
|
|
||||||
|
|
||||||
<h3>📞 Support</h3>
|
|
||||||
<div class="contact-info">
|
|
||||||
<p><strong>Matrix:</strong> @root@adhd.sh</p>
|
|
||||||
<p><strong>Discord:</strong> nu11ed</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3>💰 Donate</h3>
|
|
||||||
<div class="donate-info">
|
|
||||||
<p>
|
|
||||||
<div class="crypto-header">
|
|
||||||
<strong>BTC:</strong>
|
|
||||||
<button class="copy-crypto-btn" data-rybbit-event="Copy BTC" onclick="copyAddress('bc1ql2a3nxnhfwft7qex0cclj5ar2lfsslvs0aygeq', this)">Copy</button>
|
|
||||||
</div>
|
|
||||||
<div class="crypto-row">
|
|
||||||
<textarea class="crypto-address" readonly>bc1ql2a3nxnhfwft7qex0cclj5ar2lfsslvs0aygeq</textarea>
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<div class="crypto-header">
|
|
||||||
<strong>LTC:</strong>
|
|
||||||
<button class="copy-crypto-btn" data-rybbit-event="Copy LTC" onclick="copyAddress('ltc1qhw5z0m6kcvs0u38nlesndmm3dzu49yrxyqyll9', this)">Copy</button>
|
|
||||||
</div>
|
|
||||||
<div class="crypto-row">
|
|
||||||
<textarea class="crypto-address" readonly>ltc1qhw5z0m6kcvs0u38nlesndmm3dzu49yrxyqyll9</textarea>
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
<div class="crypto-header">
|
|
||||||
<strong>ETH:</strong>
|
|
||||||
<button class="copy-crypto-btn" data-rybbit-event="Copy ETH" onclick="copyAddress('0x30843c72DF6E9A9226d967bf2403602f1C2aB67b', this)">Copy</button>
|
|
||||||
</div>
|
|
||||||
<div class="crypto-row">
|
|
||||||
<textarea class="crypto-address" readonly>0x30843c72DF6E9A9226d967bf2403602f1C2aB67b</textarea>
|
|
||||||
</div>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function copyAddress(address, button) {
|
|
||||||
if (address === '[XMR address]') {
|
|
||||||
button.textContent = 'N/A';
|
|
||||||
button.style.background = '#6b7280';
|
|
||||||
setTimeout(() => {
|
|
||||||
button.textContent = 'Copy';
|
|
||||||
button.style.background = '#22c55e';
|
|
||||||
}, 2000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
navigator.clipboard.writeText(address).then(() => {
|
|
||||||
button.textContent = '✓';
|
|
||||||
button.classList.add('copied');
|
|
||||||
setTimeout(() => {
|
|
||||||
button.textContent = 'Copy';
|
|
||||||
button.classList.remove('copied');
|
|
||||||
}, 2000);
|
|
||||||
}).catch(err => {
|
|
||||||
console.error('Failed to copy: ', err);
|
|
||||||
button.textContent = 'Error';
|
|
||||||
setTimeout(() => {
|
|
||||||
button.textContent = 'Copy';
|
|
||||||
}, 2000);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h3>🌟 Features</h3>
|
<!-- Tabs navigation -->
|
||||||
<ul>
|
<nav class="tab-nav" role="tablist" aria-label="Primary">
|
||||||
<li>Simple HTTP API for file upload/download</li>
|
<button class="tab-btn active" role="tab" aria-selected="true" aria-controls="tab-guide" id="tab-guide-btn" data-tab="guide">Guide</button>
|
||||||
<li>Automatic cleanup after 24 hours</li>
|
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="tab-changelog" id="tab-changelog-btn" data-tab="changelog">Changelog</button>
|
||||||
<li>Storage usage tracking</li>
|
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="tab-about" id="tab-about-btn" data-tab="about">About</button>
|
||||||
<li>Developer-friendly cURL examples</li>
|
</nav>
|
||||||
<li>Health monitoring endpoint</li>
|
|
||||||
</ul>
|
<!-- Tab panels -->
|
||||||
|
<div class="tab-panels">
|
||||||
<h3>🔧 Technical Details</h3>
|
<section id="tab-guide" class="tab-panel active" role="tabpanel" aria-labelledby="tab-guide-btn">
|
||||||
<ul>
|
<div class="content-container">
|
||||||
<li>Built with Go and Gin framework</li>
|
{{template "api-endpoints" .}}
|
||||||
<li>RESTful API design</li>
|
{{template "usage-examples" .}}
|
||||||
<li>Form-based file uploads</li>
|
</div>
|
||||||
<li>JSON responses</li>
|
</section>
|
||||||
</ul>
|
|
||||||
</div>
|
<section id="tab-changelog" class="tab-panel" role="tabpanel" aria-labelledby="tab-changelog-btn" hidden>
|
||||||
|
<div class="content-container">
|
||||||
|
{{template "changelog" .}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="tab-about" class="tab-panel" role="tabpanel" aria-labelledby="tab-about-btn" hidden>
|
||||||
|
<div class="content-container">
|
||||||
|
<h2>About</h2>
|
||||||
|
<div class="about-content">
|
||||||
|
<h3>What is FITRA?</h3>
|
||||||
|
<p>FITRA (File Transfer API) is a lightweight, developer-friendly file sharing service designed for CLI usage and automation.</p>
|
||||||
|
|
||||||
|
<h3>🔒 Security</h3>
|
||||||
|
<p>Files are automatically deleted after 24 hours. This service is designed for temporary file sharing and should not be used for permanent storage.</p>
|
||||||
|
|
||||||
|
<h3>📞 Support</h3>
|
||||||
|
<div class="contact-info">
|
||||||
|
<p><strong>Matrix:</strong> @root@adhd.sh</p>
|
||||||
|
<p><strong>Discord:</strong> nu11ed</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>💰 Donate</h3>
|
||||||
|
<div class="donate-info">
|
||||||
|
<div class="donation-item">
|
||||||
|
<div class="crypto-header">
|
||||||
|
<strong>BTC:</strong>
|
||||||
|
<button class="copy-crypto-btn" data-rybbit-event="Copy BTC" onclick="copyAddress('bc1ql2a3nxnhfwft7qex0cclj5ar2lfsslvs0aygeq', this)">Copy</button>
|
||||||
|
</div>
|
||||||
|
<div class="crypto-row">
|
||||||
|
<textarea class="crypto-address" readonly>bc1ql2a3nxnhfwft7qex0cclj5ar2lfsslvs0aygeq</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="donation-item">
|
||||||
|
<div class="crypto-header">
|
||||||
|
<strong>LTC:</strong>
|
||||||
|
<button class="copy-crypto-btn" data-rybbit-event="Copy LTC" onclick="copyAddress('ltc1qhw5z0m6kcvs0u38nlesndmm3dzu49yrxyqyll9', this)">Copy</button>
|
||||||
|
</div>
|
||||||
|
<div class="crypto-row">
|
||||||
|
<textarea class="crypto-address" readonly>ltc1qhw5z0m6kcvs0u38nlesndmm3dzu49yrxyqyll9</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="donation-item">
|
||||||
|
<div class="crypto-header">
|
||||||
|
<strong>ETH:</strong>
|
||||||
|
<button class="copy-crypto-btn" data-rybbit-event="Copy ETH" onclick="copyAddress('0x30843c72DF6E9A9226d967bf2403602f1C2aB67b', this)">Copy</button>
|
||||||
|
</div>
|
||||||
|
<div class="crypto-row">
|
||||||
|
<textarea class="crypto-address" readonly>0x30843c72DF6E9A9226d967bf2403602f1C2aB67b</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function copyAddress(address, button) {
|
||||||
|
if (address === '[XMR address]') {
|
||||||
|
button.textContent = 'N/A';
|
||||||
|
button.style.background = '#6b7280';
|
||||||
|
setTimeout(() => {
|
||||||
|
button.textContent = 'Copy';
|
||||||
|
button.style.background = '#22c55e';
|
||||||
|
}, 2000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.clipboard.writeText(address).then(() => {
|
||||||
|
button.textContent = '✓';
|
||||||
|
button.classList.add('copied');
|
||||||
|
setTimeout(() => {
|
||||||
|
button.textContent = 'Copy';
|
||||||
|
button.classList.remove('copied');
|
||||||
|
}, 2000);
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('Failed to copy: ', err);
|
||||||
|
button.textContent = 'Error';
|
||||||
|
setTimeout(() => {
|
||||||
|
button.textContent = 'Copy';
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h3>🌟 Features</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Simple HTTP API for file upload/download</li>
|
||||||
|
<li>Automatic cleanup after 24 hours</li>
|
||||||
|
<li>Storage usage tracking</li>
|
||||||
|
<li>Developer-friendly cURL examples</li>
|
||||||
|
<li>Health monitoring endpoint</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>🔧 Technical Details</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Built with Go and Gin framework</li>
|
||||||
|
<li>RESTful API design</li>
|
||||||
|
<li>Form-based file uploads</li>
|
||||||
|
<li>JSON responses</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -59,13 +59,59 @@
|
|||||||
document.getElementById('storage-total').textContent = `${storage.max_gb} GB total`;
|
document.getElementById('storage-total').textContent = `${storage.max_gb} GB total`;
|
||||||
document.getElementById('progress-fill').style.width = `${storage.usage_percent}%`;
|
document.getElementById('progress-fill').style.width = `${storage.usage_percent}%`;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
document.getElementById('storage-used').textContent = 'Error loading storage info';
|
const used = document.getElementById('storage-used');
|
||||||
document.getElementById('storage-percent').textContent = '';
|
const percent = document.getElementById('storage-percent');
|
||||||
document.getElementById('storage-total').textContent = '';
|
const total = document.getElementById('storage-total');
|
||||||
|
if (used) used.textContent = 'Error loading storage info';
|
||||||
|
if (percent) percent.textContent = '';
|
||||||
|
if (total) total.textContent = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadStorageInfo();
|
function initTabs() {
|
||||||
|
const btns = Array.from(document.querySelectorAll('.tab-btn'));
|
||||||
|
const panels = Array.from(document.querySelectorAll('.tab-panel'));
|
||||||
|
if (!btns.length || !panels.length) return;
|
||||||
|
|
||||||
|
function activate(tab) {
|
||||||
|
btns.forEach(b => {
|
||||||
|
const isActive = b.dataset.tab === tab;
|
||||||
|
b.classList.toggle('active', isActive);
|
||||||
|
b.setAttribute('aria-selected', String(isActive));
|
||||||
|
});
|
||||||
|
panels.forEach(p => {
|
||||||
|
const isActive = p.id === `tab-${tab}`;
|
||||||
|
p.classList.toggle('active', isActive);
|
||||||
|
if (isActive) {
|
||||||
|
p.removeAttribute('hidden');
|
||||||
|
} else {
|
||||||
|
p.setAttribute('hidden', '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read initial state from hash
|
||||||
|
const hash = window.location.hash.replace('#', '');
|
||||||
|
if (hash === 'changelog' || hash === 'about' || hash === 'guide') {
|
||||||
|
activate(hash);
|
||||||
|
} else {
|
||||||
|
activate('guide');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wire click handlers
|
||||||
|
btns.forEach(b => b.addEventListener('click', () => {
|
||||||
|
const tab = b.dataset.tab;
|
||||||
|
activate(tab);
|
||||||
|
// push state without jumping
|
||||||
|
history.replaceState(null, '', `#${tab}`);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize on DOM ready
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
loadStorageInfo();
|
||||||
|
initTabs();
|
||||||
|
});
|
||||||
|
|
||||||
function copyToClipboard(text) {
|
function copyToClipboard(text) {
|
||||||
navigator.clipboard.writeText(text).then(() => {
|
navigator.clipboard.writeText(text).then(() => {
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
{{define "changelog"}}
|
{{define "changelog"}}
|
||||||
<h2>Changelog</h2>
|
<h2>Changelog</h2>
|
||||||
|
|
||||||
|
<div class="changelog-entry">
|
||||||
|
<div class="version">v1.1.0</div>
|
||||||
|
<div class="date">2025-08-08</div>
|
||||||
|
<ul>
|
||||||
|
<li>Improved UI</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="changelog-entry">
|
<div class="changelog-entry">
|
||||||
<div class="version">v1.0.1</div>
|
<div class="version">v1.0.1</div>
|
||||||
<div class="date">2025-08-08</div>
|
<div class="date">2025-08-08</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user