feat: Update Sidebar and Dashboard page
- Remove SidebarDrawer - Add CampfirePanelBar - Fix navigation links - Add blog files - Add aggregator file - Update Dashboard.jsx to display blog post links - Add Copyright info
This commit is contained in:
@@ -3,7 +3,7 @@ import { AppBar, AppBarSection, AppBarSpacer } from '@progress/kendo-react-layou
|
||||
import { Button } from '@progress/kendo-react-buttons';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
|
||||
const CampfireAppBar = ({ isLoggedIn, onLogin }) => {
|
||||
const CampfireAppBar = ({ isLoggedIn, onLogin, onDrawerToggle }) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleLogout = () => {
|
||||
@@ -16,11 +16,18 @@ const CampfireAppBar = ({ isLoggedIn, onLogin }) => {
|
||||
<h1 style={{ color: "#ff5733", textAlign: "center" }}>Campfire Logs</h1>
|
||||
<AppBar position="sticky" style={{ backgroundColor: "#edbd7d"}}>
|
||||
<AppBarSection>
|
||||
<Button look="flat" onClick={onDrawerToggle}>
|
||||
<span className="k-icon k-i-menu" />
|
||||
</Button>
|
||||
|
||||
<AppBarSpacer style={{ width: 800 }} />
|
||||
|
||||
<Link to="/dashboard">
|
||||
<Button look="flat">Dashboard</Button>
|
||||
</Link>
|
||||
|
||||
<AppBarSpacer style={{ width: 10 }} />
|
||||
|
||||
<Link to="/editor">
|
||||
<Button look="flat">+ New Post</Button>
|
||||
</Link>
|
||||
|
0
src/components/AuthButton.jsx
Normal file
0
src/components/AuthButton.jsx
Normal file
113
src/components/PanelBar.jsx
Normal file
113
src/components/PanelBar.jsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import React from 'react';
|
||||
import { PanelBar, PanelBarItem } from '@progress/kendo-react-layout';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { SvgIcon } from '@progress/kendo-react-common';
|
||||
import {
|
||||
bookIcon, inboxIcon, trackChangesIcon, plusOutlineIcon,
|
||||
globeOutlineIcon, linkIcon, tellAFriendIcon,
|
||||
facebookIcon, xLogoIcon, linkedinIcon, redditIcon
|
||||
} from '@progress/kendo-svg-icons';
|
||||
import { panelbarData } from '../data/panelbar-data';
|
||||
|
||||
const iconMap = {
|
||||
bookIcon, inboxIcon, trackChangesIcon, plusOutlineIcon,
|
||||
globeOutlineIcon, linkIcon, tellAFriendIcon,
|
||||
facebookIcon, xLogoIcon, linkedinIcon, redditIcon
|
||||
};
|
||||
|
||||
const CampfirePanelBar = ({ isExpanded = true }) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const renderItem = (item) => {
|
||||
// External links
|
||||
if (item.url) {
|
||||
return (
|
||||
<PanelBarItem
|
||||
key={item.title}
|
||||
title={
|
||||
<a
|
||||
href={item.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
color: 'inherit',
|
||||
textDecoration: 'none'
|
||||
}}
|
||||
>
|
||||
<SvgIcon
|
||||
icon={iconMap[item.icon]}
|
||||
size="medium"
|
||||
style={{ marginLeft: '30px' }}
|
||||
/>
|
||||
<span style={{ marginLeft: '8px' }}>{item.title}</span>
|
||||
</a>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Internal routes
|
||||
if (item.route) {
|
||||
return (
|
||||
<PanelBarItem
|
||||
key={item.title}
|
||||
title={
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}}
|
||||
onClick={() => navigate(item.route)}
|
||||
>
|
||||
<SvgIcon
|
||||
icon={iconMap[item.icon]}
|
||||
size="medium"
|
||||
style={{ marginLeft: '30px' }}
|
||||
/>
|
||||
<span style={{ marginLeft: '8px' }}>{item.title}</span>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Parent items with children
|
||||
return (
|
||||
<PanelBarItem
|
||||
key={item.title}
|
||||
title={
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<SvgIcon
|
||||
icon={iconMap[item.icon]}
|
||||
size="medium"
|
||||
style={{ marginLeft: '8px' }}
|
||||
/>
|
||||
<span style={{ marginLeft: '8px' }}>{item.title}</span>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{item.items.map(renderItem)}
|
||||
</PanelBarItem>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
width: isExpanded ? 300 : 200,
|
||||
minWidth: isExpanded ? 240 : 60,
|
||||
transition: 'width 0.3s'
|
||||
}}>
|
||||
<PanelBar>
|
||||
{panelbarData.map(renderItem)}
|
||||
</PanelBar>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CampfirePanelBar;
|
@@ -0,0 +1,135 @@
|
||||
import React from 'react';
|
||||
import { Drawer, DrawerContent } from '@progress/kendo-react-layout';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { SvgIcon } from '@progress/kendo-react-common';
|
||||
import { bookIcon, inboxIcon, trackChangesIcon, plusOutlineIcon, globeOutlineIcon, linkIcon, tellAFriendIcon, facebookIcon, xLogoIcon, linkedinIcon, redditIcon } from '@progress/kendo-svg-icons';
|
||||
|
||||
class ErrorBoundary extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hasError: false };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error) {
|
||||
return { hasError: true };
|
||||
}
|
||||
|
||||
componentDidCatch(error, errorInfo) {
|
||||
console.error("ErrorBoundary caught an error", error, errorInfo);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return <h1>Something went wrong.</h1>;
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
const iconMap = {
|
||||
bookIcon,
|
||||
inboxIcon,
|
||||
trackChangesIcon,
|
||||
plusOutlineIcon,
|
||||
globeOutlineIcon,
|
||||
linkIcon,
|
||||
tellAFriendIcon,
|
||||
facebookIcon,
|
||||
xLogoIcon,
|
||||
linkedinIcon,
|
||||
redditIcon
|
||||
};
|
||||
|
||||
const SidebarDrawer = ({ children, isExpanded, onDrawerToggle, isLoggedIn }) => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const drawerItems = [
|
||||
{ text: 'Content Summary', icon: 'bookIcon', route: '/dashboard' },
|
||||
{ separator: true },
|
||||
{ text: 'Published Posts', icon: 'inboxIcon', route: '/posts' },
|
||||
{ text: 'Drafts', icon: 'trackChangesIcon', route: '/posts' },
|
||||
{ text: 'New Post', icon: 'plusOutlineIcon', route: '/editor' },
|
||||
{ separator: true },
|
||||
{ text: 'External Links', icon: 'globeOutlineIcon', route: null },
|
||||
{ separator: true },
|
||||
{ text: 'dlseitz.dev', icon: 'linkIcon', route: 'https://dlseitz.dev', parent: 'External Links' },
|
||||
{ text: 'Gitea', icon: 'linkIcon', route: 'https://gitea.dlseitz.dev', parent: 'External Links' },
|
||||
{ text: 'Notion', icon: 'linkIcon', route: 'https://www.notion.so', parent: 'External Links' },
|
||||
{ text: 'Hashnode', icon: 'linkIcon', route: 'https://hashnode.com', parent: 'External Links' },
|
||||
{ text: 'DEV.to', icon: 'linkIcon', route: 'https://dev.to', parent: 'External Links' },
|
||||
{ text: 'Venice.ai', icon: 'linkIcon', route: 'https://venice.ai', parent: 'External Links' },
|
||||
{ separator: true },
|
||||
{ text: 'Social', icon: 'tellAFriendIcon', route: null },
|
||||
{ separator: true },
|
||||
{ text: 'FaceBook', icon: 'facebookIcon', route: 'https://facebook.com', parent: 'Social' },
|
||||
{ text: 'X', icon: 'xLogoIcon', route: 'https://x.com', parent: 'Social' },
|
||||
{ text: 'LinkedIn', icon: 'linkedinIcon', route: 'https://linkedin.com', parent: 'Social' },
|
||||
{ text: 'Reddit', icon: 'redditIcon', route: 'https://reddit.com', parent: 'Social' }
|
||||
];
|
||||
|
||||
console.log('Drawer Items:', drawerItems);
|
||||
|
||||
const drawerItemRender = (props) => {
|
||||
console.log('itemRender called with props:', props);
|
||||
const { item } = props;
|
||||
const isSelected = item.route && item.route === location.pathname;
|
||||
|
||||
console.log('Rendering item:', item);
|
||||
console.log('Icon Component:', item.icon);
|
||||
console.log('Route:', item.route);
|
||||
|
||||
if (item.separator) {
|
||||
return <li className="k-drawer-separator" />;
|
||||
}
|
||||
|
||||
if (item.route === null) {
|
||||
return (
|
||||
<li className="k-drawer-item k-text-primary">
|
||||
<SvgIcon icon={iconMap[item.icon]} size="medium" />
|
||||
<span style={{ marginLeft: '10px', fontSize: '0.9em', fontWeight: 'bold' }}>{item.text}</span>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
if (item.route.startsWith('http')) {
|
||||
return (
|
||||
<li className="k-drawer-item">
|
||||
<a href={item.route} target="_blank" rel="noopener noreferrer" className="k-drawer-link">
|
||||
<SvgIcon icon={iconMap[item.icon]} />
|
||||
<span style={{ marginLeft: '10px' }}>{item.text}</span>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<li className={`k-drawer-item ${isSelected ? 'k-selected' : ''}`} onClick={() => navigate(item.route)}>
|
||||
<span className="k-drawer-link">
|
||||
<SvgIcon icon={iconMap[item.icon]} />
|
||||
<span style={{ marginLeft: '10px' }}>{item.text}</span>
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<Drawer
|
||||
expanded={isExpanded}
|
||||
mode="push"
|
||||
mini={false}
|
||||
position="start"
|
||||
items={drawerItems.map(item => ({ ...item, selected: item.route === location.pathname }))}
|
||||
itemRender={drawerItemRender}
|
||||
>
|
||||
<DrawerContent>
|
||||
{children}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default SidebarDrawer;
|
20
src/components/UI/Copyright.jsx
Normal file
20
src/components/UI/Copyright.jsx
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright.jsx
|
||||
import React from "react";
|
||||
|
||||
export default function Copyright() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
textAlign: "center",
|
||||
marginTop: "1rem",
|
||||
}}
|
||||
>
|
||||
<p>
|
||||
© Derek L. Seitz |{" "}
|
||||
<a href="https://dlseitz.dev" target="_blank" rel="noopener noreferrer" class="k-link">
|
||||
dlseitz.dev
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user