mirror of
https://github.com/gurucomputing/headscale-ui.git
synced 2026-07-17 16:39:45 +00:00
renamed store variables to be consistent, added search bar
This commit is contained in:
parent
41a7263c45
commit
7e0153ceda
14 changed files with 121 additions and 96 deletions
|
|
@ -1,32 +1,32 @@
|
|||
<script>
|
||||
import { onMount } from 'svelte';
|
||||
import { headscaleDeviceSortStore, headscaleDeviceSortDirectionStore, headscaleUserSortStore, headscaleUserSortDirectionStore, headscaleThemeStore } from '$lib/common/stores.js';
|
||||
import { headscaleURLStore } from '$lib/common/stores.js';
|
||||
import { headscaleAPIKeyStore } from '$lib/common/stores.js';
|
||||
import { deviceSortStore, deviceSortDirectionStore, userSortStore, sortDirectionStore, themeStore } from '$lib/common/stores.js';
|
||||
import { URLStore } from '$lib/common/stores.js';
|
||||
import { APIKeyStore } from '$lib/common/stores.js';
|
||||
import { preAuthHideStore } from '$lib/common/stores.js';
|
||||
|
||||
onMount(async () => {
|
||||
// stores headscale theme
|
||||
headscaleThemeStore.set(localStorage.getItem('headscaleTheme') || 'hsui');
|
||||
headscaleThemeStore.subscribe((val) => localStorage.setItem('headscaleTheme', val));
|
||||
themeStore.set(localStorage.getItem('headscaleTheme') || 'hsui');
|
||||
themeStore.subscribe((val) => localStorage.setItem('headscaleTheme', val));
|
||||
|
||||
// stores device sort preferences
|
||||
headscaleDeviceSortStore.set(localStorage.getItem('headscaleDeviceSort') || 'id');
|
||||
headscaleDeviceSortStore.subscribe((val) => localStorage.setItem('headscaleDeviceSort', val));
|
||||
headscaleDeviceSortDirectionStore.set(localStorage.getItem('headscaleDeviceSortDirection') || 'ascending');
|
||||
headscaleDeviceSortDirectionStore.subscribe((val) => localStorage.setItem('headscaleDeviceSortDirection', val));
|
||||
deviceSortStore.set(localStorage.getItem('headscaleDeviceSort') || 'id');
|
||||
deviceSortStore.subscribe((val) => localStorage.setItem('headscaleDeviceSort', val));
|
||||
deviceSortDirectionStore.set(localStorage.getItem('headscaleDeviceSortDirection') || 'ascending');
|
||||
deviceSortDirectionStore.subscribe((val) => localStorage.setItem('headscaleDeviceSortDirection', val));
|
||||
|
||||
// stores user sort preferences
|
||||
headscaleUserSortStore.set(localStorage.getItem('headscaleUserSort') || 'id');
|
||||
headscaleUserSortStore.subscribe((val) => localStorage.setItem('headscaleUserSort', val));
|
||||
headscaleUserSortDirectionStore.set(localStorage.getItem('headscaleUserSortDirection') || 'ascending');
|
||||
headscaleUserSortDirectionStore.subscribe((val) => localStorage.setItem('headscaleUserSortDirection', val));
|
||||
userSortStore.set(localStorage.getItem('headscaleUserSort') || 'id');
|
||||
userSortStore.subscribe((val) => localStorage.setItem('headscaleUserSort', val));
|
||||
sortDirectionStore.set(localStorage.getItem('headscaleUserSortDirection') || 'ascending');
|
||||
sortDirectionStore.subscribe((val) => localStorage.setItem('headscaleUserSortDirection', val));
|
||||
|
||||
// stores URL and API key
|
||||
headscaleURLStore.set(localStorage.getItem('headscaleURL') || '');
|
||||
headscaleURLStore.subscribe((val) => localStorage.setItem('headscaleURL', val));
|
||||
headscaleAPIKeyStore.set(localStorage.getItem('headscaleAPIKey') || '');
|
||||
headscaleAPIKeyStore.subscribe((val) => localStorage.setItem('headscaleAPIKey', val));
|
||||
URLStore.set(localStorage.getItem('headscaleURL') || '');
|
||||
URLStore.subscribe((val) => localStorage.setItem('headscaleURL', val));
|
||||
APIKeyStore.set(localStorage.getItem('headscaleAPIKey') || '');
|
||||
APIKeyStore.subscribe((val) => localStorage.setItem('headscaleAPIKey', val));
|
||||
|
||||
// stores whether preauthkeys get hidden when expired/used
|
||||
preAuthHideStore.set((localStorage.getItem('headscalePreAuthHide') || 'false') == 'true');
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script context="module" lang="ts">
|
||||
import { Device, PreAuthKey, Route, User } from '$lib/common/classes';
|
||||
import { headscaleDeviceStore, headscaleUserStore, apiTestStore } from '$lib/common/stores.js'
|
||||
import { deviceStore, userStore, apiTestStore } from '$lib/common/stores.js'
|
||||
|
||||
export async function getUsers(): Promise<any> {
|
||||
// variables in local storage
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
});
|
||||
// Set the store
|
||||
apiTestStore.set('succeeded');
|
||||
headscaleUserStore.set(headscaleUsers);
|
||||
userStore.set(headscaleUsers);
|
||||
}
|
||||
|
||||
export async function editUser(currentUsername: string, newUsername: string): Promise<any> {
|
||||
|
|
@ -220,7 +220,7 @@
|
|||
});
|
||||
// set the stores
|
||||
apiTestStore.set('succeeded');
|
||||
headscaleDeviceStore.set(headscaleDevices);
|
||||
deviceStore.set(headscaleDevices);
|
||||
}
|
||||
|
||||
export async function getDeviceRoutes(deviceID: string): Promise<Route> {
|
||||
|
|
|
|||
|
|
@ -6,17 +6,19 @@ export const alertStore = writable('');
|
|||
// used to determine if the API is functioning
|
||||
export const apiTestStore = writable('');
|
||||
// stores the theme
|
||||
export const headscaleThemeStore = writable('');
|
||||
export const themeStore = writable('');
|
||||
// stores URL and API Key
|
||||
export const headscaleURLStore = writable('');
|
||||
export const headscaleAPIKeyStore = writable('');
|
||||
export const URLStore = writable('');
|
||||
export const APIKeyStore = writable('');
|
||||
// stores preauth key preference
|
||||
export const preAuthHideStore = writable(false);
|
||||
// stores user and device data
|
||||
export const headscaleUserStore = writable([new User()]);
|
||||
export const headscaleDeviceStore = writable([new Device()]);
|
||||
export const userStore = writable([new User()]);
|
||||
export const deviceStore = writable([new Device()]);
|
||||
// stores search state
|
||||
export const userSearchStore = writable('');
|
||||
// stores sorting preferences
|
||||
export const headscaleDeviceSortStore = writable('id');
|
||||
export const headscaleDeviceSortDirectionStore = writable('ascending');
|
||||
export const headscaleUserSortStore = writable('id');
|
||||
export const headscaleUserSortDirectionStore = writable('ascending');
|
||||
export const deviceSortStore = writable('id');
|
||||
export const deviceSortDirectionStore = writable('ascending');
|
||||
export const userSortStore = writable('id');
|
||||
export const sortDirectionStore = writable('ascending');
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import { fade } from 'svelte/transition';
|
||||
import { headscaleUserStore, headscaleDeviceStore } from '$lib/common/stores';
|
||||
import { userStore, deviceStore } from '$lib/common/stores';
|
||||
import { getDevices, newDevice } from '$lib/common/apiFunctions.svelte';
|
||||
import { alertStore } from '$lib/common/stores.js';
|
||||
import { base } from '$app/paths';
|
||||
|
|
@ -55,7 +55,7 @@
|
|||
<div class="flex-none">
|
||||
<label class="block text-secondary text-sm font-bold mb-2" for="select">Select User</label>
|
||||
<select class="card-select mr-3" required bind:value={selectedUser}>
|
||||
{#each $headscaleUserStore as user}
|
||||
{#each $userStore as user}
|
||||
<option>{user.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { fade } from 'svelte/transition';
|
||||
import { Device } from '$lib/common/classes';
|
||||
import { headscaleUserStore, alertStore } from '$lib/common/stores';
|
||||
import { userStore, alertStore } from '$lib/common/stores';
|
||||
import { moveDevice, getDevices } from '$lib/common/apiFunctions.svelte';
|
||||
|
||||
export let device = new Device();
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
{:else}
|
||||
<form on:submit|preventDefault={moveDeviceAction}>
|
||||
<select class="card-select mr-3" required bind:value={selectedUser}>
|
||||
{#each $headscaleUserStore as user}
|
||||
{#each $userStore as user}
|
||||
<option>{user.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { fade } from 'svelte/transition';
|
||||
import { getDevices, removeDevice } from '$lib/common/apiFunctions.svelte';
|
||||
import { headscaleDeviceStore, alertStore } from '$lib/common/stores.js';
|
||||
import { deviceStore, alertStore } from '$lib/common/stores.js';
|
||||
import { Device } from '$lib/common/classes';
|
||||
|
||||
export let device = new Device();
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<script lang="ts">
|
||||
import { getDevices } from '$lib/common/apiFunctions.svelte';
|
||||
import { headscaleDeviceSortDirectionStore, headscaleDeviceSortStore } from '$lib/common/stores.js';
|
||||
import { deviceSortDirectionStore, deviceSortStore } from '$lib/common/stores.js';
|
||||
|
||||
function sortAction() {
|
||||
if ($headscaleDeviceSortDirectionStore == 'ascending') {
|
||||
$headscaleDeviceSortDirectionStore = 'descending';
|
||||
if ($deviceSortDirectionStore == 'ascending') {
|
||||
$deviceSortDirectionStore = 'descending';
|
||||
} else {
|
||||
$headscaleDeviceSortDirectionStore = 'ascending';
|
||||
$deviceSortDirectionStore = 'ascending';
|
||||
}
|
||||
getDevices();
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
}}
|
||||
class="mx-1"
|
||||
>
|
||||
{#if $headscaleDeviceSortDirectionStore == 'ascending'}
|
||||
{#if $deviceSortDirectionStore == 'ascending'}
|
||||
<!-- ascending sort icon -->
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
|
|
@ -34,8 +34,8 @@
|
|||
</button>
|
||||
|
||||
<span class="btn-group">
|
||||
<button class:btn-active="{$headscaleDeviceSortStore == 'id'}" on:click="{() => {$headscaleDeviceSortStore = 'id'; getDevices()}}" class="btn btn-xs">ID</button>
|
||||
<button class:btn-active="{$headscaleDeviceSortStore == 'givenName'}" on:click="{() => {$headscaleDeviceSortStore = 'givenName'; getDevices()}}" class="btn btn-xs capitalize">Device Name</button>
|
||||
<button class:btn-active="{$headscaleDeviceSortStore == 'lastSeen'}" on:click="{() => {$headscaleDeviceSortStore = 'lastSeen'; getDevices()}}" class="btn btn-xs capitalize">Last Seen</button>
|
||||
<button class:btn-active="{$deviceSortStore == 'id'}" on:click="{() => {$deviceSortStore = 'id'; getDevices()}}" class="btn btn-xs">ID</button>
|
||||
<button class:btn-active="{$deviceSortStore == 'givenName'}" on:click="{() => {$deviceSortStore = 'givenName'; getDevices()}}" class="btn btn-xs capitalize">Device Name</button>
|
||||
<button class:btn-active="{$deviceSortStore == 'lastSeen'}" on:click="{() => {$deviceSortStore = 'lastSeen'; getDevices()}}" class="btn btn-xs capitalize">Last Seen</button>
|
||||
</span>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<script lang="ts">
|
||||
import { fly } from 'svelte/transition';
|
||||
import { headscaleURLStore } from '$lib/common/stores.js';
|
||||
import { headscaleAPIKeyStore } from '$lib/common/stores.js';
|
||||
import { URLStore } from '$lib/common/stores.js';
|
||||
import { APIKeyStore } from '$lib/common/stores.js';
|
||||
import { getUsers } from '$lib/common/apiFunctions.svelte';
|
||||
|
||||
// Server Settings
|
||||
let headscaleURL = $headscaleURLStore;
|
||||
let headscaleAPIKey = $headscaleAPIKeyStore;
|
||||
let headscaleURL = $URLStore;
|
||||
let headscaleAPIKey = $APIKeyStore;
|
||||
let serverSettingsForm: HTMLFormElement;
|
||||
let apiStatus = 'untested';
|
||||
|
||||
|
|
@ -22,16 +22,16 @@
|
|||
|
||||
function SaveServerSettings(): void {
|
||||
if (serverSettingsForm.reportValidity()) {
|
||||
$headscaleURLStore = headscaleURL;
|
||||
$headscaleAPIKeyStore = headscaleAPIKey;
|
||||
$URLStore = headscaleURL;
|
||||
$APIKeyStore = headscaleAPIKey;
|
||||
}
|
||||
}
|
||||
|
||||
function ClearServerSettings() {
|
||||
headscaleURL = '';
|
||||
headscaleAPIKey = '';
|
||||
$headscaleURLStore = headscaleURL;
|
||||
$headscaleAPIKeyStore = headscaleAPIKey;
|
||||
$URLStore = headscaleURL;
|
||||
$APIKeyStore = headscaleAPIKey;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
<p class="text-xs text-base-content text-italics mb-8">Generate an API key for your headscale instance and place it here.</p>
|
||||
<!-- disable the SaveServerSettings button if nothing has changed from stored values, or the dependent inputs do not validate -->
|
||||
<div class="tooltip z-10" data-tip="Note: API Key and URL currently save to localStorage (IE: Your Browser) Make sure you are using a trusted computer">
|
||||
<button disabled={headscaleAPIKey === $headscaleAPIKeyStore && headscaleURL === $headscaleURLStore} on:click={() => SaveServerSettings()} class="btn btn-sm btn-accent capitalize" type="button">Save Server Settings</button>
|
||||
<button disabled={headscaleAPIKey === $APIKeyStore && headscaleURL === $URLStore} on:click={() => SaveServerSettings()} class="btn btn-sm btn-accent capitalize" type="button">Save Server Settings</button>
|
||||
</div>
|
||||
<button on:click={() => ClearServerSettings()} class="btn btn-sm btn-primary capitalize" type="button">Clear Server Settings</button>
|
||||
<button on:click={() => TestServerSettings()} class="btn btn-sm btn-secondary capitalize" type="button">Test Server Settings</button>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<script lang="ts">
|
||||
import { headscaleThemeStore } from '$lib/common/stores.js'
|
||||
import { themeStore } from '$lib/common/stores.js'
|
||||
let themes = ['hsui', 'light', 'dark', 'cupcake', 'bumblebee', 'emerald', 'corporate', 'synthwave', 'retro', 'cyberpunk', 'valentine', 'halloween', 'garden', 'forest', 'aqua', 'lofi', 'pastel', 'fantasy', 'wireframe', 'black', 'luxury', 'dracula', 'cmyk', 'autumn', 'business', 'acid', 'lemonade', 'night', 'coffee', 'winter'];
|
||||
</script>
|
||||
|
||||
<h1 class="text-2xl bold text-primary mb-4">Theme Settings</h1>
|
||||
<select bind:value={$headscaleThemeStore} class="select select-bordered w-full select-sm max-w-xs">
|
||||
<select bind:value={$themeStore} class="select select-bordered w-full select-sm max-w-xs">
|
||||
{#each themes as localTheme}
|
||||
<option value={localTheme}>{localTheme}</option>
|
||||
{/each}
|
||||
|
|
|
|||
20
src/lib/users/SearchUsers.svelte
Normal file
20
src/lib/users/SearchUsers.svelte
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<script lang="ts">
|
||||
import { userSearchStore } from '$lib/common/stores';
|
||||
|
||||
export let overrideSort = false;
|
||||
|
||||
userSearchStore.subscribe(value => {
|
||||
if(value != '') {
|
||||
overrideSort = true;
|
||||
} else {
|
||||
overrideSort = false;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- search icon -->
|
||||
<div class="flex">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 ml-3 mr-1 inline flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||
</svg> <input type="text" placeholder="search" bind:value={$userSearchStore} class="input input-bordered input-xs w-full max-w-xs" />
|
||||
</div>
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
<script lang="ts">
|
||||
import { getUsers } from '$lib/common/apiFunctions.svelte';
|
||||
import { headscaleUserSortDirectionStore, headscaleUserSortStore } from '$lib/common/stores.js';
|
||||
import { sortDirectionStore, userSortStore } from '$lib/common/stores.js';
|
||||
|
||||
function sortAction() {
|
||||
if ($headscaleUserSortDirectionStore == 'ascending') {
|
||||
$headscaleUserSortDirectionStore = 'descending';
|
||||
if ($sortDirectionStore == 'ascending') {
|
||||
$sortDirectionStore = 'descending';
|
||||
} else {
|
||||
$headscaleUserSortDirectionStore = 'ascending';
|
||||
$sortDirectionStore = 'ascending';
|
||||
}
|
||||
getUsers();
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
}}
|
||||
class="mx-1"
|
||||
>
|
||||
{#if $headscaleUserSortDirectionStore == 'ascending'}
|
||||
{#if $sortDirectionStore == 'ascending'}
|
||||
<!-- ascending sort icon -->
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
|
|
@ -34,8 +34,8 @@
|
|||
</button>
|
||||
|
||||
<span class="btn-group">
|
||||
<button class:btn-active="{$headscaleUserSortStore == 'id'}" on:click="{() => {$headscaleUserSortStore = 'id'; getUsers()}}" class="btn btn-xs">ID</button>
|
||||
<button class:btn-active="{$headscaleUserSortStore == 'name'}" on:click="{() => {$headscaleUserSortStore = 'name'; getUsers()}}" class="btn btn-xs capitalize">User Name</button>
|
||||
<button class:btn-active="{$headscaleUserSortStore == 'createdAt'}" on:click="{() => {$headscaleUserSortStore = 'createdAt'; getUsers()}}" class="btn btn-xs capitalize">Creation Date</button>
|
||||
<button class:btn-active="{$userSortStore == 'id'}" on:click="{() => {$userSortStore = 'id'; getUsers()}}" class="btn btn-xs">ID</button>
|
||||
<button class:btn-active="{$userSortStore == 'name'}" on:click="{() => {$userSortStore = 'name'; getUsers()}}" class="btn btn-xs capitalize">User Name</button>
|
||||
<button class:btn-active="{$userSortStore == 'createdAt'}" on:click="{() => {$userSortStore = 'createdAt'; getUsers()}}" class="btn btn-xs capitalize">Creation Date</button>
|
||||
</span>
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
import Nav from '$lib/common/nav.svelte';
|
||||
import Alert from '$lib/common/Alert.svelte';
|
||||
import Stores from '$lib/common/Stores.svelte';
|
||||
import { headscaleThemeStore } from '$lib/common/stores.js'
|
||||
import { themeStore } from '$lib/common/stores.js'
|
||||
|
||||
|
||||
// NOTE: the element that is using one of the theme attributes must be in the DOM on mount
|
||||
</script>
|
||||
|
||||
<main data-theme={$headscaleThemeStore} class="flex flex-col">
|
||||
<main data-theme={$themeStore} class="flex flex-col">
|
||||
<!-- initialize localStorage -->
|
||||
<Stores></Stores>
|
||||
<div class="flex h-screen">
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import SortDevices from '$lib/devices/SortDevices.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { apiTestStore, headscaleDeviceStore } from '$lib/common/stores.js';
|
||||
import { apiTestStore, deviceStore } from '$lib/common/stores.js';
|
||||
import { getUsers, getDevices } from '$lib/common/apiFunctions.svelte';
|
||||
import { base } from '$app/paths';
|
||||
|
||||
|
|
@ -54,7 +54,7 @@
|
|||
</table>
|
||||
|
||||
<CreateDevice bind:newDeviceCardVisible />
|
||||
{#each $headscaleDeviceStore as device}
|
||||
{#each $deviceStore as device}
|
||||
<DeviceCard {device} />
|
||||
{/each}
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
<!-- typescript -->
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { getUsers } from '$lib/common/apiFunctions.svelte';
|
||||
import { headscaleUserStore, apiTestStore } from '$lib/common/stores';
|
||||
import { onMount } from 'svelte';
|
||||
import { apiTestStore, userStore } from '$lib/common/stores';
|
||||
import CreateUser from '$lib/users/CreateUser.svelte';
|
||||
import SearchUsers from '$lib/users/SearchUsers.svelte';
|
||||
import SortUsers from '$lib/users/SortUsers.svelte';
|
||||
import UserCard from '$lib/users/UserCard.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
//
|
||||
// Component Variables
|
||||
|
|
@ -30,33 +31,35 @@
|
|||
|
||||
<!-- html -->
|
||||
{#if componentLoaded}
|
||||
<div in:fade class="px-4 pt-4">
|
||||
<h1 class="text-2xl bold text-primary">User View</h1>
|
||||
</div>
|
||||
{#if $apiTestStore === 'succeeded'}
|
||||
<!-- instantiate user based components -->
|
||||
<table>
|
||||
<tr
|
||||
><td
|
||||
><!-- device creation visibility button -->
|
||||
<div class="p-4">
|
||||
{#if newUserCardVisible == false}
|
||||
<button on:click={() => (newUserCardVisible = true)} class="btn btn-primary btn-xs capitalize" type="button">+ New User</button>
|
||||
{:else}
|
||||
<button on:click={() => (newUserCardVisible = false)} class="btn btn-secondary btn-xs capitalize" type="button">- Hide New User</button>
|
||||
{/if}
|
||||
</div></td
|
||||
><td><SortUsers /></td></tr
|
||||
>
|
||||
</table>
|
||||
<CreateUser bind:newUserCardVisible />
|
||||
{#each $headscaleUserStore as user}
|
||||
<UserCard {user} />
|
||||
{/each}
|
||||
{/if}
|
||||
{#if $apiTestStore === 'failed'}
|
||||
<div in:fade class="max-w-lg mx-auto p-4 border-4 text-sm text-base-content shadow-lg text-center">
|
||||
<p>API test did not succeed.<br />Headscale might be down or API settings may need to be set<br />change server settings in the <a href="{base}/settings.html" class="link link-primary">settings</a> page</p>
|
||||
<div in:fade>
|
||||
<div class="px-4 pt-4">
|
||||
<h1 class="text-2xl bold text-primary">User View</h1>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $apiTestStore === 'succeeded'}
|
||||
<!-- instantiate user based components -->
|
||||
<table>
|
||||
<tr
|
||||
><td
|
||||
><!-- device creation visibility button -->
|
||||
<div class="p-4">
|
||||
{#if newUserCardVisible == false}
|
||||
<button on:click={() => (newUserCardVisible = true)} class="btn btn-primary btn-xs capitalize" type="button">+ New User</button>
|
||||
{:else}
|
||||
<button on:click={() => (newUserCardVisible = false)} class="btn btn-secondary btn-xs capitalize" type="button">- Hide New User</button>
|
||||
{/if}
|
||||
</div></td
|
||||
><td><SortUsers /></td><td><SearchUsers /></td></tr
|
||||
>
|
||||
</table>
|
||||
<CreateUser bind:newUserCardVisible />
|
||||
{#each $userStore as user}
|
||||
<UserCard {user} />
|
||||
{/each}
|
||||
{/if}
|
||||
{#if $apiTestStore === 'failed'}
|
||||
<div in:fade class="max-w-lg mx-auto p-4 border-4 text-sm text-base-content shadow-lg text-center">
|
||||
<p>API test did not succeed.<br />Headscale might be down or API settings may need to be set<br />change server settings in the <a href="{base}/settings.html" class="link link-primary">settings</a> page</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue