diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..5d12634847 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000..9e383e1558 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,19 @@ +module.exports = { + root: true, + env: { + browser: true, + node: true + }, + parserOptions: { + parser: '@babel/eslint-parser', + requireConfigFile: false + }, + extends: [ + '@nuxtjs', + 'plugin:nuxt/recommended' + ], + plugins: [ + ], + // add your custom rules here + rules: {} +} diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..8a1c5f1193 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: Skyost +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: Skyost +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: ['paypal.me/Skyost'] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..7e3036eb88 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,33 @@ +--- +name: Rapport de bug +about: Créer un rapport de bug +title: '' +labels: bug +assignees: Skyost + +--- + +**Description du bug** +Une description claire et concise du problème encouru. + +**Pour reproduire** +Pour reproduire : +1. Aller sur '...' +2. Cliquer sur '....' +3. Scroller jusqu'à '....' +4. Et voici l'erreur + +**Ce qui aurait dû se passer** +Une description claire et concise de ce qui aurait dû se passer. + +**Screenshots** +Si possible, joindre des captures d'écran du problème. + +**Machine (compléter les informations):** + - Appareil : [ex. iPhone6] + - OS : [ex. iOS] + - Browser : [ex. chrome, safari] + - Version : [ex. 22] + +**Informations additionnelles** +Ajoutez tout autre élément de contexte qui pourrait être utile à la résolution du problème. diff --git a/.github/ISSUE_TEMPLATE/error_fix.md b/.github/ISSUE_TEMPLATE/error_fix.md new file mode 100644 index 0000000000..a0b14cdcfa --- /dev/null +++ b/.github/ISSUE_TEMPLATE/error_fix.md @@ -0,0 +1,20 @@ +--- +name: Correction d'une erreur +about: Soumettre une correction d'erreur +title: '' +labels: error +assignees: Skyost + +--- + +**Localisation de l'erreur** +1. Aller sur la page '...' +2. Cliquer sur '....' +3. Scroller jusqu'à '....' +4. Et voici l'erreur + +**Description de l'erreur** +Une description claire et concise du problème encouru. + +**Informations additionnelles** +Ajoutez tout autre élément de contexte qui pourrait être utile à la résolution du problème. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..8283b6e910 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Idée d'amélioration +about: Suggérer une idée pour ce projet +title: '' +labels: enhancement +assignees: Skyost + +--- + +**Est-ce-que votre amélioration est liée à un problème ? Veuillez décrire.** +Une description claire et concise du problème. Exemple : Je suis toujours frustré quand [...] + +**Décrivez la solution souhaitée** +Une description claire et concise de ce qui devrait arriver selon vous. + +**Décrivez les alternatives que vous avez considérées** +Une description claire et concise des solutions alternatives que vous avez considérées. + +**Informations additionnelles** +Ajoutez tout autre élément de contexte qui pourrait être utile à la résolution du problème. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..fed24565fe --- /dev/null +++ b/.gitignore @@ -0,0 +1,110 @@ +# Created by .ignore support plugin (hsz.mobi) +### Node template +# Logs +/logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# Nuxt generate +dist + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless + +# IDE / Editor +.idea + +# Service worker +sw.* + +# macOS +.DS_Store + +# Vim swap files +*.swp + +# Vercel +.vercel + +# Generated files +*.aux +*.bbl +*.bcf +*.blg +*.fdb_latexmk +*.fls +*.sta +*.out +*.synctex.gz +*.run.xml +content/ +static/images/lessons/ +static/pdf/ +static/CNAME +latex/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..6ee8e4640f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Hugo Delaunay + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000000..51b5c3c059 --- /dev/null +++ b/README.md @@ -0,0 +1,95 @@ +# Mes cours de maths + +_Ce projet est en cours de construction, veuillez repasser plus tard !_ + +Bienvenue sur la page Github du site web [Mes cours de maths](https://mes-cours-de-maths.fr) ! +Il s'agit d'un petit site web où je dépose tous mes cours de mathématiques ainsi que les ressources +qui y sont liées. + +Ce site est totalement open-source : vous pouvez tout à fait en exécuter une instance personnelle. +Pour cela, suivez le guide ci-dessous. + +## Installation + +### Cloner le projet + +Il vous suffit de [cliquer ici](https://github.com/Skyost/MesCoursDeMaths/fork) pour cloner le projet. +Cela créera un nouveau dépôt sur Github où vous pourrez [configurer](#configuration) votre nouveau site web. + +### Création d'applications et de jetons + +Toujours sur Github, il va falloir créer une application OAUTH. Pour cela, rendez-vous sur +[ce lien](https://github.com/settings/applications/new). + +* Dans `Application name`, mettez ce que vous souhaitez. +* Dans `Homepage URL` et dans `Authorization callback URL`, mettez l'URL de votre site web. + Se référer à [https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps#redirect-urls](ce lien) + pour le dernier champ. + +Par la suite, notez votre `Client ID` quelque-part et générez un nouveau `Client secret`. Copiez-le aussi +et ne le perdez surtout pas. Nous aurons besoin de ces données plus tard. + +### Configuration + +Pour configurer le site web (nom, dépôt Github, répertoire des cours, etc.), il vous faut modifier le +fichier `site.js`. Indiquez votre `Client ID` dans le champ `clientId`. + +Pour modifier le contenu de la page d'accueil, il faut éditer le fichier `pages/index.vue`. +Une façon plus rapide (et intuitive...) de modifier les pages sera sûrement ajoutée à l'avenir. + +### Création d'un dépôt de données + +Cette étape est optionnelle : vous pouvez parfaitement vous servir du même dépôt que le site web +pour y stocker vos données LaTeX. Pour cela, dans `site.js`, laissez `dataRepository` à la même valeur que `repository`. + +Si vous souhaitez utiliser un dépôt séparé (par exemple, afin de garder les sources LaTeX privées ; ou tout +simplement pour séparer le contenu de sa présentation), créez-en un sur Github et indiquez sa valeur dans +`dataRepository`. Celui-ci doit contenir le `lessonsDirectory`. + +Si votre dépôt est privé, nous allons devoir créer un `Personal access token`. Pour cela rendez-vous sur +[ce lien](https://github.com/settings/tokens/new). Nommez-le comme vous souhaitez et cochez la case `repo`. +Notez quelque-part le jeton que vous obtenez. + +### Structure des fichiers LaTeX + +Vos fichiers LaTeX peuvent être structurés comme vous le souhaitez, du moment qu'ils sont interprétables +par [KaTeX](https://katex.org). Ceux-ci doivent tout de même définir deux environnements : + +* `doctitle` qui doit correspondre au titre de votre document. +* `docnumber` qui peut correspondre à un numéro de chapitre par exemple. + +Afin d'être compilables par [Pandoc](https://pandoc.org), vous pouvez créer un fichier `pandoc.tex`, +à placer dans le `lessonsDirectory`. Voici par exemple le contenu du mien : + +```tex +% Ceci me permet de remplacer ma commande \cours dans mes fichiers LaTeX. +\providecommand{\cours}[3]{% + \begin{doctitle}% + #2% + \end{doctitle}% +} + +% Et ceci me permet de remplacer ma commande \chapitrenumero dans mes fichiers LaTeX. +\providecommand{\chapitrenumero}[1]{% + \begin{docnumber}% + #1% + \end{docnumber}% +} +``` + +### Création d'un projet Vercel + +Ainsi, afin d'être utilisable, nous avons encore besoin de créer un projet sur [Vercel](https://vercel.com/). +Inscrivez-vous si ce n'est pas déjà fait et créez un nouveau projet à partir du dépôt Github cloné. + +Une fois créé, nous allons devoir créer deux variables d'environnements (dans les paramètres du projet Vercel) : + +* `GITHUB_CLIENT_SECRET`, qui contient votre `Client secret`. +* `ENCRYPTION_KEY`, qui contient 32 caractères générés de manière aléatoire (majuscules, minuscules et chiffres uniquement). +* `GITHUB_PERSONAL_ACCESS_TOKEN`, qui contient votre `Personal access token` créé précédemment. + Cette étape est requise uniquement si vous utilisez un dépôt séparé pour héberger vos données. + +## Tests locaux + +Pour tester localement votre site web, une commande suffit : `npm run vercel`. Il vous faut également +créer un fichier `.env` contenant toutes variables d'environnement listées [précédemment](#création-dun-projet-vercel). diff --git a/api/calendar/clear.js b/api/calendar/clear.js new file mode 100644 index 0000000000..aaa48f790a --- /dev/null +++ b/api/calendar/clear.js @@ -0,0 +1,25 @@ +import github from '../../utils/octokit' +import site from '../../site' + +export default async function handler (request, response) { + const octokit = github.createOctokitFromRequest(request, response) + if (octokit === null) { + return + } + let githubResponse = await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', { + owner: site.github.username, + repo: site.github.dataRepository, + path: site.github.calendarFile + }) + githubResponse = await octokit.request('DELETE /repos/{owner}/{repo}/contents/{path}', { + owner: site.github.username, + repo: site.github.dataRepository, + path: site.github.calendarFile, + message: 'Suppression du calendrier.', + sha: githubResponse.data.sha + }) + const data = githubResponse.data + response.status(githubResponse.status).json({ + commit: data.commit + }) +} diff --git a/api/calendar/dates.js b/api/calendar/dates.js new file mode 100644 index 0000000000..5e2ca40254 --- /dev/null +++ b/api/calendar/dates.js @@ -0,0 +1,13 @@ +import github from '../../utils/octokit' +import calendar from '../../utils/calendar' + +export default async function handler (request, response) { + const octokit = github.createOctokitFromRequest(request, response) + if (octokit === null) { + return + } + const calendarObject = await calendar.loadCalendar(octokit) + response.status(200).json({ + dates: calendarObject === null ? [] : Object.keys(calendarObject) + }) +} diff --git a/api/calendar/get.js b/api/calendar/get.js new file mode 100644 index 0000000000..be720c2ef2 --- /dev/null +++ b/api/calendar/get.js @@ -0,0 +1,17 @@ +import github from '../../utils/octokit' +import calendar from '../../utils/calendar' + +export default async function handler (request, response) { + if (!request.query.date) { + response.status(400).send('Il manque au moins un paramètre.') + return + } + const octokit = github.createOctokitFromRequest(request, response) + if (octokit === null) { + return + } + const calendar = await calendar.loadCalendar(octokit) + response.status(200).json({ + content: calendar === null || !Object.prototype.hasOwnProperty.call(calendar, request.query.date) ? '' : calendar[request.query.date] + }) +} diff --git a/api/calendar/update.js b/api/calendar/update.js new file mode 100644 index 0000000000..4363c176c5 --- /dev/null +++ b/api/calendar/update.js @@ -0,0 +1,46 @@ +import github from '../../utils/octokit' +import site from '../../site' + +export default async function handler (request, response) { + if (!request.body.date || !request.body.content) { + response.status(400).send('Il manque au moins un paramètre.') + return + } + const octokit = github.createOctokitFromRequest(request, response) + if (octokit === null) { + return + } + let githubResponse + try { + githubResponse = await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', { + owner: site.github.username, + repo: site.github.dataRepository, + path: site.github.calendarFile + }) + } catch (ex) { + githubResponse = ex + } + let sha + let calendar = {} + if (githubResponse.status !== 404) { + calendar = JSON.parse(Buffer.from(githubResponse.data.content, 'base64').toString('utf8')) + sha = githubResponse.data.sha + } + if (request.body.content.length === 0) { + delete calendar[request.body.date] + } else { + calendar[request.body.date] = request.body.content + } + githubResponse = await octokit.request('PUT /repos/{owner}/{repo}/contents/{path}', { + owner: site.github.username, + repo: site.github.dataRepository, + path: site.github.calendarFile, + message: `Mise à jour d'un événement pour la date \`${request.body.date}\`.`, + sha, + content: Buffer.from(JSON.stringify(calendar), 'utf8').toString('base64') + }) + response.status(githubResponse.status).json({ + commit: githubResponse.data.commit, + content: Object.prototype.hasOwnProperty.call(calendar, request.body.date) ? calendar[request.body.date] : '' + }) +} diff --git a/api/lessons/delete.js b/api/lessons/delete.js new file mode 100644 index 0000000000..c97850affa --- /dev/null +++ b/api/lessons/delete.js @@ -0,0 +1,25 @@ +import github from '../../utils/octokit' +import site from '../../site' + +export default async function handler (request, response) { + const path = request.query.path + if (!path || !request.query.sha) { + response.status(400).send('Il manque au moins un paramètre.') + return + } + const octokit = github.createOctokitFromRequest(request, response) + if (octokit === null) { + return + } + const githubResponse = await octokit.request('DELETE /repos/{owner}/{repo}/contents/{path}', { + owner: site.github.username, + repo: site.github.dataRepository, + path: `${site.github.lessonsDirectory}${path}`, + message: `Suppression de \`${path}\`.`, + sha: request.query.sha + }) + const data = githubResponse.data + response.status(githubResponse.status).json({ + commit: data.commit + }) +} diff --git a/api/lessons/get.js b/api/lessons/get.js new file mode 100644 index 0000000000..3ce2462995 --- /dev/null +++ b/api/lessons/get.js @@ -0,0 +1,26 @@ +import github from '../../utils/octokit' +import site from '../../site' + +export default async function handler (request, response) { + const path = request.query.path + if (!path) { + response.status(400).send('Il manque au moins un paramètre.') + return + } + const octokit = github.createOctokitFromRequest(request, response) + if (octokit === null) { + return + } + const githubResponse = await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', { + owner: site.github.username, + repo: site.github.dataRepository, + path: `${site.github.lessonsDirectory}${path}` + }) + const data = githubResponse.data + response.status(githubResponse.status).json({ + name: data.name, + path: data.path.replace(site.github.lessonsDirectory, ''), + sha: data.sha, + content: data.content + }) +} diff --git a/api/lessons/list.js b/api/lessons/list.js new file mode 100644 index 0000000000..e1a8fb955c --- /dev/null +++ b/api/lessons/list.js @@ -0,0 +1,46 @@ +import github from '../../utils/octokit' +import site from '../../site' + +export default async function handler (request, response) { + const octokit = github.createOctokitFromRequest(request, response) + if (octokit === null) { + return + } + let path = request.query.path ?? '' + path = `${site.github.lessonsDirectory}${path}` + if (path.endsWith('/')) { + path = path.substring(0, path.length - 1) + } + const githubResponse = await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', { + owner: site.github.username, + repo: site.github.dataRepository, + path + }) + const contents = githubResponse.data + const jsonResponse = [] + for (const content of contents) { + jsonResponse.push({ + name: content.name, + path: content.path.replace(site.github.lessonsDirectory, ''), + sha: content.sha, + type: content.type + }) + } + jsonResponse.sort(function (a, b) { + if (a.type === 'dir') { + if (b.type !== 'dir') { + return -1 + } + } else if (b.type === 'dir') { + return 1 + } + if (a.name < b.name) { + return -1 + } + if (a.name > b.name) { + return 1 + } + return 0 + }) + response.status(githubResponse.status).json(jsonResponse) +} diff --git a/api/lessons/rename.js b/api/lessons/rename.js new file mode 100644 index 0000000000..7129600c4a --- /dev/null +++ b/api/lessons/rename.js @@ -0,0 +1,49 @@ +import github from '../../utils/octokit' +import site from '../../site' + +export default async function handler (request, response) { + const path = request.query.path + if (!path || !request.query.sha || !request.query.name) { + response.status(400).send('Il manque au moins un paramètre.') + return + } + const octokit = github.createOctokitFromRequest(request, response) + if (octokit === null) { + return + } + let newPath = request.query.name + if (path.lastIndexOf('/') !== -1) { + newPath = path.substring(0, path.lastIndexOf('/')) + '/' + newPath + } + let githubResponse = await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', { + owner: site.github.username, + repo: site.github.dataRepository, + path: `${site.github.lessonsDirectory}${path}` + }) + const content = githubResponse.data.content + githubResponse = await octokit.request('DELETE /repos/{owner}/{repo}/contents/{path}', { + owner: site.github.username, + repo: site.github.dataRepository, + path: `${site.github.lessonsDirectory}${path}`, + message: `Renommage de \`${path}\` (1/2).`, + sha: request.query.sha + }) + const deleteCommit = githubResponse.data.commit + githubResponse = await octokit.request('PUT /repos/{owner}/{repo}/contents/{path}', { + owner: site.github.username, + repo: site.github.dataRepository, + path: `${site.github.lessonsDirectory}${newPath}`, + message: `Renommage de \`${path}\` (2/2).`, + content + }) + const createCommit = githubResponse.data.commit + const data = githubResponse.data + const file = data.content + response.status(githubResponse.status).json({ + deleteCommit, + createCommit, + name: file.name, + path: file.path.replace(site.github.lessonsDirectory, ''), + sha: file.sha + }) +} diff --git a/api/lessons/update.js b/api/lessons/update.js new file mode 100644 index 0000000000..288dbceeb9 --- /dev/null +++ b/api/lessons/update.js @@ -0,0 +1,31 @@ +import github from '../../utils/octokit' +import site from '../../site' + +export default async function handler (request, response) { + const path = request.body.path + if (!path || !request.body.sha || !request.body.content) { + response.status(400).send('Il manque au moins un paramètre.') + return + } + const octokit = github.createOctokitFromRequest(request, response) + if (octokit === null) { + return + } + const githubResponse = await octokit.request('PUT /repos/{owner}/{repo}/contents/{path}', { + owner: site.github.username, + repo: site.github.dataRepository, + path: `${site.github.lessonsDirectory}${path}`, + message: `Mise à jour de \`${path}\`.`, + sha: request.body.sha, + content: request.body.content + }) + const data = githubResponse.data + const file = data.content + response.status(githubResponse.status).json({ + commit: data.commit, + name: file.name, + path: file.path.replace(site.github.lessonsDirectory, ''), + sha: file.sha, + content: file.content + }) +} diff --git a/api/user/login.js b/api/user/login.js new file mode 100644 index 0000000000..cfc92728a7 --- /dev/null +++ b/api/user/login.js @@ -0,0 +1,30 @@ +import crypto from '../../utils/crypto' +import site from '../../site' +const cookie = require('cookie') +const { createOAuthAppAuth } = require('@octokit/auth-oauth-app') + +export default async function handler (request, response) { + if (request.query.code && request.query.state) { + const auth = createOAuthAppAuth({ + clientId: site.github.authentication.clientId, + clientSecret: site.github.authentication.clientSecret + }) + const githubResponse = await auth({ + type: 'oauth-user', + code: request.query.code, + state: request.query.state + }) + if (githubResponse.token) { + const expiration = new Date() + expiration.setDate(expiration.getDate() + site.github.authentication.cookieExpirationDays) + response.setHeader('Set-Cookie', [ + cookie.serialize('access_token', crypto.encrypt(githubResponse.token), { expires: expiration, path: '/' }) + ]) + response.redirect('/prof') + } else { + response.status(500).send("Erreur provenant sûrement d'une mauvaise configuration.") + } + return + } + response.status(401).send("Votre requête doit contenir un 'code' provenant de Github.") +} diff --git a/assets/fonts.scss b/assets/fonts.scss new file mode 100644 index 0000000000..b1baf06d8f --- /dev/null +++ b/assets/fonts.scss @@ -0,0 +1,11 @@ +h1, +h2, +h3, +h4 { + font-family: 'Raleway', sans-serif; +} + +html, +body { + font-family: 'Montserrat', sans-serif; +} diff --git a/components/Applications/Agenda/Calendar.vue b/components/Applications/Agenda/Calendar.vue new file mode 100644 index 0000000000..616e779876 --- /dev/null +++ b/components/Applications/Agenda/Calendar.vue @@ -0,0 +1,222 @@ + + + + + diff --git a/components/Applications/FileUploadButton.vue b/components/Applications/FileUploadButton.vue new file mode 100644 index 0000000000..3ee7c5e453 --- /dev/null +++ b/components/Applications/FileUploadButton.vue @@ -0,0 +1,54 @@ + + + diff --git a/components/Applications/Lessons/CodeEditor.vue b/components/Applications/Lessons/CodeEditor.vue new file mode 100644 index 0000000000..52153b6039 --- /dev/null +++ b/components/Applications/Lessons/CodeEditor.vue @@ -0,0 +1,43 @@ + + + diff --git a/components/Applications/Protected.vue b/components/Applications/Protected.vue new file mode 100644 index 0000000000..c84a5bc1b2 --- /dev/null +++ b/components/Applications/Protected.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/components/Applications/Whiteboard/Draggable.vue b/components/Applications/Whiteboard/Draggable.vue new file mode 100644 index 0000000000..35031f14ec --- /dev/null +++ b/components/Applications/Whiteboard/Draggable.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/components/Applications/Whiteboard/ImageDraggable.vue b/components/Applications/Whiteboard/ImageDraggable.vue new file mode 100644 index 0000000000..ef504907f8 --- /dev/null +++ b/components/Applications/Whiteboard/ImageDraggable.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/components/Applications/Whiteboard/PdfDraggable.vue b/components/Applications/Whiteboard/PdfDraggable.vue new file mode 100644 index 0000000000..2372d68e05 --- /dev/null +++ b/components/Applications/Whiteboard/PdfDraggable.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/components/Applications/Whiteboard/StopwatchDraggable.vue b/components/Applications/Whiteboard/StopwatchDraggable.vue new file mode 100644 index 0000000000..e834757a1f --- /dev/null +++ b/components/Applications/Whiteboard/StopwatchDraggable.vue @@ -0,0 +1,147 @@ + + + + + diff --git a/components/Applications/Whiteboard/TextDraggable.vue b/components/Applications/Whiteboard/TextDraggable.vue new file mode 100644 index 0000000000..f1bef233a5 --- /dev/null +++ b/components/Applications/Whiteboard/TextDraggable.vue @@ -0,0 +1,40 @@ +