Skip to content

Commit b3fe1b5

Browse files
committed
frontend: Fix mobile layout and styling in Settings and Manage Sources views
1 parent f4c76fb commit b3fe1b5

2 files changed

Lines changed: 172 additions & 103 deletions

File tree

frontend/src/components/views/ManageSourcesView.jsx

Lines changed: 106 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import { QRCodeSVG } from 'qrcode.react'
77
import { cn, isPS5 } from '../../utils/helpers'
88

99
const ManageSourcesView = ({ onBack, ip, addToast, showConfirm }) => {
10-
const [sources, setSources] = useState([])
11-
const [loading, setLoading] = useState(true)
12-
const [newUrl, setNewUrl] = useState('')
13-
const [adding, setAdding] = useState(false)
14-
const [addError, setAddError] = useState('')
10+
const [sources, setSources] = useState([])
11+
const [loading, setLoading] = useState(true)
12+
const [newUrl, setNewUrl] = useState('')
13+
const [adding, setAdding] = useState(false)
14+
const [addError, setAddError] = useState('')
1515
const [showAddForm, setShowAddForm] = useState(false)
1616

1717
useEffect(() => {
@@ -20,7 +20,7 @@ const ManageSourcesView = ({ onBack, ip, addToast, showConfirm }) => {
2020
.then(d => {
2121
if (d?.sources) setSources(d.sources)
2222
})
23-
.catch(() => {})
23+
.catch(() => { })
2424
.finally(() => setLoading(false))
2525
}, [])
2626

@@ -44,7 +44,7 @@ const ManageSourcesView = ({ onBack, ip, addToast, showConfirm }) => {
4444
const move = (idx, dir) => {
4545
if (idx + dir < 1 || idx + dir >= sources.length) return
4646
const updated = [...sources]
47-
;[updated[idx], updated[idx + dir]] = [updated[idx + dir], updated[idx]]
47+
;[updated[idx], updated[idx + dir]] = [updated[idx + dir], updated[idx]]
4848
setSources(updated)
4949
saveSources(updated)
5050
}
@@ -121,7 +121,7 @@ const ManageSourcesView = ({ onBack, ip, addToast, showConfirm }) => {
121121

122122
/* ---- Desktop Mode: full CRUD ---- */
123123
return (
124-
<div className="max-w-3xl mx-auto space-y-10 pb-20">
124+
<div className="w-full max-w-3xl mx-auto space-y-10 pb-20 min-w-0">
125125
{/* Header */}
126126
<div className="flex items-center space-x-6">
127127
<button
@@ -140,41 +140,97 @@ const ManageSourcesView = ({ onBack, ip, addToast, showConfirm }) => {
140140
<Loader2 className="w-10 h-10 text-ps-blue animate-spin" />
141141
</div>
142142
) : (
143-
<div className="space-y-3">
143+
<div className="space-y-3 w-full">
144144
{sources.map((src, idx) => (
145145
<div
146146
key={src.id}
147147
className={cn(
148-
'group flex items-center gap-4 p-5 glass-card rounded-2xl border transition-all',
148+
'group flex flex-col md:flex-row md:items-center gap-4 md:gap-6 p-5 md:p-6 glass-card rounded-2xl border transition-all w-full min-w-0 max-w-full overflow-hidden',
149149
src.removable
150150
? 'border-white/10 hover:border-ps-blue/30'
151151
: 'border-white/5 bg-white/[0.015]'
152152
)}
153153
>
154-
{/* Priority index */}
155-
<div className={cn(
156-
'w-9 h-9 rounded-xl flex items-center justify-center text-sm font-black shrink-0',
157-
idx === 0 ? 'bg-ps-blue/20 text-ps-blue' : 'bg-white/5 text-zinc-500'
158-
)}>
159-
{idx + 1}
160-
</div>
154+
{/* Header row on mobile / Left group on desktop */}
155+
<div className="flex items-center justify-between md:!justify-start flex-1 min-w-0 gap-4 w-full md:w-auto">
156+
<div className="flex items-center gap-3 min-w-0">
157+
{/* Priority index */}
158+
<div className={cn(
159+
'w-9 h-9 rounded-xl flex items-center justify-center text-sm font-black shrink-0',
160+
idx === 0 ? 'bg-ps-blue/20 text-ps-blue' : 'bg-white/5 text-zinc-500'
161+
)}>
162+
{idx + 1}
163+
</div>
164+
165+
{/* Icon */}
166+
<div className="p-2 bg-white/5 rounded-xl shrink-0">
167+
{src.removable
168+
? <Globe className="w-5 h-5 text-zinc-400 group-hover:text-ps-blue transition-colors" />
169+
: <Lock className="w-5 h-5 text-ps-blue" />
170+
}
171+
</div>
172+
173+
{/* Name (mobile only, inline header) */}
174+
<div className="min-w-0 md:hidden">
175+
<p className="font-bold text-white text-base leading-tight truncate">{src.name}</p>
176+
</div>
177+
</div>
178+
179+
{/* Mobile-only controls */}
180+
<div className="flex items-center space-x-2 shrink-0 md:hidden">
181+
{src.removable && (
182+
<>
183+
<button
184+
onClick={() => move(idx, -1)}
185+
disabled={idx <= 1}
186+
className="p-2 rounded-xl bg-white/5 hover:bg-white/10 disabled:opacity-20 disabled:cursor-not-allowed transition-all"
187+
title="Move up"
188+
>
189+
<ChevronUp className="w-4 h-4" />
190+
</button>
191+
<button
192+
onClick={() => move(idx, 1)}
193+
disabled={idx === sources.length - 1}
194+
className="p-2 rounded-xl bg-white/5 hover:bg-white/10 disabled:opacity-20 disabled:cursor-not-allowed transition-all"
195+
title="Move down"
196+
>
197+
<ChevronDown className="w-4 h-4" />
198+
</button>
199+
<button
200+
onClick={() => remove(idx)}
201+
className="p-2 rounded-xl bg-red-950/20 text-red-500 border border-red-500/10 hover:bg-red-500 hover:text-white transition-all"
202+
title="Remove source"
203+
>
204+
<Trash2 className="w-4 h-4" />
205+
</button>
206+
</>
207+
)}
208+
{!src.removable && (
209+
<span className="px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-widest text-ps-blue border border-ps-blue/20 bg-ps-blue/5">
210+
Default
211+
</span>
212+
)}
213+
</div>
161214

162-
{/* Icon */}
163-
<div className="p-2 bg-white/5 rounded-xl shrink-0">
164-
{src.removable
165-
? <Globe className="w-5 h-5 text-zinc-400 group-hover:text-ps-blue transition-colors" />
166-
: <Lock className="w-5 h-5 text-ps-blue" />
167-
}
215+
{/* Desktop-only Name + URL container */}
216+
<div className="hidden md:flex md:flex-col flex-1 min-w-0">
217+
<p className="font-bold text-white text-base leading-tight">{src.name}</p>
218+
<p className="text-xs text-zinc-500 truncate mt-0.5 font-mono">{src.url}</p>
219+
</div>
168220
</div>
169221

170-
{/* Name + URL */}
171-
<div className="flex-1 min-w-0">
172-
<p className="font-bold text-white text-base leading-tight">{src.name}</p>
173-
<p className="text-xs text-zinc-500 truncate mt-0.5 font-mono">{src.url}</p>
222+
{/* Mobile-only URL row (separated for more vertical height and scrollable view) */}
223+
<div className="md:hidden w-full min-w-0 max-w-full overflow-hidden">
224+
<div className="bg-white/[0.02] border border-white/5 rounded-xl p-3.5 flex flex-col gap-1 min-w-0 max-w-full overflow-hidden">
225+
<span className="text-[10px] text-zinc-500 font-bold uppercase tracking-wider">Source URL</span>
226+
<div className="overflow-x-auto py-0.5 custom-scrollbar min-w-0 w-full max-w-full">
227+
<p className="text-xs text-zinc-400 font-mono whitespace-nowrap min-w-0">{src.url}</p>
228+
</div>
229+
</div>
174230
</div>
175231

176-
{/* Controls */}
177-
<div className="flex items-center space-x-2 shrink-0">
232+
{/* Desktop-only Controls */}
233+
<div className="hidden md:flex items-center space-x-2 shrink-0">
178234
{src.removable && (
179235
<>
180236
<button
@@ -193,7 +249,6 @@ const ManageSourcesView = ({ onBack, ip, addToast, showConfirm }) => {
193249
>
194250
<ChevronDown className="w-4 h-4" />
195251
</button>
196-
{/* Remove button */}
197252
<button
198253
onClick={() => remove(idx)}
199254
className="p-2 rounded-xl bg-red-950/20 text-red-500 border border-red-500/10 hover:bg-red-500 hover:text-white transition-all"
@@ -227,34 +282,36 @@ const ManageSourcesView = ({ onBack, ip, addToast, showConfirm }) => {
227282
<form onSubmit={handleAdd} className="p-6 glass-card rounded-2xl border border-white/10 space-y-4">
228283
<p className="font-bold text-white text-lg">Add a New Source</p>
229284
<p className="text-sm text-zinc-500">
230-
Paste the URL to a JSON file. The source name will be read automatically from the JSON.
285+
Paste the URL to a JSON file.
231286
</p>
232-
<div className="flex gap-3">
287+
<div className="flex flex-col md:flex-row gap-3">
233288
<input
234289
type="url"
235290
value={newUrl}
236291
onChange={e => setNewUrl(e.target.value)}
237292
placeholder="https://example.com/payloads.json"
238-
className="flex-1 bg-white/5 border border-white/10 rounded-xl px-5 py-3 text-white text-sm placeholder-zinc-600 focus:outline-none focus:border-ps-blue/50 transition-colors"
293+
className="flex-1 bg-white/5 border border-white/10 rounded-xl px-5 py-3 text-white text-sm placeholder-zinc-600 focus:outline-none focus:border-ps-blue/50 transition-colors w-full"
239294
autoFocus
240295
disabled={adding}
241296
/>
242-
<button
243-
type="submit"
244-
disabled={adding || !newUrl.trim()}
245-
className="flex items-center space-x-2 px-6 py-3 bg-ps-blue hover:bg-ps-blue/80 disabled:opacity-50 text-white rounded-xl font-bold transition-all"
246-
>
247-
{adding ? <Loader2 className="w-4 h-4 animate-spin" /> : <Plus className="w-4 h-4" />}
248-
<span>{adding ? 'Validating...' : 'Add'}</span>
249-
</button>
250-
<button
251-
type="button"
252-
onClick={() => { setShowAddForm(false); setNewUrl(''); setAddError('') }}
253-
disabled={adding}
254-
className="px-5 py-3 bg-white/5 hover:bg-white/10 text-white rounded-xl font-bold transition-all"
255-
>
256-
Cancel
257-
</button>
297+
<div className="flex gap-3 w-full md:w-auto">
298+
<button
299+
type="submit"
300+
disabled={adding || !newUrl.trim()}
301+
className="flex-1 md:flex-initial flex items-center justify-center space-x-2 px-6 py-3 bg-ps-blue hover:bg-ps-blue/80 disabled:opacity-50 text-white rounded-xl font-bold transition-all whitespace-nowrap"
302+
>
303+
{adding ? <Loader2 className="w-4 h-4 animate-spin" /> : <Plus className="w-4 h-4" />}
304+
<span>{adding ? 'Validating...' : 'Add'}</span>
305+
</button>
306+
<button
307+
type="button"
308+
onClick={() => { setShowAddForm(false); setNewUrl(''); setAddError('') }}
309+
disabled={adding}
310+
className="flex-1 md:flex-initial px-5 py-3 bg-white/5 hover:bg-white/10 text-white rounded-xl font-bold transition-all text-center whitespace-nowrap"
311+
>
312+
Cancel
313+
</button>
314+
</div>
258315
</div>
259316
{addError && (
260317
<div className="flex items-center space-x-3 text-red-400 text-sm">

0 commit comments

Comments
 (0)