feat(equipe): conectar Google Agenda do funcionário no painel de Equipe

- botão Conectar (GoogleConnectButton, ownerId=userId do membro) no modal de edição do membro
- status de conexão por membro via /api/auth/google/status
- conexão 'na hora' (quem tem a senha do Gmail autoriza); sem auto-conexão na área do funcionário

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Builder
2026-06-09 17:08:31 +02:00
parent 0ca0aa4284
commit 4586b3bae5
+27
View File
@@ -3,6 +3,7 @@ import { Users, Building, Briefcase, Plus, Trash2, Edit2, X, Check, UserPlus, Sh
import { HybridBackend } from '../services/backend.ts';
import { useToast } from '../contexts/ToastContext.tsx';
import { PageHeader } from '../components/PageHeader.tsx';
import { GoogleConnectButton } from '../components/GoogleConnectButton.tsx';
type Tab = 'usuarios' | 'setores' | 'funcoes';
@@ -149,6 +150,17 @@ const UsuariosTab: React.FC<{ clinicaId: string; isOwner: boolean; myNivel: numb
useEffect(() => { load(); }, [load]);
// Status do Google da clínica (qual membro tem o calendário conectado).
const [googleAccounts, setGoogleAccounts] = useState<any[]>([]);
const loadGoogleStatus = useCallback(async () => {
try {
const r = await fetch(`/api/auth/google/status?clinicaId=${clinicaId}`);
const d = await r.json();
setGoogleAccounts(Array.isArray(d) ? d : []);
} catch { /* ignore */ }
}, [clinicaId]);
useEffect(() => { loadGoogleStatus(); }, [loadGoogleStatus]);
const handleAdd = async (e: React.FormEvent) => {
e.preventDefault();
setSaving(true);
@@ -376,6 +388,21 @@ const UsuariosTab: React.FC<{ clinicaId: string; isOwner: boolean; myNivel: numb
{funcoes.filter(f => isOwner || (typeof f.nivel === 'number' ? f.nivel : 1) < myNivel).map(f => <option key={f.id} value={f.id}>{f.nome}</option>)}
</select>
</div>
{/* Google Agenda do funcionário — conectado AQUI (quem tem a senha do Gmail autoriza na hora). */}
<div className="border-t border-gray-100 pt-4">
<label className="text-[10px] font-black text-gray-500 uppercase block mb-1">GOOGLE AGENDA DESTE FUNCIONÁRIO</label>
<p className="text-[10px] text-gray-400 mb-2 leading-snug">
Conecta o calendário do Gmail desta pessoa: os agendamentos viram cópia de backup e o paciente recebe convite + lembretes. Autorize com a conta dela (precisa da senha do Gmail).
</p>
<GoogleConnectButton
ownerId={editing.id}
clinicaId={clinicaId}
isConnected={googleAccounts.some(a => a.owner_id === editing.id)}
onStatusChange={loadGoogleStatus}
/>
</div>
<div className="flex gap-3 pt-2">
<button onClick={() => setEditing(null)}
className="flex-1 py-2.5 border border-gray-200 rounded-xl text-[11px] font-black uppercase hover:bg-gray-50 transition-colors">