Apply UI updates: tags gap, notifications, card click, breadcrumb, move share input

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-02-15 11:10:25 -05:00
parent 8cda50ac96
commit 6be3335ada
16 changed files with 370 additions and 76 deletions

View File

@@ -5,42 +5,37 @@ import useAppStore from '../store/useAppStore'
const CreateUser: React.FC = () => {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [bio, setBio] = useState('')
const [specialties, setSpecialties] = useState('')
const createUser = useAppStore((s) => s.createUser)
const addNotification = useAppStore((s) => s.addNotification)
const navigate = useNavigate()
const onSubmit = (e: React.FormEvent) => {
e.preventDefault()
const parsed = specialties.split(',').map((s) => s.trim()).filter(Boolean)
if (!name || !email || !bio || parsed.length === 0) return alert('All fields required and at least one specialty')
if (!name || !email || !bio || parsed.length === 0) {
addNotification('All fields required and at least one specialty', 'error')
return
}
const newUser = createUser({ name, email, bio, specialties: parsed })
navigate(`/profile/${newUser.id}`)
}
return (
<div className="container">
<div className="card">
<h2>Create User</h2>
<form onSubmit={onSubmit}>
<div>
<label>Name</label>
<input value={name} onChange={(e) => setName(e.target.value)} />
</div>
<div>
<label>Email</label>
<input value={email} onChange={(e) => setEmail(e.target.value)} />
</div>
<div>
<label>Bio</label>
<input value={bio} onChange={(e) => setBio(e.target.value)} />
</div>
<div>
<label>Specialties (comma separated)</label>
<input value={specialties} onChange={(e) => setSpecialties(e.target.value)} />
</div>
<div style={{marginTop:8}}>
<button className="button" type="submit">Create</button>
<div className="create-account-bg" style={{minHeight:'100vh',display:'flex',alignItems:'center',justifyContent:'center'}}>
<div className="create-card">
<h2>Create Account</h2>
<form onSubmit={onSubmit} style={{display:'flex',flexDirection:'column',gap:12}}>
<input className="input" placeholder="Name" value={name} onChange={(e) => setName(e.target.value)} />
<input className="input" placeholder="E-mail" value={email} onChange={(e) => setEmail(e.target.value)} />
<input type="password" className="input" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} />
<textarea className="input" placeholder="Short bio" value={bio} onChange={(e) => setBio(e.target.value)} />
<input className="input" placeholder="Specialties (comma separated)" value={specialties} onChange={(e) => setSpecialties(e.target.value)} />
<div style={{display:'flex',justifyContent:'center',marginTop:8}}>
<button className="btn-gradient" type="submit">SIGN UP</button>
<button type="button" className="btn-ghost" onClick={() => navigate('/')}>SIGN IN</button>
</div>
</form>
</div>

View File

@@ -8,6 +8,8 @@ const Home: React.FC = () => {
const currentUserId = useAppStore((s) => s.currentUserId)
const suggestions = users.filter((u) => u.id !== currentUserId).slice(0, 3)
const toggleCreatePost = useAppStore((s) => s.toggleCreatePost)
return (
<div className="feed-shell">
<div className="container main-card" style={{display:'flex',gap:20,alignItems:'flex-start'}}>
@@ -28,14 +30,15 @@ const Home: React.FC = () => {
<div className="small">Recent · Friends · Popular</div>
</header>
<Feed />
<div className="create-input card" style={{marginTop:12}}>
<input placeholder="Share something..." style={{width:'100%',border:'none',outline:'none'}} />
<div className="create-input card" style={{marginBottom:12}}>
<input placeholder="Share something..." style={{width:'100%',border:'none',outline:'none'}} onFocus={() => toggleCreatePost()} />
<div style={{display:'flex',justifyContent:'flex-end',marginTop:8}}>
<button className="button">Send</button>
<button className="button" onClick={() => toggleCreatePost()}>Send</button>
</div>
</div>
<Feed />
</main>
<aside className="right-column">

View File

@@ -1,25 +1,74 @@
import React from 'react'
import { useParams } from 'react-router-dom'
import React, { useState } from 'react'
import { useParams, Link } from 'react-router-dom'
import useAppStore from '../store/useAppStore'
import PDFPreview from '../components/PDFPreview'
import MarkdownPreview from '../components/MarkdownPreview'
import { generateToken } from '../utils/fileHelpers'
const PostDetail: React.FC = () => {
const { id } = useParams<{ id: string }>()
const post = useAppStore((s) => s.posts.find((p) => p.id === id))
const author = useAppStore((s) => s.users.find((u) => u.id === post?.authorId))
const endorsePost = useAppStore((s) => s.endorsePost)
const currentUserId = useAppStore((s) => s.currentUserId)
const addNotification = useAppStore((s) => s.addNotification)
const [shareUrl, setShareUrl] = useState<string | null>(null)
const [copied, setCopied] = useState(false)
if (!post) return <div className="card">Post not found</div>
const handleEndorse = async () => {
if (!currentUserId) {
addNotification('Select a current user (top-right) to endorse from.', 'error')
return
}
if (currentUserId === post.authorId) {
addNotification("You can't endorse your own post.", 'error')
return
}
endorsePost(post.id)
const token = generateToken(6)
const url = `https://arxiv.org/auth/endorse?x=${token}`
setShareUrl(url)
try {
await navigator.clipboard.writeText(url)
setCopied(true)
addNotification('Share link copied to clipboard', 'success')
} catch {
// ignore
}
}
const handleCopy = async () => {
if (!shareUrl) return
try {
await navigator.clipboard.writeText(shareUrl)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
addNotification('Share link copied to clipboard', 'success')
} catch {
addNotification('Could not copy share link', 'error')
}
}
return (
<div className="container">
<div style={{marginBottom:12}}>
<Link to="/"> Home</Link>
</div>
<div className="card">
<h3>{author?.name}</h3>
<div className="small">{author?.bio}</div>
<div style={{marginTop:8}}>{post.content}</div>
{post.attachedPDF && <PDFPreview url={post.attachedPDF.url} name={post.attachedPDF.name} />}
{post.attachedMarkdown && <MarkdownPreview content={post.attachedMarkdown.content} />}
<div style={{marginTop:8}}>
<button className="button" onClick={() => endorsePost(post.id)}>Endorse Post ({post.endorsements})</button>
<button className="button" onClick={handleEndorse}>Endorse Post ({post.endorsements})</button>
{shareUrl && (
<div className="small" style={{marginTop:8}}>
Share link: <a href={shareUrl} target="_blank" rel="noreferrer">{shareUrl}</a>
<button style={{marginLeft:8}} onClick={handleCopy}>{copied ? 'Copied' : 'Copy'}</button>
</div>
)}
</div>
</div>
</div>

View File

@@ -13,13 +13,30 @@ const Profile: React.FC = () => {
const currentUserId = useAppStore((s) => s.currentUserId)
const endorsementHistory = useAppStore((s) => s.endorsementHistory)
const allUsers = useAppStore((s) => s.users)
const addNotification = useAppStore((s) => s.addNotification)
if (!user) return <div className="card">User not found</div>
const onEmailClick = (e: React.MouseEvent) => {
e.preventDefault()
if (!currentUserId) {
addNotification('Select a current user (top-right) to endorse from.', 'error')
return
}
if (currentUserId === user.id) {
addNotification("You can't endorse yourself.", 'error')
return
}
const specialty = user.specialties[0] ?? 'General'
endorseUser(user.id, specialty)
addNotification(`Endorsed ${user.name} for ${specialty}`, 'success')
}
return (
<div className="container">
<div className="card">
<h2>{user.name}</h2>
<div className="small"><a href="#" onClick={onEmailClick}>{user.email}</a></div>
<div className="small">{user.bio}</div>
<div style={{marginTop:8}}>
{user.specialties.map((s) => (