Barazo default frontend barazo.forum

feat(pages): make admin page rows clickable, add view links (#158)

- Whole card is now a clickable link to the edit page
- Added external link icon to view the public page in a new tab
- Removed separate pencil icon (redundant with card click)
- Added "View page" link on the edit page header

authored by

Guido X Jansen and committed by
GitHub
9308cfae a5754fee

+46 -15
+17 -3
src/app/admin/pages/[id]/page.tsx
··· 7 7 8 8 import { useState, useEffect, useCallback } from 'react' 9 9 import { useRouter, useParams } from 'next/navigation' 10 + import { ArrowSquareOut } from '@phosphor-icons/react' 10 11 import { AdminLayout } from '@/components/admin/admin-layout' 11 12 import { ErrorAlert } from '@/components/error-alert' 12 13 import { PageForm } from '@/components/admin/pages/page-form' ··· 157 158 return ( 158 159 <AdminLayout> 159 160 <div className="mx-auto max-w-3xl space-y-6"> 160 - <h1 className="text-2xl font-bold text-foreground"> 161 - {isCreateMode ? 'Create Page' : 'Edit Page'} 162 - </h1> 161 + <div className="flex items-center gap-3"> 162 + <h1 className="text-2xl font-bold text-foreground"> 163 + {isCreateMode ? 'Create Page' : 'Edit Page'} 164 + </h1> 165 + {!isCreateMode && form.slug && ( 166 + <a 167 + href={`/p/${form.slug}`} 168 + target="_blank" 169 + rel="noopener noreferrer" 170 + className="inline-flex items-center gap-1 rounded-md px-2 py-1 text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground" 171 + > 172 + <ArrowSquareOut size={16} aria-hidden="true" /> 173 + View page 174 + </a> 175 + )} 176 + </div> 163 177 164 178 {error && <ErrorAlert message={error} onDismiss={() => setError(null)} />} 165 179
+8 -1
src/components/admin/pages/page-row.test.tsx
··· 66 66 expect(screen.getByText('Draft')).toBeInTheDocument() 67 67 }) 68 68 69 - it('renders edit link to admin page editor', () => { 69 + it('makes the whole card a link to the edit page', () => { 70 70 render(<PageRow page={mockPage} depth={0} onDelete={vi.fn()} />) 71 71 const editLink = screen.getByRole('link', { name: /edit about this community/i }) 72 72 expect(editLink).toHaveAttribute('href', '/admin/pages/page-about') 73 + }) 74 + 75 + it('renders view link that opens public page in new tab', () => { 76 + render(<PageRow page={mockPage} depth={0} onDelete={vi.fn()} />) 77 + const viewLink = screen.getByRole('link', { name: /view about this community/i }) 78 + expect(viewLink).toHaveAttribute('href', '/p/about') 79 + expect(viewLink).toHaveAttribute('target', '_blank') 73 80 }) 74 81 75 82 it('renders delete button', () => {
+21 -11
src/components/admin/pages/page-row.tsx
··· 4 4 */ 5 5 6 6 import Link from 'next/link' 7 - import { PencilSimple, TrashSimple } from '@phosphor-icons/react' 7 + import { ArrowSquareOut, TrashSimple } from '@phosphor-icons/react' 8 8 import { cn } from '@/lib/utils' 9 9 import type { PageTreeNode, PageStatus } from '@/lib/api/types' 10 10 ··· 30 30 <div 31 31 data-depth={depth} 32 32 className={cn( 33 - 'flex items-center justify-between rounded-md border border-border bg-card p-3', 33 + 'group relative flex items-center justify-between rounded-md border border-border bg-card p-3 transition-colors hover:bg-card-hover', 34 34 depth > 0 && 'ml-6' 35 35 )} 36 36 > 37 + <Link 38 + href={`/admin/pages/${page.id}`} 39 + className="absolute inset-0 rounded-md focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2" 40 + aria-label={`Edit ${page.title}`} 41 + /> 37 42 <div className="flex items-center gap-3"> 38 43 <div> 39 44 <p className="text-sm font-medium text-foreground">{page.title}</p> 40 45 <p className="text-xs text-muted-foreground">/p/{page.slug}</p> 41 46 </div> 42 47 </div> 43 - <div className="flex items-center gap-2"> 48 + <div className="relative flex items-center gap-2"> 44 49 <span 45 50 className={cn( 46 51 'rounded-full px-2 py-0.5 text-xs font-medium', ··· 49 54 > 50 55 {STATUS_LABELS[page.status]} 51 56 </span> 52 - <Link 53 - href={`/admin/pages/${page.id}`} 54 - className="rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground" 55 - aria-label={`Edit ${page.title}`} 57 + <a 58 + href={`/p/${page.slug}`} 59 + target="_blank" 60 + rel="noopener noreferrer" 61 + className="relative rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground" 62 + aria-label={`View ${page.title}`} 56 63 > 57 - <PencilSimple size={16} aria-hidden="true" /> 58 - </Link> 64 + <ArrowSquareOut size={16} aria-hidden="true" /> 65 + </a> 59 66 <button 60 67 type="button" 61 - onClick={() => onDelete(page.id)} 62 - className="rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive" 68 + onClick={(e) => { 69 + e.preventDefault() 70 + onDelete(page.id) 71 + }} 72 + className="relative rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive" 63 73 aria-label={`Delete ${page.title}`} 64 74 > 65 75 <TrashSimple size={16} aria-hidden="true" />