ui: inprogress version of iam

This commit is contained in:
Andras Bacsai 2022-09-29 15:46:52 +02:00
parent 3f76cadea9
commit 51b3293e69
7 changed files with 246 additions and 26 deletions

View File

@ -353,8 +353,10 @@ export async function getCurrentUser(
// No new token -> not switching teams
}
}
const pendingInvitations = await prisma.teamInvitation.findMany({ where: { uid: request.user.userId } })
return {
settings: await prisma.setting.findFirst(),
pendingInvitations,
supportedServiceTypesAndVersions,
token,
...request.user,

View File

@ -20,6 +20,7 @@ interface AppSession {
gitlab: string | null,
},
supportedServiceTypesAndVersions: Array<any>
pendingInvitations: Array<any>
}
interface AddToast {
type?: "info" | "success" | "error",
@ -47,7 +48,8 @@ export const appSession: Writable<AppSession> = writable({
github: null,
gitlab: null
},
supportedServiceTypesAndVersions: []
supportedServiceTypesAndVersions: [],
pendingInvitations: []
});
export const disabledButton: Writable<boolean> = writable(false);
export const isDeploymentEnabled: Writable<boolean> = writable(false);

View File

@ -66,6 +66,8 @@
<script lang="ts">
export let baseSettings: any;
export let supportedServiceTypesAndVersions: any;
export let pendingInvitations: any;
$appSession.isRegistrationEnabled = baseSettings.isRegistrationEnabled;
$appSession.ipv4 = baseSettings.ipv4;
$appSession.ipv6 = baseSettings.ipv6;
@ -74,10 +76,13 @@
$appSession.whiteLabeledDetails.icon = baseSettings.whiteLabeledIcon;
$appSession.supportedServiceTypesAndVersions = supportedServiceTypesAndVersions;
$appSession.pendingInvitations = pendingInvitations;
export let userId: string;
export let teamId: string;
export let permission: string;
export let isAdmin: boolean;
import '../tailwind.css';
import Cookies from 'js-cookie';
import { fade } from 'svelte/transition';
@ -202,11 +207,16 @@
<a
id="iam"
sveltekit:prefetch
href="/iam"
class="icons hover:text-iam"
href={$appSession.pendingInvitations.length > 0 ? '/iam/pending' : '/iam'}
class="icons hover:text-iam indicator"
class:text-iam={$page.url.pathname.startsWith('/iam')}
class:bg-coolgray-500={$page.url.pathname.startsWith('/iam')}
><svg
>
{#if $appSession.pendingInvitations.length > 0}
<span class="indicator-item badge badge-error font-mono"
>{pendingInvitations.length}</span
>
{/if}<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
class="h-9 w-9"
@ -387,7 +397,9 @@
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
<path d="M21 21v-2a4 4 0 0 0 -3 -3.85" />
</svg>
IAM
IAM {#if $appSession.pendingInvitations.length > 0}
<span class="indicator-item badge badge-error font-mono">{pendingInvitations}</span>
{/if}
</a>
</li>
<li>

View File

@ -0,0 +1,70 @@
<script lang="ts">
import { appSession } from '$lib/store';
import { page } from '$app/stores';
</script>
<ul class="menu border bg-coolgray-100 border-coolgray-200 rounded p-2 space-y-2 sticky top-4">
{#if $appSession.pendingInvitations.length > 0}
<li class="rounded" class:bg-coollabs={$page.url.pathname === `/iam/pending`}>
<a href={`/iam/pending`} class="no-underline w-full"
><svg
xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M7 10h3v-3l-3.5 -3.5a6 6 0 0 1 8 8l6 6a2 2 0 0 1 -3 3l-6 -6a6 6 0 0 1 -8 -8l3.5 3.5"
/>
</svg>Pending Invitations</a
>
</li>
{/if}
<li class="menu-title">
<span>Configuration</span>
</li>
<li class="rounded" class:bg-coollabs={$page.url.pathname === `/iam`}>
<a href={`/iam`} class="no-underline w-full"
><svg
xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M7 10h3v-3l-3.5 -3.5a6 6 0 0 1 8 8l6 6a2 2 0 0 1 -3 3l-6 -6a6 6 0 0 1 -8 -8l3.5 3.5"
/>
</svg>Accounts</a
>
</li>
<li class="rounded" class:bg-coollabs={$page.url.pathname === `/iam/teams`}>
<a href={`/iam/teams`} class="no-under line w-full"
><svg
xmlns="http://www.w3.org/2000/svg"
class="w-6 h-6"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path
d="M7 10h3v-3l-3.5 -3.5a6 6 0 0 1 8 8l6 6a2 2 0 0 1 -3 3l-6 -6a6 6 0 0 1 -8 -8l3.5 3.5"
/>
</svg>Teams</a
>
</li>
</ul>

View File

@ -0,0 +1,28 @@
<script lang="ts">
import { page } from '$app/stores';
import Menu from './_Menu.svelte';
</script>
<div class="mx-auto max-w-screen-2xl px-6 grid grid-cols-1 lg:grid-cols-2">
<nav class="header flex flex-row order-2 lg:order-1 px-0 lg:px-4 items-start">
<div class="title lg:pb-10">
{#if $page.url.pathname === `/iam`}
Identity & Access Management
{:else}
<div class="flex justify-center items-center space-x-2">
<div>Configurations</div>
</div>
{/if}
</div>
</nav>
</div>
<div class="mx-auto max-w-screen-2xl px-0 lg:px-2 grid grid-cols-1 lg:grid-cols-4">
<nav class="header flex flex-col lg:pt-0 lg:col-span-1">
<Menu />
</nav>
<div class="pt-0 lg:col-span-3 pb-24 px-6">
<slot />
</div>
</div>

View File

@ -29,9 +29,11 @@
import { addToast, appSession } from '$lib/store';
import { goto } from '$app/navigation';
import Cookies from 'js-cookie';
if (accounts.length === 0) {
accounts.push(account);
}
let search = '';
let searchResults: any = [];
// if (accounts.length === 0) {
// accounts.push(account);
// }
async function resetPassword(id: any) {
const sure = window.confirm('Are you sure you want to reset the password?');
@ -65,22 +67,6 @@
return errorNotification(error);
}
}
async function acceptInvitation(id: any, teamId: any) {
try {
await post(`/iam/team/${teamId}/invitation/accept`, { id });
return window.location.reload();
} catch (error) {
return errorNotification(error);
}
}
async function revokeInvitation(id: any, teamId: any) {
try {
await post(`/iam/team/${teamId}/invitation/revoke`, { id });
return window.location.reload();
} catch (error) {
return errorNotification(error);
}
}
async function switchTeam(selectedTeamId: any) {
try {
@ -104,9 +90,78 @@
const { id } = await post('/iam/new', {});
return await goto(`/iam/team/${id}`, { replaceState: true });
}
function searchAccount() {
searchResults = accounts.filter((account: { email: string | string[] }) => {
return account.email.includes(search);
});
}
</script>
<nav class="header">
<div class="w-full">
<div class="mx-auto w-full">
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2 items-center">
<div class="title font-bold pb-3">Accounts</div>
</div>
</div>
</div>
<div class="w-full grid gap-2">
<input
class="input w-full mb-4"
bind:value={search}
on:input={searchAccount}
placeholder="Search for account..."
/>
<div class="flex flex-col pb-2 space-y-4 lg:space-y-2">
{#if searchResults.length > 0}
{#each searchResults as account}
<div class="flex flex-col lg:flex-row lg:space-y-0 space-y-2">
<input
disabled
class="input w-full lg:w-64 text-white"
readonly
placeholder="email"
value={account.email}
/>
<div class="flex flex-row items-center justify-center space-x-2 w-full lg:w-96">
<div class="flex items-center justify-center">
<button class="btn btn-sm btn-primary">Reset Password</button>
</div>
<div class="flex justify-center">
<button class="btn btn-sm btn-error">Delete Account</button>
</div>
</div>
</div>
{/each}
{:else if searchResults.length === 0 && search !== ''}
<div>Nothing found.</div>
{:else}
{#each accounts as account}
<div class="flex flex-col lg:flex-row lg:space-y-0 space-y-2 lg:space-x-4">
<input
disabled
class="input w-full text-white"
readonly
placeholder="email"
value={account.email}
/>
<div class="flex flex-row items-center justify-center space-x-2 w-full lg:w-96">
<div class="flex items-center justify-center">
<button class="btn btn-sm btn-primary">Reset Password</button>
</div>
<div class="flex justify-center">
<button class="btn btn-sm btn-error">Delete Account</button>
</div>
</div>
</div>
{/each}
{/if}
</div>
</div>
<!-- <nav class="header">
<h1 class="mr-4 text-2xl tracking-tight font-bold">Identity and Access Management</h1>
<button on:click={newTeam} class="btn btn-square btn-sm bg-iam">
<svg
@ -240,4 +295,4 @@
</div>
{/if}
</div>
</div>
</div> -->

View File

@ -0,0 +1,51 @@
<script lang="ts">
import { post } from '$lib/api';
import { errorNotification } from '$lib/common';
import { appSession } from '$lib/store';
async function acceptInvitation(id: any, teamId: any) {
try {
await post(`/iam/team/${teamId}/invitation/accept`, { id });
return window.location.reload();
} catch (error) {
return errorNotification(error);
}
}
async function revokeInvitation(id: any, teamId: any) {
try {
await post(`/iam/team/${teamId}/invitation/revoke`, { id });
return window.location.reload();
} catch (error) {
return errorNotification(error);
}
}
</script>
<div class="w-full">
<div class="mx-auto w-full">
<div class="flex flex-row border-b border-coolgray-500 mb-6 space-x-2 items-center">
<div class="title font-bold pb-3">Pending Invitations</div>
</div>
</div>
</div>
<div class="w-full grid gap-2">
<div class="flex flex-col pb-2 space-y-4 lg:space-y-2">
{#each $appSession.pendingInvitations as invitation}
<div class="flex flex-col justify-center items-center">
<div class="text-xl pb-4 text-center">
Invited to <span class="font-bold text-pink-500">{invitation.teamName}</span> with
<span class="font-bold text-red-500">{invitation.permission}</span> permission.
</div>
<div class=" flex space-x-2">
<button
class="btn btn-primary"
on:click={() => acceptInvitation(invitation.id, invitation.teamId)}>Accept</button
>
<button class="btn" on:click={() => revokeInvitation(invitation.id, invitation.teamId)}
>Ignore</button
>
</div>
</div>
{/each}
</div>
</div>