video preview version
This commit is contained in:
@@ -17,19 +17,37 @@ import Users from './pages/Users'
|
||||
import Settings from './pages/Settings'
|
||||
import Login from './pages/Login'
|
||||
import Tutorial from './components/Tutorial'
|
||||
import Modal from './components/Modal'
|
||||
import { api } from './utils/api'
|
||||
import { useLanguage } from './i18n/LanguageContext'
|
||||
|
||||
const TEAM_ROLES = [
|
||||
{ value: 'manager', label: 'Manager' },
|
||||
{ value: 'approver', label: 'Approver' },
|
||||
{ value: 'publisher', label: 'Publisher' },
|
||||
{ value: 'content_creator', label: 'Content Creator' },
|
||||
{ value: 'producer', label: 'Producer' },
|
||||
{ value: 'designer', label: 'Designer' },
|
||||
{ value: 'content_writer', label: 'Content Writer' },
|
||||
{ value: 'social_media_manager', label: 'Social Media Manager' },
|
||||
{ value: 'photographer', label: 'Photographer' },
|
||||
{ value: 'videographer', label: 'Videographer' },
|
||||
{ value: 'strategist', label: 'Strategist' },
|
||||
]
|
||||
|
||||
export const AppContext = createContext()
|
||||
|
||||
function AppContent() {
|
||||
const { user, loading: authLoading } = useAuth()
|
||||
const { user, loading: authLoading, checkAuth } = useAuth()
|
||||
const { t } = useLanguage()
|
||||
const [teamMembers, setTeamMembers] = useState([])
|
||||
const [brands, setBrands] = useState([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [showTutorial, setShowTutorial] = useState(false)
|
||||
const [showProfilePrompt, setShowProfilePrompt] = useState(false)
|
||||
const [showProfileModal, setShowProfileModal] = useState(false)
|
||||
const [profileForm, setProfileForm] = useState({ name: '', team_role: '', phone: '', brands: '' })
|
||||
const [profileSaving, setProfileSaving] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (user && !authLoading) {
|
||||
@@ -61,11 +79,10 @@ function AppContent() {
|
||||
|
||||
const loadInitialData = async () => {
|
||||
try {
|
||||
const [members, brandsData] = await Promise.all([
|
||||
const [, brandsData] = await Promise.all([
|
||||
loadTeam(),
|
||||
api.get('/brands').then(d => Array.isArray(d) ? d : (d.data || [])).catch(() => []),
|
||||
])
|
||||
setTeamMembers(members)
|
||||
setBrands(brandsData)
|
||||
} catch (err) {
|
||||
console.error('Failed to load initial data:', err)
|
||||
@@ -109,12 +126,20 @@ function AppContent() {
|
||||
{t('profile.completeDesc')}
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<a
|
||||
href="/team"
|
||||
<button
|
||||
onClick={() => {
|
||||
setProfileForm({
|
||||
name: user?.name || '',
|
||||
team_role: user?.teamRole || user?.team_role || '',
|
||||
phone: user?.phone || '',
|
||||
brands: Array.isArray(user?.brands) ? user.brands.join(', ') : '',
|
||||
})
|
||||
setShowProfileModal(true)
|
||||
}}
|
||||
className="px-3 py-1.5 bg-amber-400 text-white text-sm font-medium rounded-lg hover:bg-amber-500 transition-colors"
|
||||
>
|
||||
{t('profile.completeProfileBtn')}
|
||||
</a>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowProfilePrompt(false)}
|
||||
className="px-3 py-1.5 text-sm font-medium text-amber-800 hover:bg-amber-100 rounded-lg transition-colors"
|
||||
@@ -133,6 +158,88 @@ function AppContent() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Profile completion modal */}
|
||||
<Modal isOpen={showProfileModal} onClose={() => setShowProfileModal(false)} title={t('profile.completeYourProfile')} size="md">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-text-primary mb-1">{t('team.name')}</label>
|
||||
<input
|
||||
type="text"
|
||||
value={profileForm.name}
|
||||
onChange={e => setProfileForm(f => ({ ...f, name: e.target.value }))}
|
||||
className="w-full px-3 py-2 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary"
|
||||
placeholder={t('team.fullName')}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-text-primary mb-1">{t('team.teamRole')}</label>
|
||||
<select
|
||||
value={profileForm.team_role}
|
||||
onChange={e => setProfileForm(f => ({ ...f, team_role: e.target.value }))}
|
||||
className="w-full px-3 py-2 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary"
|
||||
>
|
||||
<option value="">—</option>
|
||||
{TEAM_ROLES.map(r => <option key={r.value} value={r.value}>{r.label}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-text-primary mb-1">{t('team.phone')} {t('team.optional')}</label>
|
||||
<input
|
||||
type="text"
|
||||
value={profileForm.phone}
|
||||
onChange={e => setProfileForm(f => ({ ...f, phone: e.target.value }))}
|
||||
className="w-full px-3 py-2 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-text-primary mb-1">{t('team.brands')}</label>
|
||||
<input
|
||||
type="text"
|
||||
value={profileForm.brands}
|
||||
onChange={e => setProfileForm(f => ({ ...f, brands: e.target.value }))}
|
||||
className="w-full px-3 py-2 text-sm border border-border rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-primary/20 focus:border-brand-primary"
|
||||
placeholder={t('team.brandsHelp')}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-end gap-3 pt-4 border-t border-border">
|
||||
<button
|
||||
onClick={() => setShowProfileModal(false)}
|
||||
className="px-4 py-2 text-sm font-medium text-text-secondary hover:bg-surface-tertiary rounded-lg"
|
||||
>
|
||||
{t('common.cancel')}
|
||||
</button>
|
||||
<button
|
||||
onClick={async () => {
|
||||
setProfileSaving(true)
|
||||
try {
|
||||
const brandsArr = profileForm.brands
|
||||
.split(',')
|
||||
.map(b => b.trim())
|
||||
.filter(Boolean)
|
||||
await api.patch('/users/me/profile', {
|
||||
name: profileForm.name,
|
||||
team_role: profileForm.team_role,
|
||||
phone: profileForm.phone || null,
|
||||
brands: brandsArr,
|
||||
})
|
||||
await checkAuth()
|
||||
setShowProfileModal(false)
|
||||
setShowProfilePrompt(false)
|
||||
} catch (err) {
|
||||
console.error('Profile save failed:', err)
|
||||
} finally {
|
||||
setProfileSaving(false)
|
||||
}
|
||||
}}
|
||||
disabled={!profileForm.name || !profileForm.team_role || profileSaving}
|
||||
className="px-5 py-2 bg-brand-primary text-white rounded-lg text-sm font-medium hover:bg-brand-primary-light disabled:opacity-50 disabled:cursor-not-allowed shadow-sm"
|
||||
>
|
||||
{profileSaving ? t('common.loading') : t('team.saveProfile')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
{/* Tutorial overlay */}
|
||||
{showTutorial && <Tutorial onComplete={handleTutorialComplete} />}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user