feat: add button and modal to display users (partners) of a company in companies management page
All checks were successful
Staging Build / build (push) Successful in 2m49s
All checks were successful
Staging Build / build (push) Successful in 2m49s
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { Building2, Plus, Trash2, Loader2, Pencil, Mail, MapPin, Search } from 'lucide-react'
|
import { Building2, Plus, Trash2, Loader2, Pencil, Mail, MapPin, Search, Users } from 'lucide-react'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@/components/ui/dialog'
|
} from '@/components/ui/dialog'
|
||||||
import { getCompanies, createCompany, updateCompany, deleteCompany } from '@/lib/actions/companies'
|
import { getCompanies, createCompany, updateCompany, deleteCompany } from '@/lib/actions/companies'
|
||||||
|
import { getUsersByCompany } from '@/lib/actions/users'
|
||||||
|
|
||||||
export default function CompaniesPage() {
|
export default function CompaniesPage() {
|
||||||
const [companies, setCompanies] = useState<any[]>([])
|
const [companies, setCompanies] = useState<any[]>([])
|
||||||
@@ -32,6 +33,11 @@ export default function CompaniesPage() {
|
|||||||
const [deletingId, setDeletingId] = useState<string | null>(null)
|
const [deletingId, setDeletingId] = useState<string | null>(null)
|
||||||
const [searchQuery, setSearchQuery] = useState('')
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
|
|
||||||
|
const [isUsersOpen, setIsUsersOpen] = useState(false)
|
||||||
|
const [selectedCompanyForUsers, setSelectedCompanyForUsers] = useState<any>(null)
|
||||||
|
const [companyUsers, setCompanyUsers] = useState<any[]>([])
|
||||||
|
const [loadingUsers, setLoadingUsers] = useState(false)
|
||||||
|
|
||||||
const loadCompanies = async () => {
|
const loadCompanies = async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
@@ -85,6 +91,20 @@ export default function CompaniesPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleShowUsers = async (company: any) => {
|
||||||
|
setSelectedCompanyForUsers(company)
|
||||||
|
setIsUsersOpen(true)
|
||||||
|
setLoadingUsers(true)
|
||||||
|
try {
|
||||||
|
const data = await getUsersByCompany(company.id)
|
||||||
|
setCompanyUsers(data)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Fehler beim Laden der Benutzer:', err)
|
||||||
|
} finally {
|
||||||
|
setLoadingUsers(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleDelete = async (id: string, firmName: string) => {
|
const handleDelete = async (id: string, firmName: string) => {
|
||||||
if (!confirm(`Firma "${firmName}" wirklich löschen? Alle zugewiesenen Partner werden getrennt.`)) return
|
if (!confirm(`Firma "${firmName}" wirklich löschen? Alle zugewiesenen Partner werden getrennt.`)) return
|
||||||
setDeletingId(id)
|
setDeletingId(id)
|
||||||
@@ -189,6 +209,15 @@ export default function CompaniesPage() {
|
|||||||
</td>
|
</td>
|
||||||
<td className="py-4 text-right">
|
<td className="py-4 text-right">
|
||||||
<div className="flex justify-end gap-2">
|
<div className="flex justify-end gap-2">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => handleShowUsers(company)}
|
||||||
|
className="h-8 w-8 text-indigo-500 hover:text-indigo-400 hover:bg-indigo-500/10"
|
||||||
|
title="Benutzer anzeigen"
|
||||||
|
>
|
||||||
|
<Users className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
@@ -312,6 +341,66 @@ export default function CompaniesPage() {
|
|||||||
</form>
|
</form>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
{/* Dialog: Benutzer der Firma anzeigen */}
|
||||||
|
<Dialog open={isUsersOpen} onOpenChange={(val) => {
|
||||||
|
setIsUsersOpen(val);
|
||||||
|
if (!val) {
|
||||||
|
setCompanyUsers([]);
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
<DialogContent className="bg-white dark:bg-slate-950 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white shadow-2xl">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>
|
||||||
|
Benutzer von {selectedCompanyForUsers?.name}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription className="text-slate-500 dark:text-slate-400">
|
||||||
|
Liste aller Benutzer, die dieser Firma zugewiesen sind.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="py-4">
|
||||||
|
{loadingUsers ? (
|
||||||
|
<div className="flex justify-center items-center py-8">
|
||||||
|
<Loader2 className="w-6 h-6 text-indigo-500 animate-spin" />
|
||||||
|
<span className="ml-2 text-slate-400 text-sm">Lade Benutzer...</span>
|
||||||
|
</div>
|
||||||
|
) : companyUsers.length === 0 ? (
|
||||||
|
<p className="text-center text-slate-500 text-sm py-8">
|
||||||
|
Keine Benutzer für diese Firma registriert.
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-3 max-h-60 overflow-y-auto pr-1">
|
||||||
|
{companyUsers.map((user) => (
|
||||||
|
<div key={user.id} className="flex justify-between items-center p-3 rounded-lg bg-slate-50 dark:bg-white/5 border border-slate-100 dark:border-white/5">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm font-semibold text-slate-900 dark:text-white">
|
||||||
|
{user.first_name || user.last_name ? `${user.first_name} ${user.last_name}` : 'Kein Name'}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-slate-500 dark:text-slate-400">
|
||||||
|
{user.email}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<span className={`text-[10px] font-bold uppercase rounded-full px-2.5 py-0.5 border ${
|
||||||
|
user.role === 'admin'
|
||||||
|
? 'bg-purple-500/10 text-purple-400 border-purple-500/30'
|
||||||
|
: user.role === 'gesperrt'
|
||||||
|
? 'bg-red-500/10 text-red-400 border-red-500/30'
|
||||||
|
: 'bg-blue-500/10 text-blue-400 border-blue-500/30'
|
||||||
|
}`}>
|
||||||
|
{user.role}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button onClick={() => { setIsUsersOpen(false); setCompanyUsers([]); }} className="bg-slate-800 hover:bg-slate-700 text-white font-semibold">
|
||||||
|
Schließen
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,3 +230,39 @@ export async function updateUserProfile(id: string, data: { first_name: string |
|
|||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
revalidatePath('/admin/users');
|
revalidatePath('/admin/users');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getUsersByCompany(companyId: string) {
|
||||||
|
const admin = createAdminClient();
|
||||||
|
const { data: dbUsers, error: dbError } = await admin
|
||||||
|
.from('users')
|
||||||
|
.select('id, role')
|
||||||
|
.eq('company_id', companyId);
|
||||||
|
if (dbError) throw dbError;
|
||||||
|
|
||||||
|
if (dbUsers.length === 0) return [];
|
||||||
|
|
||||||
|
const { data: { users }, error } = await admin.auth.admin.listUsers();
|
||||||
|
if (error) throw error;
|
||||||
|
|
||||||
|
const authUserMap = new Map(users.map((u) => [u.id, u]));
|
||||||
|
const userIds = dbUsers.map((u) => u.id);
|
||||||
|
|
||||||
|
const { data: dbProfiles } = await admin
|
||||||
|
.from('profiles')
|
||||||
|
.select('id, first_name, last_name')
|
||||||
|
.in('id', userIds);
|
||||||
|
|
||||||
|
const profileMap = new Map((dbProfiles || []).map((p) => [p.id, p]));
|
||||||
|
|
||||||
|
return dbUsers.map((u) => {
|
||||||
|
const authUser = authUserMap.get(u.id);
|
||||||
|
const profile = profileMap.get(u.id);
|
||||||
|
return {
|
||||||
|
id: u.id,
|
||||||
|
email: authUser?.email || '',
|
||||||
|
role: u.role,
|
||||||
|
first_name: profile?.first_name || '',
|
||||||
|
last_name: profile?.last_name || '',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user