Skip to content
This repository was archived by the owner on Apr 19, 2023. It is now read-only.

Commit dd66f28

Browse files
✨ Add support for multiple memberships
1 parent 0f13aa5 commit dd66f28

File tree

4 files changed

+79
-8
lines changed

4 files changed

+79
-8
lines changed

src/api.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ export const api = async <T>({
9191
if (index === i) {
9292
return {
9393
details: user.details,
94+
memberships: user.memberships,
9495
auth: { accessToken, refreshToken },
9596
};
9697
}
@@ -159,8 +160,13 @@ export const loginWithTokenResponse = async (auth: User["auth"]) => {
159160
url: `/users/${userId}`,
160161
token: auth.accessToken,
161162
});
163+
const memberships = await api<any>({
164+
method: "GET",
165+
url: `/users/${userId}/memberships`,
166+
token: auth.accessToken,
167+
});
162168
users.update((val) =>
163-
[...val, { details, auth }].filter(
169+
[...val, { details, memberships, auth }].filter(
164170
(v, i, a) => a.map((i) => i.details.id).indexOf(v.details.id) === i
165171
)
166172
);

src/components/Table/GroupRecord.svelte

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script lang="ts">
2+
import TimeAgo from "../TimeAgo.svelte";
3+
24
export let item: any;
35
export let itemType: "membership" | "group" = "group";
46
export let data: any = {};
@@ -18,6 +20,9 @@
1820
</div>
1921
<div class="ml-5">
2022
<div class="text-sm font-medium text-gray-900">{data.name}</div>
21-
<div class="text-sm text-gray-500">#{data.id}</div>
23+
<div class="text-sm text-gray-500">
24+
Created
25+
<TimeAgo date={data.createdAt} />
26+
</div>
2227
</div>
2328
</a>

src/routes/users/[slug]/groups.svelte

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,44 @@
11
<script lang="ts">
22
import { stores } from "@sapper/app";
33
import type { Membership } from "@koj/types";
4-
import { can } from "../../../api";
4+
import { api, can } from "../../../api";
55
import DataTable from "../../../components/DataTable.svelte";
66
import DeleteModal from "../../../components/DeleteModal.svelte";
77
import GroupRecord from "../../../components/Table/GroupRecord.svelte";
8+
import Form from "../../../components/Form.svelte";
9+
import { users } from "../../../stores";
10+
import Tag from "../../../components/Tag.svelte";
811
912
const { page } = stores();
1013
const { slug } = $page.params;
1114
const primaryKeyType = "id";
1215
let data: Membership[] = [];
1316
let deleteActiveKey: number | undefined = undefined;
17+
18+
const updateData = (item: any) => {
19+
if (data.find((i) => i[primaryKeyType] === item[primaryKeyType]))
20+
data = data.map((i) => {
21+
if (i[primaryKeyType] === item[primaryKeyType]) return item;
22+
return i;
23+
});
24+
else data = [...data, item];
25+
};
26+
27+
const add = async (body: { name: string; scopes: string[] }) => {
28+
const result = await api<any>({
29+
method: "POST",
30+
url: `/users/${slug}/memberships`,
31+
body,
32+
});
33+
users.update((items) => {
34+
items = items.map((i) => {
35+
if (i.details.id === slug) return { ...i, memberships: [...i.memberships, result] };
36+
return i;
37+
});
38+
return items;
39+
});
40+
updateData(result);
41+
};
1442
</script>
1543

1644
<svelte:head>
@@ -25,15 +53,36 @@
2553
titleKey="name"
2654
text="These are the groups associated with this user."
2755
endpoint={`/users/${slug}/memberships`}
28-
headers={['Group']}
56+
headers={['Group', 'Role']}
2957
onData={(val) => (data = val)}
3058
{primaryKeyType}
3159
filters={[{ title: 'Name', name: 'name', type: 'string' }, { title: 'ID', name: primaryKeyType, type: 'string' }, { title: 'Created at', name: 'createdAt', type: 'datetime' }, { title: 'Updated at', name: 'updatedAt', type: 'datetime' }]}>
3260
<td class="px-7 py-4 whitespace-nowrap">
3361
<GroupRecord item={item.group} />
3462
</td>
63+
<td class="px-7 py-4 whitespace-nowrap">
64+
{#if item.role === 'OWNER'}
65+
<Tag href={`/users/${slug}/groups?q=${encodeURIComponent('role: OWNER')}`} color="blue">
66+
Owner
67+
</Tag>
68+
{:else if item.role === 'ADMIN'}
69+
<Tag href={`/users/${slug}/groups?q=${encodeURIComponent('role: ADMIN')}`} color="indigo">
70+
Admin
71+
</Tag>
72+
{:else if item.role === 'MEMBER'}
73+
<Tag href={`/users/${slug}/groups?q=${encodeURIComponent('role: MEMBER')}`} color="green">
74+
Member
75+
</Tag>
76+
{:else}
77+
<Tag
78+
href={`/users/${slug}/groups?q=${encodeURIComponent(`role: ${item.role}`)}`}
79+
color="gray">
80+
${item.role}
81+
</Tag>
82+
{/if}
83+
</td>
3584
<td class="px-7 py-4 whitespace-nowrap text-right text-sm font-medium">
36-
{#if can(`email:write-info-${item[primaryKeyType]}`)}
85+
{#if can(`user-${slug}:delete-membership-${item[primaryKeyType]}`)}
3786
<button
3887
aria-label="Leave"
3988
data-balloon-pos="up"
@@ -54,6 +103,15 @@
54103
</td>
55104
</DataTable>
56105

106+
<div class="p-7">
107+
<Form
108+
title="Add group"
109+
text="Create another group to invite your team to."
110+
items={[{ name: 'name', label: 'Name', required: true }]}
111+
submitText="Create group"
112+
onSubmit={add} />
113+
</div>
114+
57115
{#if deleteActiveKey}
58116
<DeleteModal
59117
title="Leave group"

src/stores.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import { writable } from "svelte/store";
22

33
export interface User {
44
auth: { accessToken: string; refreshToken: string };
5+
memberships: {
6+
id: number;
7+
group: { id: number; name: string; profilePicture: string; role: "ADMIN" | "OWNER" | "MEMBER" };
8+
}[];
59
details: {
610
checkLocationOnLogin: boolean;
711
countryCode: string;
@@ -52,6 +56,4 @@ export const activeNotification = writable<{
5256
text: string;
5357
type: string;
5458
} | null>(null);
55-
activeNotification.subscribe(() =>
56-
setTimeout(() => activeNotification.set(null), 3500)
57-
);
59+
activeNotification.subscribe(() => setTimeout(() => activeNotification.set(null), 3500));

0 commit comments

Comments
 (0)