Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 10 additions & 37 deletions client/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@ const TVShows = lazy(() => import('./pages/TVShows'))
const MediaDetails = lazy(() => import('./pages/MediaDetails/MediaDetails'))
const MediaPlayer = lazy(() => import('./pages/MediaPlayer/MediaPlayer'))
const PremiumCheckout = lazy(() => import('./pages/PremiumCheckout/PremiumCheckout'))
const ProfileSelection = lazy(() => import('./pages/Profile/ProfileSelection'))
const ProfileManage = lazy(() => import('./pages/Profile/ProfileManage'))
const ProfileForm = lazy(() => import('./pages/Profile/ProfileForm'))
const AccountSettings = lazy(() => import('./pages/Account/AccountSettings'))

const PageLoader = () => <div className="min-h-screen flex items-center justify-center" />

Expand All @@ -32,7 +30,7 @@ const RootLayout = () => {
<AuthProvider>
<ScrollToTop />
<Outlet />
<ToastContainer/>
<ToastContainer position='bottom-right' autoClose={1000} theme='dark'/>
</AuthProvider>
</AppProvider>
)
Expand Down Expand Up @@ -118,6 +116,14 @@ const router = createBrowserRouter([
</Suspense>
),
},
{
path: '/account',
element: (
<Suspense fallback={<PageLoader />}>
<AccountSettings />
</Suspense>
),
},
],
},

Expand Down Expand Up @@ -145,39 +151,6 @@ const router = createBrowserRouter([
</Suspense>
),
},

{
path: '/profiles',
element: (
<Suspense fallback={<PageLoader />}>
<ProfileSelection />
</Suspense>
),
},
{
path: '/profiles/manage',
element: (
<Suspense fallback={<PageLoader />}>
<ProfileManage />
</Suspense>
),
},
{
path: '/profiles/add',
element: (
<Suspense fallback={<PageLoader />}>
<ProfileForm />
</Suspense>
),
},
{
path: '/profiles/edit/:id',
element: (
<Suspense fallback={<PageLoader />}>
<ProfileForm />
</Suspense>
),
},
],
},
])
Expand Down
25 changes: 25 additions & 0 deletions client/src/api/userApi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { apiClient } from './axiosClient'

export const getUserProfileApi = () =>
apiClient.get('/users/profile').then((res) => res.data.data)

export const updateUserProfileApi = (data) => {
const formData = new FormData()
if (data.fullName) formData.append('fullName', data.fullName)
if (data.phone) formData.append('phone', data.phone)
if (data.dateOfBirth) formData.append('dateOfBirth', data.dateOfBirth)
if (data.gender) formData.append('gender', data.gender)
if (data.avatar) formData.append('avatar', data.avatar)
Comment thread
LoiTang1710 marked this conversation as resolved.
Outdated

return apiClient.put('/users/profile', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
}).then((res) => res.data.data)
}

export const changePasswordApi = (data) =>
apiClient.post('/users/change-password', data).then((res) => res.data.data)

export const getSubscriptionHistoryApi = () =>
apiClient.get('/users/subscription-history').then((res) => res.data.data)
4 changes: 2 additions & 2 deletions client/src/components/common/AppBar/AppBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ const AppBar = () => {
{/* Dropdown Menu */}
{isUserMenuOpen && (
<div className="absolute top-[130%] right-0 mt-3 w-72 bg-[#141414]/95 backdrop-blur-2xl border border-white/10 rounded-2xl shadow-2xl z-50 overflow-hidden ring-1 ring-black/50 transform transition-all animate-in fade-in slide-in-from-top-2">
<div className="p-5 bg-gradient-to-b from-white/[0.04] to-transparent border-b border-white/5">
<div className="p-5 bg-linear-to-b from-white/4 to-transparent border-b border-white/5">
<div className="flex items-center gap-4">
<div className="relative shrink-0">
{getAvatarUrl() ? (
Expand Down Expand Up @@ -297,7 +297,7 @@ const AppBar = () => {
<div className="p-2 flex flex-col gap-1">
<button
onClick={() => {
navigate('/profiles')
navigate('/account')
setIsUserMenuOpen(false)
}}
className="w-full px-4 py-2.5 text-sm font-medium text-gray-300 hover:text-white hover:bg-white/10 rounded-xl transition-all flex items-center gap-3 group"
Expand Down
74 changes: 74 additions & 0 deletions client/src/pages/Account/AccountSettings.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useState } from 'react'
import { User, Shield, CreditCard, Users } from 'lucide-react'
import PersonalInfo from './PersonalInfo'
import SecuritySettings from './SecuritySettings'
import SubscriptionHistory from './SubscriptionHistory'
import ProfilesManagement from './ProfilesManagement'

export default function AccountSettings() {
const [activeTab, setActiveTab] = useState('personal')

const tabs = [
{ id: 'personal', label: 'Thông tin cá nhân', icon: User },
{ id: 'security', label: 'Bảo mật', icon: Shield },
{ id: 'subscription', label: 'Gói dịch vụ', icon: CreditCard },
{ id: 'profiles', label: 'Hồ sơ xem chung', icon: Users },
]

const renderContent = () => {
switch (activeTab) {
case 'personal':
return <PersonalInfo />
case 'security':
return <SecuritySettings />
case 'subscription':
return <SubscriptionHistory />
case 'profiles':
return <ProfilesManagement />
default:
return <PersonalInfo />
}
}

return (
<div className="min-h-screen bg-bg-default text-slate-200 pt-7 pb-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-6xl mx-auto">
<div className="mb-8">
<h1 className="text-3xl font-bold text-white mb-2">Cài đặt tài khoản</h1>
<p className="text-slate-400">Quản lý thông tin, bảo mật và các gói dịch vụ của bạn</p>
</div>

<div className="flex flex-col lg:flex-row gap-8">
{/* Sidebar Navigation */}
<div className="w-full lg:w-64 shrink-0">
<nav className="flex lg:flex-col gap-2 overflow-x-auto lg:overflow-visible pb-4 lg:pb-0 hide-scrollbar">
{tabs.map((tab) => {
const Icon = tab.icon
const isActive = activeTab === tab.id
return (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`flex items-center gap-3 px-4 py-3 rounded transition-all duration-200 whitespace-nowrap lg:whitespace-normal
${isActive
? 'bg-red-500/10 text-red-500 border border-red-500/20'
: 'text-slate-400 hover:bg-white/5 hover:text-slate-200 border border-transparent'
}`}
>
<Icon className={`w-5 h-5 ${isActive ? 'text-red-500' : 'text-slate-400'}`} />
<span className="font-medium text-sm">{tab.label}</span>
</button>
)
})}
</nav>
</div>

{/* Main Content Area */}
<div className="flex-1 min-w-0">
{renderContent()}
</div>
</div>
</div>
</div>
)
}
Loading