links info

This commit is contained in:
2025-04-07 22:17:53 +08:00
parent 0c4a67e769
commit f782dba0c9
2 changed files with 110 additions and 21 deletions

View File

@@ -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 ');

View File

@@ -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() {
/>
</div>
<select
value={teamFilter || ''}
onChange={(e) => {
setTeamFilter(e.target.value || null);
setCurrentPage(1); // Reset to page 1 when filtering
}}
className="rounded-md border border-gray-300 py-2 pl-3 pr-10 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
>
<option value="">All Teams</option>
{teams.map(team => (
<option key={team.id} value={team.id}>{team.name}</option>
))}
</select>
<div className="flex items-center gap-2">
<TeamSelector
value={teamFilter || ''}
onChange={(value) => {
// 如果是多选模式,值将是数组。对于空数组,设置为 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}
/>
</div>
</div>
{/* Links table */}
@@ -339,9 +393,20 @@ export default function LinksPage() {
return (
<tr key={link.id} className="hover:bg-gray-50">
<td className="px-6 py-4">
<div className="flex flex-col">
<div className="flex flex-col space-y-1">
<span className="font-medium text-gray-900">{metadata.title}</span>
<span className="text-xs text-blue-500">{shortUrl}</span>
{/* Tags */}
{metadata.tagNames.length > 0 && (
<div className="flex flex-wrap gap-1.5 mt-1">
{metadata.tagNames.map((tag, index) => (
<span key={index} className="inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-800">
{tag}
</span>
))}
</div>
)}
</div>
</td>
<td className="px-6 py-4 text-sm text-gray-500">
@@ -356,7 +421,31 @@ export default function LinksPage() {
</a>
</td>
<td className="px-6 py-4 text-sm text-gray-500">
{metadata.teamName}
<div className="flex flex-col space-y-1">
{/* Teams */}
{metadata.teamNames.length > 0 ? (
<div className="flex flex-wrap gap-1.5">
{metadata.teamNames.map((team, index) => (
<span key={index} className="inline-flex items-center rounded-full bg-blue-100 px-2 py-0.5 text-xs font-medium text-blue-800">
{team}
</span>
))}
</div>
) : (
<span>-</span>
)}
{/* Projects */}
{metadata.projectNames.length > 0 && (
<div className="flex flex-wrap gap-1.5 mt-1">
{metadata.projectNames.map((project, index) => (
<span key={index} className="inline-flex items-center rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800">
{project}
</span>
))}
</div>
)}
</div>
</td>
<td className="px-6 py-4 text-sm text-gray-500">
{metadata.createdAt}