diff --git a/.github/helper/update_pot_file.sh b/.github/helper/update_pot_file.sh new file mode 100644 index 00000000..d07d2e25 --- /dev/null +++ b/.github/helper/update_pot_file.sh @@ -0,0 +1,40 @@ +#!/bin/bash +set -e +cd ~ || exit + +echo "Setting Up Bench..." + +pip install frappe-bench +bench -v init frappe-bench --skip-assets --skip-redis-config-generation --python "$(which python)" --frappe-branch "${BASE_BRANCH}" +cd ./frappe-bench || exit + +echo "Get LMS..." +bench get-app --skip-assets lms "${GITHUB_WORKSPACE}" + +echo "Generating POT file..." +bench generate-pot-file --app lms + +cd ./apps/lms || exit + +echo "Configuring git user..." +git config user.email "developers@erpnext.com" +git config user.name "frappe-pr-bot" + +echo "Setting the correct git remote..." +# Here, the git remote is a local file path by default. Let's change it to the upstream repo. +git remote add upstream https://github.com/frappe/lms.git + +echo "Creating a new branch..." +isodate=$(date -u +"%Y-%m-%d") +branch_name="pot_${BASE_BRANCH}_${isodate}" +git checkout -b "${branch_name}" + +echo "Commiting changes..." +git add lms/locale/main.pot +git commit -m "chore: update POT file" + +gh auth setup-git +git push -u upstream "${branch_name}" + +echo "Creating a PR..." +gh pr create --fill --base "${BASE_BRANCH}" --head "${branch_name}" -R frappe/lms \ No newline at end of file diff --git a/.github/workflows/generate-pot-file.yml b/.github/workflows/generate-pot-file.yml new file mode 100644 index 00000000..390393d7 --- /dev/null +++ b/.github/workflows/generate-pot-file.yml @@ -0,0 +1,34 @@ +name: Regenerate POT file (translatable strings) +on: + schedule: + - cron: "00 16 * * 5" + workflow_dispatch: + +jobs: + regeneratee-pot-file: + name: Release + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + branch: ["develop"] + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ matrix.branch }} + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Run script to update POT file + run: | + bash ${GITHUB_WORKSPACE}/.github/helper/update_pot_file.sh + env: + GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} + BASE_BRANCH: ${{ matrix.branch }} diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index e6d7584c..018ee5e7 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -30,4 +30,4 @@ jobs: run: pip install semgrep - name: Run Semgrep rules - run: semgrep ci --config ./frappe-semgrep-rules/rules + run: semgrep ci --config ./frappe-semgrep-rules/rules \ No newline at end of file diff --git a/.gitignore b/.gitignore index 81bbfe1a..957c519f 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ __pycache__/ *.py[cod] *$py.class node_modules -package-lock.json \ No newline at end of file +package-lock.json +lms/public/frontend +lms/www/lms.html \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b2ac7630..26145761 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,7 +32,7 @@ repos: rev: v2.7.1 hooks: - id: prettier - types_or: [javascript] + types_or: [javascript, vue] # Ignore any files that might contain jinja / bundles exclude: | (?x)^( diff --git a/README.md b/README.md index 1a94421f..0a809073 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,13 @@ cd apps/lms/docker docker-compose up ``` -Wait for some time until the setup script creates a site. After that, you can access `http://localhost:8000` in your browser and the app's login screen should show up. +Wait for some time until the setup script creates a site. After that, you can access `http://localhost:8000` in your browser and the app's login screen should appear. +You'll have to go through the setup wizard to set up the website the first time you access it. Log in using the following credentials to complete the setup wizard. + +``` +Username: Administrator +password: admin +``` ### Frappe Bench diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 00000000..ac3875da --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,8 @@ +files: + - source: /lms/locale/main.pot + translation: /lms/locale/%two_letters_code%.po +pull_request_title: "chore: sync translations from crowdin" +pull_request_labels: + - translation +commit_message: "chore: %language% translations" +append_commit_message: false \ No newline at end of file diff --git a/cypress.config.js b/cypress.config.js index 04ebc494..d93eac9e 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -13,6 +13,6 @@ module.exports = defineConfig({ openMode: 0, }, e2e: { - baseUrl: "http://pyp:8000", + baseUrl: "http://test_site_ui:8000", }, }); diff --git a/cypress/e2e/course_creation.cy.js b/cypress/e2e/course_creation.cy.js index 2be9a71c..e453ea5b 100644 --- a/cypress/e2e/course_creation.cy.js +++ b/cypress/e2e/course_creation.cy.js @@ -1,133 +1,156 @@ describe("Course Creation", () => { it("creates a new course", () => { cy.login(); - cy.visit("/courses"); + cy.wait(1000); + cy.visit("/lms/courses"); + // Create a course - cy.get("a.btn").contains("Create a Course").click(); + cy.get("a").contains("New Course").click(); cy.wait(1000); - cy.url().should("include", "/courses/new-course/edit"); - cy.get("#title").type("Test Course"); - cy.get("#intro").type("Test Course Short Introduction"); - cy.get("#description").type("Test Course Description"); - cy.get("#video-link").type("-LPmw2Znl2c"); - cy.get("#tags-input").type("Test"); - cy.get("#published").check(); + cy.url().should("include", "/courses/new/edit"); + + cy.get("label").contains("Title").type("Test Course"); + cy.get("label") + .contains("Short Introduction") + .type("Test Course Short Introduction to test the UI"); + cy.get("div[contenteditable=true").invoke( + "text", + "Test Course Description. I need a very big description to test the UI. This is a very big description. It contains more than once sentence. Its meant to be this long as this is a UI test. Its unbearably long and I'm not sure why I'm typing this much. I'm just going to keep typing until I feel like its long enough. I think its long enough now. I'm going to stop typing now." + ); + + cy.fixture("profile.png", "base64").then((fileContent) => { + cy.get('input[type="file"]').attachFile({ + fileContent, + fileName: "profile.png", + mimeType: "image/png", + encoding: "base64", + }); + }); + + cy.get("label") + .contains("Preview Video") + .type("https://www.youtube.com/embed/-LPmw2Znl2c"); + cy.get("[id=tags]").type("Learning{enter}Frappe{enter}ERPNext{enter}"); + cy.get(".search-input").click().type("frappe"); cy.wait(1000); + cy.get("[id^=headlessui-combobox-option-") + .should("be.visible") + .first() + .click(); + cy.get("label").contains("Published").click(); + cy.get("label").contains("Published On").type("2021-01-01"); cy.button("Save").click(); // Add Chapter cy.wait(1000); - cy.link("Course Outline").click(); + cy.button("Add Chapter").click(); cy.wait(1000); - cy.get(".edit-header .btn-add-chapter").click(); - cy.wait(500); - cy.get("#chapter-title").type("Test Chapter"); - cy.get("#chapter-description").type("Test Chapter Description"); - cy.button("Save").click(); + cy.get("[id^=headlessui-dialog-panel-") + .should("be.visible") + .within(() => { + cy.get("label").contains("Title").type("Test Chapter"); + cy.button("Add Chapter").click(); + }); // Add Lesson cy.wait(1000); - cy.link("Add Lesson").click(); + cy.button("Add Lesson").click(); + cy.wait(1000); + cy.url().should("include", "/learn/1-1/edit"); cy.wait(1000); - cy.get("#lesson-title").type("Test Lesson"); - // Content - cy.get(".collapse-section.collapsed:first").click(); - cy.get("#lesson-content .ce-block") + cy.get("label").contains("Title").type("Test Lesson"); + /* cy.get("#content .ce-block") .click() - .type( - "This is an extremely big paragraph that is meant to test the UI. This is a very long paragraph. It contains more than once sentence. Its meant to be this long as this is a UI test. Its unbearably long and I'm not sure why I'm typing this much. I'm just going to keep typing until I feel like its long enough. I think its long enough now. I'm going to stop typing now. {enter}" - ); - cy.get("#lesson-content .ce-toolbar__plus").click(); - cy.get('#lesson-content [data-item-name="youtube"]').click(); - cy.get('input[data-fieldname="youtube"]').type("GoDtyItReto"); - cy.button("Insert").click(); - cy.wait(1000); + .invoke("text", "https://www.youtube.com/watch?v=GoDtyItReto"); */ + /* cy.get("#content .ce-block") + .click() + .paste("https://www.youtube.com/watch?v=GoDtyItReto"); */ + + cy.fixture("Youtube.mov", "base64").then((fileContent) => { + cy.get('input[type="file"]').attachFile({ + fileContent, + fileName: "Youtube.mov", + mimeType: "image/png", + encoding: "base64", + }); + }); + cy.get("#content .ce-block").type( + "This is an extremely big paragraph that is meant to test the UI. This is a very long paragraph. It contains more than once sentence. Its meant to be this long as this is a UI test. Its unbearably long and I'm not sure why I'm typing this much. I'm just going to keep typing until I feel like its long enough. I think its long enough now. I'm going to stop typing now." + ); cy.button("Save").click(); // View Course cy.wait(1000); - cy.visit("/courses"); - cy.get(".course-card-title:first").contains("Test Course"); - cy.get(".course-card:first").click(); - cy.url().should("include", "/courses/test-course"); - cy.get("#title").contains("Test Course"); - cy.get(".preview-video").should( + cy.visit("/lms"); + cy.wait(500); + cy.url().should("include", "/lms/courses"); + cy.get(".grid a:first").within(() => { + cy.get("div").contains("Test Course"); + cy.get("div").contains( + "Test Course Short Introduction to test the UI" + ); + cy.get(".course-image") + .invoke("css", "background-image") + .should("include", "/files/profile"); + }); + cy.get(".grid a:first").click(); + cy.url().should("include", "/lms/courses/test-course"); + cy.get("div").contains("Test Course"); + cy.get("div").contains("Test Course Short Introduction to test the UI"); + cy.get("div").contains("Learning"); + cy.get("div").contains("Frappe"); + cy.get("div").contains("ERPNext"); + cy.get("iframe").should( "have.attr", "src", "https://www.youtube.com/embed/-LPmw2Znl2c" ); - cy.get("#intro").contains("Test Course Short Introduction"); // View Chapter - cy.get(".chapter-title-main:first").contains("Test Chapter"); - cy.get(".chapter-description:first").contains( - "Test Chapter Description" - ); - cy.get(".lesson-info:first").contains("Test Lesson"); - cy.get(".lesson-info:first").click(); + cy.get("div").contains("Test Chapter"); + cy.get("[id^=headlessui-disclosure-panel-").within(() => { + cy.get("div").contains("Test Lesson").click(); + }); + cy.wait(3000); // View Lesson - cy.wait(1000); - cy.url().should("include", "learn/1.1"); - cy.get("#title").contains("Test Lesson"); - cy.get(".lesson-video iframe").should( - "have.attr", - "src", - "https://www.youtube.com/embed/GoDtyItReto" - ); - cy.get(".lesson-content-card").contains( + cy.url().should("include", "/learn/1-1"); + cy.get("div").contains("Test Lesson"); + + cy.get("video") + .should("be.visible") + .children("source") + .invoke("attr", "src") + .should("include", "/files/Youtube"); + + cy.get("div").contains( "This is an extremely big paragraph that is meant to test the UI. This is a very long paragraph. It contains more than once sentence. Its meant to be this long as this is a UI test. Its unbearably long and I'm not sure why I'm typing this much. I'm just going to keep typing until I feel like its long enough. I think its long enough now. I'm going to stop typing now." ); // Add Discussion - cy.get(".reply").click(); + cy.button("New Question").click(); cy.wait(500); - cy.get(".discussion-modal").should("be.visible"); - - // Enter title - cy.get(".modal .topic-title") - .type("Discussion from tests") - .should("have.value", "Discussion from tests"); - - // Enter comment - cy.get(".modal .discussions-comment").type( - "This is a discussion from the cypress ui tests." - ); - - // Submit - cy.get(".modal .submit-discussion").click(); - cy.wait(2000); - - // Check if discussion is added to page and content is visible - cy.get(".sidebar-parent:first .discussion-topic-title").should( - "have.text", - "Discussion from tests" - ); - cy.get(".sidebar-parent:first .discussion-topic-title").click(); - cy.get(".discussion-on-page:visible").should("have.class", "show"); - cy.get( - ".discussion-on-page:visible .reply-card .reply-text .ql-editor p" - ).should( - "have.text", - "This is a discussion from the cypress ui tests." - ); - - cy.get(".discussion-form:visible .discussions-comment").type( - "This is a discussion from the cypress ui tests. \n\nThis comment was entered through the commentbox on the page." - ); - - cy.get(".discussion-form:visible .submit-discussion").click(); - cy.wait(3000); - cy.get(".discussion-on-page:visible").should("have.class", "show"); - cy.get(".discussion-on-page:visible") - .children(".reply-card") - .eq(1) - .find(".reply-text") - .should( - "have.text", - "This is a discussion from the cypress ui tests. This comment was entered through the commentbox on the page.\n" + cy.get("[id^=headlessui-dialog-panel-").within(() => { + cy.get("label").contains("Title").type("Test Discussion"); + cy.get("div[contenteditable=true]").invoke( + "text", + "This is a test discussion. This will check if the UI is working properly." ); + cy.button("Post").click(); + }); + + // View Discussion + cy.wait(500); + cy.get("div").contains("Test Discussion").click(); + cy.get("div[contenteditable=true").invoke( + "text", + "This is a test comment. This will check if the UI is working properly." + ); + + cy.get("div").contains( + "This is a test comment. This will check if the UI is working properly." + ); }); }); diff --git a/cypress/fixtures/Youtube.mov b/cypress/fixtures/Youtube.mov new file mode 100644 index 00000000..ca6b75a2 Binary files /dev/null and b/cypress/fixtures/Youtube.mov differ diff --git a/cypress/fixtures/profile.png b/cypress/fixtures/profile.png new file mode 100644 index 00000000..1b42bb97 Binary files /dev/null and b/cypress/fixtures/profile.png differ diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 336a6355..ed5c7968 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -24,6 +24,8 @@ // -- This will overwrite an existing command -- // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) +import "cypress-file-upload"; + Cypress.Commands.add("login", (email, password) => { if (!email) { email = Cypress.config("testUser") || "Administrator"; @@ -53,3 +55,13 @@ Cypress.Commands.add("iconButton", (text) => { Cypress.Commands.add("dialog", (selector) => { return cy.get(`[role=dialog] ${selector}`); }); + +Cypress.Commands.add("paste", { prevSubject: true }, (subject, text) => { + cy.wrap(subject).then(($element) => { + const element = $element[0]; + element.focus(); + element.textContent = text; + const event = new Event("paste", { bubbles: true }); + element.dispatchEvent(event); + }); +}); diff --git a/frappe-ui b/frappe-ui index 2898a0bd..aa44431c 160000 --- a/frappe-ui +++ b/frappe-ui @@ -1 +1 @@ -Subproject commit 2898a0bdd1a07433e5f6adedb42786177c473d50 +Subproject commit aa44431c185b1e4563f2ebf6af380c7743d2cd4c diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 00000000..53f7466a --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,5 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local \ No newline at end of file diff --git a/frontend/.prettierrc.json b/frontend/.prettierrc.json new file mode 100644 index 00000000..b2095be8 --- /dev/null +++ b/frontend/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "semi": false, + "singleQuote": true +} diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 00000000..1a41f83e --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,42 @@ +# Frappe UI Starter + +This template should help get you started developing custom frontend for Frappe +apps with Vue 3 and the Frappe UI package. + +This boilerplate sets up Vue 3, Vue Router, TailwindCSS, and Frappe UI out of +the box. + +## Usage + +This template is meant to be cloned inside an existing Frappe App. Assuming your +apps name is `todo`. Clone this template in the root folder of your app using `degit`. + +``` +cd apps/todo +npx degit netchampfaris/frappe-ui-starter frontend +cd frontend +yarn +yarn dev +``` + +In a development environment, you need to put the below key-value pair in your `site_config.json` file: + +``` +"ignore_csrf": 1 +``` + +This will prevent `CSRFToken` errors while using the vite dev server. In production environment, the `csrf_token` is attached to the `window` object in `index.html` for you. + +The Vite dev server will start on the port `8080`. This can be changed from `vite.config.js`. +The development server is configured to proxy your frappe app (usually running on port `8000`). If you have a site named `todo.test`, open `http://todo.test:8080` in your browser. If you see a button named "Click to send 'ping' request", congratulations! + +If you notice the browser URL is `/frontend`, this is the base URL where your frontend app will run in production. +To change this, open `src/router.js` and change the base URL passed to `createWebHistory`. + +## Resources + +- [Vue 3](https://v3.vuejs.org/guide/introduction.html) +- [Vue Router](https://next.router.vuejs.org/guide/) +- [Frappe UI](https://github.com/frappe/frappe-ui) +- [TailwindCSS](https://tailwindcss.com/docs/utility-first) +- [Vite](https://vitejs.dev/guide/) diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 00000000..1512b016 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,49 @@ + + + + + + + Frappe Learning + + + + + + + + + + + + +
+
+

{{ meta.title }}

+

+ {{ meta.description }} +

+

+ The content here is just for seo purposes. The actual content will be loaded in a few seconds. +

+

+ Seo checks if a page has more than 300 words. So, here are some more words to make it more than 300 words. + Page descriptions are the HTML meta tags that provide a brief summary of a web page. + Search engines use meta descriptions to help identify the page's topic - they don't use them to rank the page, but they do use them to determine whether or not to display the page in search results. + Meta descriptions are important because they're often the first thing people see when they're deciding which search result to click on. + They're also important because they can help improve your click-through rate (CTR) from search results. + A good meta description can entice people to click on your page instead of someone else's. +

+ Know More +
+
+
+
+ + + + + diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 00000000..6775773d --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,42 @@ +{ + "name": "frappe-ui-frontend", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "serve": "vite preview", + "build": "vite build --base=/assets/lms/frontend/ && yarn copy-html-entry", + "copy-html-entry": "cp ../lms/public/frontend/index.html ../lms/www/lms.html" + }, + "dependencies": { + "@editorjs/checklist": "^1.6.0", + "@editorjs/code": "^2.9.0", + "@editorjs/editorjs": "^2.29.0", + "@editorjs/embed": "^2.7.0", + "@editorjs/header": "^2.8.1", + "@editorjs/image": "^2.9.0", + "@editorjs/inline-code": "^1.5.0", + "@editorjs/nested-list": "^1.4.2", + "@editorjs/paragraph": "^2.11.3", + "chart.js": "^4.4.1", + "dayjs": "^1.11.6", + "feather-icons": "^4.28.0", + "frappe-ui": "^0.1.56", + "lucide-vue-next": "^0.383.0", + "markdown-it": "^14.0.0", + "pinia": "^2.0.33", + "socket.io-client": "^4.7.2", + "tailwindcss": "^3.3.3", + "vue": "^3.4.23", + "vue-chartjs": "^5.3.0", + "vue-draggable-next": "^2.2.1", + "vue-router": "^4.0.12", + "vuedraggable": "4.1.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.3", + "autoprefixer": "^10.4.2", + "postcss": "^8.4.5", + "vite": "^5.0.11" + } +} diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 00000000..1b69d43b --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/frontend/public/Youtube.mov b/frontend/public/Youtube.mov new file mode 100644 index 00000000..ca6b75a2 Binary files /dev/null and b/frontend/public/Youtube.mov differ diff --git a/frontend/public/favicon.png b/frontend/public/favicon.png new file mode 100644 index 00000000..b51db82f Binary files /dev/null and b/frontend/public/favicon.png differ diff --git a/frontend/src/App.vue b/frontend/src/App.vue new file mode 100644 index 00000000..6a9d4d5b --- /dev/null +++ b/frontend/src/App.vue @@ -0,0 +1,25 @@ + + diff --git a/frontend/src/assets/Inter/Inter-Black.woff b/frontend/src/assets/Inter/Inter-Black.woff new file mode 100644 index 00000000..c7737ed3 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-Black.woff differ diff --git a/frontend/src/assets/Inter/Inter-Black.woff2 b/frontend/src/assets/Inter/Inter-Black.woff2 new file mode 100644 index 00000000..b16b995b Binary files /dev/null and b/frontend/src/assets/Inter/Inter-Black.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-BlackItalic.woff b/frontend/src/assets/Inter/Inter-BlackItalic.woff new file mode 100644 index 00000000..b5f14476 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-BlackItalic.woff differ diff --git a/frontend/src/assets/Inter/Inter-BlackItalic.woff2 b/frontend/src/assets/Inter/Inter-BlackItalic.woff2 new file mode 100644 index 00000000..a3f1b70c Binary files /dev/null and b/frontend/src/assets/Inter/Inter-BlackItalic.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-Bold.woff b/frontend/src/assets/Inter/Inter-Bold.woff new file mode 100644 index 00000000..e3845558 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-Bold.woff differ diff --git a/frontend/src/assets/Inter/Inter-Bold.woff2 b/frontend/src/assets/Inter/Inter-Bold.woff2 new file mode 100644 index 00000000..835dd497 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-Bold.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-BoldItalic.woff b/frontend/src/assets/Inter/Inter-BoldItalic.woff new file mode 100644 index 00000000..ffac3f59 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-BoldItalic.woff differ diff --git a/frontend/src/assets/Inter/Inter-BoldItalic.woff2 b/frontend/src/assets/Inter/Inter-BoldItalic.woff2 new file mode 100644 index 00000000..1a41a14f Binary files /dev/null and b/frontend/src/assets/Inter/Inter-BoldItalic.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-ExtraBold.woff b/frontend/src/assets/Inter/Inter-ExtraBold.woff new file mode 100644 index 00000000..885ac94f Binary files /dev/null and b/frontend/src/assets/Inter/Inter-ExtraBold.woff differ diff --git a/frontend/src/assets/Inter/Inter-ExtraBold.woff2 b/frontend/src/assets/Inter/Inter-ExtraBold.woff2 new file mode 100644 index 00000000..ae956b15 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-ExtraBold.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-ExtraBoldItalic.woff b/frontend/src/assets/Inter/Inter-ExtraBoldItalic.woff new file mode 100644 index 00000000..d6cf8623 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-ExtraBoldItalic.woff differ diff --git a/frontend/src/assets/Inter/Inter-ExtraBoldItalic.woff2 b/frontend/src/assets/Inter/Inter-ExtraBoldItalic.woff2 new file mode 100644 index 00000000..86578995 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-ExtraBoldItalic.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-ExtraLight.woff b/frontend/src/assets/Inter/Inter-ExtraLight.woff new file mode 100644 index 00000000..ff769193 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-ExtraLight.woff differ diff --git a/frontend/src/assets/Inter/Inter-ExtraLight.woff2 b/frontend/src/assets/Inter/Inter-ExtraLight.woff2 new file mode 100644 index 00000000..694b2df9 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-ExtraLight.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-ExtraLightItalic.woff b/frontend/src/assets/Inter/Inter-ExtraLightItalic.woff new file mode 100644 index 00000000..c6ed13a4 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-ExtraLightItalic.woff differ diff --git a/frontend/src/assets/Inter/Inter-ExtraLightItalic.woff2 b/frontend/src/assets/Inter/Inter-ExtraLightItalic.woff2 new file mode 100644 index 00000000..9a7bd110 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-ExtraLightItalic.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-Italic.woff b/frontend/src/assets/Inter/Inter-Italic.woff new file mode 100644 index 00000000..4fdb59dc Binary files /dev/null and b/frontend/src/assets/Inter/Inter-Italic.woff differ diff --git a/frontend/src/assets/Inter/Inter-Italic.woff2 b/frontend/src/assets/Inter/Inter-Italic.woff2 new file mode 100644 index 00000000..deca637d Binary files /dev/null and b/frontend/src/assets/Inter/Inter-Italic.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-Light.woff b/frontend/src/assets/Inter/Inter-Light.woff new file mode 100644 index 00000000..42850acc Binary files /dev/null and b/frontend/src/assets/Inter/Inter-Light.woff differ diff --git a/frontend/src/assets/Inter/Inter-Light.woff2 b/frontend/src/assets/Inter/Inter-Light.woff2 new file mode 100644 index 00000000..65a7dadd Binary files /dev/null and b/frontend/src/assets/Inter/Inter-Light.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-LightItalic.woff b/frontend/src/assets/Inter/Inter-LightItalic.woff new file mode 100644 index 00000000..c4ed9a94 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-LightItalic.woff differ diff --git a/frontend/src/assets/Inter/Inter-LightItalic.woff2 b/frontend/src/assets/Inter/Inter-LightItalic.woff2 new file mode 100644 index 00000000..555fc559 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-LightItalic.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-Medium.woff b/frontend/src/assets/Inter/Inter-Medium.woff new file mode 100644 index 00000000..495faef7 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-Medium.woff differ diff --git a/frontend/src/assets/Inter/Inter-Medium.woff2 b/frontend/src/assets/Inter/Inter-Medium.woff2 new file mode 100644 index 00000000..871ce4ce Binary files /dev/null and b/frontend/src/assets/Inter/Inter-Medium.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-MediumItalic.woff b/frontend/src/assets/Inter/Inter-MediumItalic.woff new file mode 100644 index 00000000..389c7a2b Binary files /dev/null and b/frontend/src/assets/Inter/Inter-MediumItalic.woff differ diff --git a/frontend/src/assets/Inter/Inter-MediumItalic.woff2 b/frontend/src/assets/Inter/Inter-MediumItalic.woff2 new file mode 100644 index 00000000..aa805799 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-MediumItalic.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-Regular.woff b/frontend/src/assets/Inter/Inter-Regular.woff new file mode 100644 index 00000000..fa7715d1 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-Regular.woff differ diff --git a/frontend/src/assets/Inter/Inter-Regular.woff2 b/frontend/src/assets/Inter/Inter-Regular.woff2 new file mode 100644 index 00000000..b52dd0a0 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-Regular.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-SemiBold.woff b/frontend/src/assets/Inter/Inter-SemiBold.woff new file mode 100644 index 00000000..18d7749f Binary files /dev/null and b/frontend/src/assets/Inter/Inter-SemiBold.woff differ diff --git a/frontend/src/assets/Inter/Inter-SemiBold.woff2 b/frontend/src/assets/Inter/Inter-SemiBold.woff2 new file mode 100644 index 00000000..ece5204a Binary files /dev/null and b/frontend/src/assets/Inter/Inter-SemiBold.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-SemiBoldItalic.woff b/frontend/src/assets/Inter/Inter-SemiBoldItalic.woff new file mode 100644 index 00000000..8ee64396 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-SemiBoldItalic.woff differ diff --git a/frontend/src/assets/Inter/Inter-SemiBoldItalic.woff2 b/frontend/src/assets/Inter/Inter-SemiBoldItalic.woff2 new file mode 100644 index 00000000..b32c0ba3 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-SemiBoldItalic.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-Thin.woff b/frontend/src/assets/Inter/Inter-Thin.woff new file mode 100644 index 00000000..1a22286f Binary files /dev/null and b/frontend/src/assets/Inter/Inter-Thin.woff differ diff --git a/frontend/src/assets/Inter/Inter-Thin.woff2 b/frontend/src/assets/Inter/Inter-Thin.woff2 new file mode 100644 index 00000000..c56bc7ca Binary files /dev/null and b/frontend/src/assets/Inter/Inter-Thin.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-ThinItalic.woff b/frontend/src/assets/Inter/Inter-ThinItalic.woff new file mode 100644 index 00000000..d8ec8373 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-ThinItalic.woff differ diff --git a/frontend/src/assets/Inter/Inter-ThinItalic.woff2 b/frontend/src/assets/Inter/Inter-ThinItalic.woff2 new file mode 100644 index 00000000..eca5608c Binary files /dev/null and b/frontend/src/assets/Inter/Inter-ThinItalic.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-italic.var.woff2 b/frontend/src/assets/Inter/Inter-italic.var.woff2 new file mode 100644 index 00000000..1f5d9261 Binary files /dev/null and b/frontend/src/assets/Inter/Inter-italic.var.woff2 differ diff --git a/frontend/src/assets/Inter/Inter-roman.var.woff2 b/frontend/src/assets/Inter/Inter-roman.var.woff2 new file mode 100644 index 00000000..05621d8d Binary files /dev/null and b/frontend/src/assets/Inter/Inter-roman.var.woff2 differ diff --git a/frontend/src/assets/Inter/Inter.var.woff2 b/frontend/src/assets/Inter/Inter.var.woff2 new file mode 100644 index 00000000..46bb5153 Binary files /dev/null and b/frontend/src/assets/Inter/Inter.var.woff2 differ diff --git a/frontend/src/assets/Inter/inter.css b/frontend/src/assets/Inter/inter.css new file mode 100644 index 00000000..3ca1bbf6 --- /dev/null +++ b/frontend/src/assets/Inter/inter.css @@ -0,0 +1,152 @@ +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: url("Inter-Thin.woff2?v=3.12") format("woff2"), + url("Inter-Thin.woff?v=3.12") format("woff"); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: url("Inter-ThinItalic.woff2?v=3.12") format("woff2"), + url("Inter-ThinItalic.woff?v=3.12") format("woff"); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 200; + font-display: swap; + src: url("Inter-ExtraLight.woff2?v=3.12") format("woff2"), + url("Inter-ExtraLight.woff?v=3.12") format("woff"); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 200; + font-display: swap; + src: url("Inter-ExtraLightItalic.woff2?v=3.12") format("woff2"), + url("Inter-ExtraLightItalic.woff?v=3.12") format("woff"); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url("Inter-Light.woff2?v=3.12") format("woff2"), + url("Inter-Light.woff?v=3.12") format("woff"); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url("Inter-LightItalic.woff2?v=3.12") format("woff2"), + url("Inter-LightItalic.woff?v=3.12") format("woff"); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url("Inter-Regular.woff2?v=3.12") format("woff2"), + url("Inter-Regular.woff?v=3.12") format("woff"); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url("Inter-Italic.woff2?v=3.12") format("woff2"), + url("Inter-Italic.woff?v=3.12") format("woff"); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url("Inter-Medium.woff2?v=3.12") format("woff2"), + url("Inter-Medium.woff?v=3.12") format("woff"); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url("Inter-MediumItalic.woff2?v=3.12") format("woff2"), + url("Inter-MediumItalic.woff?v=3.12") format("woff"); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url("Inter-SemiBold.woff2?v=3.12") format("woff2"), + url("Inter-SemiBold.woff?v=3.12") format("woff"); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 600; + font-display: swap; + src: url("Inter-SemiBoldItalic.woff2?v=3.12") format("woff2"), + url("Inter-SemiBoldItalic.woff?v=3.12") format("woff"); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url("Inter-Bold.woff2?v=3.12") format("woff2"), + url("Inter-Bold.woff?v=3.12") format("woff"); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url("Inter-BoldItalic.woff2?v=3.12") format("woff2"), + url("Inter-BoldItalic.woff?v=3.12") format("woff"); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 800; + font-display: swap; + src: url("Inter-ExtraBold.woff2?v=3.12") format("woff2"), + url("Inter-ExtraBold.woff?v=3.12") format("woff"); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 800; + font-display: swap; + src: url("Inter-ExtraBoldItalic.woff2?v=3.12") format("woff2"), + url("Inter-ExtraBoldItalic.woff?v=3.12") format("woff"); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: url("Inter-Black.woff2?v=3.12") format("woff2"), + url("Inter-Black.woff?v=3.12") format("woff"); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: url("Inter-BlackItalic.woff2?v=3.12") format("woff2"), + url("Inter-BlackItalic.woff?v=3.12") format("woff"); +} diff --git a/frontend/src/components/Annoucements.vue b/frontend/src/components/Annoucements.vue new file mode 100644 index 00000000..f075c740 --- /dev/null +++ b/frontend/src/components/Annoucements.vue @@ -0,0 +1,62 @@ + + + diff --git a/frontend/src/components/AppSidebar.vue b/frontend/src/components/AppSidebar.vue new file mode 100644 index 00000000..dbeceba4 --- /dev/null +++ b/frontend/src/components/AppSidebar.vue @@ -0,0 +1,205 @@ + + + diff --git a/frontend/src/components/Assessments.vue b/frontend/src/components/Assessments.vue new file mode 100644 index 00000000..86798c99 --- /dev/null +++ b/frontend/src/components/Assessments.vue @@ -0,0 +1,98 @@ + + diff --git a/frontend/src/components/AudioBlock.vue b/frontend/src/components/AudioBlock.vue new file mode 100644 index 00000000..5d20e421 --- /dev/null +++ b/frontend/src/components/AudioBlock.vue @@ -0,0 +1,135 @@ + + + + diff --git a/frontend/src/components/BatchCard.vue b/frontend/src/components/BatchCard.vue new file mode 100644 index 00000000..84697c51 --- /dev/null +++ b/frontend/src/components/BatchCard.vue @@ -0,0 +1,107 @@ + + + diff --git a/frontend/src/components/BatchCourses.vue b/frontend/src/components/BatchCourses.vue new file mode 100644 index 00000000..da31db76 --- /dev/null +++ b/frontend/src/components/BatchCourses.vue @@ -0,0 +1,154 @@ + + diff --git a/frontend/src/components/BatchDashboard.vue b/frontend/src/components/BatchDashboard.vue new file mode 100644 index 00000000..3c1b6aad --- /dev/null +++ b/frontend/src/components/BatchDashboard.vue @@ -0,0 +1,26 @@ + + diff --git a/frontend/src/components/BatchOverlay.vue b/frontend/src/components/BatchOverlay.vue new file mode 100644 index 00000000..2eb225c2 --- /dev/null +++ b/frontend/src/components/BatchOverlay.vue @@ -0,0 +1,128 @@ + + diff --git a/frontend/src/components/BatchStudents.vue b/frontend/src/components/BatchStudents.vue new file mode 100644 index 00000000..dc73322f --- /dev/null +++ b/frontend/src/components/BatchStudents.vue @@ -0,0 +1,157 @@ + + diff --git a/frontend/src/components/Common/DateRange.vue b/frontend/src/components/Common/DateRange.vue new file mode 100644 index 00000000..6bc60083 --- /dev/null +++ b/frontend/src/components/Common/DateRange.vue @@ -0,0 +1,22 @@ + + + diff --git a/frontend/src/components/Controls/Autocomplete.vue b/frontend/src/components/Controls/Autocomplete.vue new file mode 100644 index 00000000..eb9a5b08 --- /dev/null +++ b/frontend/src/components/Controls/Autocomplete.vue @@ -0,0 +1,277 @@ + + + diff --git a/frontend/src/components/Controls/IconPicker.vue b/frontend/src/components/Controls/IconPicker.vue new file mode 100644 index 00000000..9efdc006 --- /dev/null +++ b/frontend/src/components/Controls/IconPicker.vue @@ -0,0 +1,114 @@ + + diff --git a/frontend/src/components/Controls/Link.vue b/frontend/src/components/Controls/Link.vue new file mode 100644 index 00000000..8ce30846 --- /dev/null +++ b/frontend/src/components/Controls/Link.vue @@ -0,0 +1,146 @@ + + + diff --git a/frontend/src/components/Controls/MultiSelect.vue b/frontend/src/components/Controls/MultiSelect.vue new file mode 100644 index 00000000..f1281eb7 --- /dev/null +++ b/frontend/src/components/Controls/MultiSelect.vue @@ -0,0 +1,255 @@ + + + diff --git a/frontend/src/components/Controls/Rating.vue b/frontend/src/components/Controls/Rating.vue new file mode 100644 index 00000000..95897d12 --- /dev/null +++ b/frontend/src/components/Controls/Rating.vue @@ -0,0 +1,38 @@ + + + diff --git a/frontend/src/components/CourseCard.vue b/frontend/src/components/CourseCard.vue new file mode 100644 index 00000000..daf76c4a --- /dev/null +++ b/frontend/src/components/CourseCard.vue @@ -0,0 +1,191 @@ + + + diff --git a/frontend/src/components/CourseCardOverlay.vue b/frontend/src/components/CourseCardOverlay.vue new file mode 100644 index 00000000..33157b85 --- /dev/null +++ b/frontend/src/components/CourseCardOverlay.vue @@ -0,0 +1,177 @@ + + diff --git a/frontend/src/components/LessonPlugins.vue b/frontend/src/components/LessonPlugins.vue new file mode 100644 index 00000000..ccaa17cf --- /dev/null +++ b/frontend/src/components/LessonPlugins.vue @@ -0,0 +1,163 @@ + + diff --git a/frontend/src/components/LiveClass.vue b/frontend/src/components/LiveClass.vue new file mode 100644 index 00000000..1bf738e7 --- /dev/null +++ b/frontend/src/components/LiveClass.vue @@ -0,0 +1,111 @@ + + diff --git a/frontend/src/components/MobileLayout.vue b/frontend/src/components/MobileLayout.vue new file mode 100644 index 00000000..0faaf1c8 --- /dev/null +++ b/frontend/src/components/MobileLayout.vue @@ -0,0 +1,95 @@ + + diff --git a/frontend/src/components/Modals/AnnouncementModal.vue b/frontend/src/components/Modals/AnnouncementModal.vue new file mode 100644 index 00000000..8d7b432e --- /dev/null +++ b/frontend/src/components/Modals/AnnouncementModal.vue @@ -0,0 +1,117 @@ + + diff --git a/frontend/src/components/Modals/BatchCourseModal.vue b/frontend/src/components/Modals/BatchCourseModal.vue new file mode 100644 index 00000000..8387020d --- /dev/null +++ b/frontend/src/components/Modals/BatchCourseModal.vue @@ -0,0 +1,77 @@ + + diff --git a/frontend/src/components/Modals/ChapterModal.vue b/frontend/src/components/Modals/ChapterModal.vue new file mode 100644 index 00000000..0ec46085 --- /dev/null +++ b/frontend/src/components/Modals/ChapterModal.vue @@ -0,0 +1,161 @@ + + diff --git a/frontend/src/components/Modals/DiscussionModal.vue b/frontend/src/components/Modals/DiscussionModal.vue new file mode 100644 index 00000000..d2e35f4e --- /dev/null +++ b/frontend/src/components/Modals/DiscussionModal.vue @@ -0,0 +1,123 @@ + + diff --git a/frontend/src/components/Modals/EditCoverImage.vue b/frontend/src/components/Modals/EditCoverImage.vue new file mode 100644 index 00000000..0c8823cf --- /dev/null +++ b/frontend/src/components/Modals/EditCoverImage.vue @@ -0,0 +1,109 @@ + + diff --git a/frontend/src/components/Modals/EditProfile.vue b/frontend/src/components/Modals/EditProfile.vue new file mode 100644 index 00000000..5eb43c08 --- /dev/null +++ b/frontend/src/components/Modals/EditProfile.vue @@ -0,0 +1,178 @@ + + diff --git a/frontend/src/components/Modals/EvaluationModal.vue b/frontend/src/components/Modals/EvaluationModal.vue new file mode 100644 index 00000000..825e38ed --- /dev/null +++ b/frontend/src/components/Modals/EvaluationModal.vue @@ -0,0 +1,191 @@ + + diff --git a/frontend/src/components/Modals/JobApplicationModal.vue b/frontend/src/components/Modals/JobApplicationModal.vue new file mode 100644 index 00000000..3da02e06 --- /dev/null +++ b/frontend/src/components/Modals/JobApplicationModal.vue @@ -0,0 +1,137 @@ + + diff --git a/frontend/src/components/Modals/LiveClassModal.vue b/frontend/src/components/Modals/LiveClassModal.vue new file mode 100644 index 00000000..71f5e9ab --- /dev/null +++ b/frontend/src/components/Modals/LiveClassModal.vue @@ -0,0 +1,210 @@ + + diff --git a/frontend/src/components/Modals/PageModal.vue b/frontend/src/components/Modals/PageModal.vue new file mode 100644 index 00000000..6c403168 --- /dev/null +++ b/frontend/src/components/Modals/PageModal.vue @@ -0,0 +1,90 @@ + + diff --git a/frontend/src/components/Modals/ReviewModal.vue b/frontend/src/components/Modals/ReviewModal.vue new file mode 100644 index 00000000..ca0fcf06 --- /dev/null +++ b/frontend/src/components/Modals/ReviewModal.vue @@ -0,0 +1,90 @@ +