list pages from pocketbase

This commit is contained in:
Guillaume Dorce 2023-05-24 22:39:09 +02:00
parent 699fbf06c5
commit 23d6930bd6
6 changed files with 163 additions and 33 deletions

View File

@ -1,5 +1,6 @@
import { memo, useEffect, useRef } from "react";
import EditorJS, { OutputData } from "@editorjs/editorjs";
import EditorJS from "@editorjs/editorjs";
import type { OutputData } from "@editorjs/editorjs";
import { EDITOR_JS_TOOLS } from "@/utils/tools";
type Props = {
@ -17,7 +18,7 @@ const EditorBlock = ({ data, onChange, holder }: Props) => {
holder: holder,
tools: EDITOR_JS_TOOLS,
data,
async onChange(api, event) {
async onChange(api) {
const data = await api.saver.save();
onChange(data);
},

View File

@ -1,27 +0,0 @@
import type { PagesRecord } from '@/@types/pocketbase-types';
import Layout from '@/layouts/Home'
import { getPageBySlug } from '@/utils/pb'
export default function Page({data}: {slug: string, data: PagesRecord}) {
return (
<Layout title={data.title}>
<h1>{data.title}</h1>
<pre>{JSON.stringify(data.content, null, 2)}</pre>
</Layout>
)
}
export async function getServerSideProps({params}: {params: {slug: string}}) {
const {data, error} = await getPageBySlug(params.slug);
if (error || !data) {
return {
notFound: true,
}
}
return {
props: {
data: data,
},
}
}

101
src/pages/test/[slug].tsx Normal file
View File

@ -0,0 +1,101 @@
import type { PagesRecord } from '@/@types/pocketbase-types';
import Layout from '@/layouts/Home'
import { getPageBySlug } from '@/utils/pb'
function parseBoldText(text: string) {
const parts = text.split(/(<b>.*?<\/b>)/g);
const result = [];
for (let i = 0; i < parts.length; i++) {
if (parts[i]?.startsWith('<b>')) {
result.push(<b key={i}>{parts[i]?.slice(3, -4)}</b>)
} else {
result.push(parts[i])
}
}
return result;
}
function Paragraph({text}: {text: string}) {
return <p className='text-xl'>{parseBoldText(text)}</p>
}
function Heading({text, level}: {text: string, level: number}) {
switch (level) {
case 1:
return (
<>
<h1 className='font-poppins text-5xl'>{text}</h1>
<div className="line h-1 w-full bg-black"></div>
</>
)
case 2:
return <h2 className='text-2xl'>{text}</h2>
case 3:
return <h3>{text}</h3>
default:
return <h4>{text}</h4>
}
}
type ParagraphBlock = {
type: 'paragraph',
data: {
text: string,
},
id: string,
}
type HeaderBlock = {
type: 'header',
data: {
text: string,
level: number,
},
id: string,
}
type Block = ParagraphBlock | HeaderBlock;
type Content = {
blocks: Block[],
version: string,
time: number,
}
export default function Page({data}: {slug: string, data: PagesRecord<Content>}) {
return (
<Layout title={data.title}>
<div className="container mx-auto p-8">
<p>Here is the content rendered:</p>
<div className="flex flex-col gap-6">
{data.content && data.content.blocks.map((block, index): (JSX.Element | null) => {
switch (block.type) {
case 'paragraph':
return <Paragraph key={index} text={block.data.text} />
case 'header':
return <Heading key={index} text={block.data.text} level={block.data.level} />
default:
return <p key={index}>Unknown block type: <pre>{JSON.stringify(block, null, 2)}</pre></p>
}
})}
</div>
</div>
<pre>{JSON.stringify(data.content, null, 2)}</pre>
</Layout>
)
}
export async function getServerSideProps({params}: {params: {slug: string}}) {
const {data, error} = await getPageBySlug(params.slug);
if (error || !data) {
return {
notFound: true,
}
}
return {
props: {
data: data,
},
}
}

View File

@ -2,6 +2,7 @@ import dynamic from 'next/dynamic'
import Layout from '@/layouts/Home'
import { useState } from 'react'
import { OutputData } from "@editorjs/editorjs";
import { api } from '@/utils/api';
const ReactEditorJS = dynamic(() => import('@/components/ReactEditor'), {
ssr: false
@ -9,8 +10,26 @@ const ReactEditorJS = dynamic(() => import('@/components/ReactEditor'), {
export default function Editor() {
const [data, setData] = useState<OutputData>()
const {mutate, isLoading, data: data2, isError} = api.pages.updatePage.useMutation();
if (data2) console.log(data2)
const handleSubmit = () => {
if (!data) return
mutate({
slug: 'test',
title: 'Test',
content: data
})
}
return (
<Layout title='React EditorJS'>
<Layout title='EditorJS'>
<button onClick={handleSubmit}>Save</button>
{isLoading && <p>Loading...</p>}
{data2 && !isError && <p>Saved!</p>}
{isError && <p>Error!</p>}
<h1>React EditorJS</h1>
<div className="container mx-auto p-8">
<h2>EditorJS with React</h2>

View File

@ -1,13 +1,13 @@
import { z } from "zod";
import { createTRPCRouter, publicProcedure } from "../trpc";
import { getPage, getPages } from "@/utils/pb";
import { getPageById, getPages, updatePage, getIdFromSlug } from "@/utils/pb";
export const pagesRouter = createTRPCRouter({
getPage: publicProcedure
.input(z.object({ id: z.string() }))
.query(({ input }) => {
return {
data: getPage(input.id),
data: getPageById(input.id),
};
}),
getPages: publicProcedure
@ -16,4 +16,28 @@ export const pagesRouter = createTRPCRouter({
data: getPages(),
};
}),
updatePage: publicProcedure
.input(z.object({ slug: z.string(), title: z.string().optional(), content: z.any().optional() }))
.mutation(async ({ input }) => {
const id = await getIdFromSlug(input.slug);
if (!id || id.error || !id.data) {
return {
error: {
code: "NOT_FOUND",
message: "Page not found",
},
};
}
let data = {};
if (input.title) {
data = { ...data, title: input.title };
}
if (input.content) {
data = { ...data, content: input.content };
}
const res = await updatePage(id.data, data);
return {
data: res,
};
}),
});

View File

@ -56,4 +56,16 @@ async function updatePage(id: string, data: PagesRecord) {
}
}
export { pb, getPages, getPageById, getPageBySlug, createPage, updatePage };
async function getIdFromSlug(slug: string) {
try {
const res = await pb
.collection("pages")
.getFirstListItem('slug="' + slug + '"');
return { data: res.id };
} catch (error) {
console.log(error);
return { error };
}
}
export { pb, getPages, getPageById, getPageBySlug, createPage, updatePage, getIdFromSlug };