Skip to content

Commit 6189f03

Browse files
committed
Add initial permissions system
1 parent 7813b76 commit 6189f03

22 files changed

Lines changed: 695 additions & 73 deletions

File tree

components/ProjectGroupPicker.vue

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,32 @@
44
class="project-group-picker form-select"
55
aria-label="Project Group Selection"
66
>
7-
<option v-for="pg in projectGroups" :key="pg.id" :value="pg.id">
7+
<option
8+
v-for="pg in projectGroups"
9+
:key="pg.tdei_project_group_id"
10+
:value="pg.tdei_project_group_id"
11+
>
812
{{ pg.name }}
913
</option>
1014
</select>
1115
</template>
1216

1317
<script setup lang="ts">
14-
import { tdeiUserClient } from '~/services/index'
18+
import { tdeiUserClient } from '~/services/index';
19+
import type { TdeiProjectGroupItem } from '~/types/tdei';
20+
import { compareStringAsc } from '~/util/compare';
1521
16-
const model = defineModel({ required: true });
17-
const projectGroups = (await tdeiUserClient.getMyProjectGroups())
18-
.sort((a, b) => a.name.localeCompare(b.name));
22+
const model = defineModel<string | undefined>({ required: true });
23+
const props = defineProps<{ options?: TdeiProjectGroupItem[] }>();
24+
25+
const projectGroups = props.options
26+
?? (await tdeiUserClient.getMyProjectGroups()).sort(compareStringAsc(g => g.name));
1927
2028
if (projectGroups.length > 0) {
21-
if (!model.value || !projectGroups.some(pg => pg.id === model.value)) {
22-
model.value = projectGroups[0].id;
29+
const pgId = model.value;
30+
31+
if (!pgId || !projectGroups.some(pg => pg.tdei_project_group_id === pgId)) {
32+
model.value = projectGroups[0]?.tdei_project_group_id;
2333
}
2434
}
2535
</script>

components/dashboard/DetailsTable.vue

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
2-
<div class="table-responsive border-top">
3-
<table class="table table-striped mb-0">
2+
<div class="table-responsive border-top mb-0">
3+
<table class="table table-striped">
44
<tbody>
55
<tr>
66
<th><app-icon variant="schedule" />Created At</th>
@@ -10,6 +10,41 @@
1010
<th><app-icon variant="person_outline" />Created By</th>
1111
<td>{{ workspace.createdByName }}</td>
1212
</tr>
13+
<tr>
14+
<th><app-icon variant="badge" />My Role</th>
15+
<td>
16+
<span
17+
v-if="workspace.role === 'lead'"
18+
class="badge bg-dark text-uppercase"
19+
>
20+
<app-icon variant="star" /> Owner
21+
</span>
22+
<span
23+
v-else-if="workspace.role === 'validator'"
24+
class="badge bg-dark text-uppercase"
25+
>
26+
<app-icon variant="task_alt" /> Validator
27+
</span>
28+
<span
29+
v-else
30+
class="badge bg-secondary text-uppercase"
31+
>
32+
<app-icon variant="person" /> Member
33+
</span>
34+
<span
35+
v-if="isPoc"
36+
class="badge bg-warning text-dark text-uppercase ms-1"
37+
>
38+
<app-icon variant="local_police" /> POC
39+
</span>
40+
<span
41+
v-else-if="isDataGenerator"
42+
class="badge bg-warning text-dark text-uppercase ms-1"
43+
>
44+
<app-icon variant="offline_bolt" /> Data Generator
45+
</span>
46+
</td>
47+
</tr>
1348
<tr>
1449
<th><app-icon variant="phonelink_setup" />App Access</th>
1550
<td>
@@ -42,12 +77,19 @@
4277
</template>
4378

4479
<script setup lang="ts">
45-
const props = defineProps({
46-
workspace: {
47-
type: Object,
48-
required: true
49-
}
50-
});
80+
import type { Workspace } from '~/types/workspaces';
81+
82+
interface Props {
83+
workspace: Workspace;
84+
myTdeiRoles: string[];
85+
}
86+
87+
const props = defineProps<Props>();
88+
89+
const isPoc = computed(() => props.myTdeiRoles.includes('poc'));
90+
const isDataGenerator = computed(() =>
91+
props.myTdeiRoles.includes(`${props.workspace.type}_data_generator`),
92+
);
5193
</script>
5294

5395
<style lang="scss">

components/dashboard/Toolbar.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
<app-icon variant="settings" size="24" no-margin />
3333
<span class="d-none d-sm-inline ms-2">Settings</span>
3434
</nuxt-link>
35-
</div>
36-
</div>
35+
</div><!-- .btn-group -->
36+
</div><!-- .btn-toolbar -->
3737
</template>
3838

3939
<script setup lang="ts">

components/dashboard/WorkspaceItem.vue

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,25 @@
99
<app-icon v-else variant="lock" />
1010
App
1111
</span>
12+
13+
<span
14+
v-if="workspace.role === 'lead'"
15+
class="badge bg-dark ms-2"
16+
>
17+
<app-icon variant="star" /> {{ ROLE_LABELS.lead }}
18+
</span>
19+
<span
20+
v-else-if="workspace.role === 'validator'"
21+
class="badge bg-dark ms-2"
22+
>
23+
<app-icon variant="task_alt" /> {{ ROLE_LABELS.validator }}
24+
</span>
1225
</button>
1326
</template>
1427

1528
<script setup lang="ts">
29+
import { ROLE_LABELS } from '~/util/roles';
30+
1631
const props = defineProps({
1732
workspace: {
1833
type: Object,

components/review/Toolbar.vue

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,27 @@
4545
{{ props.item.commentCount }}
4646
</span>
4747
</button>
48-
<button
49-
v-show="props.item.isFeedback"
50-
class="btn btn-sm btn-success ms-2"
48+
<BPopover
49+
content="Only validators and owners can resolve feedback"
50+
placement="bottom"
51+
:manual="isValidator"
5152
>
52-
<app-icon
53-
variant="check"
54-
no-margin
55-
/>
56-
<span class="d-none d-sm-inline ms-2">Mark as Resolved</span>
57-
</button>
53+
<template #target>
54+
<div class="d-inline-block ms-2">
55+
<button
56+
v-show="props.item.isFeedback && !props.item.isResolved"
57+
class="btn btn-sm btn-success"
58+
:disabled="!isValidator"
59+
>
60+
<app-icon
61+
variant="check"
62+
no-margin
63+
/>
64+
<span class="d-none d-sm-inline ms-2">Mark as Resolved</span>
65+
</button>
66+
</div>
67+
</template>
68+
</BPopover>
5869
</nav>
5970
</template>
6071

@@ -66,6 +77,7 @@ interface Props {
6677
}
6778
6879
const props = defineProps<Props>();
80+
const { isValidator } = useWorkspaceRole();
6981
7082
const emit = defineEmits(['edit']);
7183
const showDetails = defineModel<boolean>('showDetails');

components/settings/Nav.vue

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@
66
>
77
General
88
</settings-nav-link>
9+
<settings-nav-link
10+
to="/members"
11+
icon="admin_panel_settings"
12+
>
13+
Members
14+
</settings-nav-link>
915
<settings-nav-link
1016
to="/teams"
11-
icon="group"
17+
icon="diversity_3"
1218
>
1319
Teams
1420
</settings-nav-link>

components/settings/panel/Apps.vue

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,15 @@
88
External Apps
99
</h3>
1010

11+
<b-alert
12+
v-if="!isLead"
13+
variant="info"
14+
show
15+
class="mb-3"
16+
>
17+
Only workspace owners can change external app settings.
18+
</b-alert>
19+
1120
<div class="form-check form-switch">
1221
<label class="form-check-label">
1322
<input
@@ -16,6 +25,7 @@
1625
class="form-check-input"
1726
:true-value="1"
1827
:false-value="0"
28+
:disabled="!isLead"
1929
>
2030
Publish this workspace for external apps
2131
</label>
@@ -36,6 +46,7 @@
3646
type="radio"
3747
name="longFormQuestType"
3848
value="JSON"
49+
:disabled="!isLead"
3950
>
4051
</label>
4152
</div>
@@ -48,6 +59,7 @@
4859
type="radio"
4960
name="longFormQuestType"
5061
value="URL"
62+
:disabled="!isLead"
5163
>
5264
</label>
5365
</div>
@@ -61,6 +73,7 @@
6173
:class="{ 'drag-over': isDraggingQuest }"
6274
rows="5"
6375
placeholder="Optional"
76+
:disabled="!isLead"
6477
@dragover.prevent="isDraggingQuest = true"
6578
@dragleave.prevent="isDraggingQuest = false"
6679
@drop.prevent="onQuestFileDrop"
@@ -96,6 +109,7 @@
96109
type="text"
97110
class="form-control"
98111
placeholder="https://..."
112+
:disabled="!isLead"
99113
>
100114
</label>
101115
<div
@@ -131,6 +145,7 @@
131145
<button
132146
type="submit"
133147
class="btn btn-primary"
148+
:disabled="!isLead"
134149
>
135150
Save
136151
</button>
@@ -157,6 +172,7 @@ const longFormQuestSchemaUrl = import.meta.env.VITE_LONG_FORM_QUEST_SCHEMA;
157172
const longFormQuestExampleUrl = import.meta.env.VITE_LONG_FORM_QUEST_EXAMPLE_URL;
158173
159174
const workspace = inject<Workspace>('workspace')!;
175+
const { isLead } = useWorkspaceRole();
160176
161177
const [longFormQuestSettings] = await Promise.all([
162178
workspacesClient.getLongFormQuestSettings(workspace.id),

components/settings/panel/Delete.vue

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,23 @@
55
Delete Workspace
66
</h3>
77

8+
<b-alert
9+
v-if="!isLead"
10+
variant="info"
11+
show
12+
class="mb-3"
13+
>
14+
Only workspace owners can delete the workspace.
15+
</b-alert>
16+
817
<p>
918
Deleting a workspace is permanent. This action will not remove any
1019
TDEI datasets outside of Workspaces.
1120
</p>
1221

1322
<button
1423
class="btn btn-outline-danger mb-3"
15-
:disabled="accepted"
24+
:disabled="!isLead || accepted"
1625
@click="acceptDelete"
1726
>
1827
I understand, and I want to delete this workspace
@@ -30,7 +39,7 @@
3039

3140
<button
3241
class="btn btn-danger"
33-
:disabled="attestation !== 'delete'"
42+
:disabled="!isLead || attestation !== 'delete'"
3443
@click="submitDelete"
3544
>
3645
Delete this workspace
@@ -48,18 +57,27 @@ import { workspacesClient } from '~/services/index';
4857
import type { Workspace } from '~/types/workspaces';
4958
5059
const workspace = inject<Workspace>('workspace')!;
60+
const { isLead } = useWorkspaceRole();
5161
5262
const accepted = ref(false);
5363
const attestation = ref('');
5464
const input = useTemplateRef<HTMLInputElement>('input');
5565
5666
async function acceptDelete() {
67+
if (!isLead.value) {
68+
return;
69+
}
70+
5771
accepted.value = true;
5872
await nextTick();
5973
input.value!.focus();
6074
}
6175
6276
async function submitDelete() {
77+
if (!isLead.value) {
78+
return;
79+
}
80+
6381
await workspacesClient.deleteWorkspace(workspace.id);
6482
navigateTo('/dashboard');
6583
}

components/settings/panel/General.vue

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
11
<template>
22
<section class="card mb-4">
33
<div class="card-body">
4+
<b-alert
5+
v-if="!isLead"
6+
variant="info"
7+
show
8+
class="mb-3"
9+
>
10+
Only workspace owners can rename the workspace.
11+
</b-alert>
412
<form @submit.prevent="rename">
513
<label class="d-block mb-3">
614
Workspace Title
715
<input
816
v-model.trim="workspaceName"
917
class="form-control"
18+
:disabled="!isLead"
1019
>
1120
</label>
1221

1322
<button
1423
type="submit"
1524
class="btn btn-primary"
25+
:disabled="!isLead"
1626
>
1727
Rename
1828
</button>
@@ -28,6 +38,7 @@ import { workspacesClient } from '~/services/index';
2838
import type { Workspace } from '~/types/workspaces';
2939
3040
const workspace = inject<Workspace>('workspace')!;
41+
const { isLead } = useWorkspaceRole();
3142
const workspaceName = ref(workspace.title);
3243
3344
async function rename() {

0 commit comments

Comments
 (0)