From 19b7dd650e83c760d53d2a601e28338ddede0eda Mon Sep 17 00:00:00 2001 From: swve Date: Sat, 22 Oct 2022 23:03:36 +0200 Subject: [PATCH] feat: init coursechapter + elements interface --- front/components/auth/HeaderProfileBox.tsx | 70 ++--- front/components/drags/chapter.tsx | 47 ++++ front/components/drags/data.ts | 16 ++ front/components/drags/element.tsx | 31 +++ front/components/security/AuthProvider.tsx | 8 +- front/next.config.js | 2 +- front/package-lock.json | 247 ++++++++++++++++-- front/package.json | 2 + front/pages/login.tsx | 9 +- .../course/[courseid]/edit/index.tsx | 160 ++++++++++++ .../{[courseid].tsx => [courseid]/index.tsx} | 11 +- .../course/[courseid]/new/chapters.tsx | 0 front/pages/org/[orgslug]/courses/index.tsx | 7 +- front/styles/globals.css | 2 +- front/tsconfig.json | 2 +- src/services/courses.py | 3 - 16 files changed, 542 insertions(+), 75 deletions(-) create mode 100644 front/components/drags/chapter.tsx create mode 100644 front/components/drags/data.ts create mode 100644 front/components/drags/element.tsx create mode 100644 front/pages/org/[orgslug]/course/[courseid]/edit/index.tsx rename front/pages/org/[orgslug]/course/{[courseid].tsx => [courseid]/index.tsx} (80%) create mode 100644 front/pages/org/[orgslug]/course/[courseid]/new/chapters.tsx diff --git a/front/components/auth/HeaderProfileBox.tsx b/front/components/auth/HeaderProfileBox.tsx index d92586fc..f9e023e8 100644 --- a/front/components/auth/HeaderProfileBox.tsx +++ b/front/components/auth/HeaderProfileBox.tsx @@ -2,53 +2,57 @@ import React from "react"; import styled from "styled-components"; import Link from "next/link"; import { AuthContext } from "../security/AuthProvider"; +import { getBackendUrl } from "../../services/config"; export const HeaderProfileBox = () => { const auth: any = React.useContext(AuthContext); - + return ( - {" "} - - HeaderProfileBox
isLogged : {String(auth.isAuthenticated)}
user : {String(auth.userInfo.username)} -
{" "} - - - + {!auth.isAuthenticated && ( + + + + )} + {auth.isAuthenticated && +
{auth.userInfo.username}
+
+
}
); }; +const AccountArea = styled.div` + padding-right: 20px; + display: flex; + place-items: center; + + div{ + margin-right: 10px; + } + img{ + width: 29px; + border-radius: 19px; + } +`; + const ProfileArea = styled.div` display: flex; place-items: stretch; place-items: center; - span { - position: relative; - display: block; - top: 32px; - right: -20px; - padding: 6px; - font-size: 12px; - margin: 3px; - // blur - background-color: #19191960; - border-radius: 5px; - color: white; - width: auto; - } + `; const UnidentifiedArea = styled.div` diff --git a/front/components/drags/chapter.tsx b/front/components/drags/chapter.tsx new file mode 100644 index 00000000..00909443 --- /dev/null +++ b/front/components/drags/chapter.tsx @@ -0,0 +1,47 @@ +import React from "react"; +import styled from "styled-components"; +import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"; +import Element, { ElementWrapper } from "./element"; + + +const ChapterWrapper = styled.div` + margin-bottom: 5px; + padding: 11px; + background-color: #00000010; + width: 310px; + display: block; + border-radius: 9px; + border: 1px solid rgba(255, 255, 255, 0.19); + box-shadow: 0px 13px 33px -13px rgb(0 0 0 / 12%); + transition: all 0.2s ease; +`; + +function Chapter(props: any) { + return ( + + {(provided, snapshot) => ( + +

{props.info.list.chapter.title}

+ + {(provided) => ( + + {props.info.list.elements.map((element: any, index: any) => ( + + ))} + {provided.placeholder} + + )} + +
+ )} +
+ ); +} + +const ElementsList = styled.div` + padding: 10px; +`; + + + +export default Chapter; diff --git a/front/components/drags/data.ts b/front/components/drags/data.ts new file mode 100644 index 00000000..5e1ee3b8 --- /dev/null +++ b/front/components/drags/data.ts @@ -0,0 +1,16 @@ +export const initialData = { + elements: { + "element-1": { id: "element-1", content: "First element" }, + "element-2": { id: "element-2", content: "Second element" }, + "element-3": { id: "element-3", content: "Third element" }, + "element-4": { id: "element-4", content: "Fourth element" }, + "element-5": { id: "element-5", content: "Fifth element" }, + }, + chapters: { + "chapter-1": { id: "chapter-1", title: "Chapter 1", elementIds: ["element-1", "element-2", "element-3", ] }, + "chapter-2": { id: "chapter-2", title: "Chapter 2", elementIds: ["element-4"] }, + "chapter-3": { id: "chapter-3", title: "Chapter 3", elementIds: ["element-5"] }, + + }, + chapterOrder: ["chapter-1", "chapter-2", "chapter-3"], +}; diff --git a/front/components/drags/element.tsx b/front/components/drags/element.tsx new file mode 100644 index 00000000..ea7b5b84 --- /dev/null +++ b/front/components/drags/element.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import { Draggable } from "react-beautiful-dnd"; +import styled from "styled-components"; + +function Element(props: any) { + return ( + + {(provided) => ( + + {props.element.content} + + )} + + ); +} + +export const ElementWrapper = styled.div` + padding: 10px; + padding-left: 17px; + list-style: none; + /* padding-left: 2px; */ + background-color: #8c949c33; + border-radius: 28px; + margin: 15px; + + &:hover { + background-color: #8c949c7b; + } + +`; +export default Element; diff --git a/front/components/security/AuthProvider.tsx b/front/components/security/AuthProvider.tsx index acb32de8..44d48fa4 100644 --- a/front/components/security/AuthProvider.tsx +++ b/front/components/security/AuthProvider.tsx @@ -34,11 +34,15 @@ const AuthProvider = (props: any) => { } } - // TODO(mvp) : fix performance issues > no need to check auth on every render + + useEffect(() => { - if (!auth.isAuthenticated) { + if (auth.isLoading) { checkAuth(); } + return () => { + auth.isLoading = false; + } }, []); return {props.children}; diff --git a/front/next.config.js b/front/next.config.js index ce644598..6fee0674 100644 --- a/front/next.config.js +++ b/front/next.config.js @@ -1,6 +1,6 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - reactStrictMode: true, + reactStrictMode: false, swcMinify: true, compiler: { styledComponents: true, diff --git a/front/package-lock.json b/front/package-lock.json index bd75d956..8b47b95a 100644 --- a/front/package-lock.json +++ b/front/package-lock.json @@ -8,15 +8,18 @@ "name": "learnhouse", "version": "0.1.0", "dependencies": { + "crypto": "^1.0.1", "framer-motion": "^7.3.6", "next": "12.3.1", "react": "18.2.0", + "react-beautiful-dnd": "^13.1.1", "react-dom": "18.2.0", "styled-components": "^5.3.5" }, "devDependencies": { "@types/node": "18.7.18", "@types/react": "18.0.20", + "@types/react-beautiful-dnd": "^13.1.2", "@types/react-dom": "18.0.6", "@types/styled-components": "^5.1.26", "autoprefixer": "^10.4.12", @@ -221,7 +224,6 @@ "version": "7.19.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz", "integrity": "sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.13.4" }, @@ -748,7 +750,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", - "dev": true, "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -769,20 +770,27 @@ "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "node_modules/@types/react": { "version": "18.0.20", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.20.tgz", "integrity": "sha512-MWul1teSPxujEHVwZl4a5HxQ9vVNsjTchVA+xRqv/VYGCuKGAU6UhfrTdF5aBefwD1BHUD8i/zq+O/vyCm/FrA==", - "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, + "node_modules/@types/react-beautiful-dnd": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.2.tgz", + "integrity": "sha512-+OvPkB8CdE/bGdXKyIhc/Lm2U7UAYCCJgsqmopFmh9gbAudmslkI8eOrPDjg4JhwSE6wytz4a3/wRjKtovHVJg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-dom": { "version": "18.0.6", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.6.tgz", @@ -792,11 +800,21 @@ "@types/react": "*" } }, + "node_modules/@types/react-redux": { + "version": "7.1.24", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz", + "integrity": "sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "node_modules/@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "dev": true + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, "node_modules/@types/styled-components": { "version": "5.1.26", @@ -1291,6 +1309,20 @@ "node": ">= 8" } }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, + "node_modules/css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "dependencies": { + "tiny-invariant": "^1.0.6" + } + }, "node_modules/css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", @@ -1312,8 +1344,7 @@ "node_modules/csstype": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", - "dev": true + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -2728,6 +2759,11 @@ "node": ">=10" } }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2884,7 +2920,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3166,7 +3201,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -3202,6 +3236,11 @@ } ] }, + "node_modules/raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -3213,6 +3252,24 @@ "node": ">=0.10.0" } }, + "node_modules/react-beautiful-dnd": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz", + "integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==", + "dependencies": { + "@babel/runtime": "^7.9.2", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.2.0", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.5 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -3230,11 +3287,47 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-redux": { + "version": "7.2.9", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", + "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "peerDependencies": { + "react": "^16.8.3 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/react-redux/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/redux": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", + "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, "node_modules/regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", @@ -3606,6 +3699,11 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -3751,6 +3849,14 @@ "punycode": "^2.1.0" } }, + "node_modules/use-memo-one": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", + "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", @@ -3969,7 +4075,6 @@ "version": "7.19.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.0.tgz", "integrity": "sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==", - "dev": true, "requires": { "regenerator-runtime": "^0.13.4" } @@ -4325,7 +4430,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", - "dev": true, "requires": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -4346,20 +4450,27 @@ "@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" }, "@types/react": { "version": "18.0.20", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.20.tgz", "integrity": "sha512-MWul1teSPxujEHVwZl4a5HxQ9vVNsjTchVA+xRqv/VYGCuKGAU6UhfrTdF5aBefwD1BHUD8i/zq+O/vyCm/FrA==", - "dev": true, "requires": { "@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2" } }, + "@types/react-beautiful-dnd": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.2.tgz", + "integrity": "sha512-+OvPkB8CdE/bGdXKyIhc/Lm2U7UAYCCJgsqmopFmh9gbAudmslkI8eOrPDjg4JhwSE6wytz4a3/wRjKtovHVJg==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, "@types/react-dom": { "version": "18.0.6", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.6.tgz", @@ -4369,11 +4480,21 @@ "@types/react": "*" } }, + "@types/react-redux": { + "version": "7.1.24", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.24.tgz", + "integrity": "sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", - "dev": true + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, "@types/styled-components": { "version": "5.1.26", @@ -4698,6 +4819,19 @@ "which": "^2.0.1" } }, + "crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" + }, + "css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "requires": { + "tiny-invariant": "^1.0.6" + } + }, "css-color-keywords": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", @@ -4716,8 +4850,7 @@ "csstype": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", - "dev": true + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" }, "damerau-levenshtein": { "version": "1.0.8", @@ -5782,6 +5915,11 @@ "yallist": "^4.0.0" } }, + "memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5882,8 +6020,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "object-inspect": { "version": "1.12.2", @@ -6080,7 +6217,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -6099,6 +6235,11 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, "react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -6107,6 +6248,20 @@ "loose-envify": "^1.1.0" } }, + "react-beautiful-dnd": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz", + "integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==", + "requires": { + "@babel/runtime": "^7.9.2", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.2.0", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" + } + }, "react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -6121,11 +6276,38 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-redux": { + "version": "7.2.9", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", + "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", + "requires": { + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "dependencies": { + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + } + } + }, + "redux": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", + "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, "regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, "regexp.prototype.flags": { "version": "1.4.3", @@ -6371,6 +6553,11 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -6471,6 +6658,12 @@ "punycode": "^2.1.0" } }, + "use-memo-one": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", + "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", + "requires": {} + }, "use-sync-external-store": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", diff --git a/front/package.json b/front/package.json index d9e9e849..06e16429 100644 --- a/front/package.json +++ b/front/package.json @@ -12,12 +12,14 @@ "framer-motion": "^7.3.6", "next": "12.3.1", "react": "18.2.0", + "react-beautiful-dnd": "^13.1.1", "react-dom": "18.2.0", "styled-components": "^5.3.5" }, "devDependencies": { "@types/node": "18.7.18", "@types/react": "18.0.20", + "@types/react-beautiful-dnd": "^13.1.2", "@types/react-dom": "18.0.6", "@types/styled-components": "^5.1.26", "autoprefixer": "^10.4.12", diff --git a/front/pages/login.tsx b/front/pages/login.tsx index 13018e19..e97147ff 100644 --- a/front/pages/login.tsx +++ b/front/pages/login.tsx @@ -1,3 +1,4 @@ +import Router from "next/router"; import React from "react"; import { Header } from "../components/ui/header"; import Layout from "../components/ui/layout"; @@ -12,7 +13,13 @@ const Login = () => { e.preventDefault(); console.log({ email, password }); alert(JSON.stringify({ email, password })); - loginAndGetToken(email, password); + try { + loginAndGetToken(email, password); + Router.push("/"); + } + catch (e) { + console.log(e); + } }; const handleEmailChange = (e: any) => { diff --git a/front/pages/org/[orgslug]/course/[courseid]/edit/index.tsx b/front/pages/org/[orgslug]/course/[courseid]/edit/index.tsx new file mode 100644 index 00000000..f28c07ee --- /dev/null +++ b/front/pages/org/[orgslug]/course/[courseid]/edit/index.tsx @@ -0,0 +1,160 @@ +import React from "react"; +import { useState, useEffect } from "react"; +import styled from "styled-components"; +import { Header } from "../../../../../../components/ui/header"; +import Layout from "../../../../../../components/ui/layout"; +import { Title } from "../../../../../../components/ui/styles/title"; +import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd"; +import { initialData } from "../../../../../../components/drags/data"; +import Chapter from "../../../../../../components/drags/chapter"; + +function CourseEdit() { + const [data, setData] = useState(initialData) as any; + const [winReady, setwinReady] = useState(false); + + useEffect(() => { + setwinReady(true); + }, []); + + // get a list of chapters order by chapter order + const getChapters = () => { + return data.chapterOrder.map((chapterId: any) => { + const chapter = data.chapters[chapterId]; + const elements = chapter.elementIds.map((elementId: any) => data.elements[elementId]); + return { + list: { + chapter: chapter, + elements: elements, + }, + }; + }); + }; + + const onDragEnd = (result: any) => { + const { destination, source, draggableId, type } = result; + console.log(result); + + // check if the element is dropped outside the droppable area + if (!destination) { + return; + } + + // check if the element is dropped in the same place + if (destination.droppableId === source.droppableId && destination.index === source.index) { + return; + } + //////////////////////////// CHAPTERS //////////////////////////// + if (type === "chapter") { + const newChapterOrder = Array.from(data.chapterOrder); + newChapterOrder.splice(source.index, 1); + newChapterOrder.splice(destination.index, 0, draggableId); + + const newState = { + ...data, + chapterOrder: newChapterOrder, + }; + console.log(newState); + + setData(newState); + return; + } + + //////////////////////// ELEMENTS IN SAME CHAPTERS //////////////////////////// + // check if the element is dropped in the same chapter + const start = data.chapters[source.droppableId]; + const finish = data.chapters[destination.droppableId]; + + // check if the element is dropped in the same chapter + if (start === finish) { + // create new arrays for chapters and elements + const chapter = data.chapters[source.droppableId]; + const newElementIds = Array.from(chapter.elementIds); + + // remove the element from the old position + newElementIds.splice(source.index, 1); + + // add the element to the new position + newElementIds.splice(destination.index, 0, draggableId); + + const newChapter = { + ...chapter, + elementIds: newElementIds, + }; + + const newState = { + ...data, + chapters: { + ...data.chapters, + [newChapter.id]: newChapter, + }, + }; + + setData(newState); + return; + } + + //////////////////////// ELEMENTS IN DIFF CHAPTERS //////////////////////////// + // check if the element is dropped in a different chapter + if (start !== finish) { + // create new arrays for chapters and elements + const startChapterElementIds = Array.from(start.elementIds); + + // remove the element from the old position + startChapterElementIds.splice(source.index, 1); + const newStart = { + ...start, + elementIds: startChapterElementIds, + }; + + // add the element to the new position within the chapter + const finishChapterElementIds = Array.from(finish.elementIds); + finishChapterElementIds.splice(destination.index, 0, draggableId); + const newFinish = { + ...finish, + elementIds: finishChapterElementIds, + }; + + const newState = { + ...data, + chapters: { + ...data.chapters, + [newStart.id]: newStart, + [newFinish.id]: newFinish, + }, + }; + + setData(newState); + return; + } + }; + + return ( + +
+ Edit Course Chapters +
+ {winReady && ( + + + + {(provided) => ( +
+ {getChapters().map((info: any, index: any) => ( + + ))} + {provided.placeholder} +
+ )} +
+
+
+ )} +
+ ); +} + +const ChapterlistWrapper = styled.div` + display: flex; + padding-left: 30px; +`; +export default CourseEdit; diff --git a/front/pages/org/[orgslug]/course/[courseid].tsx b/front/pages/org/[orgslug]/course/[courseid]/index.tsx similarity index 80% rename from front/pages/org/[orgslug]/course/[courseid].tsx rename to front/pages/org/[orgslug]/course/[courseid]/index.tsx index e413a0b9..04b27116 100644 --- a/front/pages/org/[orgslug]/course/[courseid].tsx +++ b/front/pages/org/[orgslug]/course/[courseid]/index.tsx @@ -1,10 +1,10 @@ import { useRouter } from "next/router"; import React from "react"; import styled from "styled-components"; -import Layout from "../../../../components/ui/layout"; -import { getAPIUrl, getBackendUrl } from "../../../../services/config"; -import { getCourse } from "../../../../services/courses"; -import { getOrganizationContextInfo } from "../../../../services/orgs"; +import Layout from "../../../../../components/ui/layout"; +import { getAPIUrl, getBackendUrl } from "../../../../../services/config"; +import { getCourse } from "../../../../../services/courses"; +import { getOrganizationContextInfo } from "../../../../../services/orgs"; const CourseIdPage = () => { const router = useRouter(); @@ -26,7 +26,8 @@ const CourseIdPage = () => { if (router.isReady) { fetchCourseInfo(); } - }, [isLoading, router.isReady]); + return () => {}; + }, [router.isReady]); return ( diff --git a/front/pages/org/[orgslug]/course/[courseid]/new/chapters.tsx b/front/pages/org/[orgslug]/course/[courseid]/new/chapters.tsx new file mode 100644 index 00000000..e69de29b diff --git a/front/pages/org/[orgslug]/courses/index.tsx b/front/pages/org/[orgslug]/courses/index.tsx index 20956471..1194f953 100644 --- a/front/pages/org/[orgslug]/courses/index.tsx +++ b/front/pages/org/[orgslug]/courses/index.tsx @@ -72,7 +72,12 @@ const CoursesIndexPage = () => { - + + + + + + ))} diff --git a/front/styles/globals.css b/front/styles/globals.css index 1b5a647b..d2c85aca 100644 --- a/front/styles/globals.css +++ b/front/styles/globals.css @@ -19,7 +19,7 @@ a { @media (prefers-color-scheme: dark) { html { - color-scheme: dark; + } body { color: black; diff --git a/front/tsconfig.json b/front/tsconfig.json index 99710e85..576365de 100644 --- a/front/tsconfig.json +++ b/front/tsconfig.json @@ -15,6 +15,6 @@ "jsx": "preserve", "incremental": true }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "components/drags/chapter.stsx"], "exclude": ["node_modules"] } diff --git a/src/services/courses.py b/src/services/courses.py index 570e1426..8b9b2690 100644 --- a/src/services/courses.py +++ b/src/services/courses.py @@ -37,15 +37,12 @@ class CourseElement(BaseModel): element_id: str content: str content_type: str - position: int class CourseChapter(BaseModel): name: str description: str - course: str elements: List[CourseElement] - position: int class CourseChapterInDB(CourseChapter):