fix: centralizar OnboardingModal em /organizations e evitar reabertura (#15)

O modal de onboarding era disparado tanto na LoginPage quanto no AppShell
com navegação conflitante, fazendo ele fechar e reabrir ao escolher uma
opção. Agora o login sem org redireciona para /organizations, o AppShell
redireciona usuários sem org para lá, e o modal é exibido uma única vez
pela OrganizationsPage.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Gabriel Santana de Menezes 2026-05-16 10:58:21 -04:00
parent 2fdb375462
commit 5c53379568
3 changed files with 22 additions and 24 deletions

View file

@ -1,4 +1,4 @@
import React, { useState } from "react"; import { useState } from "react";
import { import {
Layout, Layout,
ListTodo, ListTodo,
@ -18,9 +18,8 @@ import {
Users, Users,
PlusCircle, PlusCircle,
} from "lucide-react"; } from "lucide-react";
import { NavLink, Outlet, Navigate, Link } from "react-router-dom"; import { NavLink, Outlet, Navigate, Link, useLocation } from "react-router-dom";
import { useAuth } from "../contexts/AuthContext"; import { useAuth } from "../contexts/AuthContext";
import { OnboardingModal } from "./OnboardingModal";
import { cn } from "../lib/utils"; import { cn } from "../lib/utils";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
@ -44,7 +43,7 @@ const dropdownItemClassName = ({ isActive }: { isActive: boolean }) =>
export function AppShell() { export function AppShell() {
const { user, isAuthenticated, isLoading, logout, activeOrg, setActiveOrg } = const { user, isAuthenticated, isLoading, logout, activeOrg, setActiveOrg } =
useAuth(); useAuth();
const [showOrgModal, setShowOrgModal] = useState(false); const location = useLocation();
const [showOrgSwitcher, setShowOrgSwitcher] = useState(false); const [showOrgSwitcher, setShowOrgSwitcher] = useState(false);
const [showTasksDropdown, setShowTasksDropdown] = useState(false); const [showTasksDropdown, setShowTasksDropdown] = useState(false);
const [showTeamDropdown, setShowTeamDropdown] = useState(false); const [showTeamDropdown, setShowTeamDropdown] = useState(false);
@ -53,12 +52,6 @@ export function AppShell() {
const isAdmin = const isAdmin =
activeOrg?.roles.includes("OWNER") || activeOrg?.roles.includes("ADMIN"); activeOrg?.roles.includes("OWNER") || activeOrg?.roles.includes("ADMIN");
React.useEffect(() => {
if (isAuthenticated && user && user.organizations.length === 0) {
setShowOrgModal(true);
}
}, [isAuthenticated, user]);
if (isLoading) { if (isLoading) {
return ( return (
<div className="flex min-h-screen items-center justify-center bg-app-bg text-brand"> <div className="flex min-h-screen items-center justify-center bg-app-bg text-brand">
@ -71,6 +64,15 @@ export function AppShell() {
return <Navigate to="/login" replace />; return <Navigate to="/login" replace />;
} }
if (
isAuthenticated &&
user &&
user.organizations.length === 0 &&
location.pathname !== "/organizations"
) {
return <Navigate to="/organizations" replace />;
}
return ( return (
<div className="flex min-h-screen flex-col bg-app-bg font-sans text-text-main"> <div className="flex min-h-screen flex-col bg-app-bg font-sans text-text-main">
{/* Top Navigation */} {/* Top Navigation */}
@ -622,10 +624,6 @@ export function AppShell() {
</div> </div>
</nav> </nav>
</footer> </footer>
<OnboardingModal
isOpen={showOrgModal}
onClose={() => setShowOrgModal(false)}
/>
</div> </div>
); );
} }

View file

@ -3,13 +3,11 @@ import { useNavigate, Link } from 'react-router-dom'
import { motion } from 'framer-motion' import { motion } from 'framer-motion'
import { Mail, Lock, LogIn, Layout } from 'lucide-react' import { Mail, Lock, LogIn, Layout } from 'lucide-react'
import { useAuth } from '../contexts/AuthContext' import { useAuth } from '../contexts/AuthContext'
import { OnboardingModal } from '../components/OnboardingModal'
export const LoginPage: React.FC = () => { export const LoginPage: React.FC = () => {
const [email, setEmail] = useState('') const [email, setEmail] = useState('')
const [password, setPassword] = useState('') const [password, setPassword] = useState('')
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [showOnboarding, setShowOnboarding] = useState(false)
const navigate = useNavigate() const navigate = useNavigate()
const { login } = useAuth() const { login } = useAuth()
@ -20,7 +18,7 @@ export const LoginPage: React.FC = () => {
try { try {
const response = await login(email, password) const response = await login(email, password)
if (response.organizations.length === 0) { if (response.organizations.length === 0) {
setShowOnboarding(true) navigate('/organizations')
} else { } else {
navigate('/') navigate('/')
} }
@ -113,13 +111,6 @@ export const LoginPage: React.FC = () => {
</div> </div>
</div> </div>
</motion.div> </motion.div>
<OnboardingModal
isOpen={showOnboarding}
onClose={() => {
setShowOnboarding(false)
navigate('/')
}}
/>
</div> </div>
) )
} }

View file

@ -19,9 +19,13 @@ import { Link } from 'react-router-dom'
import { useAuth } from '../contexts/AuthContext' import { useAuth } from '../contexts/AuthContext'
import { cn } from '../lib/utils' import { cn } from '../lib/utils'
import { organizationService } from '../services/organizationService' import { organizationService } from '../services/organizationService'
import { OnboardingModal } from '../components/OnboardingModal'
export function OrganizationsPage() { export function OrganizationsPage() {
const { user, activeOrg, setActiveOrg, refreshUser } = useAuth() const { user, activeOrg, setActiveOrg, refreshUser } = useAuth()
const [showOnboarding, setShowOnboarding] = useState(
() => (user?.organizations.length ?? 0) === 0,
)
const [showCreateModal, setShowCreateModal] = useState(false) const [showCreateModal, setShowCreateModal] = useState(false)
const [newOrgName, setNewOrgName] = useState('') const [newOrgName, setNewOrgName] = useState('')
const [isCreating, setIsCreating] = useState(false) const [isCreating, setIsCreating] = useState(false)
@ -250,6 +254,11 @@ export function OrganizationsPage() {
</div> </div>
</div> </div>
<OnboardingModal
isOpen={showOnboarding}
onClose={() => setShowOnboarding(false)}
/>
{/* Create Organization Modal */} {/* Create Organization Modal */}
<AnimatePresence> <AnimatePresence>
{showCreateModal && ( {showCreateModal && (