diff --git a/backend/index.js b/backend/index.js index c855703..0593763 100644 --- a/backend/index.js +++ b/backend/index.js @@ -460,30 +460,29 @@ apiRouter.post('/users/:id/avatar', upload.single('avatar'), async (req, res) => // --- Global Search --- apiRouter.get('/search', async (req, res) => { const { q } = req.query; - if (!q || q.length < 2) return res.json({ members: [], teams: [], attendances: [] }); + if (!q || q.length < 2) return res.json({ members: [], teams: [], attendances: [], organizations: [] }); const queryStr = `%${q}%`; - const results = { members: [], teams: [], attendances: [] }; + const results = { members: [], teams: [], attendances: [], organizations: [] }; try { - // 1. Search Members - let membersQ = 'SELECT id, name, email, slug, role, team_id, avatar_url FROM users WHERE (name LIKE ? OR email LIKE ?)'; - const membersParams = [queryStr, queryStr]; + // 1. Search Members (only for roles above agent) + if (req.user.role !== 'agent') { + let membersQ = 'SELECT id, name, email, slug, role, team_id, avatar_url FROM users WHERE (name LIKE ? OR email LIKE ?)'; + const membersParams = [queryStr, queryStr]; - if (req.user.role === 'super_admin') { - // No extra filters - } else if (req.user.role === 'admin' || req.user.role === 'owner') { - membersQ += ' AND tenant_id = ?'; - membersParams.push(req.user.tenant_id); - } else if (req.user.role === 'manager') { - membersQ += ' AND tenant_id = ? AND (team_id = ? OR id = ?)'; - membersParams.push(req.user.tenant_id, req.user.team_id, req.user.id); - } else { - membersQ += ' AND id = ?'; - membersParams.push(req.user.id); + if (req.user.role === 'super_admin') { + // No extra filters + } else if (req.user.role === 'admin' || req.user.role === 'owner') { + membersQ += ' AND tenant_id = ?'; + membersParams.push(req.user.tenant_id); + } else if (req.user.role === 'manager') { + membersQ += ' AND tenant_id = ? AND (team_id = ? OR id = ?)'; + membersParams.push(req.user.tenant_id, req.user.team_id, req.user.id); + } + const [members] = await pool.query(membersQ, membersParams); + results.members = members; } - const [members] = await pool.query(membersQ, membersParams); - results.members = members; // 2. Search Teams (only for roles above agent) if (req.user.role !== 'agent') { @@ -503,7 +502,13 @@ apiRouter.get('/search', async (req, res) => { results.teams = teams; } - // 3. Search Attendances + // 3. Search Organizations (only for super_admin) + if (req.user.role === 'super_admin') { + const [orgs] = await pool.query('SELECT id, name, slug, status FROM tenants WHERE name LIKE ? OR slug LIKE ?', [queryStr, queryStr]); + results.organizations = orgs; + } + + // 4. Search Attendances let attendancesQ = 'SELECT a.id, a.summary, a.created_at, u.name as user_name FROM attendances a JOIN users u ON a.user_id = u.id WHERE a.summary LIKE ?'; const attendancesParams = [queryStr]; diff --git a/components/Layout.tsx b/components/Layout.tsx index 07e86e9..d4910f7 100644 --- a/components/Layout.tsx +++ b/components/Layout.tsx @@ -32,7 +32,7 @@ export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => // Search State const [searchQuery, setSearchQuery] = useState(''); - const [searchResults, setSearchResults] = useState<{ members: User[], teams: any[], attendances: any[] }>({ members: [], teams: [], attendances: [] }); + const [searchResults, setSearchResults] = useState<{ members: User[], teams: any[], attendances: any[], organizations?: any[] }>({ members: [], teams: [], attendances: [], organizations: [] }); const [isSearching, setIsSearching] = useState(false); const [showSearchResults, setShowSearchResults] = useState(false); @@ -45,7 +45,7 @@ export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => setIsSearching(false); setShowSearchResults(true); } else { - setSearchResults({ members: [], teams: [], attendances: [] }); + setSearchResults({ members: [], teams: [], attendances: [], organizations: [] }); setShowSearchResults(false); } }, 300); @@ -219,35 +219,68 @@ export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => {showSearchResults && (
- {/* Members Section */} - {searchResults.members.length > 0 && ( + {/* Organizations Section (Super Admin only) */} + {searchResults.organizations && searchResults.organizations.length > 0 && (
-
Membros
- {searchResults.members.map(m => ( +
Organizações
+ {searchResults.organizations.map(o => ( ))}
)} + {/* Members Section */} + {searchResults.members.length > 0 && ( +
+
Membros
+ {searchResults.members.map(m => { + const backendUrl = import.meta.env.PROD ? '' : 'http://localhost:3001'; + const avatarSrc = m.avatar_url + ? (m.avatar_url.startsWith('http') ? m.avatar_url : `${backendUrl}${m.avatar_url}`) + : `https://ui-avatars.com/api/?name=${encodeURIComponent(m.name)}&background=random`; + + return ( + + ); + })} +
+ )} + {/* Teams Section */} {searchResults.teams.length > 0 && (
@@ -303,7 +336,7 @@ export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) =>
)} - {searchResults.members.length === 0 && searchResults.teams.length === 0 && searchResults.attendances.length === 0 && ( + {searchResults.members.length === 0 && searchResults.teams.length === 0 && searchResults.attendances.length === 0 && (!searchResults.organizations || searchResults.organizations.length === 0) && (
Nenhum resultado encontrado para "{searchQuery}"
diff --git a/services/dataService.ts b/services/dataService.ts index 938ecce..fe32a9f 100644 --- a/services/dataService.ts +++ b/services/dataService.ts @@ -17,7 +17,7 @@ const getHeaders = () => { }; }; -export const searchGlobal = async (query: string): Promise<{ members: User[], teams: any[], attendances: any[] }> => { +export const searchGlobal = async (query: string): Promise<{ members: User[], teams: any[], attendances: any[], organizations?: any[] }> => { try { const response = await fetch(`${API_URL}/search?q=${encodeURIComponent(query)}`, { headers: getHeaders() @@ -26,7 +26,7 @@ export const searchGlobal = async (query: string): Promise<{ members: User[], te return await response.json(); } catch (error) { console.error("API Error (searchGlobal):", error); - return { members: [], teams: [], attendances: [] }; + return { members: [], teams: [], attendances: [], organizations: [] }; } };