Skip to content

Commit a4863f6

Browse files
refactor(ui): Improve device access settings with conditional rendering and loader data handling (#204)
1 parent d49a567 commit a4863f6

File tree

1 file changed

+167
-164
lines changed

1 file changed

+167
-164
lines changed

ui/src/routes/devices.$id.settings.access._index.tsx

Lines changed: 167 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { CLOUD_APP, DEVICE_API } from "../ui.config";
66
import api from "../api";
77
import { LocalDevice } from "./devices.$id";
88
import { useDeviceUiNavigation } from "../hooks/useAppNavigation";
9-
import { isOnDevice } from "../main";
109
import { GridCard } from "../components/Card";
1110
import { ShieldCheckIcon } from "@heroicons/react/24/outline";
1211
import notifications from "../notifications";
@@ -15,16 +14,21 @@ import { useJsonRpc } from "../hooks/useJsonRpc";
1514
import { InputFieldWithLabel } from "../components/InputField";
1615
import { SelectMenuBasic } from "../components/SelectMenuBasic";
1716
import { SettingsSectionHeader } from "../components/SettingsSectionHeader";
17+
import { isOnDevice } from "../main";
1818

1919
export const loader = async () => {
20-
const status = await api
21-
.GET(`${DEVICE_API}/device`)
22-
.then(res => res.json() as Promise<LocalDevice>);
23-
return status;
20+
if (isOnDevice) {
21+
const status = await api
22+
.GET(`${DEVICE_API}/device`)
23+
.then(res => res.json() as Promise<LocalDevice>);
24+
return status;
25+
}
26+
return null;
2427
};
2528

2629
export default function SettingsAccessIndexRoute() {
27-
const { authMode } = useLoaderData() as LocalDevice;
30+
const loaderData = useLoaderData() as LocalDevice | null;
31+
2832
const { navigateTo } = useDeviceUiNavigation();
2933

3034
const [send] = useJsonRpc();
@@ -137,194 +141,193 @@ export default function SettingsAccessIndexRoute() {
137141
syncCloudUrl();
138142
}, [cloudProviders, syncCloudUrl]);
139143

140-
console.log("is adopted:", isAdopted);
141-
142144
return (
143145
<div className="space-y-4">
144146
<SettingsPageHeader
145147
title="Access"
146148
description="Manage the Access Control of the device"
147149
/>
148150

149-
<div className="space-y-4">
150-
<SettingsSectionHeader
151-
title="Local"
152-
description="Manage the mode of local access to the device"
153-
/>
154-
<SettingsItem
155-
title="Authentication Mode"
156-
description={`Current mode: ${authMode === "password" ? "Password protected" : "No password"}`}
157-
>
158-
{authMode === "password" ? (
159-
<Button
160-
size="SM"
161-
theme="light"
162-
text="Disable Protection"
163-
onClick={() => {
164-
navigateTo("./local-auth", { state: { init: "deletePassword" } });
165-
}}
166-
/>
167-
) : (
168-
<Button
169-
size="SM"
170-
theme="light"
171-
text="Enable Password"
172-
onClick={() => {
173-
navigateTo("./local-auth", { state: { init: "createPassword" } });
174-
}}
151+
{loaderData?.authMode && (
152+
<>
153+
<div className="space-y-4">
154+
<SettingsSectionHeader
155+
title="Local"
156+
description="Manage the mode of local access to the device"
175157
/>
176-
)}
177-
</SettingsItem>
158+
<SettingsItem
159+
title="Authentication Mode"
160+
description={`Current mode: ${loaderData.authMode === "password" ? "Password protected" : "No password"}`}
161+
>
162+
{loaderData.authMode === "password" ? (
163+
<Button
164+
size="SM"
165+
theme="light"
166+
text="Disable Protection"
167+
onClick={() => {
168+
navigateTo("./local-auth", { state: { init: "deletePassword" } });
169+
}}
170+
/>
171+
) : (
172+
<Button
173+
size="SM"
174+
theme="light"
175+
text="Enable Password"
176+
onClick={() => {
177+
navigateTo("./local-auth", { state: { init: "createPassword" } });
178+
}}
179+
/>
180+
)}
181+
</SettingsItem>
182+
183+
{loaderData.authMode === "password" && (
184+
<SettingsItem
185+
title="Change Password"
186+
description="Update your device access password"
187+
>
188+
<Button
189+
size="SM"
190+
theme="light"
191+
text="Change Password"
192+
onClick={() => {
193+
navigateTo("./local-auth", { state: { init: "updatePassword" } });
194+
}}
195+
/>
196+
</SettingsItem>
197+
)}
198+
</div>
199+
<div className="h-px w-full bg-slate-800/10 dark:bg-slate-300/20" />
200+
</>
201+
)}
178202

179-
{authMode === "password" && (
180-
<SettingsItem
181-
title="Change Password"
182-
description="Update your device access password"
183-
>
184-
<Button
185-
size="SM"
186-
theme="light"
187-
text="Change Password"
188-
onClick={() => {
189-
navigateTo("./local-auth", { state: { init: "updatePassword" } });
190-
}}
191-
/>
192-
</SettingsItem>
193-
)}
194-
</div>
195-
<div className="h-px w-full bg-slate-800/10 dark:bg-slate-300/20" />
196203
<div className="space-y-4">
197204
<SettingsSectionHeader
198205
title="Remote"
199206
description="Manage the mode of Remote access to the device"
200207
/>
201208

202-
{isOnDevice && (
203-
<>
204-
<div className="space-y-4">
205-
{!isAdopted && (
206-
<>
207-
<SettingsItem
208-
title="Cloud Provider"
209-
description="Select the cloud provider for your device"
210-
>
211-
<SelectMenuBasic
212-
size="SM"
213-
value={selectedUrlOption}
214-
onChange={e => {
215-
const value = e.target.value;
216-
setSelectedUrlOption(value);
217-
}}
218-
options={cloudProviders ?? []}
219-
/>
220-
</SettingsItem>
209+
<div className="space-y-4">
210+
{!isAdopted && (
211+
<>
212+
<SettingsItem
213+
title="Cloud Provider"
214+
description="Select the cloud provider for your device"
215+
>
216+
<SelectMenuBasic
217+
size="SM"
218+
value={selectedUrlOption}
219+
onChange={e => {
220+
const value = e.target.value;
221+
setSelectedUrlOption(value);
222+
}}
223+
options={cloudProviders ?? []}
224+
/>
225+
</SettingsItem>
221226

222-
{selectedUrlOption === "custom" && (
223-
<div className="mt-4 flex items-end gap-x-2 space-y-4">
224-
<InputFieldWithLabel
225-
size="SM"
226-
label="Custom Cloud URL"
227-
value={cloudUrl}
228-
onChange={e => setCloudUrl(e.target.value)}
229-
placeholder="https://api.example.com"
230-
/>
231-
</div>
232-
)}
233-
</>
227+
{selectedUrlOption === "custom" && (
228+
<div className="mt-4 flex items-end gap-x-2 space-y-4">
229+
<InputFieldWithLabel
230+
size="SM"
231+
label="Custom Cloud URL"
232+
value={cloudUrl}
233+
onChange={e => setCloudUrl(e.target.value)}
234+
placeholder="https://api.example.com"
235+
/>
236+
</div>
234237
)}
238+
</>
239+
)}
235240

236-
{/*
241+
{/*
237242
We do the harcoding here to avoid flickering when the default Cloud URL being fetched.
238243
I've tried to avoid harcoding api.jetkvm.com, but it's the only reasonable way I could think of to avoid flickering for now.
239244
*/}
240-
{selectedUrlOption === (defaultCloudUrl || "https://api.jetkvm.com") && (
241-
<GridCard>
242-
<div className="flex items-start gap-x-4 p-4">
243-
<ShieldCheckIcon className="mt-1 h-8 w-8 shrink-0 text-blue-600 dark:text-blue-500" />
244-
<div className="space-y-3">
245-
<div className="space-y-2">
246-
<h3 className="text-base font-bold text-slate-900 dark:text-white">
247-
Cloud Security
248-
</h3>
249-
<div>
250-
<ul className="list-disc space-y-1 pl-5 text-xs text-slate-700 dark:text-slate-300">
251-
<li>End-to-end encryption using WebRTC (DTLS and SRTP)</li>
252-
<li>Zero Trust security model</li>
253-
<li>OIDC (OpenID Connect) authentication</li>
254-
<li>All streams encrypted in transit</li>
255-
</ul>
256-
</div>
257-
258-
<div className="text-xs text-slate-700 dark:text-slate-300">
259-
All cloud components are open-source and available on{" "}
260-
<a
261-
href="https://github.com/jetkvm"
262-
target="_blank"
263-
rel="noopener noreferrer"
264-
className="font-medium text-blue-600 hover:text-blue-800 dark:text-blue-500 dark:hover:text-blue-400"
265-
>
266-
GitHub
267-
</a>
268-
.
269-
</div>
270-
</div>
271-
<hr className="block w-full dark:border-slate-600" />
245+
{selectedUrlOption === (defaultCloudUrl || "https://api.jetkvm.com") && (
246+
<GridCard>
247+
<div className="flex items-start gap-x-4 p-4">
248+
<ShieldCheckIcon className="mt-1 h-8 w-8 shrink-0 text-blue-600 dark:text-blue-500" />
249+
<div className="space-y-3">
250+
<div className="space-y-2">
251+
<h3 className="text-base font-bold text-slate-900 dark:text-white">
252+
Cloud Security
253+
</h3>
254+
<div>
255+
<ul className="list-disc space-y-1 pl-5 text-xs text-slate-700 dark:text-slate-300">
256+
<li>End-to-end encryption using WebRTC (DTLS and SRTP)</li>
257+
<li>Zero Trust security model</li>
258+
<li>OIDC (OpenID Connect) authentication</li>
259+
<li>All streams encrypted in transit</li>
260+
</ul>
261+
</div>
272262

273-
<div>
274-
<LinkButton
275-
to="https://jetkvm.com/docs/networking/remote-access"
276-
size="SM"
277-
theme="light"
278-
text="Learn about our cloud security"
279-
/>
280-
</div>
263+
<div className="text-xs text-slate-700 dark:text-slate-300">
264+
All cloud components are open-source and available on{" "}
265+
<a
266+
href="https://github.com/jetkvm"
267+
target="_blank"
268+
rel="noopener noreferrer"
269+
className="font-medium text-blue-600 hover:text-blue-800 dark:text-blue-500 dark:hover:text-blue-400"
270+
>
271+
GitHub
272+
</a>
273+
.
281274
</div>
282275
</div>
283-
</GridCard>
284-
)}
276+
<hr className="block w-full dark:border-slate-600" />
285277

286-
{!isAdopted ? (
287-
<div className="flex items-end gap-x-2">
278+
<div>
279+
<LinkButton
280+
to="https://jetkvm.com/docs/networking/remote-access"
281+
size="SM"
282+
theme="light"
283+
text="Learn about our cloud security"
284+
/>
285+
</div>
286+
</div>
287+
</div>
288+
</GridCard>
289+
)}
290+
291+
{!isAdopted ? (
292+
<div className="flex items-end gap-x-2">
293+
<Button
294+
onClick={() => onCloudAdoptClick(cloudUrl)}
295+
size="SM"
296+
theme="primary"
297+
text="Adopt KVM to Cloud"
298+
/>
299+
</div>
300+
) : (
301+
<div>
302+
<div className="space-y-2">
303+
<p className="text-sm text-slate-600 dark:text-slate-300">
304+
Your device is adopted to JetKVM Cloud
305+
</p>
306+
<div>
288307
<Button
289-
onClick={() => onCloudAdoptClick(cloudUrl)}
290308
size="SM"
291-
theme="primary"
292-
text="Adopt KVM to Cloud"
309+
theme="light"
310+
text="De-register from Cloud"
311+
className="text-red-600"
312+
onClick={() => {
313+
if (deviceId) {
314+
if (
315+
window.confirm(
316+
"Are you sure you want to de-register this device?",
317+
)
318+
) {
319+
deregisterDevice();
320+
}
321+
} else {
322+
notifications.error("No device ID available");
323+
}
324+
}}
293325
/>
294326
</div>
295-
) : (
296-
<div>
297-
<div className="space-y-2">
298-
<p className="text-sm text-slate-600 dark:text-slate-300">
299-
Your device is adopted to JetKVM Cloud
300-
</p>
301-
<div>
302-
<Button
303-
size="SM"
304-
theme="light"
305-
text="De-register from Cloud"
306-
className="text-red-600"
307-
onClick={() => {
308-
if (deviceId) {
309-
if (
310-
window.confirm(
311-
"Are you sure you want to de-register this device?",
312-
)
313-
) {
314-
deregisterDevice();
315-
}
316-
} else {
317-
notifications.error("No device ID available");
318-
}
319-
}}
320-
/>
321-
</div>
322-
</div>
323-
</div>
324-
)}
327+
</div>
325328
</div>
326-
</>
327-
)}
329+
)}
330+
</div>
328331
</div>
329332
</div>
330333
);

0 commit comments

Comments
 (0)