Merge pull request #61 from fossyy/staging

fix: prevent upload box from overlapping, ensuring progress is visible
This commit is contained in:
2024-09-15 19:47:31 +07:00
committed by GitHub
3 changed files with 215 additions and 180 deletions

View File

@ -2,6 +2,7 @@ package fileHandler
import ( import (
"fmt" "fmt"
"github.com/a-h/templ"
"github.com/fossyy/filekeeper/app" "github.com/fossyy/filekeeper/app"
"github.com/fossyy/filekeeper/types" "github.com/fossyy/filekeeper/types"
"github.com/fossyy/filekeeper/utils" "github.com/fossyy/filekeeper/utils"
@ -28,7 +29,12 @@ func GET(w http.ResponseWriter, r *http.Request) {
}) })
} }
component := fileView.Main("File Dashboard", filesData, userSession) var component templ.Component
if r.Header.Get("hx-request") == "true" {
component = fileView.MainContent(filesData)
} else {
component = fileView.Main("File Dashboard", filesData, userSession)
}
err = component.Render(r.Context(), w) err = component.Render(r.Context(), w)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())

View File

@ -8,130 +8,131 @@ import (
templ component(title string, files []types.FileData, user types.User) { templ component(title string, files []types.FileData, user types.User) {
@layout.BaseAuth(title) { @layout.BaseAuth(title) {
@layout.Navbar(user) @layout.Navbar(user)
<main class="flex-grow h-full bg-gray-100"> @MainContent(files)
<div class="bg-gray-50 min-h-screen px-4 py-8 sm:px-6 lg:px-8">
<a
class="inline-flex items-center space-x-2 rounded-md bg-muted px-4 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted/80 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
href="/user"
hx-get="/user"
hx-swap="innerHTML"
hx-push-url="true"
hx-target="#content"
rel="ugc"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="h-4 w-4"
>
<path d="m12 19-7-7 7-7"></path>
<path d="M19 12H5"></path>
</svg>
<span>Back</span>
</a>
<div class="mx-auto max-w-7xl">
<div id="dropzone-file" class="border-2 border-dashed border-gray-300 rounded-lg p-8 mb-8 text-center hover:bg-gray-200 transition-colors duration-200 ease-in-out cursor-pointer">
<label for="file-upload" class="flex flex-col items-center justify-center pt-5 pb-6 cursor-pointer">
<svg class="w-12 h-12 mb-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
</svg>
<p class="mb-2 text-lg text-gray-500 font-semibold">
Click to upload or drag and drop
</p>
</label>
<input id="file-upload" name="file-upload" type="file" class="hidden" />
</div>
<div class="overflow-x-auto bg-white shadow-md rounded-lg">
<table class="w-full text-sm text-left text-gray-500">
<thead class="text-xs text-gray-700 uppercase bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 w-[40%]">File Name</th>
<th scope="col" class="px-6 py-3 w-[20%]">File Size</th>
<th scope="col" class="px-6 py-3 w-[20%]">Downloads</th>
<th scope="col" class="px-6 py-3 w-[20%]">Action</th>
</tr>
</thead>
<tbody>
for _, file := range files {
<tr class="bg-white border-b">
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5 mr-2 text-green-500">
<rect width="18" height="18" x="3" y="3" rx="2" ry="2"></rect>
<circle cx="9" cy="9" r="2"></circle>
<path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"></path>
</svg>
{file.Name}
</td>
<td class="px-6 py-4">{file.Size}</td>
<td class="px-6 py-4">
<div class="flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5 mr-2 text-gray-400">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" x2="12" y1="15" y2="3"></line>
</svg>
{file.Downloaded}
</div>
</td>
<td class="px-6 py-4">
<a href={ templ.SafeURL("/file/" + file.ID) } class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded text-xs">
Download
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</main>
<div id="FileUploadBox">
</div>
<script type="text/javascript">
document.addEventListener("dragover", function (event) {
event.preventDefault();
document.getElementById('dropzone-file').classList.add('bg-gray-100', 'border-blue-500');
});
['dragenter', 'dragover'].forEach(eventName => {
document.getElementById('dropzone-file').addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
document.getElementById('dropzone-file').addEventListener(eventName, unhighlight, false);
});
function highlight(e) {
document.getElementById('dropzone-file').classList.add('bg-gray-100', 'border-blue-500');
}
function unhighlight(e) {
document.getElementById('dropzone-file').classList.remove('bg-gray-100', 'border-blue-500');
}
document.addEventListener("drop", async function (event) {
event.preventDefault();
const file = event.dataTransfer.files[0]
await handleFile(file)
});
document.getElementById('dropzone-file').addEventListener('change', async function(event) {
event.preventDefault();
const file = event.target.files[0]
await handleFile(file)
});
</script>
} }
} }
templ MainContent(files []types.FileData) {
<main class="flex-grow h-full bg-gray-100">
<div class="bg-gray-50 min-h-screen px-4 py-8 sm:px-6 lg:px-8">
<a
class="inline-flex items-center space-x-2 rounded-md bg-muted px-4 py-2 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted/80 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
href="/user"
hx-get="/user"
hx-swap="innerHTML"
hx-push-url="true"
hx-target="#content"
rel="ugc"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="h-4 w-4"
>
<path d="m12 19-7-7 7-7"></path>
<path d="M19 12H5"></path>
</svg>
<span>Back</span>
</a>
<div class="mx-auto max-w-7xl">
<div id="dropzone-file" class="border-2 border-dashed border-gray-300 rounded-lg p-8 mb-8 text-center hover:bg-gray-200 transition-colors duration-200 ease-in-out cursor-pointer">
<label for="file-upload" class="flex flex-col items-center justify-center pt-5 pb-6 cursor-pointer">
<svg class="w-12 h-12 mb-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
</svg>
<p class="mb-2 text-lg text-gray-500 font-semibold">
Click to upload or drag and drop
</p>
</label>
<input id="file-upload" name="file-upload" type="file" class="hidden" />
</div>
<div class="overflow-x-auto bg-white shadow-md rounded-lg">
<table class="w-full text-sm text-left text-gray-500">
<thead class="text-xs text-gray-700 uppercase bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 w-[40%]">File Name</th>
<th scope="col" class="px-6 py-3 w-[20%]">File Size</th>
<th scope="col" class="px-6 py-3 w-[20%]">Downloads</th>
<th scope="col" class="px-6 py-3 w-[20%]">Action</th>
</tr>
</thead>
<tbody>
for _, file := range files {
<tr class="bg-white border-b">
<td class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5 mr-2 text-green-500">
<rect width="18" height="18" x="3" y="3" rx="2" ry="2"></rect>
<circle cx="9" cy="9" r="2"></circle>
<path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"></path>
</svg>
{file.Name}
</td>
<td class="px-6 py-4">{file.Size}</td>
<td class="px-6 py-4">
<div class="flex items-center">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5 mr-2 text-gray-400">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" x2="12" y1="15" y2="3"></line>
</svg>
{file.Downloaded}
</div>
</td>
<td class="px-6 py-4">
<a href={ templ.SafeURL("/file/" + file.ID) } class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded text-xs">
Download
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</main>
<script type="text/javascript">
document.addEventListener("dragover", function (event) {
event.preventDefault();
document.getElementById('dropzone-file').classList.add('bg-gray-100', 'border-blue-500');
});
['dragenter', 'dragover'].forEach(eventName => {
document.getElementById('dropzone-file').addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
document.getElementById('dropzone-file').addEventListener(eventName, unhighlight, false);
});
function highlight(e) {
document.getElementById('dropzone-file').classList.add('bg-gray-100', 'border-blue-500');
}
function unhighlight(e) {
document.getElementById('dropzone-file').classList.remove('bg-gray-100', 'border-blue-500');
}
document.addEventListener("drop", async function (event) {
event.preventDefault();
const file = event.dataTransfer.files[0]
await handleFile(file)
});
document.getElementById('dropzone-file').addEventListener('change', async function(event) {
event.preventDefault();
const file = event.target.files[0]
await handleFile(file)
});
</script>
}
templ Main(title string, files []types.FileData, user types.User) { templ Main(title string, files []types.FileData, user types.User) {
@component(title, files, user) @component(title, files, user)
} }

View File

@ -42,61 +42,7 @@ func component(title string, files []types.FileData, user types.User) templ.Comp
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
for _, file := range files { templ_7745c5c3_Err = MainContent(files).Render(ctx, templ_7745c5c3_Buffer)
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(file.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\client\file\file.templ`, Line: 70, Col: 48}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 3)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(file.Size)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\client\file\file.templ`, Line: 72, Col: 69}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 4)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(file.Downloaded)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\client\file\file.templ`, Line: 80, Col: 64}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 5)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 templ.SafeURL = templ.SafeURL("/file/" + file.ID)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 6)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 7)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
@ -116,6 +62,88 @@ func component(title string, files []types.FileData, user types.User) templ.Comp
}) })
} }
func MainContent(files []types.FileData) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
if !templ_7745c5c3_IsBuffer {
templ_7745c5c3_Buffer = templ.GetBuffer()
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
}
ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var3 := templ.GetChildren(ctx)
if templ_7745c5c3_Var3 == nil {
templ_7745c5c3_Var3 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
for _, file := range files {
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 3)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(file.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\client\file\file.templ`, Line: 75, Col: 44}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 4)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(file.Size)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\client\file\file.templ`, Line: 77, Col: 65}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 5)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(file.Downloaded)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\client\file\file.templ`, Line: 85, Col: 60}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 6)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 templ.SafeURL = templ.SafeURL("/file/" + file.ID)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var7)))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 7)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 8)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
}
return templ_7745c5c3_Err
})
}
func Main(title string, files []types.FileData, user types.User) templ.Component { func Main(title string, files []types.FileData, user types.User) templ.Component {
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
@ -124,9 +152,9 @@ func Main(title string, files []types.FileData, user types.User) templ.Component
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer) defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
} }
ctx = templ.InitializeContext(ctx) ctx = templ.InitializeContext(ctx)
templ_7745c5c3_Var7 := templ.GetChildren(ctx) templ_7745c5c3_Var8 := templ.GetChildren(ctx)
if templ_7745c5c3_Var7 == nil { if templ_7745c5c3_Var8 == nil {
templ_7745c5c3_Var7 = templ.NopComponent templ_7745c5c3_Var8 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = component(title, files, user).Render(ctx, templ_7745c5c3_Buffer) templ_7745c5c3_Err = component(title, files, user).Render(ctx, templ_7745c5c3_Buffer)