mirror of
https://ghproxy.net/https://github.com/abhijitb/helix.git
synced 2025-08-29 01:08:27 +08:00
added widgets to dashboard
This commit is contained in:
parent
e80316be89
commit
073173e145
6 changed files with 662 additions and 50 deletions
290
src/pages/Dashboard.css
Normal file
290
src/pages/Dashboard.css
Normal 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;
|
||||
}
|
|
@ -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 “Shirley” 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>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue