feat: refactors + various UI changes

This commit is contained in:
swve 2023-06-22 18:26:25 +02:00
parent d5ad9e2f2f
commit f6d50627bd
69 changed files with 138 additions and 130 deletions

View file

@ -0,0 +1,113 @@
import React, { useState } from "react";
import { ArrowLeftIcon, Cross1Icon } from "@radix-ui/react-icons";
import DynamicPageActivityImage from "public/activities_types/dynamic-page-activity.png";
import VideoPageActivityImage from "public//activities_types/video-page-activity.png";
import DocumentPdfPageActivityImage from "public//activities_types/documentpdf-page-activity.png";
import { styled, keyframes } from '@stitches/react';
import DynamicCanvaModal from "./NewActivityModal/DynamicCanva";
import VideoModal from "./NewActivityModal/Video";
import Image from "next/image";
import DocumentPdfModal from "./NewActivityModal/DocumentPdf";
function NewActivityModal({ closeModal, submitActivity, submitFileActivity, submitExternalVideo, chapterId }: any) {
const [selectedView, setSelectedView] = useState("home");
return (
<div>
{selectedView === "home" && (
<ActivityChooserWrapper>
<ActivityOption onClick={() => { setSelectedView("dynamic") }}>
<ActivityTypeImage>
<Image alt="Dynamic Page" src={DynamicPageActivityImage}></Image>
</ActivityTypeImage>
<ActivityTypeTitle>Dynamic Page</ActivityTypeTitle>
</ActivityOption>
<ActivityOption onClick={() => { setSelectedView("video") }}>
<ActivityTypeImage>
<Image alt="Video Page" src={VideoPageActivityImage}></Image>
</ActivityTypeImage>
<ActivityTypeTitle>Video Page</ActivityTypeTitle>
</ActivityOption>
<ActivityOption onClick={() => { setSelectedView("documentpdf") }}>
<ActivityTypeImage>
<Image alt="Document PDF Page" src={DocumentPdfPageActivityImage}></Image>
</ActivityTypeImage>
<ActivityTypeTitle>PDF Document Page</ActivityTypeTitle>
</ActivityOption>
</ActivityChooserWrapper>
)}
{selectedView === "dynamic" && (
<DynamicCanvaModal submitActivity={submitActivity} chapterId={chapterId} />
)}
{selectedView === "video" && (
<VideoModal submitFileActivity={submitFileActivity} submitExternalVideo={submitExternalVideo}
chapterId={chapterId} />
)}
{selectedView === "documentpdf" && (
<DocumentPdfModal submitFileActivity={submitFileActivity} chapterId={chapterId} />
)}
</div>
);
}
const ActivityChooserWrapper = styled("div", {
display: "flex",
flexDirection: "row",
justifyContent: "start",
marginTop: 10,
});
const ActivityOption = styled("div", {
width: "180px",
textAlign: "center",
borderRadius: 10,
background: "#F6F6F6",
border: "4px solid #F5F5F5",
margin: "auto",
// hover
"&:hover": {
cursor: "pointer",
background: "#ededed",
border: "4px solid #ededed",
transition: "background 0.2s ease-in-out, border 0.2s ease-in-out",
},
});
const ActivityTypeImage = styled("div", {
height: 80,
borderRadius: 8,
margin: 2,
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "end",
textAlign: "center",
background: "#ffffff",
// hover
"&:hover": {
cursor: "pointer",
},
});
const ActivityTypeTitle = styled("div", {
display: "flex",
fontSize: 12,
height: "20px",
fontWeight: 500,
color: "rgba(0, 0, 0, 0.38);",
// center text vertically
alignItems: "center",
justifyContent: "center",
textAlign: "center",
});
export default NewActivityModal;

View file

@ -0,0 +1,58 @@
import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, FormMessage, Input, Textarea } from "@components/StyledElements/Form/Form";
import React, { useState } from "react";
import * as Form from '@radix-ui/react-form';
import BarLoader from "react-spinners/BarLoader";
function DocumentPdfModal({ submitFileActivity, chapterId }: any) {
const [documentpdf, setDocumentPdf] = React.useState(null) as any;
const [isSubmitting, setIsSubmitting] = useState(false);
const [name, setName] = React.useState("");
const handleDocumentPdfChange = (event: React.ChangeEvent<any>) => {
setDocumentPdf(event.target.files[0]);
};
const handleNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setName(event.target.value);
};
const handleSubmit = async (e: any) => {
e.preventDefault();
setIsSubmitting(true);
let status = await submitFileActivity(documentpdf, "documentpdf", { name, type: "documentpdf" }, chapterId);
setIsSubmitting(false);
};
return (
<FormLayout onSubmit={handleSubmit}>
<FormField name="documentpdf-activity-name">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel>PDF Document name</FormLabel>
<FormMessage match="valueMissing">Please provide a name for your PDF Document activity</FormMessage>
</Flex>
<Form.Control asChild>
<Input onChange={handleNameChange} type="text" required />
</Form.Control>
</FormField>
<FormField name="documentpdf-activity-file">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel>PDF Document file</FormLabel>
<FormMessage match="valueMissing">Please provide a PDF Document for your activity</FormMessage>
</Flex>
<Form.Control asChild>
<input type="file" onChange={handleDocumentPdfChange} required />
</Form.Control>
</FormField>
<Flex css={{ marginTop: 25, justifyContent: 'flex-end' }}>
<Form.Submit asChild>
<ButtonBlack type="submit" css={{ marginTop: 10 }}>
{isSubmitting ? <BarLoader cssOverride={{borderRadius:60,}} width={60} color="#ffffff" /> : "Create activity"}
</ButtonBlack>
</Form.Submit>
</Flex>
</FormLayout>
);
}
export default DocumentPdfModal;

View file

@ -0,0 +1,63 @@
import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, FormMessage, Input, Textarea } from "@components/StyledElements/Form/Form";
import React, { useState } from "react";
import * as Form from '@radix-ui/react-form';
import BarLoader from "react-spinners/BarLoader";
function DynamicCanvaModal({ submitActivity, chapterId }: any) {
const [activityName, setActivityName] = useState("");
const [activityDescription, setActivityDescription] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
const handleActivityNameChange = (e: any) => {
setActivityName(e.target.value);
};
const handleActivityDescriptionChange = (e: any) => {
setActivityDescription(e.target.value);
};
const handleSubmit = async (e: any) => {
e.preventDefault();
setIsSubmitting(true);
await submitActivity({
name: activityName,
chapterId: chapterId,
type: "dynamic",
org_id : "test",
});
setIsSubmitting(false);
};
return (
<FormLayout onSubmit={handleSubmit}>
<FormField name="dynamic-activity-name">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel>Activity name</FormLabel>
<FormMessage match="valueMissing">Please provide a name for your activity</FormMessage>
</Flex>
<Form.Control asChild>
<Input onChange={handleActivityNameChange} type="text" required />
</Form.Control>
</FormField>
<FormField name="dynamic-activity-desc">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel>Activity description</FormLabel>
<FormMessage match="valueMissing">Please provide a description for your activity</FormMessage>
</Flex>
<Form.Control asChild>
<Textarea onChange={handleActivityDescriptionChange} required />
</Form.Control>
</FormField>
<Flex css={{ marginTop: 25, justifyContent: 'flex-end' }}>
<Form.Submit asChild>
<ButtonBlack type="submit" css={{ marginTop: 10 }}>
{isSubmitting ? <BarLoader cssOverride={{borderRadius:60,}} width={60} color="#ffffff" />
: "Create activity"}
</ButtonBlack>
</Form.Submit>
</Flex>
</FormLayout>
);
}
export default DynamicCanvaModal;

View file

@ -0,0 +1,111 @@
import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, FormMessage, Input, Textarea } from "@components/StyledElements/Form/Form";
import React, { useState } from "react";
import * as Form from '@radix-ui/react-form';
import BarLoader from "react-spinners/BarLoader";
import { Youtube } from "lucide-react";
interface ExternalVideoObject {
name: string,
type: string,
uri: string
}
function VideoModal({ submitFileActivity, submitExternalVideo, chapterId }: any) {
const [video, setVideo] = React.useState(null) as any;
const [isSubmitting, setIsSubmitting] = useState(false);
const [name, setName] = React.useState("");
const [youtubeUrl, setYoutubeUrl] = React.useState("");
const [selectedView, setSelectedView] = React.useState("file") as any;
const handleVideoChange = (event: React.ChangeEvent<any>) => {
setVideo(event.target.files[0]);
};
const handleNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setName(event.target.value);
};
const handleYoutubeUrlChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setYoutubeUrl(event.target.value);
};
const handleSubmit = async (e: any) => {
e.preventDefault();
setIsSubmitting(true);
if (selectedView === "file") {
let status = await submitFileActivity(video, "video", { name, type: "video" }, chapterId);
setIsSubmitting(false);
}
if (selectedView === "youtube") {
let external_video_object: ExternalVideoObject = {
name,
type: "youtube",
uri: youtubeUrl
}
let status = await submitExternalVideo(external_video_object, 'activity' ,chapterId);
setIsSubmitting(false);
}
};
/* TODO : implement some sort of progress bar for file uploads, it is not possible yet because i'm not using axios.
and the actual upload isn't happening here anyway, it's in the submitFileActivity function */
return (
<FormLayout onSubmit={handleSubmit}>
<FormField name="video-activity-name">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel>Video name</FormLabel>
<FormMessage match="valueMissing">Please provide a name for your video activity</FormMessage>
</Flex>
<Form.Control asChild>
<Input onChange={handleNameChange} type="text" required />
</Form.Control>
</FormField>
<div className="flex flex-col rounded-md bg-gray-50 outline-dashed outline-gray-200">
<div className="">
<div className="flex m-4 justify-center space-x-2 mb-0">
<div onClick={() => { setSelectedView("file") }} className="rounded-full bg-slate-900 text-zinc-50 py-2 px-4 text-sm drop-shadow-md hover:cursor-pointer hover:bg-slate-700 ">Video upload</div>
<div onClick={() => { setSelectedView("youtube") }} className="rounded-full bg-slate-900 text-zinc-50 py-2 px-4 text-sm drop-shadow-md hover:cursor-pointer hover:bg-slate-700">YouTube Video</div>
</div>
{selectedView === "file" && (<div className="p-4 justify-center m-auto align-middle">
<FormField name="video-activity-file">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel>Video file</FormLabel>
<FormMessage match="valueMissing">Please provide a video for your activity</FormMessage>
</Flex>
<Form.Control asChild>
<input type="file" onChange={handleVideoChange} required />
</Form.Control>
</FormField>
</div>)}
{selectedView === "youtube" && (
<div className="p-4 justify-center m-auto align-middle">
<FormField name="video-activity-file">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel className="flex justify-center align-middle"><Youtube className="m-auto pr-1" /><span className="flex">YouTube URL</span></FormLabel>
<FormMessage match="valueMissing">Please provide a video for your activity</FormMessage>
</Flex>
<Form.Control asChild>
<Input className="bg-white" onChange={handleYoutubeUrlChange} type="text" required />
</Form.Control>
</FormField>
</div>
)}
</div>
</div>
<Flex css={{ marginTop: 25, justifyContent: 'flex-end' }}>
<Form.Submit asChild>
<ButtonBlack className="bg-black" type="submit" css={{ marginTop: 10 }}>
{isSubmitting ? <BarLoader cssOverride={{ borderRadius: 60, }} width={60} color="#ffffff" /> : "Create activity"}
</ButtonBlack>
</Form.Submit>
</Flex>
</FormLayout>
);
}
export default VideoModal;

View file

@ -0,0 +1,61 @@
import FormLayout, { Flex, FormField, Input, Textarea, FormLabel, ButtonBlack } from "@components/StyledElements/Form/Form";
import { FormMessage } from "@radix-ui/react-form";
import * as Form from '@radix-ui/react-form';
import React, { useState } from "react";
import BarLoader from "react-spinners/BarLoader";
function NewChapterModal({ submitChapter, closeModal }: any) {
const [chapterName, setChapterName] = useState("");
const [chapterDescription, setChapterDescription] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
const handleChapterNameChange = (e: any) => {
setChapterName(e.target.value);
};
const handleChapterDescriptionChange = (e: any) => {
setChapterDescription(e.target.value);
};
const handleSubmit = async (e: any) => {
e.preventDefault();
console.log({ chapterName, chapterDescription });
setIsSubmitting(true);
await submitChapter({ name: chapterName, description: chapterDescription, activities: [] });
setIsSubmitting(false);
};
return (
<FormLayout onSubmit={handleSubmit}>
<FormField name="chapter-name">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel>Chapter name</FormLabel>
<FormMessage match="valueMissing">Please provide a chapter name</FormMessage>
</Flex>
<Form.Control asChild>
<Input onChange={handleChapterNameChange} type="text" required />
</Form.Control>
</FormField>
<FormField name="chapter-desc">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel>Chapter description</FormLabel>
<FormMessage match="valueMissing">Please provide a chapter description</FormMessage>
</Flex>
<Form.Control asChild>
<Textarea onChange={handleChapterDescriptionChange} required />
</Form.Control>
</FormField>
<Flex css={{ marginTop: 25, justifyContent: 'flex-end' }}>
<Form.Submit asChild>
<ButtonBlack type="submit" css={{ marginTop: 10 }}>
{isSubmitting ? <BarLoader cssOverride={{borderRadius:60,}} width={60} color="#ffffff" />
: "Create Chapter"}
</ButtonBlack>
</Form.Submit>
</Flex>
</FormLayout>
);
}
export default NewChapterModal;

View file

@ -0,0 +1,116 @@
import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, Input, Textarea } from '@components/StyledElements/Form/Form'
import * as Form from '@radix-ui/react-form'
import { getAPIUrl, getUriWithOrg } from '@services/config/config';
import { FormMessage } from "@radix-ui/react-form";
import { createNewCourse } from '@services/courses/courses';
import { getOrganizationContextInfo } from '@services/organizations/orgs';
import React, { useState } from 'react'
import { BarLoader } from 'react-spinners'
import { mutate } from 'swr';
import { revalidateTags } from '@services/utils/ts/requests';
import { useRouter } from 'next/navigation';
function CreateCourseModal({ closeModal, orgslug }: any) {
const [isSubmitting, setIsSubmitting] = useState(false);
const [name, setName] = React.useState("");
const [description, setDescription] = React.useState("");
const [isLoading, setIsLoading] = React.useState(false);
const [thumbnail, setThumbnail] = React.useState(null) as any;
const router = useRouter();
const [orgId, setOrgId] = React.useState(null) as any;
const getOrgMetadata = async () => {
const org = await getOrganizationContextInfo(orgslug, { revalidate: 360, tags: ['organizations'] });
setOrgId(org.org_id);
}
const handleNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setName(event.target.value);
};
const handleDescriptionChange = (event: React.ChangeEvent<any>) => {
setDescription(event.target.value);
};
const handleThumbnailChange = (event: React.ChangeEvent<any>) => {
setThumbnail(event.target.files[0]);
};
const handleSubmit = async (e: any) => {
e.preventDefault();
setIsSubmitting(true);
let status = await createNewCourse(orgId, { name, description }, thumbnail);
revalidateTags(['courses']);
setIsSubmitting(false);
if (status.org_id == orgId) {
closeModal();
router.refresh();
} else {
alert("Error creating course, please see console logs");
console.log(status);
}
};
React.useEffect(() => {
if (orgslug) {
getOrgMetadata();
}
}, [isLoading, orgslug]);
return (
<FormLayout onSubmit={handleSubmit}>
<FormField name="course-name">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel>Course name</FormLabel>
<FormMessage match="valueMissing">Please provide a course name</FormMessage>
</Flex>
<Form.Control asChild>
<Input onChange={handleNameChange} type="text" required />
</Form.Control>
</FormField>
<FormField name="course-desc">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel>Course description</FormLabel>
<FormMessage match="valueMissing">Please provide a course description</FormMessage>
</Flex>
<Form.Control asChild>
<Textarea onChange={handleDescriptionChange} required />
</Form.Control>
</FormField>
<FormField name="course-thumbnail">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel>Course thumbnail</FormLabel>
<FormMessage match="valueMissing">Please provide a thumbnail for your course</FormMessage>
</Flex>
<Form.Control asChild>
<Input onChange={handleThumbnailChange} type="file" required />
</Form.Control>
</FormField>
<FormField name="course-learnings">
<Flex css={{ alignItems: 'baseline', justifyContent: 'space-between' }}>
<FormLabel>Course Learnings</FormLabel>
<FormMessage match="valueMissing">Please provide learning elements, separated by comma (,)</FormMessage>
</Flex>
<Form.Control asChild>
<Textarea required />
</Form.Control>
</FormField>
<Flex css={{ marginTop: 25, justifyContent: 'flex-end' }}>
<Form.Submit asChild>
<ButtonBlack type="submit" css={{ marginTop: 10 }}>
{isSubmitting ? <BarLoader cssOverride={{ borderRadius: 60, }} width={60} color="#ffffff" />
: "Create Course"}
</ButtonBlack>
</Form.Submit>
</Flex>
</FormLayout>
)
}
export default CreateCourseModal