organizacoes
This commit is contained in:
parent
e78c15898f
commit
734e5d19a7
4 changed files with 309 additions and 61 deletions
|
|
@ -1,35 +1,54 @@
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from "react";
|
||||||
import { Layout, ListTodo, PlusCircle, Columns, LogOut, Settings, Building2, Clock, ChevronDown, Shield, FolderKanban } from 'lucide-react'
|
import {
|
||||||
import { NavLink, Outlet, Navigate } from 'react-router-dom'
|
Layout,
|
||||||
import { useAuth } from '../contexts/AuthContext'
|
ListTodo,
|
||||||
import { OnboardingModal } from './OnboardingModal'
|
PlusCircle,
|
||||||
|
Columns,
|
||||||
|
LogOut,
|
||||||
|
Settings,
|
||||||
|
Building2,
|
||||||
|
Clock,
|
||||||
|
ChevronDown,
|
||||||
|
Shield,
|
||||||
|
FolderKanban,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { NavLink, Outlet, Navigate, Link } from "react-router-dom";
|
||||||
|
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";
|
||||||
|
|
||||||
const navLinkClassName = ({ isActive }: { isActive: boolean }) =>
|
const navLinkClassName = ({ isActive }: { isActive: boolean }) =>
|
||||||
cn(
|
cn(
|
||||||
'inline-flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-semibold transition-all duration-200',
|
"inline-flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-semibold transition-all duration-200",
|
||||||
isActive
|
isActive
|
||||||
? 'bg-brand text-white shadow-md shadow-brand/20'
|
? "bg-brand text-white shadow-md shadow-brand/20"
|
||||||
: 'text-text-muted hover:bg-surface-muted hover:text-text-main',
|
: "text-text-muted hover:bg-surface-muted hover:text-text-main",
|
||||||
)
|
);
|
||||||
|
|
||||||
export function AppShell() {
|
export function AppShell() {
|
||||||
const { user, isAuthenticated, isLoading, logout, activeOrg, setActiveOrg } = useAuth()
|
const { user, isAuthenticated, isLoading, logout, activeOrg, setActiveOrg } =
|
||||||
const [showOrgModal, setShowOrgModal] = useState(false)
|
useAuth();
|
||||||
const [showOrgSwitcher, setShowOrgSwitcher] = useState(false)
|
const [showOrgModal, setShowOrgModal] = useState(false);
|
||||||
|
const [showOrgSwitcher, setShowOrgSwitcher] = useState(false);
|
||||||
|
|
||||||
|
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">
|
||||||
<div className="h-10 w-10 animate-spin rounded-full border-4 border-current border-t-transparent" />
|
<div className="h-10 w-10 animate-spin rounded-full border-4 border-current border-t-transparent" />
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isAuthenticated) {
|
if (!isAuthenticated) {
|
||||||
return <Navigate to="/login" replace />
|
return <Navigate to="/login" replace />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -65,13 +84,19 @@ export function AppShell() {
|
||||||
</div>
|
</div>
|
||||||
<div className="text-left hidden sm:block">
|
<div className="text-left hidden sm:block">
|
||||||
<span className="block text-xs text-text-main leading-tight truncate max-w-[120px]">
|
<span className="block text-xs text-text-main leading-tight truncate max-w-[120px]">
|
||||||
{activeOrg?.name || 'Selecionar Org'}
|
{activeOrg?.name || "Selecionar Org"}
|
||||||
</span>
|
</span>
|
||||||
<span className="block text-[10px] text-text-muted font-black uppercase tracking-tighter leading-none">
|
<span className="block text-[10px] text-text-muted font-black uppercase tracking-tighter leading-none">
|
||||||
{activeOrg?.roles[0] || 'Visitante'}
|
{activeOrg?.roles[0] || "Visitante"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<ChevronDown size={14} className={cn("text-text-muted transition-transform", showOrgSwitcher && "rotate-180")} />
|
<ChevronDown
|
||||||
|
size={14}
|
||||||
|
className={cn(
|
||||||
|
"text-text-muted transition-transform",
|
||||||
|
showOrgSwitcher && "rotate-180",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{showOrgSwitcher && (
|
{showOrgSwitcher && (
|
||||||
|
|
@ -100,38 +125,44 @@ export function AppShell() {
|
||||||
"flex w-full items-center justify-between rounded-xl px-3 py-2.5 transition-all text-left",
|
"flex w-full items-center justify-between rounded-xl px-3 py-2.5 transition-all text-left",
|
||||||
org.id === activeOrg?.id
|
org.id === activeOrg?.id
|
||||||
? "bg-brand/10 text-brand"
|
? "bg-brand/10 text-brand"
|
||||||
: "hover:bg-surface-muted text-text-muted hover:text-text-main"
|
: "hover:bg-surface-muted text-text-muted hover:text-text-main",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className={cn(
|
<div
|
||||||
|
className={cn(
|
||||||
"flex h-8 w-8 items-center justify-center rounded-lg font-bold text-xs shadow-sm",
|
"flex h-8 w-8 items-center justify-center rounded-lg font-bold text-xs shadow-sm",
|
||||||
org.id === activeOrg?.id ? "bg-brand text-white" : "bg-white border border-border-soft text-text-muted"
|
org.id === activeOrg?.id
|
||||||
)}>
|
? "bg-brand text-white"
|
||||||
|
: "bg-white border border-border-soft text-text-muted",
|
||||||
|
)}
|
||||||
|
>
|
||||||
{org.name.substring(0, 2).toUpperCase()}
|
{org.name.substring(0, 2).toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span className="block text-sm font-bold leading-none">{org.name}</span>
|
<span className="block text-sm font-bold leading-none">
|
||||||
|
{org.name}
|
||||||
|
</span>
|
||||||
<span className="text-[10px] font-medium opacity-70">
|
<span className="text-[10px] font-medium opacity-70">
|
||||||
{org.roles.join(', ')}
|
{org.roles.join(", ")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{org.id === activeOrg?.id && <div className="h-1.5 w-1.5 rounded-full bg-brand" />}
|
{org.id === activeOrg?.id && (
|
||||||
|
<div className="h-1.5 w-1.5 rounded-full bg-brand" />
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 border-t border-border-soft p-1">
|
<div className="mt-2 border-t border-border-soft p-1">
|
||||||
<button
|
<Link
|
||||||
onClick={() => {
|
to="/organizations"
|
||||||
setShowOrgModal(true);
|
onClick={() => setShowOrgSwitcher(false)}
|
||||||
setShowOrgSwitcher(false);
|
|
||||||
}}
|
|
||||||
className="flex w-full items-center gap-2 rounded-xl px-3 py-2 text-xs font-bold text-brand hover:bg-brand/5 transition-all"
|
className="flex w-full items-center gap-2 rounded-xl px-3 py-2 text-xs font-bold text-brand hover:bg-brand/5 transition-all"
|
||||||
>
|
>
|
||||||
<PlusCircle size={14} />
|
<PlusCircle size={14} />
|
||||||
Gerenciar Organizações
|
Gerenciar Organizações
|
||||||
</button>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</>
|
</>
|
||||||
|
|
@ -159,17 +190,14 @@ export function AppShell() {
|
||||||
<PlusCircle size={18} />
|
<PlusCircle size={18} />
|
||||||
Nova Tarefa
|
Nova Tarefa
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<button
|
<NavLink to="/organizations" className={navLinkClassName}>
|
||||||
onClick={() => setShowOrgModal(true)}
|
|
||||||
className={cn(
|
|
||||||
'inline-flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-semibold transition-all duration-200',
|
|
||||||
'text-text-muted hover:bg-surface-muted hover:text-text-main'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Building2 size={18} />
|
<Building2 size={18} />
|
||||||
Organizações
|
Organizações
|
||||||
</button>
|
</NavLink>
|
||||||
{user?.organizations.some(org => org.roles.includes('OWNER') || org.roles.includes('ADMIN')) && (
|
{user?.organizations.some(
|
||||||
|
(org) =>
|
||||||
|
org.roles.includes("OWNER") || org.roles.includes("ADMIN"),
|
||||||
|
) && (
|
||||||
<NavLink to="/admin" className={navLinkClassName}>
|
<NavLink to="/admin" className={navLinkClassName}>
|
||||||
<Settings size={18} />
|
<Settings size={18} />
|
||||||
Gestão
|
Gestão
|
||||||
|
|
@ -231,17 +259,13 @@ export function AppShell() {
|
||||||
<PlusCircle size={18} />
|
<PlusCircle size={18} />
|
||||||
Criar
|
Criar
|
||||||
</NavLink>
|
</NavLink>
|
||||||
<button
|
<NavLink to="/organizations" className={navLinkClassName}>
|
||||||
onClick={() => setShowOrgModal(true)}
|
|
||||||
className={cn(
|
|
||||||
'inline-flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-semibold transition-all duration-200',
|
|
||||||
'text-text-muted hover:bg-surface-muted hover:text-text-main'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Building2 size={18} />
|
<Building2 size={18} />
|
||||||
Org
|
Org
|
||||||
</button>
|
</NavLink>
|
||||||
{user?.organizations.some(org => org.roles.includes('OWNER') || org.roles.includes('ADMIN')) && (
|
{user?.organizations.some(
|
||||||
|
(org) => org.roles.includes("OWNER") || org.roles.includes("ADMIN"),
|
||||||
|
) && (
|
||||||
<NavLink to="/admin" className={navLinkClassName}>
|
<NavLink to="/admin" className={navLinkClassName}>
|
||||||
<Settings size={18} />
|
<Settings size={18} />
|
||||||
Gestão
|
Gestão
|
||||||
|
|
@ -249,7 +273,10 @@ export function AppShell() {
|
||||||
)}
|
)}
|
||||||
</nav>
|
</nav>
|
||||||
</footer>
|
</footer>
|
||||||
<OnboardingModal isOpen={showOrgModal} onClose={() => setShowOrgModal(false)} />
|
<OnboardingModal
|
||||||
|
isOpen={showOrgModal}
|
||||||
|
onClose={() => setShowOrgModal(false)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,10 @@ export const OnboardingModal: React.FC<OnboardingModalProps> = ({ isOpen, onClos
|
||||||
|
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 relative z-10">
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 relative z-10">
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/organizations/new')}
|
onClick={() => {
|
||||||
|
navigate('/organizations');
|
||||||
|
onClose?.();
|
||||||
|
}}
|
||||||
className="group p-6 bg-surface-muted/30 border border-border-soft rounded-[2rem] text-left hover:border-brand hover:bg-brand/5 transition-all active:scale-[0.98]"
|
className="group p-6 bg-surface-muted/30 border border-border-soft rounded-[2rem] text-left hover:border-brand hover:bg-brand/5 transition-all active:scale-[0.98]"
|
||||||
>
|
>
|
||||||
<div className="w-12 h-12 bg-brand/10 text-brand rounded-xl flex items-center justify-center mb-4 group-hover:bg-brand group-hover:text-white transition-colors">
|
<div className="w-12 h-12 bg-brand/10 text-brand rounded-xl flex items-center justify-center mb-4 group-hover:bg-brand group-hover:text-white transition-colors">
|
||||||
|
|
@ -66,7 +69,10 @@ export const OnboardingModal: React.FC<OnboardingModalProps> = ({ isOpen, onClos
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => navigate('/organizations/join')}
|
onClick={() => {
|
||||||
|
navigate('/organizations');
|
||||||
|
onClose?.();
|
||||||
|
}}
|
||||||
className="group p-6 bg-surface-muted/30 border border-border-soft rounded-[2rem] text-left hover:border-accent hover:bg-accent/5 transition-all active:scale-[0.98]"
|
className="group p-6 bg-surface-muted/30 border border-border-soft rounded-[2rem] text-left hover:border-accent hover:bg-accent/5 transition-all active:scale-[0.98]"
|
||||||
>
|
>
|
||||||
<div className="w-12 h-12 bg-accent/10 text-accent rounded-xl flex items-center justify-center mb-4 group-hover:bg-accent group-hover:text-white transition-colors">
|
<div className="w-12 h-12 bg-accent/10 text-accent rounded-xl flex items-center justify-center mb-4 group-hover:bg-accent group-hover:text-white transition-colors">
|
||||||
|
|
|
||||||
210
frontend/src/pages/OrganizationsPage.tsx
Normal file
210
frontend/src/pages/OrganizationsPage.tsx
Normal file
|
|
@ -0,0 +1,210 @@
|
||||||
|
import { motion } from 'framer-motion'
|
||||||
|
import {
|
||||||
|
Building2,
|
||||||
|
Plus,
|
||||||
|
Search,
|
||||||
|
Users,
|
||||||
|
Settings,
|
||||||
|
ArrowRight,
|
||||||
|
ExternalLink,
|
||||||
|
ShieldCheck,
|
||||||
|
UserPlus
|
||||||
|
} from 'lucide-react'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
import { useAuth } from '../contexts/AuthContext'
|
||||||
|
import { cn } from '../lib/utils'
|
||||||
|
|
||||||
|
export function OrganizationsPage() {
|
||||||
|
const { user, activeOrg, setActiveOrg } = useAuth()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-12 pb-20">
|
||||||
|
{/* Header Section */}
|
||||||
|
<header className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
|
||||||
|
<div>
|
||||||
|
<h1 className="text-4xl font-black tracking-tight text-text-main">
|
||||||
|
Organizações
|
||||||
|
</h1>
|
||||||
|
<p className="mt-2 text-lg text-text-muted">
|
||||||
|
Gerencie seus espaços de trabalho e conexões.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Link
|
||||||
|
to="/organizations/join"
|
||||||
|
className="inline-flex items-center justify-center gap-2 rounded-xl bg-surface border border-border-soft px-6 py-3 text-sm font-bold text-text-main shadow-sm transition-all hover:bg-surface-muted active:scale-95"
|
||||||
|
>
|
||||||
|
<Search size={18} />
|
||||||
|
Encontrar Organização
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
to="/organizations/new"
|
||||||
|
className="inline-flex items-center justify-center gap-2 rounded-xl bg-brand px-6 py-3 text-sm font-bold text-white shadow-lg shadow-brand/20 transition-all hover:scale-[1.02] hover:bg-brand-strong active:scale-95"
|
||||||
|
>
|
||||||
|
<Plus size={18} />
|
||||||
|
Nova Organização
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{/* Your Organizations Grid */}
|
||||||
|
<section className="space-y-6">
|
||||||
|
<div className="flex items-center gap-2 px-2">
|
||||||
|
<Building2 size={20} className="text-brand" />
|
||||||
|
<h2 className="text-xl font-bold text-text-main">Suas Organizações</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{user?.organizations.map((org) => {
|
||||||
|
const isActive = org.id === activeOrg?.id
|
||||||
|
const isOwner = org.roles.includes('OWNER')
|
||||||
|
const isAdmin = org.roles.includes('ADMIN')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
key={org.id}
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
className={cn(
|
||||||
|
"group relative overflow-hidden rounded-[2.5rem] border bg-surface p-8 transition-all hover:shadow-2xl hover:shadow-brand/5",
|
||||||
|
isActive ? "border-brand ring-1 ring-brand/10" : "border-border-soft hover:border-brand/30"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{/* Active Indicator */}
|
||||||
|
{isActive && (
|
||||||
|
<div className="absolute right-6 top-6 flex items-center gap-1.5 rounded-full bg-brand/10 px-3 py-1 text-[10px] font-black uppercase tracking-widest text-brand">
|
||||||
|
<div className="h-1.5 w-1.5 rounded-full bg-brand animate-pulse" />
|
||||||
|
Ativa
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="relative z-10">
|
||||||
|
<div className={cn(
|
||||||
|
"flex h-16 w-16 items-center justify-center rounded-2xl text-2xl font-black shadow-inner transition-transform group-hover:scale-110",
|
||||||
|
isActive ? "bg-brand text-white" : "bg-surface-muted text-text-muted group-hover:bg-brand group-hover:text-white"
|
||||||
|
)}>
|
||||||
|
{org.name.substring(0, 2).toUpperCase()}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 className="mt-6 text-xl font-black text-text-main leading-tight">
|
||||||
|
{org.name}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div className="mt-4 flex flex-wrap gap-2">
|
||||||
|
{org.roles.map(role => (
|
||||||
|
<span key={role} className="inline-flex items-center gap-1 rounded-lg bg-surface-muted px-2 py-1 text-[10px] font-bold text-text-muted">
|
||||||
|
{role === 'OWNER' && <ShieldCheck size={12} className="text-brand" />}
|
||||||
|
{role === 'ADMIN' && <Settings size={12} className="text-accent" />}
|
||||||
|
{role}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-8 flex items-center justify-between gap-3">
|
||||||
|
{!isActive ? (
|
||||||
|
<button
|
||||||
|
onClick={() => setActiveOrg(org.id)}
|
||||||
|
className="flex-1 rounded-xl bg-app-bg py-3 text-xs font-black uppercase tracking-widest text-text-main transition-all hover:bg-brand hover:text-white"
|
||||||
|
>
|
||||||
|
Selecionar
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<div className="flex-1 py-3 text-xs font-black uppercase tracking-widest text-brand text-center">
|
||||||
|
Selecionada
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(isOwner || isAdmin) && (
|
||||||
|
<Link
|
||||||
|
to="/admin"
|
||||||
|
className="flex h-10 w-10 items-center justify-center rounded-xl bg-surface-muted text-text-muted transition-all hover:bg-accent hover:text-white"
|
||||||
|
title="Configurações da Organização"
|
||||||
|
>
|
||||||
|
<Settings size={18} />
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Decorations */}
|
||||||
|
<div className="absolute -bottom-10 -right-10 h-32 w-32 rounded-full bg-brand/5 blur-3xl group-hover:bg-brand/10 transition-colors" />
|
||||||
|
</motion.div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
|
||||||
|
{/* Quick Create Card */}
|
||||||
|
<Link
|
||||||
|
to="/organizations/new"
|
||||||
|
className="flex flex-col items-center justify-center gap-4 rounded-[2.5rem] border-2 border-dashed border-border-soft bg-surface/50 p-8 text-center transition-all hover:border-brand/30 hover:bg-brand/5 active:scale-[0.98]"
|
||||||
|
>
|
||||||
|
<div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-app-bg text-text-muted transition-colors group-hover:bg-brand group-hover:text-white">
|
||||||
|
<Plus size={32} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-bold text-text-main">Criar nova organização</h3>
|
||||||
|
<p className="text-sm text-text-muted">Inicie um novo projeto com sua equipe.</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Discovery Section */}
|
||||||
|
<section className="rounded-[3rem] bg-brand p-8 md:p-12 text-white overflow-hidden relative shadow-2xl shadow-brand/20">
|
||||||
|
<div className="relative z-10 max-w-2xl">
|
||||||
|
<h2 className="text-3xl font-black tracking-tight leading-none">
|
||||||
|
Procurando uma equipe?
|
||||||
|
</h2>
|
||||||
|
<p className="mt-4 text-white/80 font-medium">
|
||||||
|
Explore organizações públicas ou use um código de convite para se juntar aos seus colegas de trabalho.
|
||||||
|
</p>
|
||||||
|
<div className="mt-10 flex flex-wrap gap-4">
|
||||||
|
<Link
|
||||||
|
to="/organizations/join"
|
||||||
|
className="inline-flex items-center gap-2 rounded-2xl bg-white px-8 py-4 text-sm font-black text-brand shadow-xl transition-all hover:scale-105 active:scale-95"
|
||||||
|
>
|
||||||
|
<Search size={20} />
|
||||||
|
Buscar Organizações
|
||||||
|
<ArrowRight size={18} />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Abstract shapes */}
|
||||||
|
<div className="absolute top-0 right-0 w-64 h-64 bg-white/10 blur-3xl rounded-full translate-x-1/2 -translate-y-1/2" />
|
||||||
|
<div className="absolute bottom-0 left-0 w-48 h-48 bg-accent/20 blur-3xl rounded-full -translate-x-1/2 translate-y-1/2" />
|
||||||
|
<Users size={200} className="absolute -right-20 -bottom-20 text-white/5 rotate-12" />
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{/* Info Section */}
|
||||||
|
<div className="grid gap-8 md:grid-cols-2">
|
||||||
|
<div className="rounded-[2rem] border border-border-soft bg-surface p-8">
|
||||||
|
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-accent-soft text-accent mb-6">
|
||||||
|
<UserPlus size={24} />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-bold text-text-main">Como funciona?</h3>
|
||||||
|
<p className="mt-3 text-text-muted leading-relaxed">
|
||||||
|
Organizações são espaços isolados para seus projetos e tarefas. Você pode ser dono de várias ou participar como colaborador em outras. Toda a sua atividade é filtrada pela organização ativa.
|
||||||
|
</p>
|
||||||
|
<a href="#" className="mt-6 inline-flex items-center gap-1.5 text-sm font-bold text-accent hover:underline">
|
||||||
|
Saiba mais sobre permissões
|
||||||
|
<ExternalLink size={14} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded-[2rem] border border-border-soft bg-surface p-8">
|
||||||
|
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-brand-soft text-brand mb-6">
|
||||||
|
<ShieldCheck size={24} />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-bold text-text-main">Segurança & Funções</h3>
|
||||||
|
<p className="mt-3 text-text-muted leading-relaxed">
|
||||||
|
Cada organização define seus próprios papéis (OWNER, ADMIN, MEMBER). Apenas administradores e donos podem gerenciar membros e configurações do espaço.
|
||||||
|
</p>
|
||||||
|
<a href="#" className="mt-6 inline-flex items-center gap-1.5 text-sm font-bold text-brand hover:underline">
|
||||||
|
Ver detalhes de segurança
|
||||||
|
<ExternalLink size={14} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,7 @@ import { JoinOrganizationPage } from "./pages/JoinOrganizationPage";
|
||||||
import { OrganizationAdminPage } from "./pages/OrganizationAdminPage";
|
import { OrganizationAdminPage } from "./pages/OrganizationAdminPage";
|
||||||
import { MyRequestsPage } from "./pages/MyRequestsPage";
|
import { MyRequestsPage } from "./pages/MyRequestsPage";
|
||||||
import { ProjectsPage } from "./pages/ProjectsPage";
|
import { ProjectsPage } from "./pages/ProjectsPage";
|
||||||
|
import { OrganizationsPage } from "./pages/OrganizationsPage";
|
||||||
|
|
||||||
export const router = createBrowserRouter([
|
export const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
|
|
@ -57,6 +58,10 @@ export const router = createBrowserRouter([
|
||||||
path: "projects",
|
path: "projects",
|
||||||
element: <ProjectsPage />,
|
element: <ProjectsPage />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "organizations",
|
||||||
|
element: <OrganizationsPage />,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue