Skip to content

Frontend/view tenant users #34

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions frontend/src/lib/components/TenantUsersList/TenantUsersList.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script lang="ts">
import { type TableColumn } from 'stwui/types';
import { Card, Table } from 'stwui';
export let tenantUsers;

let orderBy: string;
let order: 'asc' | 'desc';

const columns: TableColumn[] = [
{
column: 'user_name',
label: 'User Name',
placement: 'left',
class: 'w-[50%]'
},
{
column: 'email',
label: 'Email',
placement: 'left',
class: 'w-[50%]'
}
];
</script>

<Card bordered={false} class="w-[90%]">
<Card.Content slot="content" class="p-0 sm:p-0">
<Table class="h-full overflow-hidden rounded-md" {columns}>
<Table.Header slot="header" {order} {orderBy} />
<Table.Body slot="body">
{#each tenantUsers as user}
<Table.Body.Row id={user.id}>
<Table.Body.Row.Cell column={0}
>{user.firstName + ' ' + user.lastName}</Table.Body.Row.Cell
>
<Table.Body.Row.Cell column={1}>{user.email}</Table.Body.Row.Cell>
</Table.Body.Row>
{/each}
</Table.Body>
</Table>
</Card.Content>
</Card>
21 changes: 10 additions & 11 deletions frontend/src/lib/components/Tenants/Tenants.svelte
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
<script lang="ts">
import { Button, List } from 'stwui';
import { goto } from '$app/navigation';

export let tenants;
</script>

{#if tenants}
<div class="h-full">
<List divided={false}>
{#each tenants as tenant}
<List.Item class="m-2 rounded-md py-0">
<Button class="flex w-full justify-start" on:click={() => alert('TODO')}>
{tenant.name}
</Button>
</List.Item>
{/each}
</List>
</div>
<List divided={false}>
{#each tenants as tenant}
<List.Item class="m-2 rounded-md py-0">
<Button class="flex w-full justify-start" on:click={() => goto(`/tenant/${tenant.id}`)}>
{tenant.name}
</Button>
</List.Item>
{/each}
</List>
{/if}
14 changes: 14 additions & 0 deletions frontend/src/lib/context/TenantUsersContext/TenantUsers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { z } from 'zod';

// const StatusSchema = z.union([z.literal('ENABLED'), z.literal('DISABLED')]);

export const TenantUserSchema = z.object({
id: z.number(),
email: z.string(),
// password: z.string(),
// status: StatusSchema,
firstName: z.string(),
lastName: z.string()
});

export type TenantUser = z.infer<typeof TenantUserSchema>;
3 changes: 3 additions & 0 deletions frontend/src/lib/context/UnityAuthContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,12 @@ export function createUnityAuthContext(props: UnityAuthContextProviderProps & Un
...props.unityAuthServiceProps
});

unityAuthService.setAuthInfo(unityAuthService.getLoginData());
const user: Writable<UserInfo> = writable(unityAuthService.getLoginData());
unityAuthService.subscribe('login', (args) => user.set(args));
unityAuthService.subscribe('logout', () => user.set(undefined));
unityAuthService.subscribe('login', (args) => unityAuthService.setAuthInfo(args));
unityAuthService.subscribe('logout', () => unityAuthService.setAuthInfo(undefined));

function alertError(unknown: unknown) {
console.error(unknown);
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/lib/services/TenantsResolver/shared.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { z } from 'zod';

export const TenantsSchema = z.object({
export const TenantSchema = z.object({
id: z.number(),
name: z.string()
});

export type Tenants = z.infer<typeof TenantsSchema>;
export type Tenant = z.infer<typeof TenantSchema>;

export const TenantsSuccessResponseSchema = z.array(TenantsSchema);
export const TenantsSuccessResponseSchema = z.array(TenantSchema);

export type TenantsSuccessResponse = z.infer<typeof TenantsSuccessResponseSchema>;

Expand Down
27 changes: 26 additions & 1 deletion frontend/src/lib/services/UnityAuth/UnityAuth.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import type { AxiosInstance } from 'axios';
import { BaseObservable } from '../EventBus/EventBus';
import type { CompleteLoginResponse, UnityAuthServiceProps } from './shared';
import type {
CompleteLoginResponse,
GetTenantUsersResponse,
UnityAuthLoginResponse,
UnityAuthServiceProps
} from './shared';
import {
CompleteLoginResponseSchema,
GetTenantUsersResponseSchema,
UnityAuthLoginResponseSchema,
UnityAuthServicePropsSchema
} from './shared';
Expand All @@ -20,12 +26,15 @@ export type UnityAuthService = BaseObservable<UnityAuthEventMap> & {
login(email: string, password: string): Promise<CompleteLoginResponse>;
getLoginData(): CompleteLoginResponse | undefined;
logout(): void;
getTenantUsers(id: number): Promise<GetTenantUsersResponse>;
setAuthInfo(authInfo: UnityAuthLoginResponse | undefined): void;
};

export class UnityAuthServiceImpl
extends BaseObservable<UnityAuthEventMap>
implements UnityAuthService
{
private authTokenInterceptorId: number = -1;
private loginDataKey: string = 'loginData';
private axiosInstance: AxiosInstance;
private tenantsResolver: TenantsResolver;
Expand Down Expand Up @@ -91,6 +100,22 @@ export class UnityAuthServiceImpl
return CompleteLoginResponseSchema.parse(JSON.parse(loginInfo));
}
}

async getTenantUsers(id: number): Promise<GetTenantUsersResponse> {
const res = await this.axiosInstance.get(`/api/tenants/${id}/users`);
return GetTenantUsersResponseSchema.parse(res.data);
}

setAuthInfo(authInfo: UnityAuthLoginResponse | undefined): void {
if (authInfo) {
this.authTokenInterceptorId = this.axiosInstance.interceptors.request.use(function (config) {
config.headers['Authorization'] = `Bearer ${authInfo.access_token}`;
return config;
});
} else {
this.axiosInstance.interceptors.request.eject(this.authTokenInterceptorId);
}
}
}

export function unityAuthServiceFactory(props: UnityAuthServiceProps): UnityAuthService {
Expand Down
12 changes: 10 additions & 2 deletions frontend/src/lib/services/UnityAuth/shared.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { z } from 'zod';
import type { TenantsResolver } from '../TenantsResolver/TenantsResolver';
import { TenantsSchema } from '../TenantsResolver/shared';
import { TenantSchema } from '../TenantsResolver/shared';
import { TenantUserSchema } from '$lib/context/TenantUsersContext/TenantUsers';

export const UnityAuthServicePropsSchema = z.object({
baseURL: z.string()
Expand All @@ -15,11 +16,18 @@ export const UnityAuthLoginResponseSchema = z.object({
token_type: z.string(),
expires_in: z.number(),
username: z.string(),
tenants: z.array(TenantsSchema).optional()
tenants: z.array(TenantSchema).optional()
});

export type UnityAuthLoginResponse = z.infer<typeof UnityAuthLoginResponseSchema>;

export const CompleteLoginResponseSchema = UnityAuthLoginResponseSchema;

export type CompleteLoginResponse = z.infer<typeof CompleteLoginResponseSchema>;

export const GetTenantUsersResponseSchema = z.array(TenantUserSchema);
export type GetTenantUsersResponse = z.infer<typeof GetTenantUsersResponseSchema>;

export type TenantUsersResponse = {
tenantUsers: GetTenantUsersResponse;
};
4 changes: 4 additions & 0 deletions frontend/src/lib/utils/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type Maybe<T> = T | undefined | null;
export type HasId<T> = {
id: T;
};
18 changes: 16 additions & 2 deletions frontend/src/routes/tenant/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
<script lang="ts">
import SideBarMainContentLayout from '$lib/components/SideBarMainContentLayout/SideBarMainContentLayout.svelte';
import { useUnityAuthContext } from '$lib/context/UnityAuthContext';
import Tenants from '$lib/components/Tenants/Tenants.svelte';

const unityAuthContext = useUnityAuthContext();
const user = unityAuthContext.user;
</script>

<SideBarMainContentLayout>
<slot slot="side-bar" />
<div slot="main-content" class="relative flex h-full" id="main-content"></div>
<div slot="side-bar" id="side-bar">
{#if $user && $user.tenants}
<Tenants tenants={$user.tenants} />
{/if}
</div>

<slot slot="main-content" id="main-content" class="relative flex h-full" />
</SideBarMainContentLayout>

<style>
#side-bar {
background-color: hsl(var(--secondary));
}

#main-content {
background-color: hsl(var(--background));
}
Expand Down
16 changes: 6 additions & 10 deletions frontend/src/routes/tenant/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
<script lang="ts">
import { useUnityAuthContext } from '$lib/context/UnityAuthContext';
import Tenants from '$lib/components/Tenants/Tenants.svelte';
<section class="h-full"></section>

const unityAuthContext = useUnityAuthContext();
const user = unityAuthContext.user;
</script>

{#if $user && $user.tenants}
<Tenants tenants={$user.tenants} />
{/if}
<style>
section {
background-color: hsl(var(--background));
}
</style>
33 changes: 33 additions & 0 deletions frontend/src/routes/tenant/[tenant_id]/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<script lang="ts">
import { onMount } from 'svelte';
import { useUnityAuthService } from '$lib/context/UnityAuthContext';
import { page } from '$app/stores';
import { type TenantUser } from '$lib/context/TenantUsersContext/TenantUsers';
import TenantUsersList from '$lib/components/TenantUsersList/TenantUsersList.svelte';

const unityAuthService = useUnityAuthService();

let tenantUsers: TenantUser[];

async function getTenantUserList() {
tenantUsers = await unityAuthService.getTenantUsers(Number($page.params.tenant_id));
}

onMount(async () => {
await getTenantUserList();
});
</script>

<section class="h-full w-full">
{#if tenantUsers}
<div class="flex h-full items-center justify-center">
<TenantUsersList {tenantUsers} />
</div>
{/if}
</section>

<style>
section {
background-color: hsl(var(--background));
}
</style>