Skip to content

Commit 223f026

Browse files
authored
feat: permissions refactor in frontend
* chore: enable conditional editing of confirmed date based on user role * refactor: improved role-based permissions
1 parent 46df669 commit 223f026

File tree

5 files changed

+53
-16
lines changed

5 files changed

+53
-16
lines changed

frontend/src/components/Navbar.vue

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,13 @@ import type { Speaker } from "@/dto/speakers";
3030
import type { Member } from "@/dto/members";
3131
import { useMagicKeys } from "@vueuse/core";
3232
import Notification from "./navbar/Notification.vue";
33+
import { usePermissions } from "@/composables/usePermissions";
3334
3435
const isOpen = ref(false);
3536
const authStore = useAuthStore();
37+
const { isCoordinatorOrAdmin } = usePermissions();
3638
37-
const showCoordination = computed(() => {
38-
const role = authStore.decoded?.role as string | undefined;
39-
return role === "COORDINATOR" || role === "ADMIN";
40-
});
39+
const showCoordination = computed(() => isCoordinatorOrAdmin.value === true);
4140
const router = useRouter();
4241
4342
const logout = () => {

frontend/src/components/cards/CompanyCard.vue

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ import { useCompanyInfoMutation } from "@/mutations/companies";
150150
import { useCompanyImageUploadMutation } from "@/mutations/companies";
151151
import { usePackageQuery } from "@/mutations/packages";
152152
import { deleteCompany } from "@/api/companies";
153-
import { useAuthStore } from "@/stores/auth";
153+
import { usePermissions } from "@/composables/usePermissions";
154154
import { useQueryCache } from "@pinia/colada";
155155
import { useRouter } from "vue-router";
156156
import Card from "../ui/card/Card.vue";
@@ -184,7 +184,7 @@ const isDescriptionExpanded = ref(false);
184184
const isEditing = ref(false);
185185
const isDeleteConfirmOpen = ref(false);
186186
const isDeleting = ref(false);
187-
const authStore = useAuthStore();
187+
const { isCoordinatorOrAdmin } = usePermissions();
188188
const queryCache = useQueryCache();
189189
const router = useRouter();
190190
@@ -287,9 +287,7 @@ const formatLinkedIn = (url: string): string => {
287287
};
288288
289289
const canDelete = computed(() => {
290-
if (!authStore.decoded) return false;
291-
const role = (authStore.decoded as { role?: string }).role;
292-
return role === "COORDINATOR" || role === "ADMIN";
290+
return isCoordinatorOrAdmin.value === true;
293291
});
294292
295293
const handleDelete = async () => {

frontend/src/components/cards/SpeakerCard.vue

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ import type {
133133
import { useSpeakerInfoMutation } from "@/mutations/speakers";
134134
import { useSpeakerImageUploadMutation } from "@/mutations/speakers";
135135
import { deleteSpeaker } from "@/api/speakers";
136-
import { useAuthStore } from "@/stores/auth";
137136
import { useQueryCache } from "@pinia/colada";
138137
import { useRouter } from "vue-router";
139138
import Card from "../ui/card/Card.vue";
@@ -148,6 +147,7 @@ import SpeakerInfoForm from "../speakers/SpeakerInfoForm.vue";
148147
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
149148
import { TrashIcon } from "lucide-vue-next";
150149
import ConfirmDelete from "@/components/ConfirmDelete.vue";
150+
import { usePermissions } from "@/composables/usePermissions";
151151
import ParticipationStatusBadge from "@/components/ParticipationStatusBadge.vue";
152152
153153
const props = defineProps<{
@@ -163,7 +163,7 @@ const isBioExpanded = ref(false);
163163
const isEditing = ref(false);
164164
const isDeleteConfirmOpen = ref(false);
165165
const isDeleting = ref(false);
166-
const authStore = useAuthStore();
166+
const { isCoordinatorOrAdmin } = usePermissions();
167167
const queryCache = useQueryCache();
168168
const router = useRouter();
169169
@@ -250,9 +250,7 @@ const toggleBio = () => {
250250
};
251251
252252
const canDelete = computed(() => {
253-
if (!authStore.decoded) return false;
254-
const role = (authStore.decoded as { role?: string }).role;
255-
return role === "COORDINATOR" || role === "ADMIN";
253+
return isCoordinatorOrAdmin.value === true;
256254
});
257255
258256
const handleDelete = async () => {

frontend/src/components/companies/EditableCompanyParticipation.vue

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@
9797
id="confirmed-date"
9898
v-model="editForm.confirmed"
9999
type="datetime-local"
100-
class="w-full px-3 py-2 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
100+
class="w-full px-3 py-2 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:opacity-50 disabled:bg-gray-100 disabled:cursor-not-allowed"
101+
:disabled="!canEditConfirmedDate"
101102
/>
102103
</div>
103104

@@ -170,7 +171,7 @@
170171
</template>
171172

172173
<script setup lang="ts">
173-
import { ref, reactive, watch } from "vue";
174+
import { ref, reactive, watch, computed } from "vue";
174175
import useToast from "@/lib/toast";
175176
import { useQuery } from "@pinia/colada";
176177
import { getAllEvents } from "@/api/events";
@@ -183,6 +184,7 @@ import { Label } from "@/components/ui/label";
183184
import MemberSelect from "@/components/members/MemberSelect.vue";
184185
import { useCompanyParticipationPackageMutation } from "@/mutations/companies";
185186
import { usePackagesQuery, usePackageQuery } from "@/mutations/packages";
187+
import { usePermissions } from "@/composables/usePermissions";
186188
import type { Package } from "@/dto/packages";
187189
import type {
188190
CompanyParticipation,
@@ -205,6 +207,11 @@ interface Props {
205207
206208
const props = defineProps<Props>();
207209
210+
const { isCoordinatorOrAdmin } = usePermissions();
211+
const canEditConfirmedDate = computed(() => {
212+
return isCoordinatorOrAdmin.value === true;
213+
});
214+
208215
const isEditing = ref(false);
209216
const isSaving = ref(false);
210217
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { computed } from "vue";
2+
import { useAuthStore } from "@/stores/auth";
3+
4+
export function usePermissions() {
5+
const authStore = useAuthStore();
6+
7+
/**
8+
* The user's role, or undefined if not authenticated.
9+
*/
10+
const role = computed<string | undefined>(() => authStore.decoded?.role);
11+
12+
const isAdmin = computed(() => role.value === "ADMIN");
13+
const isCoordinator = computed(() => role.value === "COORDINATOR");
14+
const isTeamLeader = computed(() => role.value === "TEAMLEADER");
15+
const isMember = computed(() => role.value === "MEMBER");
16+
17+
// Composite permissions
18+
const isCoordinatorOrAdmin = computed(
19+
() => isCoordinator.value || isAdmin.value,
20+
);
21+
22+
const isTeamLeaderOrHigher = computed(
23+
() => isTeamLeader.value || isCoordinatorOrAdmin.value,
24+
);
25+
26+
return {
27+
role,
28+
isAdmin,
29+
isCoordinator,
30+
isTeamLeader,
31+
isMember,
32+
isCoordinatorOrAdmin,
33+
isTeamLeaderOrHigher,
34+
};
35+
}

0 commit comments

Comments
 (0)