more ui improvements, sidebar, details page, navbar

This commit is contained in:
austinkelsay 2024-03-16 14:56:58 -05:00
parent 6f398f2026
commit be6bb02db0
10 changed files with 237 additions and 67 deletions

View File

@ -26,7 +26,7 @@ export default function CoursesCarousel() {
const courseTemplate = (course) => {
return (
<div onClick={() => router.push(`/course/${course.id}`)} className="flex flex-col items-center w-full px-4 cursor-pointer mt-8">
<div onClick={() => router.push(`/details/${course.id}`)} className="flex flex-col items-center w-full px-4 cursor-pointer mt-8">
<div className="w-86 h-60 bg-gray-200 overflow-hidden rounded-md shadow-lg">
<Image
alt="resource thumbnail"

View File

@ -3,31 +3,92 @@ import Image from 'next/image';
import UserAvatar from './user/UserAvatar';
import MenuTab from '../menutab/MenuTab';
import { Menubar } from 'primereact/menubar';
import { Menu } from 'primereact/menu';
import { useRouter } from 'next/router';
import 'primereact/resources/primereact.min.css';
import 'primeicons/primeicons.css';
import styles from './navbar.module.css';
const Navbar = () => {
const router = useRouter();
const menu = useRef(null);
const menuItems = [
{
label: 'Home',
icon: 'pi pi-home',
command: () => {
// Add your edit functionality here
}
},
{
label: 'Content',
icon: 'pi pi-video',
command: () => {
// Add your delete functionality here
}
},
{
label: 'Chat',
icon: 'pi pi-comment',
items: [
{
label: 'General',
icon: 'pi pi-hashtag',
command: () => {
// Add your edit functionality here
}
},
{
label: 'Nostr',
icon: 'pi pi-hashtag',
command: () => {
// Add your delete functionality here
}
},
{
label: 'Discord',
icon: 'pi pi-hashtag',
command: () => {
// Add your delete functionality here
}
},
{
label: 'Stackernews',
icon: 'pi pi-hashtag',
command: () => {
// Add your delete functionality here
}
}
]
}
];
const start = (
<div className='w-full flex flex-row justify-between'>
<div onClick={() => router.push('/')} className={styles.titleContainer}>
<div className='flex items-center'>
<div className='hidden max-tab:block max-mob:block max-tab:px-6 max-mob:px-6'>
<i className="pi pi-bars text-xl pt-1"
onClick={(e) => menu.current.toggle(e)}></i>
<Menu model={menuItems} popup ref={menu} />
</div>
<div onClick={() => router.push('/')} className="flex flex-row items-center justify-center cursor-pointer">
<Image
alt="logo"
src="/plebdevs-guy.jpg"
width={50}
height={50}
className={`${styles.logo}`}
className="rounded-full mr-2 max-tab:hidden max-mob:hidden"
/>
<h1 className={styles.title}>PlebDevs</h1>
<h1 className="text-white text-xl font-semibold max-tab:text-2xl max-mob:text-2xl">PlebDevs</h1>
</div>
</div>
);
return (
<Menubar start={start} end={UserAvatar} className='px-[2%] bg-gray-800 border-t-0 border-l-0 border-r-0 rounded-none' />
<Menubar
start={start}
end={UserAvatar}
className='px-[2%] bg-gray-800 border-t-0 border-l-0 border-r-0 rounded-none' />
);
};

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React from 'react';
import { Accordion, AccordionTab } from 'primereact/accordion';
import { useRouter } from 'next/router';
import 'primeicons/primeicons.css';
@ -6,17 +6,21 @@ import 'primeicons/primeicons.css';
const Sidebar = () => {
const router = useRouter();
// Helper function to determine if the path matches the current route
const isActive = (path) => {
return router.pathname === path;
};
return (
<div className='w-64 bg-gray-800 p-4'>
<div onClick={() => router.push('/')} className='w-full cursor-pointer hover:bg-gray-700 rounded-lg'>
<p className="p-4 pl-5 rounded-md font-bold"><i className="pi pi-home" /> Home</p>
<div className='max-mob:hidden max-tab:hidden w-72 bg-gray-800 p-4'>
<div onClick={() => router.push('/')} className={`w-full cursor-pointer hover:bg-gray-700 rounded-lg ${isActive('/') ? 'bg-gray-700' : ''}`}>
<p className="p-2 my-2 pl-5 rounded-md font-bold"><i className="pi pi-home" /> Home</p>
</div>
<div onClick={() => router.push('/content')} className='w-full cursor-pointer hover:bg-gray-700 rounded-lg'>
<p className="p-4 pl-5 rounded-md font-bold"><i className="pi pi-video" /> Content</p>
<div onClick={() => router.push('/content')} className={`w-full cursor-pointer hover:bg-gray-700 rounded-lg ${isActive('/content') ? 'bg-gray-700' : ''}`}>
<p className="p-2 my-2 pl-5 rounded-md font-bold"><i className="pi pi-video" /> Content</p>
</div>
<Accordion
className="unstyled border-none bg-transparent"
activeIndex={0}
pt={{
tab: {
@ -24,25 +28,25 @@ const Sidebar = () => {
className: 'border-none bg-transparent hover:bg-gray-700 rounded-lg',
}),
headerAction: ({ context }) => ({
className: 'border-none bg-transparent',
className: 'border-none bg-transparent py-3 my-2',
}),
content: { className: 'border-none bg-transparent pt-0' }
}
}}
>
className="unstyled border-none bg-transparent">
<AccordionTab header={"Chat"}>
<div onClick={() => router.push('/chat/general')} className='w-full cursor-pointer hover:bg-gray-700 rounded-lg'>
<p className="p-4 rounded-md font-bold"><i className="pi pi-hashtag"></i> general</p>
</div>
<div onClick={() => router.push('/chat/nostr')} className='w-full cursor-pointer hover:bg-gray-700 rounded-lg'>
<p className="p-4 rounded-md font-bold"><i className="pi pi-hashtag"></i> nostr</p>
</div>
<div onClick={() => router.push('/chat/discord')} className='w-full cursor-pointer hover:bg-gray-700 rounded-lg'>
<p className="p-4 rounded-md font-bold"><i className="pi pi-hashtag"></i> discord</p>
</div>
<div onClick={() => router.push('/chat/stackernews')} className='w-full cursor-pointer hover:bg-gray-700 rounded-lg'>
<p className="p-4 rounded-md font-bold"><i className="pi pi-hashtag"></i> stackernews</p>
</div>
<div onClick={() => router.push('/chat/general')} className={`w-full cursor-pointer hover:bg-gray-700 rounded-lg ${isActive('/chat/general') ? 'bg-gray-700' : ''}`}>
<p className="p-2 my-2 rounded-md font-bold"><i className="pi pi-hashtag"></i> general</p>
</div>
<div onClick={() => router.push('/chat/nostr')} className={`w-full cursor-pointer hover:bg-gray-700 rounded-lg ${isActive('/chat/nostr') ? 'bg-gray-700' : ''}`}>
<p className="p-2 my-2 rounded-md font-bold"><i className="pi pi-hashtag"></i> nostr</p>
</div>
<div onClick={() => router.push('/chat/discord')} className={`w-full cursor-pointer hover:bg-gray-700 rounded-lg ${isActive('/chat/discord') ? 'bg-gray-700' : ''}`}>
<p className="p-2 my-2 rounded-md font-bold"><i className="pi pi-hashtag"></i> discord</p>
</div>
<div onClick={() => router.push('/chat/stackernews')} className={`w-full cursor-pointer hover:bg-gray-700 rounded-lg ${isActive('/chat/stackernews') ? 'bg-gray-700' : ''}`}>
<p className="p-2 my-2 rounded-md font-bold"><i className="pi pi-hashtag"></i> stackernews</p>
</div>
</AccordionTab>
</Accordion>
</div>

View File

@ -9,6 +9,7 @@ import { formatTimestampToHowLongAgo } from '@/utils/time';
export default function WorkshopsCarousel() {
const resources = useSelector((state) => state.events.resources);
console.log(resources);
const [processedResources, setProcessedResources] = useState([]);
const { returnImageProxy } = useImageProxy();
@ -24,7 +25,7 @@ export default function WorkshopsCarousel() {
const resourceTemplate = (resource) => {
return (
<div onClick={() => router.push(`/resource/${resource.id}`)} className="flex flex-col items-center w-full px-4 cursor-pointer mt-8">
<div onClick={() => router.push(`/details/${resource.id}`)} className="flex flex-col items-center w-full px-4 cursor-pointer mt-8">
<div className="w-86 h-60 bg-gray-200 overflow-hidden rounded-md shadow-lg">
<Image
alt="resource thumbnail"

View File

@ -1,5 +1,4 @@
import Head from 'next/head'
import React, {useCallback, useEffect, useState} from 'react';
import React, { useEffect } from 'react';
import CoursesCarousel from '@/components/courses/CoursesCarousel'
import WorkshopsCarousel from '@/components/workshops/WorkshopsCarousel'
import MenuTab from '@/components/menutab/MenuTab'
@ -22,18 +21,10 @@ export default function Content() {
}, [fetchResources, fetchCourses]);
return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<MenuTab items={homeItems} />
<CoursesCarousel />
<WorkshopsCarousel />
</main>
</>
)
}

View File

@ -0,0 +1,62 @@
import React, { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import { useNostr } from '@/hooks/useNostr';
import { parseEvent } from '@/utils/nostr';
import { useImageProxy } from '@/hooks/useImageProxy';
import Image from 'next/image';
import 'primeicons/primeicons.css';
export default function Details() {
const [event, setEvent] = useState(null);
const [processedEvent, setProcessedEvent] = useState({});
const { returnImageProxy } = useImageProxy();
const { fetchSingleEvent } = useNostr();
const router = useRouter();
useEffect(() => {
if (router.isReady) {
const { slug } = router.query;
const fetchEvent = async (slug) => {
const event = await fetchSingleEvent(slug);
if (event) {
setEvent(event);
}
};
fetchEvent(slug);
}
}, [router.isReady, router.query]);
useEffect(() => {
if (event) {
const { id, content, title, summary, image, published_at } = parseEvent(event);
setProcessedEvent({ id, content, title, summary, image, published_at });
}
}, [event]);
return (
<div className='flex flex-row justify-between m-4'>
<i className='pi pi-arrow-left cursor-pointer hover:opacity-75' onClick={() => router.push('/')} />
<div className='flex flex-col'>
{
processedEvent && (
<>
<Image
alt="resource thumbnail"
src={returnImageProxy(processedEvent.image)}
width={344}
height={194}
className="w-full h-full object-cover object-center rounded-lg"
/>
<h2>{processedEvent.title}</h2>
<p>{processedEvent.summary}</p>
</>
)
}
</div>
</div>
);
}

View File

@ -1,6 +1,7 @@
import React from "react";
import React, {useRef} from "react";
import { Button } from "primereact/button";
import { DataTable } from 'primereact/datatable';
import { Menu } from 'primereact/menu';
import { Column } from 'primereact/column';
import { useSelector } from "react-redux";
import { useImageProxy } from "@/hooks/useImageProxy";
@ -9,44 +10,85 @@ import Image from "next/image";
const Profile = () => {
const user = useSelector((state) => state.user.user);
const { returnImageProxy } = useImageProxy();
const menu = useRef(null);
const purchases = [
{ code: '123', name: 'Product 1', category: 'Category 1', quantity: 1 },
{ code: '124', name: 'Product 2', category: 'Category 2', quantity: 2 },
{ code: '125', name: 'Product 3', category: 'Category 3', quantity: 3 },
{ code: '126', name: 'Product 4', category: 'Category 4', quantity: 4 },
{ code: '127', name: 'Product 5', category: 'Category 5', quantity: 5 },
{
cost: 100,
name: 'Course 1',
category: 'Education',
date: '2021-09-01'
},
{
cost: 200,
name: 'Course 2',
category: 'Education',
date: '2021-09-01'
},
{
cost: 300,
name: 'Course 3',
category: 'Education',
date: '2021-09-01'
},
{
cost: 400,
name: 'Course 4',
category: 'Education',
date: '2021-09-01'
}
];
const menuItems = [
{
label: 'Edit',
icon: 'pi pi-pencil',
command: () => {
// Add your edit functionality here
}
},
{
label: 'Delete',
icon: 'pi pi-trash',
command: () => {
// Add your delete functionality here
}
}
];
return (
<>
<div className="flex flex-col justify-center">
{user.avatar && (
<Image
alt="logo"
src={returnImageProxy(user.avatar)}
width={100}
height={100}
className="rounded-full mx-auto my-4"
/>
)}
<div className="flex flex-col justify-center px-12">
<div className="relative flex w-full items-center justify-center">
{user.avatar && (
<Image
alt="user's avatar"
src={returnImageProxy(user.avatar)}
width={100}
height={100}
className="rounded-full my-4"
/>
)}
<i className="pi pi-ellipsis-h absolute right-24 text-2xl my-4 cursor-pointer hover:opacity-75"
onClick={(e) => menu.current.toggle(e)}></i>
<Menu model={menuItems} popup ref={menu} />
</div>
<h1 className="text-center text-2xl my-2">{user.username}</h1>
<h2 className="text-center text-xl my-2">{user.pubkey}</h2>
<div className="flex flex-row w-1/2 mx-auto my-4 justify-between">
<Button label="Edit" className="p-button-raised text-[#f8f8ff]" />
<Button label="Delete" className="p-button-raised p-button-danger text-[#f8f8ff]" />
</div>
<div className="flex flex-col w-1/2 mx-auto my-4 justify-between items-center">
<h2>Subscription</h2>
<p>You currently have no active subscription</p>
<Button label="Subscribe" className="p-button-raised p-button-success w-auto my-2 text-[#f8f8ff]" />
</div>
</div>
<DataTable value={purchases} tableStyle={{ minWidth: '50rem' }}>
<Column field="code" header="Code"></Column>
<DataTable emptyMessage="No purchases" value={purchases} tableStyle={{ minWidth: '10rem' }}>
<Column field="cost" header="Cost"></Column>
<Column field="name" header="Name"></Column>
<Column field="category" header="Category"></Column>
<Column field="quantity" header="Quantity"></Column>
<Column field="date" header="Date"></Column>
</DataTable>
</>
)

View File

@ -5,15 +5,16 @@ const addItems = (state, action, key) => {
const existingIds = new Set(state[key].map(item => item.id));
if (Array.isArray(action.payload)) {
console.log('action.payload', action.payload);
// Filter out duplicates based on the id
const uniqueItems = action.payload.filter(item => !existingIds.has(item.id));
// If payload is an array, spread it into the existing array without duplicates
state[key] = [...state[key], ...uniqueItems];
state[key] = [...state[key], ...action.payload];
} else {
// If payload is a single item, push it into the array if it's not a duplicate
if (!existingIds.has(action.payload.id)) {
// if (!existingIds.has(action.payload.id)) {
state[key].push(action.payload);
}
// }
}
};

View File

@ -18,3 +18,8 @@
background-color: transparent !important;
border: none !important;
}
.p-menubar-button .p-icon {
display: none !important;
}

View File

@ -7,10 +7,13 @@ module.exports = {
],
theme: {
extend: {
screens: {
'max-mob': {'max': '475px'},
'max-tab': {'max': '768px'},
},
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic':
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
},
},
},