Skip to content

Commit af72c9b

Browse files
committed
frontend: layout and responsiveness fixes for storage management card and viewports
- Adjusted Installed/USB Payloads grids from md:grid-cols-2 to lg:grid-cols-2 to prevent card overlapping on narrow windows. - Reshaped cards to always use row layout on mobile to eliminate wasted empty vertical space. - Moved Update button to its own full-width row at the bottom of the card with reduced vertical padding to prevent action clashing. - Added remote version target display to the Update button. - Cleaned up width constraints in PayloadName to enable text truncation on narrow screens.
1 parent b3fe1b5 commit af72c9b

2 files changed

Lines changed: 39 additions & 45 deletions

File tree

frontend/src/components/ui/PayloadName.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const PayloadName = ({ path, className, versionClassName, stacked = false, hideI
77
const isUsb = path?.startsWith('/mnt/usb');
88

99
return (
10-
<div className={cn("flex min-w-0 flex-1", stacked ? "flex-col items-start" : "items-center space-x-3", className)}>
10+
<div className={cn("flex min-w-0 flex-1 w-full", stacked ? "flex-col items-stretch" : "items-center space-x-3", className)}>
1111
<div className="flex items-center space-x-2 min-w-0">
1212
{isDelay && !hideIcon && <Zap className="w-4 h-4 text-ps-blue shrink-0" />}
1313
{isUsb && !hideIcon && <Usb className="w-5 h-5 text-ps-blue shrink-0 mr-1" />}
@@ -16,7 +16,7 @@ const PayloadName = ({ path, className, versionClassName, stacked = false, hideI
1616
{version && (
1717
<span className={cn(
1818
stacked
19-
? "text-[11px] font-bold tracking-wider text-ps-blue mt-1 opacity-90"
19+
? "text-[11px] font-bold tracking-wider text-ps-blue mt-1 opacity-90 self-start"
2020
: "text-[10px] px-2 py-0.5 bg-ps-blue/10 text-ps-blue font-bold rounded-md border border-ps-blue/20 shrink-0",
2121
versionClassName)}>
2222
{version}

frontend/src/components/views/StorageHub.jsx

Lines changed: 37 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useState, useEffect, useMemo } from 'react'
22
import { CloudDownload, Upload, Package, Database, RefreshCw, Trash2, Loader2, AlertTriangle, HardDrive, Usb, ChevronDown, Globe } from 'lucide-react'
33
import { QRCodeSVG } from 'qrcode.react'
4-
import { cn, isPS5, isSystemPayload } from '../../utils/helpers'
4+
import { cn, isPS5, parsePayloadName } from '../../utils/helpers'
55
import PayloadName from '../ui/PayloadName'
66

77
const StorageHub = ({ payloads, payloadMeta, onInstall, onDelete, onUpload, onImportFromUsb, config, ip, scrollTarget, onClearScrollTarget }) => {
@@ -30,13 +30,14 @@ const StorageHub = ({ payloads, payloadMeta, onInstall, onDelete, onUpload, onIm
3030

3131
// Legacy single-source: auto-refresh if older than 24h
3232
if (!force && data?.last_update) {
33+
// eslint-disable-next-line react-hooks/purity
3334
const now = Math.floor(Date.now() / 1000)
3435
if (now - Number(data.last_update) > 24 * 60 * 60) {
3536
await fetchRemote(true)
3637
return
3738
}
3839
}
39-
} catch (e) {
40+
} catch {
4041
setError(true)
4142
} finally {
4243
setLoading(false)
@@ -134,7 +135,7 @@ const StorageHub = ({ payloads, payloadMeta, onInstall, onDelete, onUpload, onIm
134135
</span>
135136
</div>
136137

137-
<div className={cn("grid gap-4", isPS5 ? "grid-cols-2" : "grid-cols-1 md:grid-cols-2")}>
138+
<div className={cn("grid gap-4", isPS5 ? "grid-cols-2" : "grid-cols-1 lg:grid-cols-2")}>
138139
{internalPayloads.length === 0 ? (
139140
<div className="col-span-full py-20 border-2 border-dashed border-white/5 rounded-ps-3xl flex flex-col items-center justify-center space-y-4 bg-white/[0.01]">
140141
<Package className="w-16 h-16 text-white/5" />
@@ -149,46 +150,43 @@ const StorageHub = ({ payloads, payloadMeta, onInstall, onDelete, onUpload, onIm
149150
? enrichedSources.flatMap(s => s.payloads)
150151
: remotePayloads
151152
const remoteMatch = allRemote.find(rp => rp.filename === fileName || rp.installedFilename === fileName)
153+
const remoteVersion = remoteMatch?.filename ? parsePayloadName(remoteMatch.filename).version : null
152154
return (
153-
<div key={path} className={cn(
154-
"group flex justify-between p-4 md:p-6 glass-card rounded-ps-2xl border-white/10 hover:border-ps-blue/30 gap-4 relative overflow-hidden",
155-
isPS5 ? "flex-row items-center" : "flex-col md:flex-row md:items-center"
156-
)}>
157-
<div className="flex items-center space-x-4 md:space-x-6 min-w-0">
158-
<div className="p-3 md:p-4 bg-white/5 rounded-2xl group-hover:bg-ps-blue/10 transition-colors shrink-0">
159-
<Package className="w-6 h-6 md:w-8 md:h-8 text-zinc-400 group-hover:text-ps-blue transition-colors" />
160-
</div>
161-
<div className="min-w-0 flex-1">
162-
<PayloadName path={fileName} className="text-xl md:text-2xl text-white" stacked />
163-
{/* Source badge — floats in bottom-right, doesn't expand the row */}
164-
{sourceBadge && (
165-
<div className="absolute bottom-2 right-3 flex items-center gap-1 z-10 pointer-events-none">
166-
<Globe className="w-3 h-3 text-zinc-500 shrink-0" />
167-
<span className="text-[11px] text-zinc-400 font-medium truncate max-w-[160px] select-none">
168-
{sourceBadge}
169-
</span>
170-
</div>
171-
)}
155+
<div key={path} className="group flex flex-col p-4 md:p-6 glass-card rounded-ps-2xl border-white/10 hover:border-ps-blue/30 gap-3 md:gap-4 relative overflow-hidden">
156+
<div className="flex flex-row items-center justify-between w-full gap-4">
157+
<div className="flex items-center space-x-4 md:space-x-6 min-w-0 flex-1">
158+
<div className="p-3 md:p-4 bg-white/5 rounded-2xl group-hover:bg-ps-blue/10 transition-colors shrink-0">
159+
<Package className="w-6 h-6 md:w-8 md:h-8 text-zinc-400 group-hover:text-ps-blue transition-colors" />
160+
</div>
161+
<div className="min-w-0 flex-1 space-y-1">
162+
<PayloadName path={fileName} className="text-xl md:text-2xl text-white" stacked />
163+
{sourceBadge && (
164+
<div className="flex items-center gap-1 text-zinc-500 text-[11px] select-none font-medium">
165+
<Globe className="w-3.5 h-3.5" />
166+
<span>{sourceBadge}</span>
167+
</div>
168+
)}
169+
</div>
172170
</div>
173-
</div>
174-
<div className="flex items-center space-x-3 md:space-x-4 ml-auto md:ml-0">
175-
{remoteMatch?.isUpdate && (
171+
<div className="flex items-center shrink-0">
176172
<button
177-
onClick={() => onInstall(remoteMatch, remoteMatch.source_id, legacyRepoUrl)}
178-
className="flex items-center space-x-2 md:space-x-3 px-4 md:px-6 py-2 md:py-3 bg-emerald-600 hover:bg-emerald-500 text-white rounded-xl font-bold text-xs md:text-sm transition-all"
173+
onClick={() => onDelete(fileName)}
174+
className="p-3 md:p-4 rounded-xl bg-red-950/20 text-red-500 border border-red-500/10 hover:bg-red-500 hover:text-white transition-all"
175+
title="Remove Payload"
179176
>
180-
<RefreshCw className="w-4 h-4 md:w-5 md:h-5" />
181-
<span>Update</span>
177+
<Trash2 className="w-5 h-5 md:w-6 md:h-6" />
182178
</button>
183-
)}
179+
</div>
180+
</div>
181+
{remoteMatch?.isUpdate && (
184182
<button
185-
onClick={() => onDelete(fileName)}
186-
className="p-3 md:p-4 rounded-xl bg-red-950/20 text-red-500 border border-red-500/10 hover:bg-red-500 hover:text-white transition-all"
187-
title="Remove Payload"
183+
onClick={() => onInstall(remoteMatch, remoteMatch.source_id, legacyRepoUrl)}
184+
className="w-full flex items-center justify-center space-x-2 py-2 bg-emerald-600 hover:bg-emerald-500 text-white rounded-xl font-bold text-xs md:text-sm transition-all"
188185
>
189-
<Trash2 className="w-5 h-5 md:w-6 md:h-6" />
186+
<RefreshCw className="w-4 h-4 md:w-5 md:h-5" />
187+
<span>Update{remoteVersion ? ` (to v${remoteVersion.replace(/^v/i, '')})` : ''}</span>
190188
</button>
191-
</div>
189+
)}
192190
</div>
193191
)
194192
})
@@ -363,7 +361,7 @@ const StorageHub = ({ payloads, payloadMeta, onInstall, onDelete, onUpload, onIm
363361
</span>
364362
</div>
365363

366-
<div className={cn("grid gap-4", isPS5 ? "grid-cols-2" : "grid-cols-1 md:grid-cols-2")}>
364+
<div className={cn("grid gap-4", isPS5 ? "grid-cols-2" : "grid-cols-1 lg:grid-cols-2")}>
367365
{payloads.filter(p => p.includes('/mnt/usb')).length === 0 ? (
368366
<div className="col-span-full py-20 border-2 border-dashed border-white/5 rounded-ps-3xl flex flex-col items-center justify-center space-y-6 bg-white/[0.01]">
369367
<div className="relative">
@@ -385,13 +383,9 @@ const StorageHub = ({ payloads, payloadMeta, onInstall, onDelete, onUpload, onIm
385383
</div>
386384
</div>
387385
) : (
388-
payloads.filter(p => p.includes('/mnt/usb')).map((path, i) => {
389-
const fileName = path.split('/').pop()
386+
payloads.filter(p => p.includes('/mnt/usb')).map((path) => {
390387
return (
391-
<div key={path} className={cn(
392-
"group flex justify-between p-4 md:p-6 glass-card rounded-ps-2xl border-white/10 hover:border-ps-blue/30 gap-4",
393-
isPS5 ? "flex-row items-center" : "flex-col md:flex-row md:items-center"
394-
)}>
388+
<div key={path} className="group flex flex-row items-center justify-between p-4 md:p-6 glass-card rounded-ps-2xl border-white/10 hover:border-ps-blue/30 gap-4">
395389
<div className="flex items-center space-x-4 md:space-x-6 min-w-0">
396390
<div className="p-3 md:p-4 bg-white/5 rounded-2xl group-hover:bg-ps-blue/10 transition-colors shrink-0">
397391
<Usb className="w-6 h-6 md:w-8 md:h-8 text-zinc-400 group-hover:text-ps-blue transition-colors" />
@@ -401,7 +395,7 @@ const StorageHub = ({ payloads, payloadMeta, onInstall, onDelete, onUpload, onIm
401395
<p className="text-[10px] text-zinc-600 font-medium font-mono uppercase tracking-tighter opacity-60 truncate">{path}</p>
402396
</div>
403397
</div>
404-
<div className="flex items-center ml-auto md:ml-0">
398+
<div className="flex items-center shrink-0">
405399
<button
406400
onClick={() => onImportFromUsb(path)}
407401
className="flex items-center space-x-2 md:space-x-3 px-4 md:px-6 py-3 md:py-4 bg-white/5 hover:bg-ps-blue text-white rounded-xl font-bold text-xs md:text-sm transition-all border border-white/10 hover:border-ps-blue group/btn"

0 commit comments

Comments
 (0)