diff --git a/BasicOperations.ts b/BasicOperations.ts new file mode 100644 index 0000000..cdc4f25 --- /dev/null +++ b/BasicOperations.ts @@ -0,0 +1,179 @@ +import { CRUD, Validation } from "./interfaces.js" +import { TableCreation } from "./TableCreate.js" +import { Presentation } from "./ButtonOperations.js" +//import { Employee } from "./EmployeeModel.js"; + + +export class Operations implements CRUD +{ + static count: number; + static buffer: (string | number)[] = []; + static flag: string = "off"; + + buttonObject: Presentation = new Presentation(); + + + //Makes the entire row updatable + editRow(x: string) + { + if (Operations.flag !== "off") + this.cancelRow(Operations.flag); + Operations.flag = x; + + let rowId = document.getElementById(x)!; + let newinput: HTMLInputElement, element: ChildNode; + let errorLine: HTMLSpanElement; + + for (let j = 0; j < Operations.count; j++) + { + element = rowId.childNodes[j]; + Operations.buffer[j] = element.textContent!; + element.textContent = ""; + element.appendChild(newinput = document.createElement('input')); + element.appendChild(errorLine = document.createElement('p')); + errorLine.id = "e" + x + j; + + if (Operations.buffer[j] == "") + newinput.placeholder = TableCreation.headings[j]; + else + newinput.value = Operations.buffer[j].toString(); + } + + let selectElement = document.getElementById(x)!.getElementsByTagName('td')[5]; + selectElement.innerHTML = " " + selectElement.appendChild(errorLine = document.createElement('p')); + errorLine.id = "e" + x + '5'; + + let saveB: HTMLInputElement, cancelB: HTMLInputElement; + let lastColumn = rowId.getElementsByTagName('td')[Operations.count]; + + //FIRST CHILD IS THE EDIT BUTTON + saveB = lastColumn.getElementsByTagName('input')[0]; + // LAST CHILD IS THE DELETE BUTTON + cancelB = lastColumn.getElementsByTagName('input')[1]; + + saveB.value = "SAVE"; + saveB.onclick = () => { this.saveRow(x) }; + + cancelB.value = "CANCEL"; + cancelB.onclick = () => { this.cancelRow(x) }; + + } + + deleteRow = (x: string) => document.getElementById(x)!.remove(); + + + saveRow(x: string) + { + let row = document.getElementById(x)!; + let cell = row.getElementsByTagName('td'); + let cellInput: string[] = []; + + for (let i = 0; i < Operations.count; i++) + { + document.getElementById('e'+x+i)!.innerHTML = " "; + + if (i === 5) + { + cellInput[i] = cell[i].querySelector('select')!.value; + continue; + } + + cellInput[i] = cell[i].querySelector('input')!.value; + } + + let answer = this.validateFields(cellInput, x); + + if (answer === true) + { + Operations.flag = "off"; + + for (let i = 0; i < Operations.count; i++) + cell[i].innerHTML = cellInput[i]; + + this.buttonObject.fetchButton(x, this); + } + } + + + cancelRow(x: string) + { + Operations.flag = "off"; + let val = 0; + let cell = document.getElementById(x)!.getElementsByTagName('td')!; + + for (let i = 0; i < Operations.count; i++) { + + cell[i].innerHTML = Operations.buffer[i].toString(); + if (cell[i].innerHTML == "") val += 1; + } + + this.buttonObject.fetchButton(x, this); + + if (val == Operations.count) { + + this.deleteRow(x); + } + + } + + validateFields(employee: string[], x: string) + { + + if (!employee[0].match(/^[a-zA-Z]+$/)) { + + document.getElementById('e' + x + '0')!.innerHTML = 'Only alphabets are allowed'; + return false; + } + + if (!employee[1].match(/^[a-zA-Z]+$/)) { + + document.getElementById('e' + x + '1')!.innerHTML = 'Only alphabets are allowed'; + return false; + } + + if (!employee[2].match(/^[a-zA-Z]+$/)) { + document.getElementById('e' + x + '2')!.innerHTML = 'Only alphabets are allowed'; + return false; + } + + if (!employee[3].match(/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/)) { + + document.getElementById('e' + x + '3')!.innerHTML = 'Enter valid email'; + return false; + } + + if (!employee[4].match(/[+]?[0-9]{10,13}$/)) { + document.getElementById('e' + x + '4')!.innerHTML = 'Only digits are allowed'; + return false; + } + + if (employee[6] === "") { + document.getElementById('e' + x + '6')!.innerHTML = 'Invalid Address'; + return false; + } + + return true; + } + + + multipleDelete() + { + Operations.flag = "off"; + const box = document.getElementsByName('check') as NodeListOf; + const n = box.length; + let arr: string[] =[]; + for(let i=0; i +{ + fetchFunction(): Promise + { + return fetch("data.json") + .then(response => response.json()) + } +} \ No newline at end of file diff --git a/ButtonOperations.ts b/ButtonOperations.ts new file mode 100644 index 0000000..1bbc69d --- /dev/null +++ b/ButtonOperations.ts @@ -0,0 +1,57 @@ +import { ButtonPresentation} from "./interfaces.js" +import { Operations } from "./BasicOperations.js" + +export class Presentation implements ButtonPresentation +{ + //Creates Button for each row + createButton(row: HTMLTableRowElement, x: string, op : Operations) + { + + let cell: HTMLTableDataCellElement, editB: HTMLInputElement, deleteB: HTMLInputElement; + cell = document.createElement('td'); + row.appendChild(cell); + + editB = document.createElement('input'); + editB.type = "Button"; + editB.value = "EDIT"; + editB.className = "btn btn-success"; + editB.onclick = () => { op.editRow(x) }; + cell.appendChild(editB); + + let text = document.createElement('span') + text.innerHTML = ' ' + cell.appendChild(text); + + deleteB = document.createElement('input'); + deleteB.type = "Button"; + deleteB.value = "DELETE"; + deleteB.className = "btn btn-danger"; + deleteB.onclick = () => { op.deleteRow(x) }; + cell.appendChild(deleteB); + + cell = document.createElement('td'); + row.appendChild(cell); + + let mDelete = document.createElement('input'); + mDelete.type = "checkbox"; + mDelete.name = "check"; + cell.appendChild(mDelete); + } + + //Fetches buttons back after save and cancel operations are executed + fetchButton(x: string, op: Operations) + { + + let tuple = document.getElementById(x)!; + + let editB = tuple.getElementsByTagName('td')[Operations.count].getElementsByTagName('input')[0]; + editB.value = "EDIT"; + editB.onclick = () => { op.editRow(x) }; + + let deleteB = tuple.getElementsByTagName('td')[Operations.count].getElementsByTagName('input')[1]; + deleteB.value = "DELETE"; + deleteB.onclick = () => { op.deleteRow(x) }; + } + + +} \ No newline at end of file diff --git a/EmployeeModel.ts b/EmployeeModel.ts new file mode 100644 index 0000000..8d04e09 --- /dev/null +++ b/EmployeeModel.ts @@ -0,0 +1,13 @@ +export enum Role { Trainee, QA, Devops, Developer }; + +export class Employee +{ + firstName: string; + middleName: string; + lastName: string; + email: string; + phone: number; + role: Role; + address: string; + +} \ No newline at end of file diff --git a/TableCreate.ts b/TableCreate.ts new file mode 100644 index 0000000..a98da72 --- /dev/null +++ b/TableCreate.ts @@ -0,0 +1,127 @@ + +import { Employee, Role} from "./EmployeeModel.js" +import { Creation } from "./interfaces.js" +import { Presentation} from "./ButtonOperations.js" +import { Operations } from "./BasicOperations.js"; + + +export class TableCreation implements Creation +{ + static headings: string[] = []; + static newid: number = -1; + + buttonObject : Presentation; + operationObject : Operations; + + constructor() + { + this.buttonObject = new Presentation(); + this.operationObject = new Operations(); + const mButton = document.getElementById("mDeleteButton") as HTMLInputElement; + mButton.addEventListener("click", () => { this.operationObject.multipleDelete(); }); + } + + + //called when data is fetched + create(employee: Employee[]) + { + let loadB = document.getElementById('LoadButton')! as HTMLInputElement; + + if (loadB.value !== "Load Data") { + loadB.value = "Refresh"; + Operations.flag = "off"; + TableCreation.newid = -1; + } + else + { + + loadB.value = "Refresh"; + document.getElementById('NewButton')!.style.display = "block"; + document.getElementById('mDeleteButton')!.style.display = "block"; + let table = document.getElementsByTagName('table')[0]; + let tableHeader = document.createElement('thead'); + let tableBody = document.createElement('tbody') + table.appendChild(tableHeader); + table.appendChild(tableBody); + TableCreation.headings = Object.keys(employee[0]); + Operations.count = TableCreation.headings.length; + } + + this.displayTable(employee); + } + + //Displays table content + displayTable(employee: Employee[]) + { + + let table = document.getElementsByTagName('table')[0]; + let tableBody = document.getElementsByTagName('tbody')[0]; + let tableHeader = document.getElementsByTagName('thead')[0]; + tableBody.innerHTML = ""; + tableHeader.innerHTML = ""; + let cell: HTMLTableDataCellElement, row: HTMLTableRowElement, head: HTMLTableHeaderCellElement; + row = document.createElement('tr'); + tableHeader.appendChild(row); + row.className = "success"; + + for (let k of TableCreation.headings) + { + head = document.createElement('th'); + row.appendChild(head); + head.innerHTML = k; + } + + head = document.createElement('th') + row.appendChild(head); + head.innerHTML = "Operations"; + + + head = document.createElement('th') + row.appendChild(head); + head.innerHTML = "MultipleDelete"; + + let n = employee.length; + + for (let i = 0; i < n; i++) + { + tableBody.appendChild(row = document.createElement('tr')); + row.setAttribute("id", i.toString()); + this.addEmployee(row,employee[i]); + this.buttonObject.createButton(row, i.toString(),this.operationObject); + + } + + TableCreation.newid = n-1; + + } + + + //Adds Employees one by one + addEmployee(row: HTMLTableRowElement, employee : Employee) + { + let cell = document.createElement('td'); + cell.innerHTML = employee.firstName; + row.appendChild(cell); + cell = document.createElement('td'); + cell.innerHTML = employee.middleName; + row.appendChild(cell); + cell = document.createElement('td'); + cell.innerHTML = employee.lastName; + row.appendChild(cell); + cell = document.createElement('td'); + cell.innerHTML = employee.email; + row.appendChild(cell); + cell = document.createElement('td'); + cell.innerHTML = employee.phone.toString(); + row.appendChild(cell); + cell = document.createElement('td'); + cell.innerHTML =Role[employee.role]; + row.appendChild(cell); + cell = document.createElement('td'); + cell.innerHTML = employee.address; + row.appendChild(cell); + + return employee; + } + +} \ No newline at end of file diff --git a/data.json b/data.json new file mode 100644 index 0000000..490d3b3 --- /dev/null +++ b/data.json @@ -0,0 +1,39 @@ +[ + { + "firstName": "Rashi", + "middleName": "nil", + "lastName": "Sharma", + "email": "rashi.sharma@sourcefuse.com", + "phone": 8888888822, + "role": 0, + "address": "#123 Sector-13 Chandigarh" + }, + { + "firstName": "Rashi", + "middleName": "nil", + "lastName": "Sharma", + "email": "rashi.sharma@sourcefuse.com", + "phone": 8888888822, + "role": 1, + "address": "#123 Sector-13 Chandigarh" + }, + { + "firstName": "Rashi", + "middleName": "nil", + "lastName": "Sharma", + "email": "rashi.sharma@sourcefuse.com", + "phone": 8888888822, + "role": 2, + "address": "#123 Sector-13 Chandigarh" + }, + { + "firstName": "Rashi", + "middleName": "nil", + "lastName": "Sharma", + "email": "rashi.sharma@sourcefuse.com", + "phone": 8888888822, + "role":2, + "address": "#123 Sector-13 Chandigarh" + } + +] \ No newline at end of file diff --git a/first.js b/first.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/first.js @@ -0,0 +1 @@ + diff --git a/index.htm b/index.htm new file mode 100644 index 0000000..249e033 --- /dev/null +++ b/index.htm @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + +
+ + + +
+ + + + diff --git a/interfaces.ts b/interfaces.ts new file mode 100644 index 0000000..6d4f929 --- /dev/null +++ b/interfaces.ts @@ -0,0 +1,35 @@ +import { Operations } from "./BasicOperations"; +import {Employee} from "./EmployeeModel"; + +export interface FetchData +{ + fetchFunction(): Promise; +} + +export interface Creation +{ + create(obj: T[]): void; + displayTable(obj: T[]): void; + addEmployee(row: HTMLTableRowElement,obj: T): void; +} + +export interface CRUD +{ + editRow(rowId: T): void; + deleteRow(rowId: T): void; + saveRow(rowId: T): void; + cancelRow(rowId: T): void; +} + + +export interface ButtonPresentation +{ + createButton(row: HTMLTableRowElement,rowId: T, obj: Operations): void; + fetchButton(rowId: T, obj: Operations): void; +} + + +export interface Validation +{ + validateFields(obj: T): void; +} diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..45e4e75 --- /dev/null +++ b/main.ts @@ -0,0 +1,35 @@ +import { TableCreation } from "./TableCreate.js"; +import { FetchLogic } from "./BusinessLogic.js"; +import { NewEntry } from "./newEntry.js"; + + +//Implementation class is the main class which exeutes both Business and Presentation Logic +class Implementation +{ + fetchObject: FetchLogic; + createObject: TableCreation; + newEntryObject: NewEntry; + + constructor() + { + this.fetchObject = new FetchLogic(); + this.createObject = new TableCreation(); + this.newEntryObject = new NewEntry(); + + let loadButton = document.getElementById("LoadButton") as HTMLInputElement; + loadButton.addEventListener("click", () => { this.loadData() }); + } + + loadData() + { + this.fetchObject.fetchFunction() + .then((data) => { + this.createObject.create(data); + }) + .catch(() => console.log("data not found")); + } + +} + +let obj = new Implementation(); + diff --git a/newEntry.ts b/newEntry.ts new file mode 100644 index 0000000..df6a8a8 --- /dev/null +++ b/newEntry.ts @@ -0,0 +1,38 @@ +import { TableCreation } from "./TableCreate.js"; +import { ButtonPresentation } from "./interfaces.js"; +import { Presentation } from "./ButtonOperations.js"; +import { Operations } from "./BasicOperations.js"; + +export class NewEntry +{ + buttonObject: Presentation; + operationObject: Operations; + + constructor() + { + this.buttonObject = new Presentation(); + this.operationObject = new Operations(); + let newButton = document.getElementById("NewButton") as HTMLInputElement; + newButton.addEventListener("click",() => {this.newData()}); + + } + + newData() + { + TableCreation.newid+= 1; + + let row : HTMLTableRowElement, cell: HTMLTableDataCellElement; + let rowId = TableCreation.newid.toString(); + + let tableBody = document.querySelector('tbody')!; + tableBody.appendChild(row = document.createElement('tr')); + row.setAttribute("id", rowId); + + for (let k of TableCreation.headings) + row.appendChild(cell = document.createElement('td')); + + this.buttonObject.createButton(row, rowId,this.operationObject); + this.operationObject.editRow(rowId); + + } +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..71fd388 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "assignment", + "version": "1.0.0", + "description": "", + "main": "main.js", + "dependencies": {}, + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2dd02c4 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,66 @@ +{ + "compilerOptions": { + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ + "module": "es2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + "strictPropertyInitialization": false, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } +}