mirror of
https://github.com/ershisan99/coolify.git
synced 2026-01-30 20:52:11 +00:00
63
apps/ui/src/routes/iam/team/[id]/__layout.svelte
Normal file
63
apps/ui/src/routes/iam/team/[id]/__layout.svelte
Normal file
@@ -0,0 +1,63 @@
|
||||
<script context="module" lang="ts">
|
||||
import { del, get } from '$lib/api';
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
export const load: Load = async ({ params, url }) => {
|
||||
try {
|
||||
const response = await get(`/iam/team/${params.id}`);
|
||||
if (!response.permissions || Object.entries(response.permissions).length === 0) {
|
||||
return {
|
||||
status: 302,
|
||||
redirect: '/iam'
|
||||
};
|
||||
}
|
||||
return {
|
||||
props: {
|
||||
...response
|
||||
},
|
||||
stuff: {
|
||||
...response
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
return handlerNotFoundLoad(error, url);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import { errorNotification, handlerNotFoundLoad } from '$lib/common';
|
||||
import { appSession } from '$lib/store';
|
||||
import { t } from '$lib/translations';
|
||||
import DeleteIcon from '$lib/components/DeleteIcon.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
const { id } = $page.params;
|
||||
async function deleteTeam() {
|
||||
const sure = confirm('Are you sure you want to delete this team?');
|
||||
if (sure) {
|
||||
try {
|
||||
await del(`/iam/team/${id}`, { id });
|
||||
return await goto('/iam', { replaceState: true });
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if id !== 'new'}
|
||||
<nav class="nav-side">
|
||||
<button
|
||||
on:click={deleteTeam}
|
||||
title={$t('source.delete_git_source')}
|
||||
type="submit"
|
||||
disabled={!$appSession.isAdmin}
|
||||
class:hover:text-red-500={$appSession.isAdmin}
|
||||
class="icons tooltip-bottom bg-transparent text-sm"
|
||||
data-tooltip={$appSession.isAdmin
|
||||
? 'Delete Team'
|
||||
: $t('destination.permission_denied_delete_destination')}><DeleteIcon /></button
|
||||
>
|
||||
</nav>
|
||||
{/if}
|
||||
<slot />
|
||||
210
apps/ui/src/routes/iam/team/[id]/index.svelte
Normal file
210
apps/ui/src/routes/iam/team/[id]/index.svelte
Normal file
@@ -0,0 +1,210 @@
|
||||
<script context="module" lang="ts">
|
||||
import type { Load } from '@sveltejs/kit';
|
||||
export const load: Load = async ({ fetch, params, stuff }) => {
|
||||
return {
|
||||
props: { ...stuff }
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export let permissions: any;
|
||||
export let team: any;
|
||||
export let invitations: any[];
|
||||
import { page } from '$app/stores';
|
||||
import Explainer from '$lib/components/Explainer.svelte';
|
||||
import { post } from '$lib/api';
|
||||
import { t } from '$lib/translations';
|
||||
import { errorNotification } from '$lib/common';
|
||||
import { appSession } from '$lib/store';
|
||||
const { id } = $page.params;
|
||||
let invitation: any = {
|
||||
teamName: team.name,
|
||||
email: null,
|
||||
permission: 'read'
|
||||
};
|
||||
function isAdmin(permission: string) {
|
||||
if (permission === 'admin' || permission === 'owner') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async function sendInvitation() {
|
||||
try {
|
||||
await post(`/iam/team/${id}/invitation/invite`, {
|
||||
teamId: team.id,
|
||||
teamName: invitation.teamName,
|
||||
email: invitation.email.toLowerCase(),
|
||||
permission: invitation.permission
|
||||
});
|
||||
return window.location.reload();
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function revokeInvitation(id: string) {
|
||||
try {
|
||||
await post(`/iam/team/${id}/invitation/revoke`, { id });
|
||||
return window.location.reload();
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function removeFromTeam(uid: string) {
|
||||
try {
|
||||
await post(`/iam/team/${id}/user/remove`, { teamId: team.id, uid });
|
||||
return window.location.reload();
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function changePermission(userId: string, permissionId: string, currentPermission: string) {
|
||||
let newPermission = 'read';
|
||||
if (currentPermission === 'read') {
|
||||
newPermission = 'admin';
|
||||
}
|
||||
try {
|
||||
await post(`/iam/team/${id}/permission`, { userId, newPermission, permissionId });
|
||||
return window.location.reload();
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
async function handleSubmit() {
|
||||
try {
|
||||
await post(`/iam/team/${id}`, { ...team });
|
||||
return window.location.reload();
|
||||
} catch (error) {
|
||||
return errorNotification(error);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-1 p-6 px-6 text-2xl font-bold">
|
||||
<div class="tracking-tight">{$t('index.team')}</div>
|
||||
<span class="arrow-right-applications px-1 text-fuchsia-500">></span>
|
||||
<span class="pr-2">{team.name}</span>
|
||||
</div>
|
||||
<div class="mx-auto max-w-4xl px-6">
|
||||
<form on:submit|preventDefault={handleSubmit} class=" py-4">
|
||||
<div class="flex space-x-1 pb-5">
|
||||
<div class="title font-bold">{$t('index.settings')}</div>
|
||||
<button class="bg-fuchsia-600 hover:bg-fuchsia-500" type="submit">{$t('forms.save')}</button>
|
||||
</div>
|
||||
<div class="grid grid-flow-row gap-2 px-10">
|
||||
<div class="mt-2 grid grid-cols-2">
|
||||
<div class="flex-col">
|
||||
<label for="name" class="text-base font-bold text-stone-100">{$t('forms.name')}</label>
|
||||
{#if team.id === '0'}
|
||||
<Explainer customClass="w-full" text={$t('team.root_team_explainer')} />
|
||||
{/if}
|
||||
</div>
|
||||
<input id="name" name="name" placeholder="name" bind:value={team.name} />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="flex space-x-1 py-5 pt-10 font-bold">
|
||||
<div class="title">{$t('team.members')}</div>
|
||||
</div>
|
||||
<div class="px-4 sm:px-6">
|
||||
<table class="w-full border-separate text-left">
|
||||
<thead>
|
||||
<tr class="h-8 border-b border-coolgray-400">
|
||||
<th scope="col">{$t('forms.email')}</th>
|
||||
<th scope="col">{$t('team.permission')}</th>
|
||||
<th scope="col" class="text-center">{$t('forms.action')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{#each permissions as permission}
|
||||
<tr class="text-xs">
|
||||
<td class="py-4"
|
||||
>{permission.user.email}
|
||||
<span class="font-bold"
|
||||
>{permission.user.id === $appSession.userId ? $t('team.you') : ''}</span
|
||||
></td
|
||||
>
|
||||
<td class="py-4">{permission.permission}</td>
|
||||
{#if $appSession.isAdmin && permission.user.id !== $appSession.userId && permission.permission !== 'owner'}
|
||||
<td class="flex flex-col items-center justify-center space-y-2 py-4 text-center">
|
||||
<button
|
||||
class="w-52 bg-red-600 hover:bg-red-500"
|
||||
on:click={() => removeFromTeam(permission.user.id)}>{$t('forms.remove')}</button
|
||||
>
|
||||
<button
|
||||
class="w-52"
|
||||
on:click={() =>
|
||||
changePermission(permission.user.id, permission.id, permission.permission)}
|
||||
>{$t('team.promote_to', {
|
||||
grade: permission.permission === 'admin' ? 'read' : 'admin'
|
||||
})}</button
|
||||
>
|
||||
</td>
|
||||
{:else}
|
||||
<td class="text-center py-4 flex-col space-y-2">
|
||||
{$t('forms.no_actions_available')}
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
{/each}
|
||||
|
||||
{#each invitations as invitation}
|
||||
<tr class="text-xs">
|
||||
<td class="py-4 font-bold text-yellow-500">{invitation.email} </td>
|
||||
<td class="py-4 font-bold text-yellow-500">{invitation.permission}</td>
|
||||
{#if isAdmin(team.permissions[0].permission)}
|
||||
<td class="flex-col space-y-2 py-4 text-center">
|
||||
<button
|
||||
class="w-52 bg-red-600 hover:bg-red-500"
|
||||
on:click={() => revokeInvitation(invitation.id)}
|
||||
>{$t('team.revoke_invitation')}</button
|
||||
>
|
||||
</td>
|
||||
{:else}
|
||||
<td class="text-center py-4 flex-col space-y-2">{$t('team.pending_invitation')}</td>
|
||||
{/if}
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
</div>
|
||||
{#if $appSession.isAdmin}
|
||||
<form on:submit|preventDefault={sendInvitation} class="py-5 pt-10">
|
||||
<div class="flex space-x-1">
|
||||
<div class="flex space-x-1">
|
||||
<div class="title font-bold">{$t('team.invite_new_member')}</div>
|
||||
<button class="bg-fuchsia-600 hover:bg-fuchsia-500" type="submit"
|
||||
>{$t('team.send_invitation')}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<Explainer text={$t('team.invite_only_register_explainer')} />
|
||||
<div class="flex-col space-y-2 px-4 pt-5 sm:px-6">
|
||||
<div class="flex space-x-0">
|
||||
<input
|
||||
bind:value={invitation.email}
|
||||
placeholder={$t('forms.email')}
|
||||
class="mr-2 w-full"
|
||||
required
|
||||
/>
|
||||
<div class="flex-1" />
|
||||
<button
|
||||
on:click={() => (invitation.permission = 'read')}
|
||||
class="rounded-none rounded-l border border-dashed border-transparent"
|
||||
type="button"
|
||||
class:border-coolgray-300={invitation.permission !== 'read'}
|
||||
class:bg-fuchsia-500={invitation.permission === 'read'}>{$t('team.read')}</button
|
||||
>
|
||||
<button
|
||||
on:click={() => (invitation.permission = 'admin')}
|
||||
class="rounded-none rounded-r border border-dashed border-transparent"
|
||||
type="button"
|
||||
class:border-coolgray-300={invitation.permission !== 'admin'}
|
||||
class:bg-red-500={invitation.permission === 'admin'}>{$t('team.admin')}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
</div>
|
||||
Reference in New Issue
Block a user