Merge pull request #96 from learnhouse/swve/eng-73-fix-ui-imperfections

Fix UI Imperfections
This commit is contained in:
Badr B 2023-06-22 20:49:46 +02:00 committed by GitHub
commit 5eaa235a92
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
80 changed files with 691 additions and 980 deletions

View file

@ -6,7 +6,7 @@ export async function GET(request: NextRequest) {
revalidateTag(tag); revalidateTag(tag);
return NextResponse.json( return NextResponse.json(
{ revalidated: true, now: Date.now() }, { revalidated: true, now: Date.now(), tag },
{ {
status: 200, status: 200,
headers: { headers: {

View file

@ -1,4 +1,4 @@
import PageLoading from "@components/Pages/PageLoading"; import PageLoading from "@components/Objects/Loaders/PageLoading";
export default function Loading() { export default function Loading() {
// Or a custom loading skeleton component // Or a custom loading skeleton component

View file

@ -1,7 +1,7 @@
import { default as React, } from "react"; import { default as React, } from "react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import AuthProvider from "@components/Security/AuthProvider"; import AuthProvider from "@components/Security/AuthProvider";
import EditorWrapper from "@components/Editor/EditorWrapper"; import EditorWrapper from "@components/Objects/Editor/EditorWrapper";
import { getAPIUrl } from "@services/config/config"; import { getAPIUrl } from "@services/config/config";
import { swrFetcher } from "@services/utils/ts/requests"; import { swrFetcher } from "@services/utils/ts/requests";
import { getOrganizationContextInfo } from "@services/organizations/orgs"; import { getOrganizationContextInfo } from "@services/organizations/orgs";

View file

@ -1,6 +1,6 @@
"use client"; "use client";
import "../styles/globals.css"; import "../styles/globals.css";
import StyledComponentsRegistry from "../components/UI/libs/styled-registry"; import StyledComponentsRegistry from "../components/Utils/libs/styled-registry";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
export default function RootLayout({ children }: { children: React.ReactNode }) { export default function RootLayout({ children }: { children: React.ReactNode }) {

View file

@ -1,6 +1,5 @@
"use client"; "use client";
import React from "react"; import React from "react";
import { Title } from "../../../components/UI/Elements/Styles/Title";
import { createNewOrganization } from "../../../services/organizations/orgs"; import { createNewOrganization } from "../../../services/organizations/orgs";
const Organizations = () => { const Organizations = () => {
@ -35,7 +34,7 @@ const Organizations = () => {
return ( return (
<div> <div>
<Title>New Organization</Title> <div className="font-bold text-lg">New Organization</div>
Name: <input onChange={handleNameChange} type="text" /> Name: <input onChange={handleNameChange} type="text" />
<br /> <br />
Description: <input onChange={handleDescriptionChange} type="text" /> Description: <input onChange={handleDescriptionChange} type="text" />

View file

@ -1,7 +1,6 @@
"use client"; //todo: use server components "use client"; //todo: use server components
import Link from "next/link"; import Link from "next/link";
import React from "react"; import React from "react";
import { Title } from "../../components/UI/Elements/Styles/Title";
import { deleteOrganizationFromBackend } from "@services/organizations/orgs"; import { deleteOrganizationFromBackend } from "@services/organizations/orgs";
import useSWR, { mutate } from "swr"; import useSWR, { mutate } from "swr";
import { swrFetcher } from "@services/utils/ts/requests"; import { swrFetcher } from "@services/utils/ts/requests";
@ -19,14 +18,14 @@ const Organizations = () => {
return ( return (
<> <>
<AuthProvider /> <AuthProvider />
<Title> <div className="font-bold text-lg">
Your Organizations{" "} Your Organizations{" "}
<Link href="/organizations/new"> <Link href="/organizations/new">
<button className="bg-blue-500 text-white px-2 py-1 rounded-md hover:bg-blue-600 focus:outline-none"> <button className="bg-blue-500 text-white px-2 py-1 rounded-md hover:bg-blue-600 focus:outline-none">
+ +
</button> </button>
</Link> </Link>
</Title> </div>
<hr /> <hr />
{error && <p className="text-red-500">Failed to load</p>} {error && <p className="text-red-500">Failed to load</p>}

View file

@ -1,6 +1,6 @@
'use client'; // Error components must be Client Components 'use client'; // Error components must be Client Components
import ErrorUI from '@components/UI/Error/Error'; import ErrorUI from '@components/StyledElements/Error/Error';
import { useEffect } from 'react'; import { useEffect } from 'react';
export default function Error({ export default function Error({

View file

@ -1,4 +1,4 @@
import PageLoading from "@components/Pages/PageLoading"; import PageLoading from "@components/Objects/Loaders/PageLoading";
export default function Loading() { export default function Loading() {
return ( return (

View file

@ -1,3 +1,4 @@
import GeneralWrapperStyled from "@components/StyledElements/Wrappers/GeneralWrapper";
import { getBackendUrl, getUriWithOrg } from "@services/config/config"; import { getBackendUrl, getUriWithOrg } from "@services/config/config";
import { getCollectionByIdWithAuthHeader } from "@services/courses/collections"; import { getCollectionByIdWithAuthHeader } from "@services/courses/collections";
import { getOrganizationContextInfo } from "@services/organizations/orgs"; import { getOrganizationContextInfo } from "@services/organizations/orgs";
@ -38,7 +39,7 @@ const CollectionPage = async (params : any) => {
} }
return <div className="max-w-7xl mx-auto px-4 py-10" > return <GeneralWrapperStyled>
<h2 className="text-sm font-bold text-gray-400">Collection</h2> <h2 className="text-sm font-bold text-gray-400">Collection</h2>
<h1 className="text-3xl font-bold">{col.name}</h1> <h1 className="text-3xl font-bold">{col.name}</h1>
<br /> <br />
@ -56,7 +57,7 @@ const CollectionPage = async (params : any) => {
</div>; </GeneralWrapperStyled>;
}; };
export default CollectionPage; export default CollectionPage;

View file

@ -1,70 +1,34 @@
'use client'; 'use client';
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement';
import { AuthContext } from '@components/Security/AuthProvider'; import { AuthContext } from '@components/Security/AuthProvider';
import { getUriWithOrg } from '@services/config/config';
import { deleteCollection } from '@services/courses/collections'; import { deleteCollection } from '@services/courses/collections';
import { revalidateTags } from '@services/utils/ts/requests'; import { revalidateTags } from '@services/utils/ts/requests';
import { Link, Trash } from 'lucide-react'; import { Link, Trash } from 'lucide-react';
import { useRouter } from 'next/navigation';
import React from 'react' import React from 'react'
const CollectionAdminEditsArea = (props: any) => { const CollectionAdminEditsArea = (props: any) => {
const org_roles_values = ["admin", "owner"]; const router = useRouter();
const user_roles_values = ["role_admin"];
const auth: any = React.useContext(AuthContext);
// this is amazingly terrible code, but gotta release that MVP
// TODO: fix this
function isAuthorized() {
const org_id = props.collection.org_id;
const org_roles = auth.userInfo.user_object.orgs;
const user_roles = auth.userInfo.user_object.roles;
const org_role = org_roles.find((org: any) => org.org_id == org_id);
const user_role = user_roles.find((role: any) => role.org_id == org_id);
if (org_role && user_role) {
if (org_roles_values.includes(org_role.org_role) && user_roles_values.includes(user_role.role_id)) {
return true;
}
else {
return false;
}
} else {
return false;
}
}
const deleteCollectionUI = async (collectionId: number) => { const deleteCollectionUI = async (collectionId: number) => {
await deleteCollection(collectionId); await deleteCollection(collectionId);
revalidateTags(["collections"]); revalidateTags(["collections"]);
// reload the page // reload the page
window.location.reload(); router.refresh();
router.push(getUriWithOrg(props.orgslug, "/collections"));
} }
// this is amazingly terrible code, but gotta release that MVP return (
// TODO: fix this <AuthenticatedClientElement orgId={props.org_id} checkMethod='roles'>
<div className="flex space-x-2 py-2">
if (auth.isAuthenticated) { <button className="rounded-md text-sm px-3 font-bold text-red-800 bg-red-200 w-16 flex justify-center items-center" onClick={() => deleteCollectionUI(props.collection_id)}>
if (isAuthorized()) { Delete <Trash size={10}></Trash>
return ( </button>
<div className="flex space-x-2 py-2"> </div>
<button className="rounded-md text-sm px-3 font-bold text-red-800 bg-red-200 w-16 flex justify-center items-center" onClick={() => deleteCollectionUI(props.collection_id)}> </AuthenticatedClientElement>
Delete <Trash size={10}></Trash> )
</button>
</div>
)
} else {
return (
<div></div>
)
}
}
else {
return (
<div></div>
)
}
} }
export default CollectionAdminEditsArea; export default CollectionAdminEditsArea;

View file

@ -1,4 +1,4 @@
import PageLoading from "@components/Pages/PageLoading"; import PageLoading from "@components/Objects/Loaders/PageLoading";
export default function Loading() { export default function Loading() {
// Or a custom loading skeleton component // Or a custom loading skeleton component

View file

@ -1,7 +1,6 @@
"use client"; "use client";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import React from "react"; import React from "react";
import { Title } from "@components/UI/Elements/Styles/Title";
import { createCollection } from "@services/courses/collections"; import { createCollection } from "@services/courses/collections";
import useSWR from "swr"; import useSWR from "swr";
import { getAPIUrl, getUriWithOrg } from "@services/config/config"; import { getAPIUrl, getUriWithOrg } from "@services/config/config";
@ -45,14 +44,16 @@ function NewCollection(params: any) {
}; };
await createCollection(collection); await createCollection(collection);
revalidateTags(["collections"]); revalidateTags(["collections"]);
router.prefetch(getUriWithOrg(orgslug, "/collections"));
router.push(getUriWithOrg(orgslug, "/collections")); router.push(getUriWithOrg(orgslug, "/collections"));
router.refresh();
}; };
return ( return (
<> <>
<div className="w-64 m-auto py-20"> <div className="w-64 m-auto py-20">
<Title className="mb-4">Add new</Title> <div className="font-bold text-lg mb-4">Add new</div>
<input <input
type="text" type="text"

View file

@ -1,13 +1,12 @@
import AuthenticatedClientElement from "@components/Security/AuthenticatedClientElement";
import TypeOfContentTitle from "@components/StyledElements/Titles/TypeOfContentTitle";
import GeneralWrapperStyled from "@components/StyledElements/Wrappers/GeneralWrapper";
import { getBackendUrl, getUriWithOrg } from "@services/config/config"; import { getBackendUrl, getUriWithOrg } from "@services/config/config";
import { deleteCollection, getOrgCollectionsWithAuthHeader } from "@services/courses/collections"; import { deleteCollection, getOrgCollectionsWithAuthHeader } from "@services/courses/collections";
import { getCourseMetadataWithAuthHeader } from "@services/courses/courses";
import { getOrganizationContextInfo } from "@services/organizations/orgs"; import { getOrganizationContextInfo } from "@services/organizations/orgs";
import { revalidateTags } from "@services/utils/ts/requests";
import { Metadata } from "next"; import { Metadata } from "next";
import { revalidateTag } from "next/cache";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
import Link from "next/link"; import Link from "next/link";
import { Title } from "../courses/courses";
import CollectionAdminEditsArea from "./admin"; import CollectionAdminEditsArea from "./admin";
type MetadataProps = { type MetadataProps = {
@ -42,17 +41,19 @@ const CollectionsPage = async (params: any) => {
const collections = await getOrgCollectionsWithAuthHeader(org_id, access_token_cookie ? access_token_cookie.value : null); const collections = await getOrgCollectionsWithAuthHeader(org_id, access_token_cookie ? access_token_cookie.value : null);
return ( return (
<div className="max-w-7xl mx-auto px-4 py-10" > <GeneralWrapperStyled>
<div className="flex justify-between" > <div className="flex justify-between" >
<Title title="Collections" type="col" /> <TypeOfContentTitle title="Collections" type="col" />
<AuthenticatedClientElement checkMethod='authentication'>
<Link className="flex justify-center" href={getUriWithOrg(orgslug, "/collections/new")}> <Link className="flex justify-center" href={getUriWithOrg(orgslug, "/collections/new")}>
<button className="rounded-md bg-black antialiased ring-offset-purple-800 p-2 px-5 my-auto font text-sm font-bold text-white drop-shadow-lg">Add Collection + </button> <button className="rounded-md bg-black antialiased ring-offset-purple-800 p-2 px-5 my-auto font text-sm font-bold text-white drop-shadow-lg">Add Collection + </button>
</Link> </Link>
</AuthenticatedClientElement>
</div> </div>
<div className="home_collections flex flex-wrap"> <div className="home_collections flex flex-wrap">
{collections.map((collection: any) => ( {collections.map((collection: any) => (
<div className="pr-8 flex flex-col" key={collection.collection_id}> <div className="flex flex-col py-3 px-3" key={collection.collection_id}>
<CollectionAdminEditsArea collection_id={collection.collection_id} collection={collection} /> <CollectionAdminEditsArea org_id={org_id} collection_id={collection.collection_id} collection={collection} />
<Link href={getUriWithOrg(orgslug, "/collection/" + removeCollectionPrefix(collection.collection_id))}> <Link href={getUriWithOrg(orgslug, "/collection/" + removeCollectionPrefix(collection.collection_id))}>
<div className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-[249px] h-[180px] bg-cover flex flex-col items-center justify-center bg-indigo-600 font-bold text-zinc-50" > <div className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-[249px] h-[180px] bg-cover flex flex-col items-center justify-center bg-indigo-600 font-bold text-zinc-50" >
<h1 className="font-bold text-lg py-2 justify-center mb-2">{collection.name}</h1> <h1 className="font-bold text-lg py-2 justify-center mb-2">{collection.name}</h1>
@ -68,7 +69,7 @@ const CollectionsPage = async (params: any) => {
</div> </div>
))} ))}
</div> </div>
</div> </GeneralWrapperStyled>
); );
} }

View file

@ -1,15 +1,15 @@
"use client"; "use client";
import Link from "next/link"; import Link from "next/link";
import React, { useMemo } from "react"; import React from "react";
import { getAPIUrl, getBackendUrl, getUriWithOrg } from "@services/config/config"; import { getAPIUrl, getBackendUrl, getUriWithOrg } from "@services/config/config";
import Canva from "@components/Pages/Activities/DynamicCanva/DynamicCanva"; import Canva from "@components/Pages/Activities/DynamicCanva/DynamicCanva";
import styled from "styled-components";
import VideoActivity from "@components/Pages/Activities/Video/Video"; import VideoActivity from "@components/Pages/Activities/Video/Video";
import useSWR, { mutate } from "swr";
import { Check } from "lucide-react"; import { Check } from "lucide-react";
import { markActivityAsComplete } from "@services/courses/activity"; import { markActivityAsComplete } from "@services/courses/activity";
import ToolTip from "@components/UI/Tooltip/Tooltip";
import DocumentPdfActivity from "@components/Pages/Activities/DocumentPdf/DocumentPdf"; import DocumentPdfActivity from "@components/Pages/Activities/DocumentPdf/DocumentPdf";
import ActivityIndicators from "@components/Pages/Courses/ActivityIndicators";
import GeneralWrapperStyled from "@components/StyledElements/Wrappers/GeneralWrapper";
import { useRouter } from "next/navigation";
interface ActivityClientProps { interface ActivityClientProps {
activityid: string; activityid: string;
@ -19,6 +19,7 @@ interface ActivityClientProps {
course: any; course: any;
} }
function ActivityClient(props: ActivityClientProps) { function ActivityClient(props: ActivityClientProps) {
const activityid = props.activityid; const activityid = props.activityid;
const courseid = props.courseid; const courseid = props.courseid;
@ -26,195 +27,104 @@ function ActivityClient(props: ActivityClientProps) {
const activity = props.activity; const activity = props.activity;
const course = props.course; const course = props.course;
function getChapterName(chapterId: string) {
async function markActivityAsCompleteFront() { let chapterName = "";
const trail = await markActivityAsComplete(orgslug, courseid, activityid); course.chapters.forEach((chapter: any) => {
mutate(`${getAPIUrl()}activities/activity_${activityid}`); if (chapter.chapter_id === chapterId) {
mutate(`${getAPIUrl()}courses/meta/course_${courseid}`); chapterName = chapter.name;
}
});
return chapterName;
} }
return ( return (
<> <>
<ActivityLayout> <GeneralWrapperStyled>
<pre style={{ display: "none" }}>{JSON.stringify(activity, null, 2)}</pre> <div className="space-y-4 pt-4">
<ActivityTopWrapper> <div className="flex space-x-6">
<ActivityThumbnail> <div className="flex">
<Link href={getUriWithOrg(orgslug, "") + `/course/${courseid}`}> <Link href={getUriWithOrg(orgslug, "") + `/course/${courseid}`}>
<img src={`${getBackendUrl()}content/uploads/img/${course.course.thumbnail}`} alt="" /> <img className="w-[100px] h-[57px] rounded-md drop-shadow-md" src={`${getBackendUrl()}content/uploads/img/${course.course.thumbnail}`} alt="" />
</Link> </Link>
</ActivityThumbnail> </div>
<ActivityInfo> <div className="flex flex-col -space-y-1">
<p>Course</p> <p className="font-bold text-gray-700 text-md">Course </p>
<h1>{course.course.name}</h1> <h1 className="font-bold text-gray-950 text-2xl first-letter:uppercase" >{course.course.name}</h1>
</ActivityInfo> </div>
</ActivityTopWrapper> </div>
<ChaptersWrapper> <ActivityIndicators current_activity={activityid} orgslug={orgslug} course={course} />
{course.chapters.map((chapter: any) => {
return ( <div className="flex justify-between items-center">
<> <div className="flex flex-col -space-y-1">
<div style={{ display: "flex", flexDirection: "row" }} key={chapter.chapter_id}> <p className="font-bold text-gray-700 text-md">Chapter : {getChapterName(activity.chapter_id)}</p>
{chapter.activities.map((activity: any) => { <h1 className="font-bold text-gray-950 text-2xl first-letter:uppercase" >{activity.name}</h1>
return ( </div>
<ToolTip sideOffset={-5} slateBlack content={activity.name} key={activity.id}> <div className="flex space-x-2">
<Link href={getUriWithOrg(orgslug, "") + `/course/${courseid}/activity/${activity.id.replace("activity_", "")}`}> <MarkStatus activityid={activityid} course={course} orgslug={orgslug} courseid={courseid} />
<ChapterIndicator
done={course.trail.activities_marked_complete && course.trail.activities_marked_complete.includes(activity.id) && course.trail.status == "ongoing"} </div>
active={"activity_" + activityid === activity.id ? true : false} key={activity.id} </div>
/>
</Link>
</ToolTip>
);
})}
</div>
&nbsp;&nbsp;&nbsp;&nbsp;
</>
);
})}
</ChaptersWrapper>
{activity ? ( {activity ? (
<CourseContent> <div className={`p-7 pt-2 drop-shadow-sm rounded-lg ${activity.type == 'dynamic' ? 'bg-white' : 'bg-zinc-950'}`}>
{activity.type == "dynamic" && <Canva content={activity.content} activity={activity} />} <div>
{/* todo : use apis & streams instead of this */} {activity.type == "dynamic" && <Canva content={activity.content} activity={activity} />}
{activity.type == "video" && <VideoActivity course={course} activity={activity} />} {/* todo : use apis & streams instead of this */}
{activity.type == "video" && <VideoActivity course={course} activity={activity} />}
{activity.type == "documentpdf" && <DocumentPdfActivity course={course} activity={activity} />} {activity.type == "documentpdf" && <DocumentPdfActivity course={course} activity={activity} />}
<ActivityMarkerWrapper className="py-10"> <div className="py-10">
{course.trail.activities_marked_complete &&
course.trail.activities_marked_complete.includes("activity_" + activityid) && </div>
course.trail.status == "ongoing" ? ( </div>
<button style={{ backgroundColor: "green" }}> </div>
<i>
<Check size={20}></Check>
</i>{" "}
Already completed
</button>
) : (
<button onClick={markActivityAsCompleteFront}>
{" "}
<i>
<Check size={20}></Check>
</i>{" "}
Mark as complete
</button>
)}
</ActivityMarkerWrapper>
</CourseContent>
) : (<div></div>)} ) : (<div></div>)}
{<div style={{ height: "100px" }}></div>} {<div style={{ height: "100px" }}></div>}
</ActivityLayout> </div>
</GeneralWrapperStyled>
</> </>
); );
} }
const ActivityLayout = styled.div``;
const ActivityThumbnail = styled.div`
padding-right: 30px; export function MarkStatus(props: { activityid: string, course: any, orgslug: string, courseid: string }) {
justify-self: center; const router = useRouter();
img {
box-shadow: 0px 13px 33px -13px rgba(0, 0, 0, 0.42);
border-radius: 7px; async function markActivityAsCompleteFront() {
width: 100px; const trail = await markActivityAsComplete(props.orgslug, props.courseid, props.activityid);
height: 57px; router.refresh();
}
`;
const ActivityInfo = styled.div`
h1 {
margin-top: 0px;
} }
p { return (
margin-top: 0; <>{props.course.trail.activities_marked_complete &&
margin-bottom: 0; props.course.trail.activities_marked_complete.includes("activity_" + props.activityid) &&
font-weight: 700; props.course.trail.status == "ongoing" ? (
} <div className="bg-teal-600 rounded-md drop-shadow-md flex flex-col p-3 text-sm text-white hover:cursor-pointer transition delay-150 duration-300 ease-in-out" >
`; <i>
<Check size={15}></Check>
</i>{" "}
Already completed
</div>
) : (
<div className="bg-zinc-600 rounded-md drop-shadow-md flex flex-col p-3 text-sm text-white hover:cursor-pointer transition delay-150 duration-300 ease-in-out" onClick={markActivityAsCompleteFront}>
{" "}
<i>
<Check size={15}></Check>
</i>{" "}
Mark as complete
</div>
)}</>
)
}
const ChaptersWrapper = styled.div`
display: flex;
// row
flex-direction: row;
width: 100%;
width: 1300px;
margin: 0 auto;
`;
const ChapterIndicator = styled.div < { active?: boolean, done?: boolean } > `
border-radius: 20px;
height: 5px;
background: #151515;
border-radius: 3px;
width: 35px;
background-color: ${props => props.done ? "green" : (props.active ? "#9d9d9d" : "black")};
margin: 10px;
margin-bottom: 0px;
margin-left: 0px;
&:hover {
cursor: pointer;
background-color: #9d9d9d;
}
`;
const ActivityTopWrapper = styled.div`
width: 1300px;
padding-top: 50px;
margin: 0 auto;
display: flex;
`;
const CourseContent = styled.div`
display: flex;
flex-direction: column;
background-color: white;
min-height: 600px;
`;
const ActivityMarkerWrapper = styled.div`
display: block;
width: 1300px;
justify-content: flex-end;
margin: 0 auto;
align-items: center;
button {
background-color: #151515;
border: none;
padding: 18px;
border-radius: 15px;
margin: 15px;
margin-left: 20px;
margin-top: 20px;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
margin: auto;
color: white;
font-weight: 700;
font-family: "DM Sans";
font-size: 16px;
letter-spacing: -0.05em;
box-shadow: 0px 13px 33px -13px rgba(0, 0, 0, 0.42);
i {
margin-right: 5px;
// center the icon
display: flex;
align-items: center;
justify-content: center;
}
&:hover {
background-color: #000000;
}
}
`;
export default ActivityClient; export default ActivityClient;

View file

@ -1,6 +1,6 @@
'use client'; // Error components must be Client Components 'use client'; // Error components must be Client Components
import ErrorUI from '@components/UI/Error/Error'; import ErrorUI from '@components/StyledElements/Error/Error';
import { useEffect } from 'react'; import { useEffect } from 'react';
export default function Error({ export default function Error({

View file

@ -1,4 +1,4 @@
import PageLoading from "@components/Pages/PageLoading"; import PageLoading from "@components/Objects/Loaders/PageLoading";
export default function Loading() { export default function Loading() {
// Or a custom loading skeleton component // Or a custom loading skeleton component

View file

@ -2,68 +2,75 @@
import { EyeOpenIcon, Pencil2Icon } from "@radix-ui/react-icons"; import { EyeOpenIcon, Pencil2Icon } from "@radix-ui/react-icons";
import { removeCourse, startCourse } from "@services/courses/activity"; import { removeCourse, startCourse } from "@services/courses/activity";
import Link from "next/link"; import Link from "next/link";
import React from "react"; import React, { use } from "react";
import styled from "styled-components"; import styled from "styled-components";
import { getAPIUrl, getBackendUrl, getUriWithOrg } from "@services/config/config"; import { getAPIUrl, getBackendUrl, getUriWithOrg } from "@services/config/config";
import useSWR, { mutate } from "swr"; import useSWR, { mutate } from "swr";
import ToolTip from "@components/UI/Tooltip/Tooltip"; import ToolTip from "@components/StyledElements/Tooltip/Tooltip";
import PageLoading from "@components/Pages/PageLoading"; import PageLoading from "@components/Objects/Loaders/PageLoading";
import { revalidateTags } from "@services/utils/ts/requests"; import { revalidateTags } from "@services/utils/ts/requests";
import ActivityIndicators from "@components/Pages/Courses/ActivityIndicators";
import { useRouter } from "next/navigation";
import GeneralWrapperStyled from "@components/StyledElements/Wrappers/GeneralWrapper";
const CourseClient = (props: any) => { const CourseClient = (props: any) => {
const courseid = props.courseid; const courseid = props.courseid;
const orgslug = props.orgslug; const orgslug = props.orgslug;
const course = props.course; const course = props.course;
const router = useRouter();
async function startCourseUI() { async function startCourseUI() {
// Create activity // Create activity
await startCourse("course_" + courseid, orgslug); await startCourse("course_" + courseid, orgslug);
revalidateTags(['courses']); revalidateTags(['courses']);
router.refresh();
} }
async function quitCourse() { async function quitCourse() {
// Close activity // Close activity
let activity = await removeCourse("course_" + courseid, orgslug); let activity = await removeCourse("course_" + courseid, orgslug);
// Mutate course // Mutate course
revalidateTags(['courses']); revalidateTags(['courses']);
router.refresh();
} }
console.log(course); console.log(course);
return ( return (
<> <>
{!course ? ( {!course ? (
<PageLoading></PageLoading> <PageLoading></PageLoading>
) : ( ) : (
<CoursePageLayout className="pt-6"> <GeneralWrapperStyled>
<p className="text-lg font-bold">Course</p> <div className="pb-3">
<h1 className="text-3xl font-bold flex items-center space-x-5"> <p className="text-md font-bold text-gray-400 pb-2">Course</p>
{course.course.name}{" "} <h1 className="text-3xl -mt-3 font-bold">
<Link href={getUriWithOrg(orgslug, "") + `/course/${courseid}/edit`} rel="noopener noreferrer"> {course.course.name}
<Pencil2Icon /> </h1>
</Link>{" "} </div>
</h1>
<CourseThumbnailWrapper>
<img src={`${getBackendUrl()}content/uploads/img/${course.course.thumbnail}`} alt="" />
</CourseThumbnailWrapper>
<CourseMetaWrapper> <div className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-auto h-[300px] bg-cover bg-center mb-4" style={{ backgroundImage: `url(${getBackendUrl()}content/uploads/img/${course.course.thumbnail})` }}>
<CourseMetaLeft>
<h2 className="py-3 font-bold">Description</h2>
<BoxWrapper> </div>
<ActivityIndicators orgslug={orgslug} course={course} />
<div className="flex flex-row pt-10 flex-wrap">
<div className="course_metadata_left grow space-y-2">
<h2 className="py-3 text-2xl font-bold">Description</h2>
<StyledBox>
<p className="py-3">{course.course.description}</p> <p className="py-3">{course.course.description}</p>
</BoxWrapper> </StyledBox>
<h2 className="py-3 text-2xl font-bold">What you will learn</h2>
<StyledBox>
<h2 className="py-3 font-bold">What you will learn</h2>
<BoxWrapper>
<p className="py-3">{course.learnings == ![] ? "no data" : course.learnings}</p> <p className="py-3">{course.learnings == ![] ? "no data" : course.learnings}</p>
</BoxWrapper> </StyledBox>
<h2 className="py-3 font-bold">Course Lessons</h2> <h2 className="py-3 text-2xl font-bold">Course Lessons</h2>
<StyledBox>
<BoxWrapper>
{course.chapters.map((chapter: any) => { {course.chapters.map((chapter: any) => {
return ( return (
<div <div
@ -88,142 +95,32 @@ const CourseClient = (props: any) => {
</div> </div>
); );
})} })}
</BoxWrapper> </StyledBox>
</CourseMetaLeft>
<CourseMetaRight> </div>
<div className="course_metadata_right w-64 flex items-center ml-10 h-28 p-3 bg-white rounded-md justify-center drop-shadow-[0_33px_13px_rgba(0,0,0,0.042)] transition-all">
{course.trail.status == "ongoing" ? ( {course.trail.status == "ongoing" ? (
<button style={{ backgroundColor: "red" }} onClick={quitCourse}> <button className="py-2 px-5 rounded-xl text-white font-bold h-12 w-[200px] drop-shadow-md bg-red-600 hover:bg-red-700 hover:cursor-pointer" onClick={quitCourse}>
Quit Course Quit Course
</button> </button>
) : ( ) : (
<button onClick={startCourseUI}>Start Course</button> <button className="py-2 px-5 rounded-xl text-white font-bold h-12 w-[200px] drop-shadow-md bg-black hover:bg-gray-900 hover:cursor-pointer" onClick={startCourseUI}>Start Course</button>
)} )}
</CourseMetaRight> </div>
</CourseMetaWrapper> </div>
</CoursePageLayout> </GeneralWrapperStyled>
)} )}
</> </>
); );
}; };
const CourseThumbnailWrapper = styled.div`
display: flex;
padding-bottom: 20px;
img {
width: 100%;
height: 300px;
object-fit: cover;
object-position: center;
background: url(), #d9d9d9;
border: 1px solid rgba(255, 255, 255, 0.19);
box-shadow: 0px 13px 33px -13px rgba(0, 0, 0, 0.42);
border-radius: 7px;
}
`;
const CoursePageLayout = styled.div`
width: 1300px;
margin: 0 auto;
p {
margin-bottom: 0px;
letter-spacing: -0.05em;
color: #727272; const StyledBox = (props: any) => (
font-weight: 700; <div className="p-3 pl-10 bg-white rounded-md w-[100%] h-auto drop-shadow-[0_33px_13px_rgba(0,0,0,0.042)]">
} {props.children}
h1 { </div>
margin-top: 5px;
letter-spacing: -0.05em;
margin-bottom: 10px;
}
`;
const ChaptersWrapper = styled.div` );
display: flex;
width: 100%;
`;
const CourseIndicator = styled.div< { active?: boolean, done?: boolean } >`
border-radius: 20px;
height: 5px;
background: #151515;
border-radius: 3px;
background-color: ${props => props.done ? "green" : "black"};
width: 40px;
margin: 10px;
margin-bottom: 20px;
margin-left: 0px;
&:hover {
cursor: pointer;
background-color: #9d9d9d;
}
`;
const ChapterSeparator = styled.div`
display: flex;
flex-direction: row;
padding-right: 7px;
`;
const BoxWrapper = styled.div`
background: #ffffff;
box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.03);
border-radius: 7px;
padding: 20px;
padding-top: 7px;
padding-left: 30px;
p {
font-family: "DM Sans";
font-style: normal;
font-weight: 500;
line-height: 16px;
letter-spacing: -0.02em;
color: #9d9d9d;
}
`;
const CourseMetaWrapper = styled.div`
display: flex;
justify-content: space-between;
`;
const CourseMetaLeft = styled.div`
width: 80%;
`;
const CourseMetaRight = styled.div`
background: #ffffff;
box-shadow: 0px 4px 16px rgba(0, 0, 0, 0.03);
border-radius: 7px;
padding: 20px;
width: 30%;
display: flex;
height: 100%;
justify-content: center;
margin-left: 50px;
margin-top: 20px;
button {
width: 100%;
height: 50px;
background: #151515;
border-radius: 15px;
border: none;
color: white;
font-weight: 700;
font-family: "DM Sans";
font-size: 16px;
letter-spacing: -0.05em;
transition: all 0.2s ease;
box-shadow: 0px 13px 33px -13px rgba(0, 0, 0, 0.42);
&:hover {
cursor: pointer;
background: #000000;
}
}
`;
export default CourseClient; export default CourseClient;

View file

@ -3,18 +3,19 @@ import React from "react";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import styled from "styled-components"; import styled from "styled-components";
import { Title } from "@components/UI/Elements/Styles/Title";
import { DragDropContext, Droppable } from "react-beautiful-dnd"; import { DragDropContext, Droppable } from "react-beautiful-dnd";
import { initialData, initialData2 } from "@components/Pages/CourseEdit/Draggables/data"; import { initialData, initialData2 } from "@components/Pages/CourseEdit/Draggables/data";
import Chapter from "@components/Pages/CourseEdit/Draggables/Chapter"; import Chapter from "@components/Pages/CourseEdit/Draggables/Chapter";
import { createChapter, deleteChapter, getCourseChaptersMetadata, updateChaptersMetadata } from "@services/courses/chapters"; import { createChapter, deleteChapter, getCourseChaptersMetadata, updateChaptersMetadata } from "@services/courses/chapters";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import NewChapterModal from "@components/Modals/Chapters/NewChapter"; import NewChapterModal from "@components/Objects/Modals/Chapters/NewChapter";
import NewActivityModal from "@components/Modals/Activities/Create/NewActivity"; import NewActivityModal from "@components/Objects/Modals/Activities/Create/NewActivity";
import { createActivity, createFileActivity, createExternalVideoActivity } from "@services/courses/activities"; import { createActivity, createFileActivity, createExternalVideoActivity } from "@services/courses/activities";
import { getOrganizationContextInfo } from "@services/organizations/orgs"; import { getOrganizationContextInfo } from "@services/organizations/orgs";
import Modal from "@components/UI/Modal/Modal"; import Modal from "@components/StyledElements/Modal/Modal";
import { denyAccessToUser } from "@services/utils/react/middlewares/views"; import { denyAccessToUser } from "@services/utils/react/middlewares/views";
import { Folders, Package2, SaveIcon } from "lucide-react";
import GeneralWrapperStyled from "@components/StyledElements/Wrappers/GeneralWrapper";
function CourseEdit(params: any) { function CourseEdit(params: any) {
@ -240,120 +241,94 @@ function CourseEdit(params: any) {
return ( return (
<> <>
<Page> <div
<Title> className="bg-gradient-radial bg-fixed bg-repeat bg-[0 0],[25px 25px] bg-[50px 50px] bg-[#4744446b 1px]"
Edit Course {" "} >
<GeneralWrapperStyled>
<div className="font-bold text-lg flex space-x-2 items-center">
<p> Edit Course {" "}</p>
<div
className="bg-black hover:bg-gray-950 text-white font-bold p-1 px-2 text-sm rounded flex items-center cursor-pointer space-x-2"
onClick={() => {
updateChapters();
}}
>
<SaveIcon className="w-4 h-4" />
<p>Save</p>
</div>
</div>
<Modal <Modal
isDialogOpen={newChapterModal} isDialogOpen={newActivityModal}
onOpenChange={setNewChapterModal} onOpenChange={setNewActivityModal}
minHeight="sm" minHeight="no-min"
dialogContent={<NewChapterModal addDefCloseButton={false}
closeModal={closeNewChapterModal} dialogContent={<NewActivityModal
submitChapter={submitChapter} closeModal={closeNewActivityModal}
></NewChapterModal>} submitFileActivity={submitFileActivity}
dialogTitle="Create chapter" submitExternalVideo={submitExternalVideo}
dialogDescription="Add a new chapter to the course" submitActivity={submitActivity}
dialogTrigger={ chapterId={newActivityModalData}
<button> Add chapter + ></NewActivityModal>}
</button> dialogTitle="Create Activity"
} dialogDescription="Choose between types of activities to add to the course"
/> />
<button <br />
onClick={() => { {winReady && (
updateChapters(); <div className="flex flex-col max-w-7xl justify-center items-center mx-auto">
}} <DragDropContext onDragEnd={onDragEnd}>
> <Droppable key="chapters" droppableId="chapters" type="chapter">
Save {(provided) => (
</button> <>
</Title> <div key={"chapters"} {...provided.droppableProps} ref={provided.innerRef}>
{getChapters().map((info: any, index: any) => (
<Modal <>
isDialogOpen={newActivityModal} <Chapter
onOpenChange={setNewActivityModal} orgslug={orgslug}
minHeight="no-min" courseid={courseid}
addDefCloseButton={false} openNewActivityModal={openNewActivityModal}
dialogContent={<NewActivityModal deleteChapter={deleteChapterUI}
closeModal={closeNewActivityModal} key={index}
submitFileActivity={submitFileActivity} info={info}
submitExternalVideo={submitExternalVideo} index={index}
submitActivity={submitActivity} ></Chapter>
chapterId={newActivityModalData} </>
></NewActivityModal>} ))}
dialogTitle="Create Activity" {provided.placeholder}
dialogDescription="Choose between types of activities to add to the course" </div>
</>
/> )}
</Droppable>
<br /> </DragDropContext>
{winReady && ( <Modal
<ChapterlistWrapper> isDialogOpen={newChapterModal}
<DragDropContext onDragEnd={onDragEnd}> onOpenChange={setNewChapterModal}
<Droppable key="chapters" droppableId="chapters" type="chapter"> minHeight="sm"
{(provided) => ( dialogContent={<NewChapterModal
<> closeModal={closeNewChapterModal}
<div key={"chapters"} {...provided.droppableProps} ref={provided.innerRef}> submitChapter={submitChapter}
{getChapters().map((info: any, index: any) => ( ></NewChapterModal>}
<> dialogTitle="Create chapter"
<Chapter dialogDescription="Add a new chapter to the course"
orgslug={orgslug} dialogTrigger={
courseid={courseid} <div className="flex max-w-7xl bg-black shadow rounded-md text-white justify-center space-x-2 p-3 w-72 hover:bg-gray-900 hover:cursor-pointer">
openNewActivityModal={openNewActivityModal} <Folders size={20} />
deleteChapter={deleteChapterUI} <div>Add chapter +</div>
key={index} </div>
info={info} }
index={index} />
></Chapter> </div>
</> )}
))} </GeneralWrapperStyled >
{provided.placeholder} </div>
</div>
</>
)}
</Droppable>
</DragDropContext>
</ChapterlistWrapper>
)}
</Page >
</> </>
); );
} }
const Page = styled.div`
height: 100%;
width: 100%;
min-height: 100vh;
min-width: 100vw;
padding-top: 30px;
// dots background
background-image: radial-gradient(#4744446b 1px, transparent 1px), radial-gradient(#4744446b 1px, transparent 1px);
background-position: 0 0, 25px 25px;
background-size: 50px 50px;
background-attachment: fixed;
background-repeat: repeat;
button {
margin-left: 10px;
background-color: #000000;
border: none;
border-radius: 5px;
padding: 5px 10px;
color: white;
font-size: 13px;
cursor: pointer;
transition: 0.2s;
font-family: "DM Sans", sans-serif;
&:hover {
background-color: #474444;
transition: 0.2s;
}
}
`;
const ChapterlistWrapper = styled.div`
display: flex;
padding-left: 30px;
justify-content: center;
`;
export default CourseEdit; export default CourseEdit;

View file

@ -1,6 +1,6 @@
'use client'; // Error components must be Client Components 'use client'; // Error components must be Client Components
import ErrorUI from '@components/UI/Error/Error'; import ErrorUI from '@components/StyledElements/Error/Error';
import { useEffect } from 'react'; import { useEffect } from 'react';
export default function Error({ export default function Error({

View file

@ -1,4 +1,4 @@
import PageLoading from "@components/Pages/PageLoading"; import PageLoading from "@components/Objects/Loaders/PageLoading";
export default function Loading() { export default function Loading() {
// Or a custom loading skeleton component // Or a custom loading skeleton component

View file

@ -1,8 +1,8 @@
'use client'; 'use client';
import CreateCourseModal from '@components/Modals/Course/Create/CreateCourse'; import CreateCourseModal from '@components/Objects/Modals/Course/Create/CreateCourse';
import Modal from '@components/UI/Modal/Modal'; import Modal from '@components/StyledElements/Modal/Modal';
import { Edit2, Trash } from "lucide-react"; import { Edit2, Trash } from "lucide-react";
import { getAPIUrl, getBackendUrl, getUriWithOrg } from '@services/config/config'; import { getBackendUrl, getUriWithOrg } from '@services/config/config';
import CoursesLogo from "public/svg/courses.svg"; import CoursesLogo from "public/svg/courses.svg";
import CollectionsLogo from "public/svg/collections.svg"; import CollectionsLogo from "public/svg/collections.svg";
import { deleteCourseFromBackend } from '@services/courses/courses'; import { deleteCourseFromBackend } from '@services/courses/courses';
@ -11,6 +11,10 @@ import React from 'react'
import Image from 'next/image'; import Image from 'next/image';
import { AuthContext } from '@components/Security/AuthProvider'; import { AuthContext } from '@components/Security/AuthProvider';
import { revalidateTags } from '@services/utils/ts/requests'; import { revalidateTags } from '@services/utils/ts/requests';
import { useRouter } from 'next/navigation';
import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper';
import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentTitle';
import AuthenticatedClientElement from '@components/Security/AuthenticatedClientElement';
interface CourseProps { interface CourseProps {
orgslug: string; orgslug: string;
@ -26,48 +30,48 @@ function Courses(props: CourseProps) {
const orgslug = props.orgslug; const orgslug = props.orgslug;
const courses = props.courses; const courses = props.courses;
const [newCourseModal, setNewCourseModal] = React.useState(false); const [newCourseModal, setNewCourseModal] = React.useState(false);
const router = useRouter();
async function deleteCourses(course_id: any) { async function deleteCourses(course_id: any) {
await deleteCourseFromBackend(course_id); await deleteCourseFromBackend(course_id);
revalidateTags(['courses']); revalidateTags(['courses']);
// terrible, nextjs right now doesn't mutate the page when the data changes
window.location.reload(); router.refresh();
} }
async function closeNewCourseModal() { async function closeNewCourseModal() {
setNewCourseModal(false); setNewCourseModal(false);
} }
return ( return (
<div> <div>
<div className='max-w-7xl mx-auto px-4'> <GeneralWrapperStyled>
<div className='flex flex-wrap justify-between'> <div className='flex flex-wrap justify-between'>
<Title title="Courses" type="cou" /> <TypeOfContentTitle title="Courses" type="cou" />
<Modal <AuthenticatedClientElement checkMethod='authentication'>
isDialogOpen={newCourseModal} <Modal
onOpenChange={setNewCourseModal} isDialogOpen={newCourseModal}
minHeight="md" onOpenChange={setNewCourseModal}
dialogContent={<CreateCourseModal minHeight="md"
closeModal={closeNewCourseModal} dialogContent={<CreateCourseModal
orgslug={orgslug} closeModal={closeNewCourseModal}
></CreateCourseModal>} orgslug={orgslug}
dialogTitle="Create Course" ></CreateCourseModal>}
dialogDescription="Create a new course" dialogTitle="Create Course"
dialogTrigger={ dialogDescription="Create a new course"
<button className="rounded-md bg-black antialiased ring-offset-purple-800 p-2 px-5 my-auto font text-sm font-bold text-white drop-shadow-lg">Add Course + </button> dialogTrigger={
} <button className="rounded-md bg-black antialiased ring-offset-purple-800 p-2 px-5 my-auto font text-sm font-bold text-white drop-shadow-lg">Add Course + </button>
/> }
/>
</AuthenticatedClientElement>
</div> </div>
<div className="flex space-x-5"> <div className="flex flex-wrap">
{courses.map((course: any) => ( {courses.map((course: any) => (
<div key={course.course_id}> <div className="px-3" key={course.course_id}>
<AdminEditsArea course={course} orgslug={orgslug} course_id={course.course_id} deleteCourses={deleteCourses} /> <AdminEditsArea course={course} orgSlug={orgslug} courseId={course.course_id} deleteCourses={deleteCourses} />
<Link href={getUriWithOrg(orgslug, "/course/" + removeCoursePrefix(course.course_id))}> <Link href={getUriWithOrg(orgslug, "/course/" + removeCoursePrefix(course.course_id))}>
<div className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-[249px] h-[131px] bg-cover" style={{ backgroundImage: `url(${getBackendUrl()}content/uploads/img/${course.thumbnail})` }}> <div className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-[249px] h-[131px] bg-cover" style={{ backgroundImage: `url(${getBackendUrl()}content/uploads/img/${course.thumbnail})` }}>
@ -78,83 +82,28 @@ function Courses(props: CourseProps) {
))} ))}
</div> </div>
</div> </GeneralWrapperStyled>
</div> </div>
) )
} }
export const Title = (props: any) => { const AdminEditsArea = (props: { orgSlug: string, courseId: string, course: any, deleteCourses: any }) => {
return ( return (
<div className="home_category_title flex my-5"> <AuthenticatedClientElement checkMethod='roles' orgId={props.course.org_id}><div className="flex space-x-2 py-2">
<div className="rounded-full ring-1 ring-slate-900/5 shadow-sm p-2 my-auto mr-4"> <button className="rounded-md text-sm px-3 font-bold text-red-800 bg-red-200 w-16 flex justify-center items-center" onClick={() => props.deleteCourses(props.courseId)}>
<Image className="" src={props.type == "col" ? CollectionsLogo : CoursesLogo} alt="Courses logo" /> Delete <Trash size={10}></Trash>
</div> </button>
<h1 className="font-bold text-lg">{props.title}</h1> <Link href={getUriWithOrg(props.orgSlug, "/course/" + removeCoursePrefix(props.courseId) + "/edit")}>
<button className="rounded-md text-sm px-3 font-bold text-orange-800 bg-orange-200 w-16 flex justify-center items-center">
Edit <Edit2 size={10}></Edit2>
</button>
</Link>
</div> </div>
</AuthenticatedClientElement>
) )
} }
const AdminEditsArea = (props: any) => {
const org_roles_values = ["admin", "owner"];
const user_roles_values = ["role_admin"];
const auth: any = React.useContext(AuthContext);
console.log("auth: ", auth);
// this is amazingly terrible code, but gotta release that MVP
// TODO: fix this
function isAuthorized() {
const org_id = props.course.org_id;
const org_roles = auth.userInfo.user_object.orgs;
const user_roles = auth.userInfo.user_object.roles;
const org_role = org_roles.find((org: any) => org.org_id == org_id);
const user_role = user_roles.find((role: any) => role.org_id == org_id);
if (org_role && user_role) {
if (org_roles_values.includes(org_role.org_role) && user_roles_values.includes(user_role.role_id)) {
return true;
}
else {
return false;
}
} else {
return false;
}
}
// this is amazingly terrible code, but gotta release that MVP
// TODO: fix this
if (auth.isAuthenticated) {
if (isAuthorized()) {
return (
<div className="flex space-x-2 py-2">
<button className="rounded-md text-sm px-3 font-bold text-red-800 bg-red-200 w-16 flex justify-center items-center" onClick={() => props.deleteCourses(props.course_id)}>
Delete <Trash size={10}></Trash>
</button>
<Link href={getUriWithOrg(props.orgslug, "/course/" + removeCoursePrefix(props.course_id) + "/edit")}>
<button className="rounded-md text-sm px-3 font-bold text-orange-800 bg-orange-200 w-16 flex justify-center items-center">
Edit <Edit2 size={10}></Edit2>
</button>
</Link>
</div>
)
} else {
return (
<div></div>
)
}
}
else {
return (
<div></div>
)
}
}
export default Courses export default Courses

View file

@ -1,6 +1,6 @@
'use client'; // Error components must be Client Components 'use client'; // Error components must be Client Components
import ErrorUI from '@components/UI/Error/Error'; import ErrorUI from '@components/StyledElements/Error/Error';
import { useEffect } from 'react'; import { useEffect } from 'react';
export default function Error({ export default function Error({

View file

@ -1,4 +1,4 @@
import PageLoading from "@components/Pages/PageLoading"; import PageLoading from "@components/Objects/Loaders/PageLoading";
export default function Loading() { export default function Loading() {
// Or a custom loading skeleton component // Or a custom loading skeleton component

View file

@ -1,6 +1,6 @@
'use client'; // Error components must be Client Components 'use client'; // Error components must be Client Components
import ErrorUI from '@components/UI/Error/Error'; import ErrorUI from '@components/StyledElements/Error/Error';
import { useEffect } from 'react'; import { useEffect } from 'react';
export default function Error({ export default function Error({

View file

@ -1,5 +1,5 @@
import "@styles/globals.css"; import "@styles/globals.css";
import { Menu } from "@components/UI/Elements/Menu/Menu"; import { Menu } from "@components/Objects/Menu/Menu";
import AuthProvider from "@components/Security/AuthProvider"; import AuthProvider from "@components/Security/AuthProvider";
export default async function RootLayout({ children, params }: { children: React.ReactNode , params:any}) { export default async function RootLayout({ children, params }: { children: React.ReactNode , params:any}) {

View file

@ -1,4 +1,4 @@
import PageLoading from "@components/Pages/PageLoading"; import PageLoading from "@components/Objects/Loaders/PageLoading";
export default function Loading() { export default function Loading() {
return ( return (

View file

@ -2,14 +2,15 @@ export const dynamic = 'force-dynamic';
import { Metadata, ResolvingMetadata } from 'next'; import { Metadata, ResolvingMetadata } from 'next';
import { getBackendUrl, getUriWithOrg } from "@services/config/config"; import { getBackendUrl, getUriWithOrg } from "@services/config/config";
import { getCourse, getOrgCourses, getOrgCoursesWithAuthHeader } from "@services/courses/courses"; import { getCourse, getOrgCourses, getOrgCoursesWithAuthHeader } from "@services/courses/courses";
import CoursesLogo from "public/svg/courses.svg";
import CollectionsLogo from "public/svg/collections.svg";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { getOrgCollections, getOrgCollectionsWithAuthHeader } from "@services/courses/collections"; import { getOrgCollections, getOrgCollectionsWithAuthHeader } from "@services/courses/collections";
import { getOrganizationContextInfo } from '@services/organizations/orgs'; import { getOrganizationContextInfo } from '@services/organizations/orgs';
import { cookies } from 'next/headers'; import { cookies } from 'next/headers';
import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper';
import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentTitle';
type MetadataProps = { type MetadataProps = {
params: { orgslug: string }; params: { orgslug: string };
@ -50,12 +51,12 @@ const OrgHomePage = async (params: any) => {
return ( return (
<div> <div>
<div className="max-w-7xl mx-auto px-4 py-10"> <GeneralWrapperStyled>
{/* Collections */} {/* Collections */}
<Title title="Collections" type="col" /> <TypeOfContentTitle title="Collections" type="col" />
<div className="home_collections flex flex-wrap"> <div className="home_collections flex flex-wrap">
{collections.map((collection: any) => ( {collections.map((collection: any) => (
<div className="pr-8 flex flex-col" key={collection.collection_id}> <div className="flex flex-col py-3 px-3" key={collection.collection_id}>
<Link href={getUriWithOrg(orgslug, "/collection/" + removeCollectionPrefix(collection.collection_id))}> <Link href={getUriWithOrg(orgslug, "/collection/" + removeCollectionPrefix(collection.collection_id))}>
<div className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-[249px] h-[180px] bg-cover flex flex-col items-center justify-center bg-indigo-600 font-bold text-zinc-50" > <div className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-[249px] h-[180px] bg-cover flex flex-col items-center justify-center bg-indigo-600 font-bold text-zinc-50" >
<h1 className="font-bold text-lg py-2 justify-center mb-2">{collection.name}</h1> <h1 className="font-bold text-lg py-2 justify-center mb-2">{collection.name}</h1>
@ -73,35 +74,24 @@ const OrgHomePage = async (params: any) => {
</div> </div>
{/* Courses */} {/* Courses */}
<Title title="Courses" type="cou" /> <div className='h-5'></div>
<TypeOfContentTitle title="Courses" type="cou" />
<div className="home_courses flex flex-wrap"> <div className="home_courses flex flex-wrap">
{courses.map((course: any) => ( {courses.map((course: any) => (
<div className="pr-8" key={course.course_id}> <div className="py-3 px-3" key={course.course_id}>
<Link href={getUriWithOrg(orgslug, "/course/" + removeCoursePrefix(course.course_id))}> <Link href={getUriWithOrg(orgslug, "/course/" + removeCoursePrefix(course.course_id))}>
<div className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-[249px] h-[131px] bg-cover" style={{ backgroundImage: `url(${getBackendUrl()}content/uploads/img/${course.thumbnail})` }}> <div className="inset-0 ring-1 ring-inset ring-black/10 rounded-lg shadow-xl relative w-[249px] h-[131px] bg-cover transition-all hover:scale-102" style={{ backgroundImage: `url(${getBackendUrl()}content/uploads/img/${course.thumbnail})` }}>
</div> </div>
</Link> </Link>
<h2 className="font-bold text-lg w-[250px] py-2">{course.name}</h2> <h2 className="font-bold text-lg w-[250px] py-2">{course.name}</h2>
</div> </div>
))} ))}
</div> </div>
</div> </GeneralWrapperStyled>
</div> </div>
); );
}; };
const Title = (props: any) => {
return (
<div className="home_category_title flex my-5">
<div className="rounded-full ring-1 ring-slate-900/5 shadow-sm p-2 my-auto mr-4">
<Image className="" src={props.type == "col" ? CollectionsLogo : CoursesLogo} alt="Courses logo" />
</div>
<h1 className="font-bold text-lg">{props.title}</h1>
</div>
)
}
export default OrgHomePage; export default OrgHomePage;

View file

@ -1,5 +1,7 @@
"use client"; "use client";
import PageLoading from "@components/Pages/PageLoading"; import PageLoading from "@components/Objects/Loaders/PageLoading";
import TypeOfContentTitle from "@components/StyledElements/Titles/TypeOfContentTitle";
import GeneralWrapperStyled from "@components/StyledElements/Wrappers/GeneralWrapper";
import { getAPIUrl, getBackendUrl } from "@services/config/config"; import { getAPIUrl, getBackendUrl } from "@services/config/config";
import { swrFetcher } from "@services/utils/ts/requests"; import { swrFetcher } from "@services/utils/ts/requests";
import React from "react"; import React from "react";
@ -12,10 +14,8 @@ function Trail(params: any) {
return ( return (
<TrailLayout> <GeneralWrapperStyled>
<h1>Trail</h1> <TypeOfContentTitle title="Trail" type="tra" />
<br />
{error && <p>Failed to load</p>}
{!trail ? ( {!trail ? (
<PageLoading></PageLoading> <PageLoading></PageLoading>
) : ( ) : (
@ -36,19 +36,13 @@ function Trail(params: any) {
))} ))}
</div> </div>
)} )}
</TrailLayout> </GeneralWrapperStyled>
); );
} }
export default Trail; export default Trail;
const TrailLayout = styled.div`
display: flex;
margin: 0 auto;
width: 1300px;
height: 100%;
flex-direction: column;
`;
const TrailMetadata = styled.div` const TrailMetadata = styled.div`
display: flex; display: flex;

View file

@ -2,12 +2,11 @@
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import React, { useState } from "react"; import React, { useState } from "react";
import { styled } from '@stitches/react'; import { styled } from '@stitches/react';
import { Title } from "../../../../components/UI/Elements/Styles/Title";
import { loginAndGetToken } from "../../../../services/auth/auth"; import { loginAndGetToken } from "../../../../services/auth/auth";
import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, FormMessage, Input } from '@components/UI/Form/Form'; import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, FormMessage, Input } from '@components/StyledElements/Form/Form';
import * as Form from '@radix-ui/react-form'; import * as Form from '@radix-ui/react-form';
import { BarLoader } from 'react-spinners'; import { BarLoader } from 'react-spinners';
import Toast from '@components/UI/Toast/Toast'; import Toast from '@components/StyledElements/Toast/Toast';
import { toast } from 'react-hot-toast'; import { toast } from 'react-hot-toast';
const Login = () => { const Login = () => {

View file

@ -4,6 +4,7 @@ import { Field, Form, Formik } from 'formik';
import { updateOrganization, uploadOrganizationLogo } from '@services/settings/org'; import { updateOrganization, uploadOrganizationLogo } from '@services/settings/org';
import { UploadCloud } from 'lucide-react'; import { UploadCloud } from 'lucide-react';
import { revalidateTags } from '@services/utils/ts/requests'; import { revalidateTags } from '@services/utils/ts/requests';
import { useRouter } from 'next/navigation';
interface OrganizationValues { interface OrganizationValues {
@ -17,7 +18,7 @@ interface OrganizationValues {
function OrganizationClient(props: any) { function OrganizationClient(props: any) {
const [selectedFile, setSelectedFile] = useState<File | null>(null); const [selectedFile, setSelectedFile] = useState<File | null>(null);
const router = useRouter();
// ... // ...
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
@ -33,9 +34,8 @@ function OrganizationClient(props: any) {
await uploadOrganizationLogo(org_id, selectedFile); await uploadOrganizationLogo(org_id, selectedFile);
setSelectedFile(null); // Reset the selected file setSelectedFile(null); // Reset the selected file
revalidateTags(['organizations']); revalidateTags(['organizations']);
// reload the page router.refresh();
// terrible hack, it will fixed later
window.location.reload();
} }
}; };

View file

@ -1,6 +1,5 @@
"use client"; "use client";
import React from "react"; import React from "react";
import { Title } from "../../../../components/UI/Elements/Styles/Title";
import { signup } from "../../../../services/auth/auth"; import { signup } from "../../../../services/auth/auth";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
@ -39,9 +38,8 @@ const SignUp = (params: any) => {
return ( return (
<div> <div>
<div title="Sign up"> <div title="Sign up">
<Title>Sign up </Title> <div className="font-bold text-lg">Sign up </div>
{/* Create a login ui with tailwindcss */}
<div className="flex justify-center items-center h-screen"> <div className="flex justify-center items-center h-screen">
<div className="w-full max-w-md"> <div className="w-full max-w-md">
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>

View file

@ -4,7 +4,7 @@ import { useEditor, EditorContent } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit"; import StarterKit from "@tiptap/starter-kit";
import Collaboration from "@tiptap/extension-collaboration"; import Collaboration from "@tiptap/extension-collaboration";
import CollaborationCursor from "@tiptap/extension-collaboration-cursor"; import CollaborationCursor from "@tiptap/extension-collaboration-cursor";
import { AuthContext } from "../Security/AuthProvider"; import { AuthContext } from "../../Security/AuthProvider";
import learnhouseIcon from "public/learnhouse_icon.png"; import learnhouseIcon from "public/learnhouse_icon.png";
import { ToolbarButtons } from "./Toolbar/ToolbarButtons"; import { ToolbarButtons } from "./Toolbar/ToolbarButtons";
import { motion, AnimatePresence } from "framer-motion"; import { motion, AnimatePresence } from "framer-motion";
@ -23,7 +23,7 @@ import { Eye, Save } from "lucide-react";
import MathEquationBlock from "./Extensions/MathEquation/MathEquationBlock"; import MathEquationBlock from "./Extensions/MathEquation/MathEquationBlock";
import PDFBlock from "./Extensions/PDF/PDFBlock"; import PDFBlock from "./Extensions/PDF/PDFBlock";
import QuizBlock from "./Extensions/Quiz/QuizBlock"; import QuizBlock from "./Extensions/Quiz/QuizBlock";
import ToolTip from "@components/UI/Tooltip/Tooltip"; import ToolTip from "@components/StyledElements/Tooltip/Tooltip";
import Link from "next/link"; import Link from "next/link";
interface Editor { interface Editor {

View file

@ -5,7 +5,7 @@ import { WebrtcProvider } from "y-webrtc";
import Editor from "./Editor"; import Editor from "./Editor";
import { updateActivity } from "@services/courses/activities"; import { updateActivity } from "@services/courses/activities";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import Toast from "@components/UI/Toast/Toast"; import Toast from "@components/StyledElements/Toast/Toast";
interface EditorWrapperProps { interface EditorWrapperProps {
content: string; content: string;

View file

@ -7,7 +7,7 @@ function InfoCalloutComponent(props: any) {
return ( return (
<NodeViewWrapper> <NodeViewWrapper>
<InfoCalloutWrapper contentEditable={props.extension.options.editable}> <InfoCalloutWrapper contentEditable={props.extension.options.editable}>
<AlertCircle /> <NodeViewContent contentEditable={props.extension.options.editable} className="content" /> <AlertCircle /> <NodeViewContent contentEditable={props.extension.options.editable} className="content" />
</InfoCalloutWrapper> </InfoCalloutWrapper>
</NodeViewWrapper> </NodeViewWrapper>
); );
@ -17,7 +17,7 @@ const InfoCalloutWrapper = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
color: #1f3a8a; color: #1f3a8a;
background-color: #dbe9fe; background-color: #dbe9fe;
border: 1px solid #c1d9fb; border: 1px solid #c1d9fb;
border-radius: 16px; border-radius: 16px;
margin: 1rem 0; margin: 1rem 0;
@ -36,14 +36,6 @@ const InfoCalloutWrapper = styled.div`
} }
`; `;
const DragHandle = styled.div`
position: absolute;
top: 0;
left: 0;
width: 1rem;
height: 100%;
cursor: move;
z-index: 1;
`;
export default InfoCalloutComponent; export default InfoCalloutComponent;

View file

@ -5,8 +5,8 @@ import { Resizable } from 're-resizable';
import * as AspectRatio from '@radix-ui/react-aspect-ratio'; import * as AspectRatio from '@radix-ui/react-aspect-ratio';
import { AlertCircle, AlertTriangle, Image, ImagePlus, Info } from "lucide-react"; import { AlertCircle, AlertTriangle, Image, ImagePlus, Info } from "lucide-react";
import { getImageFile, uploadNewImageFile } from "../../../../services/blocks/Image/images"; import { getImageFile, uploadNewImageFile } from "../../../../../services/blocks/Image/images";
import { getBackendUrl } from "../../../../services/config/config"; import { getBackendUrl } from "../../../../../services/config/config";
function ImageBlockComponent(props: any) { function ImageBlockComponent(props: any) {
const [image, setImage] = React.useState(null); const [image, setImage] = React.useState(null);

View file

@ -2,8 +2,8 @@ import { NodeViewWrapper } from "@tiptap/react";
import React from "react"; import React from "react";
import styled from "styled-components"; import styled from "styled-components";
import { AlertCircle, AlertTriangle, FileText, Image, ImagePlus, Info } from "lucide-react"; import { AlertCircle, AlertTriangle, FileText, Image, ImagePlus, Info } from "lucide-react";
import { getPDFFile, uploadNewPDFFile } from "../../../../services/blocks/Pdf/pdf"; import { getPDFFile, uploadNewPDFFile } from "../../../../../services/blocks/Pdf/pdf";
import { getBackendUrl } from "../../../../services/config/config"; import { getBackendUrl } from "../../../../../services/config/config";
function PDFBlockComponent(props: any) { function PDFBlockComponent(props: any) {
const [pdf, setPDF] = React.useState(null); const [pdf, setPDF] = React.useState(null);

View file

@ -2,8 +2,8 @@ import { NodeViewWrapper } from "@tiptap/react";
import { AlertTriangle, Image, Video } from "lucide-react"; import { AlertTriangle, Image, Video } from "lucide-react";
import React from "react"; import React from "react";
import styled from "styled-components"; import styled from "styled-components";
import { getBackendUrl } from "../../../../services/config/config"; import { getBackendUrl } from "../../../../../services/config/config";
import { uploadNewVideoFile } from "../../../../services/blocks/Video/video"; import { uploadNewVideoFile } from "../../../../../services/blocks/Video/video";
function VideoBlockComponents(props: any) { function VideoBlockComponents(props: any) {
const [video, setVideo] = React.useState(null); const [video, setVideo] = React.useState(null);

View file

@ -1,7 +1,7 @@
import styled from "styled-components"; import styled from "styled-components";
import { FontBoldIcon, FontItalicIcon, StrikethroughIcon, ArrowLeftIcon, ArrowRightIcon, OpacityIcon, DividerVerticalIcon } from "@radix-ui/react-icons"; import { FontBoldIcon, FontItalicIcon, StrikethroughIcon, ArrowLeftIcon, ArrowRightIcon, OpacityIcon, DividerVerticalIcon } from "@radix-ui/react-icons";
import { AlertCircle, AlertTriangle, FileText, GraduationCap, ImagePlus, Info, Sigma, Video, Youtube } from "lucide-react"; import { AlertCircle, AlertTriangle, FileText, GraduationCap, ImagePlus, Info, Sigma, Video, Youtube } from "lucide-react";
import ToolTip from "@components/UI/Tooltip/Tooltip"; import ToolTip from "@components/StyledElements/Tooltip/Tooltip";
export const ToolbarButtons = ({ editor, props }: any) => { export const ToolbarButtons = ({ editor, props }: any) => {
if (!editor) { if (!editor) {

View file

@ -1,11 +1,9 @@
import React from "react"; import React from "react";
import learnhouseLogo from "public/learnhouse_logo.png";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image";
import { getBackendUrl, getUriWithOrg } from "@services/config/config"; import { getBackendUrl, getUriWithOrg } from "@services/config/config";
import { getOrganizationContextInfo, getOrganizationContextInfoNoAsync } from "@services/organizations/orgs"; import { getOrganizationContextInfo } from "@services/organizations/orgs";
import ClientComponentSkeleton from "@components/UI/Utils/ClientComp"; import ClientComponentSkeleton from "@components/Utils/ClientComp";
import { HeaderProfileBox } from "@components/Security/HeaderProfileBox"; import { HeaderProfileBox } from "@components/Security/HeaderProfileBox";
export const Menu = async (props: any) => { export const Menu = async (props: any) => {

View file

@ -1,4 +1,4 @@
import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, FormMessage, Input, Textarea } from "@components/UI/Form/Form"; import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, FormMessage, Input, Textarea } from "@components/StyledElements/Form/Form";
import React, { useState } from "react"; import React, { useState } from "react";
import * as Form from '@radix-ui/react-form'; import * as Form from '@radix-ui/react-form';
import BarLoader from "react-spinners/BarLoader"; import BarLoader from "react-spinners/BarLoader";

View file

@ -1,4 +1,4 @@
import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, FormMessage, Input, Textarea } from "@components/UI/Form/Form"; import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, FormMessage, Input, Textarea } from "@components/StyledElements/Form/Form";
import React, { useState } from "react"; import React, { useState } from "react";
import * as Form from '@radix-ui/react-form'; import * as Form from '@radix-ui/react-form';
import BarLoader from "react-spinners/BarLoader"; import BarLoader from "react-spinners/BarLoader";

View file

@ -1,4 +1,4 @@
import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, FormMessage, Input, Textarea } from "@components/UI/Form/Form"; import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, FormMessage, Input, Textarea } from "@components/StyledElements/Form/Form";
import React, { useState } from "react"; import React, { useState } from "react";
import * as Form from '@radix-ui/react-form'; import * as Form from '@radix-ui/react-form';
import BarLoader from "react-spinners/BarLoader"; import BarLoader from "react-spinners/BarLoader";

View file

@ -1,4 +1,4 @@
import FormLayout, { Flex, FormField, Input, Textarea, FormLabel, ButtonBlack } from "@components/UI/Form/Form"; import FormLayout, { Flex, FormField, Input, Textarea, FormLabel, ButtonBlack } from "@components/StyledElements/Form/Form";
import { FormMessage } from "@radix-ui/react-form"; import { FormMessage } from "@radix-ui/react-form";
import * as Form from '@radix-ui/react-form'; import * as Form from '@radix-ui/react-form';
import React, { useState } from "react"; import React, { useState } from "react";

View file

@ -1,4 +1,4 @@
import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, Input, Textarea } from '@components/UI/Form/Form' import FormLayout, { ButtonBlack, Flex, FormField, FormLabel, Input, Textarea } from '@components/StyledElements/Form/Form'
import * as Form from '@radix-ui/react-form' import * as Form from '@radix-ui/react-form'
import { getAPIUrl, getUriWithOrg } from '@services/config/config'; import { getAPIUrl, getUriWithOrg } from '@services/config/config';
import { FormMessage } from "@radix-ui/react-form"; import { FormMessage } from "@radix-ui/react-form";
@ -8,6 +8,7 @@ import React, { useState } from 'react'
import { BarLoader } from 'react-spinners' import { BarLoader } from 'react-spinners'
import { mutate } from 'swr'; import { mutate } from 'swr';
import { revalidateTags } from '@services/utils/ts/requests'; import { revalidateTags } from '@services/utils/ts/requests';
import { useRouter } from 'next/navigation';
function CreateCourseModal({ closeModal, orgslug }: any) { function CreateCourseModal({ closeModal, orgslug }: any) {
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
@ -15,6 +16,7 @@ function CreateCourseModal({ closeModal, orgslug }: any) {
const [description, setDescription] = React.useState(""); const [description, setDescription] = React.useState("");
const [isLoading, setIsLoading] = React.useState(false); const [isLoading, setIsLoading] = React.useState(false);
const [thumbnail, setThumbnail] = React.useState(null) as any; const [thumbnail, setThumbnail] = React.useState(null) as any;
const router = useRouter();
const [orgId, setOrgId] = React.useState(null) as any; const [orgId, setOrgId] = React.useState(null) as any;
@ -46,9 +48,7 @@ function CreateCourseModal({ closeModal, orgslug }: any) {
if (status.org_id == orgId) { if (status.org_id == orgId) {
closeModal(); closeModal();
// reload the page router.refresh();
// terrible, nextjs right now doesn't mutate the page when the data changes
window.location.reload();
} else { } else {
alert("Error creating course, please see console logs"); alert("Error creating course, please see console logs");
console.log(status); console.log(status);

View file

@ -1,75 +1,18 @@
import { getBackendUrl } from "@services/config/config"; import { getBackendUrl } from "@services/config/config";
import React from "react"; import React from "react";
import styled from "styled-components";
function DocumentPdfActivity({ activity, course }: { activity: any; course: any }) { function DocumentPdfActivity({ activity, course }: { activity: any; course: any }) {
function getChapterName() {
let chapterName = "";
let chapterId = activity.chapter_id;
course.chapters.forEach((chapter: any) => {
if (chapter.chapter_id === chapterId) {
chapterName = chapter.name;
}
});
return chapterName;
}
return ( return (
<DocumentPdfActivityLayout> <div className="m-8 bg-zinc-900 rounded-md mt-14">
<DocumentPdfTitle> <iframe
<p>Chapter : {getChapterName()}</p> className="rounded-lg w-full h-[900px]"
{activity.name} src={`${getBackendUrl()}content/uploads/documents/documentpdf/${activity.content.documentpdf.activity_id}/${activity.content.documentpdf.filename}`}
</DocumentPdfTitle> />
<DocumentPdfPlayerWrapper> </div>
<iframe
src={`${getBackendUrl()}content/uploads/documents/documentpdf/${activity.content.documentpdf.activity_id}/${activity.content.documentpdf.filename}`}
/>
</DocumentPdfPlayerWrapper>
</DocumentPdfActivityLayout>
); );
} }
export default DocumentPdfActivity; export default DocumentPdfActivity;
const DocumentPdfActivityLayout = styled.div`
display: flex;
flex-direction: column;
margin-top: 10px;
background: #141414;
min-width: 100%;
min-height: 1200px;
`;
const DocumentPdfTitle = styled.div`
display: flex;
width: 1300px;
margin: 0 auto;
padding-top: 20px;
font-size: 24px;
font-weight: 700;
color: #fff;
flex-direction: column;
p {
font-size: 14px;
padding: 0;
margin: 0;
color: #ffffffaa;
}
`;
const DocumentPdfPlayerWrapper = styled.div`
display: flex;
width: 1300px;
margin: 0 auto;
justify-content: center;
padding-top: 20px;
iframe {
width: 1300px;
height: 500px;
border-radius: 7px;
background-color: black;
border: none;
}
`;

View file

@ -4,12 +4,12 @@ import StarterKit from "@tiptap/starter-kit";
import { styled } from "styled-components"; import { styled } from "styled-components";
import Youtube from "@tiptap/extension-youtube"; import Youtube from "@tiptap/extension-youtube";
// Custom Extensions // Custom Extensions
import InfoCallout from "@editor/Extensions/Callout/Info/InfoCallout"; import InfoCallout from "@components/Objects/Editor/Extensions/Callout/Info/InfoCallout";
import WarningCallout from "@editor/Extensions/Callout/Warning/WarningCallout"; import WarningCallout from "@components/Objects/Editor/Extensions/Callout/Warning/WarningCallout";
import ImageBlock from "@editor/Extensions/Image/ImageBlock"; import ImageBlock from "@components/Objects/Editor/Extensions/Image/ImageBlock";
import VideoBlock from "@editor/Extensions/Video/VideoBlock"; import VideoBlock from "@components/Objects/Editor/Extensions/Video/VideoBlock";
import MathEquationBlock from "@components/Editor/Extensions/MathEquation/MathEquationBlock"; import MathEquationBlock from "@components/Objects/Editor/Extensions/MathEquation/MathEquationBlock";
import PDFBlock from "@components/Editor/Extensions/PDF/PDFBlock"; import PDFBlock from "@components/Objects/Editor/Extensions/PDF/PDFBlock";
interface Editor { interface Editor {
content: string; content: string;
@ -64,7 +64,7 @@ function Canva(props: Editor) {
const CanvaWrapper = styled.div` const CanvaWrapper = styled.div`
padding-top: 20px; padding-top: 20px;
width: 1300px; width: 100%;
margin: 0 auto; margin: 0 auto;
`; `;

View file

@ -7,17 +7,6 @@ function VideoActivity({ activity, course }: { activity: any; course: any }) {
const [videoId, setVideoId] = React.useState(''); const [videoId, setVideoId] = React.useState('');
const [videoType, setVideoType] = React.useState(''); const [videoType, setVideoType] = React.useState('');
function getChapterName() {
let chapterName = "";
let chapterId = activity.chapter_id;
course.chapters.forEach((chapter: any) => {
if (chapter.chapter_id === chapterId) {
chapterName = chapter.name;
}
});
return chapterName;
}
function getYouTubeEmbed(url: any) { function getYouTubeEmbed(url: any) {
// Extract video ID from the YouTube URL // Extract video ID from the YouTube URL
var videoId = url.match(/(?:\?v=|\/embed\/|\/\d\/|\/vi\/|\/v\/|https?:\/\/(?:www\.)?youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([^#\&\?\/]+)/)[1]; var videoId = url.match(/(?:\?v=|\/embed\/|\/\d\/|\/vi\/|\/v\/|https?:\/\/(?:www\.)?youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))([^#\&\?\/]+)/)[1];
@ -34,7 +23,6 @@ function VideoActivity({ activity, course }: { activity: any; course: any }) {
React.useEffect(() => { React.useEffect(() => {
console.log(activity);
if (activity.content.video) { if (activity.content.video) {
setVideoType('video'); setVideoType('video');
} }
@ -45,20 +33,16 @@ function VideoActivity({ activity, course }: { activity: any; course: any }) {
}, [activity]); }, [activity]);
return ( return (
<VideoActivityLayout> <div>
<VideoTitle>
<p>{getChapterName()}</p>
<p>{activity.name}</p>
</VideoTitle>
{videoType === 'video' && ( {videoType === 'video' && (
<VideoPlayerWrapper> <div className="m-8 bg-zinc-900 rounded-md mt-14">
<video controls src={`${getBackendUrl()}content/uploads/video/${activity.content.video.activity_id}/${activity.content.video.filename}`}></video> <video className="rounded-lg w-full h-[500px]" controls src={`${getBackendUrl()}content/uploads/video/${activity.content.video.activity_id}/${activity.content.video.filename}`}></video>
</VideoPlayerWrapper> </div>
)} )}
{videoType === 'external_video' && ( {videoType === 'external_video' && (
<VideoPlayerWrapper> <div>
<YouTube <YouTube
className="rounded-md overflow-hidden" className="rounded-md overflow-hidden m-8 bg-zinc-900 mt-14"
opts={ opts={
{ {
width: '1300', width: '1300',
@ -70,10 +54,10 @@ function VideoActivity({ activity, course }: { activity: any; course: any }) {
} }
} }
videoId={videoId} /> videoId={videoId} />
</VideoPlayerWrapper> </div>
)} )}
</VideoActivityLayout> </div>
); );
} }
@ -82,44 +66,4 @@ function VideoActivity({ activity, course }: { activity: any; course: any }) {
export default VideoActivity; export default VideoActivity;
const VideoActivityLayout = styled.div`
display: flex;
flex-direction: column;
margin-top: 10px;
background: #141414;
min-width: 100%;
min-height: 1200px;
`;
const VideoTitle = styled.div`
display: flex;
width: 1300px;
margin: 0 auto;
padding-top: 20px;
font-size: 24px;
font-weight: 700;
color: #fff;
flex-direction: column;
p {
font-size: 14px;
padding: 0;
margin: 0;
color: #ffffffaa;
}
`;
const VideoPlayerWrapper = styled.div`
display: flex;
width: 1300px;
margin: 0 auto;
justify-content: center;
padding-top: 20px;
video {
width: 1300px;
height: 500px;
border-radius: 7px;
background-color: black;
}
`;

View file

@ -4,6 +4,7 @@ import { Draggable } from "react-beautiful-dnd";
import { EyeOpenIcon, Pencil2Icon } from '@radix-ui/react-icons' import { EyeOpenIcon, Pencil2Icon } from '@radix-ui/react-icons'
import styled from "styled-components"; import styled from "styled-components";
import { getUriWithOrg } from "@services/config/config"; import { getUriWithOrg } from "@services/config/config";
import { FileText, Video, Sparkles } from "lucide-react";
function Activity(props: any) { function Activity(props: any) {
@ -11,17 +12,25 @@ function Activity(props: any) {
<Draggable key={props.activity.id} draggableId={props.activity.id} index={props.index}> <Draggable key={props.activity.id} draggableId={props.activity.id} index={props.index}>
{(provided) => ( {(provided) => (
<div <div
className="flex flex-row items-center py-2 my-3 rounded-md justify-center bg-gray-50 hover:bg-gray-100 space-x-2" key={props.activity.id} {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}> className="flex flex-row items-center py-2 my-3 rounded-md justify-center bg-gray-50 hover:bg-gray-100 space-x-2 w-auto" key={props.activity.id} {...provided.draggableProps} {...provided.dragHandleProps} ref={provided.innerRef}>
<p>{props.activity.name} </p> <div >
{props.activity.type === "video" && <Video size={16} />}
{props.activity.type === "documentpdf" && <FileText size={16} />}
{props.activity.type === "dynamic" && <Sparkles size={16} />}
</div>
<p className="first-letter:uppercase">{props.activity.name} </p>
<Link <Link
href={getUriWithOrg(props.orgslug, "") + `/course/${props.courseid}/activity/${props.activity.id.replace("activity_", "")}`} href={getUriWithOrg(props.orgslug, "") + `/course/${props.courseid}/activity/${props.activity.id.replace("activity_", "")}`}
className=" hover:cursor-pointer p-1 rounded-md bg-slate-200"
rel="noopener noreferrer"> <EyeOpenIcon /> rel="noopener noreferrer">
<EyeOpenIcon className="text-slate-700"/>
</Link> </Link>
<Link <Link
href={getUriWithOrg(props.orgslug, "") + `/course/${props.courseid}/activity/${props.activity.id.replace("activity_", "")}/edit`} href={getUriWithOrg(props.orgslug, "") + `/course/${props.courseid}/activity/${props.activity.id.replace("activity_", "")}/edit`}
className=" hover:cursor-pointer p-1 rounded-md bg-slate-200"
rel="noopener noreferrer"> rel="noopener noreferrer">
<Pencil2Icon /> <Pencil2Icon className="text-slate-700" />
</Link> </Link>
</div> </div>
)} )}

View file

@ -2,6 +2,7 @@ import React from "react";
import styled from "styled-components"; import styled from "styled-components";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"; import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
import Activity from "./Activity"; import Activity from "./Activity";
import { PlusSquare, Trash, Trash2 } from "lucide-react";
function Chapter(props: any) { function Chapter(props: any) {
return ( return (
@ -12,38 +13,44 @@ function Chapter(props: any) {
{...provided.draggableProps} {...provided.draggableProps}
ref={provided.innerRef} ref={provided.innerRef}
// isDragging={snapshot.isDragging} // isDragging={snapshot.isDragging}
className="backdrop-blur-md"
key={props.info.list.chapter.id} key={props.info.list.chapter.id}
> >
<h3 className="pt-3 font-bold text-md"> <h3 className="flex space-x-2 pt-3 font-bold text-md items-center">
{props.info.list.chapter.name} <p>{props.info.list.chapter.name}
<button </p>
onClick={() => {
props.openNewActivityModal(props.info.list.chapter.id);
}}
>
Create Activity
</button>
<button <div
className="hover:cursor-pointer p-1 rounded-md bg-slate-100"
onClick={() => { onClick={() => {
props.deleteChapter(props.info.list.chapter.id); props.deleteChapter(props.info.list.chapter.id);
}} }}
> >
X <Trash2 className="text-slate-500" size={16} />
</button> </div>
</h3> </h3>
<Droppable key={props.info.list.chapter.id} droppableId={props.info.list.chapter.id} type="activity"> <Droppable key={props.info.list.chapter.id} droppableId={props.info.list.chapter.id} type="activity">
{(provided) => ( {(provided) => (
<ActivitiesList {...provided.droppableProps} ref={provided.innerRef}> <ActivitiesList {...provided.droppableProps} ref={provided.innerRef}>
{props.info.list.activities.map((activity: any, index: any) => ( <div className="flex flex-col ">
<Activity orgslug={props.orgslug} courseid={props.courseid} key={activity.id} activity={activity} index={index}></Activity> {props.info.list.activities.map((activity: any, index: any) => (
))} <Activity orgslug={props.orgslug} courseid={props.courseid} key={activity.id} activity={activity} index={index}></Activity>
{provided.placeholder} ))}
{provided.placeholder}
<div onClick={() => {
props.openNewActivityModal(props.info.list.chapter.id);
}} className="flex space-x-2 items-center py-2 my-3 rounded-md justify-center text-slate-500 outline-slate-200 bg-slate-50 hover:cursor-pointer">
<PlusSquare className="" size={17} />
<div className="text-sm mx-auto my-auto items-center font-bold">Add Activity</div>
</div>
</div>
</ActivitiesList> </ActivitiesList>
)} )}
</Droppable> </Droppable>
</ChapterWrapper> </ChapterWrapper>
)} )}
</Draggable> </Draggable>
@ -53,7 +60,7 @@ function Chapter(props: any) {
const ChapterWrapper = styled.div` const ChapterWrapper = styled.div`
margin-bottom: 20px; margin-bottom: 20px;
padding: 4px; padding: 4px;
background-color: #ffffffc5; background-color: #ffffff9d;
width: 900px; width: 900px;
font-size: 15px; font-size: 15px;
display: block; display: block;

View file

@ -0,0 +1,73 @@
import ToolTip from '@components/StyledElements/Tooltip/Tooltip'
import { getUriWithOrg } from '@services/config/config'
import Link from 'next/link'
import React from 'react'
interface Props {
course: any
orgslug: string
current_activity?: any
}
function ActivityIndicators(props: Props) {
const course = props.course
const orgslug = props.orgslug
const courseid = course.course.course_id.replace("course_", "")
const done_activity_style = 'bg-teal-600 hover:bg-teal-700'
const black_activity_style = 'bg-black hover:bg-gray-700'
const current_activity_style = 'bg-gray-600 animate-pulse hover:bg-gray-700'
function isActivityDone(activity: any) {
if (course.trail.activities_marked_complete && course.trail.activities_marked_complete.includes(activity.id) && course.trail.status == "ongoing") {
return true
}
return false
}
function isActivityCurrent(activity: any) {
let activityid = activity.id.replace("activity_", "")
if (props.current_activity && props.current_activity == activityid) {
return true
}
return false
}
function getActivityClass(activity: any) {
if (isActivityDone(activity)) {
return done_activity_style
}
if (isActivityCurrent(activity)) {
return current_activity_style
}
return black_activity_style
}
return (
<div className='grid grid-flow-col justify-stretch space-x-6'>
{course.chapters.map((chapter: any) => {
return (
<>
<div className='grid grid-flow-col justify-stretch space-x-2'>
{chapter.activities.map((activity: any) => {
return (
<ToolTip sideOffset={8} slateBlack content={activity.name} key={activity.id}>
<Link href={getUriWithOrg(orgslug, "") + `/course/${courseid}/activity/${activity.id.replace("activity_", "")}`}>
<div className={`h-[7px] w-auto ${getActivityClass(activity)} rounded-lg shadow-md`}></div>
</Link>
</ToolTip>
);
})}
</div>
</>
);
})}
</div>
)
}
export default ActivityIndicators

View file

@ -0,0 +1,50 @@
'use client';
import React from "react";
import { AuthContext } from "./AuthProvider";
interface AuthenticatedClientElementProps {
children: React.ReactNode;
checkMethod: 'authentication' | 'roles';
orgId?: string;
}
function AuthenticatedClientElement(props: AuthenticatedClientElementProps) {
const auth: any = React.useContext(AuthContext);
// Available roles
const org_roles_values = ["admin", "owner"];
const user_roles_values = ["role_admin"];
function checkRoles() {
const org_id = props.orgId;
const org_roles = auth.userInfo.user_object.orgs;
const user_roles = auth.userInfo.user_object.roles;
const org_role = org_roles.find((org: any) => org.org_id == org_id);
const user_role = user_roles.find((role: any) => role.org_id == org_id);
if (org_role && user_role) {
if (org_roles_values.includes(org_role.org_role) && user_roles_values.includes(user_role.role_id)) {
return true;
}
else {
return false;
}
} else {
return false;
}
}
if ((props.checkMethod == 'authentication' && auth.isAuthenticated) || (auth.isAuthenticated && props.checkMethod == 'roles' && checkRoles())) {
return <>{props.children}</>;
}
return <></>;
}
export default AuthenticatedClientElement

View file

@ -79,7 +79,7 @@ const contentClose = keyframes({
const DialogOverlay = styled(Dialog.Overlay, { const DialogOverlay = styled(Dialog.Overlay, {
backgroundColor: blackA.blackA9, backgroundColor: blackA.blackA9,
position: 'fixed', position: 'fixed',
zIndex: 500,
inset: 0, inset: 0,
animation: `${overlayShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`, animation: `${overlayShow} 150ms cubic-bezier(0.16, 1, 0.3, 1)`,
'&[data-state="closed"]': { '&[data-state="closed"]': {
@ -111,6 +111,7 @@ const DialogContent = styled(Dialog.Content, {
backgroundColor: 'white', backgroundColor: 'white',
borderRadius: 18, borderRadius: 18,
zIndex: 501,
boxShadow: 'hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px', boxShadow: 'hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px',
position: 'fixed', position: 'fixed',
top: '50%', top: '50%',

View file

@ -0,0 +1,32 @@
import Image from 'next/image'
import CoursesLogo from "public/svg/courses.svg";
import CollectionsLogo from "public/svg/collections.svg";
import TrailLogo from "public/svg/trail.svg";
function TypeOfContentTitle(props: { title: string, type: string }) {
function getLogo() {
if (props.type == "col") {
return CollectionsLogo;
}
else if (props.type == "cou") {
return CoursesLogo;
}
else if (props.type == "tra") {
return TrailLogo;
}
}
return (
<div className="home_category_title flex my-5 items-center">
<div className="rounded-full ring-1 ring-slate-900/5 shadow-sm p-2 my-auto mr-4">
<Image className="" src={getLogo()} alt="Courses logo" />
</div>
<h1 className="font-bold text-2xl">{props.title}</h1>
</div>
)
}
export default TypeOfContentTitle

View file

@ -2,8 +2,6 @@
import React from 'react'; import React from 'react';
import * as Tooltip from '@radix-ui/react-tooltip'; import * as Tooltip from '@radix-ui/react-tooltip';
import { styled, keyframes } from '@stitches/react'; import { styled, keyframes } from '@stitches/react';
import { violet, blackA } from '@radix-ui/colors';
import { PlusIcon } from '@radix-ui/react-icons';
type TooltipProps = { type TooltipProps = {
@ -24,7 +22,6 @@ const ToolTip = (props: TooltipProps) => {
<Tooltip.Portal> <Tooltip.Portal>
<TooltipContent slateBlack={props.slateBlack} side="bottom" sideOffset={props.sideOffset}> <TooltipContent slateBlack={props.slateBlack} side="bottom" sideOffset={props.sideOffset}>
{props.content} {props.content}
<TooltipArrow />
</TooltipContent> </TooltipContent>
</Tooltip.Portal> </Tooltip.Portal>
</Tooltip.Root> </Tooltip.Root>
@ -62,7 +59,7 @@ const TooltipContent = styled(Tooltip.Content, {
variants: { variants: {
slateBlack: { slateBlack: {
true: { true: {
backgroundColor: " #5a5a5a", backgroundColor: " #0d0d0d",
color: 'white', color: 'white',
}, },
}, },
@ -96,25 +93,6 @@ const TooltipContent = styled(Tooltip.Content, {
}, },
}); });
const TooltipArrow = styled(Tooltip.Arrow, {
fill: 'white',
});
const IconButton = styled('button', {
all: 'unset',
fontFamily: 'inherit',
borderRadius: '100%',
height: 35,
width: 35,
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
color: violet.violet11,
backgroundColor: 'white',
boxShadow: `0 2px 10px ${blackA.blackA7}`,
'&:hover': { backgroundColor: violet.violet3 },
'&:focus': { boxShadow: `0 0 0 2px black` },
});
export default ToolTip; export default ToolTip;

View file

@ -0,0 +1,10 @@
function GeneralWrapperStyled({ children }: { children: React.ReactNode }) {
return (
<div
className='max-w-screen-2xl mx-auto px-16 py-5 tracking-tight'
>{children}</div>
)
}
export default GeneralWrapperStyled

View file

@ -1,7 +0,0 @@
import styled from "styled-components";
export const Title = styled.h1`
font-size: 1.5em;
padding-left: 20px;
font-weight: 500;
`;

189
front/package-lock.json generated
View file

@ -25,8 +25,8 @@
"avvvatars-react": "^0.4.2", "avvvatars-react": "^0.4.2",
"formik": "^2.2.9", "formik": "^2.2.9",
"framer-motion": "^7.3.6", "framer-motion": "^7.3.6",
"lucide-react": "^0.104.1", "lucide-react": "^0.248.0",
"next": "^13.4.6", "next": "^13.4.7-canary.4",
"re-resizable": "^6.9.9", "re-resizable": "^6.9.9",
"react": "^18.2.0", "react": "^18.2.0",
"react-beautiful-dnd": "^13.1.1", "react-beautiful-dnd": "^13.1.1",
@ -2123,9 +2123,9 @@
} }
}, },
"node_modules/@next/env": { "node_modules/@next/env": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.7.tgz",
"integrity": "sha512-nqUxEtvDqFhmV1/awSg0K2XHNwkftNaiUqCYO9e6+MYmqNObpKVl7OgMkGaQ2SZnFx5YqF0t60ZJTlyJIDAijg==" "integrity": "sha512-ZlbiFulnwiFsW9UV1ku1OvX/oyIPLtMk9p/nnvDSwI0s7vSoZdRtxXNsaO+ZXrLv/pMbXVGq4lL8TbY9iuGmVw=="
}, },
"node_modules/@next/eslint-plugin-next": { "node_modules/@next/eslint-plugin-next": {
"version": "13.0.6", "version": "13.0.6",
@ -2137,9 +2137,9 @@
} }
}, },
"node_modules/@next/swc-darwin-arm64": { "node_modules/@next/swc-darwin-arm64": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.7.tgz",
"integrity": "sha512-ahi6VP98o4HV19rkOXPSUu+ovfHfUxbJQ7VVJ7gL2FnZRr7onEFC1oGQ6NQHpm8CxpIzSSBW79kumlFMOmZVjg==", "integrity": "sha512-VZTxPv1b59KGiv/pZHTO5Gbsdeoxcj2rU2cqJu03btMhHpn3vwzEK0gUSVC/XW96aeGO67X+cMahhwHzef24/w==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2152,9 +2152,9 @@
} }
}, },
"node_modules/@next/swc-darwin-x64": { "node_modules/@next/swc-darwin-x64": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.7.tgz",
"integrity": "sha512-13cXxKFsPJIJKzUqrU5XB1mc0xbUgYsRcdH6/rB8c4NMEbWGdtD4QoK9ShN31TZdePpD4k416Ur7p+deMIxnnA==", "integrity": "sha512-gO2bw+2Ymmga+QYujjvDz9955xvYGrWofmxTq7m70b9pDPvl7aDFABJOZ2a8SRCuSNB5mXU8eTOmVVwyp/nAew==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2167,9 +2167,9 @@
} }
}, },
"node_modules/@next/swc-linux-arm64-gnu": { "node_modules/@next/swc-linux-arm64-gnu": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.7.tgz",
"integrity": "sha512-Ti+NMHEjTNktCVxNjeWbYgmZvA2AqMMI2AMlzkXsU7W4pXCMhrryAmAIoo+7YdJbsx01JQWYVxGe62G6DoCLaA==", "integrity": "sha512-6cqp3vf1eHxjIDhEOc7Mh/s8z1cwc/l5B6ZNkOofmZVyu1zsbEM5Hmx64s12Rd9AYgGoiCz4OJ4M/oRnkE16/Q==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2182,9 +2182,9 @@
} }
}, },
"node_modules/@next/swc-linux-arm64-musl": { "node_modules/@next/swc-linux-arm64-musl": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.7.tgz",
"integrity": "sha512-OHoC6gO7XfjstgwR+z6UHKlvhqJfyMtNaJidjx3sEcfaDwS7R2lqR5AABi8PuilGgi0BO0O0sCXqLlpp3a0emQ==", "integrity": "sha512-T1kD2FWOEy5WPidOn1si0rYmWORNch4a/NR52Ghyp4q7KyxOCuiOfZzyhVC5tsLIBDH3+cNdB5DkD9afpNDaOw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2197,9 +2197,9 @@
} }
}, },
"node_modules/@next/swc-linux-x64-gnu": { "node_modules/@next/swc-linux-x64-gnu": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.7.tgz",
"integrity": "sha512-zHZxPGkUlpfNJCboUrFqwlwEX5vI9LSN70b8XEb0DYzzlrZyCyOi7hwDp/+3Urm9AB7YCAJkgR5Sp1XBVjHdfQ==", "integrity": "sha512-zaEC+iEiAHNdhl6fuwl0H0shnTzQoAoJiDYBUze8QTntE/GNPfTYpYboxF5LRYIjBwETUatvE0T64W6SKDipvg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2212,9 +2212,9 @@
} }
}, },
"node_modules/@next/swc-linux-x64-musl": { "node_modules/@next/swc-linux-x64-musl": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.7.tgz",
"integrity": "sha512-K/Y8lYGTwTpv5ME8PSJxwxLolaDRdVy+lOd9yMRMiQE0BLUhtxtCWC9ypV42uh9WpLjoaD0joOsB9Q6mbrSGJg==", "integrity": "sha512-X6r12F8d8SKAtYJqLZBBMIwEqcTRvUdVm+xIq+l6pJqlgT2tNsLLf2i5Cl88xSsIytBICGsCNNHd+siD2fbWBA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -2227,9 +2227,9 @@
} }
}, },
"node_modules/@next/swc-win32-arm64-msvc": { "node_modules/@next/swc-win32-arm64-msvc": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.7.tgz",
"integrity": "sha512-U6LtxEUrjBL2tpW+Kr1nHCSJWNeIed7U7l5o7FiKGGwGgIlFi4UHDiLI6TQ2lxi20fAU33CsruV3U0GuzMlXIw==", "integrity": "sha512-NPnmnV+vEIxnu6SUvjnuaWRglZzw4ox5n/MQTxeUhb5iwVWFedolPFebMNwgrWu4AELwvTdGtWjqof53AiWHcw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -2242,9 +2242,9 @@
} }
}, },
"node_modules/@next/swc-win32-ia32-msvc": { "node_modules/@next/swc-win32-ia32-msvc": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.7.tgz",
"integrity": "sha512-eEBeAqpCfhdPSlCZCayjCiyIllVqy4tcqvm1xmg3BgJG0G5ITiMM4Cw2WVeRSgWDJqQGRyyb+q8Y2ltzhXOWsQ==", "integrity": "sha512-6Hxijm6/a8XqLQpOOf/XuwWRhcuc/g4rBB2oxjgCMuV9Xlr2bLs5+lXyh8w9YbAUMYR3iC9mgOlXbHa79elmXw==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@ -2257,9 +2257,9 @@
} }
}, },
"node_modules/@next/swc-win32-x64-msvc": { "node_modules/@next/swc-win32-x64-msvc": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.7.tgz",
"integrity": "sha512-OrZs94AuO3ZS5tnqlyPRNgfWvboXaDQCi5aXGve3o3C+Sj0ctMUV9+Do+0zMvvLRumR8E0PTWKvtz9n5vzIsWw==", "integrity": "sha512-sW9Yt36Db1nXJL+mTr2Wo0y+VkPWeYhygvcHj1FF0srVtV+VoDjxleKtny21QHaG05zdeZnw2fCtf2+dEqgwqA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -6239,11 +6239,10 @@
} }
}, },
"node_modules/lucide-react": { "node_modules/lucide-react": {
"version": "0.104.1", "version": "0.248.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.104.1.tgz", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.248.0.tgz",
"integrity": "sha512-BKvhulnLKmBj+6pqUN5ViYk4a5fabMgc4B0a4ZLUnbRqkDDWH3h7Iet6U4WbesJzjWauQrXUlEvQCe5XpFuRnw==", "integrity": "sha512-zMqVvwsvhLV6ooekfM8HEngR/lLGkIgOwZ4X140K+CDdAPwfp9LARnmJhMF32wlDganAIVEgN7saIv3pcCLUVw==",
"peerDependencies": { "peerDependencies": {
"prop-types": "^15.7.2",
"react": "^16.5.1 || ^17.0.0 || ^18.0.0" "react": "^16.5.1 || ^17.0.0 || ^18.0.0"
} }
}, },
@ -6375,11 +6374,11 @@
"dev": true "dev": true
}, },
"node_modules/next": { "node_modules/next": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/next/-/next-13.4.6.tgz", "resolved": "https://registry.npmjs.org/next/-/next-13.4.7.tgz",
"integrity": "sha512-sjVqjxU+U2aXZnYt4Ud6CTLNNwWjdSfMgemGpIQJcN3Z7Jni9xRWbR0ie5fQzCg87aLqQVhKA2ud2gPoqJ9lGw==", "integrity": "sha512-M8z3k9VmG51SRT6v5uDKdJXcAqLzP3C+vaKfLIAM0Mhx1um1G7MDnO63+m52qPdZfrTFzMZNzfsgvm3ghuVHIQ==",
"dependencies": { "dependencies": {
"@next/env": "13.4.6", "@next/env": "13.4.7",
"@swc/helpers": "0.5.1", "@swc/helpers": "0.5.1",
"busboy": "1.6.0", "busboy": "1.6.0",
"caniuse-lite": "^1.0.30001406", "caniuse-lite": "^1.0.30001406",
@ -6395,15 +6394,15 @@
"node": ">=16.8.0" "node": ">=16.8.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@next/swc-darwin-arm64": "13.4.6", "@next/swc-darwin-arm64": "13.4.7",
"@next/swc-darwin-x64": "13.4.6", "@next/swc-darwin-x64": "13.4.7",
"@next/swc-linux-arm64-gnu": "13.4.6", "@next/swc-linux-arm64-gnu": "13.4.7",
"@next/swc-linux-arm64-musl": "13.4.6", "@next/swc-linux-arm64-musl": "13.4.7",
"@next/swc-linux-x64-gnu": "13.4.6", "@next/swc-linux-x64-gnu": "13.4.7",
"@next/swc-linux-x64-musl": "13.4.6", "@next/swc-linux-x64-musl": "13.4.7",
"@next/swc-win32-arm64-msvc": "13.4.6", "@next/swc-win32-arm64-msvc": "13.4.7",
"@next/swc-win32-ia32-msvc": "13.4.6", "@next/swc-win32-ia32-msvc": "13.4.7",
"@next/swc-win32-x64-msvc": "13.4.6" "@next/swc-win32-x64-msvc": "13.4.7"
}, },
"peerDependencies": { "peerDependencies": {
"@opentelemetry/api": "^1.1.0", "@opentelemetry/api": "^1.1.0",
@ -10056,9 +10055,9 @@
} }
}, },
"@next/env": { "@next/env": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.7.tgz",
"integrity": "sha512-nqUxEtvDqFhmV1/awSg0K2XHNwkftNaiUqCYO9e6+MYmqNObpKVl7OgMkGaQ2SZnFx5YqF0t60ZJTlyJIDAijg==" "integrity": "sha512-ZlbiFulnwiFsW9UV1ku1OvX/oyIPLtMk9p/nnvDSwI0s7vSoZdRtxXNsaO+ZXrLv/pMbXVGq4lL8TbY9iuGmVw=="
}, },
"@next/eslint-plugin-next": { "@next/eslint-plugin-next": {
"version": "13.0.6", "version": "13.0.6",
@ -10070,57 +10069,57 @@
} }
}, },
"@next/swc-darwin-arm64": { "@next/swc-darwin-arm64": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.7.tgz",
"integrity": "sha512-ahi6VP98o4HV19rkOXPSUu+ovfHfUxbJQ7VVJ7gL2FnZRr7onEFC1oGQ6NQHpm8CxpIzSSBW79kumlFMOmZVjg==", "integrity": "sha512-VZTxPv1b59KGiv/pZHTO5Gbsdeoxcj2rU2cqJu03btMhHpn3vwzEK0gUSVC/XW96aeGO67X+cMahhwHzef24/w==",
"optional": true "optional": true
}, },
"@next/swc-darwin-x64": { "@next/swc-darwin-x64": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.7.tgz",
"integrity": "sha512-13cXxKFsPJIJKzUqrU5XB1mc0xbUgYsRcdH6/rB8c4NMEbWGdtD4QoK9ShN31TZdePpD4k416Ur7p+deMIxnnA==", "integrity": "sha512-gO2bw+2Ymmga+QYujjvDz9955xvYGrWofmxTq7m70b9pDPvl7aDFABJOZ2a8SRCuSNB5mXU8eTOmVVwyp/nAew==",
"optional": true "optional": true
}, },
"@next/swc-linux-arm64-gnu": { "@next/swc-linux-arm64-gnu": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.7.tgz",
"integrity": "sha512-Ti+NMHEjTNktCVxNjeWbYgmZvA2AqMMI2AMlzkXsU7W4pXCMhrryAmAIoo+7YdJbsx01JQWYVxGe62G6DoCLaA==", "integrity": "sha512-6cqp3vf1eHxjIDhEOc7Mh/s8z1cwc/l5B6ZNkOofmZVyu1zsbEM5Hmx64s12Rd9AYgGoiCz4OJ4M/oRnkE16/Q==",
"optional": true "optional": true
}, },
"@next/swc-linux-arm64-musl": { "@next/swc-linux-arm64-musl": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.7.tgz",
"integrity": "sha512-OHoC6gO7XfjstgwR+z6UHKlvhqJfyMtNaJidjx3sEcfaDwS7R2lqR5AABi8PuilGgi0BO0O0sCXqLlpp3a0emQ==", "integrity": "sha512-T1kD2FWOEy5WPidOn1si0rYmWORNch4a/NR52Ghyp4q7KyxOCuiOfZzyhVC5tsLIBDH3+cNdB5DkD9afpNDaOw==",
"optional": true "optional": true
}, },
"@next/swc-linux-x64-gnu": { "@next/swc-linux-x64-gnu": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.7.tgz",
"integrity": "sha512-zHZxPGkUlpfNJCboUrFqwlwEX5vI9LSN70b8XEb0DYzzlrZyCyOi7hwDp/+3Urm9AB7YCAJkgR5Sp1XBVjHdfQ==", "integrity": "sha512-zaEC+iEiAHNdhl6fuwl0H0shnTzQoAoJiDYBUze8QTntE/GNPfTYpYboxF5LRYIjBwETUatvE0T64W6SKDipvg==",
"optional": true "optional": true
}, },
"@next/swc-linux-x64-musl": { "@next/swc-linux-x64-musl": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.7.tgz",
"integrity": "sha512-K/Y8lYGTwTpv5ME8PSJxwxLolaDRdVy+lOd9yMRMiQE0BLUhtxtCWC9ypV42uh9WpLjoaD0joOsB9Q6mbrSGJg==", "integrity": "sha512-X6r12F8d8SKAtYJqLZBBMIwEqcTRvUdVm+xIq+l6pJqlgT2tNsLLf2i5Cl88xSsIytBICGsCNNHd+siD2fbWBA==",
"optional": true "optional": true
}, },
"@next/swc-win32-arm64-msvc": { "@next/swc-win32-arm64-msvc": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.7.tgz",
"integrity": "sha512-U6LtxEUrjBL2tpW+Kr1nHCSJWNeIed7U7l5o7FiKGGwGgIlFi4UHDiLI6TQ2lxi20fAU33CsruV3U0GuzMlXIw==", "integrity": "sha512-NPnmnV+vEIxnu6SUvjnuaWRglZzw4ox5n/MQTxeUhb5iwVWFedolPFebMNwgrWu4AELwvTdGtWjqof53AiWHcw==",
"optional": true "optional": true
}, },
"@next/swc-win32-ia32-msvc": { "@next/swc-win32-ia32-msvc": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.7.tgz",
"integrity": "sha512-eEBeAqpCfhdPSlCZCayjCiyIllVqy4tcqvm1xmg3BgJG0G5ITiMM4Cw2WVeRSgWDJqQGRyyb+q8Y2ltzhXOWsQ==", "integrity": "sha512-6Hxijm6/a8XqLQpOOf/XuwWRhcuc/g4rBB2oxjgCMuV9Xlr2bLs5+lXyh8w9YbAUMYR3iC9mgOlXbHa79elmXw==",
"optional": true "optional": true
}, },
"@next/swc-win32-x64-msvc": { "@next/swc-win32-x64-msvc": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.6.tgz", "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.7.tgz",
"integrity": "sha512-OrZs94AuO3ZS5tnqlyPRNgfWvboXaDQCi5aXGve3o3C+Sj0ctMUV9+Do+0zMvvLRumR8E0PTWKvtz9n5vzIsWw==", "integrity": "sha512-sW9Yt36Db1nXJL+mTr2Wo0y+VkPWeYhygvcHj1FF0srVtV+VoDjxleKtny21QHaG05zdeZnw2fCtf2+dEqgwqA==",
"optional": true "optional": true
}, },
"@nicolo-ribaudo/chokidar-2": { "@nicolo-ribaudo/chokidar-2": {
@ -12994,9 +12993,9 @@
} }
}, },
"lucide-react": { "lucide-react": {
"version": "0.104.1", "version": "0.248.0",
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.104.1.tgz", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.248.0.tgz",
"integrity": "sha512-BKvhulnLKmBj+6pqUN5ViYk4a5fabMgc4B0a4ZLUnbRqkDDWH3h7Iet6U4WbesJzjWauQrXUlEvQCe5XpFuRnw==", "integrity": "sha512-zMqVvwsvhLV6ooekfM8HEngR/lLGkIgOwZ4X140K+CDdAPwfp9LARnmJhMF32wlDganAIVEgN7saIv3pcCLUVw==",
"requires": {} "requires": {}
}, },
"magic-string": { "magic-string": {
@ -13093,20 +13092,20 @@
"dev": true "dev": true
}, },
"next": { "next": {
"version": "13.4.6", "version": "13.4.7",
"resolved": "https://registry.npmjs.org/next/-/next-13.4.6.tgz", "resolved": "https://registry.npmjs.org/next/-/next-13.4.7.tgz",
"integrity": "sha512-sjVqjxU+U2aXZnYt4Ud6CTLNNwWjdSfMgemGpIQJcN3Z7Jni9xRWbR0ie5fQzCg87aLqQVhKA2ud2gPoqJ9lGw==", "integrity": "sha512-M8z3k9VmG51SRT6v5uDKdJXcAqLzP3C+vaKfLIAM0Mhx1um1G7MDnO63+m52qPdZfrTFzMZNzfsgvm3ghuVHIQ==",
"requires": { "requires": {
"@next/env": "13.4.6", "@next/env": "13.4.7",
"@next/swc-darwin-arm64": "13.4.6", "@next/swc-darwin-arm64": "13.4.7",
"@next/swc-darwin-x64": "13.4.6", "@next/swc-darwin-x64": "13.4.7",
"@next/swc-linux-arm64-gnu": "13.4.6", "@next/swc-linux-arm64-gnu": "13.4.7",
"@next/swc-linux-arm64-musl": "13.4.6", "@next/swc-linux-arm64-musl": "13.4.7",
"@next/swc-linux-x64-gnu": "13.4.6", "@next/swc-linux-x64-gnu": "13.4.7",
"@next/swc-linux-x64-musl": "13.4.6", "@next/swc-linux-x64-musl": "13.4.7",
"@next/swc-win32-arm64-msvc": "13.4.6", "@next/swc-win32-arm64-msvc": "13.4.7",
"@next/swc-win32-ia32-msvc": "13.4.6", "@next/swc-win32-ia32-msvc": "13.4.7",
"@next/swc-win32-x64-msvc": "13.4.6", "@next/swc-win32-x64-msvc": "13.4.7",
"@swc/helpers": "0.5.1", "@swc/helpers": "0.5.1",
"busboy": "1.6.0", "busboy": "1.6.0",
"caniuse-lite": "^1.0.30001406", "caniuse-lite": "^1.0.30001406",

View file

@ -26,8 +26,8 @@
"avvvatars-react": "^0.4.2", "avvvatars-react": "^0.4.2",
"formik": "^2.2.9", "formik": "^2.2.9",
"framer-motion": "^7.3.6", "framer-motion": "^7.3.6",
"lucide-react": "^0.104.1", "lucide-react": "^0.248.0",
"next": "^13.4.6", "next": "^13.4.7-canary.4",
"re-resizable": "^6.9.9", "re-resizable": "^6.9.9",
"react": "^18.2.0", "react": "^18.2.0",
"react-beautiful-dnd": "^13.1.1", "react-beautiful-dnd": "^13.1.1",

View file

@ -1,3 +1,3 @@
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.4997 5.22045C14.4997 5.22045 14.4997 5.22045 14.4997 5.16378L14.4572 5.05753C14.4424 5.03499 14.4259 5.01367 14.4076 4.99379C14.3887 4.9638 14.3674 4.93537 14.3439 4.90878L14.2801 4.8592L14.1668 4.80254L8.85431 1.52295C8.74173 1.45259 8.61165 1.41528 8.47889 1.41528C8.34614 1.41528 8.21605 1.45259 8.10348 1.52295L2.83348 4.80254L2.76973 4.8592L2.70598 4.90878C2.6825 4.93537 2.66118 4.9638 2.64223 4.99379C2.62402 5.01367 2.60744 5.03499 2.59264 5.05753L2.55014 5.16378C2.55014 5.16378 2.55014 5.16378 2.55014 5.22045C2.54318 5.28164 2.54318 5.34343 2.55014 5.40462V11.5955C2.5499 11.7158 2.58034 11.8343 2.63859 11.9396C2.69684 12.045 2.78098 12.1337 2.88306 12.1975L8.19556 15.4771C8.22826 15.4973 8.26421 15.5117 8.30181 15.5196C8.30181 15.5196 8.33723 15.5196 8.35848 15.5196C8.47831 15.5576 8.60697 15.5576 8.72681 15.5196C8.72681 15.5196 8.76223 15.5196 8.78348 15.5196C8.82108 15.5117 8.85703 15.4973 8.88973 15.4771V15.4771L14.1668 12.1975C14.2689 12.1337 14.353 12.045 14.4113 11.9396C14.4695 11.8343 14.5 11.7158 14.4997 11.5955V5.40462C14.5067 5.34343 14.5067 5.28164 14.4997 5.22045V5.22045ZM7.79181 13.6071L3.89598 11.1988V6.67962L7.79181 9.08087V13.6071ZM8.50014 7.85545L4.53348 5.40462L8.50014 2.96087L12.4668 5.40462L8.50014 7.85545ZM13.1043 11.1988L9.20848 13.6071V9.08087L13.1043 6.67962V11.1988Z" fill="#0F0F0F"/> <path d="M14.4997 5.22045C14.4997 5.22045 14.4997 5.22045 14.4997 5.16378L14.4572 5.05753C14.4424 5.03499 14.4259 5.01367 14.4076 4.99379C14.3887 4.9638 14.3674 4.93537 14.3439 4.90878L14.2801 4.8592L14.1668 4.80254L8.85431 1.52295C8.74173 1.45259 8.61165 1.41528 8.47889 1.41528C8.34614 1.41528 8.21605 1.45259 8.10348 1.52295L2.83348 4.80254L2.76973 4.8592L2.70598 4.90878C2.6825 4.93537 2.66118 4.9638 2.64223 4.99379C2.62402 5.01367 2.60744 5.03499 2.59264 5.05753L2.55014 5.16378C2.55014 5.16378 2.55014 5.16378 2.55014 5.22045C2.54318 5.28164 2.54318 5.34343 2.55014 5.40462V11.5955C2.5499 11.7158 2.58034 11.8343 2.63859 11.9396C2.69684 12.045 2.78098 12.1337 2.88306 12.1975L8.19556 15.4771C8.22826 15.4973 8.26421 15.5117 8.30181 15.5196C8.30181 15.5196 8.33723 15.5196 8.35848 15.5196C8.47831 15.5576 8.60697 15.5576 8.72681 15.5196C8.72681 15.5196 8.76223 15.5196 8.78348 15.5196C8.82108 15.5117 8.85703 15.4973 8.88973 15.4771V15.4771L14.1668 12.1975C14.2689 12.1337 14.353 12.045 14.4113 11.9396C14.4695 11.8343 14.5 11.7158 14.4997 11.5955V5.40462C14.5067 5.34343 14.5067 5.28164 14.4997 5.22045V5.22045ZM7.79181 13.6071L3.89598 11.1988V6.67962L7.79181 9.08087V13.6071ZM8.50014 7.85545L4.53348 5.40462L8.50014 2.96087L12.4668 5.40462L8.50014 7.85545ZM13.1043 11.1988L9.20848 13.6071V9.08087L13.1043 6.67962V11.1988Z" fill="#0F0F0F"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before After
Before After

View file

@ -1,3 +1,3 @@
<svg width="13" height="15" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="16" height="18" viewBox="0 0 13 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.7507 0.416626H3.66732C2.91587 0.416626 2.1952 0.715137 1.66385 1.24649C1.1325 1.77784 0.833984 2.49851 0.833984 3.24996V11.75C0.833984 12.5014 1.1325 13.2221 1.66385 13.7534C2.1952 14.2848 2.91587 14.5833 3.66732 14.5833H10.7507C11.1264 14.5833 11.4867 14.434 11.7524 14.1684C12.0181 13.9027 12.1673 13.5423 12.1673 13.1666V1.83329C12.1673 1.45757 12.0181 1.09723 11.7524 0.831558C11.4867 0.565881 11.1264 0.416626 10.7507 0.416626ZM2.25065 3.24996C2.25065 2.87424 2.39991 2.5139 2.66558 2.24822C2.93126 1.98255 3.29159 1.83329 3.66732 1.83329H10.7507V8.91663H3.66732C3.16797 8.91872 2.67848 9.05578 2.25065 9.31329V3.24996ZM3.66732 13.1666C3.29159 13.1666 2.93126 13.0174 2.66558 12.7517C2.39991 12.486 2.25065 12.1257 2.25065 11.75C2.25065 11.3742 2.39991 11.0139 2.66558 10.7482C2.93126 10.4825 3.29159 10.3333 3.66732 10.3333H10.7507V13.1666H3.66732ZM5.08398 4.66663H7.91732C8.10518 4.66663 8.28535 4.592 8.41819 4.45916C8.55102 4.32632 8.62565 4.14615 8.62565 3.95829C8.62565 3.77043 8.55102 3.59026 8.41819 3.45743C8.28535 3.32459 8.10518 3.24996 7.91732 3.24996H5.08398C4.89612 3.24996 4.71596 3.32459 4.58312 3.45743C4.45028 3.59026 4.37565 3.77043 4.37565 3.95829C4.37565 4.14615 4.45028 4.32632 4.58312 4.45916C4.71596 4.592 4.89612 4.66663 5.08398 4.66663V4.66663Z" fill="black"/> <path d="M10.7507 0.416626H3.66732C2.91587 0.416626 2.1952 0.715137 1.66385 1.24649C1.1325 1.77784 0.833984 2.49851 0.833984 3.24996V11.75C0.833984 12.5014 1.1325 13.2221 1.66385 13.7534C2.1952 14.2848 2.91587 14.5833 3.66732 14.5833H10.7507C11.1264 14.5833 11.4867 14.434 11.7524 14.1684C12.0181 13.9027 12.1673 13.5423 12.1673 13.1666V1.83329C12.1673 1.45757 12.0181 1.09723 11.7524 0.831558C11.4867 0.565881 11.1264 0.416626 10.7507 0.416626ZM2.25065 3.24996C2.25065 2.87424 2.39991 2.5139 2.66558 2.24822C2.93126 1.98255 3.29159 1.83329 3.66732 1.83329H10.7507V8.91663H3.66732C3.16797 8.91872 2.67848 9.05578 2.25065 9.31329V3.24996ZM3.66732 13.1666C3.29159 13.1666 2.93126 13.0174 2.66558 12.7517C2.39991 12.486 2.25065 12.1257 2.25065 11.75C2.25065 11.3742 2.39991 11.0139 2.66558 10.7482C2.93126 10.4825 3.29159 10.3333 3.66732 10.3333H10.7507V13.1666H3.66732ZM5.08398 4.66663H7.91732C8.10518 4.66663 8.28535 4.592 8.41819 4.45916C8.55102 4.32632 8.62565 4.14615 8.62565 3.95829C8.62565 3.77043 8.55102 3.59026 8.41819 3.45743C8.28535 3.32459 8.10518 3.24996 7.91732 3.24996H5.08398C4.89612 3.24996 4.71596 3.32459 4.58312 3.45743C4.45028 3.59026 4.37565 3.77043 4.37565 3.95829C4.37565 4.14615 4.45028 4.32632 4.58312 4.45916C4.71596 4.592 4.89612 4.66663 5.08398 4.66663V4.66663Z" fill="black"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before After
Before After

View file

@ -0,0 +1,5 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M16.5751 7.95841C16.5059 7.82098 16.3999 7.70541 16.269 7.62451C16.1381 7.54361 15.9874 7.50054 15.8335 7.50008H11.6668V2.50008C11.6757 2.31731 11.6243 2.13669 11.5204 1.98608C11.4164 1.83547 11.2658 1.72325 11.0918 1.66674C10.9245 1.6117 10.744 1.61108 10.5763 1.66498C10.4087 1.71888 10.2624 1.82452 10.1585 1.96674L3.4918 11.1334C3.40827 11.2541 3.35811 11.3948 3.3464 11.5411C3.3347 11.6874 3.36186 11.8343 3.42513 11.9667C3.4834 12.1182 3.58462 12.2493 3.71637 12.3441C3.84812 12.4388 4.00467 12.493 4.1668 12.5001H8.33346V17.5001C8.33359 17.6758 8.38927 17.847 8.49254 17.9892C8.59581 18.1314 8.74139 18.2373 8.90846 18.2917C8.99219 18.3177 9.07915 18.3317 9.1668 18.3334C9.29828 18.3338 9.42799 18.303 9.5453 18.2436C9.66262 18.1842 9.76422 18.0979 9.8418 17.9917L16.5085 8.82508C16.5982 8.70074 16.652 8.55404 16.6637 8.40112C16.6755 8.24821 16.6448 8.09502 16.5751 7.95841ZM10.0001 14.9334V11.6667C10.0001 11.4457 9.91233 11.2338 9.75605 11.0775C9.59977 10.9212 9.38781 10.8334 9.1668 10.8334H5.83346L10.0001 5.06674V8.33341C10.0001 8.55442 10.0879 8.76638 10.2442 8.92267C10.4005 9.07895 10.6124 9.16674 10.8335 9.16674H14.1668L10.0001 14.9334Z"
fill="black" />
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -26,7 +26,7 @@
"@images/*": ["public/img/*"], "@images/*": ["public/img/*"],
"@styles/*": ["styles/*"], "@styles/*": ["styles/*"],
"@services/*": ["services/*"], "@services/*": ["services/*"],
"@editor/*": ["components/Editor/*"] "@editor/*": ["components/Objects/Editor/*"]
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx","**/**/*.tsx", ".next/types/**/*.ts"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx","**/**/*.tsx", ".next/types/**/*.ts"],