feat(DeviceDetailOverlay): conditionally render UI elements based on device type and improve cache handling
This commit is contained in:
parent
fefb7699fa
commit
e4a5eac676
|
|
@ -311,24 +311,26 @@ export default function DeviceDetailOverlay({
|
||||||
|
|
||||||
<div className="p-4 space-y-6">
|
<div className="p-4 space-y-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex items-center justify-between">
|
{!device.physical && (
|
||||||
<label className="text-sm text-neutral-500">Enabled</label>
|
<div className="flex items-center justify-between">
|
||||||
<button
|
<label className="text-sm text-neutral-500">Enabled</label>
|
||||||
onClick={() => {
|
<button
|
||||||
const path = getDevicePath();
|
onClick={() => {
|
||||||
updateDeviceSetting([...path, 'enabled'], deviceData.enabled ? 0 : 1);
|
const path = getDevicePath();
|
||||||
}}
|
updateDeviceSetting([...path, 'enabled'], deviceData.enabled ? 0 : 1);
|
||||||
className={`relative w-12 h-6 rounded-full transition-colors ${
|
}}
|
||||||
deviceData.enabled ? 'bg-blue-600' : 'bg-neutral-300'
|
className={`relative w-12 h-6 rounded-full transition-colors ${
|
||||||
}`}
|
deviceData.enabled ? 'bg-blue-600' : 'bg-neutral-300'
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`absolute top-0.5 w-5 h-5 bg-white rounded-full transition-transform ${
|
|
||||||
deviceData.enabled ? 'translate-x-6' : 'translate-x-0.5'
|
|
||||||
}`}
|
}`}
|
||||||
/>
|
>
|
||||||
</button>
|
<div
|
||||||
</div>
|
className={`absolute top-0.5 w-5 h-5 bg-white rounded-full transition-transform ${
|
||||||
|
deviceData.enabled ? 'translate-x-6' : 'translate-x-0.5'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-neutral-500 block mb-2">Type</label>
|
<label className="text-sm text-neutral-500 block mb-2">Type</label>
|
||||||
|
|
@ -350,104 +352,21 @@ export default function DeviceDetailOverlay({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
{!device.physical && <>
|
||||||
<label className="text-sm text-neutral-500 block mb-2">Base URL</label>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={deviceData.base_url ?? ''}
|
|
||||||
onChange={(e) => {
|
|
||||||
const path = getDevicePath();
|
|
||||||
updateDeviceSetting([...path, 'base_url'], e.target.value);
|
|
||||||
}}
|
|
||||||
className="flex-1 px-3 py-2 border border-neutral-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
onClick={() => setBrowsingField('base_url')}
|
|
||||||
className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-neutral-100"
|
|
||||||
title="Browse"
|
|
||||||
>
|
|
||||||
<FolderOpen className="w-5 h-5" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
const path = getDevicePath();
|
|
||||||
updateDeviceSetting([...path, 'base_url'], '');
|
|
||||||
}}
|
|
||||||
className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-red-50 hover:border-red-300 hover:text-red-500"
|
|
||||||
title="Clear Base URL"
|
|
||||||
>
|
|
||||||
<X className="w-5 h-5" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="text-sm text-neutral-500 block mb-2">URL</label>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={deviceData.url ?? ''}
|
|
||||||
onChange={(e) => {
|
|
||||||
const newUrl = e.target.value;
|
|
||||||
const devicePath = getDevicePath();
|
|
||||||
const newConfig = JSON.parse(JSON.stringify(config));
|
|
||||||
let dev = newConfig;
|
|
||||||
for (const k of devicePath) dev = dev[k];
|
|
||||||
if (isOutsideBase(newUrl, dev.base_url || '')) clearBaseAndCache(dev);
|
|
||||||
dev.url = newUrl;
|
|
||||||
setConfig(newConfig);
|
|
||||||
}}
|
|
||||||
className="flex-1 px-3 py-2 border border-neutral-300 rounded-lg"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
onClick={() => setBrowsingField('url')}
|
|
||||||
className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-neutral-100"
|
|
||||||
title="Browse"
|
|
||||||
>
|
|
||||||
<FolderOpen className="w-5 h-5" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
const devicePath = getDevicePath();
|
|
||||||
const newConfig = JSON.parse(JSON.stringify(config));
|
|
||||||
let dev = newConfig;
|
|
||||||
for (const k of devicePath) dev = dev[k];
|
|
||||||
delete dev.url;
|
|
||||||
delete dev.media_set;
|
|
||||||
setConfig(newConfig);
|
|
||||||
}}
|
|
||||||
className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-red-50 hover:border-red-300 hover:text-red-500"
|
|
||||||
title="Clear URL and media set"
|
|
||||||
>
|
|
||||||
<X className="w-5 h-5" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{mediaSetFiles && (
|
|
||||||
<div className="mt-3">
|
|
||||||
<MediaSet files={mediaSetFiles} activeUrl={deviceData.url ?? ''} onSwitch={switchMedia} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{(deviceData.cache !== undefined ||
|
|
||||||
(deviceData.base_url ?? '').includes('://') ||
|
|
||||||
(deviceData.url ?? '').includes('://')) && (
|
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-neutral-500 block mb-2">Cache</label>
|
<label className="text-sm text-neutral-500 block mb-2">Base URL</label>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={deviceData.cache ?? ''}
|
value={deviceData.base_url ?? ''}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const path = getDevicePath();
|
const path = getDevicePath();
|
||||||
updateDeviceSetting([...path, 'cache'], e.target.value);
|
updateDeviceSetting([...path, 'base_url'], e.target.value);
|
||||||
}}
|
}}
|
||||||
className="flex-1 px-3 py-2 border border-neutral-300 rounded-lg"
|
className="flex-1 px-3 py-2 border border-neutral-300 rounded-lg"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={() => setBrowsingField('cache')}
|
onClick={() => setBrowsingField('base_url')}
|
||||||
className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-neutral-100"
|
className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-neutral-100"
|
||||||
title="Browse"
|
title="Browse"
|
||||||
>
|
>
|
||||||
|
|
@ -456,35 +375,125 @@ export default function DeviceDetailOverlay({
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const path = getDevicePath();
|
const path = getDevicePath();
|
||||||
updateDeviceSetting([...path, 'cache'], '');
|
updateDeviceSetting([...path, 'base_url'], '');
|
||||||
}}
|
}}
|
||||||
className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-red-50 hover:border-red-300 hover:text-red-500"
|
className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-red-50 hover:border-red-300 hover:text-red-500"
|
||||||
title="Clear Cache"
|
title="Clear Base URL"
|
||||||
>
|
>
|
||||||
<X className="w-5 h-5" />
|
<X className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
<div>
|
||||||
|
<label className="text-sm text-neutral-500 block mb-2">URL</label>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={deviceData.url ?? ''}
|
||||||
|
onChange={(e) => {
|
||||||
|
const newUrl = e.target.value;
|
||||||
|
const devicePath = getDevicePath();
|
||||||
|
const newConfig = JSON.parse(JSON.stringify(config));
|
||||||
|
let dev = newConfig;
|
||||||
|
for (const k of devicePath) dev = dev[k];
|
||||||
|
if (isOutsideBase(newUrl, dev.base_url || '')) clearBaseAndCache(dev);
|
||||||
|
dev.url = newUrl;
|
||||||
|
setConfig(newConfig);
|
||||||
|
}}
|
||||||
|
className="flex-1 px-3 py-2 border border-neutral-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => setBrowsingField('url')}
|
||||||
|
className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-neutral-100"
|
||||||
|
title="Browse"
|
||||||
|
>
|
||||||
|
<FolderOpen className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
const devicePath = getDevicePath();
|
||||||
|
const newConfig = JSON.parse(JSON.stringify(config));
|
||||||
|
let dev = newConfig;
|
||||||
|
for (const k of devicePath) dev = dev[k];
|
||||||
|
delete dev.url;
|
||||||
|
delete dev.media_set;
|
||||||
|
setConfig(newConfig);
|
||||||
|
}}
|
||||||
|
className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-red-50 hover:border-red-300 hover:text-red-500"
|
||||||
|
title="Clear URL and media set"
|
||||||
|
>
|
||||||
|
<X className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{mediaSetFiles && (
|
||||||
|
<div className="mt-3">
|
||||||
|
<MediaSet files={mediaSetFiles} activeUrl={deviceData.url ?? ''} onSwitch={switchMedia} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{(deviceData.cache !== undefined ||
|
||||||
|
(deviceData.base_url ?? '').includes('://') ||
|
||||||
|
(deviceData.url ?? '').includes('://')) && (
|
||||||
|
<div>
|
||||||
|
<label className="text-sm text-neutral-500 block mb-2">Cache</label>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={deviceData.cache ?? ''}
|
||||||
|
onChange={(e) => {
|
||||||
|
const path = getDevicePath();
|
||||||
|
updateDeviceSetting([...path, 'cache'], e.target.value);
|
||||||
|
}}
|
||||||
|
className="flex-1 px-3 py-2 border border-neutral-300 rounded-lg"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => setBrowsingField('cache')}
|
||||||
|
className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-neutral-100"
|
||||||
|
title="Browse"
|
||||||
|
>
|
||||||
|
<FolderOpen className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
const path = getDevicePath();
|
||||||
|
updateDeviceSetting([...path, 'cache'], '');
|
||||||
|
}}
|
||||||
|
className="px-3 py-2 border border-neutral-300 rounded-lg bg-neutral-50 hover:bg-red-50 hover:border-red-300 hover:text-red-500"
|
||||||
|
title="Clear Cache"
|
||||||
|
>
|
||||||
|
<X className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>}
|
||||||
|
|
||||||
{deviceData.mode !== undefined && (
|
{deviceData.mode !== undefined && (
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<label className="text-sm text-neutral-500">Mode</label>
|
<label className="text-sm text-neutral-500">Mode</label>
|
||||||
<div className="flex rounded-lg border border-neutral-300 overflow-hidden text-sm">
|
{device.physical
|
||||||
{([0, 1] as const).map((val, i) => (
|
? <span className="text-sm text-neutral-700 px-3 py-2">
|
||||||
<button
|
{(deviceData.mode ?? 0) === 0 ? 'Read Only' : 'Write Enabled'}
|
||||||
key={val}
|
</span>
|
||||||
onClick={() => updateDeviceSetting([...getDevicePath(), 'mode'], val)}
|
: <div className="flex rounded-lg border border-neutral-300 overflow-hidden text-sm">
|
||||||
className={`px-4 py-2 ${i > 0 ? 'border-l border-neutral-300' : ''} ${
|
{([0, 1] as const).map((val, i) => (
|
||||||
(deviceData.mode ?? 0) === val
|
<button
|
||||||
? 'bg-blue-600 text-white'
|
key={val}
|
||||||
: 'bg-white text-neutral-700 hover:bg-neutral-50'
|
onClick={() => updateDeviceSetting([...getDevicePath(), 'mode'], val)}
|
||||||
}`}
|
className={`px-4 py-2 ${i > 0 ? 'border-l border-neutral-300' : ''} ${
|
||||||
>
|
(deviceData.mode ?? 0) === val
|
||||||
{val === 0 ? 'Read Only' : 'Write Enabled'}
|
? 'bg-blue-600 text-white'
|
||||||
</button>
|
: 'bg-white text-neutral-700 hover:bg-neutral-50'
|
||||||
))}
|
}`}
|
||||||
</div>
|
>
|
||||||
|
{val === 0 ? 'Read Only' : 'Write Enabled'}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user