chore: synchronize local fixes to gitea
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useUser, useOrganizationList, useOrganization } from '@clerk/clerk-react';
|
||||
import { Building2, Users, RefreshCw, Mail } from 'lucide-react';
|
||||
|
||||
export const OrganizationSelector: React.FC = () => {
|
||||
const { user } = useUser();
|
||||
const { setActive, userMemberships, userInvitations } = useOrganizationList({
|
||||
userMemberships: {
|
||||
infinite: true,
|
||||
},
|
||||
userInvitations: {
|
||||
infinite: true,
|
||||
}
|
||||
});
|
||||
const { organization } = useOrganization();
|
||||
const [isAcceptingInvites, setIsAcceptingInvites] = useState(false);
|
||||
|
||||
console.log('OrganizationSelector rendered');
|
||||
console.log('Current organization:', organization);
|
||||
console.log('User memberships:', userMemberships);
|
||||
console.log('User memberships data:', userMemberships.data);
|
||||
console.log('User invitations:', userInvitations);
|
||||
console.log('User invitations data:', userInvitations.data);
|
||||
|
||||
// Auto-accept pending invitations
|
||||
useEffect(() => {
|
||||
const acceptPendingInvitations = async () => {
|
||||
if (userInvitations.data && userInvitations.data.length > 0 && !isAcceptingInvites) {
|
||||
console.log('Found pending invitations, auto-accepting...');
|
||||
setIsAcceptingInvites(true);
|
||||
|
||||
for (const invitation of userInvitations.data) {
|
||||
try {
|
||||
console.log('Accepting invitation:', invitation);
|
||||
await invitation.accept();
|
||||
console.log('Invitation accepted successfully');
|
||||
} catch (error) {
|
||||
console.error('Error accepting invitation:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Reload memberships after accepting invitations
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
acceptPendingInvitations();
|
||||
}, [userInvitations.data, isAcceptingInvites]);
|
||||
|
||||
// Auto-select if user has only one organization
|
||||
useEffect(() => {
|
||||
console.log('Auto-select effect running...');
|
||||
if (!organization && userMemberships.data && userMemberships.data.length === 1) {
|
||||
console.log('Auto-selecting single organization...');
|
||||
const membership = userMemberships.data[0];
|
||||
if (setActive) {
|
||||
setActive({ organization: membership.organization });
|
||||
}
|
||||
}
|
||||
}, [organization, userMemberships.data, setActive]);
|
||||
|
||||
const handleSelectOrganization = async (orgId: string) => {
|
||||
console.log('Selecting organization:', orgId);
|
||||
if (setActive) {
|
||||
await setActive({ organization: orgId });
|
||||
}
|
||||
// The auth context will automatically sync after organization changes
|
||||
};
|
||||
|
||||
// Loading state - check if data exists or accepting invites
|
||||
if (!userMemberships.data || isAcceptingInvites) {
|
||||
console.log('Loading state - no data yet or accepting invites');
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
{isAcceptingInvites ? (
|
||||
<>
|
||||
<Mail className="w-12 h-12 text-primary animate-bounce mx-auto mb-4" />
|
||||
<p className="text-text-main font-bold mb-2">Aceitando convites pendentes...</p>
|
||||
<p className="text-text-muted text-sm">Por favor aguarde</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<RefreshCw className="w-12 h-12 text-primary animate-spin mx-auto mb-4" />
|
||||
<p className="text-text-muted">Carregando organizações...</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (userMemberships.data?.length === 0) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex items-center justify-center p-4">
|
||||
<div className="max-w-md w-full bg-surface rounded-2xl border border-border/40 p-8 text-center">
|
||||
<div className="w-16 h-16 rounded-2xl bg-amber-500/20 flex items-center justify-center mx-auto mb-4">
|
||||
<Building2 className="w-8 h-8 text-amber-500" />
|
||||
</div>
|
||||
<h1 className="text-2xl font-bold text-text-main mb-2">
|
||||
Nenhuma Organização
|
||||
</h1>
|
||||
<p className="text-text-muted mb-6">
|
||||
Você ainda não faz parte de nenhuma organização. Entre em contato com o administrador para receber um convite.
|
||||
</p>
|
||||
<div className="text-sm text-text-muted bg-surface-soft rounded-lg p-4">
|
||||
<p className="font-semibold mb-1">Conectado como:</p>
|
||||
<p>{user?.primaryEmailAddress?.emailAddress}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex items-center justify-center p-4">
|
||||
<div className="max-w-2xl w-full">
|
||||
<div className="text-center mb-8">
|
||||
<div className="w-16 h-16 rounded-2xl bg-primary/20 flex items-center justify-center mx-auto mb-4">
|
||||
<Building2 className="w-8 h-8 text-primary" />
|
||||
</div>
|
||||
<h1 className="text-3xl font-bold text-text-main mb-2">
|
||||
Selecione uma Organização
|
||||
</h1>
|
||||
<p className="text-text-muted">
|
||||
Escolha qual organização você deseja acessar
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4">
|
||||
{userMemberships.data?.map((membership) => (
|
||||
<button
|
||||
key={membership.organization.id}
|
||||
onClick={() => handleSelectOrganization(membership.organization.id)}
|
||||
className="w-full bg-surface hover:bg-surface-hover border border-border/40 rounded-2xl p-6 text-left transition-all hover:border-primary/50 hover:shadow-lg hover:shadow-primary/10 group"
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-14 h-14 rounded-xl bg-primary/20 flex items-center justify-center flex-shrink-0 group-hover:bg-primary/30 transition-colors">
|
||||
{membership.organization.imageUrl ? (
|
||||
<img
|
||||
src={membership.organization.imageUrl}
|
||||
alt={membership.organization.name}
|
||||
className="w-12 h-12 rounded-lg object-contain"
|
||||
/>
|
||||
) : (
|
||||
<Building2 className="w-7 h-7 text-primary" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg font-bold text-text-main group-hover:text-primary transition-colors">
|
||||
{membership.organization.name}
|
||||
</h3>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg bg-primary/20 text-primary text-xs font-semibold">
|
||||
{membership.role === 'org:admin' ? 'Administrador' :
|
||||
membership.role === 'org:member' ? 'Membro' : 'Convidado'}
|
||||
</span>
|
||||
<span className="flex items-center gap-1 text-xs text-text-muted">
|
||||
<Users className="w-3 h-3" />
|
||||
{membership.organization.membersCount || 0} membros
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-primary opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-6 text-center">
|
||||
<p className="text-sm text-text-muted">
|
||||
Conectado como <span className="font-semibold">{user?.primaryEmailAddress?.emailAddress}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user