This commit is contained in:
Chris Wanstrath 2025-06-19 23:06:11 -07:00
parent ffb6fb074c
commit 7733cb3737
2 changed files with 141 additions and 2 deletions

View File

@ -1,3 +1,6 @@
# DATA!
projects/
# dependencies (bun install)
node_modules

View File

@ -240,6 +240,15 @@ const Project = ({ project, files }: { project: Project, files: string[] }) => {
</div>
<div class="bg-white rounded-lg shadow-sm p-6">
<h2 class="text-xl font-semibold mb-4">Files</h2>
<div id="upload-progress" class="hidden mb-4">
<div class="flex items-center gap-2 text-sm text-gray-600 mb-2">
<span id="upload-filename"></span>
<span id="upload-percent">0%</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2">
<div id="upload-bar" class="bg-blue-500 h-2 rounded-full w-0 transition-all"></div>
</div>
</div>
<ul class="space-y-2">
{files.map(file => (
<li key={file} class="flex items-center justify-between py-2 px-4 hover:bg-gray-50 rounded">
@ -289,6 +298,27 @@ const Project = ({ project, files }: { project: Project, files: string[] }) => {
method: 'DELETE'
}).then(() => location.href = '/projects')
}
function showProgress(file) {
const progress = document.getElementById("upload-progress")
const filename = document.getElementById("upload-filename")
const percent = document.getElementById("upload-percent")
const bar = document.getElementById("upload-bar")
progress.classList.remove("hidden")
filename.textContent = file.name
percent.textContent = "0%"
bar.style.width = "0%"
}
function updateProgress(percent) {
document.getElementById("upload-percent").textContent = percent + "%"
document.getElementById("upload-bar").style.width = percent + "%"
}
function hideProgress() {
document.getElementById("upload-progress").classList.add("hidden")
}
</script>
`}
</div>
@ -343,8 +373,30 @@ const Layout = ({ children, title }: { children: any, title: string }) => {
<title>{title} - 💼 Attaché</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="icon" type="image/png" href="/public/favicon.png" />
<style>{`
.drag-active {
position: relative;
}
.drag-active::after {
content: "Drop files to upload";
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
background: rgba(59, 130, 246, 0.1);
backdrop-filter: blur(2px);
z-index: 50;
}
`}</style>
</head>
<body class="min-h-screen bg-gray-50">
<body
class="min-h-screen bg-gray-50"
ondragover="handleGlobalDragOver(event)"
ondragleave="handleGlobalDragLeave(event)"
ondrop="handleGlobalDrop(event)"
>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<nav class="mb-8">
<div class="flex items-center justify-between">
@ -356,6 +408,89 @@ const Layout = ({ children, title }: { children: any, title: string }) => {
</nav>
<main>{children}</main>
</div>
{html`
<script>
function handleGlobalDragOver(e) {
e.preventDefault()
e.stopPropagation()
if (window.location.pathname.startsWith("/project/")) {
document.body.classList.add("drag-active")
}
}
function handleGlobalDragLeave(e) {
e.preventDefault()
e.stopPropagation()
const rect = document.body.getBoundingClientRect()
if (
e.clientX <= rect.left ||
e.clientX >= rect.right ||
e.clientY <= rect.top ||
e.clientY >= rect.bottom
) {
document.body.classList.remove("drag-active")
}
}
function handleGlobalDrop(e) {
e.preventDefault()
e.stopPropagation()
document.body.classList.remove("drag-active")
if (!window.location.pathname.startsWith("/project/")) {
return
}
const files = e.dataTransfer.files
const projectId = document.querySelector("[data-project]").dataset.project
let currentUpload = Promise.resolve()
for (const file of files) {
currentUpload = currentUpload.then(async () => {
const formData = new FormData()
formData.append("file", file)
showProgress(file)
try {
const xhr = new XMLHttpRequest()
await new Promise((resolve, reject) => {
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
const percent = Math.round((e.loaded / e.total) * 100)
updateProgress(percent)
}
}
xhr.onload = () => {
if (xhr.status === 200) {
resolve()
} else {
reject(new Error("Upload failed"))
}
}
xhr.onerror = () => reject(new Error("Upload failed"))
xhr.open("POST", "/api/project/" + projectId + "/upload")
xhr.send(formData)
})
} catch (err) {
console.error("Upload failed:", err)
} finally {
hideProgress()
}
})
}
currentUpload.then(() => location.reload())
}
</script>
`}
</body>
</html>
)
@ -363,5 +498,6 @@ const Layout = ({ children, title }: { children: any, title: string }) => {
export default {
port: 3000,
fetch: app.fetch
fetch: app.fetch,
maxRequestBodySize: 1024 * 1024 * 1024, // 1GB
}