better markdown support, minor layout fixes
This commit is contained in:
15
src/app/_components/CodeHighlightSyle.tsx
Normal file
15
src/app/_components/CodeHighlightSyle.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
'use client'
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
export function CodeHighlightStyle() {
|
||||
const { theme } = useTheme()
|
||||
if (theme == 'dark') {
|
||||
return (
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.3.2/build/styles/atelier-lakeside-dark.min.css"/>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@10.3.2/build/styles/atelier-lakeside-light.min.min.css"/>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ export default function CvPage() {
|
||||
cat.cvEntry.length > 0 ? (
|
||||
<>
|
||||
{cat.cvEntry.map((entry) => (
|
||||
<CollapsibleCvEntryForm key={entry.id} entry={entry} />
|
||||
<CollapsibleCvEntryForm key={entry.id} entry={entry} categoryId={undefined} />
|
||||
))}
|
||||
</>
|
||||
) : (<></>)
|
||||
|
||||
@@ -16,12 +16,15 @@ import { Calendar } from "~/components/ui/calendar";
|
||||
import { cn } from "~/lib/utils";
|
||||
import { useEffect, useState } from "react"
|
||||
import { Checkbox } from "~/components/ui/checkbox"
|
||||
import MDEditor from '@uiw/react-md-editor'
|
||||
import { useTheme } from "next-themes"
|
||||
|
||||
export default function CreateCvEntryForm(params:{className:string|undefined, categoryId:string|undefined}) {
|
||||
const { theme } = useTheme()
|
||||
const categories = trpc.cv.category.list.useQuery()
|
||||
console.log(params.categoryId)
|
||||
const [categorySelectPlaceHolder,setCategorySelectPlaceHolder] = useState("Select category")
|
||||
const [categorySelectDefaultValue,setCategorySelectDefaultValue] = useState("")
|
||||
const [mdeTab,setMdeTab] = useState<"write" | "preview" | undefined>("write");
|
||||
useEffect(() => {
|
||||
console.log('category success effect')
|
||||
if (categories.data !== undefined) {
|
||||
@@ -38,6 +41,8 @@ export default function CreateCvEntryForm(params:{className:string|undefined, ca
|
||||
}
|
||||
}
|
||||
},[categories.isSuccess])
|
||||
const now = new Date();
|
||||
now.setTime(Date.now());
|
||||
const form = useForm<z.infer<typeof insertSchemaForm>>({
|
||||
resolver: zodResolver(insertSchemaForm),
|
||||
defaultValues: {
|
||||
@@ -45,6 +50,8 @@ export default function CreateCvEntryForm(params:{className:string|undefined, ca
|
||||
title: "",
|
||||
description: "",
|
||||
categoryId: params.categoryId ? params.categoryId : "",
|
||||
fromTime: now,
|
||||
toTime: now,
|
||||
hideDates: false,
|
||||
|
||||
}
|
||||
@@ -53,7 +60,7 @@ export default function CreateCvEntryForm(params:{className:string|undefined, ca
|
||||
onSuccess: (data) => { form.setValue("id", data[0] ? data[0].id : "") }
|
||||
})
|
||||
function onSubmit(values: z.infer<typeof insertSchemaForm>) {
|
||||
let { id, title, categoryId, description } = values
|
||||
let { id, title, categoryId, description, hideDates } = values
|
||||
let v: z.infer<typeof insertSchema> = {
|
||||
id: id,
|
||||
categoryId: categoryId,
|
||||
@@ -61,6 +68,7 @@ export default function CreateCvEntryForm(params:{className:string|undefined, ca
|
||||
title: title,
|
||||
fromTime: format(values.fromTime,'yyyy-MM-dd'),
|
||||
toTime: format(values.toTime,'yyyy-MM-dd'),
|
||||
hideDates: hideDates
|
||||
}
|
||||
mutation.mutate(v)
|
||||
form.setValue("id", crypto.randomUUID())
|
||||
@@ -128,7 +136,11 @@ export default function CreateCvEntryForm(params:{className:string|undefined, ca
|
||||
Description
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="description" onChange={field.onChange} value={field.value == null ? undefined : field.value} />
|
||||
<MDEditor
|
||||
value={field.value ? field.value : ""}
|
||||
onChange={field.onChange}
|
||||
data-color-mode={theme ? theme : "dark"}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
|
||||
@@ -11,14 +11,18 @@ import { Button } from "~/components/ui/button";
|
||||
import * as Card from '~/components/ui/card'
|
||||
import { format } from 'date-fns'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover";
|
||||
import { cn } from "~/lib/utils";
|
||||
import { cn, type Element } from "~/lib/utils";
|
||||
import { CalendarIcon, Delete } from "lucide-react";
|
||||
import { Calendar } from "~/components/ui/calendar";
|
||||
import { Textarea } from "~/components/ui/textarea";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { Checkbox } from "~/components/ui/checkbox";
|
||||
import MDEditor from '@uiw/react-md-editor'
|
||||
import { useTheme } from "next-themes"
|
||||
import type { EntryRouterOutputs } from "~/server/routers/cv/entry";
|
||||
|
||||
export default function UpdateCvEntryForm(params : { id: string, className: string | undefined }) {
|
||||
console.log(params)
|
||||
const { theme } = useTheme()
|
||||
const id = params.id
|
||||
const categories = trpc.cv.category.list.useQuery()
|
||||
const entry = trpc.cv.entry.get.useQuery({ id: id })
|
||||
@@ -35,25 +39,29 @@ export default function UpdateCvEntryForm(params : { id: string, className: stri
|
||||
}
|
||||
}
|
||||
})
|
||||
entry.promise.then((v) => {
|
||||
const updateFormValues = (v:EntryRouterOutputs['get']|Element<EntryRouterOutputs['update']>) => {
|
||||
form.setValue('update.title', v?.title)
|
||||
form.setValue('update.description', v?.description);
|
||||
form.setValue('update.fromTime', new Date(Date.parse(v ? v.fromTime : "")))
|
||||
form.setValue('update.toTime', new Date(Date.parse(v ? v.toTime : "")))
|
||||
form.setValue('update.categoryId', v?.categoryId)
|
||||
form.setValue('update.hideDates', v?.hideDates)
|
||||
}
|
||||
entry.promise.then((v) => {
|
||||
updateFormValues(v)
|
||||
})
|
||||
const updateMutation = trpc.cv.entry.update.useMutation({
|
||||
onSuccess: () => {
|
||||
if (pathname.includes('list')) {
|
||||
router.refresh()
|
||||
} else {
|
||||
router.back()
|
||||
onSuccess: (v) => {
|
||||
if (v[0] !== undefined) {
|
||||
updateFormValues(v[0])
|
||||
}
|
||||
}
|
||||
})
|
||||
const deleteMutation = trpc.cv.entry.delete.useMutation({
|
||||
onSuccess: () => {
|
||||
|
||||
if (!pathname.includes('list')) {
|
||||
router.back()
|
||||
}
|
||||
}
|
||||
})
|
||||
const deleteEntry = (id: string) => {
|
||||
@@ -137,7 +145,12 @@ export default function UpdateCvEntryForm(params : { id: string, className: stri
|
||||
Description
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea placeholder="description" onChange={field.onChange} value={field.value == null ? undefined : field.value} />
|
||||
<MDEditor
|
||||
value={field.value ? field.value : ""}
|
||||
onChange={field.onChange}
|
||||
data-color-mode={theme ? theme : "dark"}
|
||||
previewOptions={{skipHtml:false}}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
|
||||
@@ -22,9 +22,9 @@ export default function CvCategory(props:CvCategoryProps) {
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
{(query.data?.cvEntry.length ? query.data?.cvEntry.length : 0 ) > 0 ?
|
||||
<CardContent className={cn(props.layout == "row" ? "flex flex-row" : "flex flex-col","gap-[1rem]")}>
|
||||
<CardContent className={cn(props.layout == "row" ? "flex flex-row flex-wrap justify-center lg:justify-between" : "flex flex-col","gap-[1rem]","overflow-scroll")}>
|
||||
{query.data?.cvEntry.map((entry) => (
|
||||
<CvEntry key={entry.id} initialData={entry}/>
|
||||
<CvEntry className={props.layout == "row" ? "w-full lg:w-fit" : undefined} key={entry.id} initialData={entry}/>
|
||||
))}
|
||||
</CardContent>
|
||||
:
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { trpc } from "~/app/_trpc/Client"
|
||||
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "~/components/ui/card"
|
||||
import { Skeleton } from "~/components/ui/skeleton"
|
||||
import type { Element } from "~/lib/utils"
|
||||
import { cn, type Element } from "~/lib/utils"
|
||||
import type { CategoryRouterOutputs } from "~/server/routers/cv/category"
|
||||
import type { EntryRouterOutputs } from "~/server/routers/cv/entry"
|
||||
import Markdown from 'react-markdown'
|
||||
import rehypeRaw from 'rehype-raw'
|
||||
import { format } from 'date-fns'
|
||||
|
||||
import rehypeHighlight from 'rehype-highlight'
|
||||
import rehypeRaw from 'rehype-raw'
|
||||
export default function CvEntry(params: {
|
||||
initialData: EntryRouterOutputs['get'] | Element<Element<CategoryRouterOutputs['list']>['cvEntry']>
|
||||
className?: string
|
||||
}) {
|
||||
const query = trpc.cv.entry.get.useQuery({ id: params.initialData?.id ? params.initialData.id : "" })
|
||||
const { data } = query
|
||||
@@ -18,7 +19,7 @@ export default function CvEntry(params: {
|
||||
{
|
||||
data ?
|
||||
<>
|
||||
<Card className="w-fit">
|
||||
<Card className={params.className ? cn("w-fit",params.className) : "w-fit"}>
|
||||
{
|
||||
data.title ?
|
||||
<CardHeader>
|
||||
@@ -28,9 +29,9 @@ export default function CvEntry(params: {
|
||||
}
|
||||
{
|
||||
data.description ?
|
||||
<CardContent>
|
||||
<CardContent className="text-sm lg:text-base">
|
||||
<div>
|
||||
<Markdown rehypePlugins={[rehypeRaw]}>{data.description}</Markdown>
|
||||
<Markdown rehypePlugins={[rehypeHighlight,rehypeRaw]}>{data.description}</Markdown>
|
||||
</div>
|
||||
</CardContent> :
|
||||
<></>
|
||||
|
||||
@@ -61,8 +61,8 @@ export default function CvPage() {
|
||||
{categories.data.filter((cat) => cat.layoutPosition == 'sidebar').length > 0 ?
|
||||
<>
|
||||
<SidebarTriggerDisappearsOnMobile />
|
||||
<Sidebar className="z-[51] gsapan">
|
||||
<SidebarContent className="p-2">
|
||||
<Sidebar className="gsapan ">
|
||||
<SidebarContent className="p-2 lg:pt-[3.2rem]">
|
||||
{sidebarCategories.map((cat) => {
|
||||
return (
|
||||
<CvCategory layout="col" initialData={cat} key={cat.id} />
|
||||
@@ -74,17 +74,17 @@ export default function CvPage() {
|
||||
<></>
|
||||
}
|
||||
<div className="h-full w-full flex flex-wrap flex-row p-[1rem] pt-[2rem] ">
|
||||
<div id="mainwrap" className="flex w-full flex-col gap-[1rem]">
|
||||
<div id="header" className="flex w-full h-fit flex-row gap-[1rem]">
|
||||
<div id="mainwrap" className="flex w-full flex-col gap-[1rem] lg:px-[15vw]">
|
||||
<div id="header" className="flex w-full h-fit flex-row gap-[1rem] flex-wrap">
|
||||
{headerCategories.map((cat) => {
|
||||
return (
|
||||
<CvCategory layout="row" initialData={cat} key={cat.id} />
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div id="colwrapper" className="flex flex-col md:flex-row w-full h-3/4 gap-[1rem]">
|
||||
<div id="colwrapper" className="flex flex-col lg:flex-row w-full h-3/4 gap-[1rem]">
|
||||
{col1Categories.length > 0 ?
|
||||
<div id="col1" className={`flex flex-col w-full ${col2Categories.length > 0 ? "md:w-1/2" : ""} h-full gap-[1rem]`}>
|
||||
<div id="col1" className={`flex flex-col w-full ${col2Categories.length > 0 ? "lg:w-1/2" : ""} h-full gap-[1rem]`}>
|
||||
{col1Categories.map((cat) => {
|
||||
return (
|
||||
<CvCategory layout="col" initialData={cat} key={cat.id} />
|
||||
@@ -94,7 +94,7 @@ export default function CvPage() {
|
||||
<></>
|
||||
}
|
||||
{col2Categories.length > 0 ?
|
||||
<div id="col2" className={`flex flex-col w-full ${col1Categories.length > 0 ? "md:w-1/2" : ""} h-full gap-[1rem]`}>
|
||||
<div id="col2" className={`flex flex-col w-full ${col1Categories.length > 0 ? "lg:w-1/2" : ""} h-full gap-[1rem]`}>
|
||||
{col2Categories.map((cat) => {
|
||||
return (
|
||||
<CvCategory layout="col" initialData={cat} key={cat.id} />
|
||||
|
||||
@@ -10,6 +10,7 @@ import TrpcProvider from "./_trpc/TrpcProvider";
|
||||
// const ThemeProvider = dynamic(() => import("./_providers/ThemeProvider"),{ssr:true})
|
||||
import ThemeProvider from './_providers/ThemeProvider'
|
||||
import GsapProvider from "./_providers/GsapProvicer";
|
||||
import { CodeHighlightStyle } from "./_components/CodeHighlightSyle";
|
||||
config.autoAddCss = false;
|
||||
export const metadata: Metadata = {
|
||||
title: "Gregor Lohaus",
|
||||
@@ -34,7 +35,9 @@ export default async function RootLayout({
|
||||
<ThemeProvider>
|
||||
<GsapProvider>
|
||||
<html lang="en" className={`${geist.variable}`} suppressHydrationWarning>
|
||||
<head />
|
||||
<head>
|
||||
<CodeHighlightStyle/>
|
||||
</head>
|
||||
<body className="flex flex-col bg-background text-foreground">
|
||||
<TopNav />
|
||||
<main className="absolute lg:top-10 h-[100vh] w-[100vw]">
|
||||
|
||||
Reference in New Issue
Block a user