✨ Add Profile and Dashboard Features · javascriptcm/javascript.cm@118d33d · GitHub
Skip to content

Commit 118d33d

Browse files
committed
✨ Add Profile and Dashboard Features
1 parent 50430c2 commit 118d33d

11 files changed

Lines changed: 545 additions & 16 deletions

File tree

Lines changed: 21 additions & 0 deletions

‎app/middleware/guest_middleware.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default class GuestMiddleware {
1313
/**
1414
* The URL to redirect to when user is logged-in
1515
*/
16-
redirectTo = '/'
16+
redirectTo = '/dashboard'
1717

1818
async handle(
1919
ctx: HttpContext,

‎app/models/user.ts‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { DateTime } from 'luxon'
2-
import { BaseModel, beforeSave, column } from '@adonisjs/lucid/orm'
2+
import { BaseModel, beforeSave, column, hasMany } from '@adonisjs/lucid/orm'
33
import { compose } from '@adonisjs/core/helpers'
44
import hash from '@adonisjs/core/services/hash'
55
import { withAuthFinder } from '@adonisjs/auth/mixins/lucid'
6+
import type { HasMany } from '@adonisjs/lucid/types/relations'
7+
import Article from '#models/article'
68

79
const AuthFinder = withAuthFinder(() => hash.use('scrypt'), {
810
uids: ['email'],
@@ -52,6 +54,9 @@ export default class User extends compose(BaseModel, AuthFinder) {
5254
@column.dateTime({ autoCreate: true, autoUpdate: true })
5355
declare updatedAt: DateTime
5456

57+
@hasMany(() => Article)
58+
declare articles: HasMany<typeof Article>
59+
5560
@beforeSave()
5661
static async hashPassword(user: User) {
5762
if (user.$dirty.password) {

‎config/inertia.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ const inertiaConfig = defineConfig({
1111
* Data that should be shared with all rendered pages
1212
*/
1313
sharedData: {
14-
user: (ctx) => ctx.auth.user,
14+
auth: (ctx) => {
15+
return {
16+
user: ctx.auth.user,
17+
}
18+
},
1519
errors: (ctx) => ctx.inertia.always(() => ctx.session?.flashMessages.get('errors')),
1620
},
1721

‎inertia/components/navbar.tsx‎

Lines changed: 104 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
1-
import { Link } from '@inertiajs/react'
1+
import { Link, usePage } from '@inertiajs/react'
2+
import { Menu, MenuButton, MenuItem, MenuItems, Transition } from '@headlessui/react'
3+
import { Fragment } from 'react'
4+
5+
interface User {
6+
name: string
7+
email: string
8+
username: string
9+
avatar: string | null
10+
}
11+
12+
interface PageProps {
13+
auth: {
14+
user: User | null
15+
}
16+
[key: string]: any
17+
}
218

319
export default function Navbar() {
20+
const { auth } = usePage<PageProps>().props
421
return (
522
<nav className="bg-white border-b border-gray-100">
623
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
@@ -38,17 +55,92 @@ export default function Navbar() {
3855

3956
{/* Right Side */}
4057
<div className="hidden sm:flex sm:items-center sm:ml-6">
41-
<div className="flex items-center space-x-4">
42-
<Link href="/login" className="text-sm text-gray-500 hover:text-gray-900">
43-
Se connecter
44-
</Link>
45-
<Link
46-
href="/register"
47-
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
48-
>
49-
Créer un compte
50-
</Link>
51-
</div>
58+
{auth && auth.user && auth.user.username ? (
59+
<Menu as="div" className="relative ml-3">
60+
<MenuButton className="flex items-center space-x-3 text-sm focus:outline-none">
61+
<img
62+
className="h-8 w-8 rounded-full"
63+
src={auth.user.avatar || `https://ui-avatars.com/api/?name=${auth.user.name}`}
64+
alt={auth.user.name}
65+
/>
66+
<span className="text-gray-700">{auth.user.name}</span>
67+
</MenuButton>
68+
69+
<Transition
70+
as={Fragment}
71+
enter="transition ease-out duration-100"
72+
enterFrom="transform opacity-0 scale-95"
73+
enterTo="transform opacity-100 scale-100"
74+
leave="transition ease-in duration-75"
75+
leaveFrom="transform opacity-100 scale-100"
76+
leaveTo="transform opacity-0 scale-95"
77+
>
78+
<MenuItems className="absolute right-0 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
79+
{/* Tableau de bord */}
80+
<MenuItem>
81+
{({ focus }) => (
82+
<Link
83+
href="/dashboard"
84+
className={`${focus ? 'bg-gray-100' : ''} block px-4 py-2 text-sm text-gray-700`}
85+
>
86+
Dashboard
87+
</Link>
88+
)}
89+
</MenuItem>
90+
<MenuItem>
91+
{({ focus }) => (
92+
<Link
93+
href={`/@${auth.user?.username}`}
94+
className={`${
95+
focus ? 'bg-gray-100' : ''
96+
} block px-4 py-2 text-sm text-gray-700`}
97+
>
98+
Profile
99+
</Link>
100+
)}
101+
</MenuItem>
102+
<MenuItem>
103+
{({ focus }) => (
104+
<Link
105+
href="/settings"
106+
className={`${
107+
focus ? 'bg-gray-100' : ''
108+
} block px-4 py-2 text-sm text-gray-700`}
109+
>
110+
Settings
111+
</Link>
112+
)}
113+
</MenuItem>
114+
<MenuItem>
115+
{({ focus }) => (
116+
<Link
117+
href="/logout"
118+
method="post"
119+
as="button"
120+
className={`${
121+
focus ? 'bg-gray-100' : ''
122+
} block w-full px-4 py-2 text-left text-sm text-gray-700`}
123+
>
124+
Logout
125+
</Link>
126+
)}
127+
</MenuItem>
128+
</MenuItems>
129+
</Transition>
130+
</Menu>
131+
) : (
132+
<div className="flex items-center space-x-4">
133+
<Link href="/login" className="text-sm text-gray-500 hover:text-gray-900">
134+
Se connecter
135+
</Link>
136+
<Link
137+
href="/register"
138+
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
139+
>
140+
Créer un compte
141+
</Link>
142+
</div>
143+
)}
52144
</div>
53145
</div>
54146
</div>

‎inertia/layouts/dashboard.tsx‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { PropsWithChildren } from 'react'
2+
import Footer from '../components/footer'
3+
import Navbar from '~/components/navbar'
4+
5+
export default function DashboardLayout({ children }: PropsWithChildren) {
6+
return (
7+
<div className="min-h-screen bg-gray-100">
8+
<Navbar />
9+
<main className="container mx-auto">
10+
<div className="py-6">
11+
<div className="px-4 sm:px-6 md:px-0">{children}</div>
12+
</div>
13+
</main>
14+
<Footer />
15+
</div>
16+
)
17+
}

‎inertia/pages/dashboard/index.tsx‎

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { Head, Link } from '@inertiajs/react'
2+
import DashboardLayout from '../../layouts/dashboard'
3+
4+
interface DashboardProps {
5+
stats: {
6+
articles: number
7+
discussions: number
8+
questions: number
9+
}
10+
}
11+
12+
export default function Dashboard({ stats }: DashboardProps) {
13+
return (
14+
<DashboardLayout>
15+
<Head title="Dashboard - JavaScript Cameroun" />
16+
17+
<div className="space-y-6">
18+
{/* Stats */}
19+
<div className="grid grid-cols-1 gap-5 sm:grid-cols-3">
20+
<div className="overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6">
21+
<dt className="truncate text-sm font-medium text-gray-500">Total Articles</dt>
22+
<dd className="mt-1 text-3xl font-semibold tracking-tight text-gray-900">
23+
{stats.articles}
24+
</dd>
25+
</div>
26+
<div className="overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6">
27+
<dt className="truncate text-sm font-medium text-gray-500">Total Discussions</dt>
28+
<dd className="mt-1 text-3xl font-semibold tracking-tight text-gray-900">
29+
{stats.discussions}
30+
</dd>
31+
</div>
32+
<div className="overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6">
33+
<dt className="truncate text-sm font-medium text-gray-500">Total Questions</dt>
34+
<dd className="mt-1 text-3xl font-semibold tracking-tight text-gray-900">
35+
{stats.questions}
36+
</dd>
37+
</div>
38+
</div>
39+
<div className="flex justify-end">
40+
<Link
41+
href="/articles/create"
42+
className="btn btn-primary bg-blue-500 text-white px-4 py-2 rounded-md"
43+
>
44+
Create Article
45+
</Link>
46+
</div>
47+
48+
{/* Recent Activity */}
49+
<div className="overflow-hidden rounded-lg bg-white shadow">
50+
<div className="p-6">
51+
<h3 className="text-base font-semibold leading-6 text-gray-900">Recent Activity</h3>
52+
<div className="mt-6">
53+
<div className="text-center">
54+
<p className="text-sm text-gray-500">No recent activity</p>
55+
</div>
56+
</div>
57+
</div>
58+
</div>
59+
</div>
60+
</DashboardLayout>
61+
)
62+
}

‎inertia/pages/profile/show.tsx‎

Lines changed: 97 additions & 0 deletions

0 commit comments

Comments
 (0)