Merge pull request '69-Navbar-user' (#106) from 69-Navbar-user into main
All checks were successful
Continuous Integration / Update Development (push) Successful in 2m27s

Reviewed-on: #106
This commit was merged in pull request #106.
This commit is contained in:
2025-12-16 00:45:12 -06:00
8 changed files with 38 additions and 30 deletions

View File

@@ -5,9 +5,9 @@ import { Request, Response } from 'express';
import pool from '../db'; import pool from '../db';
import { requireLogin, requireMemberState, requireRole } from '../middleware/auth'; import { requireLogin, requireMemberState, requireRole } from '../middleware/auth';
import { getUserActiveLOA } from '../services/loaService'; import { getUserActiveLOA } from '../services/loaService';
import { getAllMembersLite, getMemberSettings, getMembersFull, getMembersLite, getUserData, setUserSettings } from '../services/memberService'; import { getAllMembersLite, getMemberSettings, getMembersFull, getMembersLite, getUserData, getUserState, setUserSettings } from '../services/memberService';
import { getUserRoles } from '../services/rolesService'; import { getUserRoles } from '../services/rolesService';
import { memberSettings, MemberState } from '@app/shared/types/member'; import { memberSettings, MemberState, myData } from '@app/shared/types/member';
//get all users //get all users
router.get('/', [requireLogin, requireMemberState(MemberState.Member)], async (req, res) => { router.get('/', [requireLogin, requireMemberState(MemberState.Member)], async (req, res) => {
@@ -37,12 +37,12 @@ router.get('/me', [requireLogin], async (req, res) => {
return res.sendStatus(401) return res.sendStatus(401)
try { try {
const { id, name, state } = await getUserData(req.user.id); const memData = await getUserData(req.user.id);
const LOAData = await getUserActiveLOA(req.user.id); const LOAData = await getUserActiveLOA(req.user.id);
const memState = await getUserState(req.user.id);
const roleData = await getUserRoles(req.user.id); const roleData = await getUserRoles(req.user.id);
const userDataFull = { id, name, state, LOAData, roleData }; const userDataFull: myData = { member: memData, LOAs: LOAData, roles: roleData, state: memState };
res.status(200).json(userDataFull); res.status(200).json(userDataFull);
} catch (error) { } catch (error) {
console.error('Error fetching user data:', error); console.error('Error fetching user data:', error);

View File

@@ -1,9 +1,9 @@
import pool from "../db"; import pool from "../db";
import { Member, MemberLight, memberSettings, MemberState } from '@app/shared/types/member' import { Member, MemberLight, memberSettings, MemberState } from '@app/shared/types/member'
export async function getUserData(userID: number) { export async function getUserData(userID: number): Promise<Member> {
const sql = `SELECT * FROM members WHERE id = ?`; const sql = `SELECT * FROM view_member_rank_unit_status_latest WHERE member_id = ?`;
const res = await pool.query(sql, [userID]); const res: Member = await pool.query(sql, [userID]);
return res[0] ?? null; return res[0] ?? null;
} }

View File

@@ -1,3 +1,6 @@
import { LOARequest } from "./loa";
import { Role } from "./roles";
export interface memberSettings { export interface memberSettings {
displayName: string; displayName: string;
} }
@@ -30,3 +33,10 @@ export interface MemberLight {
username: string username: string
color: string color: string
} }
export interface myData {
member: Member;
LOAs: LOARequest[];
roles: Role[];
state: MemberState;
}

View File

@@ -179,7 +179,9 @@ function blurAfter() {
<div> <div>
<DropdownMenu v-if="userStore.isLoggedIn"> <DropdownMenu v-if="userStore.isLoggedIn">
<DropdownMenuTrigger class="cursor-pointer"> <DropdownMenuTrigger class="cursor-pointer">
<p>{{ userStore.user.name }}</p> <Button variant="ghost" class="px-4">
{{ userStore.displayName }}
</Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent> <DropdownMenuContent>
<DropdownMenuItem @click="$router.push('/profile')">My Profile</DropdownMenuItem> <DropdownMenuItem @click="$router.push('/profile')">My Profile</DropdownMenuItem>

View File

@@ -105,7 +105,11 @@ onMounted(async () => {
console.error(error); console.error(error);
} }
if (props.adminMode) {
members.value = await getMembers(); members.value = await getMembers();
} else {
members.value.push(props.member);
}
loaTypes.value = await getLoaTypes(); loaTypes.value = await getLoaTypes();
resetForm({ values: { member_id: currentMember.value?.member_id } }); resetForm({ values: { member_id: currentMember.value?.member_id } });
}); });
@@ -153,7 +157,7 @@ const maxEndDate = computed(() => {
<ComboboxInput placeholder="Search members..." class="w-full pl-3" <ComboboxInput placeholder="Search members..." class="w-full pl-3"
:display-value="(id) => { :display-value="(id) => {
const m = members.find(mem => mem.member_id === id) const m = members.find(mem => mem.member_id === id)
return m ? m.member_name : '' return m ? m.displayName || m.member_name : ''
}" /> }" />
</ComboboxAnchor> </ComboboxAnchor>
<ComboboxList class="*:w-64"> <ComboboxList class="*:w-64">
@@ -162,7 +166,7 @@ const maxEndDate = computed(() => {
<template v-for="member in members" :key="member.member_id"> <template v-for="member in members" :key="member.member_id">
<ComboboxItem :value="member.member_id" <ComboboxItem :value="member.member_id"
class="data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative cursor-pointer select-none px-2 py-1.5 w-full"> class="data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground relative cursor-pointer select-none px-2 py-1.5 w-full">
{{ member.member_name }} {{ member.displayName || member.member_name }}
<ComboboxItemIndicator <ComboboxItemIndicator
class="absolute left-2 inline-flex items-center"> class="absolute left-2 inline-flex items-center">
<Check class="h-4 w-4" /> <Check class="h-4 w-4" />

View File

@@ -6,11 +6,11 @@ export function useAuth() {
const userStore = useUserStore(); const userStore = useUserStore();
// Account status control // Account status control
const accountStatus = computed(() => userStore.state); const accountStatus = computed(() => userStore.user?.state);
// RBAC // RBAC
const roles = computed<string[]>(() => { const roles = computed<string[]>(() => {
return userStore.user?.roleData?.map((r: Role) => r.name) ?? []; return userStore.user?.roles?.map((r: Role) => r.name) ?? [];
}); });
function isDev() { function isDev() {

View File

@@ -1,20 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import LoaForm from '@/components/loa/loaForm.vue'; import LoaForm from '@/components/loa/loaForm.vue';
import { useUserStore } from '@/stores/user'; import { useUserStore } from '@/stores/user';
import { Member } from '@/api/member'; import { Member } from '@shared/types/member';
import LoaList from '@/components/loa/loaList.vue'; import LoaList from '@/components/loa/loaList.vue';
import { ref } from 'vue'; import { ref } from 'vue';
const userStore = useUserStore(); const userStore = useUserStore();
const user = userStore.user;
const memberFull: Member = {
member_id: user.id,
member_name: user.name,
rank: null,
rank_date: null,
status: null,
status_date: null,
};
const mode = ref<'submit' | 'view'>('submit') const mode = ref<'submit' | 'view'>('submit')
</script> </script>
@@ -32,7 +23,7 @@ const mode = ref<'submit' | 'view'>('submit')
</div> </div>
</div> </div>
</div> </div>
<LoaForm v-if="mode === 'submit'" :member="memberFull"></LoaForm> <LoaForm v-if="mode === 'submit'" :member="userStore.user.member"></LoaForm>
<LoaList v-if="mode === 'view'" :admin-mode="false"></LoaList> <LoaList v-if="mode === 'view'" :admin-mode="false"></LoaList>
</div> </div>
</template> </template>

View File

@@ -1,15 +1,18 @@
import { ref, computed, watch } from 'vue' import { ref, computed, watch } from 'vue'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { myData } from '@shared/types/member'
const POLL_INTERVAL = 10_000 const POLL_INTERVAL = 10_000
export const useUserStore = defineStore('user', () => { export const useUserStore = defineStore('user', () => {
const user = ref(null) const user = ref<myData>(null)
const roles = computed(() => new Set(user.value?.roleData?.map(r => r.name) ?? [])); const roles = computed(() => new Set(user.value?.roles?.map(r => r.name) ?? []));
const loaded = ref(false); const loaded = ref(false);
const state = computed<string | undefined>(() => user.value?.state || undefined); const state = computed<string | undefined>(() => user.value?.state || undefined);
const isLoggedIn = computed(() => user.value !== null) const isLoggedIn = computed(() => user.value !== null)
const displayName = computed(() => user.value?.member.displayName || user.value?.member.member_name)
async function loadUser() { async function loadUser() {
//@ts-ignore //@ts-ignore
@@ -19,7 +22,6 @@ export const useUserStore = defineStore('user', () => {
if (res.ok) { if (res.ok) {
const data = await res.json(); const data = await res.json();
console.log(data);
user.value = data; user.value = data;
} }
@@ -40,7 +42,6 @@ export const useUserStore = defineStore('user', () => {
const router = useRouter(); const router = useRouter();
watch(user, (newUser) => { watch(user, (newUser) => {
if (!newUser) return if (!newUser) return
console.log(newUser);
const currentRoute = route.meta const currentRoute = route.meta
@@ -88,5 +89,5 @@ export const useUserStore = defineStore('user', () => {
} }
}) })
return { user, isLoggedIn, roles, loadUser, loaded, hasAnyRole, hasRole, state } return { user, displayName, isLoggedIn, roles, loadUser, loaded, hasAnyRole, hasRole, state }
}) })