ui: inprogress version of iam
This commit is contained in:
parent
3f76cadea9
commit
51b3293e69
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
70
apps/ui/src/routes/iam/_Menu.svelte
Normal file
70
apps/ui/src/routes/iam/_Menu.svelte
Normal 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>
|
28
apps/ui/src/routes/iam/__layout.svelte
Normal file
28
apps/ui/src/routes/iam/__layout.svelte
Normal 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>
|
@ -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> -->
|
||||
|
51
apps/ui/src/routes/iam/pending.svelte
Normal file
51
apps/ui/src/routes/iam/pending.svelte
Normal 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>
|
Loading…
x
Reference in New Issue
Block a user