feat: frontend create & delete collections

This commit is contained in:
swve 2022-12-22 21:18:12 +01:00
parent bca6c3000a
commit ce785fd078
6 changed files with 255 additions and 9 deletions

View file

@ -17,9 +17,7 @@ export const Menu = () => {
<Logo> <Logo>
<Image width={25} height={25} src={learnhouseIcon} alt="" /> <Image width={25} height={25} src={learnhouseIcon} alt="" />
<Link href={"/"}> <Link href={"/"}>
<Image width={108} height={28} src={learnhouseLogo} alt="" /> <Image width={108} height={28} src={learnhouseLogo} alt="" />
</Link> </Link>
</Logo> </Logo>
<div id="accounts"></div> <div id="accounts"></div>
@ -32,11 +30,11 @@ export const Menu = () => {
<MenuArea> <MenuArea>
<ul> <ul>
<li> <li>
<Link href={"/org/" + orgslug + "/courses"}> <Link href={"/org/" + orgslug + "/courses"}>Courses</Link>
Courses </li>
</Link> <li>
<Link href={"/org/" + orgslug + "/collections"}>Collections</Link>
</li> </li>
<li>Collections</li>
<li>Activity</li> <li>Activity</li>
<li>More</li> <li>More</li>
</ul> </ul>

View file

@ -0,0 +1,78 @@
import Layout from "../../../../components/UI/Layout";
import Link from "next/link";
import { useRouter } from "next/router";
import React from "react";
import styled from "styled-components";
import { Title } from "../../../../components/UI/Elements/Styles/Title";
import { deleteCollection, getOrgCollections } from "../../../../services/collections";
import { getOrganizationContextInfo } from "../../../../services/orgs";
function Collections() {
const router = useRouter();
const { orgslug } = router.query;
const [isLoading, setIsLoading] = React.useState(true);
const [collections, setCollections] = React.useState([]);
async function fetchCollections() {
setIsLoading(true);
const org = await getOrganizationContextInfo(orgslug);
const collections = await getOrgCollections(org.org_id);
setCollections(collections);
setIsLoading(false);
}
async function deleteCollectionAndFetch(collectionId: number) {
setIsLoading(true);
await deleteCollection(collectionId);
await fetchCollections();
setIsLoading(false);
}
React.useEffect(() => {
fetchCollections();
}, []);
return (
<Layout>
<Title>
{orgslug} Collections :{" "}
<Link href={"/org/" + orgslug + "/collections/new"}>
<button>+</button>
</Link>{" "}
</Title>
{isLoading ? (
<div>Loading...</div>
) : (
<div>
{collections.map((collection: any) => (
<CollectionItem key={collection.collection_id}>
<Link href={"/org/" + orgslug + "/collections/" + collection.collection_id}>{collection.name}</Link>
<button onClick={() => deleteCollectionAndFetch(collection.collection_id)}>Delete</button>
</CollectionItem>
))}
</div>
)}
</Layout>
);
}
const CollectionItem = styled.div`
display: flex;
flex-direction: column;
place-items: center;
width: 100%;
height: 100%;
padding: 10px;
border: 1px solid #e5e5e5;
border-radius: 5px;
box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.03);
background: #ffffff;
cursor: pointer;
transition: all 0.2s ease-in-out;
&:hover {
box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.1);
}
`;
export default Collections;

View file

@ -0,0 +1,95 @@
import { useRouter } from "next/router";
import React from "react";
import { Title } from "../../../../../components/UI/Elements/Styles/Title";
import Layout from "../../../../../components/UI/Layout";
import { getOrganizationContextInfo } from "../../../../../services/orgs";
import { getOrgCourses } from "../../../../../services/courses/courses";
import { createCollection } from "../../../../../services/collections";
function NewCollection() {
const router = useRouter();
const { orgslug } = router.query;
const [name, setName] = React.useState("");
const [org, setOrg] = React.useState({}) as any;
const [description, setDescription] = React.useState("");
const [selectedCourses, setSelectedCourses] = React.useState([]) as any;
const [courses, setCourses] = React.useState([]) as any;
const [isLoading, setIsLoading] = React.useState(false);
async function getCourses() {
setIsLoading(true);
const org = await getOrganizationContextInfo(orgslug);
setOrg(org);
const courses = await getOrgCourses(org.org_id);
setCourses(courses);
setIsLoading(false);
}
const handleNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setName(event.target.value);
};
const handleDescriptionChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setDescription(event.target.value);
};
const handleSubmit = async (e: any) => {
e.preventDefault();
console.log("selectedCourses", selectedCourses);
const collection = {
name: name,
description: description,
courses: selectedCourses,
org_id: org.org_id,
};
await createCollection(collection);
router.push("/org/" + orgslug + "/collections");
};
React.useEffect(() => {
if (router.isReady) {
getCourses();
}
return () => {};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [router.isReady]);
return (
<Layout>
<Title>Add new</Title>
<br />
<input type="text" placeholder="Name" value={name} onChange={handleNameChange} />
{isLoading ? (
<p>Loading...</p>
) : (
<div>
{courses.map((course: any) => (
<div key={course.course_id}>
<input
type="checkbox"
id={course.course_id}
name={course.course_id}
value={course.course_id}
onChange={(e) => {
if (e.target.checked) {
setSelectedCourses([...selectedCourses, e.target.value]);
} else {
setSelectedCourses(selectedCourses.filter((item: any) => item !== e.target.value));
}
}}
/>
<label htmlFor={course.course_id}>{course.name}</label>
</div>
))}
</div>
)}
<br />
<input type="text" placeholder="Description" value={description} onChange={handleDescriptionChange} />
<br />
<button onClick={handleSubmit}>Submit</button>
</Layout>
);
}
export default NewCollection;

View file

@ -0,0 +1,73 @@
import { getAPIUrl } from "./config";
export async function getOrgCollections(org_slug: any) {
const HeadersConfig = new Headers({ "Content-Type": "application/json" });
const requestOptions: any = {
method: "GET",
headers: HeadersConfig,
redirect: "follow",
credentials: "include",
};
return fetch(`${getAPIUrl()}collections/page/1/limit/10`, requestOptions)
.then((result) => result.json())
.catch((error) => console.log("error", error));
}
export async function getCollection(collection_slug: any) {
const HeadersConfig = new Headers({ "Content-Type": "application/json" });
const requestOptions: any = {
method: "GET",
headers: HeadersConfig,
redirect: "follow",
credentials: "include",
};
return fetch(
`${getAPIUrl()}collections/${collection_slug}`,
requestOptions
)
.then((result) => result.json())
.catch((error) => console.log("error", error));
}
export async function deleteCollection(collection_id: any) {
const HeadersConfig = new Headers({ "Content-Type": "application/json" });
const requestOptions: any = {
method: "DELETE",
headers: HeadersConfig,
redirect: "follow",
credentials: "include",
};
return fetch(
`${getAPIUrl()}collections/${collection_id}`,
requestOptions
)
.then((result) => result.json())
.catch((error) => console.log("error", error));
}
// Create a new collection
export async function createCollection(collection: any) {
const HeadersConfig = new Headers({ "Content-Type": "application/json" });
const requestOptions: any = {
method: "POST",
headers: HeadersConfig,
redirect: "follow",
credentials: "include",
body: JSON.stringify(collection),
};
return fetch(`${getAPIUrl()}collections/`, requestOptions)
.then((result) => result.json())
.catch((error) => console.log("error", error));
}

View file

@ -238,7 +238,7 @@ async def verify_rights(course_id: str, current_user: PublicUser, action: str):
if not course: if not course:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail=f"Course/CourseChapter does not exist") status_code=status.HTTP_409_CONFLICT, detail=f"Course does not exist")
hasRoleRights = await verify_user_rights_with_roles(action, current_user.user_id, course_id) hasRoleRights = await verify_user_rights_with_roles(action, current_user.user_id, course_id)
isAuthor = current_user.user_id in course["authors"] isAuthor = current_user.user_id in course["authors"]

View file

@ -15,6 +15,7 @@ class Collection(BaseModel):
name: str name: str
description: str description: str
courses: List[str] # course_id courses: List[str] # course_id
org_id: str # org_id
class CollectionInDB(Collection): class CollectionInDB(Collection):
@ -51,7 +52,8 @@ async def create_collection(collection_object: Collection, current_user: PublicU
# find if collection already exists using name # find if collection already exists using name
isCollectionNameAvailable = collections.find_one({"name": collection_object.name}) isCollectionNameAvailable = collections.find_one({"name": collection_object.name})
await verify_collection_rights("*", current_user, "create") # TODO
# await verify_collection_rights("*", current_user, "create")
if isCollectionNameAvailable: if isCollectionNameAvailable:
raise HTTPException( raise HTTPException(
@ -139,7 +141,7 @@ async def verify_collection_rights(collection_id: str, current_user: PublicUser
collection = collections.find_one({"collection_id": collection_id}) collection = collections.find_one({"collection_id": collection_id})
if not collection: if not collection and action != "create":
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist") status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist")