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:
@@ -21,13 +21,18 @@ var navItems = []fiber.Map{
|
|||||||
|
|
||||||
func Dashboard(c *fiber.Ctx) error {
|
func Dashboard(c *fiber.Ctx) error {
|
||||||
hosts := hostStore.All()
|
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{
|
return c.Render("index", fiber.Map{
|
||||||
"View": "hosts",
|
"View": "hosts",
|
||||||
"Title": "Hosts",
|
"Title": "Hosts",
|
||||||
"Hosts": hosts,
|
"Hosts": hosts,
|
||||||
"Active": countByStatus(hosts, "active"),
|
"Active": countByStatus(hosts, "active"),
|
||||||
"Offline": countByStatus(hosts, "offline"),
|
"Offline": countByStatus(hosts, "offline"),
|
||||||
"NavItems": navItems,
|
"NavItems": navItems,
|
||||||
|
"Transfers": transfers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,7 @@
|
|||||||
--color-on-surface: #191c1e;
|
--color-on-surface: #191c1e;
|
||||||
--color-surface-container-low: #f0f2f5;
|
--color-surface-container-low: #f0f2f5;
|
||||||
--color-surface-container: #eaecf0;
|
--color-surface-container: #eaecf0;
|
||||||
|
--color-surface-container-high: #e2e5e9;
|
||||||
--color-on-surface-variant: #424656;
|
--color-on-surface-variant: #424656;
|
||||||
--color-outline: #727686;
|
--color-outline: #727686;
|
||||||
--color-outline-variant: #c2c6d8;
|
--color-outline-variant: #c2c6d8;
|
||||||
@@ -294,6 +295,9 @@
|
|||||||
.z-\[180\] {
|
.z-\[180\] {
|
||||||
z-index: 180;
|
z-index: 180;
|
||||||
}
|
}
|
||||||
|
.z-\[200\] {
|
||||||
|
z-index: 200;
|
||||||
|
}
|
||||||
.col-span-full {
|
.col-span-full {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
@@ -312,6 +316,9 @@
|
|||||||
.mt-3 {
|
.mt-3 {
|
||||||
margin-top: calc(var(--spacing) * 3);
|
margin-top: calc(var(--spacing) * 3);
|
||||||
}
|
}
|
||||||
|
.mt-8 {
|
||||||
|
margin-top: calc(var(--spacing) * 8);
|
||||||
|
}
|
||||||
.mr-1 {
|
.mr-1 {
|
||||||
margin-right: var(--spacing);
|
margin-right: var(--spacing);
|
||||||
}
|
}
|
||||||
@@ -324,6 +331,9 @@
|
|||||||
.mb-2 {
|
.mb-2 {
|
||||||
margin-bottom: calc(var(--spacing) * 2);
|
margin-bottom: calc(var(--spacing) * 2);
|
||||||
}
|
}
|
||||||
|
.mb-3 {
|
||||||
|
margin-bottom: calc(var(--spacing) * 3);
|
||||||
|
}
|
||||||
.mb-4 {
|
.mb-4 {
|
||||||
margin-bottom: calc(var(--spacing) * 4);
|
margin-bottom: calc(var(--spacing) * 4);
|
||||||
}
|
}
|
||||||
@@ -390,6 +400,9 @@
|
|||||||
.h-11 {
|
.h-11 {
|
||||||
height: calc(var(--spacing) * 11);
|
height: calc(var(--spacing) * 11);
|
||||||
}
|
}
|
||||||
|
.h-12 {
|
||||||
|
height: calc(var(--spacing) * 12);
|
||||||
|
}
|
||||||
.h-14 {
|
.h-14 {
|
||||||
height: calc(var(--spacing) * 14);
|
height: calc(var(--spacing) * 14);
|
||||||
}
|
}
|
||||||
@@ -408,12 +421,24 @@
|
|||||||
.h-full {
|
.h-full {
|
||||||
height: 100%;
|
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-h-0 {
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
.min-h-\[200px\] {
|
.min-h-\[200px\] {
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
.min-h-full {
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
.min-h-screen {
|
.min-h-screen {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
@@ -453,6 +478,9 @@
|
|||||||
.w-11 {
|
.w-11 {
|
||||||
width: calc(var(--spacing) * 11);
|
width: calc(var(--spacing) * 11);
|
||||||
}
|
}
|
||||||
|
.w-12 {
|
||||||
|
width: calc(var(--spacing) * 12);
|
||||||
|
}
|
||||||
.w-16 {
|
.w-16 {
|
||||||
width: calc(var(--spacing) * 16);
|
width: calc(var(--spacing) * 16);
|
||||||
}
|
}
|
||||||
@@ -483,9 +511,6 @@
|
|||||||
.max-w-lg {
|
.max-w-lg {
|
||||||
max-width: var(--container-lg);
|
max-width: var(--container-lg);
|
||||||
}
|
}
|
||||||
.max-w-md {
|
|
||||||
max-width: var(--container-md);
|
|
||||||
}
|
|
||||||
.max-w-sm {
|
.max-w-sm {
|
||||||
max-width: var(--container-sm);
|
max-width: var(--container-sm);
|
||||||
}
|
}
|
||||||
@@ -526,6 +551,9 @@
|
|||||||
.resize {
|
.resize {
|
||||||
resize: both;
|
resize: both;
|
||||||
}
|
}
|
||||||
|
.resize-none {
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
.grid-cols-1 {
|
.grid-cols-1 {
|
||||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
@@ -589,6 +617,9 @@
|
|||||||
.gap-8 {
|
.gap-8 {
|
||||||
gap: calc(var(--spacing) * 8);
|
gap: calc(var(--spacing) * 8);
|
||||||
}
|
}
|
||||||
|
.gap-12 {
|
||||||
|
gap: calc(var(--spacing) * 12);
|
||||||
|
}
|
||||||
.space-y-0\.5 {
|
.space-y-0\.5 {
|
||||||
:where(& > :not(:last-child)) {
|
:where(& > :not(:last-child)) {
|
||||||
--tw-space-y-reverse: 0;
|
--tw-space-y-reverse: 0;
|
||||||
@@ -868,9 +899,21 @@
|
|||||||
.bg-outline {
|
.bg-outline {
|
||||||
background-color: var(--color-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 {
|
.bg-primary {
|
||||||
background-color: var(--color-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 {
|
.bg-primary-container\/15 {
|
||||||
background-color: color-mix(in srgb, #d6e3ff 15%, transparent);
|
background-color: color-mix(in srgb, #d6e3ff 15%, transparent);
|
||||||
@supports (color: color-mix(in lab, red, red)) {
|
@supports (color: color-mix(in lab, red, red)) {
|
||||||
@@ -946,6 +989,9 @@
|
|||||||
.bg-surface-container {
|
.bg-surface-container {
|
||||||
background-color: var(--color-surface-container);
|
background-color: var(--color-surface-container);
|
||||||
}
|
}
|
||||||
|
.bg-surface-container-high {
|
||||||
|
background-color: var(--color-surface-container-high);
|
||||||
|
}
|
||||||
.bg-surface-container-low {
|
.bg-surface-container-low {
|
||||||
background-color: var(--color-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);
|
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 {
|
.bg-tertiary\/20 {
|
||||||
background-color: color-mix(in srgb, #7e23cc 20%, transparent);
|
background-color: color-mix(in srgb, #7e23cc 20%, transparent);
|
||||||
@supports (color: color-mix(in lab, red, red)) {
|
@supports (color: color-mix(in lab, red, red)) {
|
||||||
@@ -979,6 +1031,12 @@
|
|||||||
background-color: color-mix(in oklab, var(--color-white) 40%, transparent);
|
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 {
|
.bg-white\/70 {
|
||||||
background-color: color-mix(in srgb, #fff 70%, transparent);
|
background-color: color-mix(in srgb, #fff 70%, transparent);
|
||||||
@supports (color: color-mix(in lab, red, red)) {
|
@supports (color: color-mix(in lab, red, red)) {
|
||||||
@@ -1257,6 +1315,12 @@
|
|||||||
.text-outline-variant {
|
.text-outline-variant {
|
||||||
color: var(--color-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 {
|
.text-outline\/40 {
|
||||||
color: color-mix(in srgb, #727686 40%, transparent);
|
color: color-mix(in srgb, #727686 40%, transparent);
|
||||||
@supports (color: color-mix(in lab, red, red)) {
|
@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));
|
--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);
|
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 {
|
.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));
|
--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);
|
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 {
|
.delay-200 {
|
||||||
transition-delay: 200ms;
|
transition-delay: 200ms;
|
||||||
}
|
}
|
||||||
|
.duration-500 {
|
||||||
|
--tw-duration: 500ms;
|
||||||
|
transition-duration: 500ms;
|
||||||
|
}
|
||||||
.select-all {
|
.select-all {
|
||||||
-webkit-user-select: all;
|
-webkit-user-select: all;
|
||||||
user-select: all;
|
user-select: all;
|
||||||
@@ -1697,16 +1769,16 @@
|
|||||||
grid-column: span 2 / span 2;
|
grid-column: span 2 / span 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.sm\:block {
|
|
||||||
@media (width >= 40rem) {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.sm\:grid {
|
.sm\:grid {
|
||||||
@media (width >= 40rem) {
|
@media (width >= 40rem) {
|
||||||
display: grid;
|
display: grid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.sm\:w-48 {
|
||||||
|
@media (width >= 40rem) {
|
||||||
|
width: calc(var(--spacing) * 48);
|
||||||
|
}
|
||||||
|
}
|
||||||
.sm\:w-60 {
|
.sm\:w-60 {
|
||||||
@media (width >= 40rem) {
|
@media (width >= 40rem) {
|
||||||
width: calc(var(--spacing) * 60);
|
width: calc(var(--spacing) * 60);
|
||||||
@@ -1717,11 +1789,6 @@
|
|||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
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 {
|
.sm\:grid-cols-4 {
|
||||||
@media (width >= 40rem) {
|
@media (width >= 40rem) {
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
@@ -2077,6 +2144,10 @@
|
|||||||
syntax: "*";
|
syntax: "*";
|
||||||
inherits: false;
|
inherits: false;
|
||||||
}
|
}
|
||||||
|
@property --tw-duration {
|
||||||
|
syntax: "*";
|
||||||
|
inherits: false;
|
||||||
|
}
|
||||||
@property --tw-scale-x {
|
@property --tw-scale-x {
|
||||||
syntax: "*";
|
syntax: "*";
|
||||||
inherits: false;
|
inherits: false;
|
||||||
@@ -2170,6 +2241,7 @@
|
|||||||
--tw-backdrop-opacity: initial;
|
--tw-backdrop-opacity: initial;
|
||||||
--tw-backdrop-saturate: initial;
|
--tw-backdrop-saturate: initial;
|
||||||
--tw-backdrop-sepia: initial;
|
--tw-backdrop-sepia: initial;
|
||||||
|
--tw-duration: initial;
|
||||||
--tw-scale-x: 1;
|
--tw-scale-x: 1;
|
||||||
--tw-scale-y: 1;
|
--tw-scale-y: 1;
|
||||||
--tw-scale-z: 1;
|
--tw-scale-z: 1;
|
||||||
|
|||||||
@@ -105,6 +105,65 @@
|
|||||||
{{template "host_list" .}}
|
{{template "host_list" .}}
|
||||||
</div>
|
</div>
|
||||||
</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 & 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" .}}
|
{{template "host_modal" .}}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,344 +0,0 @@
|
|||||||
# 🔍 Hostkeeper V2 — UI/UX Audit & Perbaikan Checklist
|
|
||||||
|
|
||||||
> **Acuan perbaikan UI** — Bandingkan HTMX implementation dengan React template di `template/src/components/`
|
|
||||||
> **Target**: Menyamakan visual dengan Lumina Design System hingga level piksel
|
|
||||||
> **Last Updated**: 2026-07-07
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 Design System Reference
|
|
||||||
|
|
||||||
| Token | Value | Status |
|
|
||||||
|-------|-------|--------|
|
|
||||||
| Primary | `#0050cb` | ✅ OK |
|
|
||||||
| Secondary | `#006e2f` | ✅ OK |
|
|
||||||
| Tertiary | `#7e23cc` | ✅ OK |
|
|
||||||
| Surface | `#f7f9fb` | ✅ OK |
|
|
||||||
| On Surface | `#191c1e` | ✅ OK |
|
|
||||||
| On Surface Variant | `#424656` | ✅ OK |
|
|
||||||
| Outline | `#727686` | ✅ OK |
|
|
||||||
| Outline Variant | `#c2c6d8` | ✅ OK |
|
|
||||||
| Error | `#b3261e` | ✅ OK |
|
|
||||||
| Surface Container Low | `#f0f2f5` | ✅ OK |
|
|
||||||
| Surface Container | `#eaecf0` | ✅ OK |
|
|
||||||
| Font Sans | Inter (300–900) | ✅ OK |
|
|
||||||
| Font Mono | JetBrains Mono (300–700) | ✅ OK |
|
|
||||||
| Dot Grid | `radial-gradient(#e2e8f0 1.5px, transparent 1.5px) 16px` | ✅ OK |
|
|
||||||
| Sidebar Width | `260px` | ✅ OK |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔴 P0 — DASHBOARD (index.html + host_list.html + host_modal.html)
|
|
||||||
|
|
||||||
### ~~Task #1: Banner Stats~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #2: Status Filter~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #3: Search Bar~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #4: View Toggle~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #5: Section Title~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #6: Host Card Grid~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #7: Add Host Card~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #8: Host Card List~~ ✅ Selesai
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task #9: Transfer Panel
|
|
||||||
|
|
||||||
**Referensi**: `template/src/components/DashboardView.tsx:306-356`
|
|
||||||
|
|
||||||
Template punya section "Background Transfers" dengan:
|
|
||||||
- Line separator + judul
|
|
||||||
- Table header: Source & File | Destination | Progress
|
|
||||||
- Tiap row: icon (download=tertiary/upload=primary), nama file, source, destination panah, progress bar (h-1.5, bg-secondary, shadow), percentage + speed
|
|
||||||
|
|
||||||
HTMX: Hanya "No active transfers" di sidebar ❌
|
|
||||||
|
|
||||||
**Files**: `app/backend/views/index.html` + `app/backend/views/host_list.html`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### ~~Task #10: Add Host Modal~~ ✅ Selesai
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔴 P0 — TERMINAL (terminal.html + terminal.js)
|
|
||||||
|
|
||||||
### Task #11: Ubah theme dari dark ke light (Lumina)
|
|
||||||
|
|
||||||
**Referensi**: `template/src/components/TerminalView.tsx:208-284`
|
|
||||||
|
|
||||||
| Detail | Template (wajib) | HTMX Saat Ini |
|
|
||||||
|--------|----------|---------------|
|
|
||||||
| Background terminal | `bg-white` | `bg-[#0d1117]` ❌ |
|
|
||||||
| Tab bar | `bg-surface-container-low` | `bg-[#161b22]` ❌ |
|
|
||||||
| Tab border | `border-b border-outline-variant/30` | `border-[#30363d]` ❌ |
|
|
||||||
| Tab active | `bg-white text-primary border-primary shadow` | `bg-[#0d1117] text-white border-t-2 border-[#58a6ff]` ❌ |
|
|
||||||
| Tab inactive | `bg-surface-container-low text-on-surface-variant` | `bg-[#161b22] text-[#8b949e]` ❌ |
|
|
||||||
| Tab close | `hover:bg-black/10 rounded` | `hover:bg-[#30363d]` ❌ |
|
|
||||||
| New tab button | `bg-surface-container hover:bg-primary-container hover:text-on-primary-container` | styling dark ❌ |
|
|
||||||
| Terminal header | `bg-surface-container-low border-b border-outline-variant/30` | tidak ada |
|
|
||||||
|
|
||||||
**Files**: `app/backend/views/terminal.html`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task #12: Traffic Light Dots
|
|
||||||
|
|
||||||
**Referensi**: `template/src/components/TerminalView.tsx:212-216`
|
|
||||||
|
|
||||||
Tambah 3 dot (red `#b3261e`, yellow `#d6e3ff` / kuning, green `#006e2f`) di kiri terminal header.
|
|
||||||
|
|
||||||
**Files**: `app/backend/views/terminal.html`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task #13: SSH Connection String
|
|
||||||
|
|
||||||
**Referensi**: `template/src/components/TerminalView.tsx:216`
|
|
||||||
|
|
||||||
Tampilkan teks: `ssh -i id_ed25519 root@{host.ip}` dengan `text-[10px] font-mono font-bold text-outline`
|
|
||||||
|
|
||||||
**Files**: `app/backend/views/terminal.html`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task #14: "Clear Buffer" Button
|
|
||||||
|
|
||||||
**Referensi**: `template/src/components/TerminalView.tsx:219-232`
|
|
||||||
|
|
||||||
Button `text-[10px] uppercase font-bold text-outline hover:text-primary` dengan `RefreshCw` icon.
|
|
||||||
|
|
||||||
**Files**: `app/backend/views/terminal.html`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task #15: Terminal Output Styles
|
|
||||||
|
|
||||||
**Referensi**: `template/src/components/TerminalView.tsx:241-255`
|
|
||||||
|
|
||||||
| Type | Template | HTMX |
|
|
||||||
|------|----------|------|
|
|
||||||
| input | `text-on-surface font-bold` | (xterm.js) |
|
|
||||||
| error | `text-error bg-error-container/20 px-2.5 py-1 rounded-md` | (xterm.js) |
|
|
||||||
| success | `text-[#006e2f] bg-secondary-container/10 px-2.5 py-1 rounded-md font-semibold` | (xterm.js) |
|
|
||||||
| output | `text-[#2244aa]` | (xterm.js) |
|
|
||||||
|
|
||||||
Ini perlu disesuaikan di terminal.js / xterm theme.
|
|
||||||
|
|
||||||
**Files**: `app/backend/static/js/terminal.js`, `app/backend/views/terminal.html`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task #16: Input Prompt Bar
|
|
||||||
|
|
||||||
**Referensi**: `template/src/components/TerminalView.tsx:259-283`
|
|
||||||
|
|
||||||
| Detail | Template | HTMX |
|
|
||||||
|--------|----------|------|
|
|
||||||
| Background | `bg-surface-container-low` | dark ❌ |
|
|
||||||
| Prompt | `root@{host}:~$` dengan icon `CornerDownLeft` | (di mock WebSocket) |
|
|
||||||
| Input | `bg-transparent font-mono text-xs focus:ring-0` | (xterm.js) |
|
|
||||||
| Execute button | `bg-primary text-white rounded-lg hover:bg-primary-container` | tidak ada |
|
|
||||||
|
|
||||||
**Files**: `app/backend/views/terminal.html`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task #17: Shortcut Pills
|
|
||||||
|
|
||||||
**Referensi**: `template/src/components/TerminalView.tsx:287-301`
|
|
||||||
|
|
||||||
Tambah rail shortcut di bawah terminal: help, ls, docker ps, uname -a, ping 8.8.8.8, cat server.js, keychain, clear.
|
|
||||||
|
|
||||||
| Detail | Template |
|
|
||||||
|--------|----------|
|
|
||||||
| Container | `flex flex-wrap gap-2 text-[11px] font-mono font-bold text-on-surface-variant` |
|
|
||||||
| Label | "SHORTCUT SUGGESTIONS:" |
|
|
||||||
| Pill | `px-2.5 py-1 rounded-md bg-surface-container border border-outline-variant/30 hover:border-primary hover:bg-primary-container/10` |
|
|
||||||
|
|
||||||
**Files**: `app/backend/views/terminal.html`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task #18: Status Info (Security + Host IP)
|
|
||||||
|
|
||||||
**Referensi**: `template/src/components/TerminalView.tsx:197-205`
|
|
||||||
|
|
||||||
| Detail | Template |
|
|
||||||
|--------|----------|
|
|
||||||
| Kiri | "SECURE CRYPTO PATH" dengan green dot + animate-pulse |
|
|
||||||
| Kanan | "HOST: {host.ip}" |
|
|
||||||
|
|
||||||
**Files**: `app/backend/views/terminal.html`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task #19: Sync Mock Commands dengan Template
|
|
||||||
|
|
||||||
**Referensi**: `template/src/components/TerminalView.tsx:61-129`
|
|
||||||
|
|
||||||
Template punya command list: help, ls, docker ps, uname -a, ping 8.8.8.8, cat server.js, keychain, clear.
|
|
||||||
|
|
||||||
HTMX di Go handler punya: clear, exit, pwd, whoami, hostname, date, uptime, uname, ls, cd, cat, echo, ssh, help.
|
|
||||||
|
|
||||||
Output di tiap command juga harus disamakan dengan template (docker ps output, ls output, dll).
|
|
||||||
|
|
||||||
**Files**: `app/backend/internal/handler/terminal.go`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔴 P0 — SNIPPETS (snippets.html + snippet_grid.html) ✅
|
|
||||||
|
|
||||||
### ~~Task #20: Layout — Ubah ke 12-col grid (sidebar + content)~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #21: Sidebar — Collections~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #22: Sidebar — Tag Categories~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #23: Snippet Card — Stacked Layout~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #24: Headers & Searching~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #25: Add Snippet Modal (style)~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #26: Data Model — Tambah field Snippet~~ ✅ Selesai
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🟡 P1 — KEYCHAIN (keychain.html + key_grid.html) ✅
|
|
||||||
|
|
||||||
### ~~Task #27: Layout — 2 col~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #28: Key Card — Style Upgrade~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #29: Add Key Modal (style)~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #30: Security Advisory~~ ✅ Selesai
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🟡 P1 — SFTP (sftp.html + sftp_pane.html) ✅
|
|
||||||
|
|
||||||
### ~~Task #31: Header — Tambah subtitle~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #32: File Pane — Table Structure~~ ✅ Selesai
|
|
||||||
### ~~Task #33: Drop Zone — Style~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #34: Toast Notification~~ ✅ Selesai
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🟡 P1 — SETTINGS (settings.html) ✅
|
|
||||||
|
|
||||||
### ~~Task #35: Layout — 2-column grid~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #36: Profile — Editable Form~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #37: Toggles — iOS-Style~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #38: Billing Card (Kanan)~~ ✅ Selesai
|
|
||||||
|
|
||||||
### ~~Task #39: Danger Zone — Style~~ ✅ Selesai
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🟡 P1 — BRIEF (brief.html)
|
|
||||||
|
|
||||||
### Task #40: Ubah jadi Overlay Panel ✅
|
|
||||||
|
|
||||||
**Referensi**: `template/src/components/BriefOverlay.tsx`
|
|
||||||
|
|
||||||
Styling updated — color palette visual, typography samples, elevation examples, keyboard shortcuts. Halaman stilistik mirror template content.
|
|
||||||
|
|
||||||
**Files**: `app/backend/views/brief.html`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🟡 GLOBAL ISSUES ✅
|
|
||||||
|
|
||||||
### Task #41: SVG Icons — Stroke-width
|
|
||||||
|
|
||||||
Periksa semua SVG inline, pastikan:
|
|
||||||
- Icon kecil (w-3.5, w-4): `stroke-width="1.5"`
|
|
||||||
- Icon besar (w-5, w-6, w-8+): `stroke-width="2"`
|
|
||||||
|
|
||||||
**Files**: Semua view files
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task #42: Border Radius — `rounded-2xl`
|
|
||||||
|
|
||||||
Ubah dari `rounded-xl` ke `rounded-2xl` di:
|
|
||||||
- Host cards (host_list.html)
|
|
||||||
- Snippet cards (snippet_grid.html)
|
|
||||||
- Key cards (key_grid.html)
|
|
||||||
- Settings cards (settings.html)
|
|
||||||
- Modal containers (host_modal.html, snippets.html, keychain.html)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task #43: Shadows
|
|
||||||
|
|
||||||
| Element | Target |
|
|
||||||
|---------|--------|
|
|
||||||
| Host card | `shadow-sm` normal, `shadow-lg` hover |
|
|
||||||
| Snippet card | `shadow-sm` |
|
|
||||||
| Key card | `shadow-sm` normal, `shadow-lg` hover |
|
|
||||||
| Modal | `shadow-2xl` |
|
|
||||||
| Settings card | `shadow-sm` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task #44: Text Styles
|
|
||||||
|
|
||||||
| Element | Target |
|
|
||||||
|---------|--------|
|
|
||||||
| Page title | `text-xl font-bold tracking-tight` |
|
|
||||||
| Subtitle | `text-xs text-on-surface-variant mt-0.5` |
|
|
||||||
| Card title | `font-bold text-base` |
|
|
||||||
| Label form | `text-xs font-bold text-on-surface-variant uppercase tracking-wider` |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task #45: Search Input — `rounded-full`
|
|
||||||
|
|
||||||
Semua search input: `rounded-full` bukan `rounded-lg`.
|
|
||||||
|
|
||||||
**Files**: index.html, snippets.html, keychain.html, sftp.html
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task #46: Nav — Icons
|
|
||||||
|
|
||||||
Periksa semua icon di nav (nav.html) apakah SVG sesuai dengan lucide equivalents:
|
|
||||||
- server → ok
|
|
||||||
- terminal → `TerminalSquare`
|
|
||||||
- folder-sync → `ArrowLeftRight`
|
|
||||||
- code2 → `Code`
|
|
||||||
- key-round → `Key`
|
|
||||||
- settings2 → `Settings`
|
|
||||||
|
|
||||||
**Files**: `app/backend/views/nav.html`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🗺️ Execution Order
|
|
||||||
|
|
||||||
```
|
|
||||||
1. Dashboard (P0) → index.html, host_list.html, host_modal.html
|
|
||||||
2. Terminal (P0) → terminal.html, terminal.js
|
|
||||||
3. Snippets (P0) → snippets.html, snippet_grid.html, model/handler
|
|
||||||
4. Keychain (P1) → keychain.html, key_grid.html
|
|
||||||
5. SFTP (P1) → sftp.html, sftp_pane.html
|
|
||||||
6. Settings (P1) → settings.html
|
|
||||||
7. Brief (P1) → brief.html, nav.html
|
|
||||||
8. Global (P1) → semua files
|
|
||||||
```
|
|
||||||
|
|
||||||
> **⚠️ Checklist selesai**: Setiap task di atas harus dihapus dari file ini setelah selesai dikerjakan.
|
|
||||||
Reference in New Issue
Block a user