122 lines
4.7 KiB
Plaintext
122 lines
4.7 KiB
Plaintext
---
|
|
import DashboardLayout from '../../layouts/DashboardLayout.astro';
|
|
import Avatar from '../../components/Avatar.astro';
|
|
import Icon from '../../components/Icon.astro';
|
|
import { db } from '../../db';
|
|
import { members, users } from '../../db/schema';
|
|
import { eq } from 'drizzle-orm';
|
|
import { getCurrentTeam } from '../../lib/getCurrentTeam';
|
|
|
|
const user = Astro.locals.user;
|
|
if (!user) return Astro.redirect('/login');
|
|
|
|
const userMembership = await getCurrentTeam(user, Astro.cookies.get('currentTeamId')?.value);
|
|
if (!userMembership) return Astro.redirect('/dashboard');
|
|
|
|
const teamMembers = await db.select({
|
|
member: members,
|
|
user: users,
|
|
})
|
|
.from(members)
|
|
.innerJoin(users, eq(members.userId, users.id))
|
|
.where(eq(members.organizationId, userMembership.organizationId))
|
|
.all();
|
|
|
|
const currentUserMember = teamMembers.find(m => m.user.id === user.id);
|
|
const isAdmin = currentUserMember?.member.role === 'owner' || currentUserMember?.member.role === 'admin';
|
|
---
|
|
|
|
<DashboardLayout title="Team - Chronus">
|
|
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-6">
|
|
<div>
|
|
<h1 class="text-2xl font-extrabold tracking-tight">Team Members</h1>
|
|
<p class="text-base-content/60 text-sm mt-1">Manage your organization's team</p>
|
|
</div>
|
|
<div class="flex gap-2">
|
|
{isAdmin && (
|
|
<>
|
|
<a href="/dashboard/team/settings" class="btn btn-ghost btn-sm">
|
|
<Icon name="cog-6-tooth" class="w-4 h-4" />
|
|
Settings
|
|
</a>
|
|
<a href="/dashboard/team/invite" class="btn btn-primary btn-sm">Invite Member</a>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card card-border bg-base-100">
|
|
<div class="card-body p-0">
|
|
<div class="overflow-x-auto">
|
|
<table class="table table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Email</th>
|
|
<th>Role</th>
|
|
<th>Joined</th>
|
|
{isAdmin && <th>Actions</th>}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{teamMembers.map(({ member, user: teamUser }) => (
|
|
<tr class="hover">
|
|
<td>
|
|
<div class="flex items-center gap-3">
|
|
<Avatar name={teamUser.name} />
|
|
<div>
|
|
<div class="font-medium">{teamUser.name}</div>
|
|
{teamUser.id === user.id && (
|
|
<span class="badge badge-xs">You</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td class="text-base-content/60">{teamUser.email}</td>
|
|
<td>
|
|
<span class={`badge badge-xs ${
|
|
member.role === 'owner' ? 'badge-primary' :
|
|
member.role === 'admin' ? 'badge-secondary' :
|
|
'badge-ghost'
|
|
}`}>
|
|
{member.role}
|
|
</span>
|
|
</td>
|
|
<td class="text-base-content/60">{member.joinedAt?.toLocaleDateString() ?? 'N/A'}</td>
|
|
{isAdmin && (
|
|
<td>
|
|
{teamUser.id !== user.id && member.role !== 'owner' && (
|
|
<div class="dropdown dropdown-end">
|
|
<div role="button" tabindex="0" class="btn btn-ghost btn-xs btn-square">
|
|
<Icon name="ellipsis-vertical" class="w-4 h-4" />
|
|
</div>
|
|
<ul tabindex="0" class="dropdown-content z-1 menu p-2 bg-base-100 rounded-box w-52 border border-base-content/20">
|
|
<li>
|
|
<form method="POST" action={`/api/team/change-role`}>
|
|
<input type="hidden" name="userId" value={teamUser.id} />
|
|
<input type="hidden" name="role" value={member.role === 'admin' ? 'member' : 'admin'} />
|
|
<button type="submit">
|
|
{member.role === 'admin' ? 'Make Member' : 'Make Admin'}
|
|
</button>
|
|
</form>
|
|
</li>
|
|
<li>
|
|
<form method="POST" action={`/api/team/remove`}>
|
|
<input type="hidden" name="userId" value={teamUser.id} />
|
|
<button type="submit" class="text-error">Remove</button>
|
|
</form>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
)}
|
|
</td>
|
|
)}
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</DashboardLayout>
|