From f124e41630b24ccd72f328aa5d63bb97ef40f92c Mon Sep 17 00:00:00 2001 From: ajdj100 Date: Thu, 18 Dec 2025 17:39:44 -0500 Subject: [PATCH] added roles to member card --- api/src/services/memberService.ts | 51 +++++++++++++++++++++--- shared/types/member.ts | 5 +++ ui/src/api/member.ts | 4 +- ui/src/components/members/MemberCard.vue | 31 ++++++++------ ui/src/stores/memberDirectory.ts | 17 ++++---- 5 files changed, 81 insertions(+), 27 deletions(-) diff --git a/api/src/services/memberService.ts b/api/src/services/memberService.ts index 844ef33..e1f4fed 100644 --- a/api/src/services/memberService.ts +++ b/api/src/services/memberService.ts @@ -1,5 +1,6 @@ +import { Role } from "@app/shared/types/roles"; import pool from "../db"; -import { Member, MemberLight, memberSettings, MemberState } from '@app/shared/types/member' +import { Member, MemberCardDetails, MemberLight, memberSettings, MemberState } from '@app/shared/types/member' export async function getUserData(userID: number): Promise { const sql = `SELECT * FROM view_member_rank_unit_status_latest WHERE member_id = ?`; @@ -60,10 +61,50 @@ export async function getAllMembersLite(): Promise { return res; } -export async function getMembersFull(ids: number[]): Promise { - const sql = `SELECT * FROM view_member_rank_unit_status_latest WHERE member_id IN (?);`; - const res: Member[] = await pool.query(sql, [ids]); - return res; +export async function getMembersFull(ids: number[]): Promise { + const sql = ` + SELECT m.*, + COALESCE( + JSON_ARRAYAGG( + CASE + WHEN r.id IS NOT NULL THEN JSON_OBJECT( + 'id', r.id, + 'name', r.name, + 'color', r.color, + 'description', r.description + ) + END + ), + JSON_ARRAY() + ) AS roles + FROM view_member_rank_unit_status_latest m + LEFT JOIN members_roles mr ON m.member_id = mr.member_id + LEFT JOIN roles r ON mr.role_id = r.id + WHERE m.member_id IN (?) + GROUP BY m.member_id; + `; + + const rows: any[] = await pool.query(sql, [ids]); + + return rows.map(row => { + const member: Member = { + member_id: row.member_id, + member_name: row.member_name, + displayName: row.displayName, + rank: row.rank, + rank_date: row.rank_date, + unit: row.unit, + unit_date: row.unit_date, + status: row.status, + status_date: row.status_date, + loa_until: row.loa_until ? new Date(row.loa_until) : undefined, + }; + + // roles comes as array of strings; parse each one + const roles: Role[] = JSON.parse(row.roles).map((r: string) => JSON.parse(r)); + + return { member, roles }; + }); } export async function mapDiscordtoID(id: number): Promise { diff --git a/shared/types/member.ts b/shared/types/member.ts index 7caa9f0..eea7f85 100644 --- a/shared/types/member.ts +++ b/shared/types/member.ts @@ -34,6 +34,11 @@ export interface MemberLight { color: string } +export interface MemberCardDetails { + member: Member; + roles: Role[]; +} + export interface myData { member: Member; LOAs: LOARequest[]; diff --git a/ui/src/api/member.ts b/ui/src/api/member.ts index 327a5ce..b6aeb47 100644 --- a/ui/src/api/member.ts +++ b/ui/src/api/member.ts @@ -1,4 +1,4 @@ -import { memberSettings, Member, MemberLight } from "@shared/types/member"; +import { memberSettings, Member, MemberLight, MemberCardDetails } from "@shared/types/member"; // @ts-ignore const addr = import.meta.env.VITE_APIHOST; @@ -71,7 +71,7 @@ export async function getLightMembers(ids: number[]): Promise { return response.json(); } -export async function getFullMembers(ids: number[]): Promise { +export async function getFullMembers(ids: number[]): Promise { if (ids.length === 0) return []; diff --git a/ui/src/components/members/MemberCard.vue b/ui/src/components/members/MemberCard.vue index 6195998..249da24 100644 --- a/ui/src/components/members/MemberCard.vue +++ b/ui/src/components/members/MemberCard.vue @@ -1,7 +1,7 @@