feat: list end customers in companies admin and improve dark/light mode contrast across all admin components
All checks were successful
Staging Build / build (push) Successful in 2m36s

This commit is contained in:
DanielS
2026-06-25 02:11:07 +02:00
parent bf27c62f62
commit ef9422908d
11 changed files with 222 additions and 240 deletions

View File

@@ -13,8 +13,7 @@ import {
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
} from '@/components/ui/dialog' } from '@/components/ui/dialog'
import { getCompanies, createCompany, updateCompany, deleteCompany } from '@/lib/actions/companies' import { getCompanies, createCompany, updateCompany, deleteCompany, getEndCustomersByCompany } 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[]>([])
@@ -33,10 +32,10 @@ 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 [isCustomersOpen, setIsCustomersOpen] = useState(false)
const [selectedCompanyForUsers, setSelectedCompanyForUsers] = useState<any>(null) const [selectedCompanyForCustomers, setSelectedCompanyForCustomers] = useState<any>(null)
const [companyUsers, setCompanyUsers] = useState<any[]>([]) const [companyCustomers, setCompanyCustomers] = useState<any[]>([])
const [loadingUsers, setLoadingUsers] = useState(false) const [loadingCustomers, setLoadingCustomers] = useState(false)
const loadCompanies = async () => { const loadCompanies = async () => {
setLoading(true) setLoading(true)
@@ -91,17 +90,17 @@ export default function CompaniesPage() {
} }
} }
const handleShowUsers = async (company: any) => { const handleShowCustomers = async (company: any) => {
setSelectedCompanyForUsers(company) setSelectedCompanyForCustomers(company)
setIsUsersOpen(true) setIsCustomersOpen(true)
setLoadingUsers(true) setLoadingCustomers(true)
try { try {
const data = await getUsersByCompany(company.id) const data = await getEndCustomersByCompany(company.id)
setCompanyUsers(data) setCompanyCustomers(data)
} catch (err) { } catch (err) {
console.error('Fehler beim Laden der Benutzer:', err) console.error('Fehler beim Laden der Endkunden:', err)
} finally { } finally {
setLoadingUsers(false) setLoadingCustomers(false)
} }
} }
@@ -212,9 +211,9 @@ export default function CompaniesPage() {
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
onClick={() => handleShowUsers(company)} onClick={() => handleShowCustomers(company)}
className="h-8 w-8 text-indigo-500 hover:text-indigo-400 hover:bg-indigo-500/10" className="h-8 w-8 text-indigo-500 hover:text-indigo-400 hover:bg-indigo-500/10"
title="Benutzer anzeigen" title="Endkunden dieser Firma anzeigen"
> >
<Users className="h-4 w-4" /> <Users className="h-4 w-4" />
</Button> </Button>
@@ -342,60 +341,54 @@ export default function CompaniesPage() {
</DialogContent> </DialogContent>
</Dialog> </Dialog>
{/* Dialog: Benutzer der Firma anzeigen */} {/* Dialog: Endkunden der Firma anzeigen */}
<Dialog open={isUsersOpen} onOpenChange={(val) => { <Dialog open={isCustomersOpen} onOpenChange={(val) => {
setIsUsersOpen(val); setIsCustomersOpen(val);
if (!val) { if (!val) {
setCompanyUsers([]); setCompanyCustomers([]);
} }
}}> }}>
<DialogContent className="bg-white dark:bg-slate-950 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white shadow-2xl"> <DialogContent className="bg-white dark:bg-slate-950 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white shadow-2xl">
<DialogHeader> <DialogHeader>
<DialogTitle> <DialogTitle>
Benutzer von {selectedCompanyForUsers?.name} Endkunden von {selectedCompanyForCustomers?.name}
</DialogTitle> </DialogTitle>
<DialogDescription className="text-slate-500 dark:text-slate-400"> <DialogDescription className="text-slate-500 dark:text-slate-400">
Liste aller Benutzer, die dieser Firma zugewiesen sind. Liste aller Endkunden, die von den Partnern dieser Firma betreut werden.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="py-4"> <div className="py-4">
{loadingUsers ? ( {loadingCustomers ? (
<div className="flex justify-center items-center py-8"> <div className="flex justify-center items-center py-8">
<Loader2 className="w-6 h-6 text-indigo-500 animate-spin" /> <Loader2 className="w-6 h-6 text-indigo-500 animate-spin" />
<span className="ml-2 text-slate-400 text-sm">Lade Benutzer...</span> <span className="ml-2 text-slate-400 text-sm">Lade Endkunden...</span>
</div> </div>
) : companyUsers.length === 0 ? ( ) : companyCustomers.length === 0 ? (
<p className="text-center text-slate-500 text-sm py-8"> <p className="text-center text-slate-500 text-sm py-8">
Keine Benutzer für diese Firma registriert. Keine Endkunden für diese Firma registriert.
</p> </p>
) : ( ) : (
<div className="space-y-3 max-h-60 overflow-y-auto pr-1"> <div className="space-y-3 max-h-60 overflow-y-auto pr-1">
{companyUsers.map((user) => ( {companyCustomers.map((customer) => (
<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 key={customer.id} className="flex flex-col gap-1 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">
<p className="text-sm font-semibold text-slate-900 dark:text-white"> {customer.company_name}
{user.first_name || user.last_name ? `${user.first_name} ${user.last_name}` : 'Kein Name'} </p>
</p> <div className="flex justify-between items-center text-xs text-slate-500 dark:text-slate-400">
<p className="text-xs text-slate-500 dark:text-slate-400"> <span>
{user.email} {customer.first_name || customer.last_name ? `${customer.first_name || ''} ${customer.last_name || ''}`.trim() : 'Kein Ansprechpartner'}
</p> </span>
<span>
{customer.zip} {customer.city}
</span>
</div> </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> </div>
)} )}
</div> </div>
<DialogFooter> <DialogFooter>
<Button onClick={() => { setIsUsersOpen(false); setCompanyUsers([]); }} className="bg-slate-800 hover:bg-slate-700 text-white font-semibold"> <Button onClick={() => { setIsCustomersOpen(false); setCompanyCustomers([]); }} className="bg-slate-800 hover:bg-slate-700 text-white font-semibold">
Schließen Schließen
</Button> </Button>
</DialogFooter> </DialogFooter>

View File

@@ -25,9 +25,9 @@ export default async function AdminLayout({
} }
return ( return (
<div className="flex min-h-screen bg-[#020617] text-white"> <div className="flex min-h-screen bg-slate-50 dark:bg-[#020617] text-slate-900 dark:text-white">
{/* Sidebar */} {/* Sidebar */}
<aside className="w-64 border-r border-white/5 bg-black/20 backdrop-blur-xl flex flex-col"> <aside className="w-64 border-r border-slate-200 dark:border-white/5 bg-white dark:bg-black/20 backdrop-blur-xl flex flex-col">
<div className="p-6"> <div className="p-6">
<Link href="/" className="flex items-center gap-2"> <Link href="/" className="flex items-center gap-2">
<div className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center"> <div className="w-8 h-8 bg-primary rounded-lg flex items-center justify-center">
@@ -38,55 +38,55 @@ export default async function AdminLayout({
</div> </div>
<nav className="flex-1 px-4 space-y-2 py-4"> <nav className="flex-1 px-4 space-y-2 py-4">
<Link href="/admin" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-400 hover:text-white hover:bg-white/5 transition-all"> <Link href="/admin" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-white/5 transition-all">
<LayoutDashboard className="w-5 h-5" /> <LayoutDashboard className="w-5 h-5" />
Dashboard Dashboard
</Link> </Link>
<Link href="/admin/products" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-400 hover:text-white hover:bg-white/5 transition-all"> <Link href="/admin/products" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-white/5 transition-all">
<Package className="w-5 h-5" /> <Package className="w-5 h-5" />
Produkte Produkte
</Link> </Link>
<Link href="/admin/categories" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-400 hover:text-white hover:bg-white/5 transition-all"> <Link href="/admin/categories" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-white/5 transition-all">
<LayoutGrid className="w-5 h-5" /> <LayoutGrid className="w-5 h-5" />
Kategorien Kategorien
</Link> </Link>
<Link href="/admin/orders" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-400 hover:text-white hover:bg-white/5 transition-all"> <Link href="/admin/orders" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-white/5 transition-all">
<ShoppingCart className="w-5 h-5" /> <ShoppingCart className="w-5 h-5" />
Bestellungen Bestellungen
</Link> </Link>
<Link href="/admin/users" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-400 hover:text-white hover:bg-white/5 transition-all"> <Link href="/admin/users" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-white/5 transition-all">
<Users className="w-5 h-5" /> <Users className="w-5 h-5" />
Benutzer Benutzer
</Link> </Link>
<Link href="/admin/companies" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-400 hover:text-white hover:bg-white/5 transition-all"> <Link href="/admin/companies" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-white/5 transition-all">
<Building2 className="w-5 h-5" /> <Building2 className="w-5 h-5" />
Firmen Firmen
</Link> </Link>
<div className="pt-2 pb-1 px-3"> <div className="pt-2 pb-1 px-3">
<span className="text-[10px] font-semibold uppercase tracking-wider text-slate-600">Einstellungen</span> <span className="text-[10px] font-semibold uppercase tracking-wider text-slate-500 dark:text-slate-600">Einstellungen</span>
</div> </div>
<Link href="/admin/einstellungen" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-400 hover:text-white hover:bg-white/5 transition-all"> <Link href="/admin/einstellungen" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-white/5 transition-all">
<Settings className="w-5 h-5" /> <Settings className="w-5 h-5" />
Allgemein Allgemein
</Link> </Link>
<Link href="/admin/settings" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-400 hover:text-white hover:bg-white/5 transition-all"> <Link href="/admin/settings" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-white/5 transition-all">
<Wrench className="w-5 h-5" /> <Wrench className="w-5 h-5" />
SMTP SMTP
</Link> </Link>
<Link href="/admin/tools" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-400 hover:text-white hover:bg-white/5 transition-all"> <Link href="/admin/tools" className="flex items-center gap-3 px-3 py-2 rounded-lg text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-white/5 transition-all">
<Database className="w-5 h-5" /> <Database className="w-5 h-5" />
DB-Tools DB-Tools
</Link> </Link>
</nav> </nav>
<div className="p-4 border-t border-white/5 space-y-4"> <div className="p-4 border-t border-slate-200 dark:border-white/5 space-y-4">
<AdminLogoutButton /> <AdminLogoutButton />
<div className="px-3 pt-2 flex items-center gap-2 border-t border-white/5 opacity-60"> <div className="px-3 pt-2 flex items-center gap-2 border-t border-slate-200 dark:border-white/5 opacity-60">
<span className="font-bold text-sm tracking-tighter text-white">CASPOS Store</span> <span className="font-bold text-sm tracking-tighter text-slate-800 dark:text-white">CASPOS Store</span>
<DemoWrapper> <DemoWrapper>
<span className="text-[10px] bg-amber-500/20 text-amber-400 px-2 py-0.5 rounded-full font-semibold uppercase tracking-wider border border-amber-500/30"> <span className="text-[10px] bg-amber-500/20 text-amber-600 dark:text-amber-400 px-2 py-0.5 rounded-full font-semibold uppercase tracking-wider border border-amber-500/30">
Demo Demo
</span> </span>
</DemoWrapper> </DemoWrapper>

View File

@@ -99,10 +99,10 @@ export function CategoryDialog({
</Button> </Button>
)} )}
</DialogTrigger> </DialogTrigger>
<DialogContent className="glass-dark border-white/10 text-white shadow-2xl"> <DialogContent className="bg-white dark:bg-slate-950 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white shadow-2xl">
<DialogHeader> <DialogHeader>
<DialogTitle>{category ? 'Kategorie bearbeiten' : 'Neue Kategorie erstellen'}</DialogTitle> <DialogTitle>{category ? 'Kategorie bearbeiten' : 'Neue Kategorie erstellen'}</DialogTitle>
<DialogDescription className="text-slate-300"> <DialogDescription className="text-slate-500 dark:text-slate-400">
Definieren Sie eine Kategorie für Ihre Produkte. Definieren Sie eine Kategorie für Ihre Produkte.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -116,9 +116,9 @@ export function CategoryDialog({
name="name" name="name"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">Name</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Name</FormLabel>
<FormControl> <FormControl>
<Input placeholder="z.B. Software" className="bg-white/5 border-white/10 text-white" {...field} /> <Input placeholder="z.B. Software" className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white placeholder:text-slate-500" {...field} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -129,9 +129,9 @@ export function CategoryDialog({
name="description" name="description"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">Beschreibung</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Beschreibung</FormLabel>
<FormControl> <FormControl>
<Input placeholder="Kurze Beschreibung" className="bg-white/5 border-white/10 text-white" {...field} /> <Input placeholder="Kurze Beschreibung" className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white placeholder:text-slate-500" {...field} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -142,16 +142,16 @@ export function CategoryDialog({
name="icon" name="icon"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">Icon</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Icon</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}> <Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl> <FormControl>
<SelectTrigger className="bg-white/5 border-white/10 text-white"> <SelectTrigger className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white">
<SelectValue placeholder="Icon wählen" /> <SelectValue placeholder="Icon wählen" />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent className="glass-dark border-white/10 text-white"> <SelectContent className="bg-white dark:bg-slate-950 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white">
{icons.map(({ name, Icon }) => ( {icons.map(({ name, Icon }) => (
<SelectItem key={name} value={name} className="text-white hover:bg-white/10"> <SelectItem key={name} value={name}>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Icon className="w-4 h-4" /> <Icon className="w-4 h-4" />
{name} {name}
@@ -169,18 +169,18 @@ export function CategoryDialog({
name="sort_order" name="sort_order"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">Reihenfolge im Bestellvorgang</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Reihenfolge im Bestellvorgang</FormLabel>
<FormControl> <FormControl>
<Input <Input
type="number" type="number"
min={0} min={0}
placeholder="0 = zuerst" placeholder="0 = zuerst"
className="bg-white/5 border-white/10 text-white" className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white placeholder:text-slate-500"
{...field} {...field}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
<p className="text-xs text-slate-400">Kleinere Zahl = weiter oben im Bestellformular angezeigt.</p> <p className="text-xs text-slate-500 dark:text-slate-400">Kleinere Zahl = weiter oben im Bestellformular angezeigt.</p>
</FormItem> </FormItem>
)} )}
/> />
@@ -188,7 +188,7 @@ export function CategoryDialog({
control={form.control} control={form.control}
name="is_required" name="is_required"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-lg border border-white/10 bg-white/5 p-4"> <FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-lg border border-slate-200 dark:border-white/10 bg-slate-50 dark:bg-white/5 p-4">
<FormControl> <FormControl>
<Checkbox <Checkbox
checked={field.value} checked={field.value}
@@ -196,10 +196,10 @@ export function CategoryDialog({
/> />
</FormControl> </FormControl>
<div className="space-y-1 leading-none"> <div className="space-y-1 leading-none">
<FormLabel className="text-white font-semibold cursor-pointer"> <FormLabel className="text-slate-900 dark:text-white font-semibold cursor-pointer">
Pflichtauswahl im Bestellvorgang Pflichtauswahl im Bestellvorgang
</FormLabel> </FormLabel>
<p className="text-xs text-slate-400"> <p className="text-xs text-slate-500 dark:text-slate-400">
Wenn aktiv, muss der Kunde aus dieser Kategorie einen Artikel wählen. Wenn aktiv, muss der Kunde aus dieser Kategorie einen Artikel wählen.
Wenn deaktiviert, ist die Kategorie optional. Wenn deaktiviert, ist die Kategorie optional.
</p> </p>
@@ -210,7 +210,7 @@ export function CategoryDialog({
</div> </div>
</ScrollArea> </ScrollArea>
<DialogFooter className="pt-4"> <DialogFooter className="pt-4">
<Button type="submit" className="w-full bg-primary hover:bg-primary/90"> <Button type="submit" className="w-full bg-primary hover:bg-primary/90 text-white">
{category ? 'Aktualisieren' : 'Erstellen'} {category ? 'Aktualisieren' : 'Erstellen'}
</Button> </Button>
</DialogFooter> </DialogFooter>

View File

@@ -41,39 +41,39 @@ export function CategoryList({ initialCategories }: { initialCategories: Categor
} }
return ( return (
<Card className="glass-dark border-white/10 shadow-2xl overflow-hidden"> <Card className="bg-white dark:bg-slate-900/50 border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden text-slate-900 dark:text-white">
<CardHeader> <CardHeader>
<CardTitle>Kategorienübersicht</CardTitle> <CardTitle>Kategorienübersicht</CardTitle>
<CardDescription className="text-slate-400"> <CardDescription className="text-slate-500 dark:text-slate-400">
Verwalten Sie die Hauptkategorien für Ihre Produkte. Verwalten Sie die Hauptkategorien für Ihre Produkte.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow className="border-white/10 hover:bg-white/5"> <TableRow className="border-slate-200 dark:border-white/10 hover:bg-slate-50 dark:hover:bg-white/5">
<TableHead className="text-white font-bold w-10">Icon</TableHead> <TableHead className="text-slate-700 dark:text-white font-bold w-10">Icon</TableHead>
<TableHead className="text-white font-bold">Name</TableHead> <TableHead className="text-slate-700 dark:text-white font-bold">Name</TableHead>
<TableHead className="text-white font-bold">Beschreibung</TableHead> <TableHead className="text-slate-700 dark:text-white font-bold">Beschreibung</TableHead>
<TableHead className="text-white font-bold w-28"> <TableHead className="text-slate-700 dark:text-white font-bold w-28">
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<ArrowUpDown className="w-3 h-3" /> Reihenfolge <ArrowUpDown className="w-3 h-3" /> Reihenfolge
</span> </span>
</TableHead> </TableHead>
<TableHead className="text-white font-bold w-24">Typ</TableHead> <TableHead className="text-slate-700 dark:text-white font-bold w-24">Typ</TableHead>
<TableHead className="text-right text-white font-bold">Aktionen</TableHead> <TableHead className="text-right text-slate-700 dark:text-white font-bold">Aktionen</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{categories.map((category) => ( {categories.map((category) => (
<TableRow key={category.id} className="border-white/5 hover:bg-white/5 transition-colors"> <TableRow key={category.id} className="border-slate-100 dark:border-white/5 hover:bg-slate-50 dark:hover:bg-white/5 transition-colors">
<TableCell> <TableCell>
<div className="w-8 h-8 rounded-lg bg-primary/10 flex items-center justify-center text-primary"> <div className="w-8 h-8 rounded-lg bg-primary/10 flex items-center justify-center text-primary">
{getIcon(category.icon)} {getIcon(category.icon)}
</div> </div>
</TableCell> </TableCell>
<TableCell className="font-medium text-white">{category.name}</TableCell> <TableCell className="font-medium text-slate-900 dark:text-white">{category.name}</TableCell>
<TableCell className="text-slate-300">{category.description}</TableCell> <TableCell className="text-slate-700 dark:text-slate-300">{category.description}</TableCell>
<TableCell> <TableCell>
<span className="inline-flex items-center justify-center w-8 h-8 rounded-full bg-primary/20 text-primary text-sm font-bold"> <span className="inline-flex items-center justify-center w-8 h-8 rounded-full bg-primary/20 text-primary text-sm font-bold">
{category.sort_order} {category.sort_order}
@@ -81,15 +81,15 @@ export function CategoryList({ initialCategories }: { initialCategories: Categor
</TableCell> </TableCell>
<TableCell> <TableCell>
{category.is_required ? ( {category.is_required ? (
<Badge className="bg-green-500/20 text-green-400 border border-green-500/30 whitespace-nowrap">Pflicht</Badge> <Badge className="bg-green-500/20 text-green-600 dark:text-green-400 border border-green-500/30 whitespace-nowrap">Pflicht</Badge>
) : ( ) : (
<Badge variant="outline" className="border-white/20 text-slate-400 whitespace-nowrap">Optional</Badge> <Badge variant="outline" className="border-slate-200 dark:border-white/20 text-slate-500 dark:text-slate-400 whitespace-nowrap">Optional</Badge>
)} )}
</TableCell> </TableCell>
<TableCell className="text-right"> <TableCell className="text-right">
<div className="flex justify-end gap-2"> <div className="flex justify-end gap-2">
<CategoryDialog category={category}> <CategoryDialog category={category}>
<Button variant="ghost" size="icon" className="h-8 w-8 text-muted-foreground hover:text-white"> <Button variant="ghost" size="icon" className="h-8 w-8 text-slate-500 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white">
<Edit2 className="h-4 w-4" /> <Edit2 className="h-4 w-4" />
</Button> </Button>
</CategoryDialog> </CategoryDialog>
@@ -107,7 +107,7 @@ export function CategoryList({ initialCategories }: { initialCategories: Categor
))} ))}
{categories.length === 0 && ( {categories.length === 0 && (
<TableRow> <TableRow>
<TableCell colSpan={6} className="text-center py-10 text-slate-400"> <TableCell colSpan={6} className="text-center py-10 text-slate-500 dark:text-slate-400">
Keine Kategorien gefunden. Keine Kategorien gefunden.
</TableCell> </TableCell>
</TableRow> </TableRow>

View File

@@ -120,12 +120,12 @@ export function CreateProductDialog({ children, categories, product }: { childre
</Button> </Button>
)} )}
</DialogTrigger> </DialogTrigger>
<DialogContent className="sm:max-w-[700px] max-h-[90vh] overflow-hidden flex flex-col glass-dark border-white/10 text-white shadow-2xl"> <DialogContent className="sm:max-w-[700px] max-h-[90vh] overflow-hidden flex flex-col bg-white dark:bg-slate-950 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white shadow-2xl">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl font-bold"> <DialogTitle className="text-2xl font-bold">
{product ? 'Produkt bearbeiten' : 'Neues Produkt erstellen'} {product ? 'Produkt bearbeiten' : 'Neues Produkt erstellen'}
</DialogTitle> </DialogTitle>
<DialogDescription className="text-slate-300"> <DialogDescription className="text-slate-500 dark:text-slate-400">
Definieren Sie das Basisprodukt und fügen Sie optionale oder erforderliche Module hinzu. Definieren Sie das Basisprodukt und fügen Sie optionale oder erforderliche Module hinzu.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -140,9 +140,9 @@ export function CreateProductDialog({ children, categories, product }: { childre
name="name" name="name"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">Produktname</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Produktname</FormLabel>
<FormControl> <FormControl>
<Input placeholder="z.B. Basic ERP" className="bg-white/5 border-white/10 text-white" {...field} /> <Input placeholder="z.B. Basic ERP" className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white placeholder:text-slate-500" {...field} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -153,9 +153,9 @@ export function CreateProductDialog({ children, categories, product }: { childre
name="base_price" name="base_price"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">Basispreis ()</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Basispreis ()</FormLabel>
<FormControl> <FormControl>
<Input type="number" step="0.01" className="bg-white/5 border-white/10 text-white" {...field} /> <Input type="number" step="0.01" className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white placeholder:text-slate-500" {...field} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -168,16 +168,16 @@ export function CreateProductDialog({ children, categories, product }: { childre
name="category_id" name="category_id"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">Kategorie</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Kategorie</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}> <Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl> <FormControl>
<SelectTrigger className="bg-white/5 border-white/10 text-white"> <SelectTrigger className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white">
<SelectValue placeholder="Kategorie wählen" className="text-white" /> <SelectValue placeholder="Kategorie wählen" />
</SelectTrigger> </SelectTrigger>
</FormControl> </FormControl>
<SelectContent className="glass-dark border-white/10 text-white"> <SelectContent className="bg-white dark:bg-slate-950 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white">
{categories.map(cat => ( {categories.map(cat => (
<SelectItem key={cat.id} value={cat.id} className="text-white">{cat.name}</SelectItem> <SelectItem key={cat.id} value={cat.id}>{cat.name}</SelectItem>
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
@@ -191,7 +191,7 @@ export function CreateProductDialog({ children, categories, product }: { childre
name="billing_interval" name="billing_interval"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">Abrechnungsmodell</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Abrechnungsmodell</FormLabel>
<div className="grid grid-cols-2 gap-2 pt-1"> <div className="grid grid-cols-2 gap-2 pt-1">
{[ {[
{ value: 'one_time', label: 'Einmalig', icon: '💳' }, { value: 'one_time', label: 'Einmalig', icon: '💳' },
@@ -203,8 +203,8 @@ export function CreateProductDialog({ children, categories, product }: { childre
onClick={() => field.onChange(opt.value)} onClick={() => field.onChange(opt.value)}
className={`flex flex-col items-center gap-1 p-3 rounded-xl border-2 text-sm font-medium transition-all ${ className={`flex flex-col items-center gap-1 p-3 rounded-xl border-2 text-sm font-medium transition-all ${
field.value === opt.value field.value === opt.value
? 'border-primary bg-primary/10 text-white' ? 'border-primary bg-primary/10 text-primary dark:text-white'
: 'border-white/10 bg-white/5 text-slate-400 hover:bg-white/10' : 'border-slate-200 dark:border-white/10 bg-slate-50 dark:bg-white/5 text-slate-500 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-white/10'
}`} }`}
> >
<span className="text-lg">{opt.icon}</span> <span className="text-lg">{opt.icon}</span>
@@ -222,16 +222,16 @@ export function CreateProductDialog({ children, categories, product }: { childre
name="description" name="description"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">Beschreibung</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Beschreibung</FormLabel>
<FormControl> <FormControl>
<Input placeholder="Kurze Beschreibung des Produkts" className="bg-white/5 border-white/10 text-white placeholder:text-slate-400" {...field} /> <Input placeholder="Kurze Beschreibung des Produkts" className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white placeholder:text-slate-500" {...field} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
)} )}
/> />
<Separator className="bg-white/10" /> <Separator className="bg-slate-200 dark:bg-white/10" />
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@@ -268,7 +268,7 @@ export function CreateProductDialog({ children, categories, product }: { childre
.filter((_, idx) => idx !== index) .filter((_, idx) => idx !== index)
return ( return (
<div key={field.id} className="p-4 rounded-lg bg-white/5 border border-white/10 space-y-4 relative group"> <div key={field.id} className="p-4 rounded-lg bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 space-y-4 relative group">
<Button <Button
type="button" type="button"
variant="ghost" variant="ghost"
@@ -285,9 +285,9 @@ export function CreateProductDialog({ children, categories, product }: { childre
name={`modules.${index}.name`} name={`modules.${index}.name`}
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">Modulname</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Modulname</FormLabel>
<FormControl> <FormControl>
<Input placeholder="z.B. Cloud Storage" className="bg-white/10 border-white/10 text-white placeholder:text-slate-400" {...field} /> <Input placeholder="z.B. Cloud Storage" className="bg-slate-100 dark:bg-white/10 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white placeholder:text-slate-500" {...field} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -298,9 +298,9 @@ export function CreateProductDialog({ children, categories, product }: { childre
name={`modules.${index}.price`} name={`modules.${index}.price`}
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">Aufpreis ()</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Aufpreis ()</FormLabel>
<FormControl> <FormControl>
<Input type="number" step="0.01" className="bg-white/10 border-white/10 text-white" {...field} /> <Input type="number" step="0.01" className="bg-slate-100 dark:bg-white/10 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white placeholder:text-slate-500" {...field} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -308,15 +308,15 @@ export function CreateProductDialog({ children, categories, product }: { childre
/> />
</div> </div>
<div className="grid grid-cols-2 gap-4 border border-white/10 rounded-lg p-3 bg-black/20"> <div className="grid grid-cols-2 gap-4 border border-slate-200 dark:border-white/10 rounded-lg p-3 bg-slate-100 dark:bg-black/20">
{/* Requirements Selection Matrix */} {/* Requirements Selection Matrix */}
<FormField <FormField
control={form.control} control={form.control}
name={`modules.${index}.requirements`} name={`modules.${index}.requirements`}
render={({ field: reqField }) => ( render={({ field: reqField }) => (
<div className="space-y-2"> <div className="space-y-2">
<FormLabel className="text-white text-xs font-semibold">Benötigt Module</FormLabel> <FormLabel className="text-slate-900 dark:text-white text-xs font-semibold">Benötigt Module</FormLabel>
<ScrollArea className="h-28 border border-white/5 rounded p-2 bg-white/5"> <ScrollArea className="h-28 border border-slate-200 dark:border-white/5 rounded p-2 bg-slate-50 dark:bg-white/5">
{otherModules.length === 0 ? ( {otherModules.length === 0 ? (
<p className="text-[10px] text-slate-500 italic">Keine anderen Module vorhanden.</p> <p className="text-[10px] text-slate-500 italic">Keine anderen Module vorhanden.</p>
) : ( ) : (
@@ -339,7 +339,7 @@ export function CreateProductDialog({ children, categories, product }: { childre
/> />
<label <label
htmlFor={`req-${index}-${other.id}`} htmlFor={`req-${index}-${other.id}`}
className="text-xs text-slate-300 cursor-pointer select-none truncate block max-w-[180px]" className="text-xs text-slate-700 dark:text-slate-300 cursor-pointer select-none truncate block max-w-[180px]"
title={other.name} title={other.name}
> >
{other.name} {other.name}
@@ -360,8 +360,8 @@ export function CreateProductDialog({ children, categories, product }: { childre
name={`modules.${index}.exclusions`} name={`modules.${index}.exclusions`}
render={({ field: exclField }) => ( render={({ field: exclField }) => (
<div className="space-y-2"> <div className="space-y-2">
<FormLabel className="text-white text-xs font-semibold">Schließt aus</FormLabel> <FormLabel className="text-slate-900 dark:text-white text-xs font-semibold">Schließt aus</FormLabel>
<ScrollArea className="h-28 border border-white/5 rounded p-2 bg-white/5"> <ScrollArea className="h-28 border border-slate-200 dark:border-white/5 rounded p-2 bg-slate-50 dark:bg-white/5">
{otherModules.length === 0 ? ( {otherModules.length === 0 ? (
<p className="text-[10px] text-slate-500 italic">Keine anderen Module vorhanden.</p> <p className="text-[10px] text-slate-500 italic">Keine anderen Module vorhanden.</p>
) : ( ) : (
@@ -384,7 +384,7 @@ export function CreateProductDialog({ children, categories, product }: { childre
/> />
<label <label
htmlFor={`excl-${index}-${other.id}`} htmlFor={`excl-${index}-${other.id}`}
className="text-xs text-slate-300 cursor-pointer select-none truncate block max-w-[180px]" className="text-xs text-slate-700 dark:text-slate-300 cursor-pointer select-none truncate block max-w-[180px]"
title={other.name} title={other.name}
> >
{other.name} {other.name}
@@ -413,7 +413,7 @@ export function CreateProductDialog({ children, categories, product }: { childre
/> />
</FormControl> </FormControl>
<div className="space-y-1 leading-none"> <div className="space-y-1 leading-none">
<FormLabel className="text-white">Erforderlich</FormLabel> <FormLabel className="text-slate-900 dark:text-white font-medium cursor-pointer">Erforderlich</FormLabel>
</div> </div>
</FormItem> </FormItem>
)} )}
@@ -424,7 +424,7 @@ export function CreateProductDialog({ children, categories, product }: { childre
})} })}
{fields.length === 0 && ( {fields.length === 0 && (
<div className="text-center py-6 border-2 border-dashed border-white/5 rounded-lg text-slate-400 text-sm"> <div className="text-center py-6 border-2 border-dashed border-slate-200 dark:border-white/5 rounded-lg text-slate-500 dark:text-slate-400 text-sm">
Noch keine Module hinzugefügt. Noch keine Module hinzugefügt.
</div> </div>
)} )}
@@ -432,9 +432,9 @@ export function CreateProductDialog({ children, categories, product }: { childre
</div> </div>
</ScrollArea> </ScrollArea>
<DialogFooter className="pt-4 border-t border-white/10"> <DialogFooter className="pt-4 border-t border-slate-200 dark:border-white/10">
<Button type="button" variant="ghost" onClick={() => setOpen(false)}>Abbrechen</Button> <Button type="button" variant="ghost" onClick={() => setOpen(false)} className="text-slate-500 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white">Abbrechen</Button>
<Button type="submit" className="bg-primary hover:bg-primary/90">Speichern</Button> <Button type="submit" className="bg-primary hover:bg-primary/90 text-white">Speichern</Button>
</DialogFooter> </DialogFooter>
</form> </form>
</Form> </Form>

View File

@@ -42,7 +42,7 @@ export function AdminLogoutButton() {
return ( return (
<button <button
onClick={handleLogout} onClick={handleLogout}
className="flex w-full items-center gap-3 px-3 py-2 rounded-lg text-slate-400 hover:text-white hover:bg-white/5 transition-all text-left" className="flex w-full items-center gap-3 px-3 py-2 rounded-lg text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-white/5 transition-all text-left"
> >
<LogOut className="w-5 h-5" /> <LogOut className="w-5 h-5" />
Abmelden Abmelden

View File

@@ -32,28 +32,28 @@ export function ProductList({ initialProducts, categories }: { initialProducts:
} }
return ( return (
<Card className="glass-dark border-white/10 shadow-2xl overflow-hidden"> <Card className="bg-white dark:bg-slate-900/50 border border-slate-200 dark:border-white/10 shadow-sm overflow-hidden text-slate-900 dark:text-white">
<CardHeader> <CardHeader>
<CardTitle>Produktübersicht</CardTitle> <CardTitle>Produktübersicht</CardTitle>
<CardDescription className="text-slate-400"> <CardDescription className="text-slate-500 dark:text-slate-400">
Alle konfigurierten Software-Lösungen und Erweiterungen. Alle konfigurierten Software-Lösungen und Erweiterungen.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow className="border-white/10 hover:bg-white/5"> <TableRow className="border-slate-200 dark:border-white/10 hover:bg-slate-50 dark:hover:bg-white/5">
<TableHead className="text-white font-bold">Name</TableHead> <TableHead className="text-slate-700 dark:text-white font-bold">Name</TableHead>
<TableHead className="text-white font-bold">Basispreis</TableHead> <TableHead className="text-slate-700 dark:text-white font-bold">Basispreis</TableHead>
<TableHead className="text-white font-bold">Module</TableHead> <TableHead className="text-slate-700 dark:text-white font-bold">Module</TableHead>
<TableHead className="text-white font-bold">Status</TableHead> <TableHead className="text-slate-700 dark:text-white font-bold">Status</TableHead>
<TableHead className="text-right text-white font-bold">Aktionen</TableHead> <TableHead className="text-right text-slate-700 dark:text-white font-bold">Aktionen</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{products.map((product) => ( {products.map((product) => (
<TableRow key={product.id} className="border-white/5 hover:bg-white/5 transition-colors"> <TableRow key={product.id} className="border-slate-100 dark:border-white/5 hover:bg-slate-50 dark:hover:bg-white/5 transition-colors">
<TableCell className="font-medium text-white"> <TableCell className="font-medium text-slate-900 dark:text-white">
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{product.name} {product.name}
@@ -63,20 +63,20 @@ export function ProductList({ initialProducts, categories }: { initialProducts:
</Badge> </Badge>
)} )}
{product.billing_interval === 'monthly' && ( {product.billing_interval === 'monthly' && (
<Badge className="text-[10px] bg-blue-500/20 text-blue-400 border border-blue-500/30 py-0 h-4">Abo/Monat</Badge> <Badge className="text-[10px] bg-blue-500/20 text-blue-600 dark:text-blue-400 border border-blue-500/30 py-0 h-4">Abo/Monat</Badge>
)} )}
{product.billing_interval === 'one_time' && ( {product.billing_interval === 'one_time' && (
<Badge className="text-[10px] bg-slate-500/20 text-slate-300 border border-slate-500/30 py-0 h-4">Einmalig</Badge> <Badge className="text-[10px] bg-slate-500/20 text-slate-600 dark:text-slate-300 border border-slate-500/30 py-0 h-4">Einmalig</Badge>
)} )}
</div> </div>
<p className="text-xs text-slate-300 font-normal">{product.description}</p> <p className="text-xs text-slate-500 dark:text-slate-400 font-normal">{product.description}</p>
</div> </div>
</TableCell> </TableCell>
<TableCell className="text-white font-semibold">{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(product.base_price)}</TableCell> <TableCell className="text-slate-900 dark:text-white font-semibold">{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(product.base_price)}</TableCell>
<TableCell> <TableCell>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Layers className="w-3 h-3 text-primary" /> <Layers className="w-3 h-3 text-primary" />
<span className="text-slate-200">{product.modules?.length || 0} Module</span> <span className="text-slate-700 dark:text-slate-200">{product.modules?.length || 0} Module</span>
</div> </div>
</TableCell> </TableCell>
<TableCell> <TableCell>
@@ -87,7 +87,7 @@ export function ProductList({ initialProducts, categories }: { initialProducts:
<TableCell className="text-right"> <TableCell className="text-right">
<div className="flex justify-end gap-2"> <div className="flex justify-end gap-2">
<CreateProductDialog categories={categories} product={product}> <CreateProductDialog categories={categories} product={product}>
<Button variant="ghost" size="icon" className="h-8 w-8 text-muted-foreground hover:text-white"> <Button variant="ghost" size="icon" className="h-8 w-8 text-slate-500 hover:text-slate-900 dark:text-slate-400 dark:hover:text-white">
<Edit2 className="h-4 w-4" /> <Edit2 className="h-4 w-4" />
</Button> </Button>
</CreateProductDialog> </CreateProductDialog>
@@ -105,7 +105,7 @@ export function ProductList({ initialProducts, categories }: { initialProducts:
))} ))}
{products.length === 0 && ( {products.length === 0 && (
<TableRow> <TableRow>
<TableCell colSpan={5} className="text-center py-10 text-slate-400"> <TableCell colSpan={5} className="text-center py-10 text-slate-500 dark:text-slate-400">
Keine Produkte gefunden. Erstellen Sie Ihr erstes Produkt! Keine Produkte gefunden. Erstellen Sie Ihr erstes Produkt!
</TableCell> </TableCell>
</TableRow> </TableRow>

View File

@@ -121,10 +121,10 @@ export function UserDialog({ companies }: UserDialogProps) {
<Plus className="mr-2 h-4 w-4" /> Benutzer neu anlegen <Plus className="mr-2 h-4 w-4" /> Benutzer neu anlegen
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="glass-dark border-white/10 text-white shadow-2xl"> <DialogContent className="bg-white dark:bg-slate-950 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white shadow-2xl">
<DialogHeader> <DialogHeader>
<DialogTitle>Neuen Benutzer anlegen</DialogTitle> <DialogTitle>Neuen Benutzer anlegen</DialogTitle>
<DialogDescription className="text-slate-300"> <DialogDescription className="text-slate-500 dark:text-slate-400">
Geben Sie die E-Mail, die Rolle und optional die Firma für den neuen Benutzer ein. Nach der Erstellung erhält der Benutzer eine E-Mail zum Festlegen seines Passworts. Geben Sie die E-Mail, die Rolle und optional die Firma für den neuen Benutzer ein. Nach der Erstellung erhält der Benutzer eine E-Mail zum Festlegen seines Passworts.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -136,9 +136,9 @@ export function UserDialog({ companies }: UserDialogProps) {
name="email" name="email"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">E-Mail</FormLabel> <FormLabel className="text-slate-900 dark:text-white">E-Mail</FormLabel>
<FormControl> <FormControl>
<Input placeholder="user@example.com" className="bg-white/5 border-white/10 text-white" {...field} /> <Input placeholder="user@example.com" className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white placeholder:text-slate-500" {...field} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -150,9 +150,9 @@ export function UserDialog({ companies }: UserDialogProps) {
name="first_name" name="first_name"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">Vorname</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Vorname</FormLabel>
<FormControl> <FormControl>
<Input placeholder="Vorname" className="bg-white/5 border-white/10 text-white" {...field} /> <Input placeholder="Vorname" className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white placeholder:text-slate-500" {...field} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -163,9 +163,9 @@ export function UserDialog({ companies }: UserDialogProps) {
name="last_name" name="last_name"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">Nachname</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Nachname</FormLabel>
<FormControl> <FormControl>
<Input placeholder="Nachname" className="bg-white/5 border-white/10 text-white" {...field} /> <Input placeholder="Nachname" className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white placeholder:text-slate-500" {...field} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@@ -177,14 +177,14 @@ export function UserDialog({ companies }: UserDialogProps) {
name="role" name="role"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-white">Rolle</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Rolle</FormLabel>
<FormControl> <FormControl>
<select <select
className="flex h-10 w-full rounded-md border border-white/10 bg-slate-950/80 px-3 py-2 text-sm text-white focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2" className="flex h-10 w-full rounded-md border border-slate-200 dark:border-white/10 bg-slate-50 dark:bg-slate-950/80 px-3 py-2 text-sm text-slate-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2"
{...field} {...field}
> >
<option value="partner">Partner</option> <option value="partner" className="bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100">Partner</option>
<option value="admin">Admin</option> <option value="admin" className="bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100">Admin</option>
</select> </select>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -198,7 +198,7 @@ export function UserDialog({ companies }: UserDialogProps) {
const selectedCompany = localCompanies.find((c) => c.id === field.value) const selectedCompany = localCompanies.find((c) => c.id === field.value)
return ( return (
<FormItem className="flex flex-col"> <FormItem className="flex flex-col">
<FormLabel className="text-white">Firma</FormLabel> <FormLabel className="text-slate-900 dark:text-white">Firma</FormLabel>
<FormControl> <FormControl>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
<div className="relative" ref={dropdownRef}> <div className="relative" ref={dropdownRef}>
@@ -210,20 +210,20 @@ export function UserDialog({ companies }: UserDialogProps) {
setDropdownOpen(!dropdownOpen) setDropdownOpen(!dropdownOpen)
setSearch('') setSearch('')
}} }}
className="w-full text-left text-sm bg-white/5 border border-white/10 rounded-md px-3 py-2 text-slate-300 hover:bg-white/10 cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 max-w-full truncate" className="w-full text-left text-sm bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-md px-3 py-2 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-white/10 cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 max-w-full truncate"
> >
{selectedCompany ? selectedCompany.name : 'Keine Firma'} {selectedCompany ? selectedCompany.name : 'Keine Firma'}
</button> </button>
</div> </div>
{dropdownOpen && ( {dropdownOpen && (
<div className="absolute z-50 mt-1 left-0 w-full bg-slate-900 border border-white/10 rounded-lg shadow-xl overflow-hidden"> <div className="absolute z-50 mt-1 left-0 w-full bg-white dark:bg-slate-900 border border-slate-200 dark:border-white/10 rounded-lg shadow-xl overflow-hidden">
<div className="p-2 border-b border-white/10"> <div className="p-2 border-b border-slate-200 dark:border-white/10">
<input <input
type="text" type="text"
placeholder="Firma suchen..." placeholder="Firma suchen..."
value={search} value={search}
onChange={(e) => setSearch(e.target.value)} onChange={(e) => setSearch(e.target.value)}
className="w-full text-xs bg-white/5 border border-white/10 rounded px-2 py-1.5 text-slate-200 placeholder-slate-500 focus:outline-none focus:ring-1 focus:ring-primary/50" className="w-full text-xs bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded px-2 py-1.5 text-slate-900 dark:text-slate-200 placeholder-slate-500 focus:outline-none focus:ring-1 focus:ring-primary/50"
autoFocus autoFocus
/> />
</div> </div>
@@ -235,8 +235,8 @@ export function UserDialog({ companies }: UserDialogProps) {
setDropdownOpen(false) setDropdownOpen(false)
setSearch('') setSearch('')
}} }}
className={`w-full text-left text-xs px-3 py-2 hover:bg-white/10 transition-colors ${ className={`w-full text-left text-xs px-3 py-2 hover:bg-slate-100 dark:hover:bg-white/10 transition-colors ${
!field.value ? 'text-primary font-semibold' : 'text-slate-300' !field.value ? 'text-primary font-semibold' : 'text-slate-700 dark:text-slate-300'
}`} }`}
> >
Keine Firma Keine Firma
@@ -252,8 +252,8 @@ export function UserDialog({ companies }: UserDialogProps) {
setDropdownOpen(false) setDropdownOpen(false)
setSearch('') setSearch('')
}} }}
className={`w-full text-left text-xs px-3 py-2 hover:bg-white/10 transition-colors ${ className={`w-full text-left text-xs px-3 py-2 hover:bg-slate-100 dark:hover:bg-white/10 transition-colors ${
field.value === c.id ? 'text-primary font-semibold' : 'text-slate-300' field.value === c.id ? 'text-primary font-semibold' : 'text-slate-700 dark:text-slate-300'
}`} }`}
> >
{c.name} {c.name}
@@ -270,7 +270,7 @@ export function UserDialog({ companies }: UserDialogProps) {
placeholder="Name der neuen Firma" placeholder="Name der neuen Firma"
value={newCompanyName} value={newCompanyName}
onChange={(e) => setNewCompanyName(e.target.value)} onChange={(e) => setNewCompanyName(e.target.value)}
className="bg-white/5 border-white/10 text-white text-xs h-8" className="bg-slate-50 dark:bg-white/5 border-slate-200 dark:border-white/10 text-slate-900 dark:text-white text-xs h-8"
/> />
<Button <Button
type="button" type="button"
@@ -307,7 +307,7 @@ export function UserDialog({ companies }: UserDialogProps) {
setShowNewCompanyInput(false) setShowNewCompanyInput(false)
setNewCompanyName('') setNewCompanyName('')
}} }}
className="text-xs h-8 text-slate-400" className="text-xs h-8 text-slate-500 dark:text-slate-400"
> >
Abbrechen Abbrechen
</Button> </Button>
@@ -329,7 +329,7 @@ export function UserDialog({ companies }: UserDialogProps) {
}} }}
/> />
<DialogFooter> <DialogFooter>
<Button type="submit" className="w-full bg-primary hover:bg-primary/90"> <Button type="submit" className="w-full bg-primary hover:bg-primary/90 text-white">
Benutzer erstellen Benutzer erstellen
</Button> </Button>
</DialogFooter> </DialogFooter>

View File

@@ -147,10 +147,10 @@ export function UserList({ initialUsers, companies }: UserListProps) {
let lastCompanyName: string | null = null let lastCompanyName: string | null = null
return ( return (
<Card className="glass-dark border-white/10 shadow-2xl overflow-visible"> <Card className="bg-white dark:bg-slate-900/50 border border-slate-200 dark:border-white/10 shadow-sm overflow-visible text-slate-900 dark:text-white">
<CardHeader> <CardHeader>
<CardTitle>Benutzerverwaltung</CardTitle> <CardTitle>Benutzerverwaltung</CardTitle>
<CardDescription className="text-slate-400"> <CardDescription className="text-slate-500 dark:text-slate-400">
Rollen, Firmenzuweisungen, Passwörter und Benutzerkonten verwalten. Rollen, Firmenzuweisungen, Passwörter und Benutzerkonten verwalten.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
@@ -163,31 +163,31 @@ export function UserList({ initialUsers, companies }: UserListProps) {
placeholder="Benutzer suchen (E-Mail, Firma, Rolle)..." placeholder="Benutzer suchen (E-Mail, Firma, Rolle)..."
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
className="w-full pl-9 pr-4 py-2 text-sm bg-white/5 border border-white/10 rounded-md text-slate-200 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:ring-offset-slate-950 transition-all" className="w-full pl-9 pr-4 py-2 text-sm bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-md text-slate-900 dark:text-slate-200 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:ring-offset-slate-50 dark:focus:ring-offset-slate-950 transition-all"
/> />
</div> </div>
<div className="flex flex-wrap items-center gap-3"> <div className="flex flex-wrap items-center gap-3">
<div className="flex items-center gap-2 bg-white/5 border border-white/10 rounded-md px-3 py-2 cursor-pointer hover:bg-white/10 transition-colors"> <div className="flex items-center gap-2 bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-md px-3 py-2 cursor-pointer hover:bg-slate-100 dark:hover:bg-white/10 transition-colors">
<input <input
type="checkbox" type="checkbox"
id="hideLocked" id="hideLocked"
checked={hideLocked} checked={hideLocked}
onChange={(e) => setHideLocked(e.target.checked)} onChange={(e) => setHideLocked(e.target.checked)}
className="h-4 w-4 rounded border-white/10 bg-slate-950 text-primary focus:ring-primary focus:ring-offset-slate-950 accent-primary cursor-pointer" className="h-4 w-4 rounded border-slate-200 dark:border-white/10 bg-white dark:bg-slate-950 text-primary focus:ring-primary focus:ring-offset-slate-50 dark:focus:ring-offset-slate-950 accent-primary cursor-pointer"
/> />
<label htmlFor="hideLocked" className="text-xs text-slate-300 font-medium cursor-pointer select-none"> <label htmlFor="hideLocked" className="text-xs text-slate-700 dark:text-slate-300 font-medium cursor-pointer select-none">
Gesperrte ausblenden Gesperrte ausblenden
</label> </label>
</div> </div>
<div className="flex items-center gap-2 bg-white/5 border border-white/10 rounded-md px-3 py-2 cursor-pointer hover:bg-white/10 transition-colors"> <div className="flex items-center gap-2 bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded-md px-3 py-2 cursor-pointer hover:bg-slate-100 dark:hover:bg-white/10 transition-colors">
<input <input
type="checkbox" type="checkbox"
id="groupByCompany" id="groupByCompany"
checked={groupByCompany} checked={groupByCompany}
onChange={(e) => setGroupByCompany(e.target.checked)} onChange={(e) => setGroupByCompany(e.target.checked)}
className="h-4 w-4 rounded border-white/10 bg-slate-950 text-primary focus:ring-primary focus:ring-offset-slate-950 accent-primary cursor-pointer" className="h-4 w-4 rounded border-slate-200 dark:border-white/10 bg-white dark:bg-slate-950 text-primary focus:ring-primary focus:ring-offset-slate-50 dark:focus:ring-offset-slate-950 accent-primary cursor-pointer"
/> />
<label htmlFor="groupByCompany" className="text-xs text-slate-300 font-medium cursor-pointer select-none"> <label htmlFor="groupByCompany" className="text-xs text-slate-700 dark:text-slate-300 font-medium cursor-pointer select-none">
Nach Firma gruppieren Nach Firma gruppieren
</label> </label>
</div> </div>
@@ -195,12 +195,12 @@ export function UserList({ initialUsers, companies }: UserListProps) {
</div> </div>
<Table containerClassName="overflow-visible"> <Table containerClassName="overflow-visible">
<TableHeader> <TableHeader>
<TableRow className="border-white/10 hover:bg-white/5"> <TableRow className="border-slate-200 dark:border-white/10 hover:bg-slate-50 dark:hover:bg-white/5">
<TableHead className="text-slate-200">E-Mail</TableHead> <TableHead className="text-slate-700 dark:text-slate-200">E-Mail</TableHead>
<TableHead className="text-slate-200">Rolle</TableHead> <TableHead className="text-slate-700 dark:text-slate-200">Rolle</TableHead>
<TableHead className="text-slate-200">Firma</TableHead> <TableHead className="text-slate-700 dark:text-slate-200">Firma</TableHead>
<TableHead className="text-slate-200">Zuletzt angemeldet</TableHead> <TableHead className="text-slate-700 dark:text-slate-200">Zuletzt angemeldet</TableHead>
<TableHead className="text-right text-slate-200">Aktionen</TableHead> <TableHead className="text-right text-slate-700 dark:text-slate-200">Aktionen</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
@@ -217,14 +217,14 @@ export function UserList({ initialUsers, companies }: UserListProps) {
return ( return (
<Fragment key={user.id}> <Fragment key={user.id}>
{showGroupHeader && ( {showGroupHeader && (
<TableRow className="bg-white/5 border-y border-white/10 hover:bg-white/5"> <TableRow className="bg-slate-50 dark:bg-white/5 border-y border-slate-200 dark:border-white/10 hover:bg-slate-100 dark:hover:bg-white/5">
<TableCell colSpan={5} className="font-semibold text-primary py-2 px-4 text-xs tracking-wider uppercase"> <TableCell colSpan={5} className="font-semibold text-primary py-2 px-4 text-xs tracking-wider uppercase">
Firma: {currentCompanyName} Firma: {currentCompanyName}
</TableCell> </TableCell>
</TableRow> </TableRow>
)} )}
<TableRow className="border-white/5 hover:bg-white/5 transition-colors"> <TableRow className="border-slate-150 dark:border-white/5 hover:bg-slate-50 dark:hover:bg-white/5 transition-colors">
<TableCell className="font-medium text-white"> <TableCell className="font-medium text-slate-900 dark:text-white">
<div className="flex flex-col"> <div className="flex flex-col">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Mail className="w-4 h-4 text-slate-500" /> <Mail className="w-4 h-4 text-slate-500" />
@@ -251,7 +251,7 @@ export function UserList({ initialUsers, companies }: UserListProps) {
(e.target as HTMLInputElement).blur() (e.target as HTMLInputElement).blur()
} }
}} }}
className="bg-transparent border-b border-transparent hover:border-white/20 focus:border-primary focus:outline-none text-xs text-slate-400 w-20 px-1 py-0.5 transition-all placeholder:text-slate-600 placeholder:italic" className="bg-transparent border-b border-transparent hover:border-slate-300 dark:hover:border-white/20 focus:border-primary focus:outline-none text-xs text-slate-600 dark:text-slate-400 w-20 px-1 py-0.5 transition-all placeholder:text-slate-500 placeholder:italic"
/> />
<input <input
type="text" type="text"
@@ -273,7 +273,7 @@ export function UserList({ initialUsers, companies }: UserListProps) {
(e.target as HTMLInputElement).blur() (e.target as HTMLInputElement).blur()
} }
}} }}
className="bg-transparent border-b border-transparent hover:border-white/20 focus:border-primary focus:outline-none text-xs text-slate-400 w-24 px-1 py-0.5 transition-all placeholder:text-slate-600 placeholder:italic" className="bg-transparent border-b border-transparent hover:border-slate-300 dark:hover:border-white/20 focus:border-primary focus:outline-none text-xs text-slate-600 dark:text-slate-400 w-24 px-1 py-0.5 transition-all placeholder:text-slate-500 placeholder:italic"
/> />
</div> </div>
</div> </div>
@@ -286,18 +286,18 @@ export function UserList({ initialUsers, companies }: UserListProps) {
<select <select
value={role} value={role}
onChange={(e) => handleRoleChange(user.id, e.target.value as Role)} onChange={(e) => handleRoleChange(user.id, e.target.value as Role)}
className={`text-xs font-semibold rounded px-3 py-1.5 cursor-pointer focus:outline-none focus:ring-2 transition-colors ${ className={`text-xs font-semibold rounded px-3 py-1.5 cursor-pointer focus:outline-none focus:ring-2 transition-colors bg-white dark:bg-slate-950/80 ${
role === 'admin' role === 'admin'
? 'bg-purple-500/20 text-purple-300 border border-purple-500/40 focus:ring-purple-500/50' ? 'text-purple-600 dark:text-purple-300 border border-purple-500/40 focus:ring-purple-500/50'
: role === 'gesperrt' : role === 'gesperrt'
? 'bg-red-500/20 text-red-300 border border-red-500/40 focus:ring-red-500/50' ? 'text-red-600 dark:text-red-300 border border-red-500/40 focus:ring-red-500/50'
: 'bg-blue-500/20 text-blue-300 border border-blue-500/40 focus:ring-blue-500/50' : 'text-blue-600 dark:text-blue-300 border border-blue-500/40 focus:ring-blue-500/50'
}`} }`}
style={{ appearance: 'auto' }} style={{ appearance: 'auto' }}
> >
<option value="admin">Admin</option> <option value="admin" className="bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100">Admin</option>
<option value="partner">Partner</option> <option value="partner" className="bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100">Partner</option>
<option value="gesperrt">Gesperrt</option> <option value="gesperrt" className="bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100">Gesperrt</option>
</select> </select>
)} )}
</div> </div>
@@ -315,21 +315,21 @@ export function UserList({ initialUsers, companies }: UserListProps) {
setCompanyDropdownOpen(companyDropdownOpen === user.id ? null : user.id) setCompanyDropdownOpen(companyDropdownOpen === user.id ? null : user.id)
setCompanySearch('') setCompanySearch('')
}} }}
className="text-xs bg-white/5 border border-white/10 rounded px-3 py-1.5 text-slate-300 hover:bg-white/10 cursor-pointer focus:outline-none focus:ring-1 focus:ring-primary/50 max-w-[180px] truncate text-left" className="text-xs bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded px-3 py-1.5 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-white/10 cursor-pointer focus:outline-none focus:ring-1 focus:ring-primary/50 max-w-[180px] truncate text-left"
> >
{user.company_name || 'Keine Firma'} {user.company_name || 'Keine Firma'}
</button> </button>
)} )}
</div> </div>
{companyDropdownOpen === user.id && ( {companyDropdownOpen === user.id && (
<div className="absolute z-50 mt-1 left-0 w-56 bg-slate-900 border border-white/10 rounded-lg shadow-xl overflow-hidden"> <div className="absolute z-50 mt-1 left-0 w-56 bg-white dark:bg-slate-900 border border-slate-200 dark:border-white/10 rounded-lg shadow-xl overflow-hidden">
<div className="p-2 border-b border-white/10"> <div className="p-2 border-b border-slate-200 dark:border-white/10">
<input <input
type="text" type="text"
placeholder="Firma suchen..." placeholder="Firma suchen..."
value={companySearch} value={companySearch}
onChange={(e) => setCompanySearch(e.target.value)} onChange={(e) => setCompanySearch(e.target.value)}
className="w-full text-xs bg-white/5 border border-white/10 rounded px-2 py-1.5 text-slate-200 placeholder-slate-500 focus:outline-none focus:ring-1 focus:ring-primary/50" className="w-full text-xs bg-slate-50 dark:bg-white/5 border border-slate-200 dark:border-white/10 rounded px-2 py-1.5 text-slate-900 dark:text-slate-200 placeholder-slate-500 focus:outline-none focus:ring-1 focus:ring-primary/50"
autoFocus autoFocus
/> />
</div> </div>
@@ -337,8 +337,8 @@ export function UserList({ initialUsers, companies }: UserListProps) {
<button <button
type="button" type="button"
onClick={() => { handleCompanyChange(user.id, ''); setCompanyDropdownOpen(null); setCompanySearch(''); }} onClick={() => { handleCompanyChange(user.id, ''); setCompanyDropdownOpen(null); setCompanySearch(''); }}
className={`w-full text-left text-xs px-3 py-2 hover:bg-white/10 transition-colors ${ className={`w-full text-left text-xs px-3 py-2 hover:bg-slate-100 dark:hover:bg-white/10 transition-colors ${
!user.company_id ? 'text-primary font-semibold' : 'text-slate-300' !user.company_id ? 'text-primary font-semibold' : 'text-slate-700 dark:text-slate-300'
}`} }`}
> >
Keine Firma Keine Firma
@@ -350,8 +350,8 @@ export function UserList({ initialUsers, companies }: UserListProps) {
key={c.id} key={c.id}
type="button" type="button"
onClick={() => { handleCompanyChange(user.id, c.id); setCompanyDropdownOpen(null); setCompanySearch(''); }} onClick={() => { handleCompanyChange(user.id, c.id); setCompanyDropdownOpen(null); setCompanySearch(''); }}
className={`w-full text-left text-xs px-3 py-2 hover:bg-white/10 transition-colors ${ className={`w-full text-left text-xs px-3 py-2 hover:bg-slate-100 dark:hover:bg-white/10 transition-colors ${
user.company_id === c.id ? 'text-primary font-semibold' : 'text-slate-300' user.company_id === c.id ? 'text-primary font-semibold' : 'text-slate-700 dark:text-slate-300'
}`} }`}
> >
{c.name} {c.name}

View File

@@ -52,3 +52,27 @@ export async function assignCompanyToUser(userId: string, companyId: string | nu
revalidatePath('/admin/users') revalidatePath('/admin/users')
return true return true
} }
export async function getEndCustomersByCompany(companyId: string) {
const admin = createAdminClient()
// 1. Get all partners of this company
const { data: partners, error: partnersError } = await admin
.from('users')
.select('id')
.eq('company_id', companyId)
if (partnersError) throw partnersError
if (!partners || partners.length === 0) return []
const partnerIds = partners.map((p) => p.id)
// 2. Get all end customers belonging to these partners
const { data: customers, error: customersError } = await admin
.from('end_customers')
.select('*')
.in('partner_id', partnerIds)
if (customersError) throw customersError
return customers || []
}

View File

@@ -231,38 +231,3 @@ export async function updateUserProfile(id: string, data: { first_name: string |
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 || '',
};
});
}