feat: add transfer panel to dashboard + remove PERBAIKANUI.md

- Add Background Transfers section with mock download/upload rows
- Progress bars with green glow shadow, source/destination layout
- Empty state when no transfers active
- Delete PERBAIKANUI.md (all UI tasks complete)
This commit is contained in:
swanadiva
2026-07-09 18:27:47 +07:00
parent 56eaf57649
commit 7accb86203
4 changed files with 155 additions and 363 deletions
+11 -6
View File
@@ -21,13 +21,18 @@ var navItems = []fiber.Map{
func Dashboard(c *fiber.Ctx) error {
hosts := hostStore.All()
transfers := []model.Transfer{
{ID: "1", FileName: "backup_v4.tar.gz", Source: "production-api-01", Destination: "Local Machine", Progress: 82, Speed: "12.4 MB/s", Type: "download", Status: "transferring"},
{ID: "2", FileName: "assets_deploy.zip", Source: "Local Storage", Destination: "web-frontend-02", Progress: 35, Speed: "4.8 MB/s", Type: "upload", Status: "transferring"},
}
return c.Render("index", fiber.Map{
"View": "hosts",
"Title": "Hosts",
"Hosts": hosts,
"Active": countByStatus(hosts, "active"),
"Offline": countByStatus(hosts, "offline"),
"NavItems": navItems,
"View": "hosts",
"Title": "Hosts",
"Hosts": hosts,
"Active": countByStatus(hosts, "active"),
"Offline": countByStatus(hosts, "offline"),
"NavItems": navItems,
"Transfers": transfers,
})
}
+85 -13
View File
@@ -69,6 +69,7 @@
--color-on-surface: #191c1e;
--color-surface-container-low: #f0f2f5;
--color-surface-container: #eaecf0;
--color-surface-container-high: #e2e5e9;
--color-on-surface-variant: #424656;
--color-outline: #727686;
--color-outline-variant: #c2c6d8;
@@ -294,6 +295,9 @@
.z-\[180\] {
z-index: 180;
}
.z-\[200\] {
z-index: 200;
}
.col-span-full {
grid-column: 1 / -1;
}
@@ -312,6 +316,9 @@
.mt-3 {
margin-top: calc(var(--spacing) * 3);
}
.mt-8 {
margin-top: calc(var(--spacing) * 8);
}
.mr-1 {
margin-right: var(--spacing);
}
@@ -324,6 +331,9 @@
.mb-2 {
margin-bottom: calc(var(--spacing) * 2);
}
.mb-3 {
margin-bottom: calc(var(--spacing) * 3);
}
.mb-4 {
margin-bottom: calc(var(--spacing) * 4);
}
@@ -390,6 +400,9 @@
.h-11 {
height: calc(var(--spacing) * 11);
}
.h-12 {
height: calc(var(--spacing) * 12);
}
.h-14 {
height: calc(var(--spacing) * 14);
}
@@ -408,12 +421,24 @@
.h-full {
height: 100%;
}
.h-px {
height: 1px;
}
.max-h-32 {
max-height: calc(var(--spacing) * 32);
}
.max-h-\[90vh\] {
max-height: 90vh;
}
.min-h-0 {
min-height: 0;
}
.min-h-\[200px\] {
min-height: 200px;
}
.min-h-full {
min-height: 100%;
}
.min-h-screen {
min-height: 100vh;
}
@@ -453,6 +478,9 @@
.w-11 {
width: calc(var(--spacing) * 11);
}
.w-12 {
width: calc(var(--spacing) * 12);
}
.w-16 {
width: calc(var(--spacing) * 16);
}
@@ -483,9 +511,6 @@
.max-w-lg {
max-width: var(--container-lg);
}
.max-w-md {
max-width: var(--container-md);
}
.max-w-sm {
max-width: var(--container-sm);
}
@@ -526,6 +551,9 @@
.resize {
resize: both;
}
.resize-none {
resize: none;
}
.grid-cols-1 {
grid-template-columns: repeat(1, minmax(0, 1fr));
}
@@ -589,6 +617,9 @@
.gap-8 {
gap: calc(var(--spacing) * 8);
}
.gap-12 {
gap: calc(var(--spacing) * 12);
}
.space-y-0\.5 {
:where(& > :not(:last-child)) {
--tw-space-y-reverse: 0;
@@ -868,9 +899,21 @@
.bg-outline {
background-color: var(--color-outline);
}
.bg-outline-variant\/30 {
background-color: color-mix(in srgb, #c2c6d8 30%, transparent);
@supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--color-outline-variant) 30%, transparent);
}
}
.bg-primary {
background-color: var(--color-primary);
}
.bg-primary-container\/10 {
background-color: color-mix(in srgb, #d6e3ff 10%, transparent);
@supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--color-primary-container) 10%, transparent);
}
}
.bg-primary-container\/15 {
background-color: color-mix(in srgb, #d6e3ff 15%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@@ -946,6 +989,9 @@
.bg-surface-container {
background-color: var(--color-surface-container);
}
.bg-surface-container-high {
background-color: var(--color-surface-container-high);
}
.bg-surface-container-low {
background-color: var(--color-surface-container-low);
}
@@ -955,6 +1001,12 @@
background-color: color-mix(in oklab, var(--color-surface-container-low) 50%, transparent);
}
}
.bg-tertiary\/10 {
background-color: color-mix(in srgb, #7e23cc 10%, transparent);
@supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--color-tertiary) 10%, transparent);
}
}
.bg-tertiary\/20 {
background-color: color-mix(in srgb, #7e23cc 20%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@@ -979,6 +1031,12 @@
background-color: color-mix(in oklab, var(--color-white) 40%, transparent);
}
}
.bg-white\/50 {
background-color: color-mix(in srgb, #fff 50%, transparent);
@supports (color: color-mix(in lab, red, red)) {
background-color: color-mix(in oklab, var(--color-white) 50%, transparent);
}
}
.bg-white\/70 {
background-color: color-mix(in srgb, #fff 70%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@@ -1257,6 +1315,12 @@
.text-outline-variant {
color: var(--color-outline-variant);
}
.text-outline\/30 {
color: color-mix(in srgb, #727686 30%, transparent);
@supports (color: color-mix(in lab, red, red)) {
color: color-mix(in oklab, var(--color-outline) 30%, transparent);
}
}
.text-outline\/40 {
color: color-mix(in srgb, #727686 40%, transparent);
@supports (color: color-mix(in lab, red, red)) {
@@ -1338,6 +1402,10 @@
--tw-shadow: 0 -4px 12px var(--tw-shadow-color, rgba(0,80,203,0.06));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.shadow-\[0_0_8px_rgba\(0\,110\,47\,0\.3\)\] {
--tw-shadow: 0 0 8px var(--tw-shadow-color, rgba(0,110,47,0.3));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.shadow-lg {
--tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -1420,6 +1488,10 @@
.delay-200 {
transition-delay: 200ms;
}
.duration-500 {
--tw-duration: 500ms;
transition-duration: 500ms;
}
.select-all {
-webkit-user-select: all;
user-select: all;
@@ -1697,16 +1769,16 @@
grid-column: span 2 / span 2;
}
}
.sm\:block {
@media (width >= 40rem) {
display: block;
}
}
.sm\:grid {
@media (width >= 40rem) {
display: grid;
}
}
.sm\:w-48 {
@media (width >= 40rem) {
width: calc(var(--spacing) * 48);
}
}
.sm\:w-60 {
@media (width >= 40rem) {
width: calc(var(--spacing) * 60);
@@ -1717,11 +1789,6 @@
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.sm\:grid-cols-3 {
@media (width >= 40rem) {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
.sm\:grid-cols-4 {
@media (width >= 40rem) {
grid-template-columns: repeat(4, minmax(0, 1fr));
@@ -2077,6 +2144,10 @@
syntax: "*";
inherits: false;
}
@property --tw-duration {
syntax: "*";
inherits: false;
}
@property --tw-scale-x {
syntax: "*";
inherits: false;
@@ -2170,6 +2241,7 @@
--tw-backdrop-opacity: initial;
--tw-backdrop-saturate: initial;
--tw-backdrop-sepia: initial;
--tw-duration: initial;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-scale-z: 1;
+59
View File
@@ -105,6 +105,65 @@
{{template "host_list" .}}
</div>
</div>
<!-- Transfer Activity Panel -->
<div class="space-y-4 mt-8">
<div class="flex items-center gap-4">
<h3 class="text-base font-bold text-on-surface tracking-tight shrink-0">Background Transfers</h3>
<div class="flex-1 h-px bg-outline-variant/30"></div>
</div>
<div class="bg-white/50 backdrop-blur-sm border border-outline-variant rounded-2xl overflow-hidden shadow-sm">
<div class="p-4 border-b border-outline-variant flex items-center justify-between bg-surface-container-low">
<div class="flex gap-12 text-xs font-bold text-on-surface-variant uppercase tracking-tight">
<span>Source &amp; File</span>
<span>Destination</span>
</div>
<span class="text-xs font-bold text-on-surface-variant uppercase tracking-tight">Progress</span>
</div>
<div class="divide-y divide-outline-variant/30 bg-white">
{{range .Transfers}}
<div class="p-4 flex flex-col sm:flex-row sm:items-center justify-between gap-4 hover:bg-primary/5 transition-colors">
<div class="flex items-center gap-4 min-w-0 flex-1">
<div class="p-2 rounded-lg {{if eq .Type "download"}}bg-tertiary/10 text-tertiary{{else}}bg-primary/10 text-primary{{end}}">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
</div>
<div class="min-w-0 flex-1 grid grid-cols-1 sm:grid-cols-2 gap-2">
<div>
<p class="text-sm font-bold truncate text-on-surface">{{.FileName}}</p>
<span class="text-[10px] font-mono text-outline block">{{.Source}}</span>
</div>
<div class="flex items-center gap-1 text-on-surface-variant text-xs">
<svg class="w-4 h-4 text-outline shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3"/></svg>
<span class="truncate">{{.Destination}}</span>
</div>
</div>
</div>
<div class="w-full sm:w-48 shrink-0">
<div class="flex justify-between items-center mb-1">
<span class="text-[10px] font-bold text-secondary">{{.Progress}}%</span>
<span class="text-[10px] text-outline font-mono">{{.Speed}}</span>
</div>
<div class="h-1.5 w-full bg-surface-container-high rounded-full overflow-hidden">
<div class="h-full bg-secondary rounded-full transition-all duration-500 shadow-[0_0_8px_rgba(0,110,47,0.3)]"
style="width: {{.Progress}}%"></div>
</div>
</div>
</div>
{{end}}
{{if not .Transfers}}
<div class="p-8 text-center text-on-surface-variant">
<svg class="w-10 h-10 mx-auto mb-2 text-outline/30" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"/></svg>
<p class="text-sm font-medium">No active transfers</p>
</div>
{{end}}
</div>
</div>
</div>
{{template "host_modal" .}}
</main>
</div>