diff --git a/.gitignore b/.gitignore index 13ed183..5584cd5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ tmp .DS_Store .idea src/libs -data/db.json \ No newline at end of file +data/db.json +dist/ +src/dist/ \ No newline at end of file diff --git a/README.md b/README.md index d93a6f8..96fa4d8 100644 --- a/README.md +++ b/README.md @@ -18,4 +18,38 @@ To run the application we type `npm start` - this loads the application using a local webserver, check the console for the port number to use. -The application is a simple contacts application where you can search, create or edit a contact. \ No newline at end of file +The application is a simple contacts application where you can search, create or edit a contact. + +# Steps for Migration. + +Preparing for migration: + +STEP 1: + +Follow the step 1 of angular style guide https://github.com/johnpapa/angular-styleguide + +Single Responsibility: Organize the code in such a maner that 1 file contains only one component. + +STEP 2: + +Move all the dependencies from bower to npm with approprate npm naming and version in package.json and run npm install. + +Convert all the JS files to TS files. +Add index.ts files in each directory importing all the entities. + +install following dev dependencies: rimraf, ts-loader, typescript, webpack, @types/angular +npm install rimraf ts-loader typescript webpack @types/angular --save-dev + +run tsc --init in project dir to generate tsconfig.json file and set appropriate ts configuration options. + +Create webpack.config.js file and configure webpack options accordingly. + +add amin.ts file containing all the imports corresponding to the scripts in index.html file and remove scripts from index.html file. + +add bundle.js file in script tag in index.html file. + +add build script in package.json rimraf src/dist && webpack --bail --progress --profile + +run build scipt start the app and verify if everything works file. + +optionally can add dist folders to .gitignore file. \ No newline at end of file diff --git a/package.json b/package.json index 2e9e524..64dec83 100644 --- a/package.json +++ b/package.json @@ -6,14 +6,36 @@ "scripts": { "server": "cp ./data/orig-db.json ./data/db.json && json-server --watch ./data/db.json", "setup": "bower install", - "start": "cd src && serve" + "start": "cd src && serve", + "build": "rimraf src/dist && webpack --bail --progress --profile" }, "author": "", "license": "ISC", - "dependencies": {}, + "dependencies": { + "angular": "1.4.0", + "angular-animate": "1.4.0", + "angular-auto-validate": "^1.19.0", + "angular-ladda": "^0.4.3", + "angular-resource": "1.4.0", + "angular-spinner": "^1.0.1", + "angular-strap": "^2.3.12", + "angular-ui-router": "^0.4.2", + "angularjs-toaster": "^2.1.0", + "bootstrap": "3.3.2", + "bootstrap-additions": "0.3.1", + "font-awesome": "4.3.0", + "jquery": "2.1.3", + "ng-infinite-scroll": "1.2.1" + }, "devDependencies": { + "@types/angular": "^1.8.4", "bower": "^1.8.0", "json-server": "^0.9.6", - "serve": "^5.1.2" + "rimraf": "^3.0.2", + "serve": "^5.1.2", + "ts-loader": "^9.4.1", + "typescript": "^4.8.4", + "webpack": "^5.74.0", + "webpack-cli": "^4.10.0" } } diff --git a/src/app/app.main.js b/src/app/app.main.ts similarity index 80% rename from src/app/app.main.js rename to src/app/app.main.ts index 197434d..5065e73 100644 --- a/src/app/app.main.js +++ b/src/app/app.main.ts @@ -11,10 +11,10 @@ angular "ui.router" ]) .config(function( - $httpProvider, - $resourceProvider, - laddaProvider, - $datepickerProvider + $httpProvider: any, + $resourceProvider: any, + laddaProvider: any, + $datepickerProvider: any ) { laddaProvider.setOption({ style: "expand-right" diff --git a/src/app/app.routes.js b/src/app/app.routes.ts similarity index 92% rename from src/app/app.routes.js rename to src/app/app.routes.ts index 2f98217..6e883b2 100644 --- a/src/app/app.routes.js +++ b/src/app/app.routes.ts @@ -1,6 +1,6 @@ angular .module("codecraft") - .config(function($stateProvider, $urlRouterProvider) { + .config(function($stateProvider: any, $urlRouterProvider: any) { $stateProvider .state("list", { url: "/", diff --git a/src/app/controllers.js b/src/app/controllers.js deleted file mode 100644 index f78828c..0000000 --- a/src/app/controllers.js +++ /dev/null @@ -1,48 +0,0 @@ -angular - .module("codecraft") - .controller("PersonCreateController", function( - $scope, - $state, - ContactService - ) { - $scope.contacts = ContactService; - $scope.person = {}; - - $scope.save = function() { - console.log("createContact"); - $scope.contacts.createContact($scope.person).then(function() { - $state.go("list"); - }); - }; - }) - .controller("PersonEditController", function( - $scope, - $stateParams, - $state, - ContactService - ) { - $scope.contacts = ContactService; - $scope.person = $scope.contacts.getPerson($stateParams.email); - - $scope.save = function() { - $scope.contacts.updateContact($scope.person).then(function() { - $state.go("list"); - }); - }; - - $scope.remove = function() { - $scope.contacts.removeContact($scope.person).then(function() { - $state.go("list"); - }); - }; - }) - .controller("PersonListController", function($scope, ContactService) { - $scope.contacts = ContactService; - }) - .controller("SearchController", function($scope, ContactService) { - $scope.contacts = ContactService; - - $scope.loadMore = function() { - $scope.contacts.loadMore(); - }; - }); diff --git a/src/app/controllers/index.ts b/src/app/controllers/index.ts new file mode 100644 index 0000000..acd2c80 --- /dev/null +++ b/src/app/controllers/index.ts @@ -0,0 +1,4 @@ +import './person-create.controller'; +import './person-edit.controller'; +import './person-list.controller'; +import './search.controller'; diff --git a/src/app/controllers/person-create.controller.ts b/src/app/controllers/person-create.controller.ts new file mode 100644 index 0000000..0b90189 --- /dev/null +++ b/src/app/controllers/person-create.controller.ts @@ -0,0 +1,17 @@ +angular + .module("codecraft") + .controller("PersonCreateController", function( + $scope, + $state, + ContactService + ) { + $scope.contacts = ContactService; + $scope.person = {}; + + $scope.save = function() { + console.log("createContact"); + $scope.contacts.createContact($scope.person).then(function() { + $state.go("list"); + }); + }; + }) diff --git a/src/app/controllers/person-edit.controller.ts b/src/app/controllers/person-edit.controller.ts new file mode 100644 index 0000000..f6d76f7 --- /dev/null +++ b/src/app/controllers/person-edit.controller.ts @@ -0,0 +1,23 @@ +angular + .module("codecraft") + .controller("PersonEditController", function( + $scope, + $stateParams, + $state, + ContactService + ) { + $scope.contacts = ContactService; + $scope.person = $scope.contacts.getPerson($stateParams.email); + + $scope.save = function() { + $scope.contacts.updateContact($scope.person).then(function() { + $state.go("list"); + }); + }; + + $scope.remove = function() { + $scope.contacts.removeContact($scope.person).then(function() { + $state.go("list"); + }); + }; + }) \ No newline at end of file diff --git a/src/app/controllers/person-list.controller.ts b/src/app/controllers/person-list.controller.ts new file mode 100644 index 0000000..258191e --- /dev/null +++ b/src/app/controllers/person-list.controller.ts @@ -0,0 +1,5 @@ +angular + .module("codecraft") + .controller("PersonListController", function($scope, ContactService) { + $scope.contacts = ContactService; + }) diff --git a/src/app/controllers/search.controller.ts b/src/app/controllers/search.controller.ts new file mode 100644 index 0000000..5c70fed --- /dev/null +++ b/src/app/controllers/search.controller.ts @@ -0,0 +1,9 @@ +angular + .module("codecraft") + .controller("SearchController", function($scope, ContactService) { + $scope.contacts = ContactService; + + $scope.loadMore = function() { + $scope.contacts.loadMore(); + }; + }); diff --git a/src/app/directives.js b/src/app/directives/card.directive.ts similarity index 71% rename from src/app/directives.js rename to src/app/directives/card.directive.ts index 5a6d920..b7ccd3a 100644 --- a/src/app/directives.js +++ b/src/app/directives/card.directive.ts @@ -18,13 +18,3 @@ angular } }; }) - .directive("ccSpinner", function() { - return { - restrict: "AE", - templateUrl: "templates/spinner.html", - scope: { - isLoading: "=", - message: "@" - } - }; - }); diff --git a/src/app/directives/index.ts b/src/app/directives/index.ts new file mode 100644 index 0000000..94de91b --- /dev/null +++ b/src/app/directives/index.ts @@ -0,0 +1,2 @@ +import './card.directive'; +import './spinner.directive'; diff --git a/src/app/directives/spinner.directive.ts b/src/app/directives/spinner.directive.ts new file mode 100644 index 0000000..0d452cc --- /dev/null +++ b/src/app/directives/spinner.directive.ts @@ -0,0 +1,12 @@ +angular + .module("codecraft") + .directive("ccSpinner", function() { + return { + restrict: "AE", + templateUrl: "templates/spinner.html", + scope: { + isLoading: "=", + message: "@" + } + }; + }); diff --git a/src/app/filters.js b/src/app/filters.js deleted file mode 100644 index 13193f3..0000000 --- a/src/app/filters.js +++ /dev/null @@ -1,11 +0,0 @@ -angular.module("codecraft").filter("defaultImage", function() { - return function(input, param) { - if (!param) { - param = "/img/avatar.png"; - } - if (!input) { - return param; - } - return input; - }; -}); diff --git a/src/app/filters/default-image.filter.ts b/src/app/filters/default-image.filter.ts new file mode 100644 index 0000000..0dbfee2 --- /dev/null +++ b/src/app/filters/default-image.filter.ts @@ -0,0 +1,12 @@ +angular.module("codecraft").filter("defaultImage", function() { + return function(input: any, param: any) { + if (!param) { + param = "/img/avatar.png"; + } + if (!input) { + return param; + } + return input; + }; + }); + \ No newline at end of file diff --git a/src/app/filters/index.ts b/src/app/filters/index.ts new file mode 100644 index 0000000..b3ecb76 --- /dev/null +++ b/src/app/filters/index.ts @@ -0,0 +1 @@ +import './default-image.filter'; \ No newline at end of file diff --git a/src/app/main.ts b/src/app/main.ts new file mode 100644 index 0000000..43b7a3e --- /dev/null +++ b/src/app/main.ts @@ -0,0 +1,19 @@ +import 'angular'; +import 'angular-resource'; +import 'angular-animate' + +import 'ng-infinite-scroll'; +import 'angular-spinner'; +import 'angular-auto-validate/dist/jcs-auto-validate'; +import 'angular-ladda'; +import 'angular-strap'; +import 'angularjs-toaster'; +import 'angular-ui-router'; + + +import './app.main'; +import './services'; +import './filters'; +import './directives'; +import './controllers'; +import './app.routes'; diff --git a/src/app/services/contact.resource.ts b/src/app/services/contact.resource.ts new file mode 100644 index 0000000..12e6620 --- /dev/null +++ b/src/app/services/contact.resource.ts @@ -0,0 +1,13 @@ +angular + .module("codecraft") + .factory("Contact", function($resource : any) { + return $resource( + "http://localhost:3000/contacts/:id", + {id: "@id"}, + { + update: { + method: "PUT" + } + } + ); + }) diff --git a/src/app/services.js b/src/app/services/contact.service.ts similarity index 81% rename from src/app/services.js rename to src/app/services/contact.service.ts index dc5196b..7cf64d9 100644 --- a/src/app/services.js +++ b/src/app/services/contact.service.ts @@ -1,22 +1,11 @@ angular .module("codecraft") - .factory("Contact", function($resource) { - return $resource( - "http://localhost:3000/contacts/:id", - {id: "@id"}, - { - update: { - method: "PUT" - } - } - ); - }) - .factory("ContactService", function(Contact, $rootScope, $q, toaster) { + .factory("ContactService", function(Contact: any, $rootScope: any, $q: any, toaster: any) { var self = { - getPerson: function(email) { + getPerson: function(email: any) { console.log(email); for (var i = 0; i < self.persons.length; i++) { - var obj = self.persons[i]; + var obj: any = self.persons[i]; if (obj.email == email) { return obj; } @@ -26,10 +15,11 @@ angular hasMore: true, isLoading: false, isSaving: false, - persons: [], + persons: [] as any[], search: null, sorting: "name", ordering: "ASC", + isDeleting: false, doSearch: function() { self.hasMore = true; self.page = 1; @@ -53,7 +43,7 @@ angular q: self.search }; - Contact.query(params, function(data) { + Contact.query(params, function(data: any) { console.debug(data); angular.forEach(data, function(person) { self.persons.push(new Contact(person)); @@ -72,7 +62,7 @@ angular self.loadContacts(); } }, - updateContact: function(person) { + updateContact: function(person: any) { var d = $q.defer(); self.isSaving = true; person.$update().then(function() { @@ -82,10 +72,10 @@ angular }); return d.promise; }, - removeContact: function(person) { + removeContact: function(person: any) { var d = $q.defer(); self.isDeleting = true; - name = person.name; + var name = person.name; person.$remove().then(function() { self.isDeleting = false; var index = self.persons.indexOf(person); @@ -95,7 +85,7 @@ angular }); return d.promise; }, - createContact: function(person) { + createContact: function(person: any) { var d = $q.defer(); self.isSaving = true; Contact.save(person).$promise.then(function() { diff --git a/src/app/services/index.ts b/src/app/services/index.ts new file mode 100644 index 0000000..df2669f --- /dev/null +++ b/src/app/services/index.ts @@ -0,0 +1,2 @@ +import './contact.resource'; +import './contact.service'; diff --git a/src/index.html b/src/index.html index 25ff5e7..37bb54e 100644 --- a/src/index.html +++ b/src/index.html @@ -49,30 +49,9 @@ - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..3671d0b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,105 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es5", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "lib": ["es6", "dom"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ + "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "es6", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + "baseUrl": "", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + "typeRoots": [ + "./node_modules/@types" + ], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./dist/out-tsc", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..ada5a21 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,20 @@ +module.exports = { + entry: './src/app/main.ts', + output: { + filename: '../src/dist/bundle.js', + }, + optimization: { + minimize: false, + }, + resolve: { + extensions: ['.webpack.js', '.web.js', '.ts', '.tsx', '.js'], + }, + module: { + rules: [ + { + test: /\.tsx?$/, + loader: 'ts-loader', + } + ] + } +}