mirror of
https://github.com/gurucomputing/headscale-ui.git
synced 2026-01-23 02:34:43 +00:00
feat: redesign tweaks (#150)
This commit is contained in:
parent
8e54c94a8d
commit
ae481f54cc
7 changed files with 105 additions and 82 deletions
|
|
@ -8,7 +8,7 @@
|
|||
}
|
||||
|
||||
.card-primary {
|
||||
@apply grid grid-cols-1 divide-y p-2 max-w-screen-lg border mx-4 border-base-content rounded-md text-sm text-base-content shadow
|
||||
@apply grid grid-cols-1 divide-y p-2 max-w-screen-lg mx-4 border-base-content rounded-md text-sm text-base-content shadow
|
||||
}
|
||||
|
||||
.card-pending {
|
||||
|
|
|
|||
|
|
@ -1,71 +1,72 @@
|
|||
export class Device {
|
||||
public id: string = '';
|
||||
public name: string = '';
|
||||
public givenName: string = '';
|
||||
public lastSeen: string = '';
|
||||
public ipAddresses: string[] = []
|
||||
public forcedTags: string[] = []
|
||||
public validTags: string[] = []
|
||||
public invalidTags: string[] = []
|
||||
public user: { name: string } = { name: '' }
|
||||
public id: string = '';
|
||||
public name: string = '';
|
||||
public givenName: string = '';
|
||||
public lastSeen: string = '';
|
||||
public ipAddresses: string[] = [];
|
||||
public forcedTags: string[] = [];
|
||||
public validTags: string[] = [];
|
||||
public invalidTags: string[] = [];
|
||||
public user: { name: string } = { name: '' };
|
||||
public online?: boolean;
|
||||
|
||||
public constructor(init?: Partial<Device>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
public constructor(init?: Partial<Device>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
export class ACL {
|
||||
public groups: {[key: string]: [string]} = {}
|
||||
public groups: { [key: string]: [string] } = {};
|
||||
|
||||
public constructor(init?: Partial<Route>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
public constructor(init?: Partial<Route>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
export class Route {
|
||||
// current (hs 18+) method of handling a route
|
||||
advertised: boolean = true;
|
||||
prefix: string = "";
|
||||
enabled: boolean = false;
|
||||
id: number = 0;
|
||||
// current (hs 18+) method of handling a route
|
||||
advertised: boolean = true;
|
||||
prefix: string = '';
|
||||
enabled: boolean = false;
|
||||
id: number = 0;
|
||||
|
||||
public constructor(init?: Partial<Route>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
public constructor(init?: Partial<Route>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
export class APIKey {
|
||||
id: string = '';
|
||||
prefix: string = '';
|
||||
expiration: string = '';
|
||||
createdAt: string = '';
|
||||
lastSeen: string = '';
|
||||
id: string = '';
|
||||
prefix: string = '';
|
||||
expiration: string = '';
|
||||
createdAt: string = '';
|
||||
lastSeen: string = '';
|
||||
|
||||
public constructor(init?: Partial<Route>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
public constructor(init?: Partial<Route>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
export class PreAuthKey {
|
||||
public user: string = '';
|
||||
public id: string = '';
|
||||
public key: string = '';
|
||||
public createdAt: string = '';
|
||||
public expiration: string = '';
|
||||
public reusable: boolean = false;
|
||||
public ephemeral: boolean = false;
|
||||
public used: boolean = false;
|
||||
public user: string = '';
|
||||
public id: string = '';
|
||||
public key: string = '';
|
||||
public createdAt: string = '';
|
||||
public expiration: string = '';
|
||||
public reusable: boolean = false;
|
||||
public ephemeral: boolean = false;
|
||||
public used: boolean = false;
|
||||
|
||||
public constructor(init?: Partial<PreAuthKey>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
public constructor(init?: Partial<PreAuthKey>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
||||
export class User {
|
||||
public id: string = '';
|
||||
public name: string = '';
|
||||
public createdAt: string = '';
|
||||
public constructor(init?: Partial<User>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
public id: string = '';
|
||||
public name: string = '';
|
||||
public createdAt: string = '';
|
||||
public constructor(init?: Partial<User>) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,21 @@
|
|||
} else if (timeDifference < 86400) {
|
||||
return 'bg-warning';
|
||||
}
|
||||
|
||||
return 'bg-error';
|
||||
}
|
||||
|
||||
// return button colour based on online status
|
||||
function onlineBackground(online: boolean) {
|
||||
return online ? 'bg-success' : 'bg-error';
|
||||
}
|
||||
|
||||
function getBadgeColour(date: Date, online?: boolean) {
|
||||
if (online !== undefined) {
|
||||
return onlineBackground(online);
|
||||
}
|
||||
|
||||
return timeDifference(date);
|
||||
}
|
||||
|
||||
// returns time last seen in human readable format
|
||||
|
|
@ -57,11 +72,11 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="card-primary">
|
||||
<div on:keypress on:click={() => (cardExpanded = !cardExpanded)} class="flex">
|
||||
<div class="card-primary bg-base-200">
|
||||
<div on:keypress on:click={() => (cardExpanded = !cardExpanded)} class="flex items-center">
|
||||
<span class="min-w-64 w-1/2 font-bold">
|
||||
{#if cardEditing == false}
|
||||
<span class="badge badge-xs tooltip {timeDifference(new Date(device.lastSeen))}" data-tip={timeSince(new Date(device.lastSeen))} /> {device.id}: {device.givenName}
|
||||
<span class="badge badge-xs tooltip {getBadgeColour(new Date(device.lastSeen), device.online)}" data-tip={timeSince(new Date(device.lastSeen))} /> {device.id}: {device.givenName}
|
||||
{/if}
|
||||
<RenameDevice bind:cardEditing {device} />
|
||||
</span>
|
||||
|
|
@ -85,7 +100,7 @@
|
|||
</div>
|
||||
{#if cardExpanded}
|
||||
<!-- we put a conditional on the outro transition so page changes do not trigger the animation -->
|
||||
<div in:slide|global out:slide|global={{ duration: cardExpanded ? 0 : 500 }} class="pt-2 pl-2">
|
||||
<div in:slide|global out:slide|global={{ duration: cardExpanded ? 0 : 500 }} class="mt-2 pt-2 pl-2">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-compact w-full">
|
||||
<tbody>
|
||||
|
|
|
|||
|
|
@ -20,21 +20,23 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<span><NewDeviceTag {device}/></span>
|
||||
<div class="flex gap-1">
|
||||
<span><NewDeviceTag {device}/></span>
|
||||
|
||||
{#each device.forcedTags as tag}
|
||||
<span class="mb-1 mr-1 btn btn-xs btn-primary normal-case">{tag.replace("tag:","")}
|
||||
<!-- Cancel symbol -->
|
||||
<button on:click|stopPropagation={() => {updateTagsAction(tag)}}
|
||||
class="ml-1"
|
||||
><svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 inline flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg></button
|
||||
>
|
||||
</span>
|
||||
{/each}
|
||||
{#each device.forcedTags as tag}
|
||||
<span class="btn btn-xs btn-primary normal-case">{tag.replace("tag:","")}
|
||||
<!-- Cancel symbol -->
|
||||
<button on:click|stopPropagation={() => {updateTagsAction(tag)}}
|
||||
class="ml-1"
|
||||
><svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 inline flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg></button
|
||||
>
|
||||
</span>
|
||||
{/each}
|
||||
|
||||
{#each device.validTags as tag}
|
||||
<span class="mb-1 mr-1 btn btn-xs btn-secondary normal-case">{tag.replace("tag:","")}</span>
|
||||
{/each}
|
||||
{#each device.validTags as tag}
|
||||
<span class="mb-1 mr-1 btn btn-xs btn-secondary normal-case">{tag.replace("tag:","")}</span>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
let cardExpanded = false;
|
||||
</script>
|
||||
|
||||
<div in:fade|global class="card-primary">
|
||||
<div in:fade|global class="card-primary bg-base-200">
|
||||
<div on:keypress on:click={() => (cardExpanded = !cardExpanded)} class="flex justify-between">
|
||||
<div>
|
||||
<EditUser {user} />
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
</div>
|
||||
{#if cardExpanded}
|
||||
<!-- we put a conditional on the outro transition so page changes do not trigger the animation -->
|
||||
<div in:slide|global out:slide|global={{ duration: cardExpanded ? 0 : 500 }} class="pt-2 pl-2">
|
||||
<div in:slide|global out:slide|global={{ duration: cardExpanded ? 0 : 500 }} class="mt-2 pt-2 pl-2">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-compact w-full">
|
||||
<tbody>
|
||||
|
|
|
|||
|
|
@ -64,11 +64,13 @@
|
|||
|
||||
<CreateDevice bind:newDeviceCardVisible bind:newDeviceKey />
|
||||
|
||||
{#each $deviceStore as device}
|
||||
{#if $deviceFilterStore.includes(device)}
|
||||
<DeviceCard {device} />
|
||||
{/if}
|
||||
{/each}
|
||||
<div class="flex flex-col gap-2">
|
||||
{#each $deviceStore as device}
|
||||
{#if $deviceFilterStore.includes(device)}
|
||||
<DeviceCard {device} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if $apiTestStore === 'failed'}
|
||||
<div in:fade|global class="max-w-lg mx-auto p-4 border-4 text-sm text-base-content shadow-lg text-center">
|
||||
|
|
|
|||
|
|
@ -52,11 +52,14 @@
|
|||
>
|
||||
</table>
|
||||
<CreateUser bind:newUserCardVisible />
|
||||
{#each $userStore as user}
|
||||
{#if $userFilterStore.includes(user)}
|
||||
<UserCard {user} />
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
{#each $userStore as user}
|
||||
{#if $userFilterStore.includes(user)}
|
||||
<UserCard {user} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if $apiTestStore === 'failed'}
|
||||
<div in:fade|global class="max-w-lg mx-auto p-4 border-4 text-sm text-base-content shadow-lg text-center">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue