Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
"@mdx-js/mdx": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@mdx-js/rollup": "^3.1.0",
"@milkdown/crepe": "^7.5.9",
"@milkdown/kit": "^7.5.9",
"@milkdown/react": "^7.5.9",
"@milkdown/theme-nord": "^7.5.9",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.4",
"@radix-ui/react-avatar": "^1.0.3",
Expand Down
2 changes: 1 addition & 1 deletion dashboard/src/components/common/Checkbox/Check.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const Check = ({ name, label, controllerProps, alignWithLabel = false, ru
name={name}
rules={rules}
render={({ field: { name, onChange, ref, value } }) => (
<Checkbox name={name} ref={ref} checked={value} onCheckedChange={onChange} {...props} />
<Checkbox name={name} ref={ref} checked={value ? true : false} onCheckedChange={onChange} {...props} />
)}
{...controllerProps}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,7 @@ export const FormElement = ({ name, label, children, tooltip, ...props }: FormEl
</Control>
)
};

export const FormHelperText = ({ children }: { children: React.ReactNode }) => {
return <div className="text-sm text-gray-500">{children}</div>
}
37 changes: 37 additions & 0 deletions dashboard/src/components/common/MarkdownEditor/MarkdownEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Crepe } from '@milkdown/crepe';
import '@milkdown/crepe/theme/common/style.css'
import '@milkdown/crepe/theme/frame.css';
import { useEffect } from 'react';
import { Editor } from '@milkdown/kit/core';
import './markdown.css'

export interface MarkdownEditorProps {
value: string;
setCrepeInstance: React.Dispatch<React.SetStateAction<Promise<Editor> | null>>
}

const MarkdownEditor = ({ value, setCrepeInstance }: MarkdownEditorProps) => {

useEffect(() => {
const crepe = new Crepe({
root: '#editor',
defaultValue: value,
}).create()
setCrepeInstance(crepe);

return () => {
crepe.then((editor) => {
editor.destroy();
});
};
}, []);

return (
<div id="editor" className="markdown-editor">
{/* The editor will be initialized here */}
</div>

);
};

export default MarkdownEditor
7 changes: 7 additions & 0 deletions dashboard/src/components/common/MarkdownEditor/markdown.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.milkdown .ProseMirror {
padding:0 !important;
}
.milkdown .ProseMirror h1, .milkdown .ProseMirror h2, .milkdown .ProseMirror h3, .milkdown .ProseMirror h4, .milkdown .ProseMirror h5, .milkdown .ProseMirror h6 {
font-family: inherit;
font-weight: 600;
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,7 @@ const APIClientContent = ({ endpoint, open, parameters }: APIClientContentProps)
return obj
}
else {
return Object.keys(data)?.filter((key) => !(key.includes('key') || key.includes('value'))).reduce((acc, key) => {
acc[key] = data[key]
return acc
}, {} as Record<string, string>)
return data
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Check } from "@/components/common/Checkbox/Check";
import { FormElement, FormHelperText } from "@/components/common/Forms/FormControl/FormElement";
import { Input } from "@/components/ui/input";
import { CommitDocsPage } from "@/types/commit/CommitDocsPage";
import { ChevronDown, ChevronRight } from "lucide-react";
import { useState } from "react";
import { useFormContext } from "react-hook-form";

export const DocsPageForm = () => {
const { register } = useFormContext<CommitDocsPage>();
const [isExpanded, setIsExpanded] = useState(false);

return (
<div className="flex flex-col gap-4 p-6 border border-gray-200 rounded-lg shadow-sm bg-white">
{/* Title Field */}
<div className="flex flex-row gap-4 items-end">
<FormElement name="title" label="Title" aria-required className="flex-grow" autoFocus>
<Input
{...register("title", { required: "Title is required" })}
id="title"
type="text"
placeholder="e.g. Get Started"
autoFocus
/>
</FormElement>
<button
type="button"
onClick={() => setIsExpanded(!isExpanded)}
className="p-2 rounded-md hover:bg-gray-100 transition-all"
aria-expanded={isExpanded}
aria-controls="additional-fields"
>
{isExpanded ? <ChevronDown className="h-5 w-5" /> : <ChevronRight className="h-5 w-5" />}
</button>
</div>

{/* Hidden Fields */}
{isExpanded && (
<div id="additional-fields" className="mt-4 space-y-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<FormElement name="badge" label="Badge">
<Input
{...register("badge")}
id="badge"
type="text"
placeholder="e.g. GET"
/>
<FormHelperText>
Badge is a short text that appears on the left side of the title in the sidebar.
</FormHelperText>
</FormElement>
<FormElement name="badge_color" label="Badge Color">
<Input
{...register("badge_color")}
id="badge_color"
type="text"
placeholder="e.g. green"
/>
<FormHelperText>
Add Tailwind colors like red, green, blue, yellow, etc.
</FormHelperText>
</FormElement>
<FormElement name="icon" label="Icon">
<Input
{...register("icon")}
id="icon"
type="text"
placeholder="e.g. Twitter"
/>
<FormHelperText>
Icon is the Lucide icon that appears on the left side of the title in the sidebar.
</FormHelperText>
</FormElement>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<FormElement name="published">
<Check name="published" label="Published" />
</FormElement>
<FormElement name="allow_guest">
<Check name="allow_guest" label="Allow Guest" />
</FormElement>
<FormElement name="is_group_page">
<Check name="is_group_page" label="Is Group Page" />
</FormElement>
</div>
</div>
)}
</div>
);
};
84 changes: 84 additions & 0 deletions dashboard/src/pages/features/docs/EditorComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { ErrorBanner } from '@/components/common/ErrorBanner/ErrorBanner';
import { FullPageLoader } from '@/components/common/FullPageLoader/FullPageLoader';
import { DocsPageForm } from '@/components/features/documentation/DocsPage/DocsPageForm';
import { Button } from '@/components/ui/button';
import { useToast } from '@/components/ui/use-toast';
import { CommitDocsPage } from '@/types/commit/CommitDocsPage';
import { removeFrappeFields } from '@/utils/removeFrappeFields';
import { Editor } from '@milkdown/kit/core';
import { FrappeDoc, useFrappeUpdateDoc } from 'frappe-react-sdk';
import { lazy, useState, Suspense } from 'react';
import { useForm, FormProvider } from 'react-hook-form';
import { getMarkdown } from '@milkdown/utils';

const MarkdownEditor = lazy(() => import('@/components/common/MarkdownEditor/MarkdownEditor'));


export const EditorComponent = ({ data, setEdit, mutate }: { data: CommitDocsPage, setEdit: React.Dispatch<React.SetStateAction<boolean>>, mutate: VoidFunction }) => {
const [crepeInstance, setCrepeInstance] = useState<Promise<Editor> | null>(null);

const methods = useForm<CommitDocsPage>({
defaultValues: {
...removeFrappeFields(data as FrappeDoc<CommitDocsPage>)
}
})
const { updateDoc, error, loading } = useFrappeUpdateDoc<CommitDocsPage>()

const handleGetMarkdown = async () => {
if (crepeInstance) {
const markdownContent = await crepeInstance.then((editor) => {
return editor.action(getMarkdown());
})
return markdownContent;
}
return ''
};

const { toast } = useToast();

const onSubmit = async (data: CommitDocsPage) => {
const markdownContent = await handleGetMarkdown();
updateDoc("Commit Docs Page", data.name, {
...data,
content: markdownContent,
}).then(() => {
mutate()
toast({
description: "Page Updated Successfully",
duration: 1500
})
}).then(() => {
setEdit(false)
})
}
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
<div className="flex flex-col w-full py-6 px-16 h-full pt-40 lg:pt-2">
<div className="flex flex-row w-full justify-end gap-2 pt-4">
<Button onClick={() => setEdit(false)}
variant={'ghost'}
size={'sm'}
>Cancel</Button>
<Button
type="submit"
className="bg-blue-500 hover:bg-blue-600 text-white font-bold"
size={'sm'}
onClick={methods.handleSubmit(onSubmit)}
>Save</Button>
</div>
<div className="flex flex-col gap-4 w-full py-6 ">
{error && <ErrorBanner error={error} />}
{loading && <FullPageLoader />}
<DocsPageForm />
<div className="flex flex-col gap-4 pl-24 pr-4 py-4 border border-gray-200 rounded-lg min-h-[60vh] shadow-sm bg-white">
<Suspense fallback={<FullPageLoader />}>
<MarkdownEditor value={data?.content ?? ''} setCrepeInstance={setCrepeInstance} />
</Suspense>
</div>
</div>
</div>
</form>
</FormProvider>
)
}
Loading