links info
This commit is contained in:
@@ -30,7 +30,7 @@ export async function GET(request: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (team) {
|
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 ');
|
const whereClause = whereConditions.join(' AND ');
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useEffect, useState } from 'react';
|
|||||||
import { getSupabaseClient } from '../utils/supabase';
|
import { getSupabaseClient } from '../utils/supabase';
|
||||||
import { AuthChangeEvent } from '@supabase/supabase-js';
|
import { AuthChangeEvent } from '@supabase/supabase-js';
|
||||||
import { Loader2, ExternalLink, Search } from 'lucide-react';
|
import { Loader2, ExternalLink, Search } from 'lucide-react';
|
||||||
|
import { TeamSelector } from '@/app/components/ui/TeamSelector';
|
||||||
|
|
||||||
// Define attribute type to avoid using 'any'
|
// Define attribute type to avoid using 'any'
|
||||||
interface LinkAttributes {
|
interface LinkAttributes {
|
||||||
@@ -136,28 +137,75 @@ export default function LinksPage() {
|
|||||||
console.error('Error parsing attributes:', e);
|
console.error('Error parsing attributes:', e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get team name
|
// Get team names
|
||||||
let teamName = '';
|
const teamNames: string[] = [];
|
||||||
try {
|
try {
|
||||||
if (link.teams) {
|
if (link.teams) {
|
||||||
const teams = typeof link.teams === 'string'
|
const teams = typeof link.teams === 'string'
|
||||||
? JSON.parse(link.teams)
|
? JSON.parse(link.teams)
|
||||||
: link.teams || [];
|
: link.teams || [];
|
||||||
|
|
||||||
if (Array.isArray(teams) && teams.length > 0 && teams[0].team_name) {
|
if (Array.isArray(teams)) {
|
||||||
teamName = teams[0].team_name;
|
teams.forEach(team => {
|
||||||
|
if (team.team_name) {
|
||||||
|
teamNames.push(team.team_name);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error parsing teams:', 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 {
|
return {
|
||||||
title: link.title || attributes.title || 'Untitled',
|
title: link.title || attributes.title || 'Untitled',
|
||||||
slug: link.slug || attributes.slug || '',
|
slug: link.slug || attributes.slug || '',
|
||||||
domain: domain,
|
domain: domain,
|
||||||
originalUrl: link.original_url || attributes.originalUrl || attributes.original_url || '',
|
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(),
|
createdAt: new Date(link.created_at).toLocaleDateString(),
|
||||||
visits: link.click_count || 0
|
visits: link.click_count || 0
|
||||||
};
|
};
|
||||||
@@ -168,6 +216,9 @@ export default function LinksPage() {
|
|||||||
slug: '',
|
slug: '',
|
||||||
domain: 'shorturl.example.com',
|
domain: 'shorturl.example.com',
|
||||||
originalUrl: '',
|
originalUrl: '',
|
||||||
|
teamNames: [],
|
||||||
|
projectNames: [],
|
||||||
|
tagNames: [],
|
||||||
teamName: '',
|
teamName: '',
|
||||||
createdAt: '',
|
createdAt: '',
|
||||||
visits: 0
|
visits: 0
|
||||||
@@ -305,19 +356,22 @@ export default function LinksPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<select
|
<div className="flex items-center gap-2">
|
||||||
|
<TeamSelector
|
||||||
value={teamFilter || ''}
|
value={teamFilter || ''}
|
||||||
onChange={(e) => {
|
onChange={(value) => {
|
||||||
setTeamFilter(e.target.value || null);
|
// 如果是多选模式,值将是数组。对于空数组,设置为 null
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
setTeamFilter(value.length > 0 ? value[0] : null);
|
||||||
|
} else {
|
||||||
|
setTeamFilter(value || null);
|
||||||
|
}
|
||||||
setCurrentPage(1); // Reset to page 1 when filtering
|
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"
|
className="w-64"
|
||||||
>
|
multiple={true}
|
||||||
<option value="">All Teams</option>
|
/>
|
||||||
{teams.map(team => (
|
</div>
|
||||||
<option key={team.id} value={team.id}>{team.name}</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Links table */}
|
{/* Links table */}
|
||||||
@@ -339,9 +393,20 @@ export default function LinksPage() {
|
|||||||
return (
|
return (
|
||||||
<tr key={link.id} className="hover:bg-gray-50">
|
<tr key={link.id} className="hover:bg-gray-50">
|
||||||
<td className="px-6 py-4">
|
<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="font-medium text-gray-900">{metadata.title}</span>
|
||||||
<span className="text-xs text-blue-500">{shortUrl}</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>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 text-sm text-gray-500">
|
<td className="px-6 py-4 text-sm text-gray-500">
|
||||||
@@ -356,7 +421,31 @@ export default function LinksPage() {
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-6 py-4 text-sm text-gray-500">
|
<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>
|
||||||
<td className="px-6 py-4 text-sm text-gray-500">
|
<td className="px-6 py-4 text-sm text-gray-500">
|
||||||
{metadata.createdAt}
|
{metadata.createdAt}
|
||||||
|
|||||||
Reference in New Issue
Block a user