Skip to content

Commit fdabcfc

Browse files
committed
feat: add admin dashboard with sub-pages
- Add admin layout with sidebar navigation - Add admin pages: dashboard, blog, invoice, time, files, client, payment - Add corresponding admin page components
1 parent 043fa9a commit fdabcfc

16 files changed

Lines changed: 787 additions & 0 deletions

File tree

src/app/admin/blog/page.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use client'
2+
3+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4+
import { faPlus } from '@fortawesome/free-solid-svg-icons'
5+
import { AdminLayout, DataTable } from '@/components/admin/admin-layout'
6+
import { ActionButton, Separator } from '@/components/breadcrumbs'
7+
8+
const blogPosts = [
9+
{ title: 'Scaling Realtime Web Apps', url: 'blog/scaling-realtime-web-apps', views: '4,181' },
10+
{ title: 'Designing Better Payment UX', url: 'blog/designing-better-payment-ux', views: '2,744' },
11+
{ title: 'A Practical Laravel to Next.js Migration', url: 'blog/laravel-to-nextjs-migration', views: '1,913' },
12+
]
13+
14+
export default function AdminBlogPage() {
15+
return (
16+
<AdminLayout title="CMS & Blog" breadcrumbLabel="CMS &amp; Blog">
17+
<div className="border border-[#ececec] rounded bg-white shadow-[0_8px_24px_rgba(0,0,0,0.04)] p-6 lg:p-8">
18+
<div className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
19+
<h1 className="text-[34px] font-normal leading-[1.15] text-[#2a2a2a]">CMS &amp; Blog</h1>
20+
<ActionButton href="/admin/blog" variant="primary">
21+
<FontAwesomeIcon icon={faPlus} />
22+
<span>New</span>
23+
</ActionButton>
24+
</div>
25+
<Separator />
26+
<div className="mb-5 flex gap-2 text-[13px]">
27+
<button className="rounded-t-[3px] border border-b-0 border-[#ececec] bg-white px-4 py-2 text-[#444]">
28+
CMS
29+
</button>
30+
<button className="rounded-t-[3px] border border-b-0 border-[#ececec] bg-[#fafafa] px-4 py-2 text-[#777]">
31+
Blog Posts
32+
</button>
33+
</div>
34+
<DataTable
35+
headers={['Title', 'URL', 'Views']}
36+
rows={blogPosts.map((post) => [post.title, post.url, post.views])}
37+
/>
38+
</div>
39+
</AdminLayout>
40+
)
41+
}

src/app/admin/client/page.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use client'
2+
3+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4+
import { faPlus } from '@fortawesome/free-solid-svg-icons'
5+
import { AdminLayout, DataTable } from '@/components/admin/admin-layout'
6+
import { ActionButton, Separator } from '@/components/breadcrumbs'
7+
8+
const clients = [
9+
{ name: 'Alice Johnson', firm: 'Northgrid Studio', email: 'alice@northgrid.io' },
10+
{ name: 'Marcus Chen', firm: 'Velox Health', email: 'marcus@veloxhealth.com' },
11+
{ name: 'Priya Desai', firm: 'Harbor Commerce', email: 'priya@harborcommerce.co' },
12+
]
13+
14+
export default function AdminClientPage() {
15+
return (
16+
<AdminLayout title="Clients" breadcrumbLabel="Clients">
17+
<div className="border border-[#ececec] rounded bg-white shadow-[0_8px_24px_rgba(0,0,0,0.04)] p-6 lg:p-8">
18+
<div className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
19+
<h1 className="text-[34px] font-normal leading-[1.15] text-[#2a2a2a]">Clients</h1>
20+
<ActionButton href="/admin/client" variant="primary">
21+
<FontAwesomeIcon icon={faPlus} />
22+
<span>New</span>
23+
</ActionButton>
24+
</div>
25+
<Separator />
26+
<DataTable headers={['Name', 'Firm', 'Email']} rows={clients.map((c) => [c.name, c.firm, c.email])} />
27+
</div>
28+
</AdminLayout>
29+
)
30+
}

src/app/admin/files/page.tsx

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
'use client'
2+
3+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4+
import { faFolderOpen, faPlus } from '@fortawesome/free-solid-svg-icons'
5+
import { AdminLayout } from '@/components/admin/admin-layout'
6+
import { ActionButton, Separator } from '@/components/breadcrumbs'
7+
8+
const files = [
9+
{ name: 'Northgrid Brand Guide.pdf', added: '01/01/17', size: '25 MB', hits: '25' },
10+
{ name: 'Q4 Sprint Scope.xlsx', added: '01/01/17', size: '7 MB', hits: '13' },
11+
{ name: 'Invoice Assets.zip', added: '01/01/17', size: '38 MB', hits: '41' },
12+
]
13+
14+
export default function AdminFilesPage() {
15+
return (
16+
<AdminLayout title="File Manager" breadcrumbLabel="File Manager">
17+
<div className="border border-[#ececec] rounded bg-white shadow-[0_8px_24px_rgba(0,0,0,0.04)] p-6 lg:p-8">
18+
<div className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
19+
<h1 className="text-[34px] font-normal leading-[1.15] text-[#2a2a2a]">File Manager</h1>
20+
<ActionButton href="/admin/files" variant="primary">
21+
<FontAwesomeIcon icon={faPlus} />
22+
<span>Upload</span>
23+
</ActionButton>
24+
</div>
25+
<Separator />
26+
<div className="grid gap-5 md:grid-cols-2 xl:grid-cols-3">
27+
<div className="border border-[#ececec] rounded bg-white shadow-[0_8px_24px_rgba(0,0,0,0.04)] flex h-[250px] flex-col justify-center border-dashed border-[#cfdce1] p-6 text-center">
28+
<div className="mx-auto inline-flex h-12 w-12 items-center justify-center rounded-full bg-[#09afdf]/10 text-[#09afdf]">
29+
<FontAwesomeIcon icon={faPlus} />
30+
</div>
31+
<div className="mt-4 text-[18px] text-[#2f2f2f]">Upload</div>
32+
<div className="mt-2 text-[14px] text-[#777]">Dropzone-style upload surface placeholder</div>
33+
</div>
34+
{files.map((file) => (
35+
<div
36+
key={file.name}
37+
className="border border-[#ececec] rounded bg-white shadow-[0_8px_24px_rgba(0,0,0,0.04)] h-[250px] p-5"
38+
>
39+
<div className="flex h-full flex-col justify-between">
40+
<div>
41+
<div className="mb-4 flex h-[72px] items-center justify-center rounded-[3px] bg-[#fafafa]">
42+
<FontAwesomeIcon icon={faFolderOpen} className="text-[28px] text-[#09afdf]" />
43+
</div>
44+
<h3 className="text-center text-[16px] text-[#2f2f2f]">{file.name}</h3>
45+
<div className="mt-4 overflow-hidden rounded-[3px] border border-[#ececec]">
46+
{[
47+
['Added', file.added],
48+
['Size', file.size],
49+
['Hits', file.hits],
50+
].map(([label, value]) => (
51+
<div
52+
key={label}
53+
className="flex justify-between border-b border-[#ececec] px-3 py-2 text-[13px] last:border-b-0"
54+
>
55+
<span className="text-[#666]">{label}</span>
56+
<span className="text-[#888]">{value}</span>
57+
</div>
58+
))}
59+
</div>
60+
</div>
61+
<div className="mt-4 flex justify-between text-[12px] text-[#777]">
62+
<span>Delete</span>
63+
<span>Download</span>
64+
</div>
65+
</div>
66+
</div>
67+
))}
68+
</div>
69+
</div>
70+
</AdminLayout>
71+
)
72+
}

src/app/admin/index/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { DashboardPage } from '@/components/admin/dashboard/dashboard-page'
2+
3+
export default function AdminIndexPage() {
4+
return <DashboardPage />
5+
}

src/app/admin/invoice/page.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
'use client'
2+
3+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4+
import { faPlus } from '@fortawesome/free-solid-svg-icons'
5+
import { AdminLayout, DataTable } from '@/components/admin/admin-layout'
6+
import { ActionButton, Separator } from '@/components/breadcrumbs'
7+
8+
const invoices = [
9+
{
10+
date: 'November 03rd, 2024',
11+
id: 'CB-2024-117',
12+
amount: '$4,180.00',
13+
client: 'ACME Product Team',
14+
status: 'Unpaid',
15+
},
16+
{ date: 'October 22nd, 2024', id: 'CB-2024-109', amount: '$2,240.00', client: 'Northgrid Studio', status: 'Paid' },
17+
{ date: 'October 06th, 2024', id: 'CB-2024-098', amount: '$8,900.00', client: 'Velox Health', status: 'Paid' },
18+
]
19+
20+
export default function AdminInvoicePage() {
21+
return (
22+
<AdminLayout title="Invoices" breadcrumbLabel="Invoices">
23+
<div className="border border-[#ececec] rounded bg-white shadow-[0_8px_24px_rgba(0,0,0,0.04)] p-6 lg:p-8">
24+
<div className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
25+
<h1 className="text-[34px] font-normal leading-[1.15] text-[#2a2a2a]">Invoices</h1>
26+
<ActionButton href="/admin/invoice" variant="primary">
27+
<FontAwesomeIcon icon={faPlus} />
28+
<span>New</span>
29+
</ActionButton>
30+
</div>
31+
<Separator />
32+
<DataTable
33+
headers={['Date', 'Invoice ID', 'Amount', 'Client', 'Status']}
34+
rows={invoices.map((inv) => [inv.date, inv.id, inv.amount, inv.client, inv.status])}
35+
/>
36+
</div>
37+
</AdminLayout>
38+
)
39+
}

src/app/admin/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { DashboardPage } from '@/components/admin/dashboard/dashboard-page'
2+
3+
export default function AdminPage() {
4+
return <DashboardPage />
5+
}

src/app/admin/payment/page.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
'use client'
2+
3+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4+
import { faPlus } from '@fortawesome/free-solid-svg-icons'
5+
import { AdminLayout, DataTable } from '@/components/admin/admin-layout'
6+
import { ActionButton, Separator } from '@/components/breadcrumbs'
7+
8+
const payments = [
9+
{ id: 'pi_1Q90L...', date: '11/03/2024', type: 'Stripe', status: 'Confirmed', amount: '$1,500.00' },
10+
{ id: 'btc_83728', date: '10/28/2024', type: 'Bitcoin', status: 'Pending', amount: '$740.00' },
11+
{ id: 'pp_46291', date: '10/23/2024', type: 'Paypal', status: 'Confirmed', amount: '$2,240.00' },
12+
]
13+
14+
export default function AdminPaymentPage() {
15+
return (
16+
<AdminLayout title="Payments" breadcrumbLabel="Payments">
17+
<div className="border border-[#ececec] rounded bg-white shadow-[0_8px_24px_rgba(0,0,0,0.04)] p-6 lg:p-8">
18+
<div className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
19+
<h1 className="text-[34px] font-normal leading-[1.15] text-[#2a2a2a]">Payments</h1>
20+
<ActionButton href="/admin/payment" variant="primary">
21+
<FontAwesomeIcon icon={faPlus} />
22+
<span>New</span>
23+
</ActionButton>
24+
</div>
25+
<Separator />
26+
<DataTable
27+
headers={['Transaction ID', 'Date', 'Type', 'Status', 'Amount']}
28+
rows={payments.map((p) => [p.id, p.date, p.type, p.status, p.amount])}
29+
/>
30+
</div>
31+
</AdminLayout>
32+
)
33+
}

src/app/admin/time/page.tsx

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
'use client'
2+
3+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4+
import { faPlus } from '@fortawesome/free-solid-svg-icons'
5+
import { AdminLayout } from '@/components/admin/admin-layout'
6+
import { ActionButton, Separator } from '@/components/breadcrumbs'
7+
8+
const timesheets = [
9+
{
10+
date: 'November 02nd, 2024',
11+
time: '9:00 AM - 1:30 PM',
12+
hours: '4.50 hrs',
13+
user: 'Andrew Corbin',
14+
client: 'ACME Product Team',
15+
firm: 'ACME Labs LLC',
16+
description:
17+
'Polish invoice payment UX, fix edge cases in router transition handling, and review responsive behavior.',
18+
},
19+
{
20+
date: 'October 28th, 2024',
21+
time: '2:15 PM - 6:00 PM',
22+
hours: '3.75 hrs',
23+
user: 'Tom Goodrie',
24+
client: 'Northgrid Studio',
25+
firm: 'Northgrid Studio',
26+
description: 'Review sitemap IA, implement content page scaffolding, and prep migration notes for API transition.',
27+
},
28+
]
29+
30+
export default function AdminTimePage() {
31+
return (
32+
<AdminLayout title="Timesheets" breadcrumbLabel="Timesheets">
33+
<div className="border border-[#ececec] rounded bg-white shadow-[0_8px_24px_rgba(0,0,0,0.04)] p-6 lg:p-8">
34+
<div className="flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
35+
<h1 className="text-[34px] font-normal leading-[1.15] text-[#2a2a2a]">Timesheets</h1>
36+
<ActionButton href="/admin/time" variant="primary">
37+
<FontAwesomeIcon icon={faPlus} />
38+
<span>New</span>
39+
</ActionButton>
40+
</div>
41+
<Separator />
42+
<div className="overflow-x-auto">
43+
<table className="w-full min-w-[880px] text-left text-[14px]">
44+
<thead>
45+
<tr className="border-b border-[#ececec] text-[#555]">
46+
{['Date', 'Hours', 'User', 'Client', 'Description', ''].map((header) => (
47+
<th key={header} className="px-4 py-3">
48+
{header}
49+
</th>
50+
))}
51+
</tr>
52+
</thead>
53+
<tbody>
54+
{timesheets.map((row) => (
55+
<tr key={`${row.date}-${row.user}`} className="border-b border-[#f3f3f3] text-[#666]">
56+
<td className="px-4 py-4">
57+
<div className="text-center">
58+
{row.date}
59+
<br />
60+
<small>({row.time})</small>
61+
</div>
62+
</td>
63+
<td className="px-4 py-4 font-semibold">{row.hours}</td>
64+
<td className="px-4 py-4">{row.user}</td>
65+
<td className="px-4 py-4">
66+
{row.client}
67+
<br />
68+
<small>{row.firm}</small>
69+
</td>
70+
<td className="px-4 py-4">
71+
<textarea className="h-[150px] w-full rounded-[3px] border border-[#ddd] px-3 py-2 text-[13px] outline-none">
72+
{row.description}
73+
</textarea>
74+
</td>
75+
<td className="px-4 py-4 text-center text-[#c46]">Delete</td>
76+
</tr>
77+
))}
78+
</tbody>
79+
</table>
80+
</div>
81+
</div>
82+
</AdminLayout>
83+
)
84+
}

0 commit comments

Comments
 (0)