feat: enhance session management by resolving collaborators to users and integrating CollaboratorDisplay component across motivators and sessions pages
This commit is contained in:
@@ -60,6 +60,53 @@ export async function getUserByEmail(email: string) {
|
||||
});
|
||||
}
|
||||
|
||||
// Check if string looks like an email
|
||||
function isEmail(str: string): boolean {
|
||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str);
|
||||
}
|
||||
|
||||
export interface ResolvedCollaborator {
|
||||
raw: string; // Original value (name or email)
|
||||
matchedUser: {
|
||||
id: string;
|
||||
email: string;
|
||||
name: string | null;
|
||||
} | null;
|
||||
}
|
||||
|
||||
// Resolve collaborator string to user - try email first, then name
|
||||
export async function resolveCollaborator(collaborator: string): Promise<ResolvedCollaborator> {
|
||||
const trimmed = collaborator.trim();
|
||||
|
||||
// 1. Try email match first
|
||||
if (isEmail(trimmed)) {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { email: trimmed.toLowerCase() },
|
||||
select: { id: true, email: true, name: true },
|
||||
});
|
||||
|
||||
if (user) {
|
||||
return { raw: collaborator, matchedUser: user };
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fallback: try matching by name (case-insensitive via raw query for SQLite)
|
||||
// SQLite LIKE is case-insensitive by default for ASCII
|
||||
const users = await prisma.user.findMany({
|
||||
where: {
|
||||
name: { not: null },
|
||||
},
|
||||
select: { id: true, email: true, name: true },
|
||||
});
|
||||
|
||||
const normalizedSearch = trimmed.toLowerCase();
|
||||
const userByName = users.find(
|
||||
(u) => u.name?.toLowerCase() === normalizedSearch
|
||||
) || null;
|
||||
|
||||
return { raw: collaborator, matchedUser: userByName };
|
||||
}
|
||||
|
||||
export async function getUserById(id: string) {
|
||||
return prisma.user.findUnique({
|
||||
where: { id },
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { prisma } from '@/services/database';
|
||||
import { resolveCollaborator } from '@/services/auth';
|
||||
import type { ShareRole, MotivatorType } from '@prisma/client';
|
||||
|
||||
// ============================================
|
||||
@@ -56,9 +57,19 @@ export async function getMotivatorSessionsByUserId(userId: string) {
|
||||
sharedAt: s.createdAt,
|
||||
}));
|
||||
|
||||
return [...ownedWithRole, ...sharedWithRole].sort(
|
||||
const allSessions = [...ownedWithRole, ...sharedWithRole].sort(
|
||||
(a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
|
||||
);
|
||||
|
||||
// Resolve participants to users
|
||||
const sessionsWithResolved = await Promise.all(
|
||||
allSessions.map(async (s) => ({
|
||||
...s,
|
||||
resolvedParticipant: await resolveCollaborator(s.participant),
|
||||
}))
|
||||
);
|
||||
|
||||
return sessionsWithResolved;
|
||||
}
|
||||
|
||||
export async function getMotivatorSessionById(sessionId: string, userId: string) {
|
||||
@@ -92,7 +103,10 @@ export async function getMotivatorSessionById(sessionId: string, userId: string)
|
||||
const role = isOwner ? ('OWNER' as const) : share?.role || ('VIEWER' as const);
|
||||
const canEdit = isOwner || role === 'EDITOR';
|
||||
|
||||
return { ...session, isOwner, role, canEdit };
|
||||
// Resolve participant to user if it's an email
|
||||
const resolvedParticipant = await resolveCollaborator(session.participant);
|
||||
|
||||
return { ...session, isOwner, role, canEdit, resolvedParticipant };
|
||||
}
|
||||
|
||||
// Check if user can access session (owner or shared)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { prisma } from '@/services/database';
|
||||
import { resolveCollaborator } from '@/services/auth';
|
||||
import type { SwotCategory, ShareRole } from '@prisma/client';
|
||||
|
||||
// ============================================
|
||||
@@ -58,9 +59,19 @@ export async function getSessionsByUserId(userId: string) {
|
||||
sharedAt: s.createdAt,
|
||||
}));
|
||||
|
||||
return [...ownedWithRole, ...sharedWithRole].sort(
|
||||
const allSessions = [...ownedWithRole, ...sharedWithRole].sort(
|
||||
(a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
|
||||
);
|
||||
|
||||
// Resolve collaborators to users
|
||||
const sessionsWithResolved = await Promise.all(
|
||||
allSessions.map(async (s) => ({
|
||||
...s,
|
||||
resolvedCollaborator: await resolveCollaborator(s.collaborator),
|
||||
}))
|
||||
);
|
||||
|
||||
return sessionsWithResolved;
|
||||
}
|
||||
|
||||
export async function getSessionById(sessionId: string, userId: string) {
|
||||
@@ -104,7 +115,10 @@ export async function getSessionById(sessionId: string, userId: string) {
|
||||
const role = isOwner ? ('OWNER' as const) : share?.role || ('VIEWER' as const);
|
||||
const canEdit = isOwner || role === 'EDITOR';
|
||||
|
||||
return { ...session, isOwner, role, canEdit };
|
||||
// Resolve collaborator to user if it's an email
|
||||
const resolvedCollaborator = await resolveCollaborator(session.collaborator);
|
||||
|
||||
return { ...session, isOwner, role, canEdit, resolvedCollaborator };
|
||||
}
|
||||
|
||||
// Check if user can access session (owner or shared)
|
||||
|
||||
Reference in New Issue
Block a user