added widgets to dashboard

This commit is contained in:
Abhijit Bhatnagar 2025-08-11 01:56:57 +05:30
parent e80316be89
commit 073173e145
6 changed files with 662 additions and 50 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,19 +1,31 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Dashboard from './pages/Dashboard';
import Settings from './pages/Settings';
import TwoFA from './pages/TwoFA';
import './pages/Dashboard.css';
// Main App component for the dashboard page
export default function App() {
return <Dashboard />;
}
// Posts page component
function PostsApp() {
return (
<Router>
<Routes>
<Route path="/" element={ <Dashboard /> } />
<Route path="/settings" element={ <Settings /> } />
<Route path="/2fa" element={ <TwoFA /> } />
</Routes>
</Router>
<div className="helix-page">
<h1>Posts Management</h1>
<p>Posts management interface will be implemented here.</p>
</div>
);
}
// Users page component
function UsersApp() {
return (
<div className="helix-page">
<h1>Users Management</h1>
<p>Users management interface will be implemented here.</p>
</div>
);
}
@ -37,13 +49,13 @@ document.addEventListener( 'DOMContentLoaded', function () {
const postsRoot = document.getElementById( 'helix-posts-root' );
if ( postsRoot ) {
const root = createRoot( postsRoot );
root.render( <Dashboard /> ); // For now, render Dashboard
root.render( <PostsApp /> );
}
// Users page
const usersRoot = document.getElementById( 'helix-users-root' );
if ( usersRoot ) {
const root = createRoot( usersRoot );
root.render( <Dashboard /> ); // For now, render Dashboard
root.render( <UsersApp /> );
}
} );

290
src/pages/Dashboard.css Normal file
View file

@ -0,0 +1,290 @@
.helix-dashboard {
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
}
.helix-page {
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
}
.helix-page h1 {
font-size: 2rem;
margin-bottom: 1rem;
color: #1e293b;
}
.helix-page p {
color: #64748b;
margin-bottom: 2rem;
}
.helix-dashboard__header {
margin-bottom: 2rem;
}
.helix-dashboard__header h1 {
font-size: 2.5rem;
margin: 0 0 0.5rem 0;
color: #1e293b;
}
.helix-dashboard__header p {
font-size: 1.1rem;
color: #64748b;
margin: 0;
}
.helix-loading {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 4rem;
}
.helix-loading__spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f4f6;
border-top: 4px solid #3b82f6;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 1rem;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.helix-dashboard__stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.helix-stats-card {
background: #ffffff;
border-radius: 0.5rem;
padding: 1.5rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
gap: 1rem;
}
.helix-stats-card--blue {
border-left: 4px solid #3b82f6;
}
.helix-stats-card--green {
border-left: 4px solid #10b981;
}
.helix-stats-card--orange {
border-left: 4px solid #f59e0b;
}
.helix-stats-card--purple {
border-left: 4px solid #8b5cf6;
}
.helix-stats-card__icon {
font-size: 2rem;
}
.helix-stats-card__count {
font-size: 2rem;
font-weight: bold;
margin: 0;
color: #1e293b;
}
.helix-stats-card__title {
font-size: 0.9rem;
color: #64748b;
margin: 0;
}
.helix-dashboard__content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 1.5rem;
}
.helix-widget {
background: #ffffff;
border-radius: 0.5rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.helix-widget__header {
padding: 1rem 1.5rem;
border-bottom: 1px solid #e2e8f0;
display: flex;
justify-content: space-between;
align-items: center;
}
.helix-widget__header h2 {
margin: 0;
font-size: 1.25rem;
color: #1e293b;
flex-grow: 1;
}
.helix-widget__link {
color: #3b82f6;
text-decoration: none;
font-size: 0.9rem;
}
.helix-widget__link:hover {
text-decoration: underline;
}
.helix-widget__content {
padding: 1.5rem;
}
.helix-widget__empty {
color: #64748b;
font-style: italic;
}
.helix-posts-list,
.helix-comments-list {
list-style: none;
padding: 0;
margin: 0;
}
.helix-posts-list__item,
.helix-comments-list__item {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 1rem 0;
border-bottom: 1px solid #f1f5f9;
}
.helix-posts-list__item:last-child,
.helix-comments-list__item:last-child {
border-bottom: none;
}
.helix-posts-list__title a,
.helix-comments-list__author {
color: #1e293b;
text-decoration: none;
font-weight: 500;
}
.helix-posts-list__title a:hover {
color: #3b82f6;
}
.helix-posts-list__date,
.helix-comments-list__date {
color: #64748b;
font-size: 0.9rem;
margin: 0.25rem 0 0 0;
}
.helix-comments-list__excerpt {
color: #64748b;
margin: 0.25rem 0;
font-size: 0.9rem;
}
.helix-status {
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.8rem;
font-weight: 500;
}
.helix-status--publish {
background: #dcfce7;
color: #166534;
}
.helix-status--draft {
background: #fef3c7;
color: #92400e;
}
.helix-status--approved {
background: #dcfce7;
color: #166534;
}
.helix-status--pending {
background: #fef3c7;
color: #92400e;
}
.helix-quick-actions {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 1rem;
}
.helix-quick-action {
display: flex;
flex-direction: column;
align-items: center;
padding: 1rem;
background: #f8fafc;
border-radius: 0.5rem;
text-decoration: none;
color: #1e293b;
transition: background-color 0.2s;
}
.helix-quick-action:hover {
background: #e2e8f0;
}
.helix-quick-action__icon {
font-size: 1.5rem;
margin-bottom: 0.5rem;
}
.helix-quick-action__text {
font-size: 0.9rem;
font-weight: 500;
}
.helix-news-placeholder {
color: #64748b;
}
.helix-news-item h4 {
margin: 0 0 0.5rem 0;
color: #1e293b;
}
.helix-news-item p {
margin: 0 0 0.5rem 0;
font-size: 0.9rem;
}
.helix-news-item a {
color: #3b82f6;
text-decoration: none;
font-size: 0.9rem;
}
.helix-news-item a:hover {
text-decoration: underline;
}

View file

@ -1,4 +1,341 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import './Dashboard.css';
export default function Dashboard() {
return <div>Welcome to the Helix Admin Dashboard</div>;
const [ dashboardData, setDashboardData ] = useState( {
postsCount: 0,
pagesCount: 0,
commentsCount: 0,
usersCount: 0,
recentPosts: [],
recentComments: [],
loading: true,
} );
useEffect( () => {
// Fetch dashboard data from WordPress REST API
const fetchDashboardData = async () => {
try {
const [ posts, pages, comments, users ] = await Promise.all( [
fetch(
`${
window.helixData?.restUrl || '/wp-json/wp/v2/'
}posts?per_page=5`
),
fetch(
`${
window.helixData?.restUrl || '/wp-json/wp/v2/'
}pages?per_page=5`
),
fetch(
`${
window.helixData?.restUrl || '/wp-json/wp/v2/'
}comments?per_page=5`
),
fetch(
`${
window.helixData?.restUrl || '/wp-json/wp/v2/'
}users?per_page=5`
),
] );
const [ postsData, pagesData, commentsData, usersData ] =
await Promise.all( [
posts.json(),
pages.json(),
comments.json(),
users.json(),
] );
setDashboardData( {
postsCount:
posts.headers.get( 'X-WP-Total' ) || postsData.length,
pagesCount:
pages.headers.get( 'X-WP-Total' ) || pagesData.length,
commentsCount:
comments.headers.get( 'X-WP-Total' ) ||
commentsData.length,
usersCount:
users.headers.get( 'X-WP-Total' ) || usersData.length,
recentPosts: postsData.slice( 0, 5 ),
recentComments: commentsData.slice( 0, 5 ),
loading: false,
} );
} catch ( error ) {
// Error fetching dashboard data
setDashboardData( ( prev ) => ( { ...prev, loading: false } ) );
}
};
fetchDashboardData();
}, [] );
const StatsCard = ( { title, count, icon, color } ) => (
<div className={ `helix-stats-card helix-stats-card--${ color }` }>
<div className="helix-stats-card__icon">{ icon }</div>
<div className="helix-stats-card__content">
<h3 className="helix-stats-card__count">{ count }</h3>
<p className="helix-stats-card__title">{ title }</p>
</div>
</div>
);
const formatDate = ( dateString ) => {
return new Date( dateString ).toLocaleDateString( 'en-US', {
year: 'numeric',
month: 'short',
day: 'numeric',
} );
};
if ( dashboardData.loading ) {
return (
<div className="helix-dashboard">
<div className="helix-dashboard__header">
<h1>Dashboard</h1>
</div>
<div className="helix-loading">
<div className="helix-loading__spinner"></div>
<p>Loading dashboard...</p>
</div>
</div>
);
}
return (
<div className="helix-dashboard">
<div className="helix-dashboard__header">
<h1>Welcome to Helix</h1>
<p>Your modern WordPress admin experience</p>
</div>
{ /* Statistics Overview */ }
<div className="helix-dashboard__stats">
<StatsCard
title="Posts"
count={ dashboardData.postsCount }
icon="📝"
color="blue"
/>
<StatsCard
title="Pages"
count={ dashboardData.pagesCount }
icon="📄"
color="green"
/>
<StatsCard
title="Comments"
count={ dashboardData.commentsCount }
icon="💬"
color="orange"
/>
<StatsCard
title="Users"
count={ dashboardData.usersCount }
icon="👥"
color="purple"
/>
</div>
{ /* Main Content Area */ }
<div className="helix-dashboard__content">
{ /* Recent Posts Widget */ }
<div className="helix-widget">
<div className="helix-widget__header">
<h2>Recent Posts</h2>
<a
href="/wp-admin/edit.php"
className="helix-widget__link"
>
View All
</a>
</div>
<div className="helix-widget__content">
{ dashboardData.recentPosts.length > 0 ? (
<ul className="helix-posts-list">
{ dashboardData.recentPosts.map( ( post ) => (
<li
key={ post.id }
className="helix-posts-list__item"
>
<div className="helix-posts-list__content">
<h3 className="helix-posts-list__title">
<a
href={ `/wp-admin/post.php?post=${ post.id }&action=edit` }
>
{ post.title.rendered ||
'Untitled' }
</a>
</h3>
<p className="helix-posts-list__date">
{ formatDate( post.date ) }
</p>
</div>
<div className="helix-posts-list__status">
<span
className={ `helix-status helix-status--${ post.status }` }
>
{ post.status }
</span>
</div>
</li>
) ) }
</ul>
) : (
<p className="helix-widget__empty">
No posts yet.{ ' ' }
<a href="/wp-admin/post-new.php">
Create your first post!
</a>
</p>
) }
</div>
</div>
{ /* Recent Comments Widget */ }
<div className="helix-widget">
<div className="helix-widget__header">
<h2>Recent Comments</h2>
<a
href="/wp-admin/edit-comments.php"
className="helix-widget__link"
>
View All
</a>
</div>
<div className="helix-widget__content">
{ dashboardData.recentComments.length > 0 ? (
<ul className="helix-comments-list">
{ dashboardData.recentComments.map(
( comment ) => (
<li
key={ comment.id }
className="helix-comments-list__item"
>
<div className="helix-comments-list__content">
<h4 className="helix-comments-list__author">
{ comment.author_name }
</h4>
<p className="helix-comments-list__excerpt">
{ comment.content.rendered
.replace(
/<[^>]*>/g,
''
)
.substring( 0, 100 ) }
...
</p>
<p className="helix-comments-list__date">
{ formatDate(
comment.date
) }
</p>
</div>
<div className="helix-comments-list__status">
<span
className={ `helix-status helix-status--${ comment.status }` }
>
{ comment.status }
</span>
</div>
</li>
)
) }
</ul>
) : (
<p className="helix-widget__empty">
No comments yet.
</p>
) }
</div>
</div>
{ /* Quick Actions Widget */ }
<div className="helix-widget">
<div className="helix-widget__header">
<h2>Quick Actions</h2>
</div>
<div className="helix-widget__content">
<div className="helix-quick-actions">
<a
href="/wp-admin/post-new.php"
className="helix-quick-action"
>
<span className="helix-quick-action__icon">
</span>
<span className="helix-quick-action__text">
Write a Post
</span>
</a>
<a
href="/wp-admin/post-new.php?post_type=page"
className="helix-quick-action"
>
<span className="helix-quick-action__icon">
📄
</span>
<span className="helix-quick-action__text">
Create a Page
</span>
</a>
<a
href="/wp-admin/upload.php"
className="helix-quick-action"
>
<span className="helix-quick-action__icon">
📁
</span>
<span className="helix-quick-action__text">
Upload Media
</span>
</a>
<a
href="/wp-admin/edit-comments.php"
className="helix-quick-action"
>
<span className="helix-quick-action__icon">
💬
</span>
<span className="helix-quick-action__text">
Moderate Comments
</span>
</a>
</div>
</div>
</div>
{ /* WordPress News Widget */ }
<div className="helix-widget">
<div className="helix-widget__header">
<h2>WordPress News</h2>
</div>
<div className="helix-widget__content">
<p>
Stay updated with the latest WordPress news and
updates.
</p>
<div className="helix-news-placeholder">
<div className="helix-news-item">
<h4>
WordPress 6.4 &ldquo;Shirley&rdquo; Released
</h4>
<p>
The latest version includes new features and
improvements...
</p>
<a
href="https://wordpress.org/news/"
target="_blank"
rel="noopener noreferrer"
>
Read more
</a>
</div>
</div>
</div>
</div>
</div>
</div>
);
}