Skip to main content
Glama
northernvariables

FedMCP - Federal Parliamentary Information

CollectionManager.tsx7.75 kB
/** * CollectionManager Component * * Tier-aware collection management for organizing bookmarks * Available for BASIC+ tiers only */ 'use client'; import { useState } from 'react'; import { Folder, Plus, X, Lock } from 'lucide-react'; import { useAuth } from '@/contexts/AuthContext'; interface Collection { id: string; name: string; description?: string; bookmark_count: number; } interface CollectionManagerProps { collections?: Collection[]; selectedCollectionId?: string | null; onSelectCollection?: (collectionId: string | null) => void; onCreateCollection?: (name: string, description?: string) => Promise<void>; onDeleteCollection?: (collectionId: string) => Promise<void>; } export function CollectionManager({ collections = [], selectedCollectionId, onSelectCollection, onCreateCollection, onDeleteCollection, }: CollectionManagerProps) { const { profile } = useAuth(); const [showCreateForm, setShowCreateForm] = useState(false); const [newCollectionName, setNewCollectionName] = useState(''); const [newCollectionDesc, setNewCollectionDesc] = useState(''); const [creating, setCreating] = useState(false); const tier = profile?.subscription_tier || 'FREE'; const canUseCollections = tier === 'BASIC' || tier === 'PRO'; const handleCreate = async () => { if (!newCollectionName.trim() || !onCreateCollection) return; setCreating(true); try { await onCreateCollection(newCollectionName.trim(), newCollectionDesc.trim() || undefined); setNewCollectionName(''); setNewCollectionDesc(''); setShowCreateForm(false); } catch (error) { console.error('Failed to create collection:', error); alert('Failed to create collection'); } finally { setCreating(false); } }; const handleDelete = async (collectionId: string) => { if (!onDeleteCollection) return; if (!confirm('Delete this collection? Bookmarks will not be deleted.')) return; try { await onDeleteCollection(collectionId); } catch (error) { console.error('Failed to delete collection:', error); alert('Failed to delete collection'); } }; // Free tier - show upgrade prompt if (!canUseCollections) { return ( <div className="p-4 bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800 rounded-lg"> <div className="flex items-start gap-3"> <Lock className="w-5 h-5 text-amber-600 dark:text-amber-400 flex-shrink-0 mt-0.5" /> <div> <h4 className="font-semibold text-amber-900 dark:text-amber-100 mb-1"> Collections (BASIC+) </h4> <p className="text-sm text-amber-800 dark:text-amber-200 mb-2"> Organize your bookmarks into folders with BASIC or PRO tier. </p> <a href="/pricing" className="text-sm text-accent-red hover:text-red-700 dark:hover:text-red-400 font-medium" > Upgrade to unlock → </a> </div> </div> </div> ); } return ( <div className="space-y-2"> {/* Header */} <div className="flex items-center justify-between mb-2"> <h3 className="text-sm font-semibold text-text-primary flex items-center gap-2"> <Folder size={16} /> Collections </h3> <button onClick={() => setShowCreateForm(!showCreateForm)} className="p-1 rounded hover:bg-bg-overlay text-text-tertiary hover:text-accent-red transition-colors" title="Create collection" > <Plus size={16} /> </button> </div> {/* Create Form */} {showCreateForm && ( <div className="p-3 bg-bg-elevated border border-border-subtle rounded-lg space-y-2"> <input type="text" value={newCollectionName} onChange={(e) => setNewCollectionName(e.target.value)} placeholder="Collection name" maxLength={50} className="w-full px-3 py-2 rounded bg-bg-primary border border-border text-sm text-text-primary focus:border-accent-red focus:outline-none" onKeyDown={(e) => { if (e.key === 'Enter') handleCreate(); if (e.key === 'Escape') setShowCreateForm(false); }} /> <textarea value={newCollectionDesc} onChange={(e) => setNewCollectionDesc(e.target.value)} placeholder="Description (optional)" maxLength={200} rows={2} className="w-full px-3 py-2 rounded bg-bg-primary border border-border text-sm text-text-primary focus:border-accent-red focus:outline-none resize-none" /> <div className="flex gap-2"> <button onClick={handleCreate} disabled={!newCollectionName.trim() || creating} className="flex-1 px-3 py-1.5 bg-accent-red text-white text-sm font-medium rounded hover:bg-accent-red-hover disabled:opacity-50 disabled:cursor-not-allowed" > {creating ? 'Creating...' : 'Create'} </button> <button onClick={() => { setShowCreateForm(false); setNewCollectionName(''); setNewCollectionDesc(''); }} className="px-3 py-1.5 bg-bg-overlay text-text-secondary text-sm font-medium rounded hover:bg-bg-elevated" > Cancel </button> </div> </div> )} {/* Collections List */} {collections.length > 0 ? ( <div className="space-y-1"> {/* All Bookmarks (default) */} <button onClick={() => onSelectCollection?.(null)} className={`w-full flex items-center justify-between px-3 py-2 rounded-lg text-sm transition-colors ${ selectedCollectionId === null ? 'bg-accent-red/10 text-accent-red font-medium' : 'text-text-secondary hover:bg-bg-overlay hover:text-text-primary' }`} > <span className="flex items-center gap-2"> <Folder size={14} /> All Bookmarks </span> </button> {/* User Collections */} {collections.map((collection) => ( <div key={collection.id} className={`group flex items-center justify-between px-3 py-2 rounded-lg text-sm transition-colors ${ selectedCollectionId === collection.id ? 'bg-accent-red/10 text-accent-red font-medium' : 'text-text-secondary hover:bg-bg-overlay hover:text-text-primary' }`} > <button onClick={() => onSelectCollection?.(collection.id)} className="flex-1 flex items-center gap-2 text-left" > <Folder size={14} /> <span className="truncate">{collection.name}</span> <span className="text-xs opacity-60">({collection.bookmark_count})</span> </button> <button onClick={() => handleDelete(collection.id)} className="opacity-0 group-hover:opacity-100 p-1 rounded hover:bg-red-50 dark:hover:bg-red-900/20 text-red-600 transition-opacity" title="Delete collection" > <X size={12} /> </button> </div> ))} </div> ) : ( <div className="text-sm text-text-tertiary italic text-center py-4"> No collections yet </div> )} </div> ); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/northernvariables/FedMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server