├── .DS_Store ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── bash_profile.save ├── deno.json ├── deps.tsx ├── dist └── run │ └── static │ └── js │ └── main.js ├── import_map.json ├── index.html ├── index.tsx ├── server ├── controllers │ ├── accountController.ts │ ├── authorize.ts │ └── projectController.ts ├── createDb.js ├── routes │ ├── accountRoutes.ts │ └── projectRoutes.ts ├── server.ts └── utils │ ├── apiKey.ts │ ├── db.ts │ └── dbNotes.txt ├── src ├── .DS_Store ├── App.tsx ├── Buttons.tsx ├── Customization.tsx ├── Login.tsx ├── Popup2.tsx ├── SideBar.tsx ├── Signup.tsx ├── popup.tsx ├── preview │ ├── MainContainer.tsx │ ├── Navbar.tsx │ ├── Preview.tsx │ └── pages │ │ ├── CodePreview.tsx │ │ └── IslandPreview.tsx └── utils │ ├── elementsList.js │ └── types.ts ├── static ├── css │ ├── App.css │ ├── buttons.css │ ├── codePreview.css │ ├── customizationStyles.css │ ├── index.css │ ├── islandPreview.css │ ├── login.css │ ├── navBarStyling.css │ ├── popup.css │ ├── sideBarStyle.css │ ├── signup.css │ └── styling.css ├── favicon3.ico ├── images │ ├── Finallogo.png │ ├── favicon2.ico │ ├── logo.svg │ └── sq.png └── static │ └── images │ └── Finallogo.pn └── tsconfig.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Squeezed/d2a7edd17667a2e7f733d97baf5adbba8bc6350a/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dotenv environment variables file 2 | .env 3 | 4 | # vs code file 5 | .vscode -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Squeezed/d2a7edd17667a2e7f733d97baf5adbba8bc6350a/Dockerfile -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 OSLabs Beta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![squeezed](https://user-images.githubusercontent.com/29562751/182937867-42d393fc-654d-4f30-895e-dbd5255de30a.png) 2 | 3 | # Squeezed 4 | A prototyping tool for the Freshest Deno framework 5 | 6 | Squeezed is a charming web application prototyping tool for rendering the islands of your Fresh app. 7 | Squeezed uses a drag-and-drop canvas that enables users to see how their codebase is laid out, see the island being rendered dynamically, and edit HTML elements. 8 | HTML elements can be readily modified, and the code preview will render live updates as you modify your code. 9 | # Let's Get Started! 10 | First, make sure to download deno on your local machine. Squeezed uses deno v1.24.1. For more information and instructions, head to: https://deno.land/ 11 | 12 | After installing Deno, Linux users will have to update their bashrc file to gain access to Deno commands globally. Mac users will have to run these commands in every new instance of their terminal: 13 | 14 | `export DENO_INSTALL="/$HOME/.deno"` 15 | 16 | `export PATH="$DENO_INSTALL/bin:$PATH"` 17 | 18 | 19 | Next, run this command in your terminal: 20 | 21 | `deno install -A --unstable -n deno-create-react-app https://deno.land/x/create_react_app/mod.ts` 22 | 23 | For more information on Create React App, visit: https://deno.land/x/create_react_app@v0.1.2 24 | 25 | 26 | Fork and clone this repo, and create a .env file with the following: 27 | 28 | `POSTGRES_URI = “”` 29 | 30 | Then run the following command to create your database tables: 31 | 32 | `deno run --allow-env --allow-read --allow-net server/createDb.js` 33 | 34 | Lastly, run this command inside your terminal, and you’re done! 35 | 36 | `deno-create-react-app run & deno run --allow-env --allow-read --allow-net server/server.ts` 37 | 38 | 39 | Squeezed should be running on your localhost:8000 with full functionality. 40 | Make sure your environment is set up by setting up a working database as well as running the proper paths to enable Deno commands globally. 41 | 42 | # How to Use? 43 | 44 | You can create an account by entering a new username, password, and email, or sign in to an existing account in your database. 45 | 46 | From there, you can load new projects that are stored in your database or start working on a fresh project right away. When you save progress on a new project, you will be prompted to input a project name. 47 | If you try to save progress while working on an existing project, the database will just update your previous work instead of asking for a new project name. 48 | 49 | Clear project will only clear the HTML elements that your project currently uses but will keep you in the same project that you have loaded. 50 | Starting a new project will take you out of your current project and into a new one as well as clear all HTML elements. 51 | Deleting a project will clear your elements and delete the project inside your database. 52 | 53 | To add HTML elements to your project, just drag and drop from the list on the right to the main area. 54 | You can now select elements by clicking on each one in the list as well as rearrange them by dragging them and dropping them in the desired position. 55 | To add styling attributes to your HTML elements, select the intended element, input the value to the corresponding attribute in the styling area, and click submit. 56 | 57 | You should see your HTML elements update inside the code preview! 58 | You can also see what that code will render by clicking on island preview at the top right. 59 | To use the code that is produced, just click on the clipboard at the bottom of the code preview window and paste the code wherever it is needed! 60 | 61 | # Future Tasks: 62 | - Adding drag and drop nesting functionality for html elements. 63 | - Squeezed currently relies on a third-party library and requires a local download to ensure full functionality. Future plans including updating the codebase to ensure compatibility when deploying. 64 | - We hope to migrate from a browser application to a desktop application once a tool that supports Deno browser applications in javascript is released. 65 | 66 | If you’d like to contribute to Squeezed or have any feedback, please let us know! 67 | 68 | Check us out at our [Website](https://www.squeezed.dev/) and our [Linkedin](https://www.linkedin.com/company/squeezed-dev)! 69 | 70 | # Tech Stack: 71 | Deno, TypeScript, Javascript, PostgreSQL, oak, dex, bcrypt, React (Hooks and Router) 72 | 73 | # License 74 | Distributed under the MIT License. See [LICENSE](https://github.com/oslabs-beta/squeezed/blob/master/LICENSE) for more information. 75 | 76 | # The Engineers 77 | Alexa Roberts: [GitHub](https://github.com/alexarobertss) | [Linkedin](https://www.linkedin.com/in/alexarobertss/) 78 | 79 | Carol Xia: [GitHub](https://github.com/carol-xia) | [Linkedin](https://www.linkedin.com/in/carol-xia-2bb508134/) 80 | 81 | James Bui: [GitHub](https://github.com/James-Minh-Bui) | [Linkedin](https://www.linkedin.com/in/jamesminhbui/) 82 | 83 | Wayne Tsai: [GitHub](https://github.com/WayneTsai45) | [Linkedin](https://www.linkedin.com/in/wayne-tsai45/) 84 | -------------------------------------------------------------------------------- /bash_profile.save: -------------------------------------------------------------------------------- 1 | export DENO_INSTALL="/$HOME/.deno" 2 | export PATH="$DENO_INSTALL/bin:$PATH" 3 | 4 | -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "start": "deno run -A --watch=static/,routes/ dev.ts", 4 | "dev": "deno-create-react-app run & denon run --allow-net --allow-env --allow-read ./server/server.ts" 5 | }, 6 | "compilerOptions": { 7 | "allowJs": true, 8 | "lib": ["deno.window", "es6", "dom"], 9 | "strict": true, 10 | "jsx": "react-jsx", 11 | "jsxImportSource": "https://esm.sh/react" 12 | }, 13 | "importMap": "./import_map.json", 14 | "lint": { 15 | "files": { 16 | "include": ["src/"], 17 | "exclude": ["src/testdata/"] 18 | }, 19 | "rules": { 20 | "tags": ["recommended"], 21 | "include": ["ban-untagged-todo"], 22 | "exclude": ["no-unused-vars"] 23 | } 24 | }, 25 | "fmt": { 26 | "files": { 27 | "include": ["src/"], 28 | "exclude": ["src/testdata/"] 29 | }, 30 | "options": { 31 | "useTabs": true, 32 | "lineWidth": 80, 33 | "indentWidth": 4, 34 | "singleQuote": true, 35 | "proseWrap": "preserve" 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /deps.tsx: -------------------------------------------------------------------------------- 1 | //React 2 | import * as React from "https://esm.sh/react@18.2.0"; 3 | import { 4 | BrowserRouter, 5 | HashRouter, 6 | Routes, 7 | Route, 8 | useNavigate, 9 | Link 10 | } from "https://esm.sh/react-router-dom@6.3.0"; 11 | 12 | export * as serve from "https://deno.land/std@0.140.0/http/server.ts"; 13 | 14 | export * as ReactDOM from "https://esm.sh/react-dom@18.2.0"; 15 | export { React, BrowserRouter, HashRouter, Routes, Route, Link, useNavigate }; 16 | -------------------------------------------------------------------------------- /import_map.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "$fresh/": "https://deno.land/x/fresh@1.0.1/", 4 | "preact": "https://esm.sh/preact@10.8.2", 5 | "preact/": "https://esm.sh/preact@10.8.2/", 6 | "preact-render-to-string": "https://esm.sh/preact-render-to-string@5.2.0?deps=preact@10.8.2", 7 | "@twind": "./utils/twind.ts", 8 | "twind": "https://esm.sh/twind@0.16.17", 9 | "twind/": "https://esm.sh/twind@0.16.17/", 10 | "oak": "https://deno.land/x/oak/mod.ts", 11 | "~/": "./", 12 | "std/": "https://deno.land/std@0.145.0/", 13 | "@unocss/": "https://deno.land/x/aleph@1.0.0-alpha.88/lib/@unocss/", 14 | "aleph/": "https://deno.land/x/aleph@1.0.0-alpha.88/", 15 | "aleph/server": "https://deno.land/x/aleph@1.0.0-alpha.88/server/mod.ts", 16 | "aleph/dev": "https://deno.land/x/aleph@1.0.0-alpha.88/server/dev.ts", 17 | "aleph/react": "https://deno.land/x/aleph@1.0.0-alpha.88/framework/react/mod.ts", 18 | "aleph/react-client": "https://deno.land/x/aleph@1.0.0-alpha.88/framework/react/client.ts", 19 | "aleph/react-ssr": "https://deno.land/x/aleph@1.0.0-alpha.88/framework/react/ssr.ts", 20 | "react": "https://esm.sh/react@18.2.0", 21 | "react-dom": "https://esm.sh/react-dom@18.2.0", 22 | "react-dom/": "https://esm.sh/react-dom@18.2.0/", 23 | "postgres": "https://deno.land/x/postgres@v0.16.1/mod.ts" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Squeezed 11 | 12 | 13 | 14 |
15 | 16 | 20 | 21 | -------------------------------------------------------------------------------- /index.tsx: -------------------------------------------------------------------------------- 1 | import { React, ReactDOM } from './deps.tsx'; 2 | import { HashRouter, Routes, Route} from './deps.tsx'; 3 | import App from './src/App.tsx'; 4 | import Login from './src/Login.tsx'; 5 | import Signup from './src/Signup.tsx'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | 11 | } /> 12 | } /> 13 | } /> 14 | 15 | 16 | , 17 | document.getElementById('root') 18 | ); 19 | -------------------------------------------------------------------------------- /server/controllers/accountController.ts: -------------------------------------------------------------------------------- 1 | import db from '../utils/db.ts'; 2 | import Dex from "https://deno.land/x/dex/mod.ts"; 3 | import * as bcrypt from "https://deno.land/x/bcrypt/mod.ts"; 4 | import { key } from "../../server/utils/apiKey.ts"; 5 | import { create } from "https://deno.land/x/djwt@v2.7/mod.ts"; 6 | const dex = Dex({client: 'postgres'}); 7 | const salt = await bcrypt.genSalt(10); 8 | 9 | const accountController: any = {}; 10 | 11 | //Creates a new account with username,password,email inputs and stores them in the database. 12 | accountController.createAccount = async(ctx:any) => { 13 | try{ 14 | const {username, password, email} = await ctx.request.body().value; 15 | const newPass = await bcrypt.hash(password,salt); 16 | const user = dex("users").insert({username: username, password: newPass, email: email}).returning(['id','username', 'password','email']).toString(); 17 | const data = await db.queryObject(user); 18 | ctx.response.body = data.rows[0]; 19 | ctx.response.status = 200; 20 | return; 21 | } 22 | catch (err) { 23 | ctx.response.body = { status: false, data: null}; 24 | ctx.response.status = 500; 25 | console.log(err); 26 | } 27 | } 28 | 29 | // Confirms that input password matches with the bcrypt password in database 30 | accountController.loginCheck = async(ctx: any, next: any) => { 31 | try{ 32 | const {username, password} = await ctx.request.body().value; 33 | const user = dex.select().from("users").where({ username: username }).toString(); 34 | const data = await db.queryObject(user); 35 | const confirmPassword = await bcrypt.compare(password, data.rows[0].password); 36 | if(!user || !confirmPassword){ 37 | ctx.response.body = 404; 38 | ctx.response.body = {message: "Invalid login credentials"}; 39 | return; 40 | } 41 | //authenticate a user 42 | const payload = { 43 | id: user.id, 44 | username: username 45 | }; 46 | const jwt = await create({ alg: "HS512", typ: "JWT" }, { payload }, key); 47 | 48 | // sends userId, jwt, and true/false in response body 49 | if(jwt) { 50 | ctx.response.status = 200; 51 | ctx.response.headers.set("Content-Type", "application/json"); 52 | ctx.response.body = JSON.stringify({ 53 | userId: data.rows[0].id, 54 | token: jwt, 55 | result: confirmPassword 56 | }) 57 | } else { 58 | ctx.response.status = 500; 59 | ctx.response.body = { 60 | message: "internal server error" 61 | } 62 | } 63 | return; 64 | } 65 | catch (err) { 66 | ctx.response.body = { status: false, data: null}; 67 | ctx.response.status = 500; 68 | return err; 69 | } 70 | 71 | } 72 | 73 | export default accountController; -------------------------------------------------------------------------------- /server/controllers/authorize.ts: -------------------------------------------------------------------------------- 1 | import { verify } from "https://deno.land/x/djwt@v2.4/mod.ts"; 2 | import { key } from "../../server/utils/apiKey.ts"; 3 | import { Context } from "https://deno.land/x/oak/mod.ts"; 4 | 5 | //checks user credentials to ensure authorization for request 6 | export const authorize = async (ctx: Context, next:any) => { 7 | try { 8 | const {authorization} = await ctx.request.body().value; 9 | if(!authorization) { 10 | ctx.response.status = 401; 11 | return; 12 | } 13 | 14 | const payload = await verify(authorization, key); 15 | if(!payload){ 16 | throw new Error("!payload") 17 | } 18 | await next(); 19 | } 20 | catch(error) { 21 | ctx.response.status = 401; 22 | ctx.response.body ={message: "You are not authorized to access this route"} 23 | return; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /server/controllers/projectController.ts: -------------------------------------------------------------------------------- 1 | //deno-create-react-app run & deno run --allow-env --allow-read --allow-net server/server.ts 2 | import db from '../utils/db.ts'; 3 | import Dex from "https://deno.land/x/dex/mod.ts"; 4 | const dex = Dex({client: 'postgres'}); 5 | 6 | const projectController: any = {}; 7 | 8 | //Will only obtain projects with matching user_id value and when delete_status is false 9 | projectController.getproject = async(ctx: any) => { 10 | try { 11 | const { value } = await ctx.request.body({type: 'json'}); 12 | const obj = await value; 13 | let { user_id } = obj; 14 | let getQuery = dex.select().from("projects").where({user_id: user_id, delete_status: 'false'}).toString(); 15 | const data = await db.queryObject(getQuery); 16 | ctx.response.status = 200; 17 | ctx.response.body = data.rows 18 | return; 19 | } catch (err) { 20 | ctx.response.body = { status: false, data: null}; 21 | ctx.response.status = 500; 22 | console.log(err); 23 | } 24 | }; 25 | 26 | //Loads all projects with the matching project_id value 27 | projectController.loadProject = async (ctx: any) => { 28 | try { 29 | const { value } = await ctx.request.body({type: 'json'}); 30 | const obj = await value; 31 | let { project_id } = obj; 32 | let loadQuery = dex.select().from("elements").where({project_id: project_id}).toString(); 33 | const data = await db.queryObject(loadQuery); 34 | ctx.response.status = 200; 35 | ctx.response.body = data.rows; 36 | return; 37 | } catch (err) { 38 | ctx.response.body = { status: false, data: null}; 39 | ctx.response.status = 500; 40 | console.log(err); 41 | } 42 | }; 43 | 44 | //Does not actually delete project in database. Updates delete_status property to true and project will no longer be accessible. 45 | projectController.deleteProject = async (ctx: any) => { 46 | try { 47 | const { value } = await ctx.request.body({type: 'json'}); 48 | const obj = await value; 49 | let { project_id } = obj; 50 | let deleteProjQuery = dex("projects").where({id: project_id}).update({delete_status: "true"}).toString(); 51 | await db.queryArray(deleteProjQuery); 52 | return ctx.response.status = 200; 53 | } catch (err) { 54 | ctx.response.body = { status: false, data: null}; 55 | ctx.response.status = 500; 56 | console.log(err); 57 | } 58 | }; 59 | 60 | 61 | projectController.saveProject = async (ctx: any) => { 62 | try { 63 | //get request body and store in constants 64 | const { value } = await ctx.request.body({type: 'json'}); 65 | const obj = await value; 66 | let { project_id, elementsArr, user_id, project_name } = obj; 67 | 68 | //if project doesn't exist in db, create a new row in projects table 69 | if(!project_id){ 70 | let insertQuery = dex("projects").insert({name: project_name, user_id: user_id, delete_status: "false"}).returning('id').toString(); 71 | const newData = await db.queryObject(insertQuery); 72 | project_id = newData.rows[0].id; 73 | } 74 | 75 | //project exists, loop through elementsArr 76 | elementsArr.forEach(async el => { 77 | //if element already exists, update elements table 78 | const elementQuery = 79 | dex 80 | .select() 81 | .from("elements") 82 | .where({project_id: project_id, id: el.id}) 83 | .returning('id', 'element').toString(); 84 | const elementData = await db.queryObject(elementQuery); 85 | 86 | const { id, element, inputText, texAlign, textDecoration, backgroundColor, color, margin, width, height, padding, fontSize, className } = el; 87 | if(elementData.rows.length > 0){ 88 | const updateQuery = 89 | dex 90 | .from("elements") 91 | .where({id: id}) 92 | .update({ 93 | element: element, 94 | text: inputText, 95 | textalign: texAlign, 96 | textdecoration: textDecoration, 97 | backgroundcolor: backgroundColor, 98 | color: color, 99 | margin: margin, 100 | width: width, 101 | height: height, 102 | padding: padding, 103 | fontsize: fontSize, 104 | classname: className 105 | }) 106 | .returning('id', 'element').toString(); 107 | const updateData = await db.queryObject(updateQuery); 108 | } 109 | else { 110 | // create new row in elements with user's input 111 | const createQuery = 112 | dex("elements").insert({ 113 | id: id, 114 | element: element, 115 | text: inputText, 116 | textalign: texAlign, 117 | textdecoration: textDecoration, 118 | backgroundcolor: backgroundColor, 119 | color: color, 120 | margin: margin, 121 | height: height, 122 | padding: padding, 123 | fontsize: fontSize, 124 | classname: className, 125 | project_id: project_id 126 | }) 127 | .returning('id', 'element').toString(); 128 | const createData = await db.queryObject(createQuery); 129 | } 130 | }) 131 | ctx.response.status = 200; 132 | ctx.response.body = { 133 | project_id: project_id 134 | } 135 | return; 136 | } catch (err) { 137 | ctx.response.body = { status: false, data: null}; 138 | ctx.response.status = 500; 139 | console.log(err); 140 | } 141 | }; 142 | 143 | export default projectController; 144 | 145 | 146 | -------------------------------------------------------------------------------- /server/createDb.js: -------------------------------------------------------------------------------- 1 | import db from './utils/db.ts'; 2 | 3 | // create user table 4 | await db.queryObject( 5 | `CREATE TABLE IF NOT EXISTS users 6 | ( 7 | id SERIAL PRIMARY KEY, 8 | username VARCHAR(25) NOT NULL, 9 | password VARCHAR(100) NOT NULL, 10 | email VARCHAR(50) NOT NULL 11 | )` 12 | ); 13 | 14 | // create projects table 15 | await db.queryObject( 16 | `CREATE TABLE IF NOT EXISTS projects 17 | ( 18 | id SERIAL PRIMARY KEY, 19 | name VARCHAR(50), 20 | date TIMESTAMP default CURRENT_TIMESTAMP, 21 | delete_status VARCHAR(50), 22 | user_id INTEGER REFERENCES users(id) 23 | )` 24 | ); 25 | 26 | //create entries table 27 | await db.queryObject( 28 | `CREATE TABLE IF NOT EXISTS elements 29 | ( 30 | id INTEGER NOT NULL, 31 | element VARCHAR(100) NOT NULL, 32 | text VARCHAR(100), 33 | textAlign VARCHAR(100), 34 | textDecoration VARCHAR(100), 35 | backgroundColor VARCHAR(100), 36 | color VARCHAR(100), 37 | margin VARCHAR(100), 38 | width VARCHAR(100), 39 | height VARCHAR(100), 40 | padding VARCHAR(100), 41 | fontsize VARCHAR(100), 42 | classname VARCHAR(100), 43 | project_id INTEGER REFERENCES projects(id) 44 | )` 45 | ); 46 | 47 | -------------------------------------------------------------------------------- /server/routes/accountRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router, RouterContext } from "oak"; 2 | import db from '../utils/db.ts'; 3 | import accountController from '../controllers/accountController.ts'; 4 | import { oakCors } from "https://deno.land/x/cors/mod.ts"; 5 | 6 | const router = new Router(); 7 | 8 | router 9 | .post("/account", oakCors(), accountController.createAccount) 10 | .post('/login', oakCors(), accountController.loginCheck) 11 | 12 | export default router; -------------------------------------------------------------------------------- /server/routes/projectRoutes.ts: -------------------------------------------------------------------------------- 1 | import { Router, RouterContext } from "oak"; 2 | import db from '../utils/db.ts'; 3 | import projectController from '../controllers/projectController.ts'; 4 | import {authorize} from '../controllers/authorize.ts'; 5 | 6 | const router = new Router(); 7 | 8 | router 9 | .post('/home/get', authorize, projectController.getproject) 10 | .post('/home/load', authorize, projectController.loadProject) 11 | .post('/home/save', authorize, projectController.saveProject) 12 | .post('/home/delete', authorize, projectController.deleteProject) 13 | 14 | export default router; -------------------------------------------------------------------------------- /server/server.ts: -------------------------------------------------------------------------------- 1 | import { Application, isHttpError, Status, Router} from "oak"; 2 | import db from './utils/db.ts'; 3 | import accountRoutes from './routes/accountRoutes.ts'; 4 | import projectRoutes from './routes/projectRoutes.ts'; 5 | import { oakCors } from "https://deno.land/x/cors/mod.ts"; 6 | 7 | const port = 8080; 8 | const app: Application = new Application(); 9 | 10 | const router = new Router(); 11 | 12 | //Configures the Access-Control-Allow-Origin CORS header. 13 | app.use(oakCors({origin: "http://localhost:8000", methods: "POST, GET, DELETE"})); 14 | 15 | app.use(router.routes()); 16 | app.use(router.allowedMethods()); 17 | app.use(accountRoutes.routes()); 18 | app.use(accountRoutes.allowedMethods()); 19 | app.use(projectRoutes.routes()); 20 | app.use(projectRoutes.allowedMethods()); 21 | 22 | //Request Body parser 23 | app.use(async (ctx) => { 24 | const reqBody = await ctx.request.body().value; 25 | ctx.response.status = 200; 26 | }); 27 | 28 | app.addEventListener("error", (evt) => { 29 | console.log(evt.error); 30 | }); 31 | 32 | app.use((ctx: any) => { 33 | ctx.throw(500); 34 | }); 35 | 36 | app.use(async (ctx, next) => { 37 | try { 38 | await next(); 39 | } catch (err) { 40 | if (isHttpError(err)) { 41 | switch (err.status) { 42 | case Status.NotFound: 43 | break; 44 | default: 45 | } 46 | } else { 47 | throw err; 48 | } 49 | } 50 | }); 51 | 52 | app.addEventListener('listen', () => { 53 | console.log(`Listening on localhost:${port}`); 54 | }); 55 | 56 | await app.listen({ port }); 57 | 58 | 59 | -------------------------------------------------------------------------------- /server/utils/apiKey.ts: -------------------------------------------------------------------------------- 1 | export const key = await crypto.subtle.generateKey( 2 | { name: "HMAC", hash: "SHA-512" }, 3 | true, 4 | ["sign", "verify"], 5 | ); -------------------------------------------------------------------------------- /server/utils/db.ts: -------------------------------------------------------------------------------- 1 | import { Client } from "postgres"; 2 | import { config } from 'https://deno.land/x/dotenv/mod.ts'; 3 | 4 | const env = config(); 5 | const client = await new Client(`${env.POSTGRES_URI}`); 6 | 7 | await client.connect(); 8 | 9 | export default client; 10 | 11 | -------------------------------------------------------------------------------- /server/utils/dbNotes.txt: -------------------------------------------------------------------------------- 1 | Create Table Queries 2 | 3 | CREATE TABLE users 4 | ( 5 | id SERIAL PRIMARY KEY, 6 | username VARCHAR(15) NOT NULL, 7 | password VARCHAR(100) NOT NULL, 8 | email VARCHAR(50) NOT NULL 9 | ); 10 | 11 | CREATE TABLE projects 12 | ( 13 | id SERIAL PRIMARY KEY, 14 | name VARCHAR(50), 15 | date TIMESTAMP default CURRENT_TIMESTAMP, 16 | delete_status VARCHAR(50), 17 | user_id INTEGER REFERENCES users(id) 18 | ); 19 | 20 | CREATE TABLE elements 21 | ( 22 | id INTEGER NOT NULL, 23 | element VARCHAR(100) NOT NULL, 24 | text VARCHAR(100), 25 | textAlign VARCHAR(100), 26 | textDecoration VARCHAR(100), 27 | backgroundColor VARCHAR(100), 28 | color VARCHAR(100), 29 | margin VARCHAR(100), 30 | width VARCHAR(100), 31 | height VARCHAR(100), 32 | padding VARCHAR(100), 33 | project_id INTEGER REFERENCES projects(id) 34 | ); 35 | 36 | //table insert examples 37 | INSERT INTO users (username, password, email) 38 | VALUES ('username', 'password', 'user@user.com'); 39 | 40 | INSERT INTO projects (name, user_id) 41 | VALUES ('fresh', 1); 42 | 43 | INSERT INTO elements (id, element, text, textAlign, textDecoration, backgroundColor, color, margin, width, height, padding, project_id) 44 | VALUES (1, 'div', 'text', 'textAlign', 'textDecoration', 'backgroundColor', 'color', 'margin', 'width', 'height', 'padding', 1); 45 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Squeezed/d2a7edd17667a2e7f733d97baf5adbba8bc6350a/src/.DS_Store -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { React, Link } from '../deps.tsx'; 2 | import SideBar from './SideBar.tsx'; 3 | import Customization from './Customization.tsx'; 4 | import Preview from './preview/Preview.tsx'; 5 | import Buttons from './Buttons.tsx'; 6 | import { IHtmlElement, IProps} from './utils/types.ts'; 7 | 8 | const App: React.FC = () => { 9 | 10 | //State 11 | const [elementsArr, setElementsArr] = React.useState([] as IHtmlElement[]); 12 | const [currentElement, setCurrentElement] = React.useState({} as IHtmlElement); 13 | const [user, setUser] = React.useState(''); 14 | const [projectId, setProjectId] = React.useState(''); 15 | const [projectList, setProjectList] = React.useState([]); 16 | const [loadProj, setLoadProj] = React.useState(''); 17 | 18 | //Styling State 19 | const [inputText, setInputText] = React.useState(''); 20 | const [textAlign, setTextAlign] = React.useState(''); 21 | const [textDecoration, setTextDecoration] = React.useState(''); 22 | const [backgroundColor, setBackgroundColor] = React.useState(''); 23 | const [color, setColor] = React.useState(''); 24 | const [margin, setMargin] = React.useState(''); 25 | const [width, setWidth] = React.useState(''); 26 | const [height, setHeight] = React.useState(''); 27 | const [padding, setPadding] = React.useState(''); 28 | const [fontSize, setFontSize] = React.useState(''); 29 | const [className, setClassName] = React.useState(''); 30 | 31 | //update user state after user has logged in 32 | React.useEffect(() => { 33 | setUser(sessionStorage.getItem('userId') as string); 34 | }, [user]); 35 | 36 | return ( 37 |
38 | 39 |
40 |
41 | 58 |
59 |
60 | 64 |
65 |
66 |
67 |
68 | 82 |
83 |
84 | 112 |
113 |
114 |
115 | ); 116 | }; 117 | 118 | export default App; 119 | 120 | 121 | -------------------------------------------------------------------------------- /src/Buttons.tsx: -------------------------------------------------------------------------------- 1 | import { Link, React, useNavigate, serve } from "../deps.tsx"; 2 | import Popup from "./popup.tsx"; 3 | import Popup2 from "./Popup2.tsx"; 4 | 5 | export default function Buttons(props: any) { 6 | const [isOpen, setIsOpen] = React.useState(false); 7 | const [isOpen2, setIsOpen2] = React.useState(false); 8 | const [saveName, setSaveName] = React.useState(""); 9 | const { 10 | elementsArr, 11 | setElementsArr, 12 | currentElement, 13 | setCurrentElement, 14 | projectId, 15 | setProjectId, 16 | user, 17 | setUser, 18 | projectList, 19 | setProjectList, 20 | loadProj, 21 | setLoadProj, 22 | } = props; 23 | 24 | function togglePopup() { 25 | setIsOpen(!isOpen); 26 | } 27 | 28 | function togglePopup2() { 29 | if (!projectId) { 30 | setIsOpen2(!isOpen2); 31 | } else { 32 | alert("Project Saved"); 33 | save(); 34 | } 35 | } 36 | 37 | async function save() { 38 | let jwt = sessionStorage.getItem("Authorization"); 39 | const body = { 40 | project_id: projectId, 41 | elementsArr: elementsArr, 42 | project_name: saveName, 43 | user_id: user, 44 | authorization: jwt, 45 | }; 46 | await fetch("http://localhost:8080/home/save", { 47 | method: "POST", 48 | headers: { 49 | "Content-Type": "application/json", 50 | }, 51 | body: JSON.stringify(body), 52 | }) 53 | .then((data) => data.json()) 54 | .then((data) => { 55 | if(!projectId) setProjectId(data.project_id); 56 | }) 57 | .catch((err) => console.log(err)); 58 | } 59 | 60 | async function load() { 61 | let jwt = sessionStorage.getItem("Authorization"); 62 | await fetch("http://localhost:8080/home/get", { 63 | method: "POST", 64 | headers: { "Content-Type": "application/json" }, 65 | body: JSON.stringify({ 66 | user_id: user, 67 | authorization: jwt, 68 | }), 69 | }) 70 | .then((data) => data.json()) 71 | .then((data) => { 72 | setProjectList(data); 73 | }) 74 | .catch((err) => console.log(err)); 75 | } 76 | 77 | async function deleteData() { 78 | let jwt = sessionStorage.getItem("Authorization"); 79 | await fetch("http://localhost:8080/home/delete", { 80 | method: "POST", 81 | headers: { "Content-Type": "application/json" }, 82 | body: JSON.stringify({ 83 | project_id: projectId, 84 | authorization: jwt, 85 | }), 86 | }) 87 | .then((data) => data.json()) 88 | .catch((err) => console.log(err)); 89 | setElementsArr([]); 90 | setCurrentElement(""); 91 | } 92 | 93 | function clear() { 94 | setElementsArr([]); 95 | setCurrentElement(""); 96 | } 97 | 98 | function startNew() { 99 | setProjectId(""); 100 | setElementsArr([]); 101 | setCurrentElement(""); 102 | setProjectList([]); 103 | setLoadProj(""); 104 | } 105 | 106 | async function loadProject(id: any) { 107 | let jwt = sessionStorage.getItem("Authorization"); 108 | await fetch("http://localhost:8080/home/load", { 109 | method: "POST", 110 | headers: { "Content-Type": "application/json" }, 111 | body: JSON.stringify({ 112 | project_id: id, 113 | authorization: jwt, 114 | }), 115 | }) 116 | .then((data) => data.json()) 117 | .then((data) => { 118 | setElementsArr(data); 119 | setProjectId(id); 120 | }) 121 | .catch((err) => console.log(err)); 122 | } 123 | 124 | const navigate = useNavigate(); 125 | const navigateToLogin = () => { 126 | navigate("/"); 127 | }; 128 | 129 | function logout() { 130 | sessionStorage.clear(); 131 | navigateToLogin(); 132 | } 133 | 134 | const projs = projectList.map((elements: any, index: any) => { 135 | return ( 136 |
137 | 138 |
{ 141 | setLoadProj(elements.id); 142 | }} 143 | > 144 |
{elements.name}
145 |
146 |
147 | ); 148 | }); 149 | 150 | return ( 151 |
152 | 153 |
154 | 162 | 170 | {isOpen2 && ( 171 | 174 |
175 |
{ 177 | save(); 178 | togglePopup2(); 179 | }} 180 | > 181 | setSaveName(e.target.value)} 184 | type="text" 185 | id="inputFields" 186 | placeholder="Enter Name" 187 | required 188 | > 189 | 192 |
193 |
194 | 195 | } 196 | handleClose={togglePopup2} 197 | /> 198 | )} 199 | 208 | 209 |
210 | 219 | {isOpen && ( 220 | 223 |
224 | 225 | 226 |
{projs}
227 | 228 |
229 |
230 | 239 | 240 | } 241 | handleClose={togglePopup} 242 | /> 243 | )} 244 |
245 | 253 | 261 |
262 |
263 | ); 264 | } 265 | -------------------------------------------------------------------------------- /src/Customization.tsx: -------------------------------------------------------------------------------- 1 | import { React } from '../deps.tsx'; 2 | import { ICustomizationProps } from './utils/types.ts'; 3 | 4 | const Customization = (props: ICustomizationProps) => { 5 | 6 | const { elementsArr, 7 | setElementsArr, 8 | currentElement, 9 | setCurrentElement, 10 | inputText, 11 | setInputText, 12 | textAlign, 13 | setTextAlign, 14 | textDecoration, 15 | setTextDecoration, 16 | backgroundColor, 17 | setBackgroundColor, 18 | color, 19 | setColor, 20 | margin, 21 | setMargin, 22 | width, 23 | setWidth, 24 | height, 25 | setHeight, 26 | padding, 27 | setPadding, 28 | fontSize, 29 | setFontSize, 30 | className, 31 | setClassName, 32 | } = props; 33 | 34 | const [customizationPage, setCustomizationPage] = React.useState('styling'); 35 | 36 | // updates current element and elementsArr 37 | const handleSubmit = async (e: React.FormEvent) => { 38 | e.preventDefault(); 39 | 40 | const updateCurrentElement = { 41 | id: currentElement.id, 42 | element: currentElement.element, 43 | inputText: inputText, 44 | texAlign: textAlign, 45 | textDecoration: textDecoration, 46 | backgroundColor: backgroundColor, 47 | color: color, 48 | margin: margin, 49 | width: width, 50 | height: height, 51 | padding: padding, 52 | fontSize: fontSize, 53 | className: className 54 | }; 55 | setCurrentElement(updateCurrentElement); 56 | elementsArr[currentElement.id] = updateCurrentElement; 57 | 58 | setInputText(''); 59 | setTextAlign(''); 60 | setTextDecoration(''); 61 | setBackgroundColor(''); 62 | setColor(''); 63 | setMargin(''); 64 | setWidth(''); 65 | setHeight(''); 66 | setPadding(''); 67 | setFontSize(''); 68 | setClassName(''); 69 | }; 70 | 71 | return ( 72 |
73 |
74 | 75 |
Element selected: {currentElement.element}
76 |
77 |
78 | 79 | setInputText(e.target.value)} 82 | type="text" 83 | placeholder="Enter text" 84 | className="input" 85 | /> 86 |
87 | 88 | 89 | setFontSize(e.target.value)} 92 | type="text" 93 | placeholder="Enter font size" 94 | className="input" 95 | /> 96 |
97 | 98 | 99 | setBackgroundColor(e.target.value)} 102 | type="text" 103 | placeholder="Enter color name" 104 | className="input" 105 | /> 106 |
107 | 108 | 109 | setColor(e.target.value)} 112 | type="text" 113 | placeholder="Enter color name" 114 | className="input" 115 | /> 116 |
117 | 118 | 119 | setMargin(e.target.value)} 122 | type="text" 123 | placeholder="Enter margin value" 124 | className="input" 125 | /> 126 |
127 |
128 | 129 |
130 | 131 | 132 | setHeight(e.target.value)} 135 | type="text" 136 | placeholder="Enter height" 137 | className="input" 138 | /> 139 |
140 | 141 | 142 | setWidth(e.target.value)} 145 | type="text" 146 | placeholder="Enter width" 147 | className="input" 148 | /> 149 |
150 | 151 | 152 | setPadding(e.target.value)} 155 | type="text" 156 | placeholder="Enter padding" 157 | className="input" 158 | /> 159 |
160 | 161 | 162 | setClassName(e.target.value)} 165 | type="text" 166 | placeholder="Enter Class Name" 167 | className="input" 168 | /> 169 |
170 | 171 | 172 | 179 |
180 | 181 | 182 | 189 |
190 |
191 |
192 |
193 | 194 | 197 |
198 |
199 | ); 200 | }; 201 | 202 | export default Customization; -------------------------------------------------------------------------------- /src/Login.tsx: -------------------------------------------------------------------------------- 1 | import { React, Link, useNavigate } from '../deps.tsx'; 2 | 3 | const Login = () => { 4 | 5 | const [username, setUsername] = React.useState('') 6 | const [password, setPassword] = React.useState('') 7 | 8 | //navigates to /home 9 | const navigate = useNavigate() 10 | const navigateToHome = () => { 11 | navigate('/home') 12 | } 13 | 14 | const handleClick = (e: any) => { 15 | e.preventDefault(); 16 | const body = { 17 | username: username, 18 | password: password 19 | } 20 | 21 | fetch('http://localhost:8080/login',{ 22 | method: 'POST', 23 | headers: { 24 | 'Connection': 'keep-alive', 25 | 'Content-Type': 'application/json', 26 | 'Access-Control-Allow-Methods': 'POST', 27 | 'Access-Control-Max-Age': '86400', 28 | 'Access-Control-Allow-Credentials': 'true' 29 | }, 30 | body: JSON.stringify(body), 31 | }) 32 | .then((data) => { 33 | return data.json(); 34 | }) 35 | .then((data) => { 36 | if (data.result === true) { 37 | // stores userId and jwt token in session storage 38 | sessionStorage.setItem("userId", data.userId); 39 | sessionStorage.setItem("Authorization", data.token); 40 | navigateToHome(); 41 | } else { 42 | alert('Wrong username and password combination') 43 | } 44 | }) 45 | .catch((error) => console.log(error)) 46 | } 47 | 48 | return ( 49 |
50 | 51 | 52 |
53 |
54 |
55 |
56 |
57 |

LOG IN

58 | setUsername(e.target.value)} 63 | required /> 64 | setPassword(e.target.value)} required> 65 |
66 | 67 |

Need an account? Sign up!

68 | 69 |
70 |
71 |
72 |
73 |
74 | 75 |
76 |
77 |
78 |
79 | ); 80 | } 81 | 82 | export default Login 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/Popup2.tsx: -------------------------------------------------------------------------------- 1 | import { React } from "../deps.tsx"; 2 | 3 | const Popup2 = (props:any) => { 4 | return ( 5 |
6 | 7 | 8 |
9 | 10 | x 11 | 12 | {props.content} 13 |
14 |
15 | ); 16 | }; 17 | 18 | export default Popup2; 19 | -------------------------------------------------------------------------------- /src/SideBar.tsx: -------------------------------------------------------------------------------- 1 | import { React } from "../deps.tsx"; 2 | import { IHtmlElement, IProps, ISideBarProps } from "./utils/types.ts"; 3 | import elementsList from "./utils/elementsList.js"; 4 | 5 | const SideBar: React.FC = (props: ISideBarProps) => { 6 | const { elementsArr, setElementsArr, currentElement, setCurrentElement } = 7 | props; 8 | const { 9 | setInputText, 10 | setTextAlign, 11 | setTextDecoration, 12 | setBackgroundColor, 13 | setColor, 14 | setMargin, 15 | setWidth, 16 | setHeight, 17 | setPadding, 18 | setFontSize, 19 | setClassName, 20 | } = props; 21 | const [dragOver, setDragOver] = React.useState(false); 22 | const [content, setContent] = React.useState("drag into here"); 23 | 24 | const handleDragOverStart = () => setDragOver(true); 25 | const handleDragOverEnd = () => setDragOver(false); 26 | 27 | const dragItem = React.useRef(null); 28 | const dragOverItem = React.useRef(null); 29 | 30 | //when drag start, stores data for drag event 31 | const handleDragStart = ( 32 | event: React.DragEvent, 33 | area: string 34 | ) => { 35 | if (area === "dragArea") { 36 | event.dataTransfer.setData("id", event.currentTarget.id); 37 | } else if (area === "dropArea") { 38 | dragItem.current = event.currentTarget.id; 39 | } 40 | event.dataTransfer.setData("area", area); 41 | }; 42 | 43 | //when dragging over a element, stores data for drag event 44 | const dragEnter = (e: React.DragEvent, position: number) => { 45 | dragOverItem.current = position; 46 | }; 47 | 48 | //enables drop 49 | const enableDropping = (event: React.DragEvent) => { 50 | event.preventDefault(); 51 | const id = event.dataTransfer.getData("id"); 52 | setContent(id); 53 | }; 54 | 55 | //on drop, adds to or reorders elements array depending on drag start area 56 | const handleDrop = (event: React.DragEvent) => { 57 | const area = event.dataTransfer.getData("area"); 58 | const newElementsArr = [...elementsArr]; 59 | 60 | if (area === "dragArea") { 61 | const id = event.dataTransfer.getData("id"); 62 | setContent(id); 63 | const newElement = { 64 | id: elementsArr.length, 65 | element: id, 66 | inputText: "", 67 | texAlign: "", 68 | textDecoration: "", 69 | backgroundColor: "", 70 | color: "", 71 | margin: "", 72 | width: "", 73 | height: "", 74 | padding: "", 75 | fontSize: "", 76 | className: "", 77 | }; 78 | newElementsArr.push(newElement); 79 | setElementsArr(newElementsArr); 80 | setCurrentElement(newElement); 81 | } else if (area === "dropArea") { 82 | const dragItemContent = newElementsArr[dragItem.current]; 83 | const dragItemEnterContent = newElementsArr[dragOverItem.current]; 84 | newElementsArr.splice(dragItem.current, 1); 85 | newElementsArr.splice(dragOverItem.current, 0, dragItemContent); 86 | dragItem.current = null; 87 | dragOverItem.current = null; 88 | reorderElArr(newElementsArr); 89 | setElementsArr(newElementsArr); 90 | } 91 | }; 92 | 93 | //restores styling information 94 | const handleClick = (id: number) => { 95 | const a = elementsArr[id].inputText; 96 | const b = elementsArr[id].texAlign; 97 | const c = elementsArr[id].textDecoration; 98 | const d = elementsArr[id].backgroundColor; 99 | const e = elementsArr[id].color; 100 | const f = elementsArr[id].margin; 101 | const g = elementsArr[id].width; 102 | const h = elementsArr[id].height; 103 | const i = elementsArr[id].padding; 104 | const j = elementsArr[id].fontSize; 105 | const k = elementsArr[id].className; 106 | 107 | setCurrentElement(elementsArr[id]); 108 | setInputText(a); 109 | setTextAlign(b); 110 | setTextDecoration(c); 111 | setBackgroundColor(d); 112 | setColor(e); 113 | setMargin(f); 114 | setWidth(g); 115 | setHeight(h); 116 | setPadding(i); 117 | setFontSize(j); 118 | setClassName(k); 119 | }; 120 | 121 | //removes element 122 | const deleteElement = (id: number) => { 123 | const newElementsArr = [...elementsArr]; 124 | newElementsArr.splice(id, 1); 125 | reorderElArr(newElementsArr); 126 | setElementsArr(newElementsArr); 127 | setCurrentElement({} as IHtmlElement); 128 | }; 129 | 130 | //reassigns element ids to match array index 131 | const reorderElArr = (arr: IHtmlElement[]) => { 132 | arr.forEach((el: IHtmlElement, ind: number) => { 133 | el.id = ind; 134 | }); 135 | }; 136 | 137 | //on drag start, stores element information 138 | function onDragStart(event: any) { 139 | event.dataTransfer.setData("text/plain", event.target.id); 140 | 141 | event.currentTarget.style.backgroundColor = "yellow"; 142 | } 143 | 144 | //reads elementList constant and renders onto page 145 | const renderElementsList = elementsList.map( 146 | (el: { id: string; element: string; backgroundColor: string }) => { 147 | return ( 148 |
handleDragStart(e, "dragArea")}> 149 | 150 | 158 |
159 | ); 160 | } 161 | ); 162 | 163 | //reads elementsArr and renders onto page 164 | const createdElements = elementsArr.map((el: IHtmlElement, index: number) => { 165 | return ( 166 |
handleDragStart(e, "dropArea")} 170 | onDragEnter={(e) => { 171 | dragEnter(e, index); 172 | }} 173 | className="draggedTags" 174 | onDragOver={enableDropping} 175 | onClick={() => handleClick(index)} 176 | id={index.toString()} 177 | > 178 | {elementsArr[index].element} 179 | 180 | 183 |
184 | ); 185 | }); 186 | 187 | return ( 188 |
189 | 190 |
191 |
{renderElementsList}
192 |
199 |
{createdElements}
200 |
201 |
202 | ); 203 | }; 204 | 205 | export default SideBar; 206 | -------------------------------------------------------------------------------- /src/Signup.tsx: -------------------------------------------------------------------------------- 1 | import { React, Link, useNavigate } from '../deps.tsx'; 2 | 3 | const Signup = () => { 4 | 5 | const [username, setUsername] = React.useState('') 6 | const [password, setPassword] = React.useState('') 7 | const [email, setEmail] = React.useState('') 8 | 9 | //nagigate to login page 10 | const navigate = useNavigate(); 11 | const navigateToLogin = () => { 12 | navigate('/'); 13 | } 14 | 15 | const handleClick = (e: any) => { 16 | e.preventDefault(); 17 | const body = { 18 | username: username, 19 | password: password, 20 | email: email 21 | } 22 | //post form inputs to database and redirects to login page 23 | fetch('http://localhost:8080/account', { 24 | method: 'POST', 25 | headers: {'Content-Type': 'application/json'}, 26 | body: JSON.stringify(body), 27 | }) 28 | .then((data) => data.json()) 29 | .then(data => { 30 | navigateToLogin(); 31 | }) 32 | .catch((err) => console.log(err)); 33 | } 34 | 35 | return ( 36 |
37 | 38 |
39 |
40 |
41 |
42 |
43 |

SIGN UP

44 | setEmail(e.target.value)} required> 45 | setUsername(e.target.value)} required> 46 | setPassword(e.target.value)} required> 47 | 48 | 49 |

Have an account? Log in!

50 | 51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | ) 61 | } 62 | 63 | export default Signup 64 | 65 | -------------------------------------------------------------------------------- /src/popup.tsx: -------------------------------------------------------------------------------- 1 | import { React } from "../deps.tsx"; 2 | 3 | const Popup = (props:any) => { 4 | return ( 5 |
6 | 7 | 8 |
9 | 10 | x 11 | 12 | {props.content} 13 |
14 |
15 | ); 16 | }; 17 | 18 | export default Popup; 19 | -------------------------------------------------------------------------------- /src/preview/MainContainer.tsx: -------------------------------------------------------------------------------- 1 | import { React } from '../../deps.tsx'; 2 | import CodePreview from './pages/CodePreview.tsx'; 3 | import IslandPreview from './pages/IslandPreview.tsx'; 4 | import { IPreviewProps, IPreviewMainContainerProps } from './../utils/types.ts'; 5 | 6 | const MainContainer: any = (props: (IPreviewProps & IPreviewMainContainerProps)) => { 7 | const { previewPage, setPreviewPage, elementsArr, setElementsArr } = props; 8 | 9 | let page: any; 10 | 11 | if (previewPage === 'codePreview') page = ; 12 | if (previewPage === 'islandPreview') page = ; 13 | 14 | return
{page}
; 15 | }; 16 | 17 | export default MainContainer; -------------------------------------------------------------------------------- /src/preview/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import { React } from "../../deps.tsx"; 2 | import { IPreviewProps } from "./../utils/types.ts"; 3 | 4 | type IPreviewNavBarProps = { 5 | setPreviewPage: (previewPage: string) => void; 6 | }; 7 | 8 | const Navbar: any = (props: IPreviewNavBarProps) => { 9 | const { setPreviewPage } = props; 10 | 11 | return ( 12 |
13 | 14 | 22 | 30 |
31 | ); 32 | }; 33 | 34 | export default Navbar; 35 | -------------------------------------------------------------------------------- /src/preview/Preview.tsx: -------------------------------------------------------------------------------- 1 | import { React } from "../../deps.tsx"; 2 | import MainContainer from "./MainContainer.tsx"; 3 | import Navbar from "./Navbar.tsx"; 4 | import { IPreviewProps } from "./../utils/types.ts"; 5 | 6 | const Preview = (props: IPreviewProps) => { 7 | const { elementsArr, setElementsArr } = props; 8 | const [previewPage, setPreviewPage] = React.useState("codePreview"); 9 | 10 | return ( 11 |
12 | 13 | 19 |
20 | ); 21 | }; 22 | 23 | export default Preview; 24 | -------------------------------------------------------------------------------- /src/preview/pages/CodePreview.tsx: -------------------------------------------------------------------------------- 1 | import { React } from '../../../deps.tsx'; 2 | import { IHtmlElement, IPreviewProps } from './../../utils/types.ts'; 3 | 4 | //generates code preview 5 | const CodePreview: any = (props: IPreviewProps) => { 6 | const {elementsArr, setElementsArr} = props; 7 | 8 | let testArray: string[] = []; 9 | 10 | const htmlTags = elementsArr.map((elements: IHtmlElement, index: number) => { 11 | let eleFirst:any; 12 | let eleSecond:any; 13 | let midText: any; 14 | let endBr: any; 15 | 16 | if(elementsArr[index].element === 'div') { 17 | eleFirst = `
` 20 | 21 | } 22 | if(elementsArr[index].element === 'paragraph') { 23 | eleFirst = `

` 26 | } 27 | if(elementsArr[index].element === 'img') { 28 | eleFirst = `` 31 | } 32 | if(elementsArr[index].element === 'button') { 33 | eleFirst = `` 36 | } 37 | if(elementsArr[index].element === 'form') { 38 | eleFirst = `

` 41 | } 42 | if(elementsArr[index].element === 'ol') { 43 | eleFirst = `
    ` 46 | } 47 | if(elementsArr[index].element === 'ul') { 48 | eleFirst = `
      ` 51 | } 52 | if(elementsArr[index].element === 'h1') { 53 | eleFirst = `

      ` 56 | } 57 | if(elementsArr[index].element === 'h2') { 58 | eleFirst = `

      ` 61 | } 62 | if(elementsArr[index].element === 'h3') { 63 | eleFirst = `

      ` 66 | } 67 | if(elementsArr[index].element === 'footer') { 68 | eleFirst = `