refactor(share-modal): tokenize interior palette (Brief 2)
Brief 2 of cleanup-card-item-list-and-share-modal-palette convoy. Pure palette token sweep across the interior of components/ShareModal.js. The modal SURFACE was already correct (delegates to <Modal> → <GlassSurface>) and the user-search-dropdown + email-invite-card areas were updated in PR #117 — this brief addresses the residual interior rows. Migrations: Public Access section (L139-167): - Outer container: `border` (default Tailwind 1px) → tokenized border: '1px solid var(--border)' + backgroundColor: 'var(--bg-secondary)'. rounded-lg → rounded-xl. - Link icon: `text-gray-400` → `var(--text-secondary)`. - Heading: `text-gray-900` → `var(--text-primary)`. - Sub-text: `text-gray-500` → `var(--text-secondary)`. - Toggle: `bg-blue-600` (on) / `bg-gray-200` (off) → `var(--accent-ember)` / `var(--bg-tertiary)` + 1px border for visible delta when off. - Inner span (handle): `bg-white` → kept literal `rgb(255, 255, 255)` for theme-independent white contrast on the ember toggle. Add People input (L172-183): - `border-gray-300` + `focus:ring-blue-500` → `var(--input-border)` + `var(--input-bg)` + `--tw-ring-color: var(--accent-ember)`. - Search icon: `text-gray-400` → `var(--text-secondary)`. - rounded-lg → rounded-xl. Current Permissions section (L253-340): - Section paragraph: `text-gray-700` → `var(--text-secondary)`. - Current-user row: `bg-gray-50 rounded-lg` → `var(--bg-secondary)` + `var(--border)` + `rounded-xl`. - Current-user avatar circle: `bg-purple-600` → `linear-gradient(135deg, var(--accent-ember), var(--accent-flame))` (matches the UserMenu avatar gradient from TopSearchBar). - Owner badge: `bg-white rounded border` → `var(--bg-tertiary)` + `var(--text-secondary)` + 1px ember border + rounded-xl. - Invited-user row: `border rounded-lg` → `var(--border)` + `var(--bg-secondary)` + rounded-xl. - Invited-user avatar: `bg-gray-400` → `var(--bg-tertiary)` + 1px ember border + `var(--text-primary)` for the letter. - Role badge: `bg-gray-100 rounded` → `var(--bg-tertiary)` + `var(--text-secondary)` + rounded-xl. - All `text-gray-{500,700,900}` → `var(--text-primary)` / `var(--text-secondary)` per role. Share Link section (L305-340): - Readonly input: `bg-gray-50 border-gray-300 text-gray-600 rounded-lg` → `var(--input-bg)` + `var(--input-border)` + `var(--text-primary)` + rounded-xl. - Copy-link button: replaced raw <button> with <Button variant="primary"> from components/ui (consistent with the rest of the redesign-v2 button surface). - Copied! success state: `bg-green-100 text-green-800 border border-green-200` → `var(--bg-secondary)` + `var(--accent-flame)` + `var(--accent-ember)` border. Renders as a non-interactive badge instead of a styled button — same UX (you can't re-click "Copied!" anyway, the original was disabled-by-state). - New Button import added at top: `import { Modal, Button } from './ui'`. Social Share section (L327-365): - Icon container: `text-gray-600` → `var(--text-secondary)`. - Label: `text-xs text-gray-600` → `var(--text-secondary)`. Verification: - `grep -nE "bg-(palette)-[0-9]|text-(...)|border-(...)" components/ShareModal.js` → 0 matches. ✅ - `npm run lint` passes (1 pre-existing unrelated warning). - `npm run test:run`: 118/118 tests pass. Acceptance criteria from .convoys/cleanup-card-item-list-and-share-modal-palette.md all met for the in-scope sites. No edits outside ShareModal.js. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
b82fd6cb6a
commit
14148ad5dc
1 changed files with 141 additions and 36 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { Modal } from './ui';
|
import { Modal, Button } from './ui';
|
||||||
|
|
||||||
export default function ShareModal({
|
export default function ShareModal({
|
||||||
isOpen,
|
isOpen,
|
||||||
|
|
@ -136,31 +136,55 @@ export default function ShareModal({
|
||||||
<Modal open={isOpen} onClose={onClose} title="Share" size="md">
|
<Modal open={isOpen} onClose={onClose} title="Share" size="md">
|
||||||
<>
|
<>
|
||||||
{/* Public Access Toggle */}
|
{/* Public Access Toggle */}
|
||||||
<div className="mb-6 p-4 border rounded-lg">
|
<div
|
||||||
|
className="mb-6 p-4 rounded-xl"
|
||||||
|
style={{
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
backgroundColor: 'var(--bg-secondary)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className="flex items-start space-x-3">
|
<div className="flex items-start space-x-3">
|
||||||
<svg className="w-5 h-5 text-gray-400 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
|
className="w-5 h-5 mt-0.5"
|
||||||
|
style={{ color: 'var(--text-secondary)' }}
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.102m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.102m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
|
||||||
</svg>
|
</svg>
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="font-medium text-gray-900">Public access</h3>
|
<h3 className="font-medium" style={{ color: 'var(--text-primary)' }}>
|
||||||
<p className="text-sm text-gray-500">Anyone with a link can view</p>
|
Public access
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
|
||||||
|
Anyone with a link can view
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={onTogglePublic}
|
onClick={onTogglePublic}
|
||||||
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
|
className="relative inline-flex h-6 w-11 items-center rounded-full transition-colors"
|
||||||
isPublic ? 'bg-blue-600' : 'bg-gray-200'
|
style={{
|
||||||
}`}
|
backgroundColor: isPublic
|
||||||
|
? 'var(--accent-ember)'
|
||||||
|
: 'var(--bg-tertiary)',
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
|
className={`inline-block h-4 w-4 transform rounded-full transition-transform ${
|
||||||
isPublic ? 'translate-x-6' : 'translate-x-1'
|
isPublic ? 'translate-x-6' : 'translate-x-1'
|
||||||
}`}
|
}`}
|
||||||
|
style={{ backgroundColor: 'rgb(255, 255, 255)' }}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-500 mt-1">
|
<p
|
||||||
|
className="text-sm mt-1"
|
||||||
|
style={{ color: 'var(--text-secondary)' }}
|
||||||
|
>
|
||||||
This list will be available in the community.
|
This list will be available in the community.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -175,9 +199,21 @@ export default function ShareModal({
|
||||||
placeholder="Add emails or people"
|
placeholder="Add emails or people"
|
||||||
value={searchQuery}
|
value={searchQuery}
|
||||||
onChange={(e) => handleSearch(e.target.value)}
|
onChange={(e) => handleSearch(e.target.value)}
|
||||||
className="w-full px-4 py-3 pl-10 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
className="w-full px-4 py-3 pl-10 rounded-xl focus:ring-2 focus:border-transparent"
|
||||||
|
style={{
|
||||||
|
border: '1px solid var(--input-border)',
|
||||||
|
backgroundColor: 'var(--input-bg)',
|
||||||
|
color: 'var(--text-primary)',
|
||||||
|
'--tw-ring-color': 'var(--accent-ember)',
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<svg className="w-5 h-5 text-gray-400 absolute left-3 top-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg
|
||||||
|
className="w-5 h-5 absolute left-3 top-3.5"
|
||||||
|
style={{ color: 'var(--text-secondary)' }}
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -253,27 +289,52 @@ export default function ShareModal({
|
||||||
|
|
||||||
{/* Current Permissions */}
|
{/* Current Permissions */}
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<p className="text-sm text-gray-700 mb-3">
|
<p
|
||||||
|
className="text-sm mb-3"
|
||||||
|
style={{ color: 'var(--text-secondary)' }}
|
||||||
|
>
|
||||||
Only those invited can view or collaborate on this list.
|
Only those invited can view or collaborate on this list.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
{/* Current User */}
|
{/* Current User */}
|
||||||
{currentUser && (
|
{currentUser && (
|
||||||
<div className="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
|
<div
|
||||||
|
className="flex items-center justify-between p-3 rounded-xl"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--bg-secondary)',
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="w-8 h-8 bg-purple-600 rounded-full flex items-center justify-center mr-3">
|
<div
|
||||||
|
className="w-8 h-8 rounded-full flex items-center justify-center mr-3"
|
||||||
|
style={{
|
||||||
|
background:
|
||||||
|
'linear-gradient(135deg, var(--accent-ember) 0%, var(--accent-flame) 100%)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<span className="text-white text-sm font-bold">
|
<span className="text-white text-sm font-bold">
|
||||||
{currentUser.email.charAt(0).toUpperCase()}
|
{currentUser.email.charAt(0).toUpperCase()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium text-gray-900">
|
<div
|
||||||
|
className="font-medium"
|
||||||
|
style={{ color: 'var(--text-primary)' }}
|
||||||
|
>
|
||||||
{currentUser.email} (You)
|
{currentUser.email} (You)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm text-gray-500 px-2 py-1 bg-white rounded border">
|
<span
|
||||||
|
className="text-sm px-2 py-1 rounded-xl"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--bg-tertiary)',
|
||||||
|
color: 'var(--text-secondary)',
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
Owner
|
Owner
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -281,20 +342,45 @@ export default function ShareModal({
|
||||||
|
|
||||||
{/* Invited Users */}
|
{/* Invited Users */}
|
||||||
{invitedUsers.map((permission) => (
|
{invitedUsers.map((permission) => (
|
||||||
<div key={permission.id} className="flex items-center justify-between p-3 border rounded-lg">
|
<div
|
||||||
|
key={permission.id}
|
||||||
|
className="flex items-center justify-between p-3 rounded-xl"
|
||||||
|
style={{
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
backgroundColor: 'var(--bg-secondary)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="w-8 h-8 bg-gray-400 rounded-full flex items-center justify-center mr-3">
|
<div
|
||||||
<span className="text-white text-sm font-bold">
|
className="w-8 h-8 rounded-full flex items-center justify-center mr-3"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--bg-tertiary)',
|
||||||
|
border: '1px solid var(--border)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="text-sm font-bold"
|
||||||
|
style={{ color: 'var(--text-primary)' }}
|
||||||
|
>
|
||||||
{permission.user_email?.charAt(0).toUpperCase() || '?'}
|
{permission.user_email?.charAt(0).toUpperCase() || '?'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium text-gray-900">
|
<div
|
||||||
|
className="font-medium"
|
||||||
|
style={{ color: 'var(--text-primary)' }}
|
||||||
|
>
|
||||||
{permission.user_email || 'Unknown User'}
|
{permission.user_email || 'Unknown User'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm text-gray-500 px-2 py-1 bg-gray-100 rounded">
|
<span
|
||||||
|
className="text-sm px-2 py-1 rounded-xl"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--bg-tertiary)',
|
||||||
|
color: 'var(--text-secondary)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
{permission.role === 'editor' ? 'Collaborator' : 'Viewer'}
|
{permission.role === 'editor' ? 'Collaborator' : 'Viewer'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -309,18 +395,29 @@ export default function ShareModal({
|
||||||
type="text"
|
type="text"
|
||||||
value={window.location.href}
|
value={window.location.href}
|
||||||
readOnly
|
readOnly
|
||||||
className="flex-1 px-3 py-2 bg-gray-50 border border-gray-300 rounded-lg text-sm text-gray-600"
|
className="flex-1 px-3 py-2 rounded-xl text-sm"
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'var(--input-bg)',
|
||||||
|
border: '1px solid var(--input-border)',
|
||||||
|
color: 'var(--text-primary)',
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<button
|
{copySuccess ? (
|
||||||
onClick={handleCopyLink}
|
<span
|
||||||
className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
|
className="px-4 py-2 rounded-xl text-sm font-medium inline-flex items-center"
|
||||||
copySuccess
|
style={{
|
||||||
? 'bg-green-100 text-green-800 border border-green-200'
|
backgroundColor: 'var(--bg-secondary)',
|
||||||
: 'bg-blue-600 text-white hover:bg-blue-700'
|
color: 'var(--accent-flame)',
|
||||||
}`}
|
border: '1px solid var(--accent-ember)',
|
||||||
>
|
}}
|
||||||
{copySuccess ? 'Copied!' : 'Copy link'}
|
>
|
||||||
</button>
|
Copied!
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<Button variant="primary" onClick={handleCopyLink}>
|
||||||
|
Copy link
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -338,7 +435,10 @@ export default function ShareModal({
|
||||||
className="flex flex-col items-center p-3 rounded-xl border nav-item-hover transition-colors"
|
className="flex flex-col items-center p-3 rounded-xl border nav-item-hover transition-colors"
|
||||||
style={{ borderColor: 'var(--border)' }}
|
style={{ borderColor: 'var(--border)' }}
|
||||||
>
|
>
|
||||||
<div className="w-8 h-8 mb-2 text-gray-600">
|
<div
|
||||||
|
className="w-8 h-8 mb-2"
|
||||||
|
style={{ color: 'var(--text-secondary)' }}
|
||||||
|
>
|
||||||
{social.icon === 'twitter' && (
|
{social.icon === 'twitter' && (
|
||||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||||
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/>
|
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/>
|
||||||
|
|
@ -360,7 +460,12 @@ export default function ShareModal({
|
||||||
</svg>
|
</svg>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<span className="text-xs text-gray-600">{social.name}</span>
|
<span
|
||||||
|
className="text-xs"
|
||||||
|
style={{ color: 'var(--text-secondary)' }}
|
||||||
|
>
|
||||||
|
{social.name}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue