mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-04-19 19:01:19 +00:00
Changed resource to document on the frontend
This commit is contained in:
parent
aa13faaf44
commit
cb3f124c3a
@ -15,7 +15,7 @@ const BottomBar = () => {
|
||||
<i className="pi pi-home text-2xl" />
|
||||
</div>
|
||||
<div onClick={() => router.push('/content?tag=all')} className={`hover:bg-gray-700 cursor-pointer px-4 py-3 rounded-lg ${isActive('/content') ? 'bg-gray-700' : ''}`}>
|
||||
<i className="pi pi-video text-2xl" />
|
||||
<i className="pi pi-play-circle text-2xl" />
|
||||
</div>
|
||||
<div onClick={() => router.push('/feed?channel=global')} className={`hover:bg-gray-700 cursor-pointer px-4 py-3 rounded-lg ${isActive('/feed') ? 'bg-gray-700' : ''}`}>
|
||||
<i className="pi pi-comments text-2xl" />
|
||||
|
@ -4,6 +4,7 @@ import { useImageProxy } from "@/hooks/useImageProxy";
|
||||
import { formatUnixTimestamp } from "@/utils/time";
|
||||
import GenericButton from "@/components/buttons/GenericButton";
|
||||
const SelectedContentItem = ({ content, onRemove }) => {
|
||||
console.log('content:', content);
|
||||
const { returnImageProxy } = useImageProxy();
|
||||
|
||||
return (
|
||||
|
@ -1,10 +1,9 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Carousel } from 'primereact/carousel';
|
||||
import { parseEvent } from '@/utils/nostr';
|
||||
// import ResourceTemplate from '@/components/content/carousels/templates/ResourceTemplate';
|
||||
import { DocumentTemplate } from '@/components/content/carousels/templates/DocumentTemplate';
|
||||
import TemplateSkeleton from '@/components/content/carousels/skeletons/TemplateSkeleton';
|
||||
import { useResources } from '@/hooks/nostr/useResources';
|
||||
import { useDocuments } from '@/hooks/nostr/useDocuments';
|
||||
|
||||
const responsiveOptions = [
|
||||
{
|
||||
@ -24,44 +23,42 @@ const responsiveOptions = [
|
||||
}
|
||||
];
|
||||
|
||||
export default function ResourcesCarousel() {
|
||||
const [processedResources, setProcessedResources] = useState([]);
|
||||
const { resources, resourcesLoading, resourcesError } = useResources()
|
||||
export default function DocumentsCarousel() {
|
||||
const [processedDocuments, setProcessedDocuments] = useState([]);
|
||||
const { documents, documentsLoading, documentsError } = useDocuments()
|
||||
|
||||
useEffect(() => {
|
||||
const fetch = async () => {
|
||||
try {
|
||||
if (resources && resources.length > 0) {
|
||||
const processedResources = resources.map(resource => parseEvent(resource));
|
||||
if (documents && documents.length > 0) {
|
||||
const processedDocuments = documents.map(document => parseEvent(document));
|
||||
|
||||
// Sort resources by created_at in descending order (most recent first)
|
||||
const sortedResources = processedResources.sort((a, b) => b.created_at - a.created_at);
|
||||
// Sort documents by created_at in descending order (most recent first)
|
||||
const sortedDocuments = processedDocuments.sort((a, b) => b.created_at - a.created_at);
|
||||
|
||||
console.log("Sorted resources:", sortedResources);
|
||||
|
||||
setProcessedResources(sortedResources);
|
||||
setProcessedDocuments(sortedDocuments);
|
||||
} else {
|
||||
console.log('No resources fetched or empty array returned');
|
||||
console.log('No documents fetched or empty array returned');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching resources:', error);
|
||||
console.error('Error fetching documents:', error);
|
||||
}
|
||||
};
|
||||
fetch();
|
||||
}, [resources]);
|
||||
}, [documents]);
|
||||
|
||||
if (resourcesError) {
|
||||
return <div>Error: {resourcesError.message}</div>
|
||||
if (documentsError) {
|
||||
return <div>Error: {documentsError.message}</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<h3 className="ml-[6%] mt-4">Resources</h3>
|
||||
<h3 className="ml-[6%] mt-4">Documents</h3>
|
||||
<Carousel
|
||||
value={resourcesLoading || !processedResources.length ? [{}, {}, {}] : [...processedResources]}
|
||||
value={documentsLoading || !processedDocuments.length ? [{}, {}, {}] : [...processedDocuments]}
|
||||
numVisible={2}
|
||||
itemTemplate={(item) =>
|
||||
processedResources.length > 0 ?
|
||||
processedDocuments.length > 0 ?
|
||||
<DocumentTemplate key={item.id} document={item} /> :
|
||||
<TemplateSkeleton key={Math.random()} />
|
||||
}
|
@ -64,7 +64,7 @@ export default function GenericCarousel({items, selectedTopic, title}) {
|
||||
value={carouselItems}
|
||||
itemTemplate={(item) => {
|
||||
if (carouselItems.length > 0) {
|
||||
if (item.type === 'resource') {
|
||||
if (item.type === 'document') {
|
||||
return <DocumentTemplate key={item.id} document={item} />;
|
||||
} else if (item.type === 'video') {
|
||||
return <VideoTemplate key={item.id} video={item} />;
|
||||
|
@ -33,9 +33,9 @@ const promotions = [
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
category: "RESOURCES",
|
||||
category: "DOCUMENTS",
|
||||
title: "In-depth Resources and Documentation",
|
||||
description: "Access our extensive library of resources, including guides, documentation, and best practices for Bitcoin development.",
|
||||
description: "Access our extensive library of documents, including guides, resources, and best practices for Bitcoin development.",
|
||||
icon: "pi pi-file",
|
||||
image: "https://img.freepik.com/free-photo/programming-background-with-person-working-with-codes-computer_23-2150010125.jpg",
|
||||
},
|
||||
@ -101,9 +101,9 @@ const InteractivePromotionalCarousel = () => {
|
||||
return (
|
||||
<GenericButton onClick={() => router.push('/content?tag=videos')} icon={<i className="pi pi-video pr-2" />} label="View All Videos" className="w-fit py-2 font-semibold" size="small" outlined />
|
||||
);
|
||||
case "RESOURCES":
|
||||
case "DOCUMENTS":
|
||||
return (
|
||||
<GenericButton onClick={() => router.push('/content?tag=resources')} icon={<i className="pi pi-file pr-2 pb-1" />} label="View All Resources" className="w-fit py-2 font-semibold" size="small" outlined />
|
||||
<GenericButton onClick={() => router.push('/content?tag=documents')} icon={<i className="pi pi-file pr-2 pb-1" />} label="View All Documents" className="w-fit py-2 font-semibold" size="small" outlined />
|
||||
);
|
||||
case "COMMUNITY":
|
||||
return (
|
||||
@ -149,9 +149,9 @@ const InteractivePromotionalCarousel = () => {
|
||||
return (
|
||||
<GenericButton onClick={() => router.push('/content?tag=videos')} icon={<i className="pi pi-video pr-2" />} label="View All Videos" className="py-2 font-semibold" size="small" outlined />
|
||||
);
|
||||
case "RESOURCES":
|
||||
case "DOCUMENTS":
|
||||
return (
|
||||
<GenericButton onClick={() => router.push('/content?tag=resources')} icon={<i className="pi pi-file pr-2 pb-1" />} label="View All Resources" className="py-2 font-semibold" size="small" outlined />
|
||||
<GenericButton onClick={() => router.push('/content?tag=documents')} icon={<i className="pi pi-file pr-2 pb-1" />} label="View All Documents" className="py-2 font-semibold" size="small" outlined />
|
||||
);
|
||||
case "COMMUNITY":
|
||||
return (
|
||||
|
@ -94,14 +94,14 @@ export function CourseTemplate({ course }) {
|
||||
{course.description || course.summary && (
|
||||
<>
|
||||
{course.description && (
|
||||
<div className="text-xl mt-4">
|
||||
<div>
|
||||
{course.description.split('\n').map((line, index) => (
|
||||
<p key={index}>{line}</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{course.summary && (
|
||||
<div className="text-xl mt-4">
|
||||
<div>
|
||||
{course.summary.split('\n').map((line, index) => (
|
||||
<p key={index}>{line}</p>
|
||||
))}
|
||||
|
@ -82,14 +82,14 @@ export function DocumentTemplate({ document }) {
|
||||
{document.description || document.summary && (
|
||||
<>
|
||||
{document.description && (
|
||||
<div className="text-xl mt-4">
|
||||
<div>
|
||||
{document.description.split('\n').map((line, index) => (
|
||||
<p key={index}>{line}</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{document.summary && (
|
||||
<div className="text-xl mt-4">
|
||||
<div>
|
||||
{document.summary.split('\n').map((line, index) => (
|
||||
<p key={index}>{line}</p>
|
||||
))}
|
||||
|
@ -83,14 +83,14 @@ export function VideoTemplate({ video }) {
|
||||
{video.description || video.summary && (
|
||||
<>
|
||||
{video.description && (
|
||||
<div className="text-xl mt-4">
|
||||
<div>
|
||||
{video.description.split('\n').map((line, index) => (
|
||||
<p key={index}>{line}</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{video.summary && (
|
||||
<div className="text-xl mt-4">
|
||||
<div>
|
||||
{video.summary.split('\n').map((line, index) => (
|
||||
<p key={index}>{line}</p>
|
||||
))}
|
||||
|
@ -95,7 +95,7 @@ const CourseLesson = ({ lesson, course, decryptionPerformed, isPaid }) => {
|
||||
{lesson && (
|
||||
<div className='flex flex-col items-center justify-between rounded-lg h-72 p-4 bg-gray-700 drop-shadow-md'>
|
||||
<Image
|
||||
alt="resource thumbnail"
|
||||
alt="course thumbnail"
|
||||
src={returnImageProxy(lesson.image)}
|
||||
width={344}
|
||||
height={194}
|
||||
|
@ -129,9 +129,7 @@ const DocumentLesson = ({ lesson, course, decryptionPerformed, isPaid }) => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Divider />
|
||||
</div>
|
||||
{renderContent()}
|
||||
<Divider />
|
||||
{lesson?.additionalLinks && lesson.additionalLinks.length > 0 && (
|
||||
<div className='mt-6 bg-gray-800/90 rounded-lg p-4'>
|
||||
<h3 className='text-lg font-semibold mb-2 text-white'>External links:</h3>
|
||||
@ -146,6 +144,8 @@ const DocumentLesson = ({ lesson, course, decryptionPerformed, isPaid }) => {
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{renderContent()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -212,7 +212,7 @@ export default function DraftCourseDetails({ processedEvent, draftId, lessons })
|
||||
|
||||
// Step 6: Show success message and redirect
|
||||
showToast('success', 'Success', 'Course created successfully');
|
||||
router.push(`/course/${courseEvent.id}`);
|
||||
router.push("/");
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error creating course:', error);
|
||||
@ -252,7 +252,7 @@ export default function DraftCourseDetails({ processedEvent, draftId, lessons })
|
||||
console.log('Draft:', draft);
|
||||
|
||||
switch (draft?.type) {
|
||||
case 'resource':
|
||||
case 'document':
|
||||
if (draft?.price) {
|
||||
// encrypt the content with NEXT_PUBLIC_APP_PRIV_KEY to NEXT_PUBLIC_APP_PUBLIC_KEY
|
||||
encryptedContent = await nip04.encrypt(process.env.NEXT_PUBLIC_APP_PRIV_KEY, process.env.NEXT_PUBLIC_APP_PUBLIC_KEY, draft.content);
|
||||
@ -273,7 +273,7 @@ export default function DraftCourseDetails({ processedEvent, draftId, lessons })
|
||||
...(draft?.additionalLinks ? draft.additionalLinks.map(link => ['r', link]) : []),
|
||||
];
|
||||
|
||||
type = 'resource';
|
||||
type = 'document';
|
||||
break;
|
||||
case 'video':
|
||||
if (draft?.price) {
|
||||
|
@ -110,7 +110,7 @@ const DraftCourseLesson = ({ lesson, course }) => {
|
||||
{lesson && (
|
||||
<div className='flex flex-col items-center justify-between rounded-lg h-72 p-4 bg-gray-700 drop-shadow-md'>
|
||||
<Image
|
||||
alt="resource thumbnail"
|
||||
alt="course thumbnail"
|
||||
src={returnImageProxy(lesson.image)}
|
||||
width={344}
|
||||
height={194}
|
||||
|
@ -22,7 +22,7 @@ import { Tooltip } from 'primereact/tooltip';
|
||||
import 'primereact/resources/primereact.min.css';
|
||||
|
||||
// todo make the summarry save in a formatted way so we can keep this spaces and line breaks
|
||||
const ResourceForm = ({ draft = null, isPublished = false }) => {
|
||||
const DocumentForm = ({ draft = null, isPublished = false }) => {
|
||||
const [title, setTitle] = useState(draft?.title || '');
|
||||
const [summary, setSummary] = useState(draft?.summary || '');
|
||||
const [isPaidResource, setIsPaidResource] = useState(draft?.price ? true : false);
|
||||
@ -104,7 +104,7 @@ const ResourceForm = ({ draft = null, isPublished = false }) => {
|
||||
content,
|
||||
d: draft.d,
|
||||
image: coverImage,
|
||||
topics: [...new Set([...topics.map(topic => topic.trim().toLowerCase()), 'resource'])],
|
||||
topics: [...new Set([...topics.map(topic => topic.trim().toLowerCase()), 'document'])],
|
||||
additionalLinks: additionalLinks.filter(link => link.trim() !== '')
|
||||
}
|
||||
|
||||
@ -127,14 +127,14 @@ const ResourceForm = ({ draft = null, isPublished = false }) => {
|
||||
// update the resource with new noteId
|
||||
const response = await axios.put(`/api/resources/${draft.d}`, { noteId: event.id });
|
||||
console.log('response', response);
|
||||
showToast('success', 'Success', 'Resource published successfully.');
|
||||
showToast('success', 'Success', 'Document published successfully.');
|
||||
router.push(`/details/${event.id}`);
|
||||
} else {
|
||||
showToast('error', 'Error', 'Failed to publish resource. Please try again.');
|
||||
showToast('error', 'Error', 'Failed to publish document. Please try again.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
showToast('error', 'Error', 'Failed to publish resource. Please try again.');
|
||||
showToast('error', 'Error', 'Failed to publish document. Please try again.');
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,11 +151,11 @@ const ResourceForm = ({ draft = null, isPublished = false }) => {
|
||||
const payload = {
|
||||
title,
|
||||
summary,
|
||||
type: 'resource',
|
||||
type: 'document',
|
||||
price: isPaidResource ? price : null,
|
||||
content,
|
||||
image: coverImage,
|
||||
topics: [...new Set([...topics.map(topic => topic.trim().toLowerCase()), 'resource'])],
|
||||
topics: [...new Set([...topics.map(topic => topic.trim().toLowerCase()), 'document'])],
|
||||
additionalLinks: additionalLinks.filter(link => link.trim() !== '')
|
||||
};
|
||||
|
||||
@ -171,7 +171,7 @@ const ResourceForm = ({ draft = null, isPublished = false }) => {
|
||||
axios[method](url, payload)
|
||||
.then(response => {
|
||||
if (response.status === 200 || response.status === 201) {
|
||||
showToast('success', 'Success', draft ? 'Resource updated successfully.' : 'Resource saved as draft.');
|
||||
showToast('success', 'Success', draft ? 'Document updated successfully.' : 'Document saved as draft.');
|
||||
|
||||
if (response.data?.id) {
|
||||
router.push(`/draft/${response.data.id}`);
|
||||
@ -180,7 +180,7 @@ const ResourceForm = ({ draft = null, isPublished = false }) => {
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
showToast('error', 'Error', 'Failed to save resource. Please try again.');
|
||||
showToast('error', 'Error', 'Failed to save document. Please try again.');
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -230,7 +230,7 @@ const ResourceForm = ({ draft = null, isPublished = false }) => {
|
||||
</div>
|
||||
|
||||
<div className="p-inputgroup flex-1 mt-8 flex-col">
|
||||
<p className="py-2">Paid Resource</p>
|
||||
<p className="py-2">Paid Document</p>
|
||||
<InputSwitch autoResize checked={isPaidResource} onChange={(e) => setIsPaidResource(e.value)} />
|
||||
{isPaidResource && (
|
||||
<div className="p-inputgroup flex-1 py-4">
|
||||
@ -292,4 +292,4 @@ const ResourceForm = ({ draft = null, isPublished = false }) => {
|
||||
);
|
||||
}
|
||||
|
||||
export default ResourceForm;
|
||||
export default DocumentForm;
|
@ -9,7 +9,7 @@ import { useRouter } from 'next/router';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { parseEvent } from '@/utils/nostr';
|
||||
import { useDraftsQuery } from '@/hooks/apiQueries/useDraftsQuery';
|
||||
import { useResources } from '@/hooks/nostr/useResources';
|
||||
import { useDocuments } from '@/hooks/nostr/useDocuments';
|
||||
import { useVideos } from '@/hooks/nostr/useVideos';
|
||||
import axios from 'axios';
|
||||
import LessonSelector from './LessonSelector';
|
||||
@ -27,16 +27,17 @@ const CourseForm = ({ draft = null }) => {
|
||||
const { data: session } = useSession();
|
||||
const router = useRouter();
|
||||
const { showToast } = useToast();
|
||||
const { resources, resourcesLoading, resourcesError } = useResources();
|
||||
const { documents, documentsLoading, documentsError } = useDocuments();
|
||||
const { videos, videosLoading, videosError } = useVideos();
|
||||
const { drafts, draftsLoading, draftsError } = useDraftsQuery();
|
||||
|
||||
useEffect(() => {
|
||||
if (draft && resources && videos && drafts) {
|
||||
if (draft && documents && videos && drafts) {
|
||||
const populatedLessons = draft.draftLessons.map((lesson, index) => {
|
||||
if (lesson?.resource) {
|
||||
const matchingResource = resources.find((resource) => resource.d === lesson.resource.d);
|
||||
return { ...parseEvent(matchingResource), index };
|
||||
const matchingResource = documents.find((resource) => resource.d === lesson.resource.d);
|
||||
const matchingParsedResource = parseEvent(matchingResource);
|
||||
return { ...matchingParsedResource, index };
|
||||
} else if (lesson?.draft) {
|
||||
const matchingDraft = drafts.find((draft) => draft.id === lesson.draft.id);
|
||||
return { ...matchingDraft, index };
|
||||
@ -46,24 +47,15 @@ const CourseForm = ({ draft = null }) => {
|
||||
|
||||
setLessons(populatedLessons);
|
||||
}
|
||||
}, [draft, resources, videos, drafts]);
|
||||
}, [draft, documents, videos, drafts]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('allContent', allContent);
|
||||
}, [allContent]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('fasfsa', videos)
|
||||
}, [videos])
|
||||
|
||||
useEffect(() => {
|
||||
if (!resourcesLoading && !videosLoading && !draftsLoading) {
|
||||
if (!documentsLoading && !videosLoading && !draftsLoading) {
|
||||
let combinedContent = [];
|
||||
if (resources) {
|
||||
combinedContent = [...combinedContent, ...resources];
|
||||
if (documents) {
|
||||
combinedContent = [...combinedContent, ...documents];
|
||||
}
|
||||
if (videos) {
|
||||
console.log('workssdfsdfdsf', videos)
|
||||
combinedContent = [...combinedContent, ...videos];
|
||||
}
|
||||
if (drafts) {
|
||||
@ -71,7 +63,7 @@ const CourseForm = ({ draft = null }) => {
|
||||
}
|
||||
setAllContent(combinedContent);
|
||||
}
|
||||
}, [resources, videos, drafts, resourcesLoading, videosLoading, draftsLoading]);
|
||||
}, [documents, videos, drafts, documentsLoading, videosLoading, draftsLoading]);
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
@ -171,7 +163,7 @@ const CourseForm = ({ draft = null }) => {
|
||||
}
|
||||
};
|
||||
|
||||
if (resourcesLoading || videosLoading || draftsLoading) {
|
||||
if (documentsLoading || videosLoading || draftsLoading) {
|
||||
return <ProgressSpinner />;
|
||||
}
|
||||
|
||||
|
@ -3,14 +3,14 @@ import { Dropdown } from 'primereact/dropdown';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { Dialog } from 'primereact/dialog';
|
||||
import { Accordion, AccordionTab } from 'primereact/accordion';
|
||||
import EmbeddedResourceForm from '@/components/forms/course/embedded/EmbeddedResourceForm';
|
||||
import EmbeddedDocumentForm from '@/components/forms/course/embedded/EmbeddedDocumentForm';
|
||||
import EmbeddedVideoForm from '@/components/forms/course/embedded/EmbeddedVideoForm';
|
||||
import ContentDropdownItem from '@/components/content/dropdowns/ContentDropdownItem';
|
||||
import SelectedContentItem from '@/components/content/SelectedContentItem';
|
||||
import { parseEvent } from '@/utils/nostr';
|
||||
|
||||
const LessonSelector = ({ isPaidCourse, lessons, setLessons, allContent, onNewResourceCreate, onNewVideoCreate }) => {
|
||||
const [showResourceForm, setShowResourceForm] = useState(false);
|
||||
const [showDocumentForm, setShowDocumentForm] = useState(false);
|
||||
const [showVideoForm, setShowVideoForm] = useState(false);
|
||||
const [contentOptions, setContentOptions] = useState([]);
|
||||
const [openTabs, setOpenTabs] = useState([]);
|
||||
@ -47,7 +47,7 @@ const LessonSelector = ({ isPaidCourse, lessons, setLessons, allContent, onNewRe
|
||||
|
||||
console.log('filtered content', filteredContent)
|
||||
|
||||
const draftResourceOptions = filteredContent.filter(content => content?.topics.includes('resource') && !content.kind).map(content => ({
|
||||
const draftDocumentOptions = filteredContent.filter(content => content?.topics.includes('document') && !content.kind).map(content => ({
|
||||
label: content.title,
|
||||
value: content
|
||||
}));
|
||||
@ -57,7 +57,7 @@ const LessonSelector = ({ isPaidCourse, lessons, setLessons, allContent, onNewRe
|
||||
value: content
|
||||
}));
|
||||
|
||||
const resourceOptions = filteredContent.filter(content => content?.topics.includes('resource') && content.kind).map(content => ({
|
||||
const documentOptions = filteredContent.filter(content => content?.topics.includes('document') && content.kind).map(content => ({
|
||||
label: content.title,
|
||||
value: content
|
||||
}));
|
||||
@ -69,16 +69,16 @@ const LessonSelector = ({ isPaidCourse, lessons, setLessons, allContent, onNewRe
|
||||
|
||||
setContentOptions([
|
||||
{
|
||||
label: 'Draft Resources',
|
||||
items: draftResourceOptions
|
||||
label: 'Draft Documents',
|
||||
items: draftDocumentOptions
|
||||
},
|
||||
{
|
||||
label: 'Draft Videos',
|
||||
items: draftVideoOptions
|
||||
},
|
||||
{
|
||||
label: 'Published Resources',
|
||||
items: resourceOptions
|
||||
label: 'Published Documents',
|
||||
items: documentOptions
|
||||
},
|
||||
{
|
||||
label: 'Published Videos',
|
||||
@ -116,16 +116,15 @@ const LessonSelector = ({ isPaidCourse, lessons, setLessons, allContent, onNewRe
|
||||
setLessons([...lessons, { index: lessons.length }]);
|
||||
};
|
||||
|
||||
const handleNewResourceSave = async (newResource) => {
|
||||
const createdResource = await onNewResourceCreate(newResource);
|
||||
if (createdResource) {
|
||||
handleContentSelect(createdResource, lessons.length);
|
||||
setShowResourceForm(false);
|
||||
const handleNewDocumentSave = async (newDocument) => {
|
||||
const createdDocument = await onNewDocumentCreate(newDocument);
|
||||
if (createdDocument) {
|
||||
handleContentSelect(createdDocument, lessons.length);
|
||||
setShowDocumentForm(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleNewVideoSave = async (newVideo) => {
|
||||
console.log('newVideo', newVideo);
|
||||
const createdVideo = await onNewVideoCreate(newVideo);
|
||||
if (createdVideo) {
|
||||
handleContentSelect(createdVideo, lessons.length);
|
||||
@ -167,7 +166,7 @@ const LessonSelector = ({ isPaidCourse, lessons, setLessons, allContent, onNewRe
|
||||
<div className="flex mt-4">
|
||||
{lesson.id ? null : (
|
||||
<>
|
||||
<GenericButton label="New Resource" onClick={(e) => {e.preventDefault(); setShowResourceForm(true)}} className="mr-2" />
|
||||
<GenericButton label="New Document" onClick={(e) => {e.preventDefault(); setShowDocumentForm(true)}} className="mr-2" />
|
||||
<GenericButton label="New Video" onClick={(e) => {e.preventDefault(); setShowVideoForm(true)}} className="mr-2" />
|
||||
</>
|
||||
)}
|
||||
@ -187,11 +186,11 @@ const LessonSelector = ({ isPaidCourse, lessons, setLessons, allContent, onNewRe
|
||||
label="Add New Lesson"
|
||||
onClick={addNewLesson}
|
||||
className="mt-4"
|
||||
type="button" // Explicitly set type to "button"
|
||||
type="button"
|
||||
/>
|
||||
|
||||
<Dialog className='w-full max-w-screen-md' visible={showResourceForm} onHide={() => setShowResourceForm(false)} header="Create New Resource">
|
||||
<EmbeddedResourceForm onSave={handleNewResourceSave} isPaid={isPaidCourse} />
|
||||
<Dialog className='w-full max-w-screen-md' visible={showDocumentForm} onHide={() => setShowDocumentForm(false)} header="Create New Document">
|
||||
<EmbeddedDocumentForm onSave={handleNewDocumentSave} isPaid={isPaidCourse} />
|
||||
</Dialog>
|
||||
|
||||
<Dialog className='w-full max-w-screen-md' visible={showVideoForm} onHide={() => setShowVideoForm(false)} header="Create New Video">
|
||||
|
@ -19,7 +19,7 @@ import 'primeicons/primeicons.css';
|
||||
import { Tooltip } from 'primereact/tooltip';
|
||||
import 'primereact/resources/primereact.min.css';
|
||||
|
||||
const EmbeddedResourceForm = ({ draft = null, isPublished = false, onSave, isPaid }) => {
|
||||
const EmbeddedDocumentForm = ({ draft = null, isPublished = false, onSave, isPaid }) => {
|
||||
const [title, setTitle] = useState(draft?.title || '');
|
||||
const [summary, setSummary] = useState(draft?.summary || '');
|
||||
const [isPaidResource, setIsPaidResource] = useState(isPaid);
|
||||
@ -96,11 +96,11 @@ const EmbeddedResourceForm = ({ draft = null, isPublished = false, onSave, isPai
|
||||
const payload = {
|
||||
title,
|
||||
summary,
|
||||
type: 'resource',
|
||||
type: 'document',
|
||||
price: isPaidResource ? price : null,
|
||||
content,
|
||||
image: coverImage,
|
||||
topics: [...new Set([...topics.map(topic => topic.trim().toLowerCase()), 'resource'])],
|
||||
topics: [...new Set([...topics.map(topic => topic.trim().toLowerCase()), 'document'])],
|
||||
additionalLinks: additionalLinks.filter(link => link.trim() !== ''),
|
||||
user: user?.id || user?.pubkey
|
||||
};
|
||||
@ -108,10 +108,10 @@ const EmbeddedResourceForm = ({ draft = null, isPublished = false, onSave, isPai
|
||||
if (onSave) {
|
||||
try {
|
||||
await onSave(payload);
|
||||
showToast('success', 'Success', draft ? 'Resource updated successfully.' : 'Resource created successfully.');
|
||||
showToast('success', 'Success', draft ? 'Document updated successfully.' : 'Document created successfully.');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
showToast('error', 'Error', 'Failed to save resource. Please try again.');
|
||||
showToast('error', 'Error', 'Failed to save document. Please try again.');
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -161,7 +161,7 @@ const EmbeddedResourceForm = ({ draft = null, isPublished = false, onSave, isPai
|
||||
</div>
|
||||
|
||||
<div className="p-inputgroup flex-1 mt-8 flex-col">
|
||||
<p className="py-2">Paid Resource</p>
|
||||
<p className="py-2">Paid Document</p>
|
||||
<InputSwitch checked={isPaidResource} onChange={(e) => setIsPaidResource(e.value)} />
|
||||
{isPaidResource && (
|
||||
<div className="p-inputgroup flex-1 py-4">
|
||||
@ -223,4 +223,4 @@ const EmbeddedResourceForm = ({ draft = null, isPublished = false, onSave, isPai
|
||||
);
|
||||
}
|
||||
|
||||
export default EmbeddedResourceForm;
|
||||
export default EmbeddedDocumentForm;
|
@ -3,7 +3,7 @@ import { useRouter } from "next/router";
|
||||
import GenericButton from "@/components/buttons/GenericButton";
|
||||
import MenuTab from "@/components/menutab/MenuTab";
|
||||
import { useCourses } from "@/hooks/nostr/useCourses";
|
||||
import { useResources } from "@/hooks/nostr/useResources";
|
||||
import { useDocuments } from "@/hooks/nostr/useDocuments";
|
||||
import { useVideos } from "@/hooks/nostr/useVideos";
|
||||
import { useDraftsQuery } from "@/hooks/apiQueries/useDraftsQuery";
|
||||
import { useCourseDraftsQuery } from "@/hooks/apiQueries/useCourseDraftsQuery";
|
||||
@ -31,7 +31,7 @@ const UserContent = () => {
|
||||
const { showToast } = useToast();
|
||||
const {ndk, addSigner} = useNDKContext();
|
||||
const { courses, coursesLoading, coursesError } = useCourses();
|
||||
const { resources, resourcesLoading, resourcesError } = useResources();
|
||||
const { documents, documentsLoading, documentsError } = useDocuments();
|
||||
const { videos, videosLoading, videosError } = useVideos();
|
||||
const { courseDrafts, courseDraftsLoading, courseDraftsError } = useCourseDraftsQuery();
|
||||
const { drafts, draftsLoading, draftsError } = useDraftsQuery();
|
||||
@ -51,7 +51,7 @@ const UserContent = () => {
|
||||
{ label: "Published", icon: "pi pi-verified" },
|
||||
{ label: "Drafts", icon: "pi pi-file-edit" },
|
||||
{ label: "Draft Courses", icon: "pi pi-book" },
|
||||
{ label: "Resources", icon: "pi pi-file" },
|
||||
{ label: "Documents", icon: "pi pi-file" },
|
||||
{ label: "Videos", icon: "pi pi-video" },
|
||||
{ label: "Courses", icon: "pi pi-desktop" },
|
||||
];
|
||||
@ -98,8 +98,8 @@ const UserContent = () => {
|
||||
case 2:
|
||||
return courseDrafts || [];
|
||||
case 3:
|
||||
return resources?.map(parseEvent) || [];
|
||||
case 3:
|
||||
return documents?.map(parseEvent) || [];
|
||||
case 4:
|
||||
return videos?.map(parseEvent) || [];
|
||||
case 4:
|
||||
return courses?.map(parseEvent) || [];
|
||||
@ -110,10 +110,10 @@ const UserContent = () => {
|
||||
|
||||
setContent(getContentByIndex(activeIndex));
|
||||
}
|
||||
}, [activeIndex, isClient, drafts, resources, videos, courses, publishedContent, courseDrafts])
|
||||
}, [activeIndex, isClient, drafts, documents, videos, courses, publishedContent, courseDrafts])
|
||||
|
||||
const isLoading = coursesLoading || resourcesLoading || videosLoading || draftsLoading || contentIdsLoading || courseDraftsLoading;
|
||||
const isError = coursesError || resourcesError || videosError || draftsError || contentIdsError || courseDraftsError;
|
||||
const isLoading = coursesLoading || documentsLoading || videosLoading || draftsLoading || contentIdsLoading || courseDraftsLoading;
|
||||
const isError = coursesError || documentsError || videosError || draftsError || contentIdsError || courseDraftsError;
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
|
@ -40,38 +40,42 @@ const Sidebar = ({ course = false }) => {
|
||||
if (router.isReady) {
|
||||
const { slug } = router.query;
|
||||
|
||||
if (slug && course) {
|
||||
const { data } = nip19.decode(slug)
|
||||
try {
|
||||
if (slug && course) {
|
||||
const { data } = nip19.decode(slug)
|
||||
|
||||
if (!data) {
|
||||
showToast('error', 'Error', 'Course not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const id = data?.identifier;
|
||||
const fetchCourse = async (id) => {
|
||||
try {
|
||||
await ndk.connect();
|
||||
|
||||
const filter = {
|
||||
ids: [id]
|
||||
}
|
||||
|
||||
const event = await ndk.fetchEvent(filter);
|
||||
|
||||
if (event) {
|
||||
// all a tags are lessons
|
||||
const lessons = event.tags.filter(tag => tag[0] === 'a');
|
||||
const uniqueLessons = [...new Set(lessons.map(lesson => lesson[1]))];
|
||||
setLessons(uniqueLessons);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching event:', error);
|
||||
if (!data) {
|
||||
showToast('error', 'Error', 'Course not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const id = data?.identifier;
|
||||
const fetchCourse = async (id) => {
|
||||
try {
|
||||
await ndk.connect();
|
||||
|
||||
const filter = {
|
||||
ids: [id]
|
||||
}
|
||||
|
||||
const event = await ndk.fetchEvent(filter);
|
||||
|
||||
if (event) {
|
||||
// all a tags are lessons
|
||||
const lessons = event.tags.filter(tag => tag[0] === 'a');
|
||||
const uniqueLessons = [...new Set(lessons.map(lesson => lesson[1]))];
|
||||
setLessons(uniqueLessons);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching event:', error);
|
||||
}
|
||||
};
|
||||
if (ndk && id) {
|
||||
fetchCourse(id);
|
||||
}
|
||||
};
|
||||
if (ndk && id) {
|
||||
fetchCourse(id);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
}, [router.isReady, router.query, ndk, course]);
|
||||
@ -136,8 +140,8 @@ const Sidebar = ({ course = false }) => {
|
||||
<div onClick={() => router.push('/content?tag=videos')} className={`w-full cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/content?tag=videos') ? 'bg-gray-700' : ''}`}>
|
||||
<p className="pl-3 rounded-md font-bold text-lg"><i className="pi pi-video text-sm pr-1"></i> Videos</p>
|
||||
</div>
|
||||
<div onClick={() => router.push('/content?tag=resources')} className={`w-full cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/content?tag=resources') ? 'bg-gray-700' : ''}`}>
|
||||
<p className="pl-3 rounded-md font-bold text-lg"><i className="pi pi-file text-sm pr-1"></i> Resources</p>
|
||||
<div onClick={() => router.push('/content?tag=documents')} className={`w-full cursor-pointer py-2 my-2 hover:bg-gray-700 rounded-lg ${isActive('/content?tag=documents') ? 'bg-gray-700' : ''}`}>
|
||||
<p className="pl-3 rounded-md font-bold text-lg"><i className="pi pi-file text-sm pr-1"></i> Documents</p>
|
||||
</div>
|
||||
</AccordionTab>
|
||||
</Accordion>
|
||||
|
@ -4,12 +4,12 @@ import { useContentIdsQuery } from '@/hooks/apiQueries/useContentIdsQuery';
|
||||
|
||||
const AUTHOR_PUBKEY = process.env.NEXT_PUBLIC_AUTHOR_PUBKEY;
|
||||
|
||||
export function useResources() {
|
||||
export function useDocuments() {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
const [resources, setResources] = useState();
|
||||
const [documents, setDocuments] = useState();
|
||||
// Add new state variables for loading and error
|
||||
const [resourcesLoading, setResourcesLoading] = useState(false);
|
||||
const [resourcesError, setResourcesError] = useState(null);
|
||||
const [documentsLoading, setDocumentsLoading] = useState(false);
|
||||
const [documentsError, setDocumentsError] = useState(null);
|
||||
|
||||
const { contentIds } = useContentIdsQuery()
|
||||
const {ndk, addSigner} = useNDKContext();
|
||||
@ -19,18 +19,18 @@ export function useResources() {
|
||||
}, []);
|
||||
|
||||
const hasRequiredProperties = (event, contentIds) => {
|
||||
const hasResource = event.tags.some(([tag, value]) => tag === "t" && value === "resource");
|
||||
const hasDocument = event.tags.some(([tag, value]) => tag === "t" && value === "document");
|
||||
const hasId = event.tags.some(([tag, value]) => tag === "d" && contentIds.includes(value));
|
||||
return hasResource && hasId;
|
||||
return hasDocument && hasId;
|
||||
};
|
||||
|
||||
const fetchResourcesFromNDK = async () => {
|
||||
setResourcesLoading(true);
|
||||
setResourcesError(null);
|
||||
const fetchDocumentsFromNDK = async () => {
|
||||
setDocumentsLoading(true);
|
||||
setDocumentsError(null);
|
||||
try {
|
||||
if (!contentIds || contentIds.length === 0) {
|
||||
console.log('No content IDs found');
|
||||
setResourcesLoading(false);
|
||||
setDocumentsLoading(false);
|
||||
return []; // Return early if no content IDs are found
|
||||
}
|
||||
|
||||
@ -41,29 +41,29 @@ export function useResources() {
|
||||
|
||||
if (events && events.size > 0) {
|
||||
const eventsArray = Array.from(events);
|
||||
const resources = eventsArray.filter(event => hasRequiredProperties(event, contentIds));
|
||||
setResourcesLoading(false);
|
||||
return resources;
|
||||
const documents = eventsArray.filter(event => hasRequiredProperties(event, contentIds));
|
||||
setDocumentsLoading(false);
|
||||
return documents;
|
||||
}
|
||||
setResourcesLoading(false);
|
||||
setDocumentsLoading(false);
|
||||
return [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching resources from NDK:', error);
|
||||
setResourcesError(error);
|
||||
setResourcesLoading(false);
|
||||
console.error('Error fetching documents from NDK:', error);
|
||||
setDocumentsError(error);
|
||||
setDocumentsLoading(false);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isClient && contentIds) {
|
||||
fetchResourcesFromNDK().then(fetchedResources => {
|
||||
if (fetchedResources && fetchedResources.length > 0) {
|
||||
setResources(fetchedResources);
|
||||
fetchDocumentsFromNDK().then(fetchedDocuments => {
|
||||
if (fetchedDocuments && fetchedDocuments.length > 0) {
|
||||
setDocuments(fetchedDocuments);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [isClient, contentIds]);
|
||||
|
||||
return { resources, resourcesLoading, resourcesError };
|
||||
return { documents, documentsLoading, documentsError };
|
||||
}
|
@ -5,7 +5,7 @@ import axios from 'axios';
|
||||
|
||||
const AUTHOR_PUBKEY = process.env.NEXT_PUBLIC_AUTHOR_PUBKEY;
|
||||
|
||||
export function useResourcesQuery() {
|
||||
export function useDocumentsQuery() {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
const {ndk, addSigner} = useNDKContext();
|
||||
|
||||
@ -14,12 +14,12 @@ export function useResourcesQuery() {
|
||||
}, []);
|
||||
|
||||
const hasRequiredProperties = (event, contentIds) => {
|
||||
const hasResource = event.tags.some(([tag, value]) => tag === "t" && value === "resource");
|
||||
const hasDocument = event.tags.some(([tag, value]) => tag === "t" && value === "document");
|
||||
const hasId = event.tags.some(([tag, value]) => tag === "d" && contentIds.includes(value));
|
||||
return hasResource && hasId;
|
||||
return hasDocument && hasId;
|
||||
};
|
||||
|
||||
const fetchResourcesFromNDK = async () => {
|
||||
const fetchDocumentsFromNDK = async () => {
|
||||
try {
|
||||
const response = await axios.get(`/api/content/all`);
|
||||
const contentIds = response.data;
|
||||
@ -36,23 +36,23 @@ export function useResourcesQuery() {
|
||||
|
||||
if (events && events.size > 0) {
|
||||
const eventsArray = Array.from(events);
|
||||
const resources = eventsArray.filter(event => hasRequiredProperties(event, contentIds));
|
||||
return resources;
|
||||
const documents = eventsArray.filter(event => hasRequiredProperties(event, contentIds));
|
||||
return documents;
|
||||
}
|
||||
return [];
|
||||
} catch (error) {
|
||||
console.error('Error fetching resources from NDK:', error);
|
||||
console.error('Error fetching documents from NDK:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const { data: resources, isLoading: resourcesLoading, error: resourcesError, refetch: refetchResources } = useQuery({
|
||||
queryKey: ['resources', isClient],
|
||||
queryFn: fetchResourcesFromNDK,
|
||||
const { data: documents, isLoading: documentsLoading, error: documentsError, refetch: refetchDocuments } = useQuery({
|
||||
queryKey: ['documents', isClient],
|
||||
queryFn: fetchDocumentsFromNDK,
|
||||
// staleTime: 1000 * 60 * 30, // 30 minutes
|
||||
// refetchInterval: 1000 * 60 * 30, // 30 minutes
|
||||
enabled: isClient,
|
||||
});
|
||||
|
||||
return { resources, resourcesLoading, resourcesError, refetchResources };
|
||||
return { documents, documentsLoading, documentsError, refetchDocuments };
|
||||
}
|
@ -29,7 +29,7 @@ export default function MyApp({
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
setIsCourseView(router.pathname.includes('course'));
|
||||
setIsCourseView(router.pathname.includes('course') && !router.pathname.includes('draft'));
|
||||
}, [router.pathname]);
|
||||
|
||||
// const [sidebarExpanded, setSidebarExpanded] = useState(true);
|
||||
|
@ -60,9 +60,9 @@ const AboutPage = () => {
|
||||
title="Content Types"
|
||||
description={
|
||||
<ul className="list-disc list-inside ml-6 space-y-2">
|
||||
<li><span className="font-bold">Resources:</span> Markdown documents posted as NIP-23 long-form events on Nostr.</li>
|
||||
<li><span className="font-bold">Documents:</span> Markdown documents posted as NIP-23 long-form events on Nostr.</li>
|
||||
<li><span className="font-bold">Videos:</span> Enhanced markdown files with rich media support, including embedded videos, also saved as NIP-23 events.</li>
|
||||
<li><span className="font-bold">Courses:</span> Nostr lists that combine multiple resources and videos into a structured learning path.</li>
|
||||
<li><span className="font-bold">Courses:</span> Nostr lists that combine multiple documents and videos into a structured learning path.</li>
|
||||
</ul>
|
||||
}
|
||||
/>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useState, useMemo } from 'react';
|
||||
import GenericCarousel from '@/components/content/carousels/GenericCarousel';
|
||||
import { parseEvent, parseCourseEvent } from '@/utils/nostr';
|
||||
import { useResources } from '@/hooks/nostr/useResources';
|
||||
import { useDocuments } from '@/hooks/nostr/useDocuments';
|
||||
import { useVideos } from '@/hooks/nostr/useVideos';
|
||||
import { useCourses } from '@/hooks/nostr/useCourses';
|
||||
import { TabMenu } from 'primereact/tabmenu';
|
||||
@ -17,7 +17,7 @@ const MenuTab = ({ items, selectedTopic, onTabChange }) => {
|
||||
const menuItems = allItems.map((item, index) => {
|
||||
let icon = 'pi pi-tag';
|
||||
if (item === 'All') icon = 'pi pi-eye';
|
||||
else if (item === 'Resources') icon = 'pi pi-file';
|
||||
else if (item === 'Documents') icon = 'pi pi-file';
|
||||
else if (item === 'Videos') icon = 'pi pi-video';
|
||||
else if (item === 'Courses') icon = 'pi pi-desktop';
|
||||
|
||||
@ -67,11 +67,11 @@ const MenuTab = ({ items, selectedTopic, onTabChange }) => {
|
||||
|
||||
const ContentPage = () => {
|
||||
const router = useRouter();
|
||||
const { resources, resourcesLoading } = useResources();
|
||||
const { documents, documentsLoading } = useDocuments();
|
||||
const { videos, videosLoading } = useVideos();
|
||||
const { courses, coursesLoading } = useCourses();
|
||||
|
||||
const [processedResources, setProcessedResources] = useState([]);
|
||||
const [processedDocuments, setProcessedDocuments] = useState([]);
|
||||
const [processedVideos, setProcessedVideos] = useState([]);
|
||||
const [processedCourses, setProcessedCourses] = useState([]);
|
||||
const [allContent, setAllContent] = useState([]);
|
||||
@ -92,11 +92,11 @@ const ContentPage = () => {
|
||||
}, [router.query.tag]);
|
||||
|
||||
useEffect(() => {
|
||||
if (resources && !resourcesLoading) {
|
||||
const processedResources = resources.map(resource => ({...parseEvent(resource), type: 'resource'}));
|
||||
setProcessedResources(processedResources);
|
||||
if (documents && !documentsLoading) {
|
||||
const processedDocuments = documents.map(document => ({...parseEvent(document), type: 'document'}));
|
||||
setProcessedDocuments(processedDocuments);
|
||||
}
|
||||
}, [resources, resourcesLoading]);
|
||||
}, [documents, documentsLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
if (videos && !videosLoading) {
|
||||
@ -113,11 +113,11 @@ const ContentPage = () => {
|
||||
}, [courses, coursesLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
const allContent = [...processedResources, ...processedVideos, ...processedCourses];
|
||||
const allContent = [...processedDocuments, ...processedVideos, ...processedCourses];
|
||||
setAllContent(allContent);
|
||||
|
||||
const uniqueTopics = new Set(allContent.map(item => item.topics).flat());
|
||||
const priorityItems = ['All', 'Courses', 'Videos', 'Resources'];
|
||||
const priorityItems = ['All', 'Courses', 'Videos', 'Documents'];
|
||||
const otherTopics = Array.from(uniqueTopics).filter(topic => !priorityItems.includes(topic));
|
||||
const combinedTopics = [...priorityItems.slice(1), ...otherTopics];
|
||||
setAllTopics(combinedTopics);
|
||||
@ -125,13 +125,13 @@ const ContentPage = () => {
|
||||
if (selectedTopic) {
|
||||
filterContent(selectedTopic, allContent);
|
||||
}
|
||||
}, [processedResources, processedVideos, processedCourses]);
|
||||
}, [processedDocuments, processedVideos, processedCourses]);
|
||||
|
||||
const filterContent = (topic, content) => {
|
||||
let filtered = content;
|
||||
if (topic !== 'All') {
|
||||
const topicLower = topic.toLowerCase();
|
||||
if (['courses', 'videos', 'resources'].includes(topicLower)) {
|
||||
if (['courses', 'videos', 'documents'].includes(topicLower)) {
|
||||
filtered = content.filter(item => item.type === topicLower.slice(0, -1));
|
||||
} else {
|
||||
filtered = content.filter(item => item.topics && item.topics.includes(topic.toLowerCase()));
|
||||
@ -166,7 +166,7 @@ const ContentPage = () => {
|
||||
<h1 className="text-3xl font-bold mb-4 ml-1">All Content</h1>
|
||||
</div>
|
||||
<MenuTab
|
||||
items={['Courses', 'Videos', 'Resources', ...allTopics.filter(topic => !['Courses', 'Videos', 'Resources'].includes(topic))]}
|
||||
items={['Courses', 'Videos', 'Documents', ...allTopics.filter(topic => !['Courses', 'Videos', 'Documents'].includes(topic))]}
|
||||
selectedTopic={selectedTopic}
|
||||
onTabChange={handleTopicChange}
|
||||
className="max-w-[90%] mx-auto"
|
||||
|
@ -44,7 +44,6 @@ const DraftCourse = () => {
|
||||
axios.get(`/api/courses/drafts/${slug}`)
|
||||
.then(res => {
|
||||
setCourse(res.data);
|
||||
console.log('coursesssss:', res.data);
|
||||
setLessons(res.data.draftLessons);
|
||||
})
|
||||
.catch(err => {
|
||||
|
@ -53,16 +53,19 @@ const useCourseData = (ndk, fetchAuthor, router) => {
|
||||
return { course, lessonIds };
|
||||
};
|
||||
|
||||
const useLessons = (ndk, fetchAuthor, lessonIds) => {
|
||||
const useLessons = (ndk, fetchAuthor, lessonIds, pubkey) => {
|
||||
const [lessons, setLessons] = useState([]);
|
||||
const [uniqueLessons, setUniqueLessons] = useState([]);
|
||||
|
||||
console.log('lessonIds', lessonIds);
|
||||
|
||||
useEffect(() => {
|
||||
if (lessonIds.length > 0) {
|
||||
const fetchLesson = async (lessonId) => {
|
||||
console.log('lessonId', lessonId);
|
||||
try {
|
||||
await ndk.connect();
|
||||
const filter = { "#d": [lessonId] };
|
||||
const filter = { "#d": [lessonId], kinds:[30023, 30402], authors: [pubkey] };
|
||||
const event = await ndk.fetchEvent(filter);
|
||||
if (event) {
|
||||
const author = await fetchAuthor(event.pubkey);
|
||||
@ -83,6 +86,10 @@ const useLessons = (ndk, fetchAuthor, lessonIds) => {
|
||||
setUniqueLessons(newUniqueLessons);
|
||||
}, [lessons]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('uniqueLessons', uniqueLessons);
|
||||
}, [uniqueLessons]);
|
||||
|
||||
return { lessons, uniqueLessons, setLessons };
|
||||
};
|
||||
|
||||
@ -139,7 +146,7 @@ const Course = () => {
|
||||
}, [ndk]);
|
||||
|
||||
const { course, lessonIds } = useCourseData(ndk, fetchAuthor, router);
|
||||
const { lessons, uniqueLessons, setLessons } = useLessons(ndk, fetchAuthor, lessonIds);
|
||||
const { lessons, uniqueLessons, setLessons } = useLessons(ndk, fetchAuthor, lessonIds, course?.pubkey);
|
||||
const { decryptionPerformed, loading } = useDecryption(session, paidCourse, course, lessons, setLessons);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import MenuTab from "@/components/menutab/MenuTab";
|
||||
import ResourceForm from "@/components/forms/ResourceForm";
|
||||
import DocumentForm from "@/components/forms/DocumentForm";
|
||||
import VideoForm from "@/components/forms/VideoForm";
|
||||
import CourseForm from "@/components/forms/course/CourseForm";
|
||||
import { useIsAdmin } from "@/hooks/useIsAdmin";
|
||||
@ -12,7 +12,7 @@ const Create = () => {
|
||||
const { isAdmin, isLoading } = useIsAdmin();
|
||||
const router = useRouter();
|
||||
const homeItems = [
|
||||
{ label: 'Resource', icon: 'pi pi-book' },
|
||||
{ label: 'Document', icon: 'pi pi-file' },
|
||||
{ label: 'Video', icon: 'pi pi-video' },
|
||||
{ label: 'Course', icon: 'pi pi-desktop' }
|
||||
];
|
||||
@ -32,8 +32,8 @@ const Create = () => {
|
||||
return <CourseForm />;
|
||||
case 'Video':
|
||||
return <VideoForm />;
|
||||
case 'Resource':
|
||||
return <ResourceForm />;
|
||||
case 'Document':
|
||||
return <DocumentForm />;
|
||||
default:
|
||||
return null; // or a default component
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { parseEvent } from "@/utils/nostr";
|
||||
import ResourceForm from "@/components/forms/ResourceForm";
|
||||
import DocumentForm from "@/components/forms/DocumentForm";
|
||||
import VideoForm from "@/components/forms/VideoForm";
|
||||
import CourseForm from "@/components/forms/course/CourseForm";
|
||||
import { useNDKContext } from "@/context/NDKContext";
|
||||
@ -41,7 +41,7 @@ export default function Edit() {
|
||||
<h2 className="text-center mb-8">Edit Published Event</h2>
|
||||
{event?.topics.includes('course') && <CourseForm draft={event} isPublished />}
|
||||
{!event?.topics.includes('video') && <VideoForm draft={event} isPublished />}
|
||||
{event?.topics.includes('resource') && <ResourceForm draft={event} isPublished />}
|
||||
{event?.topics.includes('document') && <DocumentForm draft={event} isPublished />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import axios from "axios";
|
||||
import ResourceForm from "@/components/forms/ResourceForm";
|
||||
import DocumentForm from "@/components/forms/DocumentForm";
|
||||
import VideoForm from "@/components/forms/VideoForm";
|
||||
import CourseForm from "@/components/forms/course/CourseForm";
|
||||
import { useIsAdmin } from "@/hooks/useIsAdmin";
|
||||
@ -38,7 +38,7 @@ const Edit = () => {
|
||||
<h2 className="text-center mb-8">Edit Draft</h2>
|
||||
{draft?.type === 'course' && <CourseForm draft={draft} />}
|
||||
{draft?.type === 'video' && <VideoForm draft={draft} />}
|
||||
{draft?.type === 'resource' && <ResourceForm draft={draft} />}
|
||||
{draft?.type === 'document' && <DocumentForm draft={draft} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -32,6 +32,7 @@ export default function Draft() {
|
||||
const { returnImageProxy } = useImageProxy();
|
||||
const { data: session, status } = useSession();
|
||||
const [user, setUser] = useState(null);
|
||||
const [nAddress, setNAddress] = useState(null);
|
||||
const [videoId, setVideoId] = useState(null);
|
||||
const { width, height } = useResponsiveImageDimensions();
|
||||
const router = useRouter();
|
||||
@ -187,7 +188,7 @@ export default function Draft() {
|
||||
console.log('NewDTag:', NewDTag);
|
||||
|
||||
switch (draft?.type) {
|
||||
case 'resource':
|
||||
case 'document':
|
||||
if (draft?.price) {
|
||||
// encrypt the content with NEXT_PUBLIC_APP_PRIV_KEY to NEXT_PUBLIC_APP_PUBLIC_KEY
|
||||
encryptedContent = await nip04.encrypt(process.env.NEXT_PUBLIC_APP_PRIV_KEY, process.env.NEXT_PUBLIC_APP_PUBLIC_KEY, draft.content);
|
||||
@ -208,7 +209,7 @@ export default function Draft() {
|
||||
...(draft?.additionalLinks ? draft.additionalLinks.map(link => ['r', link]) : []),
|
||||
];
|
||||
|
||||
type = 'resource';
|
||||
type = 'document';
|
||||
break;
|
||||
case 'video':
|
||||
if (draft?.price) {
|
||||
@ -315,7 +316,7 @@ export default function Draft() {
|
||||
</div>
|
||||
<div className='flex flex-col max-tab:mt-12 max-mob:mt-12'>
|
||||
{draft && (
|
||||
<div style={{ width: width < 768 ? "auto" : width }} onClick={() => router.push(`/details/${draft.id}`)} className="flex flex-col items-center mx-auto cursor-pointer rounded-md shadow-lg">
|
||||
<div style={{ width: width < 768 ? "auto" : width }} onClick={() => router.push(`/details/${nAddress}`)} className="flex flex-col items-center mx-auto cursor-pointer rounded-md shadow-lg">
|
||||
<div style={{ maxWidth: width, minWidth: width }} className="max-tab:h-auto max-mob:h-auto">
|
||||
<Image
|
||||
alt="resource thumbnail"
|
||||
|
@ -2,7 +2,7 @@ import Head from 'next/head';
|
||||
import React from 'react';
|
||||
import CoursesCarousel from '@/components/content/carousels/CoursesCarousel';
|
||||
import VideosCarousel from '@/components/content/carousels/VideosCarousel';
|
||||
import ResourcesCarousel from '@/components/content/carousels/ResourcesCarousel';
|
||||
import DocumentsCarousel from '@/components/content/carousels/DocumentsCarousel';
|
||||
import InteractivePromotionalCarousel from '@/components/content/carousels/InteractivePromotionalCarousel';
|
||||
|
||||
export default function Home() {
|
||||
@ -18,7 +18,7 @@ export default function Home() {
|
||||
<InteractivePromotionalCarousel />
|
||||
<CoursesCarousel />
|
||||
<VideosCarousel />
|
||||
<ResourcesCarousel />
|
||||
<DocumentsCarousel />
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
|
@ -63,7 +63,7 @@ export const parseEvent = (event) => {
|
||||
image: '',
|
||||
published_at: '',
|
||||
topics: [], // Added to hold all topics
|
||||
type: 'resource', // Default type
|
||||
type: 'document', // Default type
|
||||
};
|
||||
|
||||
// Iterate over the tags array to extract data
|
||||
|
Loading…
x
Reference in New Issue
Block a user