From cef31b9248da3721d8c053e98c936fb618af32df Mon Sep 17 00:00:00 2001 From: Fendy Date: Thu, 27 Aug 2026 00:04:03 +0800 Subject: [PATCH] feat(web): redesign frontend UI with modern card layout, drag-drop upload, toast notifications, and responsive design --- web/app.js | 49 +++++-- web/index.html | 122 +++++++++++++---- web/login.html | 26 ++-- web/style.css | 348 +++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 498 insertions(+), 47 deletions(-) diff --git a/web/app.js b/web/app.js index a56daca..552cb3c 100644 --- a/web/app.js +++ b/web/app.js @@ -2,6 +2,8 @@ function app() { return { inboxFiles: [], outboxFiles: [], + dragover: false, + toast: { show: false, message: '' }, async loadFiles() { const [inbox, outbox] = await Promise.all([ fetch('/api/files?dir=inbox').then(r => r.json()), @@ -10,28 +12,57 @@ function app() { this.inboxFiles = inbox.files || []; this.outboxFiles = outbox.files || []; }, + showToast(msg) { + this.toast.message = msg; + this.toast.show = true; + setTimeout(() => { this.toast.show = false; }, 2500); + }, async upload(event, dir) { - const file = event.target.files[0]; - const form = new FormData(); - form.append('file', file); - form.append('dir', dir); - await fetch('/api/upload', {method: 'POST', body: form}); + const files = event.target.files; + if (!files || !files.length) return; + for (const file of files) { + const form = new FormData(); + form.append('file', file); + form.append('dir', dir); + await fetch('/api/upload', { method: 'POST', body: form }); + } + event.target.value = ''; + this.showToast('File uploaded'); + this.loadFiles(); + }, + async uploadDropped(event, dir) { + const files = event.dataTransfer.files; + if (!files || !files.length) return; + for (const file of files) { + const form = new FormData(); + form.append('file', file); + form.append('dir', dir); + await fetch('/api/upload', { method: 'POST', body: form }); + } + this.showToast('File uploaded'); this.loadFiles(); }, download(id) { window.location.href = '/api/files/' + id + '/download'; }, async deleteFile(id) { - await fetch('/api/files/' + id, {method: 'DELETE'}); + if (!confirm('Delete this file?')) return; + await fetch('/api/files/' + id, { method: 'DELETE' }); + this.showToast('File deleted'); this.loadFiles(); }, async share(id) { - const res = await fetch('/api/files/' + id + '/share', {method: 'POST'}); + const res = await fetch('/api/files/' + id + '/share', { method: 'POST' }); const data = await res.json(); - alert('Share URL: ' + data.url); + try { + await navigator.clipboard.writeText(data.url); + this.showToast('Share link copied to clipboard'); + } catch (_) { + alert('Share URL: ' + data.url); + } }, async logout() { - await fetch('/api/logout', {method: 'POST'}); + await fetch('/api/logout', { method: 'POST' }); window.location.href = '/login'; } }; diff --git a/web/index.html b/web/index.html index fd3d1a3..4c717b7 100644 --- a/web/index.html +++ b/web/index.html @@ -2,40 +2,118 @@ + yDropbox -
+
-

yDropbox

- +

yDropbox

+
-
-

Inbox

- - + + + +
    + +
+
-
-

Outbox

- +
+
+

+ + Outbox + +

+
+
+ + +
    + +
+
+ +
diff --git a/web/login.html b/web/login.html index 54b5d79..e03a82c 100644 --- a/web/login.html +++ b/web/login.html @@ -2,28 +2,38 @@ + yDropbox Login -
-

yDropbox

-
- - -
+ diff --git a/web/style.css b/web/style.css index 9052c67..3140df4 100644 --- a/web/style.css +++ b/web/style.css @@ -1,8 +1,340 @@ -body { font-family: sans-serif; max-width: 800px; margin: 40px auto; padding: 0 20px; } -header { display: flex; justify-content: space-between; align-items: center; } -section { margin: 20px 0; } -.file-row { display: flex; gap: 10px; align-items: center; margin: 10px 0; } -button { cursor: pointer; } -.container { max-width: 400px; margin: 100px auto; } -form { display: flex; flex-direction: column; gap: 10px; } -input[type="password"] { padding: 8px; font-size: 16px; } +*,*::before,*::after{box-sizing:border-box;margin:0;padding:0} + +:root { + --bg: #f5f6f8; + --surface: #fff; + --border: #e2e5ea; + --text: #1a1d23; + --text-secondary: #6b7280; + --primary: #2563eb; + --primary-hover: #1d4ed8; + --danger: #ef4444; + --danger-hover: #dc2626; + --success: #10b981; + --radius: 10px; + --shadow: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04); + --shadow-lg: 0 4px 12px rgba(0,0,0,.08); +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + background: var(--bg); + color: var(--text); + line-height: 1.5; + min-height: 100vh; +} + +.app { + max-width: 720px; + margin: 0 auto; + padding: 24px 20px 60px; +} + +header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 32px; +} + +header h1 { + font-size: 22px; + font-weight: 700; + letter-spacing: -.3px; + color: var(--text); +} + +header h1 span { + color: var(--primary); +} + +.btn { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 7px 14px; + font-size: 13px; + font-weight: 500; + border: 1px solid var(--border); + border-radius: 7px; + background: var(--surface); + color: var(--text); + cursor: pointer; + transition: all .15s ease; + white-space: nowrap; +} + +.btn:hover { background: #f0f1f3; } + +.btn-primary { + background: var(--primary); + color: #fff; + border-color: var(--primary); +} + +.btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); } + +.btn-danger { + color: var(--danger); + border-color: #fecaca; +} + +.btn-danger:hover { background: #fef2f2; border-color: var(--danger); } + +.btn-ghost { + border-color: transparent; + background: transparent; +} + +.btn-ghost:hover { background: #f0f1f3; } + +.btn-sm { padding: 5px 10px; font-size: 12px; } + +.btn-logout { + border-color: transparent; + color: var(--text-secondary); + font-size: 13px; +} + +.btn-logout:hover { color: var(--text); background: #f0f1f3; } + +.section { + background: var(--surface); + border: 1px solid var(--border); + border-radius: var(--radius); + box-shadow: var(--shadow); + margin-bottom: 20px; + overflow: hidden; +} + +.section-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px 20px; + border-bottom: 1px solid var(--border); +} + +.section-header h2 { + font-size: 15px; + font-weight: 600; + display: flex; + align-items: center; + gap: 8px; +} + +.section-header .badge { + font-size: 11px; + font-weight: 600; + background: var(--bg); + color: var(--text-secondary); + padding: 2px 7px; + border-radius: 10px; +} + +.section-body { padding: 0; } + +.drop-zone { + margin: 16px 20px; + padding: 28px 20px; + border: 2px dashed var(--border); + border-radius: 8px; + text-align: center; + cursor: pointer; + transition: all .2s ease; + position: relative; +} + +.drop-zone:hover, .drop-zone.dragover { + border-color: var(--primary); + background: #eff6ff; +} + +.drop-zone input[type="file"] { + position: absolute; + inset: 0; + opacity: 0; + cursor: pointer; +} + +.drop-zone-icon { + font-size: 24px; + margin-bottom: 6px; + color: var(--text-secondary); +} + +.drop-zone-text { + font-size: 13px; + color: var(--text-secondary); +} + +.drop-zone-text strong { + color: var(--primary); +} + +.file-list { list-style: none; } + +.file-row { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 20px; + border-bottom: 1px solid #f3f4f6; + transition: background .1s ease; +} + +.file-row:last-child { border-bottom: none; } +.file-row:hover { background: #fafbfc; } + +.file-icon { + width: 36px; + height: 36px; + border-radius: 8px; + background: #eff6ff; + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; + flex-shrink: 0; +} + +.file-info { + flex: 1; + min-width: 0; +} + +.file-name { + font-size: 14px; + font-weight: 500; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.file-meta { + font-size: 12px; + color: var(--text-secondary); +} + +.file-actions { + display: flex; + gap: 4px; + flex-shrink: 0; + opacity: 0; + transition: opacity .15s ease; +} + +.file-row:hover .file-actions { opacity: 1; } + +.empty-state { + padding: 40px 20px; + text-align: center; + color: var(--text-secondary); +} + +.empty-state-icon { + font-size: 32px; + margin-bottom: 8px; + opacity: .5; +} + +.empty-state-text { + font-size: 14px; +} + +.login-wrap { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + padding: 20px; +} + +.login-card { + width: 100%; + max-width: 360px; + background: var(--surface); + border: 1px solid var(--border); + border-radius: 12px; + box-shadow: var(--shadow-lg); + padding: 40px 32px; + text-align: center; +} + +.login-card h1 { + font-size: 24px; + font-weight: 700; + margin-bottom: 4px; +} + +.login-card h1 span { color: var(--primary); } + +.login-card p { + font-size: 14px; + color: var(--text-secondary); + margin-bottom: 28px; +} + +.login-card form { + display: flex; + flex-direction: column; + gap: 12px; +} + +.login-card input { + padding: 10px 14px; + font-size: 14px; + border: 1px solid var(--border); + border-radius: 8px; + outline: none; + transition: border-color .15s ease; + width: 100%; +} + +.login-card input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px rgba(37,99,235,.1); } + +.login-card .btn-primary { + width: 100%; + padding: 10px; + font-size: 14px; + justify-content: center; +} + +.login-error { + font-size: 13px; + color: var(--danger); + display: none; +} + +.login-error.show { display: block; } + +.toast { + position: fixed; + bottom: 24px; + left: 50%; + transform: translateX(-50%) translateY(80px); + background: var(--text); + color: #fff; + padding: 10px 20px; + border-radius: 8px; + font-size: 13px; + font-weight: 500; + box-shadow: var(--shadow-lg); + opacity: 0; + transition: all .3s ease; + z-index: 1000; + pointer-events: none; +} + +.toast.show { + opacity: 1; + transform: translateX(-50%) translateY(0); +} + +@media (max-width: 600px) { + .app { padding: 16px 12px 40px; } + .section-header { padding: 12px 16px; } + .file-row { padding: 10px 16px; } + .file-actions { opacity: 1; } + .drop-zone { margin: 12px 16px; } +}