feat: add complex user invitation and management features

- Implemented complex member and invitation schemas using Zod for validation.
- Created new API endpoints for inviting, accepting, revoking, and canceling complex user invitations.
- Developed handlers for managing complex users, including listing, inviting, revoking, and canceling invitations.
- Added frontend components for displaying and managing complex users and invitations.
- Introduced session storage management for pending invitations.
- Integrated Gravatar for user avatars based on email.
- Created database migration for complex invitations table.
This commit is contained in:
Jose Selesan
2026-04-22 15:30:04 -03:00
parent 5f2a7fd494
commit 6ad4fc9ee9
37 changed files with 1935 additions and 78 deletions

View File

@@ -1,16 +1,17 @@
import { apiClient } from '@/lib/api-client';
import { setCurrentComplexSlug } from '@/lib/current-complex';
import { useQuery } from '@tanstack/react-query';
import { Building2, MapPin, Settings } from 'lucide-react';
import { Building2, MapPin, Settings, Users } from 'lucide-react';
import { useEffect, useState } from 'react';
import { ComplexCourtsSection } from './components/complex-courts-section';
import { ComplexDetailsSection } from './components/complex-details-section';
import { ComplexUsersSection } from './components/complex-users-section';
type ComplexSettingsPageProps = {
complexSlug: string;
};
type TabOption = 'details' | 'courts';
type TabOption = 'details' | 'courts' | 'users';
export function ComplexSettingsPage({ complexSlug }: ComplexSettingsPageProps) {
const [activeTab, setActiveTab] = useState<TabOption>('details');
@@ -24,11 +25,21 @@ export function ComplexSettingsPage({ complexSlug }: ComplexSettingsPageProps) {
queryFn: () => apiClient.complexes.getBySlug(complexSlug),
});
const myComplexesQuery = useQuery({
queryKey: ['my-complexes'],
queryFn: () => apiClient.complexes.listMine(),
});
const complexId = complexQuery.data?.id ?? null;
const currentMembership = myComplexesQuery.data?.find(
(complex) => complex.complexSlug === complexSlug
);
const canManageUsers = currentMembership?.role === 'ADMIN';
const tabs = [
{ id: 'details' as const, label: 'Datos del complejo', icon: Building2 },
{ id: 'courts' as const, label: 'Canchas', icon: MapPin },
{ id: 'users' as const, label: 'Usuarios', icon: Users },
];
return (
@@ -76,6 +87,10 @@ export function ComplexSettingsPage({ complexSlug }: ComplexSettingsPageProps) {
)}
{activeTab === 'courts' && <ComplexCourtsSection complexId={complexId} />}
{activeTab === 'users' && (
<ComplexUsersSection complexId={complexId} canManageUsers={canManageUsers} />
)}
</div>
</div>
</div>