diff --git a/app/api/shortlinks/route.ts b/app/api/shortlinks/route.ts index 8f3e4d6..b2b84e0 100644 --- a/app/api/shortlinks/route.ts +++ b/app/api/shortlinks/route.ts @@ -30,7 +30,7 @@ export async function GET(request: NextRequest) { } if (team) { - whereConditions.push(`hasToken(teams, 'team_id', '${team}')`); + whereConditions.push(`arrayExists(x -> JSONExtractString(x, 'team_id') = '${team}', JSONExtractArrayRaw(teams))`); } const whereClause = whereConditions.join(' AND '); diff --git a/app/links/page.tsx b/app/links/page.tsx index 8f13ef6..618ef5b 100644 --- a/app/links/page.tsx +++ b/app/links/page.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from 'react'; import { getSupabaseClient } from '../utils/supabase'; import { AuthChangeEvent } from '@supabase/supabase-js'; import { Loader2, ExternalLink, Search } from 'lucide-react'; +import { TeamSelector } from '@/app/components/ui/TeamSelector'; // Define attribute type to avoid using 'any' interface LinkAttributes { @@ -136,28 +137,75 @@ export default function LinksPage() { console.error('Error parsing attributes:', e); } - // Get team name - let teamName = ''; + // Get team names + const teamNames: string[] = []; try { if (link.teams) { const teams = typeof link.teams === 'string' ? JSON.parse(link.teams) : link.teams || []; - if (Array.isArray(teams) && teams.length > 0 && teams[0].team_name) { - teamName = teams[0].team_name; + if (Array.isArray(teams)) { + teams.forEach(team => { + if (team.team_name) { + teamNames.push(team.team_name); + } + }); } } } catch (e) { console.error('Error parsing teams:', e); } + // Get project names + const projectNames: string[] = []; + try { + if (link.projects) { + const projects = typeof link.projects === 'string' + ? JSON.parse(link.projects) + : link.projects || []; + + if (Array.isArray(projects)) { + projects.forEach(project => { + if (project.project_name) { + projectNames.push(project.project_name); + } + }); + } + } + } catch (e) { + console.error('Error parsing projects:', e); + } + + // Get tag names + const tagNames: string[] = []; + try { + if (link.tags) { + const tags = typeof link.tags === 'string' + ? JSON.parse(link.tags) + : link.tags || []; + + if (Array.isArray(tags)) { + tags.forEach(tag => { + if (tag.tag_name) { + tagNames.push(tag.tag_name); + } + }); + } + } + } catch (e) { + console.error('Error parsing tags:', e); + } + return { title: link.title || attributes.title || 'Untitled', slug: link.slug || attributes.slug || '', domain: domain, originalUrl: link.original_url || attributes.originalUrl || attributes.original_url || '', - teamName: teamName, + teamNames: teamNames, + projectNames: projectNames, + tagNames: tagNames, + teamName: teamNames[0] || '', // Keep for backward compatibility createdAt: new Date(link.created_at).toLocaleDateString(), visits: link.click_count || 0 }; @@ -168,6 +216,9 @@ export default function LinksPage() { slug: '', domain: 'shorturl.example.com', originalUrl: '', + teamNames: [], + projectNames: [], + tagNames: [], teamName: '', createdAt: '', visits: 0 @@ -305,19 +356,22 @@ export default function LinksPage() { /> - +
+ { + // 如果是多选模式,值将是数组。对于空数组,设置为 null + if (Array.isArray(value)) { + setTeamFilter(value.length > 0 ? value[0] : null); + } else { + setTeamFilter(value || null); + } + setCurrentPage(1); // Reset to page 1 when filtering + }} + className="w-64" + multiple={true} + /> +
{/* Links table */} @@ -339,9 +393,20 @@ export default function LinksPage() { return ( -
+
{metadata.title} {shortUrl} + + {/* Tags */} + {metadata.tagNames.length > 0 && ( +
+ {metadata.tagNames.map((tag, index) => ( + + {tag} + + ))} +
+ )}
@@ -356,7 +421,31 @@ export default function LinksPage() { - {metadata.teamName} +
+ {/* Teams */} + {metadata.teamNames.length > 0 ? ( +
+ {metadata.teamNames.map((team, index) => ( + + {team} + + ))} +
+ ) : ( + - + )} + + {/* Projects */} + {metadata.projectNames.length > 0 && ( +
+ {metadata.projectNames.map((project, index) => ( + + {project} + + ))} +
+ )} +
{metadata.createdAt}