├── .browserslistrc
├── .eslintrc.js
├── .gitignore
├── .prettierrc.js
├── Dockerfile
├── README.md
├── build
├── icon.icns
└── icon.ico
├── favicon.ico
├── index.html
├── jest.config.js
├── package.json
├── scrapcode.txt
├── server
├── controllers
│ ├── accountController.js
│ ├── authController.js
│ ├── cookieController.js
│ ├── oAuthController.js
│ └── projectController.js
├── models
│ ├── accountModels.js
│ └── projectModels.js
├── routes
│ ├── accountRouter.js
│ └── projectRouter.js
└── server.js
├── src
├── App.vue
├── assets
│ ├── 3.png
│ ├── ComponentMp4.mp4
│ ├── ModalMp4.mp4
│ ├── PreVueDemo.gif
│ ├── PreVueDemo.mp4
│ ├── PreVueDemo2.mp4
│ ├── PreVueExportDemo.gif
│ ├── SaveMp4.mp4
│ ├── april-photo.jpg
│ ├── background.jpg
│ ├── button.svg
│ ├── cole-photo.jpg
│ ├── componentdisplay.png
│ ├── form.svg
│ ├── github-icon-white.svg
│ ├── homeview.png
│ ├── ilay-photo.jpg
│ ├── img.png
│ ├── img.svg
│ ├── input.svg
│ ├── jason-photo.jpg
│ ├── link.svg
│ ├── linkedin-svg.svg
│ ├── list-item.svg
│ ├── list-ol.svg
│ ├── list-ul.svg
│ ├── logo.png
│ ├── logo.svg
│ ├── modal-image.png
│ ├── nathan-photo.jpg
│ ├── navbar.png
│ ├── navbar.svg
│ ├── new-banner.png
│ ├── newcomp.png
│ ├── newcompvue.png
│ ├── p1.png
│ ├── paragraph.svg
│ ├── prevue-large-green-bottom.png
│ ├── prevue-large-green.png
│ ├── prevue-large.png
│ ├── prevue-logo.png
│ ├── prevue-recording.gif
│ ├── prevue-recording.mp4
│ ├── prevue.png
│ ├── prevue_color_white.png
│ ├── pvv.png
│ ├── robert-photo.jpeg
│ ├── routecreator.png
│ ├── sean-photo.jpeg
│ ├── tree-demo.png
│ ├── treeview.png
│ ├── viewcreator.png
│ └── zach-photo.jpeg
├── components
│ ├── ChildrenMultiselect.vue
│ ├── Component.vue
│ ├── ComponentDisplay.vue
│ ├── ExportProjectComponent.vue
│ ├── HomeQueue.vue
│ ├── HomeSidebar.vue
│ ├── Icons.vue
│ ├── LogOutComponent.vue
│ ├── Modal
│ │ ├── ComponentCodeDisplay.vue
│ │ ├── EditQueue.vue
│ │ ├── EditSidebar.vue
│ │ └── Modal.vue
│ ├── NavBar.vue
│ ├── NewProjectComponent.vue
│ ├── OpenProjectComponent.vue
│ ├── ProjectTabs.vue
│ ├── RouteDisplay.vue
│ ├── Routes.vue
│ ├── SaveProjectComponent.vue
│ └── TreeGraph.vue
├── main.ts
├── router.ts
├── store
│ ├── actions.ts
│ ├── index.ts
│ ├── mutations.ts
│ ├── state
│ │ ├── htmlElementMap.ts
│ │ ├── icons.ts
│ │ └── stateIndex.ts
│ └── storeTypes.ts
├── types.ts
└── views
│ ├── HomeView.vue
│ ├── SplashView.vue
│ └── TreeView.vue
├── tests
├── integration
│ └── supertest.spec.js
└── unit
│ ├── .eslintrc.js
│ ├── App.spec.js
│ ├── ChildrenMultiselect.spec.js
│ ├── Component.spec.js
│ ├── ComponentDisplay.spec.js
│ ├── HomeQueue.spec.js
│ ├── HomeSidebar.spec.js
│ ├── Icons.spec.js
│ ├── RouteDisplay.spec.js
│ ├── TreeGraph.spec.js
│ └── __snapshots__
│ └── App.spec.js.snap
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not ie <= 8
4 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | es2021: true,
5 | node: true
6 | },
7 | extends: [
8 | 'plugin:vue/essential',
9 | 'plugin:prettier/recommended',
10 | '@vue/prettier',
11 | 'eslint:recommended',
12 | 'plugin:@typescript-eslint/recommended'
13 | ],
14 | parser: 'vue-eslint-parser',
15 | plugins: ['@typescript-eslint, vue'],
16 | rules: {
17 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn',
18 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # dotenv environment variables files (local env files)
6 | .env.local
7 | .env.*.local
8 | .env
9 | .env.test
10 |
11 |
12 | # Log files
13 | npm-debug.log*
14 | yarn-debug.log*
15 | yarn-error.log*
16 |
17 |
18 | # Editor directories and files
19 | .idea
20 | .vscode
21 | *.suo
22 | *.ntvs*
23 | *.njsproj
24 | *.sln
25 | *.sw*
26 |
27 |
28 | package-lock.json
29 | # Elastic Beanstalk Files
30 | .elasticbeanstalk/*
31 | !.elasticbeanstalk/*.cfg.yml
32 | !.elasticbeanstalk/*.global.yml
33 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true
3 | };
4 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # base image that provides runtime environment for the application
2 | FROM node:16.13
3 | # where the application code will be copied to in the docker container
4 | WORKDIR /usr/src/app
5 | # copies all files from the current directory (where the Dockerfile is located) to the working directory in the docker image (which we set on line 4)
6 | COPY . .
7 | RUN npm install
8 | RUN npm run build
9 | # Exposes port 4173 to the host machine, so that it can access the Node.js application running inside the Docker container
10 | EXPOSE 8080
11 | # Specifies the command to run when the Docker container starts
12 | ENTRYPOINT npm run server
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ---
6 |
7 |
8 |
9 |
PreVue
10 |
11 |
12 |
13 | All-in-One Prototyping Tool
14 | For Vue Developers
15 |
16 |
17 |
18 | From Component Architecture to Code Exporting
19 |
20 |
21 |
24 |
25 | PreVue allows users to conceptualize and visualize component architecture by making it possible to :
26 |
27 | - Build components, visualize UI and preview the associated code
28 | - Set up different routes and views for each project
29 | - Establish parent-child component relationships
30 | - View application hierarchy in tree format
31 | - Save and open projects that are currently in progress, ensuring that completed work is not lost and can be revisited at any time
32 | - Export component architecture as a Vue application created with default Vite settings
33 |
34 | Use PreVue to create projects in single sessions or sign in with GitHub to save projects and update them at your convenience!
35 |
36 |
39 |
40 | ## Getting Started
41 |
42 | ---
43 |
44 | ### Adding Views
45 |
46 | - Select an existing view from the View Creator dropdown, or enter a new view name, then select your custom view from the View Creator dropdown
47 | - Any components created on a given view will be automatically saved to that specific view
48 | - See your application’s hierarchy by clicking the ‘Tree’ icon in the navigation bar
49 |
50 |
51 |
52 |
53 |
54 | Tree View of Application Architecture
55 |
56 |
57 |
58 |
59 |
60 | ### Adding Components
61 |
62 | - Enter a component name in the Component Creator field and select HTML elements
63 | - Clicked elements will be shown in the right sidebar -- drag elements to change their order
64 | - Once you're satisfied, click ‘add component’ button and it will show up in the working area -- resize and move components to fit the design you have in mind
65 |
66 |
67 |
68 | ### Editing Components
69 |
70 | - Double click elements to bring up the modal view
71 | - Add additional elements to a component with a live preview of the component code
72 | - Drag selected elements to the right to nest elements
73 | - Establish parent-child component relationships via the dropdown menu when creating or editing components
74 |
75 |
76 |
77 | ### Saving / Opening / Exporting Projects
78 |
79 | - If you're signed in with GitHub, click the ‘Save Project’ icon to save it to PreVue’s database
80 | - Click ‘Open Project’ to retrieve past projects
81 | - Once you're satisfied, click the export project icon to export your awesome project as new Vue application
82 | - Other users can use PreVue's playground to create and export projects in single sessions without signing in
83 |
84 |
86 |
87 | ### Code Exporting
88 |
89 | Below is the generated directory structure of the Vue application that is created when you export your design.
90 |
91 | ```
92 | src/
93 | assets/
94 | App.vue
95 | components/
96 | UserCreatedComponent1.vue
97 | UserCreatedComponent2.vue
98 | ...
99 | views/
100 | HomeView.vue
101 | UserCreatedRouteComponent1.vue
102 | UserCreatedRouteComponent2.vue
103 | ...
104 | ```
105 |
106 |
111 |
112 | ## Built With
113 |
114 | ---
115 |
116 | - [Express](https://expressjs.com/)
117 | - [Jest](https://jestjs.io/)
118 | - [MongoDB](https://www.mongodb.com/)
119 | - [Mongoose](https://mongoosejs.com/)
120 | - [Node.js](https://nodejs.org/en)
121 | - [SuperTest](https://www.npmjs.com/package/supertest)
122 | - [Vite](https://vitejs.dev/)
123 | - [Vue Router](https://router.vuejs.org/guide/#html)
124 | - [Vue Test Utils](https://test-utils.vuejs.org/)
125 | - [Vue.js](https://vuejs.org/)
126 | - [Vuex](https://vuex.vuejs.org/)
127 | - [Vuetify](https://vuetifyjs.com/)
128 |
129 | ## Changelog
130 |
131 | ---
132 |
133 | PreVue 3.0 Updates:
134 |
135 | - OAuth integration with GitHub for secure authentication
136 | - Full CRUD functionality for prototype creation
137 | - Implementation of appropriate hierarchical relationships reflected in UI
138 | - Website and Homepage redesign for seamless user experience
139 | - Realistic rendering of elements to Component Display
140 | - Delete and Undo functionality for individual Components
141 | - Project saving, loading & export ability
142 |
143 | PreVue 2.0 Updates:
144 |
145 | - Implementation of PreVue as a web application
146 | - TypeScript integration
147 | - Backend infrastructure built with Node/Express
148 | - General UI/UX enhancements
149 | - Testing with Vitest and Supertest (and Jest)
150 |
151 | ## Contributing to PreVue
152 |
153 | ---
154 |
155 | We encourage you to submit issues for any bugs or ideas for enhancements. Please feel free to fork this repo and submit pull requests to contribute as well. Follow PreVue on [LinkedIn](https://www.linkedin.com/company/prevue-live/) for more updates.
156 |
157 | Ideas for additional features include:
158 |
159 | - Project livesharing for collaborative sessions (via Websockets)
160 | - Migrate state management from Vuex to Pinia
161 | - More thorough testing with Jest
162 | - Ability to rename and add styling to individual components
163 | - Containerization of PreVue App
164 | - User Authentication updates via OAuth
165 |
166 | ## Authors
167 |
168 | Prevue 3.0
169 |
170 | - **April Sanders** [@algorithmrhythm](https://github.com/algorithmrhythm)
171 | - **Cole Jaeger** [@colejaeger0](https://github.com/colejaeger0)
172 | - **Ilay Eskinazi** [@Pixolino](https://github.com/Pixolino)
173 | - **Nathan Bornstein** [@greenteaisgreat](https://github.com/greenteaisgreat)
174 |
175 | PreVue 2.0
176 |
177 | - **Jason Boo** [@jasonboo123](https://github.com/jasonboo123)
178 | - **Robert Drake** [@rmdrake8](https://github.com/rmdrake8)
179 | - **Sean Flynn** [@seanflynn5](http://github.com/seanflynn5)
180 | - **Zach Pestaina** [@zachpestaina](https://github.com/zachpestaina)
181 |
182 | PreVue 1.0
183 |
184 | - **Hubert Lin** [@hubelin](https://github.com/hubelin)
185 | - **Franklin Pinnock** [@pinnockf](https://github.com/pinnockf)
186 | - **Annette Lin** [@al2613](https://github.com/al2613)
187 | - **Daniel Shu** [@danshuu](https://github.com/danshuu)
188 |
189 | ## License
190 |
191 | ---
192 |
193 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
194 |
195 | [](https://github.com/teamprevue/PreVue/pulls)
196 | 
197 |
--------------------------------------------------------------------------------
/build/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/build/icon.icns
--------------------------------------------------------------------------------
/build/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/build/icon.ico
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/favicon.ico
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
15 |
16 | PreVue
17 |
18 |
19 |
20 |
21 | We're sorry but prevue doesn't work properly without JavaScript
23 | enabled. Please enable it to continue.
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
3 | transform: {
4 | '^.+\\.vue$': 'vue-jest',
5 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
6 | 'jest-transform-stub',
7 | '^.+\\.jsx?$': 'babel-jest',
8 | },
9 | transformIgnorePatterns: ['/node_modules/'],
10 | moduleNameMapper: {
11 | '^@/(.*)$': '/src/$1',
12 | },
13 | snapshotSerializers: ['jest-serializer-vue'],
14 | testMatch: [
15 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)',
16 | ],
17 | testEnvironmentOptions: {
18 | url: 'http://localhost:5174', // replace with your test URL
19 | },
20 | testEnvironment: 'node',
21 | };
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "module": {
3 | "type": "commonjs"
4 | },
5 | "name": "prevue",
6 | "author": "teamprevue (www.prevue.io)",
7 | "version": "3.0.0",
8 | "description": "Developer prototyping app built with Vue",
9 | "scripts": {
10 | "dev": "vite",
11 | "build": "vite build",
12 | "serve": "vite preview",
13 | "vite": "npm run server",
14 | "server": "node server/server.js",
15 | "test": "NODE_ENV=test vitest",
16 | "coverage": "vitest run --coverage",
17 | "start": "node server/server.js"
18 | },
19 | "license": "MIT",
20 | "dependencies": {
21 | "@he-tree/vue": "^2.2.7",
22 | "@progress/jszip-esm": "^1.0.3",
23 | "@ssthouse/vue3-tree-chart": "^0.2.6",
24 | "axios": "^1.3.4",
25 | "concurrently": "^7.6.0",
26 | "connect-history-api-fallback": "^2.0.0",
27 | "cookie-parser": "^1.4.6",
28 | "cors": "^2.8.5",
29 | "dotenv": "^16.3.1",
30 | "express": "^4.18.2",
31 | "file-saver": "^2.0.5",
32 | "fs-extra": "^7.0.1",
33 | "handlebars": "^4.7.7",
34 | "happy-dom": "^8.9.0",
35 | "he-tree-vue": "^3.1.2",
36 | "jsonwebtoken": "^9.0.2",
37 | "mongodb": "^5.1.0",
38 | "mongodb-connection-string-url": "^2.6.0",
39 | "mongoose": "^6.10.0",
40 | "mousetrap": "^1.6.3",
41 | "sass": "^1.58.3",
42 | "vue": "^3.2.47",
43 | "vue-multiselect": "^3.0.0-alpha.2",
44 | "vue-router": "^4.1.6",
45 | "vue-template-compiler": "^2.6.11",
46 | "vue3-draggable-resizable": "^1.6.5",
47 | "vued3tree": "^3.6.4",
48 | "vuedraggable": "^4.1.0",
49 | "vuetify": "^3.1.6"
50 | },
51 | "devDependencies": {
52 | "@typescript-eslint/eslint-plugin": "^5.54.0",
53 | "@typescript-eslint/parser": "^5.54.0",
54 | "@vitejs/plugin-vue": "^4.0.0",
55 | "@vue/eslint-config-prettier": "^4.0.1",
56 | "@vue/test-utils": "^2.3.1",
57 | "eslint": "^8.35.0",
58 | "eslint-plugin-typescript": "^0.14.0",
59 | "eslint-plugin-vue": "^8.7.1",
60 | "jest": "^29.5.0",
61 | "jsdom": "^21.1.0",
62 | "sinon": "^15.0.1",
63 | "supertest": "^6.3.3",
64 | "typescript": "^4.9.5",
65 | "vite": "^4.1.4",
66 | "vitest": "^0.29.2",
67 | "vue-eslint-parser": "^9.1.0",
68 | "vue-template-compiler": "^2.5.21",
69 | "vue-tsc": "^1.0.24",
70 | "vuex": "^4.1.0"
71 | },
72 | "main": "background.js",
73 | "directories": {
74 | "test": "tests"
75 | },
76 | "repository": {
77 | "type": "git",
78 | "url": "git+https://github.com/oslabs-beta/PreVue.git"
79 | },
80 | "bugs": {
81 | "url": "https://github.com/oslabs-beta/PreVue/issues"
82 | },
83 | "homepage": "https://github.com/oslabs-beta/PreVue#readme"
84 | }
--------------------------------------------------------------------------------
/scrapcode.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 | {{ editedProjectName }}
16 |
17 |
18 |
19 |
20 |
54 |
55 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/server/controllers/accountController.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 | const Users = require('../models/accountModels');
3 | const jwt = require('jsonwebtoken');
4 | const accountController = {};
5 |
6 | // enters a user into the database after GitHub OAuth if an entry does not already exist
7 | accountController.createUser = (req, res, next) => {
8 | const { username, id } = res.locals;
9 | //Making sure username exists
10 | Users.findOne({ username })
11 | .then(data => {
12 | if (!data) {
13 | Users.create({ username, id }).then(data => {
14 | //data here is full entry, includes _id key
15 | res.locals.id = data._id; // sending ID for cookie auth
16 | return next();
17 | });
18 | } else {
19 | return next();
20 | }
21 | })
22 | .catch(err => {
23 | next({
24 | log: `accountController.createUser failed: ${err}`,
25 | message: `User already exists!`
26 | });
27 | });
28 | };
29 |
30 | // showing logged in user's projects before mounting
31 | accountController.userProjects = (req, res, next) => {
32 | Users.findOne({ username: res.locals.username })
33 | .then(data => {
34 | res.locals.userProjects = data.project_ids;
35 | return next();
36 | })
37 | .catch(err => {
38 | next({
39 | log: 'accountController.userProjects failed',
40 | message: `could not find projects for user`
41 | });
42 | });
43 | };
44 |
45 | // just for test purposes; returns all users in the database
46 | accountController.findUser = (req, res, next) => {
47 | Users.find({})
48 | .then(data => {
49 | res.locals.username = data;
50 | return next();
51 | })
52 | .catch(err => {
53 | // if (err.message === `Username Doesn't Exist`) res.redirect("/signup");
54 | return next({
55 | log: err,
56 | error: `error found in userController.findUser`
57 | });
58 | });
59 | };
60 |
61 | module.exports = accountController;
62 |
--------------------------------------------------------------------------------
/server/controllers/authController.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 | const jwt = require('jsonwebtoken');
3 |
4 | //secret key generated when integrating your app with Github OAuth (must create .env file in root directory)
5 | const privateKey = process.env.SECRET_KEY;
6 | const authController = {};
7 |
8 | // assigns a JWT to a user upon login
9 | authController.sign = (req, res, next) => {
10 | try {
11 | const { username, id } = res.locals;
12 | console.log(`username: ${username}`);
13 | const token = jwt.sign(
14 | {
15 | username,
16 | id
17 | },
18 | privateKey,
19 | { expiresIn: '6h' }
20 | );
21 | res.locals.token = token;
22 | return next();
23 | } catch (err) {
24 | return next(err);
25 | }
26 | };
27 |
28 | // authenticates user base on info stored on the JWT
29 | authController.authenticate = (req, res, next) => {
30 | const token = req.cookies.ssid;
31 | // Check if token is present
32 | if (!token) {
33 | // If no token, respond with 401 Unauthorized status
34 | return res.status(401).send('Authentication required');
35 | }
36 | try {
37 | // If token present, verify it
38 | const decoded = jwt.verify(token, privateKey);
39 | res.locals.username = decoded.username;
40 | res.locals.id = decoded.id;
41 | return next();
42 | } catch (err) {
43 | // If token verification fails, handle error
44 | return next({
45 | log: `authController.authenticate failed: ${err}`,
46 | message: `Authentication error: Invalid token`
47 | });
48 | }
49 | };
50 |
51 | module.exports = authController;
52 |
--------------------------------------------------------------------------------
/server/controllers/cookieController.js:
--------------------------------------------------------------------------------
1 | const cookieController = {};
2 |
3 | // sets cookie with key of 'ssid' and value of generated JWT
4 | cookieController.setSSIDCookie = (req, res, next) => {
5 | res.cookie('ssid', res.locals.token, { httpOnly: true });
6 | return next();
7 | };
8 |
9 | // used to log out a user
10 | cookieController.deleteCookie = (req, res, next) => {
11 | res.clearCookie('ssid');
12 | return next();
13 | };
14 |
15 | module.exports = cookieController;
16 |
--------------------------------------------------------------------------------
/server/controllers/oAuthController.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 | const axios = require('axios');
3 |
4 | const oAuthController = {};
5 | const GITHUB_OAUTH_CLIENT_ID = process.env.GITHUB_OAUTH_CLIENT_ID;
6 | const GITHUB_OAUTH_CLIENT_SECRET = process.env.GITHUB_OAUTH_CLIENT_SECRET;
7 | const GITHUB_ACCESS_TOKEN_REQUEST_URL = `https://github.com/login/oauth/access_token`;
8 | const GITHUB_REDIRECT_URI = process.env.GITHUB_REDIRECT_URI;
9 | let str = GITHUB_OAUTH_CLIENT_ID
10 | let newStr = GITHUB_REDIRECT_URI
11 |
12 | // first step of OAuth: redirects user to github with specific client id and redirect uri's concatenated
13 | oAuthController.oAuthLogin = async (req, res, next) => {
14 | try {
15 | console.log('sending get request to github ');
16 | let redirectStr =
17 | `https://github.com/login/oauth/authorize?` +
18 | 'client_id=' +
19 | str +
20 | '&redirect_uri=' +
21 | newStr;
22 | let redirectURL = new URL(redirectStr);
23 | res.locals.url = redirectURL;
24 | return next();
25 | } catch (error) {
26 | return next({
27 | log: 'Error occurred in the oauthController.oAuthLogin middleware',
28 | status: 400, // bad request
29 | err: {
30 | err: 'Error occurred in sending user to login to GitHub to login'
31 | }
32 | });
33 | }
34 | };
35 |
36 | // Get temporary "code" from Github (in req.query) in oauthController and AWAIT post request it back to exchange it for an access token (to Github API for user data)
37 | oAuthController.requestGitHubIdentity = async (req, res, next) => {
38 | try {
39 | const { code } = req.query;
40 | const { data } = await axios.post(
41 | GITHUB_ACCESS_TOKEN_REQUEST_URL,
42 | {
43 | client_id: GITHUB_OAUTH_CLIENT_ID,
44 | client_secret: GITHUB_OAUTH_CLIENT_SECRET,
45 | code
46 | },
47 | {
48 | headers: {
49 | Accept: 'application/json'
50 | }
51 | }
52 | );
53 | // if all is good, attach access_token to res.locals
54 | res.locals.access_token = data.access_token;
55 | console.log(`access_token aquired`);
56 | return next();
57 | } catch (error) {
58 | console.log(error);
59 | return next({
60 | log: `Error occurred in the oauthController.requestGitHubIdentity middleware\n Error: ${error.message}`,
61 | status: 400, // bad request
62 | err: { err: 'Error occurred in getting your Github user identity' }
63 | });
64 | }
65 | };
66 |
67 | // how a given user is actually authenticated
68 | // https://docs.github.com/en/rest/users/users?apiVersion=2022-11-28#get-the-authenticated-user
69 | oAuthController.queryGitHubAPIWithAccessToken = async (req, res, next) => {
70 | try {
71 | const auth = res.locals.access_token;
72 | const { data } = await axios.get('https://api.github.com/user', {
73 | headers: { Authorization: `Bearer ${auth}` }
74 | });
75 |
76 | // Check if necessary fields are present
77 | if (!data || !data.login || !data.id) {
78 | throw new Error('Required GitHub user data not found');
79 | }
80 |
81 | // set info from api to res.locals. -> Process GitHub Data
82 | const processedData = processGitHubData(data);
83 | res.locals = {
84 | ...res.locals,
85 | ...processedData
86 | };
87 |
88 | return next();
89 | } catch (error) {
90 | return next({
91 | log: `Error occurred in the oauthController.queryGitHubAPIWithAccessToken middleware\n Error: ${error.message}`,
92 | status: 400, // bad request
93 | err: { err: 'Error occurred in querying Github API with access token' }
94 | });
95 | }
96 | };
97 |
98 | // Helper function for converting Github API data to fields for database input
99 | function processGitHubData(data) {
100 | const { login, id } = data;
101 | return {
102 | username: login,
103 | id: id
104 | };
105 | }
106 |
107 | module.exports = oAuthController;
108 |
--------------------------------------------------------------------------------
/server/controllers/projectController.js:
--------------------------------------------------------------------------------
1 | const Project = require('../models/projectModels');
2 | const User = require('../models/accountModels');
3 | const projectController = {};
4 |
5 | // saving a project:
6 | projectController.saveProject = (req, res, next) => {
7 | const { project_name, projectObject } = req.body;
8 | Project.findOne({ project_name }).then(data => {
9 | console.log(`Saving project...`);
10 | if (!data) {
11 | Project.create({
12 | project_name,
13 | projectObject,
14 | projectOwner: res.locals.username
15 | })
16 | .then(data => {
17 | res.locals.newProject = data.projectObject;
18 | res.locals.projectName = data.project_name;
19 | return next();
20 | })
21 | .catch(err => {
22 | next({
23 | log: `projectController.saveProject failed, ${err}`,
24 | message: `Can't save new project!`
25 | });
26 | });
27 | } else {
28 | // if project already exists, update with new state
29 | Project.findOneAndUpdate(
30 | { project_name },
31 | { projectObject: req.body.projectObject }
32 | )
33 | .then(data => {
34 | res.locals.newProject = data.projectObject;
35 | res.locals.projectName = data.project_name;
36 | return next();
37 | })
38 | .catch(err => {
39 | next({
40 | log: `projectController.saveProject failed, ${err}`,
41 | message: `Can't update project!`
42 | });
43 | });
44 | }
45 | });
46 | };
47 |
48 | // Updates project_ids of User who's saving a project. If it already exists, it's not added.
49 | projectController.userQuery = (req, res, next) => {
50 | User.findOneAndUpdate(
51 | { username: res.locals.username },
52 | { $addToSet: { project_ids: res.locals.projectName } },
53 | { new: true }
54 | )
55 | .then(data => {
56 | res.locals.user = data;
57 | return next();
58 | })
59 | .catch(err => {
60 | next({
61 | log: `projectController.userQuery failed, ${err}`,
62 | message: `user already exists!`
63 | });
64 | });
65 | };
66 |
67 | //For retrieving projects ('open project' on frontend)
68 | projectController.getProject = (req, res, next) => {
69 | Project.findOne({
70 | project_name: req.body.project_name,
71 | projectOwner: res.locals.username
72 | })
73 | .then(data => {
74 | res.locals.project = data.projectObject;
75 | return next();
76 | })
77 | .catch(err => {
78 | next({
79 | log: `projectController.getProject failed, ${err}`,
80 | message: `Can't find project!`
81 | });
82 | });
83 | };
84 |
85 | //general query to find all projects; not used in app itself
86 | projectController.findProject = (req, res, next) => {
87 | Project.find({})
88 | .then(data => {
89 | res.locals.username = data;
90 | return next();
91 | })
92 | .catch(err => {
93 | // if (err.message === `Username Doesn't Exist`) res.redirect("/signup");
94 | return next({
95 | log: err,
96 | error: `error found in userController.verifyUser`
97 | });
98 | });
99 | };
100 |
101 | module.exports = projectController;
102 |
--------------------------------------------------------------------------------
/server/models/accountModels.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const Schema = mongoose.Schema;
4 |
5 | // schema for users stored in database
6 | const userSchema = new Schema({
7 | username: { type: String, required: true },
8 | id: { type: String, required: true },
9 | project_ids: { type: Array }
10 | });
11 |
12 | const Users = mongoose.model('Users', userSchema);
13 | module.exports = Users;
14 |
--------------------------------------------------------------------------------
/server/models/projectModels.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 |
3 | const Schema = mongoose.Schema;
4 |
5 | // schema for projects stored in database
6 | const projectSchema = new Schema({
7 | project_name: { type: String, required: true },
8 | projectObject: { type: Object, required: true },
9 | projectOwner: { type: String, required: true }
10 | });
11 |
12 | const Project = mongoose.model('Project', projectSchema);
13 | module.exports = Project;
14 |
--------------------------------------------------------------------------------
/server/routes/accountRouter.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const accountController = require('../controllers/accountController');
3 | const cookieController = require('../controllers/cookieController');
4 | const oAuthController = require('../controllers/oAuthController');
5 | const authController = require('../controllers/authController');
6 | const accountRouter = express.Router();
7 |
8 | accountRouter.get('/oauth', oAuthController.oAuthLogin, (req, res) => {
9 | // Instead of sending the URL back in the response, redirect the client to it
10 | res.redirect(res.locals.url.toString());
11 | });
12 |
13 | // retrieves specific user projects
14 | accountRouter.get(
15 | '/userProjects',
16 | authController.authenticate,
17 | accountController.userProjects,
18 | (req, res) => {
19 | return res.status(200).json(res.locals.userProjects);
20 | }
21 | );
22 |
23 | // github OAuth route
24 | accountRouter.get(
25 | '/oauth/access_token/redirect',
26 | oAuthController.requestGitHubIdentity,
27 | oAuthController.queryGitHubAPIWithAccessToken,
28 | accountController.createUser,
29 | authController.sign,
30 | cookieController.setSSIDCookie,
31 | (req, res) => {
32 | console.log('Succesful login');
33 | res.redirect('/home');
34 | }
35 | );
36 |
37 | // validates user on login
38 | accountRouter.get(
39 | '/validateSession',
40 | authController.authenticate,
41 | (req, res) => {
42 | res.status(200).json(res.locals.username);
43 | }
44 | );
45 |
46 | // logs out user by deleting cookie
47 | accountRouter.get('/logout', cookieController.deleteCookie, (req, res) => {
48 | return res.sendStatus(200);
49 | });
50 |
51 | // general route for querying users in database
52 | accountRouter.get(
53 | '/find',
54 | accountController.findUser,
55 | (req, res) => {
56 | return res.status(200).json(res.locals.username);
57 | }
58 | );
59 |
60 | module.exports = accountRouter;
61 |
--------------------------------------------------------------------------------
/server/routes/projectRouter.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const projectController = require('../controllers/projectController');
3 | const authController = require('../controllers/authController');
4 | const projectRouter = express.Router();
5 |
6 | // signup route
7 | projectRouter.post(
8 | '/saveProject',
9 | authController.authenticate,
10 | projectController.saveProject,
11 | projectController.userQuery,
12 | (req, res) => {
13 | return res.status(201).json(res.locals.user);
14 | }
15 | );
16 |
17 | projectRouter.post(
18 | '/getProject',
19 | authController.authenticate,
20 | projectController.getProject,
21 | (req, res) => {
22 | console.log('testing route');
23 | return res.status(201).json(res.locals.project);
24 | }
25 | );
26 |
27 | // used to test Supertest functionality; not used in actual app
28 | projectRouter.get(
29 | '/find',
30 | projectController.findProject,
31 | (req, res) => {
32 | return res
33 | .status(200)
34 | .json({ hello: test, 'res.locals.usename': res.locals.username });
35 | }
36 | );
37 |
38 | module.exports = projectRouter;
39 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 | const express = require('express');
3 | const path = require('path');
4 | const cookieParser = require('cookie-parser');
5 | const app = express();
6 | const PORT = process.env.PORT;
7 |
8 | const cors = require('cors');
9 | const corsOptions = {
10 | origin: process.env.CORS_ORIGIN,
11 | methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
12 | credentials: true,
13 | };
14 | const accountRouter = require('./routes/accountRouter');
15 | const projectRouter = require('./routes/projectRouter');
16 |
17 | // connecting to MongoDB
18 | const mongoose = require('mongoose');
19 | const myURI = process.env.MONGO_URI;
20 | const { MongoClient } = require('mongodb');
21 | mongoose
22 | .connect(myURI, {
23 | // options for the connect method to parse the URI
24 | useNewUrlParser: true,
25 | useUnifiedTopology: true,
26 | // sets the name of the DB that our collections are part of
27 | dbName: 'prevueDB',
28 | })
29 | .then(() => {
30 | console.log('Connected to Mongo DB.');
31 | })
32 | .catch((err) => console.log(err));
33 |
34 | // Global Middleware
35 | app.use(express.json());
36 | app.use(cookieParser());
37 | app.use(cors(corsOptions));
38 | app.use(express.urlencoded({ extended: true }));
39 |
40 | app.use(express.static(path.join(__dirname, '..', '.')));
41 |
42 | app.get('/', (req, res) => {
43 | res.status(200).sendFile(path.resolve(__dirname, 'index.html'));
44 | });
45 |
46 | // Routers
47 | app.use('/users', accountRouter);
48 | app.use('/projects', projectRouter);
49 |
50 | app.use((req, res) => res.sendStatus(404));
51 |
52 | // Global error handler
53 | app.use((err, req, res, next) => {
54 | const defaultErr = {
55 | log: 'Express error handler caught unknown middleware error',
56 | status: 400,
57 | message: { err: 'An error occurred' },
58 | };
59 | const errorObj = Object.assign({}, defaultErr, err);
60 | console.log(errorObj.log);
61 | return res.status(errorObj.status).json(errorObj.message);
62 | });
63 |
64 | // starts server
65 | app.listen(PORT, () => {
66 | console.log(`Server listening on port: ${PORT}`);
67 | });
68 |
69 | module.exports = app;
70 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
24 |
25 |
36 |
--------------------------------------------------------------------------------
/src/assets/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/3.png
--------------------------------------------------------------------------------
/src/assets/ComponentMp4.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/ComponentMp4.mp4
--------------------------------------------------------------------------------
/src/assets/ModalMp4.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/ModalMp4.mp4
--------------------------------------------------------------------------------
/src/assets/PreVueDemo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/PreVueDemo.gif
--------------------------------------------------------------------------------
/src/assets/PreVueDemo.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/PreVueDemo.mp4
--------------------------------------------------------------------------------
/src/assets/PreVueDemo2.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/PreVueDemo2.mp4
--------------------------------------------------------------------------------
/src/assets/PreVueExportDemo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/PreVueExportDemo.gif
--------------------------------------------------------------------------------
/src/assets/SaveMp4.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/SaveMp4.mp4
--------------------------------------------------------------------------------
/src/assets/april-photo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/april-photo.jpg
--------------------------------------------------------------------------------
/src/assets/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/background.jpg
--------------------------------------------------------------------------------
/src/assets/button.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/cole-photo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/cole-photo.jpg
--------------------------------------------------------------------------------
/src/assets/componentdisplay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/componentdisplay.png
--------------------------------------------------------------------------------
/src/assets/form.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/github-icon-white.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/assets/homeview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/homeview.png
--------------------------------------------------------------------------------
/src/assets/ilay-photo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/ilay-photo.jpg
--------------------------------------------------------------------------------
/src/assets/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/img.png
--------------------------------------------------------------------------------
/src/assets/img.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/input.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/jason-photo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/jason-photo.jpg
--------------------------------------------------------------------------------
/src/assets/link.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/linkedin-svg.svg:
--------------------------------------------------------------------------------
1 | LinkedIn icon
--------------------------------------------------------------------------------
/src/assets/list-item.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/list-ol.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/list-ul.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 | Artboard 46
2 |
--------------------------------------------------------------------------------
/src/assets/modal-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/modal-image.png
--------------------------------------------------------------------------------
/src/assets/nathan-photo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/nathan-photo.jpg
--------------------------------------------------------------------------------
/src/assets/navbar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/navbar.png
--------------------------------------------------------------------------------
/src/assets/navbar.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/new-banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/new-banner.png
--------------------------------------------------------------------------------
/src/assets/newcomp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/newcomp.png
--------------------------------------------------------------------------------
/src/assets/newcompvue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/newcompvue.png
--------------------------------------------------------------------------------
/src/assets/p1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/p1.png
--------------------------------------------------------------------------------
/src/assets/paragraph.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/prevue-large-green-bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/prevue-large-green-bottom.png
--------------------------------------------------------------------------------
/src/assets/prevue-large-green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/prevue-large-green.png
--------------------------------------------------------------------------------
/src/assets/prevue-large.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/prevue-large.png
--------------------------------------------------------------------------------
/src/assets/prevue-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/prevue-logo.png
--------------------------------------------------------------------------------
/src/assets/prevue-recording.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/prevue-recording.gif
--------------------------------------------------------------------------------
/src/assets/prevue-recording.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/prevue-recording.mp4
--------------------------------------------------------------------------------
/src/assets/prevue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/prevue.png
--------------------------------------------------------------------------------
/src/assets/prevue_color_white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/prevue_color_white.png
--------------------------------------------------------------------------------
/src/assets/pvv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/pvv.png
--------------------------------------------------------------------------------
/src/assets/robert-photo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/robert-photo.jpeg
--------------------------------------------------------------------------------
/src/assets/routecreator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/routecreator.png
--------------------------------------------------------------------------------
/src/assets/sean-photo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/sean-photo.jpeg
--------------------------------------------------------------------------------
/src/assets/tree-demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/tree-demo.png
--------------------------------------------------------------------------------
/src/assets/treeview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/treeview.png
--------------------------------------------------------------------------------
/src/assets/viewcreator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/viewcreator.png
--------------------------------------------------------------------------------
/src/assets/zach-photo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/open-source-labs/PreVue/deedda8b44e97f51b06ad6e2da17baf4997bf281/src/assets/zach-photo.jpeg
--------------------------------------------------------------------------------
/src/components/ChildrenMultiselect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
21 |
22 |
23 |
24 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/components/Component.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
25 | {{ compName }}
26 |
27 |
28 |
29 |
53 |
58 |
59 |
--------------------------------------------------------------------------------
/src/components/ComponentDisplay.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
27 |
28 | {{ componentData.componentName}}
29 |
30 | {
56 | draggableAgain();
57 | updatePosition($event, i, index);
58 | }"
59 | >
60 |
64 | div
65 |
66 |
67 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
278 |
359 |
--------------------------------------------------------------------------------
/src/components/ExportProjectComponent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Export Project
6 |
7 |
8 |
9 |
212 |
213 |
222 |
--------------------------------------------------------------------------------
/src/components/HomeQueue.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
19 | Selected Elements
20 |
21 |
30 |
31 |
32 |
33 | {{ element.text }}
34 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
86 |
87 |
--------------------------------------------------------------------------------
/src/components/HomeSidebar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
68 |
69 |
70 |
133 |
134 |
143 |
--------------------------------------------------------------------------------
/src/components/Icons.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
14 |
15 |
16 | {{ elementName }}
17 |
18 |
19 |
20 |
21 |
38 |
39 |
60 |
--------------------------------------------------------------------------------
/src/components/LogOutComponent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Logout
6 |
7 |
8 |
9 |
28 |
29 |
39 |
--------------------------------------------------------------------------------
/src/components/Modal/ComponentCodeDisplay.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ activeComponent }} Code Display
4 |
5 |
6 |
7 |
8 |
82 |
83 |
96 |
--------------------------------------------------------------------------------
/src/components/Modal/EditQueue.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Selected Elements:
4 |
5 |
6 |
7 |
8 |
9 |
64 |
65 |
115 |
--------------------------------------------------------------------------------
/src/components/Modal/EditSidebar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 |
34 |
47 |
--------------------------------------------------------------------------------
/src/components/Modal/Modal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
53 |
54 |
60 |
--------------------------------------------------------------------------------
/src/components/NavBar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
12 | PreVue
13 |
14 |
15 |
16 | Component Tree
17 |
18 |
19 |
20 |
21 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
72 |
73 |
124 |
125 |
--------------------------------------------------------------------------------
/src/components/NewProjectComponent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NEW PROJECT
6 |
7 |
8 |
9 |
58 |
59 |
69 |
--------------------------------------------------------------------------------
/src/components/OpenProjectComponent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Open Project
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
20 | OPEN
21 |
22 | Close
23 |
24 |
25 |
26 |
27 |
28 |
29 |
79 |
80 |
95 |
--------------------------------------------------------------------------------
/src/components/ProjectTabs.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
19 |
20 |
21 | {{ projectTabName }}
22 |
23 |
24 |
25 |
26 |
70 |
71 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/src/components/RouteDisplay.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
41 |
42 |
43 |
73 |
74 |
88 |
--------------------------------------------------------------------------------
/src/components/Routes.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
20 |
21 |
22 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/components/SaveProjectComponent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Save Project
7 |
8 |
9 |
10 |
11 |
12 |
14 |
15 |
16 |
17 |
21 | Save
22 |
23 | Close
24 |
25 |
26 |
27 |
28 |
29 |
30 |
82 |
83 |
96 |
--------------------------------------------------------------------------------
/src/components/TreeGraph.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
15 |
Return to Workspace
16 |
17 |
18 |
19 |
20 |
98 |
99 |
119 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 | import App from './App.vue';
3 | import router from './router';
4 | import store from './store';
5 |
6 | // vuetify imports
7 | import 'vuetify/styles';
8 | import { createVuetify } from 'vuetify';
9 | import * as components from 'vuetify/components';
10 | import * as directives from 'vuetify/directives';
11 |
12 | // vuetify config
13 | const vuetify = createVuetify({
14 | components,
15 | directives
16 | });
17 |
18 | // Creation and mounting of the app
19 | const app = createApp({
20 | extends: App,
21 | beforeCreate() {
22 | store.commit('initialiseStore');
23 | }
24 | });
25 |
26 | app.use(vuetify);
27 | app.use(router);
28 | app.use(store);
29 | app.mount('#app');
30 |
--------------------------------------------------------------------------------
/src/router.ts:
--------------------------------------------------------------------------------
1 | import * as VueRouter from 'vue-router';
2 | import Home from './views/HomeView.vue';
3 | import Splash from './views/SplashView.vue';
4 | // flagged due to linting configuration, remains functional
5 | const router = VueRouter.createRouter({
6 | history: VueRouter.createWebHistory(),
7 | base: import.meta.env.BASE_URL,
8 | routes: [
9 | {
10 | path: '/',
11 | name: 'login',
12 | component: Splash,
13 | meta: {
14 | hideNavbar: true
15 | }
16 | },
17 | {
18 | path: '/tree',
19 | name: 'tree',
20 | // route level code-splitting
21 | // this generates a separate chunk (about.[hash].js) for this route
22 | // which is lazy-loaded when the route is visited.
23 | component: () => import('./views/TreeView.vue')
24 | },
25 | {
26 | path: '/home',
27 | name: 'home',
28 | component: Home
29 | }
30 | ]
31 | });
32 |
33 | export default router;
--------------------------------------------------------------------------------
/src/store/actions.ts:
--------------------------------------------------------------------------------
1 | import * as types from './storeTypes';
2 | import { Actions } from '../types';
3 |
4 | const actions: Actions = {
5 | [types.incRerenderKey]: ({ commit }) => {
6 | commit(types.INC_RERENDER_KEY);
7 | },
8 | [types.updateProjectName]: ({ commit }, payload) => {
9 | commit(types.UPDATE_PROJECT_NAME, payload);
10 | },
11 | [types.setLogin]: ({ commit }, payload) => {
12 | commit(types.SET_LOGIN, payload);
13 | },
14 | [types.replaceState]: ({ commit }, payload) => {
15 | commit('replaceState', payload);
16 | },
17 | [types.nameProject]: ({ commit }, payload) => {
18 | commit(types.NAME_PROJECT, payload);
19 | },
20 | [types.initialiseStore]: ({ commit }) => {
21 | commit(types.INITIALISESTORE);
22 | },
23 |
24 | [types.registerComponent]: ({ state, commit }, payload) => {
25 | const { componentName } = payload;
26 |
27 | if (!state.componentMap[componentName]) {
28 | commit(types.ADD_COMPONENT_TO_COMPONENT_MAP, payload);
29 |
30 | commit(
31 | types.ADD_COMPONENT_TO_ACTIVE_ROUTE_CHILDREN,
32 | payload.componentName
33 | );
34 | commit(types.ADD_COMPONENT_TO_ACTIVE_ROUTE_IN_ROUTE_MAP, payload);
35 |
36 | const component = state.componentNameInputValue;
37 | const value = state.componentChildrenMultiselectValue.map(
38 | (component: string) => {
39 | return state.componentMap[component].componentName;
40 | }
41 | );
42 | commit(types.UPDATE_COMPONENT_CHILDREN_VALUE, { component, value });
43 | commit(types.UPDATE_COMPONENT_CHILDREN_MULTISELECT_VALUE, []);
44 | commit(types.UPDATE_COMPONENT_NAME_INPUT_VALUE, '');
45 | commit(types.SET_SELECTED_ELEMENT_LIST, []);
46 | }
47 | },
48 | [types.setSelectedElementList]: ({ commit }, payload) => {
49 | if (payload) {
50 |
51 | commit(types.SET_SELECTED_ELEMENT_LIST, payload);
52 | }
53 | },
54 | [types.addToSelectedElementList]: ({ commit }, payload) => {
55 | //console.log('action payload is', payload);
56 | commit(types.ADD_TO_SELECTED_ELEMENT_LIST, payload);
57 | },
58 | [types.addToComponentElementList]: ({ commit }, payload) => {
59 | commit(types.ADD_TO_COMPONENT_HTML_LIST, payload);
60 | },
61 | [types.setClickedElementList]: ({ commit }, payload) => {
62 | commit(types.SET_CLICKED_ELEMENT_LIST, payload);
63 | },
64 | [types.deleteActiveComponent]: ({ state, commit }) => {
65 | commit(types.DELETE_ACTIVE_COMPONENT);
66 | const activeRouteArray = [...state.routes[state.activeRoute]];
67 | const newActiveRouteArray = activeRouteArray.filter(componentData => {
68 | return state.activeComponent !== componentData.componentName;
69 | });
70 | commit(types.SET_ACTIVE_ROUTE_ARRAY, newActiveRouteArray);
71 | commit(types.SET_ACTIVE_COMPONENT, '');
72 | },
73 | [types.deleteSelectedElement]: ({ commit }, payload) => {
74 | //console.log('this is the payload', payload)
75 | commit(types.DELETE_SELECTED_ELEMENT, payload);
76 | },
77 | [types.setState]: ({ commit }, payload) => {
78 | commit(types.SET_STATE, payload);
79 | },
80 | [types.addProject]: ({ commit }, payload) => {
81 | commit(types.ADD_PROJECT, payload);
82 | },
83 | [types.deleteFromComponentHtmlList]: ({ commit }, payload) => {
84 | commit(types.DELETE_FROM_COMPONENT_HTML_LIST, payload);
85 | },
86 | [types.changeActiveTab]: ({ commit }, payload) => {
87 | commit(types.CHANGE_ACTIVE_TAB, payload);
88 | },
89 | [types.setComponentMap]: ({ commit }, payload) => {
90 | commit(types.SET_COMPONENT_MAP, payload);
91 | },
92 | [types.addRouteToRouteMap]: ({ state, commit }, payload) => {
93 | commit(types.ADD_ROUTE, payload);
94 | commit(types.SET_ACTIVE_ROUTE, payload);
95 | const route = state.activeRoute;
96 | const children: string[] = [];
97 | commit(types.ADD_ROUTE_TO_COMPONENT_MAP, { route, children });
98 | const component = 'App';
99 | const value = state.componentMap[state.activeRoute].componentName;
100 | commit(types.ADD_COMPONENT_TO_COMPONENT_CHILDREN, { component, value });
101 | },
102 | [types.setActiveRoute]: ({ commit }, payload) => {
103 | commit(types.SET_ACTIVE_ROUTE, payload);
104 | },
105 | [types.setActiveComponent]: ({ commit }, payload) => {
106 | commit(types.SET_ACTIVE_COMPONENT, payload);
107 | },
108 |
109 | [types.setActiveElement]: ({ commit }, payload) => { //new
110 | commit(types.SET_ACTIVE_ELEMENT, payload);
111 | },
112 | [types.deleteActiveElement]: ({ commit }) => {
113 | commit(types.DELETE_ACTIVE_ELEMENT)
114 | },
115 | [types.setComponentIndex]: ({ commit }, payload) => {
116 | commit(types.SET_COMPONENT_INDEX, payload)
117 | },
118 | [types.setElementIndex]: ({ commit }, payload) => {
119 | commit(types.SET_ELEMENT_INDEX, payload)
120 | },
121 | [types.saveState]: ({ commit }) => {
122 | commit(types.SAVE_STATE)
123 | },
124 | [types.restoreState]: ({ commit }) => {
125 | commit(types.RESTORE_STATE)
126 | },
127 |
128 | [types.setRoutes]: ({ commit }, payload) => {
129 | commit(types.SET_ROUTES, payload);
130 | },
131 | [types.deleteProjectTab]: ({ commit }, payload) => {
132 | commit(types.DELETE_PROJECT_TAB, payload);
133 | },
134 | [types.updateComponentChildrenMultiselectValue]: ({ commit }, payload) => {
135 | commit(types.UPDATE_COMPONENT_CHILDREN_MULTISELECT_VALUE, payload);
136 | },
137 | [types.updateActiveComponentChildrenValue]: ({ commit }, payload) => {
138 | console.log('payload', payload);
139 | commit(types.UPDATE_ACTIVE_COMPONENT_CHILDREN_VALUE, payload);
140 | },
141 | [types.updateComponentNameInputValue]: ({ commit }, payload) => {
142 | commit(types.UPDATE_COMPONENT_NAME_INPUT_VALUE, payload);
143 | },
144 | [types.updateOpenModal]: ({ commit }, payload) => {
145 | commit(types.UPDATE_OPEN_MODAL, payload);
146 | }
147 | };
148 |
149 | export default actions;
150 |
--------------------------------------------------------------------------------
/src/store/index.ts:
--------------------------------------------------------------------------------
1 | import { createStore } from 'vuex';
2 |
3 | import state from './state/stateIndex';
4 | import actions from './actions';
5 | import mutations from './mutations';
6 |
7 | const store = createStore({ state, mutations, actions });
8 |
9 | store.subscribe((mutation, state) => {
10 | localStorage.setItem('store', JSON.stringify(state));
11 | });
12 |
13 | export default store;
--------------------------------------------------------------------------------
/src/store/mutations.ts:
--------------------------------------------------------------------------------
1 | import * as types from './storeTypes';
2 | import { MutationTree } from 'vuex';
3 | import { State, Mutations, HtmlList, HtmlChild } from '../types';
4 |
5 | const mutations: Mutations = {
6 | initialiseStore(state: State) {
7 |
8 | if (localStorage.getItem('store')) {
9 | this.replaceState(
10 | Object.assign(
11 | state,
12 | JSON.parse(localStorage.getItem('store') || `${state}`)
13 | )
14 | );
15 | }
16 | },
17 | replaceState(state: State, payload) {
18 | this.replaceState(payload);
19 | },
20 | [types.INC_RERENDER_KEY]: (state: State) => {
21 | state.rerenderKey++;
22 | },
23 | [types.UPDATE_PROJECT_NAME]: (state: State, payload) => {
24 | state.editedProjectName = payload;
25 | },
26 | [types.SET_LOGIN]: (state: State, payload) => {
27 | state.loggedIn = payload;
28 | },
29 | [types.NAME_PROJECT]: (state: State, payload) => {
30 | state.projectName = payload;
31 | },
32 | [types.ADD_COMPONENT_TO_COMPONENT_MAP]: (state: State, payload) => {
33 | const { componentName, htmlList, children, isActive } = payload;
34 | state.componentMap = {
35 | ...state.componentMap,
36 | [componentName]: {
37 | componentName,
38 | x: 0,
39 | y: 0,
40 | w: 200,
41 | h: 200,
42 | children,
43 | htmlList,
44 | isActive
45 | }
46 | };
47 | },
48 | [types.ADD_TO_SELECTED_ELEMENT_LIST]: (state: State, payload) => {
49 | state.selectedElementList.push({
50 | text: payload,
51 | children: [],
52 | id: Date.now(),
53 | x: 20,
54 | y: 20,
55 | w: 100,
56 | h: 100,
57 | isActive: Boolean
58 | });
59 | },
60 | [types.SET_SELECTED_ELEMENT_LIST]: (state: State, payload) => {
61 | state.selectedElementList = payload;
62 | },
63 | [types.ADD_TO_COMPONENT_HTML_LIST]: (state: State, elementName) => { //and this
64 | const componentName: string = state.activeComponent;
65 |
66 | // state.componentMap[componentName].htmlList.push({
67 | // text: elementName,
68 | // children: [],
69 | // x: 20,
70 | // y: 20,
71 | // w: 100,
72 | // h: 100
73 | // })
74 |
75 | //find the active component and save the index
76 | const findIndex = function(obj){
77 | for(const num in obj){
78 | if(obj[num].componentName === componentName){
79 | return num
80 | }
81 | }
82 | }
83 | let index = findIndex(state.routes[state.activeRoute])
84 | console.log("index", index)
85 |
86 | //also adds to routes
87 | console.log("COMPONENT HTML LIST FUNCTION", state.routes[state.activeRoute])
88 | state.routes[state.activeRoute][index].htmlList.push({
89 | text: elementName,
90 | children: [],
91 | id: Date.now(),
92 | x: 20,
93 | y: 20,
94 | w: 100,
95 | h: 100,
96 | isActive: Boolean
97 | })
98 | },
99 |
100 | [types.DELETE_FROM_COMPONENT_HTML_LIST]: (state: State, id) => {
101 | const componentName = state.activeComponent;
102 | const htmlList = state.componentMap[componentName].htmlList;
103 |
104 | function parseAndDelete(htmlList: HtmlList) {
105 | htmlList.forEach((element, index) => {
106 | if (element.children.length > 0) {
107 | parseAndDelete(element.children);
108 | }
109 | if (id === element._id) {
110 | htmlList.splice(index, 1);
111 | }
112 | });
113 |
114 | const copied = htmlList.slice(0);
115 | state.componentMap[componentName].htmlList = copied;
116 | }
117 | parseAndDelete(htmlList);
118 | },
119 |
120 | [types.SET_CLICKED_ELEMENT_LIST]: (state: State, payload) => {
121 | const componentName = state.activeComponent;
122 | state.componentMap[componentName].htmlList = payload;
123 | },
124 |
125 | //
126 | [types.DELETE_ACTIVE_COMPONENT]: (state: State) => {
127 | const { routes, activeRoute, componentMap, activeComponent, arrayOfStates } = state;
128 |
129 | const newObj = Object.assign({}, componentMap);
130 |
131 | delete newObj[activeComponent];
132 |
133 | for (const compKey in newObj) {
134 | const children = newObj[compKey].children;
135 | children.forEach((child, index) => {
136 | if (activeComponent === child) children.splice(index, 1);
137 | });
138 | }
139 | state.componentMap = newObj;
140 | },
141 |
142 | // gets the component, traverses the component document and performs a splice on the element when it finds it
143 | [types.DELETE_ACTIVE_ELEMENT]: (state: State) => {//new
144 | let { routes, activeElement, activeRoute, componentIndex } = state;
145 | // routes, activeRoute, arrayOfStates
146 | const component = routes[activeRoute][componentIndex];
147 |
148 | let newList
149 | let oldIndex = []
150 |
151 | function findAndDelete(arr, id) {
152 | for (const [i, el] of arr.entries()) {
153 | console.log("EL", el)
154 | if (el.id === id) {
155 | newList = arr.slice(); // create a shallow copy
156 | newList.splice(i, 1); // delete the id'd element
157 | if(!oldIndex.length){
158 | component.htmlList = newList
159 | } else {
160 | if(oldIndex.length === 1){
161 | component.htmlList[oldIndex[0]].children = newList
162 | } else if (oldIndex.length === 2){
163 | component.htmlList[oldIndex[0]].children[oldIndex[1]].children = newList
164 | } else if (oldIndex.length === 3){
165 | component.htmlList[oldIndex[0]].children[oldIndex[1]].children[oldIndex[2]].children = newList
166 | } else if (oldIndex.length === 4){
167 | component.htmlList[oldIndex[0]].children[oldIndex[1]].children[oldIndex[2]].children[oldIndex[3]].children = newList
168 | } else if (oldIndex.length === 5){
169 | component.htmlList[oldIndex[0]].children[oldIndex[1]].children[oldIndex[2]].children[oldIndex[3]].children[oldIndex[4]].children = newList
170 | } else if (oldIndex.length === 6){
171 | component.htmlList[oldIndex[0]].children[oldIndex[1]].children[oldIndex[2]].children[oldIndex[3]].children[oldIndex[4]].children[oldIndex[5]].children = newList
172 | } else if (oldIndex.length === 7){
173 | component.htmlList[oldIndex[0]].children[oldIndex[1]].children[oldIndex[2]].children[oldIndex[3]].children[oldIndex[4]].children[oldIndex[5]].children[oldIndex[6]].children = newList
174 | }
175 | }
176 | } else if (el.children.length > 0) {
177 | console.log("CHILD")
178 | oldIndex.push(i)
179 | findAndDelete(el.children, id);
180 | }
181 | oldIndex = []
182 | }
183 | }
184 | findAndDelete(component.htmlList, activeElement.id);
185 | },
186 |
187 | [types.SET_ACTIVE_ELEMENT]: (state: State, payload) => {//new
188 | state.activeElement = payload;
189 | },
190 | [types.SET_COMPONENT_INDEX]: (state: State, payload) => {//new
191 | state.componentIndex = payload;
192 | },
193 | [types.SET_ELEMENT_INDEX]: (state: State, payload) => {//new
194 | state.elementIndex = payload;
195 | },
196 |
197 | // pushes new state to arrayOfStates
198 | [types.SAVE_STATE]: (state: State) => {//new
199 | const { routes, activeRoute, arrayOfStates } = state;
200 | const cloneOfActiveRoute = JSON.parse(JSON.stringify(routes[activeRoute]))
201 | state.arrayOfStates = [...state.arrayOfStates, cloneOfActiveRoute]
202 | if(arrayOfStates.length > 120){
203 | state.arrayOfStates = arrayOfStates.slice(20, arrayOfStates.length)
204 | }
205 | // console.log("pt 1", arrayOfStates)
206 | },
207 | // removing the top element of arrayOfStates, aka, the current state
208 | // assigning routes[activeRoute] to state we just removed
209 | [types.RESTORE_STATE]: (state: State) => {//new
210 | const { routes, activeRoute, arrayOfStates } = state;
211 | const prevRoute = arrayOfStates.pop();
212 | console.log('prevRoute',prevRoute)
213 | // console.log("pt 2", arrayOfStates[arrayOfStates.length - 1])
214 | routes[activeRoute] = prevRoute;
215 | console.log("pt 2", arrayOfStates)
216 | // console.log("pt 3", routes[activeRoute])
217 | },
218 |
219 | [types.SET_COMPONENT_MAP]: (state: State, payload) => {
220 | console.log(payload);
221 | state.componentMap = payload;
222 | },
223 | [types.DELETE_SELECTED_ELEMENT]: (state: State, payload) => {
224 | console.log(state.selectedElementList)
225 | state.selectedElementList.splice(payload, 1);
226 | },
227 | [types.SET_STATE]: (state: State, payload) => {
228 | Object.assign(state, payload);
229 | },
230 |
231 | [types.ADD_ROUTE]: (state: State, payload) => {
232 | state.routes = {
233 | ...state.routes,
234 | [payload]: []
235 | };
236 | },
237 | [types.ADD_ROUTE_TO_COMPONENT_MAP]: (state: State, payload) => {
238 | const { route, children } = payload;
239 | state.componentMap = {
240 | ...state.componentMap,
241 | [route]: {
242 | componentName: route,
243 | children
244 | }
245 | };
246 | },
247 | [types.SET_ACTIVE_ROUTE]: (state: State, payload) => {
248 | state.activeRoute = payload;
249 | },
250 | [types.ADD_COMPONENT_TO_ACTIVE_ROUTE_IN_ROUTE_MAP]: (
251 | state: State,
252 | payload
253 | ) => {
254 | state.routes[state.activeRoute].push(payload);
255 | },
256 | [types.SET_ACTIVE_COMPONENT]: (state: State, payload) => {
257 | state.activeComponent = payload;
258 | },
259 | [types.SET_ROUTES]: (state: State, payload) => {
260 | state.routes = Object.assign({}, payload);
261 | },
262 | [types.SET_ACTIVE_ROUTE_ARRAY]: (state: State, payload) => {
263 | state.routes[state.activeRoute] = payload;
264 | },
265 | [types.ADD_COMPONENT_TO_ACTIVE_ROUTE_CHILDREN]: (
266 | state: State,
267 | payload: string
268 | ) => {
269 | state.componentMap[state.activeRoute].children.push(payload);
270 | },
271 | [types.DELETE_PROJECT_TAB]: (state: State, payload) => {
272 | // delete project tab functionality yet to be implemented
273 | },
274 | [types.UPDATE_COMPONENT_CHILDREN_MULTISELECT_VALUE]: (
275 | state: State,
276 | payload
277 | ) => {
278 | console.log('payload', payload);
279 | state.componentChildrenMultiselectValue = payload;
280 | },
281 | [types.UPDATE_COMPONENT_CHILDREN_VALUE]: (state: State, payload) => {
282 | const { component, value } = payload;
283 | state.componentMap[component].children = value;
284 | },
285 | [types.UPDATE_ACTIVE_COMPONENT_CHILDREN_VALUE]: (state: State, payload) => {
286 | state.componentMap[state.activeComponent].children = payload;
287 | },
288 | [types.UPDATE_COMPONENT_NAME_INPUT_VALUE]: (state: State, payload) => {
289 | state.componentNameInputValue = payload;
290 | },
291 | [types.ADD_COMPONENT_TO_COMPONENT_CHILDREN]: (state: State, payload) => {
292 | const { component, value } = payload;
293 | state.componentMap[component].children.push(value);
294 | },
295 | [types.UPDATE_OPEN_MODAL]: (state: State, payload) => {
296 | state.modalOpen = payload;
297 | }
298 | };
299 |
300 | export default mutations;
301 |
--------------------------------------------------------------------------------
/src/store/state/htmlElementMap.ts:
--------------------------------------------------------------------------------
1 | // Map of html element opening and closing tags
2 |
3 | import { HtmlElementMap } from '../../types';
4 |
5 | const htmlElementMap: HtmlElementMap = {
6 | div: ['', '
'],
7 | button: ['', ' '],
8 | form: [''],
9 | img: [' ', ''],
10 | link: [' ', ''],
11 | 'list-item': ['', ' '],
12 | paragraph: ['', '
'],
13 | 'list-ol': ['', ' '],
14 | 'list-ul': [''],
15 | input: [' ', ''],
16 | navbar: ['', ' ']
17 | };
18 |
19 | export default htmlElementMap;
20 |
--------------------------------------------------------------------------------
/src/store/state/icons.ts:
--------------------------------------------------------------------------------
1 | // Font awesome icons for each html tag
2 |
3 | import { Icons } from '../../types';
4 |
5 | const icons: Icons = {
6 | div: 'far fa-square fa-lg',
7 | button: 'fas fa-toggle-off fa-lg',
8 | form: 'fab fa-wpforms fa-lg',
9 | img: 'far fa-image fa-lg',
10 | link: 'fas fa-link fa-lg',
11 | 'list-item': 'fas fa-circle fa-lg',
12 | paragraph: 'fas fa-paragraph fa-lg',
13 | 'list-ol': 'fas fa-list-ol fa-lg',
14 | 'list-ul': 'fas fa-list-ul fa-lg',
15 | input: 'fas fa-pen fa-lg',
16 | navbar: 'fas fa-window-maximize fa-lg'
17 | };
18 |
19 | export default icons;
20 |
--------------------------------------------------------------------------------
/src/store/state/stateIndex.ts:
--------------------------------------------------------------------------------
1 | // Vuex state, single source of truth
2 |
3 | import icons from './icons';
4 | import htmlElementMap from './htmlElementMap';
5 | import { State } from '../../types';
6 |
7 | const newState: State = {
8 | icons,
9 | htmlElementMap,
10 | componentMap: {
11 | App: {
12 | componentName: 'App',
13 | children: ['HomeView'],
14 | htmlList: []
15 | },
16 | HomeView: {
17 | componentName: 'HomeView',
18 | children: [],
19 | htmlList: []
20 | }
21 | },
22 | routes: {
23 | HomeView: []
24 | },
25 |
26 | componentNameInputValue: '',
27 | activeRoute: 'HomeView',
28 | activeComponent: '',
29 |
30 | activeElement: '',
31 | componentIndex: 0,
32 | elementIndex: 0,
33 |
34 | selectedElementList: [],
35 | projectName: 'Project-Name',
36 | editedProjectName: 'Project-Name',
37 | componentChildrenMultiselectValue: [],
38 | modalOpen: false,
39 | htmlElements: [],
40 | saved: false,
41 | loggedIn: false,
42 | rerenderKey: 0,
43 |
44 | arrayOfStates: [],
45 | };
46 |
47 | console.log('newState',newState)
48 |
49 | export default newState;
50 |
--------------------------------------------------------------------------------
/src/store/storeTypes.ts:
--------------------------------------------------------------------------------
1 | import { Type } from '../types';
2 |
3 | //Mutations
4 | export const INC_RERENDER_KEY: Type = 'INC_RERENDER_KEY';
5 | export const SET_LOGIN: Type = 'SET_LOGIN';
6 | export const REPLACE_STATE: Type = 'REPLACE_STATE';
7 | export const NAME_PROJECT: Type = 'NAME_PROJECT';
8 | export const INITIALISESTORE: Type = 'INITIALISESTORE';
9 | export const ADD_COMPONENT: Type = 'ADD_COMPONENT';
10 |
11 | export const UPDATE_PROJECT_NAME = 'UPDATE_PROJECT_NAME'; //new
12 | export const SET_ACTIVE_ELEMENT: Type = 'SET_ACTIVE_ELEMENT';//new
13 | export const DELETE_ACTIVE_ELEMENT: Type = 'DELETE_ACTIVE_ELEMENT'//new
14 | export const SET_ELEMENT_INDEX: Type = 'SET_ELEMENT_INDEX';//new
15 | export const SET_COMPONENT_INDEX: Type = 'SET_COMPONENT_INDEX';//new
16 | export const SAVE_STATE: Type = 'SAVE_STATE'; //new
17 | export const RESTORE_STATE: Type = 'RESTORE_STATE'; //new
18 |
19 | export const ADD_COMPONENT_TO_COMPONENT_MAP: Type =
20 | 'ADD_COMPONENT_TO_COMPONENT_MAP';
21 | export const SET_SELECTED_ELEMENT_LIST: Type = 'SET_SELECTED_ELEMENT_LIST';
22 | export const ADD_TO_SELECTED_ELEMENT_LIST: Type =
23 | 'ADD_TO_SELECTED_ELEMENT_LIST';
24 | export const ADD_TO_COMPONENT_HTML_LIST: Type = 'ADD_TO_COMPONENT_HTML_LIST';
25 | export const SET_CLICKED_ELEMENT_LIST: Type = 'SET_CLICKED_ELEMENT_LIST';
26 | export const DELETE_ACTIVE_COMPONENT: Type = 'DELETE_ACTIVE_COMPONENT';
27 | export const SET_COMPONENT_MAP: Type = 'SET_COMPONENT_MAP';
28 | export const DELETE_FROM_QUEUE: Type = 'DELETE_FROM_QUEUE';
29 | export const DELETE_SELECTED_ELEMENT: Type = 'DELETE_SELECTED_ELEMENT';
30 | export const SET_STATE: Type = 'SET_STATE';
31 | export const ADD_PROJECT: Type = 'ADD_PROJECT';
32 | export const DELETE_FROM_COMPONENT_HTML_LIST: Type =
33 | 'DELETE_FROM_COMPONENT_HTML_LIST';
34 | export const CHANGE_ACTIVE_TAB: Type = 'CHANGE_ACTIVE_TAB';
35 | export const ADD_COMPONENT_TO_ACTIVE_ROUTE_IN_ROUTE_MAP: Type =
36 | 'ADD_COMPONENT_TO_ACTIVE_ROUTE_IN_ROUTE_MAP';
37 | export const ADD_ROUTE: Type = 'ADD_ROUTE';
38 | export const SET_ACTIVE_COMPONENT: Type = 'SET_ACTIVE_COMPONENT';
39 | export const SET_ACTIVE_PROJECT: Type = 'SET_ACTIVE_PROJECT';
40 | export const SET_ACTIVE_ROUTE: Type = 'SET_ACTIVE_ROUTE';
41 | export const INCREMENT_PROJECT_ID: Type = 'INCREMENT_PROJECT_ID';
42 | export const SET_ROUTES: Type = 'SET_ROUTES';
43 | export const SET_COMPONENT_HTML_LIST: Type = 'SET_COMPONENT_HTML_LIST';
44 | export const SET_ACTIVE_ROUTE_ARRAY: Type = 'SET_ACTIVE_ROUTE_ARRAY';
45 | export const ADD_COMPONENT_TO_ACTIVE_ROUTE_CHILDREN: Type =
46 | 'ADD_COMPONENT_TO_ACTIVE_ROUTE_CHILDREN';
47 | export const ADD_ROUTE_TO_COMPONENT_MAP: Type = 'ADD_ROUTE_TO_COMPONENT_MAP';
48 | export const DELETE_PROJECT_TAB: Type = 'DELETE_PROJECT_TAB';
49 | export const UPDATE_COMPONENT_CHILDREN_MULTISELECT_VALUE: Type =
50 | 'UPDATE_COMPONENT_CHILDREN_MULTISELECT_VALUE';
51 | export const UPDATE_COMPONENT_NAME_INPUT_VALUE: Type =
52 | 'UPDATE_COMPONENT_NAME_INPUT_VALUE';
53 | export const UPDATE_COMPONENT_CHILDREN_VALUE: Type =
54 | 'UPDATE_COMPONENT_CHILDREN_VALUE';
55 | export const UPDATE_ACTIVE_COMPONENT_CHILDREN_VALUE: Type =
56 | 'UPDATE_ACTIVE_COMPONENT_CHILDREN_VALUE';
57 | export const ADD_COMPONENT_TO_COMPONENT_CHILDREN: Type =
58 | 'ADD_COMPONENT_TO_COMPONENT_CHILDREN';
59 | export const UPDATE_OPEN_MODAL: Type = 'UPDATE_OPEN_MODAL';
60 |
61 | //Actions
62 | export const registerComponent: Type = 'registerComponent';
63 | export const setSelectedElementList: Type = 'setSelectedElementList';
64 | export const addToSelectedElementList: Type = 'addToSelectedElementList';
65 | export const addToComponentElementList: Type = 'addToComponentElementList';
66 | export const setClickedElementList: Type = 'setClickedElementList';
67 | export const deleteActiveComponent: Type = 'deleteActiveComponent';
68 | export const setComponentMap: Type = 'setComponentMap';
69 | export const deleteFromQueue: Type = 'deleteFromQueue';
70 | export const deleteSelectedElement: Type = 'deleteSelectedElement';
71 | export const setState: Type = 'setState';
72 | export const addProject: Type = 'addProject';
73 | export const deleteFromComponentHtmlList: Type = 'deleteFromComponentHtmlList';
74 | export const changeActiveTab: Type = 'changeActiveTab';
75 | export const addRouteToRouteMap: Type = 'addRouteToRouteMap';
76 | export const setActiveComponent: Type = 'setActiveComponent';
77 |
78 | export const updateProjectName: Type = 'updateProjectName'; //new
79 | export const setActiveElement: Type = 'setActiveElement'; //new
80 | export const deleteActiveElement: Type = 'deleteActiveElement'; //new
81 | export const setComponentIndex: Type = 'setComponentIndex'; //new
82 | export const setElementIndex: Type = 'setElementIndex'; //new
83 | export const saveState: Type = 'saveState'; //new
84 | export const restoreState: Type = 'restoreState'; //new
85 |
86 |
87 | export const setActiveRoute: Type = 'setActiveRoute';
88 | export const incrementProjectId: Type = 'incrementProjectId';
89 | export const setRoutes: Type = 'setRoutes';
90 | export const setComponentHtmlList: Type = 'setComponentHtmlList';
91 | export const deleteProjectTab: Type = 'deleteProjectTab';
92 | export const updateComponentChildrenMultiselectValue: Type =
93 | 'updateComponentChildrenMultiselectValue';
94 | export const updateActiveComponentChildrenValue: Type =
95 | 'updateActiveComponentChildrenValue';
96 | export const updateComponentChildrenValue: Type =
97 | 'updateComponentChildrenValue';
98 | export const updateComponentNameInputValue: Type =
99 | 'updateComponentNameInputValue';
100 | export const updateOpenModal: Type = 'updateOpenModal';
101 | export const addElement: Type = 'addElement';
102 | export const initialiseStore: Type = 'initialiseStore';
103 | export const nameProject: Type = 'nameProject';
104 | export const replaceState: Type = 'replaceState';
105 | export const setLogin: Type = 'setLogin';
106 | export const incRerenderKey: Type = 'incRerenderKey';
107 |
108 | export const resetComponentChildrenMultiselectValue: Type =
109 | 'resetComponentChildrenMultiselectValue';
110 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | // TypeScript - type declaration file
2 |
3 | export type Icons = {
4 | div: string;
5 | button: string;
6 | form: string;
7 | img: string;
8 | link: string;
9 | 'list-item': string;
10 | paragraph: string;
11 | 'list-ol': string;
12 | 'list-ul': string;
13 | input: string;
14 | navbar: string;
15 | };
16 |
17 | export type HtmlElementMap = {
18 | div: string[];
19 | button: string[];
20 | form: string[];
21 | img: string[];
22 | link: string[];
23 | 'list-item': string[];
24 | paragraph: string[];
25 | 'list-ol': string[];
26 | 'list-ul': string[];
27 | input: string[];
28 | navbar: string[];
29 | };
30 |
31 | export type Component = {
32 | componentName: string;
33 | children: string[];
34 | htmlList: HtmlList;
35 | isActive?: boolean;
36 | x?: number;
37 | y?: number;
38 | h?: number;
39 | w?: number;
40 | id?: number;
41 | };
42 | export type ComponentMap = {
43 | [k: string]: Component;
44 | };
45 |
46 | export type Routes = {
47 | [k: string]: string[];
48 | };
49 |
50 | export type Project = {
51 | filename: string;
52 | lastSavedLocation: string;
53 | };
54 | export type Type = string;
55 |
56 | export type State = {
57 | icons: Icons;
58 | htmlElementMap: HtmlElementMap;
59 | componentMap: ComponentMap;
60 | routes: Routes;
61 | componentNameInputValue: string;
62 | activeRoute: string;
63 | activeComponent: string;
64 |
65 | activeElement: string; //new
66 | componentIndex: number; //new
67 | elementIndex: number; //new
68 |
69 | projectName: string;
70 | editedProjectName: string;
71 | selectedElementList: object[];
72 | componentChildrenMultiselectValue: string[];
73 | modalOpen: boolean;
74 | htmlElements: any[];
75 | saved: boolean;
76 | loggedIn: boolean;
77 | rerenderKey: number;
78 |
79 | arrayOfStates: Array;
80 | };
81 |
82 | // export type StateQueue = State[]
83 | export type Mutations = {
84 | [k: Type]: (
85 | state: State,
86 | payload?: any,
87 | elementName?: string,
88 | id?: number
89 | ) => void;
90 | };
91 | export type Actions = {
92 | [k: Type]: (context: any, payload?: any) => void;
93 | };
94 |
95 | export type HtmlChild = {
96 | text: string;
97 | children: HtmlChild[];
98 | _id?: number;
99 | x?: number;
100 | y?: number;
101 | w?: number;
102 | h?: number;
103 | };
104 |
105 | export type HtmlList = HtmlChild[];
106 |
--------------------------------------------------------------------------------
/src/views/HomeView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
14 |
15 |
16 |
17 |
23 |
24 |
25 |
26 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
76 |
77 |
84 |
--------------------------------------------------------------------------------
/src/views/SplashView.vue:
--------------------------------------------------------------------------------
1 |
2 |
217 |
218 |
219 |
279 |
280 |
--------------------------------------------------------------------------------
/src/views/TreeView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
17 |
23 |
--------------------------------------------------------------------------------
/tests/integration/supertest.spec.js:
--------------------------------------------------------------------------------
1 | const request = require('supertest');
2 | const mongoose = require('mongoose');
3 | let server;
4 | // const server = require('../../server/server');
5 | const accountRouter = require('../../server/routes/accountRouter');
6 | const projectRouter = require('../../server/routes/projectRouter');
7 | const authController = require('../../server/controllers/authController');
8 | const sinon = require('sinon');
9 |
10 | beforeAll(async () => {
11 | // connect to MongoDB before all tests
12 | await mongoose.connect(process.env.MONGO_URI, {
13 | useNewUrlParser: true,
14 | useUnifiedTopology: true
15 | });
16 | });
17 |
18 | afterAll(async () => {
19 | // disconnect from MongoDB after all tests
20 | await mongoose.disconnect();
21 | });
22 |
23 | let sandbox;
24 | let authenticateStub;
25 |
26 | beforeEach(() => {
27 | // create sinon sandbox to simplify working with fakes that need to be restored, eases cleanup
28 | sandbox = sinon.createSandbox();
29 |
30 | // create sinon stub (object) for the authenticate middleware
31 | authenticateStub = sandbox.stub(authController, 'authenticate');
32 |
33 | // in mock auth middleware, mock res.locals.username and res.locals.id to be the values from testAccount
34 | authenticateStub.callsFake((req, res, next) => {
35 | res.locals.username = testAccount.username;
36 | res.locals.id = testAccount.id;
37 | res.cookie('ssid', 'test token', { httpOnly: true });
38 | next();
39 | });
40 |
41 | // use the mock middleware by replacing the original middleware in the middleware chain
42 | // server.use(authController.authenticate);
43 |
44 | // create server/app AFTER stubbing authenticate middleware (so stub is made before a reference to authController.authenticate is set up when creating app)
45 | server = require('../../server/server');
46 | });
47 |
48 | afterEach(() => {
49 | // restore original middleware function after each test
50 | sandbox.restore();
51 | });
52 |
53 | describe('/projects projectRouter', () => {
54 | // make a testAccount
55 | const testAccount = {
56 | username: 'TestProjectOwner',
57 | id: '12345678',
58 | project_ids: ['1']
59 | };
60 |
61 | // make a testProject
62 | const testProject = {
63 | project_name: 'Test Project',
64 | projectObject: { state: 'state' },
65 | projectOwner: 'TestProjectOwner'
66 | };
67 |
68 | describe('find a project, GET to /find', () => {
69 | it('responds with 200 status', () => {
70 | return request(server)
71 | .get('/projects/find')
72 | .expect(200);
73 | });
74 |
75 | it('should retrieve all projects from the database', () => {});
76 | });
77 |
78 | describe('saving a project, POST to /saveProject', () => {
79 | it('responds with 201 status', () => {
80 | return request(server)
81 | .post('/projects/saveProject')
82 | .send(testProject) // sends the testProject in req.body
83 | .expect(201);
84 | });
85 | });
86 | });
87 |
--------------------------------------------------------------------------------
/tests/unit/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | jest: true
4 | }
5 | }
--------------------------------------------------------------------------------
/tests/unit/App.spec.js:
--------------------------------------------------------------------------------
1 | import { shallowMount, mount } from '@vue/test-utils';
2 | import { describe, it, expect } from 'vitest';
3 | import { createRouter, createWebHistory } from 'vue-router';
4 | import { createStore } from 'vuex';
5 | import App from '@/App.vue';
6 | import NavBar from '@/components/NavBar.vue';
7 |
8 | describe('App.vue', () => {
9 | const router = createRouter({
10 | history: createWebHistory(),
11 | routes: [
12 | {
13 | path: '/',
14 | component: App,
15 | meta: {
16 | hideNavbar: true
17 | }
18 | }
19 | ]
20 | });
21 |
22 | const store = createStore({
23 | state() {
24 | return {};
25 | }
26 | });
27 |
28 | it('contains v-main element that wraps router-view', () => {
29 | const wrapper = shallowMount(App, {
30 | global: {
31 | plugins: [router, store]
32 | }
33 | });
34 | expect(wrapper.find('v-main')).toBeTruthy();
35 | expect(wrapper.find('v-main > router-view')).toBeTruthy();
36 | });
37 | });
38 |
39 | describe('App.vue', () => {
40 | // create store with fake properties
41 | const store = createStore({
42 | state() {
43 | return {
44 | store1: '',
45 | store2: '',
46 | componentNameInputValue: ''
47 | };
48 | }
49 | });
50 |
51 | const router = createRouter({
52 | history: createWebHistory(),
53 | routes: [
54 | {
55 | path: '/',
56 | component: App,
57 | meta: {
58 | hideNavbar: true
59 | }
60 | }
61 | ]
62 | });
63 | it('should renders elements found in children', () => {
64 | const wrapper = mount(App, {
65 | components: {
66 | NavBar
67 | },
68 | global: {
69 | plugins: [store, router]
70 | }
71 | });
72 | expect(wrapper.find('h1').exists()).toBeTruthy();
73 | });
74 |
75 | it('should renders elements found in children', () => {
76 | const wrapper = mount(App, {
77 | components: {
78 | NavBar
79 | },
80 | global: {
81 | plugins: [store, router]
82 | }
83 | });
84 | expect(wrapper.findComponent({ name: 'NavBar' }).exists()).toBeTruthy();
85 | });
86 | });
87 |
--------------------------------------------------------------------------------
/tests/unit/ChildrenMultiselect.spec.js:
--------------------------------------------------------------------------------
1 | import { shallowMount, mount, createVuexStore } from '@vue/test-utils';
2 | import { createStore } from 'vuex';
3 | import { describe, it, expect, vi } from 'vitest';
4 | import ChildrenMultiselect from '@/components/ChildrenMultiselect.vue';
5 | import VueMultiselect from 'vue-multiselect';
6 |
7 | describe('ChildrenMultiselect', () => {
8 | const store = createStore({
9 | state() {
10 | return {
11 | routes: {},
12 | componentMap: {}
13 | };
14 | }
15 | });
16 | it('properly renders div container', () => {
17 | const wrapper = mount(ChildrenMultiselect, {
18 | global: { plugins: [store] }
19 | });
20 | expect(wrapper.find('div').exists()).toBeTruthy();
21 | });
22 |
23 | it('properly renders children component', () => {
24 | const wrapper = mount(ChildrenMultiselect, {
25 | components: { VueMultiselect },
26 | global: { plugins: [store] }
27 | });
28 | expect(
29 | wrapper.findComponent({ name: 'VueMultiselect' }).exists()
30 | ).toBeTruthy();
31 | });
32 | });
33 |
--------------------------------------------------------------------------------
/tests/unit/Component.spec.js:
--------------------------------------------------------------------------------
1 | import { shallowMount, mount, createVuexStore } from '@vue/test-utils';
2 | import { createStore } from 'vuex';
3 | import { describe, it, expect, vi } from 'vitest';
4 | import Vue3DraggableResizable from 'vue3-draggable-resizable';
5 | import Component from '@/components/Component.vue';
6 |
7 | describe('Component.vue', () => {
8 | it('properly renders the html element of children', () => {
9 | const wrapper = mount(Component);
10 | expect(wrapper.find('h3').exists()).toBeTruthy();
11 | expect(wrapper.find('h1').exists()).toBeFalsy();
12 | expect(wrapper.find('div').exists()).toBeTruthy();
13 | expect(wrapper.find('h5').exists()).toBeFalsy();
14 | });
15 | });
16 |
17 | describe('Component.vue', () => {
18 | it('properly imports Vue3Draggable and renders the component', () => {
19 | const store = createStore({
20 | state() {
21 | return {
22 | store1: '',
23 | store2: ''
24 | };
25 | }
26 | });
27 |
28 | const wrapper = mount(Component, {
29 | components: {
30 | Vue3DraggableResizable
31 | },
32 | global: {
33 | plugins: [store]
34 | }
35 | });
36 | expect(
37 | wrapper.findComponent({ name: 'Vue3DraggableResizable' }).exists()
38 | ).toBeTruthy();
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/tests/unit/ComponentDisplay.spec.js:
--------------------------------------------------------------------------------
1 | import { shallowMount, mount } from '@vue/test-utils';
2 | import { createStore } from 'vuex';
3 | import { describe, it, expect } from 'vitest';
4 | import ComponentDisplay from '@/components/ComponentDisplay.vue';
5 | import Vue3DraggableResizable from 'vue3-draggable-resizable';
6 |
7 | describe('ComponentDisplay', () => {
8 | const store = createStore({
9 | state() {
10 | return {
11 | activeComponent: 'active',
12 | activeRoute: 'test',
13 | routes: {
14 | test: ["test"],
15 | },
16 | componentMap: {
17 | active: {
18 | children: [],
19 | },
20 | },
21 | };
22 | },
23 | });
24 | it('properly renders HTML element', () => {
25 | const wrapper = shallowMount(ComponentDisplay, {
26 | global: { plugins: [store] },
27 | });
28 |
29 | expect(wrapper.find('div').exists()).toBeTruthy();
30 | });
31 |
32 | it('properly renders children component', () => {
33 | const wrapper = mount(ComponentDisplay, {
34 | components: { Vue3DraggableResizable },
35 | global: { plugins: [store] },
36 | });
37 | expect(
38 | wrapper.findComponent({ name: 'Vue3DraggableResizable' }).exists()
39 | ).toBeTruthy();
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/tests/unit/HomeQueue.spec.js:
--------------------------------------------------------------------------------
1 | import { shallowMount, mount } from '@vue/test-utils';
2 | import { createStore } from 'vuex';
3 | import { describe, it, expect } from 'vitest';
4 | import HomeQueue from '@/components/HomeQueue.vue';
5 | import draggable from 'vuedraggable';
6 |
7 | describe('HomeQueue', () => {
8 | const store = createStore({
9 | state() {
10 | return {};
11 | },
12 | });
13 | it('properly renders children component', () => {
14 | const wrapper = mount(HomeQueue, {
15 | components: { draggable },
16 | global: { plugins: [store] },
17 | });
18 | expect(wrapper.findComponent({ name: 'draggable' }).exists()).toBeTruthy();
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/tests/unit/HomeSidebar.spec.js:
--------------------------------------------------------------------------------
1 | import { shallowMount, mount, createVuexStore } from '@vue/test-utils';
2 | import { createStore } from 'vuex';
3 | import HomeSidebar from '@/components/HomeSidebar.vue';
4 | import Icons from '@/components/Icons.vue';
5 | import ChildrenMultiselect from '@/components/ChildrenMultiselect.vue';
6 |
7 | describe('HomeSidebar.vue', () => {
8 | const store = createStore({
9 | state() {
10 | return {
11 | store1: '',
12 | store2: '',
13 | componentNameInputValue: ''
14 | };
15 | }
16 | });
17 | it('properly renders elements and checks to see proper wrapping', () => {
18 | const wrapper = shallowMount(HomeSidebar, {
19 | components: {
20 | ChildrenMultiselect,
21 | Icons
22 | },
23 | global: {
24 | plugins: [store]
25 | }
26 | });
27 | expect(wrapper.find('v-card')).toBeTruthy();
28 | expect(wrapper.find('v-card-actions > router-view')).toBeTruthy();
29 | });
30 |
31 | it('renders the ChildrenMultiselect component', () => {
32 | const wrapper = shallowMount(HomeSidebar, {
33 | components: {
34 | ChildrenMultiselect,
35 | Icons
36 | },
37 | global: {
38 | plugins: [store]
39 | }
40 | });
41 | expect(
42 | wrapper.findComponent({ name: 'ChildrenMultiselect' }).exists()
43 | ).toBeTruthy();
44 | });
45 | });
46 |
--------------------------------------------------------------------------------
/tests/unit/Icons.spec.js:
--------------------------------------------------------------------------------
1 | import { shallowMount } from '@vue/test-utils';
2 | import { createStore } from 'vuex';
3 | import { describe, it, expect, vi } from 'vitest';
4 | import Icons from '@/components/Icons.vue';
5 |
6 | describe('Icons', () => {
7 | const store = createStore({
8 | state() {
9 | return {
10 | icons: { elementName: 'previousElement' }
11 | };
12 | }
13 | });
14 |
15 | it('after button, elementName is changed to newElement', () => {
16 | const wrapper = shallowMount(Icons, {
17 | global: { plugins: [store] }
18 | });
19 |
20 | // wrapper.vm is Vue instance of mounted component, containing changeState function
21 | // spy will be object of changeState's metadata
22 | const spy = vi.spyOn(wrapper.vm, 'changeState');
23 | const button = wrapper.find('button');
24 | button.trigger('click');
25 |
26 | expect(spy.called).toBeTruthy();
27 | expect(spy.callCount).toEqual(1);
28 |
29 | button.trigger('click');
30 | expect(spy.callCount).toEqual(2);
31 |
32 | vi.restoreAllMocks();
33 |
34 | expect(spy.callCount).toEqual(0);
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/tests/unit/RouteDisplay.spec.js:
--------------------------------------------------------------------------------
1 | import { shallowMount, mount } from '@vue/test-utils';
2 | import { createStore } from 'vuex';
3 | import { describe, it, expect } from 'vitest';
4 | import RouteDisplay from '@/components/RouteDisplay.vue';
5 | import Routes from '@/components/Routes.vue';
6 |
7 | describe('RouteDisplay', () => {
8 | const store = createStore({
9 | state() {
10 | return {
11 | routes: {
12 | test: ["test"],
13 | },
14 | };
15 | },
16 | });
17 | it('properly renders HTML elements', () => {
18 | const wrapper = shallowMount(RouteDisplay, {
19 | global: { plugins: [store] },
20 | });
21 |
22 | expect(wrapper.find('strong').exists()).toBeTruthy();
23 | });
24 |
25 | it('properly renders children component', () => {
26 | const wrapper = mount(RouteDisplay, {
27 | components: { Routes },
28 | global: { plugins: [store] },
29 | });
30 | expect(
31 | wrapper.findComponent({ name: 'Routes' }).exists()
32 | ).toBeTruthy();
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/tests/unit/TreeGraph.spec.js:
--------------------------------------------------------------------------------
1 | import { shallowMount, mount } from '@vue/test-utils';
2 | import { createStore } from 'vuex';
3 | import { describe, it, expect } from 'vitest';
4 | import TreeGraph from '@/components/TreeGraph.vue';
5 | import VueTree from '@ssthouse/vue3-tree-chart';
6 |
7 | describe('TreeGraph', () => {
8 | const store = createStore({
9 | state() {
10 | return {
11 | componentMap: {},
12 | };
13 | },
14 | });
15 | it('properly renders HTML element', () => {
16 | const wrapper = shallowMount(TreeGraph, {
17 | global: { plugins: [store] },
18 | });
19 |
20 | expect(wrapper.find('div').exists()).toBeTruthy();
21 | });
22 |
23 | it('properly renders children component', () => {
24 | const wrapper = mount(TreeGraph, {
25 | components: { VueTree },
26 | global: { plugins: [store] },
27 | });
28 | expect(wrapper.findComponent({ name: 'VueTree' }).exists()).toBeTruthy();
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/tests/unit/__snapshots__/App.spec.js.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2 |
3 | exports[`App.vue > renders App correctly 1`] = `
4 |
7 |
8 |
11 |
12 |
16 |
19 |
20 |
21 |
22 |
23 |
32 |
33 |
34 | `;
35 |
36 | exports[`App.vue > renders App correctly 2`] = `
37 |
40 |
41 |
44 |
45 |
49 |
52 |
53 |
54 |
55 |
56 |
65 |
66 |
67 | `;
68 |
69 | exports[`App.vue > renders html correct 1`] = `
70 |
73 |
74 |
77 |
78 |
82 |
85 |
86 |
87 |
88 |
89 |
98 |
99 |
100 | `;
101 |
102 | exports[`App.vue > renders html correctly 1`] = `
103 |
106 |
107 |
110 |
111 |
115 |
118 |
119 |
120 |
121 |
122 |
131 |
132 |
133 | `;
134 |
135 | exports[`App.vue renders correct 1`] = `
136 |
140 |
141 |
142 |
147 |
148 | `;
149 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "moduleResolution": "Node",
7 | "jsx": "preserve",
8 | "resolveJsonModule": true,
9 | "isolatedModules": true,
10 | "esModuleInterop": true,
11 | "lib": ["ESNext", "DOM"],
12 | "skipLibCheck": true,
13 | "allowJs": true
14 | },
15 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
16 | "references": [{ "path": "./tsconfig.node.json" }]
17 | }
18 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { defineConfig } from 'vite';
3 | import vue from '@vitejs/plugin-vue';
4 | import checker from 'vite-plugin-checker';
5 | const path = require('path');
6 | export default defineConfig({
7 | plugins: [vue()],
8 | test: {
9 | globals: true,
10 | environment: 'jsdom'
11 | },
12 | resolve: {
13 | alias: {
14 | '@': path.resolve(__dirname, './src')
15 | }
16 | },
17 | server: {
18 | host: '0.0.0.0',
19 | port: 4173,
20 | proxy: {
21 | '/users': {
22 | target: 'http://localhost:8080',
23 | changeOrigin: true,
24 | secure: false
25 | }
26 | }
27 | }
28 | });
29 |
--------------------------------------------------------------------------------