mirror of
https://github.com/AustinKelsay/plebdevs.git
synced 2025-06-14 11:35:04 +00:00
first set of user progress flow fixes / refactor
This commit is contained in:
parent
5d1e9d32bb
commit
e8c5c161dd
@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useNDKContext } from '@/context/NDKContext';
|
||||
import { parseEvent } from '@/utils/nostr';
|
||||
import { parseEvent, parseCourseEvent } from '@/utils/nostr';
|
||||
import { ProgressSpinner } from 'primereact/progressspinner';
|
||||
import { nip19 } from 'nostr-tools';
|
||||
import appConfig from '@/config/appConfig';
|
||||
@ -8,50 +8,73 @@ import appConfig from '@/config/appConfig';
|
||||
const PurchasedListItem = ({ eventId, category }) => {
|
||||
const { ndk } = useNDKContext();
|
||||
const [event, setEvent] = useState(null);
|
||||
const [naddr, setNaddr] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchEvent = async () => {
|
||||
if (!eventId) return;
|
||||
if (!eventId || !ndk) return;
|
||||
|
||||
try {
|
||||
await ndk.connect();
|
||||
const event = await ndk.fetchEvent(eventId);
|
||||
if (event) {
|
||||
setEvent(parseEvent(event));
|
||||
const filter = category === 'courses'
|
||||
? {
|
||||
kinds: [30004],
|
||||
authors: appConfig.authorPubkeys,
|
||||
'#d': [eventId],
|
||||
}
|
||||
: {
|
||||
kinds: [30023, 30402],
|
||||
authors: appConfig.authorPubkeys,
|
||||
'#d': [eventId],
|
||||
};
|
||||
|
||||
const fetchedEvent = await ndk.fetchEvent(filter);
|
||||
if (fetchedEvent) {
|
||||
setEvent(category === 'courses' ? parseCourseEvent(fetchedEvent) : parseEvent(fetchedEvent));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching event:', error);
|
||||
console.error('Error fetching event in PurchasedListItem:', error);
|
||||
setEvent(null);
|
||||
}
|
||||
};
|
||||
fetchEvent();
|
||||
}, [eventId, ndk]);
|
||||
}, [eventId, ndk, category]);
|
||||
|
||||
useEffect(() => {
|
||||
if (event) {
|
||||
encodeNaddr();
|
||||
}
|
||||
}, [event]);
|
||||
|
||||
const encodeNaddr = () => {
|
||||
setNaddr(
|
||||
nip19.naddrEncode({
|
||||
const encodeNaddrForLink = () => {
|
||||
if (!event || !event.pubkey || !event.d) return null;
|
||||
try {
|
||||
return nip19.naddrEncode({
|
||||
pubkey: event.pubkey,
|
||||
identifier: event.d,
|
||||
kind: event.kind,
|
||||
kind: category === 'courses' ? 30004 : event.kind,
|
||||
relays: appConfig.defaultRelayUrls,
|
||||
})
|
||||
);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error encoding naddr:", error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return !event || !ndk ? (
|
||||
<ProgressSpinner className="w-[40px] h-[40px]" />
|
||||
) : (
|
||||
if (!ndk) {
|
||||
return <ProgressSpinner className="w-[40px] h-[40px]" title="Initializing..." />;
|
||||
}
|
||||
if (!eventId) {
|
||||
return <span className="text-gray-500">Missing item ID</span>
|
||||
}
|
||||
if (!event) {
|
||||
return <ProgressSpinner className="w-[40px] h-[40px]" />;
|
||||
}
|
||||
|
||||
const naddrValue = encodeNaddrForLink();
|
||||
|
||||
if (!naddrValue) {
|
||||
return <span className="text-red-500">Error generating link. (Invalid Event ID?)</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
className="text-blue-500 underline hover:text-blue-600"
|
||||
href={category === 'courses' ? `/course/${naddr}` : `/details/${naddr}`}
|
||||
href={category === 'courses' ? `/course/${naddrValue}` : `/details/${naddrValue}`}
|
||||
>
|
||||
{event.title}
|
||||
{event.name || event.title || 'Unnamed Item'}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { DataTable } from 'primereact/datatable';
|
||||
import { Column } from 'primereact/column';
|
||||
import GenericDataTable from '@/components/ui/DataTables/DataTable';
|
||||
import ProgressListItem from '@/components/content/lists/ProgressListItem';
|
||||
import { formatDateTime } from '@/utils/time';
|
||||
import { ProgressSpinner } from 'primereact/progressspinner';
|
||||
@ -92,7 +91,7 @@ const UserProgressTable = ({ session, ndk }) => {
|
||||
return progressData.sort((a, b) => new Date(b.date) - new Date(a.date));
|
||||
};
|
||||
|
||||
const header = (
|
||||
const tableHeader = (
|
||||
<div className="flex flex-wrap align-items-center justify-content-between gap-2">
|
||||
<span className="text-xl text-900 font-bold text-[#f8f8ff]">Progress</span>
|
||||
</div>
|
||||
@ -161,6 +160,13 @@ const UserProgressTable = ({ session, ndk }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ field: 'type', header: 'Type', body: typeTemplate },
|
||||
{ field: 'eventType', header: 'Event', body: eventTemplate },
|
||||
{ field: 'name', header: 'Name', body: nameTemplate },
|
||||
{ field: 'date', header: 'Date', body: dateTemplate },
|
||||
];
|
||||
|
||||
if (!session || !session?.user || !ndk) {
|
||||
return (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
@ -170,10 +176,11 @@ const UserProgressTable = ({ session, ndk }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
<GenericDataTable
|
||||
emptyMessage="No Courses or Milestones completed"
|
||||
value={prepareProgressData()}
|
||||
header={header}
|
||||
header={tableHeader}
|
||||
columns={columns}
|
||||
className="mt-2 mx-2 max-lap:mx-0"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -199,12 +206,10 @@ const UserProgressTable = ({ session, ndk }) => {
|
||||
},
|
||||
}}
|
||||
stripedRows
|
||||
dataKey="id"
|
||||
>
|
||||
<Column field="type" header="Type" body={typeTemplate}></Column>
|
||||
<Column field="eventType" header="Event" body={eventTemplate}></Column>
|
||||
<Column field="name" header="Name" body={nameTemplate}></Column>
|
||||
<Column field="date" body={dateTemplate} header="Date"></Column>
|
||||
</DataTable>
|
||||
{/* Original Column definitions removed */}
|
||||
</GenericDataTable>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import { DataTable } from 'primereact/datatable';
|
||||
import { Column } from 'primereact/column';
|
||||
import GenericDataTable from '@/components/ui/DataTables/DataTable';
|
||||
import PurchasedListItem from '@/components/content/lists/PurchasedListItem';
|
||||
import { formatDateTime } from '@/utils/time';
|
||||
|
||||
@ -47,13 +46,22 @@ const UserPurchaseTable = ({ session, windowWidth }) => {
|
||||
);
|
||||
};
|
||||
|
||||
// Define columns for GenericDataTable
|
||||
const columns = [
|
||||
{ field: 'amountPaid', header: 'Cost', body: costTemplate },
|
||||
{ field: 'name', header: 'Name', body: nameTemplate },
|
||||
{ field: 'category', header: 'Category', body: categoryTemplate },
|
||||
{ field: 'createdAt', header: 'Date', body: dateTemplate },
|
||||
];
|
||||
|
||||
return (
|
||||
session &&
|
||||
session?.user && (
|
||||
<DataTable
|
||||
<GenericDataTable
|
||||
emptyMessage="No purchases"
|
||||
value={session.user?.purchased}
|
||||
header={purchasesHeader}
|
||||
columns={columns}
|
||||
className="mt-2 mx-2 max-lap:mx-0"
|
||||
style={{
|
||||
width: '100%',
|
||||
@ -80,11 +88,8 @@ const UserPurchaseTable = ({ session, windowWidth }) => {
|
||||
}}
|
||||
stripedRows
|
||||
>
|
||||
<Column field="amountPaid" header="Cost" body={costTemplate}></Column>
|
||||
<Column field="name" header="Name" body={nameTemplate}></Column>
|
||||
<Column field="category" header="Category" body={categoryTemplate}></Column>
|
||||
<Column field="createdAt" header="Date" body={dateTemplate}></Column>
|
||||
</DataTable>
|
||||
{/* Original Column definitions removed */}
|
||||
</GenericDataTable>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { DataTable } from 'primereact/datatable';
|
||||
import { Column } from 'primereact/column';
|
||||
import GenericDataTable from '@/components/ui/DataTables/DataTable';
|
||||
import { InputText } from 'primereact/inputtext';
|
||||
import GenericButton from '@/components/buttons/GenericButton';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
@ -54,7 +53,7 @@ const UserRelaysTable = ({ ndk, userRelays, setUserRelays, reInitializeNDK }) =>
|
||||
}
|
||||
};
|
||||
|
||||
const header = (
|
||||
const tableHeader = (
|
||||
<div className="text-[#f8f8ff]">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
@ -62,9 +61,10 @@ const UserRelaysTable = ({ ndk, userRelays, setUserRelays, reInitializeNDK }) =>
|
||||
</div>
|
||||
<GenericButton
|
||||
outlined
|
||||
icon="pi pi-plus"
|
||||
label="Add Relay"
|
||||
icon={collapsed ? 'pi pi-plus' : 'pi pi-minus'}
|
||||
label={collapsed ? 'Add Relay' : 'Hide'}
|
||||
severity="success"
|
||||
className="w-fit"
|
||||
onClick={() => setCollapsed(!collapsed)}
|
||||
/>
|
||||
</div>
|
||||
@ -77,12 +77,14 @@ const UserRelaysTable = ({ ndk, userRelays, setUserRelays, reInitializeNDK }) =>
|
||||
onChange={e => setNewRelayUrl(e.target.value)}
|
||||
className="flex-1"
|
||||
/>
|
||||
<GenericButton label="+" severity="success" outlined onClick={addRelay} />
|
||||
<GenericButton className="w-fit" label="+" severity="success" outlined onClick={addRelay} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const relayUrlBody = rowData => rowData;
|
||||
|
||||
const relayStatusBody = url => {
|
||||
const isConnected = relayStatuses[url];
|
||||
return (
|
||||
@ -118,13 +120,23 @@ const UserRelaysTable = ({ ndk, userRelays, setUserRelays, reInitializeNDK }) =>
|
||||
);
|
||||
};
|
||||
|
||||
// Define columns for GenericDataTable
|
||||
const columns = [
|
||||
{ field: 'url', header: 'Relay URL', body: relayUrlBody },
|
||||
{ header: 'Status', body: relayStatusBody },
|
||||
{ header: 'Actions', body: relayActionsBody },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<DataTable value={userRelays} className="border-none" header={header}>
|
||||
<Column field={url => url} header="Relay URL"></Column>
|
||||
<Column body={relayStatusBody} header="Status"></Column>
|
||||
<Column body={relayActionsBody} header="Actions"></Column>
|
||||
</DataTable>
|
||||
<GenericDataTable
|
||||
value={userRelays}
|
||||
columns={columns}
|
||||
className="border-none"
|
||||
header={tableHeader}
|
||||
dataKey={rowData => rowData}
|
||||
>
|
||||
</GenericDataTable>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
114
src/components/ui/DataTables/DataTable.js
Normal file
114
src/components/ui/DataTables/DataTable.js
Normal file
@ -0,0 +1,114 @@
|
||||
import React from 'react';
|
||||
import { DataTable } from 'primereact/datatable';
|
||||
import { Column } from 'primereact/column';
|
||||
|
||||
const GenericDataTable = ({
|
||||
value,
|
||||
columns,
|
||||
header,
|
||||
emptyMessage = "No records found.",
|
||||
className,
|
||||
style,
|
||||
pt,
|
||||
stripedRows = false,
|
||||
dataKey,
|
||||
// Pagination props
|
||||
paginator = false,
|
||||
rows,
|
||||
rowsPerPageOptions,
|
||||
paginatorTemplate = "FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown",
|
||||
currentPageReportTemplate = "Showing {first} to {last} of {totalRecords} entries",
|
||||
// Sorting props
|
||||
sortMode,
|
||||
sortField,
|
||||
sortOrder,
|
||||
onSort,
|
||||
// Filtering props
|
||||
filters,
|
||||
onFilter,
|
||||
globalFilter,
|
||||
globalFilterFields,
|
||||
filterDisplay = "menu",
|
||||
// Selection props
|
||||
selection,
|
||||
onSelectionChange,
|
||||
selectionMode,
|
||||
// Row expansion
|
||||
rowExpansionTemplate,
|
||||
expandedRows,
|
||||
onRowToggle,
|
||||
// Context Menu
|
||||
onContextMenu,
|
||||
contextMenuSelection,
|
||||
onContextMenuSelectionChange,
|
||||
// Other common props
|
||||
loading = false,
|
||||
...rest // Allows passing any other DataTable props
|
||||
}) => {
|
||||
return (
|
||||
<DataTable
|
||||
value={value}
|
||||
header={header}
|
||||
emptyMessage={emptyMessage}
|
||||
className={className}
|
||||
style={style}
|
||||
pt={pt}
|
||||
stripedRows={stripedRows}
|
||||
dataKey={dataKey}
|
||||
// Pagination
|
||||
paginator={paginator}
|
||||
rows={rows}
|
||||
rowsPerPageOptions={rowsPerPageOptions}
|
||||
paginatorTemplate={paginatorTemplate}
|
||||
currentPageReportTemplate={currentPageReportTemplate}
|
||||
// Sorting
|
||||
sortMode={sortMode}
|
||||
sortField={sortField}
|
||||
sortOrder={sortOrder}
|
||||
onSort={onSort}
|
||||
// Filtering
|
||||
filters={filters}
|
||||
onFilter={onFilter}
|
||||
globalFilter={globalFilter}
|
||||
globalFilterFields={globalFilterFields}
|
||||
filterDisplay={filterDisplay}
|
||||
// Selection
|
||||
selection={selection}
|
||||
onSelectionChange={onSelectionChange}
|
||||
selectionMode={selectionMode}
|
||||
// Row Expansion
|
||||
rowExpansionTemplate={rowExpansionTemplate}
|
||||
expandedRows={expandedRows}
|
||||
onRowToggle={onRowToggle}
|
||||
// Context Menu
|
||||
onContextMenu={onContextMenu}
|
||||
contextMenuSelection={contextMenuSelection}
|
||||
onContextMenuSelectionChange={onContextMenuSelectionChange}
|
||||
// Loading state
|
||||
loading={loading}
|
||||
{...rest}
|
||||
>
|
||||
{columns && columns.map((col, i) => (
|
||||
<Column
|
||||
key={col.field || i}
|
||||
field={col.field}
|
||||
header={col.header}
|
||||
body={col.body}
|
||||
sortable={col.sortable}
|
||||
filter={col.filter}
|
||||
filterPlaceholder={col.filterPlaceholder}
|
||||
filterElement={col.filterElement}
|
||||
selectionMode={col.selectionMode}
|
||||
headerStyle={col.headerStyle}
|
||||
bodyStyle={col.bodyStyle}
|
||||
style={col.style}
|
||||
expander={col.expander}
|
||||
// Add other column props as needed
|
||||
{...col.columnProps} // Allows passing any other Column props
|
||||
/>
|
||||
))}
|
||||
</DataTable>
|
||||
);
|
||||
};
|
||||
|
||||
export default GenericDataTable;
|
@ -11,7 +11,8 @@ const appConfig = {
|
||||
],
|
||||
authorPubkeys: [
|
||||
'f33c8a9617cb15f705fc70cd461cfd6eaf22f9e24c33eabad981648e5ec6f741',
|
||||
'c67cd3e1a83daa56cff16f635db2fdb9ed9619300298d4701a58e68e84098345'
|
||||
'c67cd3e1a83daa56cff16f635db2fdb9ed9619300298d4701a58e68e84098345',
|
||||
'6260f29fa75c91aaa292f082e5e87b438d2ab4fdf96af398567b01802ee2fcd4'
|
||||
],
|
||||
customLightningAddresses: [
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user