feat: format with prettier

This commit is contained in:
swve 2024-02-09 21:22:15 +01:00
parent 03fb09c3d6
commit a147ad6610
164 changed files with 11257 additions and 8154 deletions

View file

@ -1,29 +1,29 @@
import { mergeAttributes, Node } from "@tiptap/core";
import { ReactNodeViewRenderer } from "@tiptap/react";
import { mergeAttributes, Node } from '@tiptap/core'
import { ReactNodeViewRenderer } from '@tiptap/react'
import InfoCalloutComponent from "./InfoCalloutComponent";
import InfoCalloutComponent from './InfoCalloutComponent'
export default Node.create({
name: "calloutInfo",
group: "block",
name: 'calloutInfo',
group: 'block',
draggable: true,
content: "text*",
content: 'text*',
// TODO : multi line support
parseHTML() {
return [
{
tag: "callout-info",
tag: 'callout-info',
},
];
]
},
renderHTML({ HTMLAttributes }) {
return ["callout-info", mergeAttributes(HTMLAttributes), 0];
return ['callout-info', mergeAttributes(HTMLAttributes), 0]
},
addNodeView() {
return ReactNodeViewRenderer(InfoCalloutComponent);
return ReactNodeViewRenderer(InfoCalloutComponent)
},
});
})

View file

@ -1,35 +1,38 @@
import { useEditorProvider } from "@components/Contexts/Editor/EditorContext";
import { NodeViewContent, NodeViewWrapper } from "@tiptap/react";
import { AlertCircle } from "lucide-react";
import React from "react";
import styled from "styled-components";
import { useEditorProvider } from '@components/Contexts/Editor/EditorContext'
import { NodeViewContent, NodeViewWrapper } from '@tiptap/react'
import { AlertCircle } from 'lucide-react'
import React from 'react'
import styled from 'styled-components'
function InfoCalloutComponent(props: any) {
const editorState = useEditorProvider() as any;
const isEditable = editorState.isEditable;
const editorState = useEditorProvider() as any
const isEditable = editorState.isEditable
return (
<NodeViewWrapper>
<InfoCalloutWrapper className="flex space-x-2 items-center bg-blue-200 rounded-lg text-blue-900 px-3 shadow-inner" contentEditable={isEditable}>
<AlertCircle /> <NodeViewContent contentEditable={isEditable} className="content" />
<InfoCalloutWrapper
className="flex space-x-2 items-center bg-blue-200 rounded-lg text-blue-900 px-3 shadow-inner"
contentEditable={isEditable}
>
<AlertCircle />{' '}
<NodeViewContent contentEditable={isEditable} className="content" />
</InfoCalloutWrapper>
</NodeViewWrapper>
);
)
}
const InfoCalloutWrapper = styled.div`
svg{
svg {
padding: 3px;
}
.content {
margin: 5px;
padding: 0.5rem;
border: ${(props) => (props.contentEditable ? "2px dashed #1f3a8a12" : "none")};
border: ${(props) =>
props.contentEditable ? '2px dashed #1f3a8a12' : 'none'};
border-radius: 0.5rem;
}
`;
`
export default InfoCalloutComponent;
export default InfoCalloutComponent

View file

@ -1,29 +1,29 @@
import { mergeAttributes, Node } from "@tiptap/core";
import { ReactNodeViewRenderer } from "@tiptap/react";
import { mergeAttributes, Node } from '@tiptap/core'
import { ReactNodeViewRenderer } from '@tiptap/react'
import WarningCalloutComponent from "./WarningCalloutComponent";
import WarningCalloutComponent from './WarningCalloutComponent'
export default Node.create({
name: "calloutWarning",
group: "block",
name: 'calloutWarning',
group: 'block',
draggable: true,
content: "text*",
content: 'text*',
// TODO : multi line support
parseHTML() {
return [
{
tag: "callout-warning",
tag: 'callout-warning',
},
];
]
},
renderHTML({ HTMLAttributes }) {
return ["callout-info", mergeAttributes(HTMLAttributes), 0];
return ['callout-info', mergeAttributes(HTMLAttributes), 0]
},
addNodeView() {
return ReactNodeViewRenderer(WarningCalloutComponent);
return ReactNodeViewRenderer(WarningCalloutComponent)
},
});
})

View file

@ -1,25 +1,27 @@
import { useEditorProvider } from "@components/Contexts/Editor/EditorContext";
import { NodeViewContent, NodeViewWrapper } from "@tiptap/react";
import { AlertTriangle } from "lucide-react";
import React from "react";
import styled from "styled-components";
import { useEditorProvider } from '@components/Contexts/Editor/EditorContext'
import { NodeViewContent, NodeViewWrapper } from '@tiptap/react'
import { AlertTriangle } from 'lucide-react'
import React from 'react'
import styled from 'styled-components'
function WarningCalloutComponent(props: any) {
const editorState = useEditorProvider() as any;
const isEditable = editorState.isEditable;
const editorState = useEditorProvider() as any
const isEditable = editorState.isEditable
return (
<NodeViewWrapper>
<CalloutWrapper className="flex space-x-2 items-center bg-yellow-200 rounded-lg text-yellow-900 px-3 shadow-inner" contentEditable={isEditable}>
<AlertTriangle /> <NodeViewContent contentEditable={isEditable} className="content" />
<CalloutWrapper
className="flex space-x-2 items-center bg-yellow-200 rounded-lg text-yellow-900 px-3 shadow-inner"
contentEditable={isEditable}
>
<AlertTriangle />{' '}
<NodeViewContent contentEditable={isEditable} className="content" />
</CalloutWrapper>
</NodeViewWrapper>
);
)
}
const CalloutWrapper = styled.div`
svg {
padding: 3px;
}
@ -27,10 +29,11 @@ const CalloutWrapper = styled.div`
.content {
margin: 5px;
padding: 0.5rem;
border: ${(props) => (props.contentEditable ? "2px dashed #713f1117" : "none")};
border: ${(props) =>
props.contentEditable ? '2px dashed #713f1117' : 'none'};
border-radius: 0.5rem;
}
`;
`
const DragHandle = styled.div`
position: absolute;
@ -40,6 +43,6 @@ const DragHandle = styled.div`
height: 100%;
cursor: move;
z-index: 1;
`;
`
export default WarningCalloutComponent;
export default WarningCalloutComponent

View file

@ -1,11 +1,11 @@
import { mergeAttributes, Node } from "@tiptap/core";
import { ReactNodeViewRenderer } from "@tiptap/react";
import { mergeAttributes, Node } from '@tiptap/core'
import { ReactNodeViewRenderer } from '@tiptap/react'
import ImageBlockComponent from "./ImageBlockComponent";
import ImageBlockComponent from './ImageBlockComponent'
export default Node.create({
name: "blockImage",
group: "block",
name: 'blockImage',
group: 'block',
atom: true,
@ -17,22 +17,22 @@ export default Node.create({
size: {
width: 300,
},
};
}
},
parseHTML() {
return [
{
tag: "block-image",
tag: 'block-image',
},
];
]
},
renderHTML({ HTMLAttributes }) {
return ["block-image", mergeAttributes(HTMLAttributes), 0];
return ['block-image', mergeAttributes(HTMLAttributes), 0]
},
addNodeView() {
return ReactNodeViewRenderer(ImageBlockComponent);
return ReactNodeViewRenderer(ImageBlockComponent)
},
});
})

View file

@ -1,95 +1,136 @@
import { NodeViewWrapper } from "@tiptap/react";
import React, { useEffect } from "react";
import styled from "styled-components";
import { Resizable } from 're-resizable';
import { AlertTriangle, Image, Loader } from "lucide-react";
import { uploadNewImageFile } from "../../../../../services/blocks/Image/images";
import { UploadIcon } from "@radix-ui/react-icons";
import { getActivityBlockMediaDirectory } from "@services/media/media";
import { useOrg } from "@components/Contexts/OrgContext";
import { useCourse } from "@components/Contexts/CourseContext";
import { useEditorProvider } from "@components/Contexts/Editor/EditorContext";
import { NodeViewWrapper } from '@tiptap/react'
import React, { useEffect } from 'react'
import styled from 'styled-components'
import { Resizable } from 're-resizable'
import { AlertTriangle, Image, Loader } from 'lucide-react'
import { uploadNewImageFile } from '../../../../../services/blocks/Image/images'
import { UploadIcon } from '@radix-ui/react-icons'
import { getActivityBlockMediaDirectory } from '@services/media/media'
import { useOrg } from '@components/Contexts/OrgContext'
import { useCourse } from '@components/Contexts/CourseContext'
import { useEditorProvider } from '@components/Contexts/Editor/EditorContext'
function ImageBlockComponent(props: any) {
const org = useOrg() as any;
const course = useCourse() as any;
const editorState = useEditorProvider() as any;
const isEditable = editorState.isEditable;
const [image, setImage] = React.useState(null);
const [isLoading, setIsLoading] = React.useState(false);
const [blockObject, setblockObject] = React.useState(props.node.attrs.blockObject);
const [imageSize, setImageSize] = React.useState({ width: props.node.attrs.size ? props.node.attrs.size.width : 300 });
const fileId = blockObject ? `${blockObject.content.file_id}.${blockObject.content.file_format}` : null;
const org = useOrg() as any
const course = useCourse() as any
const editorState = useEditorProvider() as any
const isEditable = editorState.isEditable
const [image, setImage] = React.useState(null)
const [isLoading, setIsLoading] = React.useState(false)
const [blockObject, setblockObject] = React.useState(
props.node.attrs.blockObject
)
const [imageSize, setImageSize] = React.useState({
width: props.node.attrs.size ? props.node.attrs.size.width : 300,
})
const fileId = blockObject
? `${blockObject.content.file_id}.${blockObject.content.file_format}`
: null
const handleImageChange = (event: React.ChangeEvent<any>) => {
setImage(event.target.files[0]);
};
setImage(event.target.files[0])
}
const handleSubmit = async (e: any) => {
e.preventDefault();
setIsLoading(true);
let object = await uploadNewImageFile(image, props.extension.options.activity.activity_uuid);
setIsLoading(false);
setblockObject(object);
e.preventDefault()
setIsLoading(true)
let object = await uploadNewImageFile(
image,
props.extension.options.activity.activity_uuid
)
setIsLoading(false)
setblockObject(object)
props.updateAttributes({
blockObject: object,
size: imageSize,
});
};
useEffect(() => {
})
}
, [course, org]);
useEffect(() => {}, [course, org])
return (
<NodeViewWrapper className="block-image">
{!blockObject && isEditable && (
<BlockImageWrapper className="flex items-center space-x-3 py-7 bg-gray-50 rounded-xl text-gray-900 px-3 border-dashed border-gray-150 border-2" contentEditable={isEditable}>
<BlockImageWrapper
className="flex items-center space-x-3 py-7 bg-gray-50 rounded-xl text-gray-900 px-3 border-dashed border-gray-150 border-2"
contentEditable={isEditable}
>
{isLoading ? (
<Loader className="animate-spin animate-pulse text-gray-200" size={50} />
<Loader
className="animate-spin animate-pulse text-gray-200"
size={50}
/>
) : (
<>
<div>
<Image className="text-gray-200" size={50} />
</div>
<input className="p-3 rounded-lg file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 hover:file:cursor-pointer file:bg-gray-200 cursor-pointer file:text-gray-500" onChange={handleImageChange} type="file" name="" id="" />
<button className='p-2 px-3 bg-gray-200 rounded-lg text-gray-500 hover:bg-gray-300 transition space-x-2 items-center flex' onClick={handleSubmit}><UploadIcon></UploadIcon><p>Submit</p></button>
<input
className="p-3 rounded-lg file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 hover:file:cursor-pointer file:bg-gray-200 cursor-pointer file:text-gray-500"
onChange={handleImageChange}
type="file"
name=""
id=""
/>
<button
className="p-2 px-3 bg-gray-200 rounded-lg text-gray-500 hover:bg-gray-300 transition space-x-2 items-center flex"
onClick={handleSubmit}
>
<UploadIcon></UploadIcon>
<p>Submit</p>
</button>
</>
)}
</BlockImageWrapper>
)}
{blockObject && (
<Resizable defaultSize={{ width: imageSize.width, height: "100%" }}
<Resizable
defaultSize={{ width: imageSize.width, height: '100%' }}
handleStyles={{
right: { position: 'unset', width: 7, height: 30, borderRadius: 20, cursor: 'col-resize', backgroundColor: 'black', opacity: '0.3', margin: 'auto', marginLeft: 5 },
right: {
position: 'unset',
width: 7,
height: 30,
borderRadius: 20,
cursor: 'col-resize',
backgroundColor: 'black',
opacity: '0.3',
margin: 'auto',
marginLeft: 5,
},
}}
style={{
margin: 'auto',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100%',
}}
style={{ margin: "auto", display: "flex", justifyContent: "center", alignItems: "center", height: "100%" }}
maxWidth={1000}
minWidth={200}
onResizeStop={(e, direction, ref, d) => {
props.updateAttributes({
size: {
width: imageSize.width + d.width,
}
});
},
})
setImageSize({
width: imageSize.width + d.width,
});
})
}}
>
<img
src={`${getActivityBlockMediaDirectory(org?.org_uuid,
src={`${getActivityBlockMediaDirectory(
org?.org_uuid,
course?.courseStructure.course_uuid,
props.extension.options.activity.activity_uuid,
blockObject.block_uuid,
blockObject ? fileId : ' ', 'imageBlock')}`}
blockObject ? fileId : ' ',
'imageBlock'
)}`}
alt=""
className="rounded-lg shadow "
/>
</Resizable>
)}
{isLoading && (
@ -98,29 +139,24 @@ function ImageBlockComponent(props: any) {
</div>
)}
</NodeViewWrapper>
);
)
}
export default ImageBlockComponent;
export default ImageBlockComponent
const BlockImageWrapper = styled.div`
align-items: center;
justify-content: center;
text-align: center;
font-size: 14px;
`;
`
const BlockImage = styled.div`
display: flex;
// center
align-items: center;
justify-content: center;
text-align: center;
font-size: 14px;
`;
`

View file

@ -1,35 +1,35 @@
import { mergeAttributes, Node } from "@tiptap/core";
import { ReactNodeViewRenderer } from "@tiptap/react";
import { mergeAttributes, Node } from '@tiptap/core'
import { ReactNodeViewRenderer } from '@tiptap/react'
import MathEquationBlockComponent from "./MathEquationBlockComponent";
import MathEquationBlockComponent from './MathEquationBlockComponent'
export default Node.create({
name: "blockMathEquation",
group: "block",
name: 'blockMathEquation',
group: 'block',
atom: true,
addAttributes() {
return {
math_equation: {
default: "",
default: '',
},
};
}
},
parseHTML() {
return [
{
tag: "block-math-equation",
tag: 'block-math-equation',
},
];
]
},
renderHTML({ HTMLAttributes }) {
return ["block-math-equation", mergeAttributes(HTMLAttributes), 0];
return ['block-math-equation', mergeAttributes(HTMLAttributes), 0]
},
addNodeView() {
return ReactNodeViewRenderer(MathEquationBlockComponent);
return ReactNodeViewRenderer(MathEquationBlockComponent)
},
});
})

View file

@ -1,31 +1,31 @@
import { NodeViewWrapper } from "@tiptap/react";
import React from "react";
import styled from "styled-components";
import "katex/dist/katex.min.css";
import { BlockMath } from "react-katex";
import { Save } from "lucide-react";
import Link from "next/link";
import { useEditorProvider } from "@components/Contexts/Editor/EditorContext";
import { NodeViewWrapper } from '@tiptap/react'
import React from 'react'
import styled from 'styled-components'
import 'katex/dist/katex.min.css'
import { BlockMath } from 'react-katex'
import { Save } from 'lucide-react'
import Link from 'next/link'
import { useEditorProvider } from '@components/Contexts/Editor/EditorContext'
function MathEquationBlockComponent(props: any) {
const [equation, setEquation] = React.useState(props.node.attrs.math_equation);
const [isEditing, setIsEditing] = React.useState(true);
const editorState = useEditorProvider() as any;
const isEditable = editorState.isEditable;
const [equation, setEquation] = React.useState(props.node.attrs.math_equation)
const [isEditing, setIsEditing] = React.useState(true)
const editorState = useEditorProvider() as any
const isEditable = editorState.isEditable
const handleEquationChange = (event: React.ChangeEvent<any>) => {
setEquation(event.target.value);
setEquation(event.target.value)
props.updateAttributes({
math_equation: equation,
});
};
})
}
const saveEquation = () => {
props.updateAttributes({
math_equation: equation,
});
})
//setIsEditing(false);
};
}
return (
<NodeViewWrapper className="block-math-equation">
@ -34,24 +34,38 @@ function MathEquationBlockComponent(props: any) {
{isEditing && isEditable && (
<>
<EditBar>
<input value={equation} onChange={handleEquationChange} placeholder="Insert a Math Equation (LaTeX) " type="text" />
<input
value={equation}
onChange={handleEquationChange}
placeholder="Insert a Math Equation (LaTeX) "
type="text"
/>
<button className="opacity-1" onClick={() => saveEquation()}>
<Save size={15}></Save>
</button>
</EditBar>
<span className="pt-2 text-zinc-500 text-sm">Please refer to this <Link className="text-zinc-900 after:content-['↗']" href="https://katex.org/docs/supported.html" target="_blank"> guide</Link> for supported TeX functions </span>
<span className="pt-2 text-zinc-500 text-sm">
Please refer to this{' '}
<Link
className="text-zinc-900 after:content-['↗']"
href="https://katex.org/docs/supported.html"
target="_blank"
>
{' '}
guide
</Link>{' '}
for supported TeX functions{' '}
</span>
</>
)}
</MathEqWrapper>
</NodeViewWrapper>
);
)
}
export default MathEquationBlockComponent;
export default MathEquationBlockComponent
const MathEqWrapper = styled.div`
`;
const MathEqWrapper = styled.div``
const EditBar = styled.div`
display: flex;
@ -82,7 +96,7 @@ const EditBar = styled.div`
font-size: 14px;
color: #494949;
width: 100%;
font-family: "DM Sans", sans-serif;
font-family: 'DM Sans', sans-serif;
padding-left: 10px;
&:focus {
outline: none;
@ -92,4 +106,4 @@ const EditBar = styled.div`
color: #49494936;
}
}
`;
`

View file

@ -1,21 +1,29 @@
import { Extension } from '@tiptap/core';
import { Plugin, PluginKey } from 'prosemirror-state';
import { Extension } from '@tiptap/core'
import { Plugin, PluginKey } from 'prosemirror-state'
export const NoTextInput = Extension.create({
name: 'noTextInput',
addProseMirrorPlugins() {
return [
new Plugin({
key: new PluginKey('noTextInput'),
filterTransaction: (transaction) => {
// If the transaction is adding text, stop it
return !transaction.docChanged || transaction.steps.every((step) => {
const { slice } = step.toJSON();
return !slice || !slice.content.some((node: { type: string; }) => node.type === 'text');
});
},
}),
];
},
});
name: 'noTextInput',
addProseMirrorPlugins() {
return [
new Plugin({
key: new PluginKey('noTextInput'),
filterTransaction: (transaction) => {
// If the transaction is adding text, stop it
return (
!transaction.docChanged ||
transaction.steps.every((step) => {
const { slice } = step.toJSON()
return (
!slice ||
!slice.content.some(
(node: { type: string }) => node.type === 'text'
)
)
})
)
},
}),
]
},
})

View file

@ -1,11 +1,11 @@
import { mergeAttributes, Node } from "@tiptap/core";
import { ReactNodeViewRenderer } from "@tiptap/react";
import { mergeAttributes, Node } from '@tiptap/core'
import { ReactNodeViewRenderer } from '@tiptap/react'
import PDFBlockComponent from "./PDFBlockComponent";
import PDFBlockComponent from './PDFBlockComponent'
export default Node.create({
name: "blockPDF",
group: "block",
name: 'blockPDF',
group: 'block',
atom: true,
@ -14,22 +14,22 @@ export default Node.create({
blockObject: {
default: null,
},
};
}
},
parseHTML() {
return [
{
tag: "block-pdf",
tag: 'block-pdf',
},
];
]
},
renderHTML({ HTMLAttributes }) {
return ["block-pdf", mergeAttributes(HTMLAttributes), 0];
return ['block-pdf', mergeAttributes(HTMLAttributes), 0]
},
addNodeView() {
return ReactNodeViewRenderer(PDFBlockComponent);
return ReactNodeViewRenderer(PDFBlockComponent)
},
});
})

View file

@ -1,56 +1,79 @@
import { NodeViewWrapper } from "@tiptap/react";
import React, { useEffect } from "react";
import styled from "styled-components";
import { AlertTriangle, FileText, Loader } from "lucide-react";
import { uploadNewPDFFile } from "../../../../../services/blocks/Pdf/pdf";
import { UploadIcon } from "@radix-ui/react-icons";
import { getActivityBlockMediaDirectory } from "@services/media/media";
import { useOrg } from "@components/Contexts/OrgContext";
import { useCourse } from "@components/Contexts/CourseContext";
import { useEditorProvider } from "@components/Contexts/Editor/EditorContext";
import { NodeViewWrapper } from '@tiptap/react'
import React, { useEffect } from 'react'
import styled from 'styled-components'
import { AlertTriangle, FileText, Loader } from 'lucide-react'
import { uploadNewPDFFile } from '../../../../../services/blocks/Pdf/pdf'
import { UploadIcon } from '@radix-ui/react-icons'
import { getActivityBlockMediaDirectory } from '@services/media/media'
import { useOrg } from '@components/Contexts/OrgContext'
import { useCourse } from '@components/Contexts/CourseContext'
import { useEditorProvider } from '@components/Contexts/Editor/EditorContext'
function PDFBlockComponent(props: any) {
const org = useOrg() as any;
const course = useCourse() as any;
const [pdf, setPDF] = React.useState(null);
const [isLoading, setIsLoading] = React.useState(false);
const [blockObject, setblockObject] = React.useState(props.node.attrs.blockObject);
const fileId = blockObject ? `${blockObject.content.file_id}.${blockObject.content.file_format}` : null;
const editorState = useEditorProvider() as any;
const isEditable = editorState.isEditable;
const org = useOrg() as any
const course = useCourse() as any
const [pdf, setPDF] = React.useState(null)
const [isLoading, setIsLoading] = React.useState(false)
const [blockObject, setblockObject] = React.useState(
props.node.attrs.blockObject
)
const fileId = blockObject
? `${blockObject.content.file_id}.${blockObject.content.file_format}`
: null
const editorState = useEditorProvider() as any
const isEditable = editorState.isEditable
const handlePDFChange = (event: React.ChangeEvent<any>) => {
setPDF(event.target.files[0]);
};
setPDF(event.target.files[0])
}
const handleSubmit = async (e: any) => {
e.preventDefault();
setIsLoading(true);
let object = await uploadNewPDFFile(pdf, props.extension.options.activity.activity_uuid);
setIsLoading(false);
setblockObject(object);
e.preventDefault()
setIsLoading(true)
let object = await uploadNewPDFFile(
pdf,
props.extension.options.activity.activity_uuid
)
setIsLoading(false)
setblockObject(object)
props.updateAttributes({
blockObject: object,
});
};
useEffect(() => {
})
}
, [course, org]);
useEffect(() => {}, [course, org])
return (
<NodeViewWrapper className="block-pdf">
{!blockObject && (
<BlockPDFWrapper className="flex items-center space-x-3 py-7 bg-gray-50 rounded-xl text-gray-900 px-3 border-dashed border-gray-150 border-2" contentEditable={isEditable}>
<BlockPDFWrapper
className="flex items-center space-x-3 py-7 bg-gray-50 rounded-xl text-gray-900 px-3 border-dashed border-gray-150 border-2"
contentEditable={isEditable}
>
{isLoading ? (
<Loader className="animate-spin animate-pulse text-gray-200" size={50} />
<Loader
className="animate-spin animate-pulse text-gray-200"
size={50}
/>
) : (
<>
<div>
<FileText className="text-gray-200" size={50} />
</div>
<input className="p-3 rounded-lg file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 hover:file:cursor-pointer file:bg-gray-200 cursor-pointer file:text-gray-500" onChange={handlePDFChange} type="file" name="" id="" />
<button className='p-2 px-3 bg-gray-200 rounded-lg text-gray-500 hover:bg-gray-300 transition space-x-2 items-center flex' onClick={handleSubmit}><UploadIcon></UploadIcon><p>Submit</p></button>
<input
className="p-3 rounded-lg file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 hover:file:cursor-pointer file:bg-gray-200 cursor-pointer file:text-gray-500"
onChange={handlePDFChange}
type="file"
name=""
id=""
/>
<button
className="p-2 px-3 bg-gray-200 rounded-lg text-gray-500 hover:bg-gray-300 transition space-x-2 items-center flex"
onClick={handleSubmit}
>
<UploadIcon></UploadIcon>
<p>Submit</p>
</button>
</>
)}
</BlockPDFWrapper>
@ -59,11 +82,14 @@ function PDFBlockComponent(props: any) {
<BlockPDF>
<iframe
className="shadow rounded-lg h-96 w-full object-scale-down bg-black"
src={`${getActivityBlockMediaDirectory(org?.org_uuid,
src={`${getActivityBlockMediaDirectory(
org?.org_uuid,
course?.courseStructure.course_uuid,
props.extension.options.activity.activity_uuid,
blockObject.block_uuid,
blockObject ? fileId : ' ', 'pdfBlock')}`}
blockObject ? fileId : ' ',
'pdfBlock'
)}`}
/>
</BlockPDF>
)}
@ -73,19 +99,18 @@ function PDFBlockComponent(props: any) {
</div>
)}
</NodeViewWrapper>
);
)
}
export default PDFBlockComponent;
export default PDFBlockComponent
const BlockPDFWrapper = styled.div`
// center
align-items: center;
justify-content: center;
text-align: center;
font-size: 14px;
`;
`
const BlockPDF = styled.div`
display: flex;
@ -97,5 +122,5 @@ const BlockPDF = styled.div`
// cover
object-fit: cover;
}
`;
const PDFNotFound = styled.div``;
`
const PDFNotFound = styled.div``

View file

@ -1,11 +1,11 @@
import { mergeAttributes, Node } from "@tiptap/core";
import { ReactNodeViewRenderer } from "@tiptap/react";
import { mergeAttributes, Node } from '@tiptap/core'
import { ReactNodeViewRenderer } from '@tiptap/react'
import QuizBlockComponent from "./QuizBlockComponent";
import QuizBlockComponent from './QuizBlockComponent'
export default Node.create({
name: "blockQuiz",
group: "block",
name: 'blockQuiz',
group: 'block',
atom: true,
addAttributes() {
@ -16,22 +16,22 @@ export default Node.create({
questions: {
default: [],
},
};
}
},
parseHTML() {
return [
{
tag: "block-quiz",
tag: 'block-quiz',
},
];
]
},
renderHTML({ HTMLAttributes }) {
return ["block-quiz", mergeAttributes(HTMLAttributes), 0];
return ['block-quiz', mergeAttributes(HTMLAttributes), 0]
},
addNodeView() {
return ReactNodeViewRenderer(QuizBlockComponent);
return ReactNodeViewRenderer(QuizBlockComponent)
},
});
})

View file

@ -1,188 +1,206 @@
import { NodeViewWrapper } from "@tiptap/react";
import { v4 as uuidv4 } from "uuid";
import { NodeViewWrapper } from '@tiptap/react'
import { v4 as uuidv4 } from 'uuid'
import { twMerge } from 'tailwind-merge'
import React from "react";
import { BadgeHelp, Check, Minus, Plus, RefreshCcw } from "lucide-react";
import ReactConfetti from "react-confetti";
import { useEditorProvider } from "@components/Contexts/Editor/EditorContext";
import React from 'react'
import { BadgeHelp, Check, Minus, Plus, RefreshCcw } from 'lucide-react'
import ReactConfetti from 'react-confetti'
import { useEditorProvider } from '@components/Contexts/Editor/EditorContext'
interface Answer {
answer_id: string;
answer: string;
correct: boolean;
answer_id: string
answer: string
correct: boolean
}
interface Question {
question_id: string;
question: string;
type: "multiple_choice" | 'custom_answer'
answers: Answer[];
question_id: string
question: string
type: 'multiple_choice' | 'custom_answer'
answers: Answer[]
}
function QuizBlockComponent(props: any) {
const [questions, setQuestions] = React.useState(props.node.attrs.questions) as [Question[], any];
const [userAnswers, setUserAnswers] = React.useState([]) as [any[], any];
const [submitted, setSubmitted] = React.useState(false) as [boolean, any];
const [submissionMessage, setSubmissionMessage] = React.useState("") as [string, any];
const editorState = useEditorProvider() as any;
const isEditable = editorState.isEditable;
const [questions, setQuestions] = React.useState(
props.node.attrs.questions
) as [Question[], any]
const [userAnswers, setUserAnswers] = React.useState([]) as [any[], any]
const [submitted, setSubmitted] = React.useState(false) as [boolean, any]
const [submissionMessage, setSubmissionMessage] = React.useState('') as [
string,
any,
]
const editorState = useEditorProvider() as any
const isEditable = editorState.isEditable
const handleAnswerClick = (question_id: string, answer_id: string) => {
// if the quiz is submitted, do nothing
if (submitted) {
return;
return
}
const userAnswer = {
question_id: question_id,
answer_id: answer_id
answer_id: answer_id,
}
const newAnswers = [...userAnswers, userAnswer];
const newAnswers = [...userAnswers, userAnswer]
// only accept one answer per question
const filteredAnswers = newAnswers.filter((answer: any) => answer.question_id !== question_id);
setUserAnswers([...filteredAnswers, userAnswer]);
const filteredAnswers = newAnswers.filter(
(answer: any) => answer.question_id !== question_id
)
setUserAnswers([...filteredAnswers, userAnswer])
}
const refreshUserSubmission = () => {
setUserAnswers([]);
setSubmitted(false);
setUserAnswers([])
setSubmitted(false)
}
const handleUserSubmission = () => {
if (userAnswers.length === 0) {
setSubmissionMessage("Please answer at least one question!");
return;
setSubmissionMessage('Please answer at least one question!')
return
}
setSubmitted(true);
setSubmitted(true)
// check if all submitted answers are correct
const correctAnswers = questions.map((question: Question) => {
const correctAnswer: any = question.answers.find((answer: Answer) => answer.correct);
const userAnswer = userAnswers.find((userAnswer: any) => userAnswer.question_id === question.question_id);
const correctAnswer: any = question.answers.find(
(answer: Answer) => answer.correct
)
const userAnswer = userAnswers.find(
(userAnswer: any) => userAnswer.question_id === question.question_id
)
if (correctAnswer.answer_id === userAnswer.answer_id) {
return true;
return true
} else {
return false;
return false
}
});
})
// check if all answers are correct
const allCorrect = correctAnswers.every((answer: boolean) => answer === true);
const allCorrect = correctAnswers.every(
(answer: boolean) => answer === true
)
if (allCorrect) {
setSubmissionMessage("All answers are correct!");
console.log("All answers are correct!");
setSubmissionMessage('All answers are correct!')
console.log('All answers are correct!')
} else {
setSubmissionMessage('Some answers are incorrect!')
console.log('Some answers are incorrect!')
}
else {
setSubmissionMessage("Some answers are incorrect!");
console.log("Some answers are incorrect!");
}
}
const getAnswerID = (answerIndex: number, questionId : string) => {
const alphabet = Array.from({ length: 26 }, (_, i) => String.fromCharCode('A'.charCodeAt(0) + i));
let alphabetID = alphabet[answerIndex];
const getAnswerID = (answerIndex: number, questionId: string) => {
const alphabet = Array.from({ length: 26 }, (_, i) =>
String.fromCharCode('A'.charCodeAt(0) + i)
)
let alphabetID = alphabet[answerIndex]
// Get question index
const questionIndex = questions.findIndex((question: Question) => question.question_id === questionId);
let questionID = questionIndex + 1;
const questionIndex = questions.findIndex(
(question: Question) => question.question_id === questionId
)
let questionID = questionIndex + 1
return `${alphabetID}`;
return `${alphabetID}`
}
const saveQuestions = (questions: any) => {
props.updateAttributes({
questions: questions,
});
setQuestions(questions);
};
})
setQuestions(questions)
}
const addSampleQuestion = () => {
const newQuestion = {
question_id: uuidv4(),
question: "",
type: "multiple_choice",
question: '',
type: 'multiple_choice',
answers: [
{
answer_id: uuidv4(),
answer: "",
correct: false
answer: '',
correct: false,
},
]
],
}
setQuestions([...questions, newQuestion]);
setQuestions([...questions, newQuestion])
}
const addAnswer = (question_id: string) => {
const newAnswer = {
answer_id: uuidv4(),
answer: "",
correct: false
answer: '',
correct: false,
}
// check if there is already more thqn 5 answers
const question: any = questions.find((question: Question) => question.question_id === question_id);
const question: any = questions.find(
(question: Question) => question.question_id === question_id
)
if (question.answers.length >= 5) {
return;
return
}
const newQuestions = questions.map((question: Question) => {
if (question.question_id === question_id) {
question.answers.push(newAnswer);
question.answers.push(newAnswer)
}
return question;
});
return question
})
saveQuestions(newQuestions);
saveQuestions(newQuestions)
}
const changeAnswerValue = (question_id: string, answer_id: string, value: string) => {
const changeAnswerValue = (
question_id: string,
answer_id: string,
value: string
) => {
const newQuestions = questions.map((question: Question) => {
if (question.question_id === question_id) {
question.answers.map((answer: Answer) => {
if (answer.answer_id === answer_id) {
answer.answer = value;
answer.answer = value
}
return answer;
});
return answer
})
}
return question;
});
saveQuestions(newQuestions);
return question
})
saveQuestions(newQuestions)
}
const changeQuestionValue = (question_id: string, value: string) => {
const newQuestions = questions.map((question: Question) => {
if (question.question_id === question_id) {
question.question = value;
question.question = value
}
return question;
});
saveQuestions(newQuestions);
return question
})
saveQuestions(newQuestions)
}
const deleteQuestion = (question_id: string) => {
const newQuestions = questions.filter((question: Question) => question.question_id !== question_id);
saveQuestions(newQuestions);
const newQuestions = questions.filter(
(question: Question) => question.question_id !== question_id
)
saveQuestions(newQuestions)
}
const deleteAnswer = (question_id: string, answer_id: string) => {
const newQuestions = questions.map((question: Question) => {
if (question.question_id === question_id) {
question.answers = question.answers.filter((answer: Answer) => answer.answer_id !== answer_id);
question.answers = question.answers.filter(
(answer: Answer) => answer.answer_id !== answer_id
)
}
return question;
});
saveQuestions(newQuestions);
return question
})
saveQuestions(newQuestions)
}
const markAnswerCorrect = (question_id: string, answer_id: string) => {
@ -190,54 +208,68 @@ function QuizBlockComponent(props: any) {
if (question.question_id === question_id) {
question.answers.map((answer: Answer) => {
if (answer.answer_id === answer_id) {
answer.correct = true;
answer.correct = true
} else {
answer.correct = false;
answer.correct = false
}
return answer;
});
return answer
})
}
return question;
});
saveQuestions(newQuestions);
return question
})
saveQuestions(newQuestions)
}
return (
<NodeViewWrapper className="block-quiz">
<div
//style={{ background: "radial-gradient(152.15% 150.08% at 56.45% -6.67%, rgba(180, 255, 250, 0.10) 5.53%, rgba(202, 201, 255, 0.10) 66.76%)" }}
className="rounded-xl px-5 py-2 bg-slate-100 transition-all ease-linear"
>
<div className="flex space-x-2 pt-1 items-center text-sm overflow-hidden">
{(submitted && submissionMessage == "All answers are correct!") &&
{submitted && submissionMessage == 'All answers are correct!' && (
<ReactConfetti
numberOfPieces={submitted ? 1400 : 0}
recycle={false}
className="w-full h-screen"
/>
}
)}
<div className="flex space-x-2 items-center text-sm">
<BadgeHelp className='text-slate-400' size={15} />
<p className="uppercase tracking-widest text-xs font-bold py-1 text-slate-400">Quiz</p>
<BadgeHelp className="text-slate-400" size={15} />
<p className="uppercase tracking-widest text-xs font-bold py-1 text-slate-400">
Quiz
</p>
</div>
<div className="grow flex items-center justify-center">
</div>
{isEditable ?
<div className="grow flex items-center justify-center"></div>
{isEditable ? (
<div>
<button onClick={addSampleQuestion} className="bg-slate-200 hover:bg-slate-300 text-slate-800 font-bold py-1 px-2 rounded-lg text-xs">Add Question</button>
<button
onClick={addSampleQuestion}
className="bg-slate-200 hover:bg-slate-300 text-slate-800 font-bold py-1 px-2 rounded-lg text-xs"
>
Add Question
</button>
</div>
:
) : (
<div className="flex space-x-1 items-center">
<div onClick={() => refreshUserSubmission()} className="cursor-pointer px-2">
<RefreshCcw className='text-slate-400 cursor-pointer' size={15} />
<div
onClick={() => refreshUserSubmission()}
className="cursor-pointer px-2"
>
<RefreshCcw
className="text-slate-400 cursor-pointer"
size={15}
/>
</div>
<button onClick={() => handleUserSubmission()} className="bg-slate-200 hover:bg-slate-300 text-slate-800 font-bold py-1 px-2 rounded-lg text-xs">Submit</button>
<button
onClick={() => handleUserSubmission()}
className="bg-slate-200 hover:bg-slate-300 text-slate-800 font-bold py-1 px-2 rounded-lg text-xs"
>
Submit
</button>
</div>
}
)}
</div>
{questions.map((question: Question) => (
@ -245,20 +277,32 @@ function QuizBlockComponent(props: any) {
<div className="question">
<div className="flex space-x-2 items-center">
<div className="flex-grow">
{isEditable ?
<input value={question.question} placeholder="Your Question" onChange={(e) => changeQuestionValue(question.question_id, e.target.value)} className="text-slate-800 bg-[#00008b00] border-2 border-gray-200 rounded-md border-dotted text-md font-bold w-full"></input>
:
<p className="text-slate-800 bg-[#00008b00] rounded-md text-md font-bold w-full">{question.question}</p>
}
{isEditable ? (
<input
value={question.question}
placeholder="Your Question"
onChange={(e) =>
changeQuestionValue(
question.question_id,
e.target.value
)
}
className="text-slate-800 bg-[#00008b00] border-2 border-gray-200 rounded-md border-dotted text-md font-bold w-full"
></input>
) : (
<p className="text-slate-800 bg-[#00008b00] rounded-md text-md font-bold w-full">
{question.question}
</p>
)}
</div>
{isEditable &&
{isEditable && (
<div
onClick={() => deleteQuestion(question.question_id)}
className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-slate-200 hover:bg-slate-300 text-sm transition-all ease-linear cursor-pointer">
<Minus
className="mx-auto text-slate-400" size={12} />
className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-slate-200 hover:bg-slate-300 text-sm transition-all ease-linear cursor-pointer"
>
<Minus className="mx-auto text-slate-400" size={12} />
</div>
}
)}
</div>
<div className="answers flex py-2 space-x-3">
{question.answers.map((answer: Answer) => (
@ -266,60 +310,119 @@ function QuizBlockComponent(props: any) {
key={answer.answer_id}
className={twMerge(
'outline outline-3 pr-2 shadow w-full flex items-center space-x-2 h-[30px] bg-opacity-50 hover:bg-opacity-100 hover:shadow-md rounded-s rounded-lg bg-white text-sm hover:scale-105 active:scale-110 duration-150 cursor-pointer ease-linear',
answer.correct && isEditable ? 'outline-lime-300' : 'outline-white',
userAnswers.find((userAnswer: any) => (userAnswer.question_id === question.question_id && userAnswer.answer_id === answer.answer_id) && !isEditable) ? 'outline-slate-300' : '',
(submitted && answer.correct) ? 'outline-lime-300 text-lime' : '',
(submitted && !answer.correct) && userAnswers.find((userAnswer: any) => userAnswer.question_id === question.question_id && userAnswer.answer_id === answer.answer_id) ? 'outline-red-400' : '',
)
answer.correct && isEditable
? 'outline-lime-300'
: 'outline-white',
userAnswers.find(
(userAnswer: any) =>
userAnswer.question_id === question.question_id &&
userAnswer.answer_id === answer.answer_id &&
!isEditable
)
? 'outline-slate-300'
: '',
submitted && answer.correct
? 'outline-lime-300 text-lime'
: '',
submitted &&
!answer.correct &&
userAnswers.find(
(userAnswer: any) =>
userAnswer.question_id === question.question_id &&
userAnswer.answer_id === answer.answer_id
)
? 'outline-red-400'
: ''
)}
onClick={() =>
handleAnswerClick(question.question_id, answer.answer_id)
}
onClick={() => handleAnswerClick(question.question_id, answer.answer_id)}
>
<div className={twMerge(
"bg-white font-bold text-base flex items-center h-full w-[40px] rounded-l-md text-slate-800",
answer.correct && isEditable ? 'bg-lime-300 text-lime-800 outline-none' : 'bg-white',
(submitted && answer.correct) ? 'bg-lime-300 text-lime-800 outline-none' : '',
(submitted && !answer.correct) && userAnswers.find((userAnswer: any) => userAnswer.question_id === question.question_id && userAnswer.answer_id === answer.answer_id) ? 'bg-red-400 text-red-800 outline-none' : '',
)}>
<p className="mx-auto font-bold text-sm ">{getAnswerID(question.answers.indexOf(answer),question.question_id)}</p>
<div
className={twMerge(
'bg-white font-bold text-base flex items-center h-full w-[40px] rounded-l-md text-slate-800',
answer.correct && isEditable
? 'bg-lime-300 text-lime-800 outline-none'
: 'bg-white',
submitted && answer.correct
? 'bg-lime-300 text-lime-800 outline-none'
: '',
submitted &&
!answer.correct &&
userAnswers.find(
(userAnswer: any) =>
userAnswer.question_id === question.question_id &&
userAnswer.answer_id === answer.answer_id
)
? 'bg-red-400 text-red-800 outline-none'
: ''
)}
>
<p className="mx-auto font-bold text-sm ">
{getAnswerID(
question.answers.indexOf(answer),
question.question_id
)}
</p>
</div>
{isEditable ?
<input value={answer.answer} onChange={(e) => changeAnswerValue(question.question_id, answer.answer_id, e.target.value)} placeholder="Answer" className="w-full mx-2 px-3 pr-6 text-neutral-600 bg-[#00008b00] border-2 border-gray-200 rounded-md border-dotted text-sm font-bold"></input>
:
<p className="w-full mx-2 px-3 pr-6 text-neutral-600 bg-[#00008b00] rounded-md ext-sm font-bold">{answer.answer}</p>
}
{isEditable &&
{isEditable ? (
<input
value={answer.answer}
onChange={(e) =>
changeAnswerValue(
question.question_id,
answer.answer_id,
e.target.value
)
}
placeholder="Answer"
className="w-full mx-2 px-3 pr-6 text-neutral-600 bg-[#00008b00] border-2 border-gray-200 rounded-md border-dotted text-sm font-bold"
></input>
) : (
<p className="w-full mx-2 px-3 pr-6 text-neutral-600 bg-[#00008b00] rounded-md ext-sm font-bold">
{answer.answer}
</p>
)}
{isEditable && (
<div className="flex space-x-1 items-center">
<div
onClick={() => markAnswerCorrect(question.question_id, answer.answer_id)}
className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-lime-300 hover:bg-lime-400 transition-all ease-linear text-sm cursor-pointer ">
<Check
className="mx-auto text-lime-800" size={12} />
onClick={() =>
markAnswerCorrect(
question.question_id,
answer.answer_id
)
}
className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-lime-300 hover:bg-lime-400 transition-all ease-linear text-sm cursor-pointer "
>
<Check className="mx-auto text-lime-800" size={12} />
</div>
<div
onClick={() => deleteAnswer(question.question_id, answer.answer_id)}
className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-slate-200 hover:bg-slate-300 text-sm transition-all ease-linear cursor-pointer">
<Minus
className="mx-auto text-slate-400" size={12} />
onClick={() =>
deleteAnswer(question.question_id, answer.answer_id)
}
className="w-[20px] flex-none flex items-center h-[20px] rounded-lg bg-slate-200 hover:bg-slate-300 text-sm transition-all ease-linear cursor-pointer"
>
<Minus className="mx-auto text-slate-400" size={12} />
</div>
</div>
}
)}
</div>
))}
{isEditable &&
<div onClick={() => addAnswer(question.question_id)} className="outline outline-3 w-[30px] flex-none flex items-center h-[30px] outline-white hover:bg-opacity-100 hover:shadow-md rounded-lg bg-white text-sm hover:scale-105 active:scale-110 duration-150 cursor-pointer ease-linear">
{isEditable && (
<div
onClick={() => addAnswer(question.question_id)}
className="outline outline-3 w-[30px] flex-none flex items-center h-[30px] outline-white hover:bg-opacity-100 hover:shadow-md rounded-lg bg-white text-sm hover:scale-105 active:scale-110 duration-150 cursor-pointer ease-linear"
>
<Plus className="mx-auto text-slate-800" size={15} />
</div>
}
)}
</div>
</div>
</div>
))}
</div>
</NodeViewWrapper>
);
)
}
export default QuizBlockComponent;
export default QuizBlockComponent

View file

@ -1,34 +1,34 @@
import { mergeAttributes, Node } from "@tiptap/core";
import { ReactNodeViewRenderer } from "@tiptap/react";
import { mergeAttributes, Node } from '@tiptap/core'
import { ReactNodeViewRenderer } from '@tiptap/react'
import VideoBlockComponent from "./VideoBlockComponent";
import VideoBlockComponent from './VideoBlockComponent'
export default Node.create({
name: "blockVideo",
group: "block",
name: 'blockVideo',
group: 'block',
atom: true,
addAttributes() {
return {
blockObject: {
default: null,
},
};
}
},
parseHTML() {
return [
{
tag: "block-video",
tag: 'block-video',
},
];
]
},
renderHTML({ HTMLAttributes }) {
return ["block-video", mergeAttributes(HTMLAttributes), 0];
return ['block-video', mergeAttributes(HTMLAttributes), 0]
},
addNodeView() {
return ReactNodeViewRenderer(VideoBlockComponent);
return ReactNodeViewRenderer(VideoBlockComponent)
},
});
})

View file

@ -1,56 +1,79 @@
import { NodeViewWrapper } from "@tiptap/react";
import { Loader, Video } from "lucide-react";
import React, { useEffect } from "react";
import styled from "styled-components";
import { uploadNewVideoFile } from "../../../../../services/blocks/Video/video";
import { getActivityBlockMediaDirectory } from "@services/media/media";
import { UploadIcon } from "@radix-ui/react-icons";
import { useOrg } from "@components/Contexts/OrgContext";
import { useCourse } from "@components/Contexts/CourseContext";
import { useEditorProvider } from "@components/Contexts/Editor/EditorContext";
import { NodeViewWrapper } from '@tiptap/react'
import { Loader, Video } from 'lucide-react'
import React, { useEffect } from 'react'
import styled from 'styled-components'
import { uploadNewVideoFile } from '../../../../../services/blocks/Video/video'
import { getActivityBlockMediaDirectory } from '@services/media/media'
import { UploadIcon } from '@radix-ui/react-icons'
import { useOrg } from '@components/Contexts/OrgContext'
import { useCourse } from '@components/Contexts/CourseContext'
import { useEditorProvider } from '@components/Contexts/Editor/EditorContext'
function VideoBlockComponents(props: any) {
const org = useOrg() as any;
const course = useCourse() as any;
const editorState = useEditorProvider() as any;
const isEditable = editorState.isEditable;
const [video, setVideo] = React.useState(null);
const [isLoading, setIsLoading] = React.useState(false);
const [blockObject, setblockObject] = React.useState(props.node.attrs.blockObject);
const fileId = blockObject ? `${blockObject.content.file_id}.${blockObject.content.file_format}` : null;
const org = useOrg() as any
const course = useCourse() as any
const editorState = useEditorProvider() as any
const isEditable = editorState.isEditable
const [video, setVideo] = React.useState(null)
const [isLoading, setIsLoading] = React.useState(false)
const [blockObject, setblockObject] = React.useState(
props.node.attrs.blockObject
)
const fileId = blockObject
? `${blockObject.content.file_id}.${blockObject.content.file_format}`
: null
const handleVideoChange = (event: React.ChangeEvent<any>) => {
setVideo(event.target.files[0]);
};
setVideo(event.target.files[0])
}
const handleSubmit = async (e: any) => {
e.preventDefault();
setIsLoading(true);
let object = await uploadNewVideoFile(video, props.extension.options.activity.activity_uuid);
setIsLoading(false);
setblockObject(object);
e.preventDefault()
setIsLoading(true)
let object = await uploadNewVideoFile(
video,
props.extension.options.activity.activity_uuid
)
setIsLoading(false)
setblockObject(object)
props.updateAttributes({
blockObject: object,
});
};
useEffect(() => {
})
}
, [course, org]);
useEffect(() => {}, [course, org])
return (
<NodeViewWrapper className="block-video">
{!blockObject && (
<BlockVideoWrapper className="flex items-center space-x-3 py-7 bg-gray-50 rounded-xl text-gray-900 px-3 border-dashed border-gray-150 border-2" contentEditable={isEditable}>
<BlockVideoWrapper
className="flex items-center space-x-3 py-7 bg-gray-50 rounded-xl text-gray-900 px-3 border-dashed border-gray-150 border-2"
contentEditable={isEditable}
>
{isLoading ? (
<Loader className="animate-spin animate-pulse text-gray-200" size={50} />
<Loader
className="animate-spin animate-pulse text-gray-200"
size={50}
/>
) : (
<>
<div>
<Video className="text-gray-200" size={50} />
</div>
<input className="p-3 rounded-lg file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 hover:file:cursor-pointer file:bg-gray-200 cursor-pointer file:text-gray-500" onChange={handleVideoChange} type="file" name="" id="" />
<button className='p-2 px-3 bg-gray-200 rounded-lg text-gray-500 hover:bg-gray-300 transition space-x-2 items-center flex' onClick={handleSubmit}><UploadIcon></UploadIcon><p>Submit</p></button>
<input
className="p-3 rounded-lg file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 hover:file:cursor-pointer file:bg-gray-200 cursor-pointer file:text-gray-500"
onChange={handleVideoChange}
type="file"
name=""
id=""
/>
<button
className="p-2 px-3 bg-gray-200 rounded-lg text-gray-500 hover:bg-gray-300 transition space-x-2 items-center flex"
onClick={handleSubmit}
>
<UploadIcon></UploadIcon>
<p>Submit</p>
</button>
</>
)}
</BlockVideoWrapper>
@ -60,31 +83,33 @@ function VideoBlockComponents(props: any) {
<video
controls
className="rounded-lg shadow h-96 w-full object-scale-down bg-black"
src={`${getActivityBlockMediaDirectory(org?.org_uuid,
src={`${getActivityBlockMediaDirectory(
org?.org_uuid,
course?.courseStructure.course_uuid,
props.extension.options.activity.activity_uuid,
blockObject.block_uuid,
blockObject ? fileId : ' ', 'videoBlock')}`}
blockObject ? fileId : ' ',
'videoBlock'
)}`}
></video>
</BlockVideo>
)}
</NodeViewWrapper>
);
)
}
const BlockVideoWrapper = styled.div`
//border: ${(props) => (props.contentEditable ? "2px dashed #713f1117" : "none")};
//border: ${(props) =>
props.contentEditable ? '2px dashed #713f1117' : 'none'};
// center
align-items: center;
justify-content: center;
text-align: center;
font-size: 14px;
`;
`
const BlockVideo = styled.div`
display: flex;
flex-direction: column;
`;
export default VideoBlockComponents;
`
export default VideoBlockComponents