v0.3.1: More admin UI improvements and bug fixes
All checks were successful
Build and Push Docker Image to GHCR / build-and-push (push) Successful in 29s

This commit further refines the admin UI, and introduces a very SPA-like
creating process for links and categories. In-place editing has also
been improved, the styling is more correct and better formatted, as well
as having some cleaner code.

This PR also fixes a few bugs:
- Image uploads not being URL encoded, so special characters would break
  images
- If an image has exif, but no orientation tag, the image would be
  wrongfully rejected
- In-place editing forms were not correctly sized, and title inputs
  would not break with line breaks in the titles

This PR also greatly improves performance on the admin UI.
This commit is contained in:
Zoe
2025-09-30 19:45:58 -05:00
parent 462ed6491c
commit 01a147d2d3
5 changed files with 573 additions and 315 deletions

View File

@@ -17,6 +17,7 @@ import (
"log/slog"
"mime/multipart"
"net/http"
"net/url"
"os"
"os/signal"
"path/filepath"
@@ -312,25 +313,23 @@ func UploadFile(file *multipart.FileHeader, fileName, contentType string, c fibe
// if there *is* exif, parse it
if err == nil {
tag, err := x.Get(exif.Orientation)
if err != nil {
return "", fmt.Errorf("failed to get orientation: %v", err)
}
if err == nil {
if tag.Count == 1 && tag.Format() == tiff.IntVal {
orientation, err := tag.Int(0)
if err != nil {
return "", fmt.Errorf("failed to get orientation: %v", err)
}
if tag.Count == 1 && tag.Format() == tiff.IntVal {
orientation, err := tag.Int(0)
if err != nil {
return "", fmt.Errorf("failed to get orientation: %v", err)
}
slog.Debug("Orientation tag found", "orientation", orientation)
slog.Debug("Orientation tag found", "orientation", orientation)
switch orientation {
case 3:
img = imaging.Rotate180(img)
case 6:
img = imaging.Rotate270(img)
case 8:
img = imaging.Rotate90(img)
switch orientation {
case 3:
img = imaging.Rotate180(img)
case 6:
img = imaging.Rotate270(img)
case 8:
img = imaging.Rotate90(img)
}
}
}
}
@@ -381,7 +380,7 @@ func UploadFile(file *multipart.FileHeader, fileName, contentType string, c fibe
}
}
iconPath = "/uploads/" + fileName
iconPath = "/uploads/" + url.PathEscape(fileName)
return iconPath, nil
}

View File

@@ -84,6 +84,7 @@ input:not(.search) {
transition-duration: 150ms;
transition-timing-function: cubic-bezier(0.45, 0, 0.55, 1);
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
contain: layout style paint;
&:not(.admin) {
&:hover {
@@ -113,12 +114,12 @@ input:not(.search) {
}
/* Div that holds the image */
.link-card div:has(img):first-child {
.link-card div[data-img-container] {
flex-shrink: 0;
margin-right: 0.5rem;
}
.link-card div:first-child img {
.link-card div[data-img-container] img {
user-select: none;
border-radius: 0.375rem;
aspect-ratio: 1/1;
@@ -126,10 +127,33 @@ input:not(.search) {
}
/* Div that holds the text */
.link-card div:nth-child(2) {
.link-card div[data-text-container] {
word-break: break-all;
}
.link-card div:nth-child(2) p {
.link-card div[data-text-container] p {
color: var(--color-subtle);
}
.categoy-header {
display: flex;
align-items: center;
}
.category-header div[data-img-container] {
@apply shrink-0 relative mr-2 h-full flex items-center justify-center size-8;
}
.categoy-header div[data-img-container] img {
user-select: none;
object-fit: cover;
aspect-ratio: 1/1;
}
.category-header h2 {
text-transform: capitalize;
word-break: break-all;
border-width: 1px;
border-color: #0000;
}

File diff suppressed because it is too large Load Diff

View File

@@ -76,17 +76,17 @@
<div class="w-full sm:w-4/5 p-2.5">
{{#each Categories}}
<div class="flex items-center mt-2 first:mt-0">
<img class="object-contain mr-2 select-none" width="32" height="32" draggable="false" alt="{{this.Name}}"
src="{{this.Icon}}" />
<img class="object-contain mr-2 select-none size-8" width="32" height="32" draggable="false"
alt="{{this.Name}}" src="{{this.Icon}}" />
<h2 class="capitalize break-all">{{this.Name}}</h2>
</div>
<div class="p-2.5 grid grid-cols-[repeat(auto-fill,_minmax(min(330px,_100%),_1fr))] gap-2">
{{#each this.Links}} <a href="{{this.URL}}" class="link-card" draggable="false" target="_blank"
rel="noopener noreferrer">
<div>
<div data-img-container>
<img width="64" height="64" draggable="false" src="{{this.Icon}}" alt="{{this.Name}}" />
</div>
<div>
<div data-text-container>
<h3>{{this.Name}}</h3>
<p class="min-h-5">{{this.Description}}</p>
</div>

View File

@@ -1,6 +1,6 @@
{
"name": "passport",
"version": "0.3.0",
"version": "0.3.1",
"description": "Passport is a simple, lightweight, and fast dashboard/new tab page for your browser.",
"author": "juls0730",
"license": "BSL-1.0",