feat: design system overhaul — sidebar, AI chats, settings, brainstorm, color cleanup
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 12s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 12s
- Sidebar: dynamic brand-accent colors, brainstorm section restyled - AI chat general: popup panel with expand/collapse, hides when contextual AI open - AI chat contextual: tabs reordered (Actions first), X close button, height fix - Settings: all tabs restyled, 6 new color presets (sage, terracotta, iron, etc.) - Global color cleanup: emerald/orange hardcoded → brand-accent dynamic - Brainstorm page: orange → brand-accent throughout - PageEntry animation component added to key pages - Floating AI button: bg-brand-accent instead of hardcoded black - i18n: all 15 locales updated with new AI/billing keys - Billing: freemium quota tracking, BYOK, stripe subscription scaffolding - Admin: integrated into new design - AGENTS.md + CLAUDE.md project rules added
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
import { UserPlus, Check, AlertCircle, Globe, Lock, Copy } from 'lucide-react'
|
||||
import { createBrainstormShare } from '@/app/actions/brainstorm'
|
||||
import { useUpdateBrainstormSettings } from '@/hooks/use-brainstorm'
|
||||
import { useLanguage } from '@/lib/i18n'
|
||||
|
||||
interface BrainstormShareDialogProps {
|
||||
open: boolean
|
||||
@@ -28,15 +29,15 @@ interface UserResult {
|
||||
image: string | null
|
||||
}
|
||||
|
||||
const MESSAGES: Record<string, { text: string; type: 'success' | 'info' }> = {
|
||||
invited: { text: 'Invitation envoyée !', type: 'success' },
|
||||
re_invited: { text: 'Invitation renvoyée !', type: 'success' },
|
||||
const FEEDBACK_BY_MESSAGE: Record<string, { key: string; type: 'success' | 'info' }> = {
|
||||
invited: { key: 'brainstorm.feedbackInviteSent', type: 'success' },
|
||||
re_invited: { key: 'brainstorm.feedbackInviteResent', type: 'success' },
|
||||
already_shared: {
|
||||
text: 'Cette personne a déjà accès à ce brainstorm.',
|
||||
key: 'brainstorm.feedbackAlreadyShared',
|
||||
type: 'info',
|
||||
},
|
||||
already_pending: {
|
||||
text: 'Une invitation est déjà en attente pour cette personne.',
|
||||
key: 'brainstorm.feedbackAlreadyPending',
|
||||
type: 'info',
|
||||
},
|
||||
}
|
||||
@@ -49,6 +50,7 @@ export function BrainstormShareDialog({
|
||||
isPublic = false,
|
||||
guestCanEdit = false,
|
||||
}: BrainstormShareDialogProps) {
|
||||
const { t } = useLanguage()
|
||||
const [query, setQuery] = useState('')
|
||||
const [results, setResults] = useState<UserResult[]>([])
|
||||
const [showDropdown, setShowDropdown] = useState(false)
|
||||
@@ -115,17 +117,17 @@ export function BrainstormShareDialog({
|
||||
startTransition(async () => {
|
||||
try {
|
||||
const result = await createBrainstormShare(sessionId, query.trim())
|
||||
if (result.message && MESSAGES[result.message]) {
|
||||
const m = MESSAGES[result.message]
|
||||
setFeedback({ text: m.text, type: m.type })
|
||||
if (result.message && FEEDBACK_BY_MESSAGE[result.message]) {
|
||||
const m = FEEDBACK_BY_MESSAGE[result.message]
|
||||
setFeedback({ text: t(m.key), type: m.type })
|
||||
} else {
|
||||
setFeedback({ text: 'Invitation envoyée !', type: 'success' })
|
||||
setFeedback({ text: t('brainstorm.feedbackInviteSent'), type: 'success' })
|
||||
}
|
||||
if (result.message === 'invited' || result.message === 're_invited') {
|
||||
setQuery('')
|
||||
}
|
||||
} catch (err: any) {
|
||||
setFeedback({ text: err.message || 'Erreur', type: 'error' })
|
||||
setFeedback({ text: err.message || t('brainstorm.feedbackGenericError'), type: 'error' })
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -138,7 +140,7 @@ export function BrainstormShareDialog({
|
||||
<div className="w-7 h-7 rounded-lg bg-orange-500/10 flex items-center justify-center">
|
||||
<UserPlus size={14} className="text-orange-500" />
|
||||
</div>
|
||||
<span className="font-serif">Partager le brainstorm</span>
|
||||
<span className="font-serif">{t('brainstorm.shareDialogTitle')}</span>
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-muted-foreground text-xs italic font-serif truncate">
|
||||
{seedIdea.length > 60 ? seedIdea.substring(0, 60) + '…' : seedIdea}
|
||||
@@ -148,7 +150,7 @@ export function BrainstormShareDialog({
|
||||
<form onSubmit={handleShare} className="space-y-3 mt-2">
|
||||
<div className="relative" ref={dropdownRef}>
|
||||
<label className="text-[10px] font-bold uppercase tracking-[0.15em] text-muted-foreground mb-1.5 block">
|
||||
Rechercher une personne
|
||||
{t('brainstorm.shareSearchLabel')}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
@@ -156,7 +158,7 @@ export function BrainstormShareDialog({
|
||||
onChange={(e) => handleInputChange(e.target.value)}
|
||||
onFocus={() => results.length > 0 && setShowDropdown(true)}
|
||||
onBlur={() => setTimeout(() => setShowDropdown(false), 150)}
|
||||
placeholder="Nom ou email…"
|
||||
placeholder={t('brainstorm.shareNameOrEmailPlaceholder')}
|
||||
className="w-full px-4 py-3 text-sm border border-border rounded-xl bg-transparent focus:outline-none focus:ring-2 focus:ring-orange-500/20 focus:border-orange-500/40 transition-all"
|
||||
autoFocus
|
||||
/>
|
||||
@@ -178,7 +180,7 @@ export function BrainstormShareDialog({
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm font-medium text-foreground truncate">
|
||||
{user.name || 'Sans nom'}
|
||||
{user.name || t('brainstorm.unnamedPerson')}
|
||||
</p>
|
||||
<p className="text-[11px] text-muted-foreground truncate">
|
||||
{user.email}
|
||||
@@ -215,12 +217,12 @@ export function BrainstormShareDialog({
|
||||
className="w-full py-3 bg-orange-500 hover:bg-orange-600 text-white text-[10px] font-bold uppercase tracking-[0.15em] rounded-xl disabled:opacity-50 transition-all flex items-center justify-center gap-1.5"
|
||||
>
|
||||
<UserPlus size={12} />
|
||||
{isPending ? 'Envoi…' : 'Partager'}
|
||||
{isPending ? t('brainstorm.shareSubmitting') : t('brainstorm.shareSubmit')}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p className="text-[10px] text-muted-foreground/60 text-center mt-1">
|
||||
La personne recevra une notification pour accepter ou refuser.
|
||||
{t('brainstorm.shareFooterHint')}
|
||||
</p>
|
||||
|
||||
<div className="border-t border-border mt-4 pt-4">
|
||||
@@ -232,7 +234,7 @@ export function BrainstormShareDialog({
|
||||
<Lock size={14} className="text-muted-foreground" />
|
||||
)}
|
||||
<span className="text-[10px] font-bold uppercase tracking-[0.15em] text-muted-foreground">
|
||||
Lien public
|
||||
{t('brainstorm.sharePublicLink')}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
@@ -272,7 +274,7 @@ export function BrainstormShareDialog({
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-[10px] text-muted-foreground">
|
||||
Autoriser les invités à modifier
|
||||
{t('brainstorm.shareGuestsCanEdit')}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => {
|
||||
|
||||
Reference in New Issue
Block a user