diff --git a/.firebase/hosting.ZGlzdA.cache b/.firebase/hosting.ZGlzdA.cache new file mode 100644 index 00000000..2b8d6547 --- /dev/null +++ b/.firebase/hosting.ZGlzdA.cache @@ -0,0 +1,84 @@ +runtime.09255a3e23c2d0a0.js,1772763336449,f27c7798f95b1affa1bf6ac8bdaa5eed920fe2453f748b9e46b006cb24fcdb6d +polyfills.c8055348faee040f.js,1772763336449,1e3d37bc80612a31f569cdc374dd765ff3d201d95ca884ee44bcab88ca190a74 +marker-icon.d577052aa271e13f.png,1772763336449,1a6e41ae33c0fd2c0a25649c041ed4a90bee68c5a5436fd8fa41741047a0cde8 +layers.ef6db8722c2c3f9a.png,1772763336449,355f4c5b9f8cb9eb8791dbb27e19c14866d91773e6f8ec00f46426aaf9ad9e41 +layers-2x.9859cd1231006a4a.png,1772763336449,7c9ef90b26a2d729e7fb8d0aae345e4dab1818dcd70f7aebd8870979821f8640 +index.html,1772763338255,8275a921b8712396bc759c5fef629c72bab773dbf8d72177da5f6b6239c56c78 +favicon.ico,1772672015981,5978e5ff3c809da711312a2b378839ac208d79c5b955e9927894edb2631d39d7 +3rdpartylicenses.txt,1772763336449,ea284519abad102c258443e58844466a642ce5bdac3401bda7ad43190aeb3222 +assets/scss/paper-dashboard.scss,1772672015940,ac0b404c9f6cbbffac139fe58e09a137314afcd5515aaee7d7a7f6455bdbc725 +assets/scss/paper-dashboard/_typography.scss,1772672015959,469973ee492fc6e97e21511939a14e6fc0174bee474f55b710064d91dc0f39e1 +assets/scss/paper-dashboard/_tables.scss,1772672015959,68fe9f19a050f3084743ab4a87e380ade6d1dcb8e631b7170c56494f2814a96c +assets/scss/paper-dashboard/_variables.scss,1772672015960,8ca5a68e71d18bb3021bab7efd0d1d95d1b5e2f2bc7de60ba2e7184e7235b20d +assets/scss/paper-dashboard/_sidebar-and-main-panel.scss,1772672015957,de24405fe4d52d1ab700350e4c7c4e42d30e634dff475ab8c762416f01d166b0 +assets/scss/paper-dashboard/_page-header.scss,1772672015956,9adbdeb303d9938b1d144c34fd7ec6b8ce12a70a125bfc929e67adf162fcc8b9 +assets/scss/paper-dashboard/_nucleo-outline.scss,1772672015955,6999358e1993c84e4801b45c454052b64592180bb432d13cf36ec13846a41278 +assets/scss/paper-dashboard/_responsive.scss,1772672015957,28ffb7a9ee70972e53f06d6365755537f8091c4d981abb2298bd0ea9a38c9e41 +assets/scss/paper-dashboard/_navbar.scss,1772672015954,8a25cd0bb5fad7d4b5a3b1fc49510aa6ec5f58a86130dbc3ff87045bc7d41dde +assets/scss/paper-dashboard/_mixins.scss,1772672015953,3c5686d3f2234fc68d720f06938a7c910e4c7c4c8469d7e624b565e92dbfeff0 +assets/scss/paper-dashboard/_misc.scss,1772672015952,566292dc1078eb8360cef5d6a509e7891c8cd28656a3e5ed263ffc23f2a2d9f4 +assets/scss/paper-dashboard/_images.scss,1772672015950,3a01737a78a6ed69b37679d9651cd438ee40ac4856bb0995f7e33fdcdca9d968 +assets/scss/paper-dashboard/_inputs.scss,1772672015951,80ddc61674ba44fb44ce9dcd97f2ad8b6cbfe488e5feacd3a801f20d55c8416c +assets/scss/paper-dashboard/_footers.scss,1772672015950,c172eecacc2d723112534a4c70b770b139a47626f36a985b5a7f03f6cd8db476 +assets/scss/paper-dashboard/_fixed-plugin.scss,1772672015949,39c7f17ca2e5443d586bdf61109e0821bd8e1a373aa29a4609c381c0f1161ad7 +assets/scss/paper-dashboard/_checkboxes-radio.scss,1772672015947,55250923dc7b42b3c87757a13208ae43210aae345713db969802fd3a259ecab8 +assets/scss/paper-dashboard/_dropdown.scss,1772672015948,6e046245c8cfde4aa3efbc923956ce0a01de5842cb32bb95727a705cc5b2293a +assets/scss/paper-dashboard/_cards.scss,1772672015945,f9086a0de74d107de9b16f1d005b13a0772307dba04a37d6046313eceee296db +assets/scss/paper-dashboard/_buttons.scss,1772672015943,97b5cde789a6a2b8c13cda25e5c23fac33a86cbde16118078d1997f93987dfab +assets/scss/paper-dashboard/_animated-buttons.scss,1772672015942,1ab885436d897aebbfe695865cda8e44b2bea2c53046c70d84cde6c296faaf6e +assets/scss/paper-dashboard/_alerts.scss,1772672015941,f9ffeb5837cc09926f3e90baf3e261d083ef78c80ea0bdb989b92930483ad3e9 +assets/scss/paper-dashboard/plugins/_plugin-perfect-scrollbar.scss,1772672015978,041a5a030a5a151944a110945b7edb46fc723ddcbcbf2e3b875d4932ab7fb06c +assets/scss/paper-dashboard/plugins/_plugin-animate-bootstrap-notify.scss,1772672015978,b2a2b197f9fa18a11269bc1242155131d27cae582d7a48fc759f28ee5aedbcae +assets/scss/paper-dashboard/mixins/_vendor-prefixes.scss,1772672015976,59591eb5087cd0fcc4bf7731649a96c4610f51e737d67251b0b8363a4c307756 +assets/scss/paper-dashboard/mixins/_transparency.scss,1772672015975,bc3ea20fbd35d6ba00fc339af44d6f1a8e31ef78ae05f95aa3041dd8e84c5e84 +assets/scss/paper-dashboard/mixins/_page-header.scss,1772672015975,27616d0be28c219d3a6826db0d1edbb4846cc0acf579ddedd17f476888116cc9 +assets/scss/paper-dashboard/mixins/_inputs.scss,1772672015974,358239179abe2b385ebf0849b9b52b0b910e81426ed0e4b7c38ef8a9996489cc +assets/scss/paper-dashboard/mixins/_dropdown.scss,1772672015973,4214215ee19f92989c22e7734c09839d60852e5a2c9f65abf7fc94d2b5fc2078 +assets/scss/paper-dashboard/mixins/_cards.scss,1772672015972,aef41bb677d15e5ad2799a0d2785d0d248c05e77944a565711605738d04b8d3c +assets/scss/paper-dashboard/mixins/_buttons.scss,1772672015972,c87ff7af6a74fe475a7f43bc81fec2b5fd95993ffb559f03f26fb29e905105c4 +assets/scss/paper-dashboard/cards/_card-user.scss,1772672015970,c76ce2053df89f45a0f424116d4ea04941ddc889763163eeebf7a4c438022ad7 +assets/scss/paper-dashboard/cards/_card-stats.scss,1772672015969,b9cc4817d2f89e390818f44b0eb2c0f0a79836f15f6fa4b23fbac1610e3ee7e6 +assets/scss/paper-dashboard/cards/_card-plain.scss,1772672015969,c610728c0951d750831a643b149714adfa6dcda4309fe4ff9d8eeab7ee382397 +assets/scss/paper-dashboard/cards/_card-map.scss,1772672015968,0d7c9226b51632d1ba23e6457343dc8a87f1a91acb594678059338f815dede45 +assets/scss/paper-dashboard/cards/_card-chart.scss,1772672015967,5de1ff5bb84f65454e820fa43c01cfcfb1c0a973074c7b4f5977b54dbaf9513b +nucleo-icons.f0cec18396767c90.ttf,1772763336449,b4c0377b0a6f1c55eb8a6861c65930b7d0751523f3c865c4f10dcad5edfad435 +nucleo-icons.df4fb178af8e863b.eot,1772763336449,68f913eff52214a77a24bfa3e97a989c923cd925f04b96ae347a4df7168f8917 +nucleo-icons.d34c7c344c14e3ba.woff,1772763336449,66acd8630854c9d48ae7ab175b5d729359a0a6c440bcef406656e96753c0e4b6 +nucleo-icons.5619d1ffcff41e66.woff2,1772763336449,0e76721fabc4c7d8d0c0249e21fd32612fc635ecb10d04d2423e96b99b89017e +832.3cbf3b4dc87ddff6.js,1772763336449,cbbdc891a04c059a1057d42cddb2720edc5b4b3df2b163aa0605b75a45996bdf +assets/scss/paper-dashboard/angular/_sidebar-and-main-panel.scss,1772672015966,67343891c48a966440833a913c5c54064adf80b8f217e533707c45717dcdff1e +assets/scss/paper-dashboard/angular/_plugin-ngx-toastr.scss,1772672015965,cc4036180a5eb071428cce32425fac7cab2e59dea1fe499b0c68e8a789ed9d71 +assets/scss/paper-dashboard/angular/_inputs.scss,1772672015964,7310a5ab0aba75326afc22736447b150223fcbcb7b08f23d6c3d428ba2228ace +assets/scss/paper-dashboard/angular/_fixed-plugin.scss,1772672015962,ac305a9046c2bac4764c5d8e0e59749ff1fabd9f296af3b030b6fe298a0f10fe +assets/scss/paper-dashboard/angular/_dropdown.scss,1772672015961,43e224f03b6ed9919a6bb6ce0165f4488810b464b8509b7c2db89dbaaad9a98a +assets/img/favicon.png,1772672015926,6ef4abaa26e564899cb22170d51ac50b90acaea91bd9f4e74afb84bf7af027bc +assets/img/default-avatar.png,1772672015905,027caf309a8c6cf9e294f22bedbd4ea415bc164f4c4068e0f88cd6e9c7e25207 +assets/img/apple-icon.png,1772672015900,d9e3168e236499246328d42a4cef0a54548bed63478698ff47e32bfaaefad7fb +assets/img/angular2-logo.png,1772672015899,af3fe6a52e20a9555d6f870417a66874beb6a9ff4cf6bfda880951c4768d1cb9 +assets/img/angular2-logo-white.png,1772672015898,a7ec8aa786c163f03f58a7eee089d96dcfcd2913edb6ba691d77a06d9e5c949f +assets/img/angular.png,1772672015898,0c090f0888b0c7f80a7616294af9f89c06515396c2f1a30892ca63a6d2967e54 +assets/css/demo.css,1772672015892,1d0709b3da87c1ab19ef720d981f5330730b3e6dc403f6bbc2faaf60a27f66d0 +assets/fonts/nucleo-icons.woff2,1772672015896,0e76721fabc4c7d8d0c0249e21fd32612fc635ecb10d04d2423e96b99b89017e +assets/fonts/nucleo-icons.woff,1772672015895,66acd8630854c9d48ae7ab175b5d729359a0a6c440bcef406656e96753c0e4b6 +assets/fonts/nucleo-icons.eot,1772672015893,68f913eff52214a77a24bfa3e97a989c923cd925f04b96ae347a4df7168f8917 +assets/fonts/nucleo-icons.ttf,1772672015894,b4c0377b0a6f1c55eb8a6861c65930b7d0751523f3c865c4f10dcad5edfad435 +assets/img/logo-small.png,1772672015938,ae9402d80bbd5edff3451044bf1a69cb9f3dfeeceaee0285458192fc73efaabd +assets/img/faces/erik-lucatero-2.jpg,1772672015920,c028c062b41024a45c4ddff46b920d7c7febb92a27ef62dbde76ad321ac6af1c +styles.09899aa9342a31fe.css,1772763336449,1defa849b2ca6314f5f2081b640ed4b1656b8ab6d39985223b2921e73af39c74 +assets/img/mike.jpg,1772672015938,0a16b32205458ff1ab990c62a25749618df98fd04bf059d07a2ac18654a39f2b +assets/img/faces/kaci-baum-2.jpg,1772672015925,3e28662f4de56668ad8417e7aeaf73eefbf3b54d05fba9cd5b775da33cdbf9f0 +assets/img/faces/joe-gardner-2.jpg,1772672015922,b392a62b3f568ff9c362da74f6ebb46baa3a74fc2ccef070930ba84686044f1e +assets/img/faces/clem-onojeghuo-3.jpg,1772672015915,d3dcd08aea8e710ba1b754fc7edcc3a8aed0351f0b6b71274470b15068620f41 +assets/img/faces/clem-onojeghuo-2.jpg,1772672015914,fc817d250d6a1456cbb8342c1e2b83c29edfcfc72bd7909c11b6d26f378004de +assets/img/faces/ayo-ogunseinde-2.jpg,1772672015909,704a012f1d23896542e1afc1e9f75daae1fcf17fad799a2dcc1594e5743791a9 +assets/img/faces/erik-lucatero-1.jpg,1772672015919,dd7b4a324f51f09eedcd82ade28699584f68239e53205bd4335985fcbe309638 +assets/img/faces/joe-gardner-1.jpg,1772672015921,cb196c4fccf2de612df8621a259e947abc1285bf5e91ae9222ea121f76e0c27e +assets/img/faces/kaci-baum-1.jpg,1772672015924,4bbf9279b216aede8b764cd615280594f3bd33a67dc565f02348b153b55be211 +assets/img/faces/clem-onojeghuo-4.jpg,1772672015917,33ff47bad4223a7048bc865d14648576c24a3adb4c91f1e1016bfbe979b11ed0 +assets/img/faces/ayo-ogunseinde-1.jpg,1772672015908,e67d5f8ec8f7eeeede19015753f6aee37668fc7a73d98962a35cd4604f7c7575 +main.8f5e36d40320d92e.js,1772763336449,65ff5b3ecf565f08a95e69c980b52fb0865902739b5e79721c50ec14c70dc961 +assets/img/bg5.jpg,1772672015902,2b4c253b7978645d3885afb6bf2ec4e919ec4fe949b470d1f839e6116d034dfc +assets/img/faces/clem-onojeghuo-1.jpg,1772672015911,6b1f1d2918a80bb62fb2cdf5a1ecd5dd10c58cd0abccc4fcdf6a4a739db56e49 +assets/img/damir-bosnjak.jpg,1772672015904,a581682d93597a725e6c1cd778f99798a0e8847a3c42170d65f984c74a73d7ce +assets/img/jan-sendereks.jpg,1772672015936,622a83598185a9d8e153bfa185f85a925acd510902d300ee68b991bddd34c8fb +assets/img/header.jpg,1772672015933,1e077b1282e7f93bbf2020b3ae6b6e98ea96f7058b193a9447cb9ad0d64c6638 diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 00000000..c249e442 --- /dev/null +++ b/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "angular-wifi" + } +} diff --git a/README.md b/README.md index 0ebac96d..f2ce68c0 100644 --- a/README.md +++ b/README.md @@ -1,218 +1,237 @@ -# [Paper Dashboard Angular](https://www.creative-tim.com/product/paper-dashboard-angular) -![version](https://img.shields.io/badge/version-2.4.0-blue.svg) ![license](https://img.shields.io/badge/license-MIT-blue.svg) [![GitHub issues open](https://img.shields.io/github/issues/creativetimofficial/paper-dashboard-angular.svg?maxAge=2592000)]() [![GitHub issues closed](https://img.shields.io/github/issues-closed-raw/creativetimofficial/paper-dashboard-angular.svg?maxAge=2592000)]() [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/E4aHAQy) +📡 Mapa Wi-Fi CDMX -![Product Gif](https://raw.githubusercontent.com/creativetimofficial/public-assets/master/paper-dashboard-angular/paper-dashboard-angular.gif) - -Paper Dashboard Angular is a Bootstrap Admin Template which combines soft colors with beautiful typography and spacious cards and graphics. It was built on top of Google's [Angular Framework](https://angular.io/) and the HTML version of [Paper Dashboard](https://www.creative-tim.com/product/paper-dashboard). It is a powerful dashboard but it is light and easy to be used. It has enough features to allow you to get the job done, but it is not crowded to the point where you can't find the files for a specific component. - - We like consistency and design that blends into its purpose. Paper Dashboard Angular is a perfect example of our most thoughtful work. It combines over a dozen components and plugins, while looking like everything fits together. For an easy start or inspiration for you project, we have also create a set of example pages, like the user settings or usage graphics. - - Paper Dashboard Angular is built using the same design language as [Paper Kit](http://www.creative-tim.com/product/paper-kit), which you can use for the presentation pages of your website. You can easily use them together, or pick between them depending on the project you have. - - **Example Pages** We wanted to fully display the power of this dashboard, so the kit comes packed with examples showing you how to use the components. - - ## Table of Contents - - * [Versions](#versions) - * [Demo](#demo) - * [Quick Start](#quick-start) - * [Deploy](#deploy) - * [Documentation](#documentation) - * [File Structure](#file-structure) - * [Browser Support](#browser-support) - * [Resources](#resources) - * [Reporting Issues](#reporting-issues) - * [Technical Support or Questions](#technical-support-or-questions) - * [Licensing](#licensing) - * [Useful Links](#useful-links) - - ## Versions - - [](https://www.creative-tim.com/product/paper-dashboard-2) - [](https://www.creative-tim.com/product/paper-dashboard-angular) - [](https://www.creative-tim.com/product/paper-dashboard-react) - - | HTML | Angular | React | - | --- | --- | --- | - | [![Paper Dashboard 2 HTML](https://s3.amazonaws.com/creativetim_bucket/products/86/thumb/opt_pd2_thumbnail.jpg)](https://www.creative-tim.com/product/paper-dashboard-2) | [![Paper Dashboard Angular](https://s3.amazonaws.com/creativetim_bucket/products/58/thumb/opt_pd_angular_thumbnail.jpg)](https://www.creative-tim.com/product/paper-dashboard-angular) | [![Paper Dashboard React](https://s3.amazonaws.com/creativetim_bucket/products/98/thumb/opt_pd_react_thumbnail.jpg)](https://www.creative-tim.com/product/paper-dashboard-react) | - - ## Demo - - | Dashboard | User Profile | Tables | Icons | Notifications | - | --- | --- | --- | --- | --- | - | [![Start page](https://raw.githubusercontent.com/creativetimofficial/public-assets/master/paper-dashboard-angular/dashboard.png?raw=true)](https://demos.creative-tim.com/paper-dashboard-angular/#/dashboard) | [![User profile page](https://raw.githubusercontent.com/creativetimofficial/public-assets/master/paper-dashboard-angular/user-profile.png?raw=true)](https://demos.creative-tim.com/paper-dashboard-angular/#/user-profile) | [![Tables page ](https://raw.githubusercontent.com/creativetimofficial/public-assets/master/paper-dashboard-angular/tables.png?raw=true)](https://demos.creative-tim.com/paper-dashboard-angular/#/table-list) | [![Icons Page](https://raw.githubusercontent.com/creativetimofficial/public-assets/master/paper-dashboard-angular/icons.png?raw=true)](https://demos.creative-tim.com/paper-dashboard-angular/#/maps) | [![Notifications page](https://raw.githubusercontent.com/creativetimofficial/public-assets/master/paper-dashboard-angular/notifications.png?raw=true)](https://demos.creative-tim.com/paper-dashboard-angular/#/notifications) - - [View More](https://demos.creative-tim.com/paper-dashboard-angular/#/dashboard). - - -## Quick start - -Quick start options: - -- [Download from Creative Tim](https://www.creative-tim.com/product/paper-dashboard-angular). - -## Deploy - -:rocket: You can deploy your own version of the template to Genezio with one click: - -[![Deploy to Genezio](https://raw.githubusercontent.com/Genez-io/graphics/main/svg/deploy-button.svg)](https://app.genez.io/start/deploy?repository=https://github.com/creativetimofficial/paper-dashboard-angular&utm_source=github&utm_medium=referral&utm_campaign=github-creativetim&utm_term=deploy-project&utm_content=button-head) - -## Terminal Commands - -1. Install NodeJs from [NodeJs Official Page](https://nodejs.org/en). -2. Open Terminal -3. Go to your file project -4. Run in terminal: ```npm install -g @angular/cli``` -5. Then: ```npm install``` -6. And: ```ng serve``` -7. Navigate to: [http://localhost:4200/](http://localhost:4200/) - -### What's included - -Within the download you'll find the following directories and files: - -``` -paper-dashboard-angular -├── CHANGELOG.md -├── ISSUES_TEMPLATE.md -├── LICENSE.md -├── README.md -├── angular.json -├── browserslist -├── documentation -│   └── tutorial-components.html -├── e2e -├── karma.conf.js -├── package-lock.json -├── package.json -├── protractor.conf.js -├── src -│   ├── app -│   │   ├── app.component.css -│   │   ├── app.component.html -│   │   ├── app.component.spec.ts -│   │   ├── app.component.ts -│   │   ├── app.module.ts -│   │   ├── app.routing.ts -│   │   ├── layouts -│   │   │   └── admin-layout -│   │   │   ├── admin-layout.component.html -│   │   │   ├── admin-layout.component.scss -│   │   │   ├── admin-layout.component.spec.ts -│   │   │   ├── admin-layout.component.ts -│   │   │   ├── admin-layout.module.ts -│   │   │   └── admin-layout.routing.ts -│   │   ├── pages -│   │   │   ├── dashboard -│   │   │   │   ├── dashboard.component.html -│   │   │   │   └── dashboard.component.ts -│   │   │   ├── icons -│   │   │   │   ├── icons.component.html -│   │   │   │   └── icons.component.ts -│   │   │   ├── maps -│   │   │   │   ├── maps.component.html -│   │   │   │   └── maps.component.ts -│   │   │   ├── notifications -│   │   │   │   ├── notifications.component.html -│   │   │   │   └── notifications.component.ts -│   │   │   ├── table -│   │   │   │   ├── table.component.html -│   │   │   │   └── table.component.ts -│   │   │   ├── typography -│   │   │   │   ├── typography.component.html -│   │   │   │   └── typography.component.ts -│   │   │   ├── upgrade -│   │   │   │   ├── upgrade.component.html -│   │   │   │   └── upgrade.component.ts -│   │   │   └── user -│   │   │   ├── user.component.html -│   │   │   └── user.component.ts -│   │   ├── shared -│   │   │   ├── fixedplugin -│   │   │   │   ├── fixedplugin.component.html -│   │   │   │   ├── fixedplugin.component.ts -│   │   │   │   └── fixedplugin.module.ts -│   │   │   ├── footer -│   │   │   │   ├── footer.component.html -│   │   │   │   ├── footer.component.ts -│   │   │   │   └── footer.module.ts -│   │   │   └── navbar -│   │   │   ├── navbar.component.html -│   │   │   ├── navbar.component.ts -│   │   │   └── navbar.module.ts -│   │   └── sidebar -│   │   ├── sidebar.component.html -│   │   ├── sidebar.component.ts -│   │   └── sidebar.module.ts -│   ├── assets -│   │   ├── css -│   │   ├── fonts -│   │   ├── img -│   │   └── scss -│   │   ├── paper-dashboard -│   │   └── paper-dashboard.scss -│   ├── environments -│   ├── favicon.ico -│   ├── index.html -│   ├── main.ts -│   ├── polyfills.ts -│   ├── styles.css -│   ├── test.ts -│   ├── tsconfig.app.json -│   ├── tsconfig.spec.json -│   └── typings.d.ts -├── tsconfig.json -└── tslint.json -``` -## Browser Support - -At present, we officially aim to support the last two versions of the following browsers: - - - - -## Resources -- Demo: https://demos.creative-tim.com/paper-dashboard-angular/#/dashboard -- Download Page: https://www.creative-tim.com/product/paper-dashboard-angular -- Documentation: https://demos.creative-tim.com/paper-dashboard-angular/#/documentation/tutorial -- License Agreement: https://www.creative-tim.com/license -- Support: https://www.creative-tim.com/contact-us -- Issues: [Github Issues Page](https://github.com/creativetimofficial/paper-dashboard-angular/issues) - -## Reporting Issues -We use GitHub Issues as the official bug tracker for the Paper Dashboard Angular. Here are some advices for our users that want to report an issue: - -1. Make sure that you are using the latest version of the Paper Dashboard Angular. Check the CHANGELOG from your dashboard on our [website](https://www.creative-tim.com/). -2. Providing us reproducible steps for the issue will shorten the time it takes for it to be fixed. -3. Some issues may be browser specific, so specifying in what browser you encountered the issue might help. - -## Technical Support or Questions - -If you have questions or need help integrating the product please [contact us](https://www.creative-tim.com/contact-us) instead of opening an issue. - -## Licensing - -- Copyright 2019 Creative Tim (https://www.creative-tim.com) -- Licensed under MIT (https://github.com/creativetimofficial/paper-dashboard-angular/blob/master/LICENSE.md) - -## Useful Links - -More products from Creative Tim: - -Tutorials: - -Freebies: - -Affiliate Program (earn money): - -Social Media: - -Twitter: - -Facebook: - -Dribbble: - -Google+: - -Instagram: +Aplicación web desarrollada con Angular y Firebase que permite visualizar más de 35,000 puntos Wi-Fi públicos de la Ciudad de México en un mapa interactivo, incluyendo funcionalidades de filtrado por alcaldía, clustering de marcadores y recomendación de puntos cercanos basada en geolocalización del usuario. + +🔗 URL pública: +https://angular-wifi.web.app/#/ + +🎯 Reto elegido y alcance + +El proyecto implementa el reto "Mapa Wi-Fi CDMX + recomendación", cuyo objetivo es mostrar los puntos de acceso Wi-Fi públicos en la Ciudad de México y recomendar ubicaciones cercanas al usuario. + +Funcionalidades implementadas + +✔ Visualización de más de 35,000 puntos Wi-Fi en un mapa. +✔ Clustering de marcadores para mejorar la visualización con grandes volúmenes de datos. +✔ Filtro por alcaldía para explorar zonas específicas. +✔ Recomendación de puntos cercanos basada en la ubicación del usuario. +✔ Cálculo de distancia aproximada entre el usuario y los puntos Wi-Fi. +✔ Vista de tabla con registros del dataset. + +Secciones de la aplicación + +Dashboard + Mapa con clustering + Filtrado por alcaldía + +![Mapa Clustering](src/assets/img/docs/mapClustering.png) + +![Mapa Clustering Filtrado por alcaldías](src/assets/img/docs/filterClusters.png) + +Maps + Obtiene la ubicación del usuario + Calcula los 5 puntos Wi-Fi más cercanos + Muestra el punto del usuario y los puntos recomendados + +![Mapa scope por aproximación](src/assets/img/docs/mapScore.png) + +![Puntos wifi más cercanos](src/assets/img/docs/wifi.png) + +Table + Muestra la cantidad total de puntos Wi-Fi + Lista los primeros 20 registros + +![Tabla](src/assets/img/docs/table.png) + +🧱 Arquitectura + +La aplicación sigue una arquitectura basada en frontend SPA con servicios externos. + +Usuario (Navegador) + │ + ▼ +Firebase Hosting + │ + ▼ +Aplicación Angular + │ │ + ▼ ▼ +Google Maps Firebase +JavaScript Realtime Database +API + +![arquitectura](src/assets/img/docs/mermaid-diagram.png) + +![arquitectura2](src/assets/img/docs/mermaid-diagram2.png) + +Componentes principales + +Angular Frontend + Renderiza la interfaz + Maneja navegación y lógica de UI + +Google Maps API + Renderiza el mapa + Maneja marcadores y clustering + +Firebase Realtime Database + Almacena el dataset de puntos Wi-Fi + +Firebase Hosting + Publica la aplicación web + +🧰 Tecnologías y dependencias + + Angular + + TypeScript + + Firebase Hosting + + Firebase Realtime Database + + Google Maps JavaScript API + + Marker Clustering + + Bootstrap (Paper Dashboard Template) + +✔ Compatibilidad + Node 18+ + Angular 17+ + +🗄 Modelo de datos + +La información de los puntos Wi-Fi proviene de un dataset público y se almacena en Firebase Realtime Database. + +Ejemplo de registro: +{ + "id": "MEX-AIM-AER-AICMT1-M-GW001", + "programa": "Aeropuerto", + "latitud": "19.432707", + "longitud": "-99.086743", + "alcaldia": "Venustiano Carranza" +} + +El dataset contiene aproximadamente 35,351 registros. + +🧭 Estado y navegación + +La aplicación utiliza Angular Router para manejar la navegación entre vistas. + +Rutas principales: +/dashboard → mapa con clustering y filtro por alcaldía +/maps → recomendación de Wi-Fi cercano al usuario +/table → visualización de registros en tabla + +⚙️ Decisiones técnicas + +Uso de clustering + Debido al gran volumen de datos (35k puntos), se implementó clustering de marcadores para mejorar rendimiento y legibilidad del mapa. + +Uso de Firebase Realtime Database + Se eligió Firebase para almacenar el dataset y permitir una integración sencilla con Angular. + +Aplicación pública sin autenticación + Debido a que los datos son públicos, no se implementó autenticación. + +Recomendación basada en proximidad + Se calculan las distancias entre el usuario y los puntos Wi-Fi y se muestran los cinco más cercanos. + +🔐 Seguridad y validaciones + +Las reglas de Firebase son: + +{ + "rules": { + ".read": true, + ".write": false + } +} + +Esto permite: + acceso público de lectura + evitar modificaciones en la base de datos + +⚡ Rendimiento + +Para mejorar la experiencia con grandes volúmenes de datos se implementaron: + Clustering de marcadores + Renderizado dinámico en Google Maps + Visualización limitada de registros en tabla + +♿ Accesibilidad + +Se tomaron en cuenta algunos principios básicos de accesibilidad: + Navegación clara entre secciones + Uso de contraste adecuado en los mapas + Información textual acompañando los marcadores + +📈 Escalabilidad y mantenimiento + +El sistema puede escalar mediante: + paginación en la vista de tabla + consultas por zona geográfica + implementación de índices en base de datos + migración a Firestore si se requiere mayor flexibilidad en consultas + +🤖 Uso de IA + +La inteligencia artificial fue utilizada como apoyo en varias etapas del desarrollo: + + selección del reto a implementar + "Tengo esta lista de proyectos *lista*, cuál consideras que sea un reto? 2.- Crees que para el que consideraste un reto pueda ayudarme a hacerlo al 100%? 2.- Crees que para el que consideraste un reto necesite buscar una plantilla o sería mejor hacer el diseño completo? Ten en cuenta que tengo max de entregar el viernes" + + generación de ejemplos de código + "Si, quiero que me enseñes a hacerlo, ademas de eso solo ten en cuenta que radico en córdoba y no podría probar la proximidad a menos que se lo mande a alguien que sepa que vive por alla o dame opciones de prueba, Otra cosa, que es más retador de las 3 opciones que me dieron a elegir para hacer :clustering / score simple por proximidad / lista ordenada por score?" + + integración de Google Maps API + "¿Cómo integrar Google Maps JavaScript API en un proyecto Angular para mostrar marcadores a partir de coordenadas almacenadas en Firebase Realtime Database?" + + implementación de clustering + "1.- Es normal que salgan de color rojo y azul? 2.- A que te refieres con los colores verde, naranja y rojo? 3.- Y estas mejoras que mencionaste "const bounds = new google.maps.LatLngBounds(); this.markers.forEach(marker => { bounds.extend(marker.getPosition()); }); this.map.fitBounds(bounds);" y esta "this.cluster = new MarkerClusterer({ map: this.map, markers: this.markers, algorithmOptions: { maxZoom: 15 } });" en qué beneficia y en donde irían?" + + cálculo de proximidad entre ubicaciones + "Si me gusta como se ve cuando selecciono alcaldías, el problema es que al principio señalaba a la ciudad de mexico const cdmx = new google.maps.LatLng(19.4326, -99.1332); y ahora me aparece mucho mas de lejos y hasta que filtro, se acerca" + + configuración de Firebase y despliegue + "Visualmente ya quedó lo principal, todavia me falta configurar lo que va a hacer la página web, necesito subirlo antes a Firebase (Hosting/App Hosting), Firestore o Realtime DB, no sé cual sea la mejor opción o después de programar?" + + También se utilizó para resolver errores técnicos durante el desarrollo. + "Te voy a hacer una pregunta que siento que no tiene mucho que ver con lo que estaba haciendo pero en #/maps, tengo el mismo mapa con los puntos y cuando selecciono #/dashboard donde esta el clustering no me muestra el mapa y regreso a la otra ventana y ya no se muestra tampoco el mapa, sabes a que se debe? algo de lo configurado hace que no se muestre el mapa hasta que recargo la página? + +Se mantuvo una revisión manual del código generado para asegurar su funcionamiento y adaptación al proyecto. + +🚀 Instalación y ejecución + +1️⃣ Clonar repositorio: git clone [URL_DEL_REPOSITORIO] +2️⃣ Instalar dependencias: npm install +3️⃣ Ejecutar aplicación: ng serve / npm start +4️⃣ Abrir en navegador: http://localhost:4200 + +🚀 Despliegue + +La aplicación se despliega en Firebase Hosting. + +Pasos principales: + firebase login + firebase init + firebase deploy + +⚠ Limitaciones + + No permite agregar nuevos puntos Wi-Fi + + No cuenta con paginación avanzada + + No incluye búsqueda en tabla + +🔮 Mejoras futuras + + búsqueda por nombre o coordenadas + + paginación en tabla + + filtros adicionales + + optimización de consultas por región diff --git a/angular.json b/angular.json index 0138ae15..e2373440 100644 --- a/angular.json +++ b/angular.json @@ -27,7 +27,9 @@ "node_modules/bootstrap/scss/bootstrap.scss", "src/assets/scss/paper-dashboard.scss", "node_modules/ngx-toastr/toastr.css", - "src/assets/css/demo.css" + "src/assets/css/demo.css", + "node_modules/leaflet/dist/leaflet.css", + "src/styles.css" ], "scripts": [] }, @@ -161,4 +163,4 @@ "cli": { "analytics": false } -} +} \ No newline at end of file diff --git a/firebase.json b/firebase.json new file mode 100644 index 00000000..631738b6 --- /dev/null +++ b/firebase.json @@ -0,0 +1,16 @@ +{ + "hosting": { + "public": "dist", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + } +} \ No newline at end of file diff --git a/package.json b/package.json index 47451989..d7b049ca 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,10 @@ "@angular/platform-browser": "^14.2.0", "@angular/platform-browser-dynamic": "^14.2.0", "@angular/router": "^14.2.0", + "@googlemaps/markerclusterer": "^2.6.2", "@ng-bootstrap/ng-bootstrap": "12.0.1", - "@popperjs/core": "^2.11.4", "@ngui/map": "0.30.3", + "@popperjs/core": "^2.11.4", "@types/googlemaps": "3.39.14", "arrive": "2.4.1", "bootstrap": "4.5.2", @@ -37,21 +38,24 @@ "chartist": "0.11.4", "express": "4.18.0", "jquery": "3.5.1", + "leaflet": "^1.9.4", "moment": "2.29.1", "ngx-toastr": "14.3.0", "rxjs": "~7.5.0", - "zone.js": "~0.11.4", - "web-animations-js": "2.3.2" + "web-animations-js": "2.3.2", + "zone.js": "~0.11.4" }, "devDependencies": { "@angular-devkit/build-angular": "^14.2.7", "@angular/cli": "~14.2.7", "@angular/compiler-cli": "^14.2.0", "@angular/language-service": "14.2.0", + "@types/google.maps": "^3.58.1", "@types/jasmine": "~4.0.0", "@types/jasminewd2": "~2.0.10", "@types/node": "^17.0.21", "codelyzer": "6.0.2", + "cross-env": "^7.0.3", "jasmine-core": "~4.4.0", "jasmine-spec-reporter": "~7.0.0", "karma": "~6.4.0", @@ -62,7 +66,18 @@ "karma-jasmine-html-reporter": "~2.0.0", "protractor": "7.0.0", "ts-node": "~10.9.1", - "typescript": "~4.7.2", - "cross-env": "^7.0.3" - } + "typescript": "~4.7.2" + }, + "description": "![version](https://img.shields.io/badge/version-2.4.0-blue.svg) ![license](https://img.shields.io/badge/license-MIT-blue.svg) [![GitHub issues open](https://img.shields.io/github/issues/creativetimofficial/paper-dashboard-angular.svg?maxAge=2592000)]() [![GitHub issues closed](https://img.shields.io/github/issues-closed-raw/creativetimofficial/paper-dashboard-angular.svg?maxAge=2592000)]() [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/E4aHAQy)", + "main": "karma.conf.js", + "repository": { + "type": "git", + "url": "git+https://github.com/ErikaViveroz/App-Angular-con-IA.git" + }, + "keywords": [], + "author": "", + "bugs": { + "url": "https://github.com/ErikaViveroz/App-Angular-con-IA/issues" + }, + "homepage": "https://github.com/ErikaViveroz/App-Angular-con-IA#readme" } diff --git a/public/index.html b/public/index.html new file mode 100644 index 00000000..4237f9e0 --- /dev/null +++ b/public/index.html @@ -0,0 +1,89 @@ + + + + + + Welcome to Firebase Hosting + + + + + + + + + + + + + + + + + + + +
+

Welcome

+

Firebase Hosting Setup Complete

+

You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!

+ Open Hosting Documentation +
+

Firebase SDK Loading…

+ + + + diff --git a/src/app/app.component.css b/src/app/app.component.css index e69de29b..84827015 100644 --- a/src/app/app.component.css +++ b/src/app/app.component.css @@ -0,0 +1,4 @@ +.map { + height: 500px; + width: 100%; +} \ No newline at end of file diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 5cc6ad69..ac44bae9 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -5,13 +5,15 @@ import { ToastrModule } from "ngx-toastr"; import { SidebarModule } from './sidebar/sidebar.module'; import { FooterModule } from './shared/footer/footer.module'; -import { NavbarModule} from './shared/navbar/navbar.module'; -import { FixedPluginModule} from './shared/fixedplugin/fixedplugin.module'; +import { NavbarModule } from './shared/navbar/navbar.module'; +import { FixedPluginModule } from './shared/fixedplugin/fixedplugin.module'; import { AppComponent } from './app.component'; import { AppRoutes } from './app.routing'; import { AdminLayoutComponent } from './layouts/admin-layout/admin-layout.component'; +import { HttpClientModule } from '@angular/common/http'; +import { FormsModule } from '@angular/forms'; @NgModule({ @@ -21,14 +23,16 @@ import { AdminLayoutComponent } from './layouts/admin-layout/admin-layout.compon ], imports: [ BrowserAnimationsModule, - RouterModule.forRoot(AppRoutes,{ + RouterModule.forRoot(AppRoutes, { useHash: true }), SidebarModule, NavbarModule, ToastrModule.forRoot(), FooterModule, - FixedPluginModule + FixedPluginModule, + HttpClientModule, + FormsModule ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/pages/dashboard/dashboard.component.html b/src/app/pages/dashboard/dashboard.component.html index d7072afc..dc09aa30 100644 --- a/src/app/pages/dashboard/dashboard.component.html +++ b/src/app/pages/dashboard/dashboard.component.html @@ -1,171 +1,35 @@ -
-
-
-
-
-
-
- -
-
-
-
-

Capacity

-

150GB -

-

-
-
-
- -
-
-
-
-
-
-
-
- -
-
-
-
-

Revenue

-

$ 1,345 -

-

-
-
-
- -
-
-
-
-
-
-
-
- -
-
-
-
-

Errors

-

23 -

-

-
-
-
- -
-
-
-
-
-
-
-
- -
-
-
-
-

Followers

-

+45K -

-

-
-
-
- -
-
-
-
-
-
Users Behavior
-

24 Hours performance

-
-
- -
- -
-
-
-
-
-
-
-
Email Statistics
-

Last Campaign Performance

-
-
- -
- -
-
-
-
+ +
+
-
NASDAQ: AAPL
-

Line Chart with Points

+ Google Maps
+
- -
- + +
+
+ + + +
+
+ +
+ +
+
+
-
+ \ No newline at end of file diff --git a/src/app/pages/dashboard/dashboard.component.ts b/src/app/pages/dashboard/dashboard.component.ts index 8ba58148..cd56c0e7 100644 --- a/src/app/pages/dashboard/dashboard.component.ts +++ b/src/app/pages/dashboard/dashboard.component.ts @@ -1,209 +1,128 @@ -import { Component, OnInit } from '@angular/core'; -import Chart from 'chart.js'; +/// +import { Component, OnInit, AfterViewInit, ViewChild, ElementRef } from '@angular/core'; +import { MarkerClusterer } from "@googlemaps/markerclusterer"; +import { HttpClient } from '@angular/common/http'; + +declare var google: any; @Component({ - selector: 'dashboard-cmp', - moduleId: module.id, - templateUrl: 'dashboard.component.html' + selector: 'dashboard-cmp', + moduleId: module.id, + templateUrl: 'dashboard.component.html' }) -export class DashboardComponent implements OnInit{ - - public canvas : any; - public ctx; - public chartColor; - public chartEmail; - public chartHours; - - ngOnInit(){ - this.chartColor = "#FFFFFF"; - - this.canvas = document.getElementById("chartHours"); - this.ctx = this.canvas.getContext("2d"); - - this.chartHours = new Chart(this.ctx, { - type: 'line', - - data: { - labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct"], - datasets: [{ - borderColor: "#6bd098", - backgroundColor: "#6bd098", - pointRadius: 0, - pointHoverRadius: 0, - borderWidth: 3, - data: [300, 310, 316, 322, 330, 326, 333, 345, 338, 354] - }, - { - borderColor: "#f17e5d", - backgroundColor: "#f17e5d", - pointRadius: 0, - pointHoverRadius: 0, - borderWidth: 3, - data: [320, 340, 365, 360, 370, 385, 390, 384, 408, 420] - }, - { - borderColor: "#fcc468", - backgroundColor: "#fcc468", - pointRadius: 0, - pointHoverRadius: 0, - borderWidth: 3, - data: [370, 394, 415, 409, 425, 445, 460, 450, 478, 484] - } - ] - }, - options: { - legend: { - display: false - }, - - tooltips: { - enabled: false - }, - - scales: { - yAxes: [{ - - ticks: { - fontColor: "#9f9f9f", - beginAtZero: false, - maxTicksLimit: 5, - //padding: 20 - }, - gridLines: { - drawBorder: false, - zeroLineColor: "#ccc", - color: 'rgba(255,255,255,0.05)' - } - - }], - - xAxes: [{ - barPercentage: 1.6, - gridLines: { - drawBorder: false, - color: 'rgba(255,255,255,0.1)', - zeroLineColor: "transparent", - display: false, - }, - ticks: { - padding: 20, - fontColor: "#9f9f9f" - } - }] - }, - } +export class DashboardComponent implements OnInit, AfterViewInit { + + @ViewChild('mapContainer') mapElement!: ElementRef; + + wifiPoints: any[] = []; + map!: google.maps.Map; + markers: google.maps.Marker[] = []; + cluster!: MarkerClusterer; + alcaldias: string[] = []; + selectedAlcaldia: string = ''; + + constructor(private http: HttpClient) { } + + ngOnInit() { } + + ngAfterViewInit() { + + const cdmx = new google.maps.LatLng(19.4326, -99.1332); + + const mapOptions = { + zoom: 12, + center: cdmx, + scrollwheel: false + }; + + this.map = new google.maps.Map( + this.mapElement.nativeElement, + mapOptions + ); + + this.loadWifiPoints(); + } + + loadWifiPoints() { + this.http.get( + 'https://wifi-cdmx-24918-default-rtdb.firebaseio.com/.json' + ) + .subscribe(data => { + + const array = Object.values(data); + + this.wifiPoints = array; + + this.alcaldias = [...new Set(array.map((p: any) => p.alcaldia))]; + + this.renderMarkers(this.wifiPoints); + }); + } + renderMarkers(points: any[], adjustZoom: boolean = false) { - this.canvas = document.getElementById("chartEmail"); - this.ctx = this.canvas.getContext("2d"); - this.chartEmail = new Chart(this.ctx, { - type: 'pie', - data: { - labels: [1, 2, 3], - datasets: [{ - label: "Emails", - pointRadius: 0, - pointHoverRadius: 0, - backgroundColor: [ - '#e3e3e3', - '#4acccd', - '#fcc468', - '#ef8157' - ], - borderWidth: 0, - data: [342, 480, 530, 120] - }] - }, + // limpiar marcadores + this.markers.forEach(m => m.setMap(null)); + this.markers = []; - options: { - - legend: { - display: false - }, - - pieceLabel: { - render: 'percentage', - fontColor: ['white'], - precision: 2 - }, - - tooltips: { - enabled: false - }, - - scales: { - yAxes: [{ - - ticks: { - display: false - }, - gridLines: { - drawBorder: false, - zeroLineColor: "transparent", - color: 'rgba(255,255,255,0.05)' - } - - }], - - xAxes: [{ - barPercentage: 1.6, - gridLines: { - drawBorder: false, - color: 'rgba(255,255,255,0.1)', - zeroLineColor: "transparent" - }, - ticks: { - display: false, - } - }] - }, - } + // limpiar cluster anterior + if (this.cluster) { + this.cluster.clearMarkers(); + } + + points.forEach(point => { + + const marker = new google.maps.Marker({ + position: { + lat: parseFloat(point.latitud), + lng: parseFloat(point.longitud) + }, + title: point.id }); - var speedCanvas = document.getElementById("speedChart"); - - var dataFirst = { - data: [0, 19, 15, 20, 30, 40, 40, 50, 25, 30, 50, 70], - fill: false, - borderColor: '#fbc658', - backgroundColor: 'transparent', - pointBorderColor: '#fbc658', - pointRadius: 4, - pointHoverRadius: 4, - pointBorderWidth: 8, - }; - - var dataSecond = { - data: [0, 5, 10, 12, 20, 27, 30, 34, 42, 45, 55, 63], - fill: false, - borderColor: '#51CACF', - backgroundColor: 'transparent', - pointBorderColor: '#51CACF', - pointRadius: 4, - pointHoverRadius: 4, - pointBorderWidth: 8 - }; - - var speedData = { - labels: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], - datasets: [dataFirst, dataSecond] - }; - - var chartOptions = { - legend: { - display: false, - position: 'top' - } - }; - - var lineChart = new Chart(speedCanvas, { - type: 'line', - hover: false, - data: speedData, - options: chartOptions + this.markers.push(marker); + + }); + + // crear cluster + this.cluster = new MarkerClusterer({ + map: this.map, + markers: this.markers, + algorithmOptions: { + maxZoom: 15 + } + }); + + // centrado automático + if (adjustZoom && this.markers.length > 0) { + + const bounds = new google.maps.LatLngBounds(); + + this.markers.forEach(marker => { + bounds.extend(marker.getPosition()); }); + + this.map.fitBounds(bounds); + } + + } + + filterByAlcaldia() { + + if (!this.selectedAlcaldia) { + this.renderMarkers(this.wifiPoints); + return; } + + const filtered = this.wifiPoints.filter( + p => p.alcaldia === this.selectedAlcaldia + ); + + this.renderMarkers(filtered, true); + + } + } diff --git a/src/app/pages/maps/maps.component.html b/src/app/pages/maps/maps.component.html index 12352b6a..5bb2b462 100644 --- a/src/app/pages/maps/maps.component.html +++ b/src/app/pages/maps/maps.component.html @@ -1,13 +1,51 @@ -
Google Maps
+
+ + 📶 WiFi recomendado: + {{wifiPoints[0].id}} + +
+ + Alcaldía: {{wifiPoints[0].alcaldia}} + +
+ + Distancia: + {{wifiPoints[0].distance | number:'1.2-2'}} km + +
-
+
+
+
-
+ \ No newline at end of file diff --git a/src/app/pages/maps/maps.component.ts b/src/app/pages/maps/maps.component.ts index 97ed988b..44e49041 100644 --- a/src/app/pages/maps/maps.component.ts +++ b/src/app/pages/maps/maps.component.ts @@ -1,4 +1,5 @@ -import { Component,OnInit } from '@angular/core'; +import { Component, OnInit, AfterViewInit, ViewChild, ElementRef } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; declare var google: any; @@ -8,24 +9,120 @@ declare var google: any; templateUrl: 'maps.component.html' }) -export class MapsComponent implements OnInit { - ngOnInit() { - var myLatlng = new google.maps.LatLng(40.748817, -73.985428); - var mapOptions = { - zoom: 13, - center: myLatlng, - scrollwheel: false, //we disable de scroll over the map, it is a really annoing when you scroll through page - styles: [{"featureType":"water","stylers":[{"saturation":43},{"lightness":-11},{"hue":"#0088ff"}]},{"featureType":"road","elementType":"geometry.fill","stylers":[{"hue":"#ff0000"},{"saturation":-100},{"lightness":99}]},{"featureType":"road","elementType":"geometry.stroke","stylers":[{"color":"#808080"},{"lightness":54}]},{"featureType":"landscape.man_made","elementType":"geometry.fill","stylers":[{"color":"#ece2d9"}]},{"featureType":"poi.park","elementType":"geometry.fill","stylers":[{"color":"#ccdca1"}]},{"featureType":"road","elementType":"labels.text.fill","stylers":[{"color":"#767676"}]},{"featureType":"road","elementType":"labels.text.stroke","stylers":[{"color":"#ffffff"}]},{"featureType":"poi","stylers":[{"visibility":"off"}]},{"featureType":"landscape.natural","elementType":"geometry.fill","stylers":[{"visibility":"on"},{"color":"#b8cb93"}]},{"featureType":"poi.park","stylers":[{"visibility":"on"}]},{"featureType":"poi.sports_complex","stylers":[{"visibility":"on"}]},{"featureType":"poi.medical","stylers":[{"visibility":"on"}]},{"featureType":"poi.business","stylers":[{"visibility":"simplified"}]}] - - } - var map = new google.maps.Map(document.getElementById("map"), mapOptions); - - var marker = new google.maps.Marker({ - position: myLatlng, - title:"Hello World!" +export class MapsComponent implements OnInit, AfterViewInit { + + @ViewChild('mapContainer') mapElement!: ElementRef; + + map: any; + wifiPoints: any[] = []; + + constructor(private http: HttpClient) { } + + ngOnInit() { } + + ngAfterViewInit() { + const cdmx = new google.maps.LatLng(19.4326, -99.1332); + + const mapOptions = { + zoom: 12, + center: cdmx, + scrollwheel: false + }; + + this.map = new google.maps.Map( + this.mapElement.nativeElement, + mapOptions + ); + + this.loadWifiPoints(); + + } + + loadWifiPoints() { + + navigator.geolocation.getCurrentPosition(position => { + + const userLocation = { + lat: position.coords.latitude, + lng: position.coords.longitude + }; + this.map.setCenter(userLocation); + this.map.setZoom(17); + + + // marcador del usuario + new google.maps.Marker({ + position: userLocation, + map: this.map, + title: "Tu ubicación", + icon: { + url: "http://maps.google.com/mapfiles/ms/icons/blue-dot.png" + } + }); + + this.http.get( + 'https://wifi-cdmx-24918-default-rtdb.firebaseio.com/.json' + ) + .subscribe(data => { + + const array = Object.values(data); + + data.forEach(point => { + + const distance = this.getDistance( + userLocation.lat, + userLocation.lng, + parseFloat(point.latitud), + parseFloat(point.longitud) + ); + + point.distance = distance; + + }); + + // ordenar por distancia + this.wifiPoints = data.sort((a, b) => a.distance - b.distance); + + this.wifiPoints.slice(0, 5).forEach(point => { + + new google.maps.Marker({ + position: { + lat: parseFloat(point.latitud), + lng: parseFloat(point.longitud) + }, + map: this.map, + title: point.id + }); + + }); + + }); + }); - // To add the marker to the map, call setMap(); - marker.setMap(map); } + + getDistance(lat1: number, lon1: number, lat2: number, lon2: number) { + + const R = 6371; // radio de la tierra km + + const dLat = this.deg2rad(lat2 - lat1); + const dLon = this.deg2rad(lon2 - lon1); + + const a = + Math.sin(dLat / 2) * Math.sin(dLat / 2) + + Math.cos(this.deg2rad(lat1)) * + Math.cos(this.deg2rad(lat2)) * + Math.sin(dLon / 2) * + Math.sin(dLon / 2); + + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + + return R * c; + } + + deg2rad(deg: number) { + return deg * (Math.PI / 180); + } + } diff --git a/src/app/pages/table/table.component.html b/src/app/pages/table/table.component.html index e5f44e39..f6b702ad 100644 --- a/src/app/pages/table/table.component.html +++ b/src/app/pages/table/table.component.html @@ -1,257 +1,65 @@
+
+
+
-

Simple Table

+

Puntos WiFi

+ + + +

+ Mostrando {{filteredWifiPoints.length}} de {{wifiPoints.length}} puntos +

+
+
+
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - + + + - - - - - - - - - - - - - -
- Name - - Country - - City - - Salary -
- Dakota Rice - - Niger - - Oud-Turnhout - - $36,738 -
- Minerva Hooper - - Curaçao - - Sinaai-Waas - - $23,789 -
- Sage Rodriguez - - Netherlands - - Baileux - - $56,142 -
- Philip Chaney - - Korea, South - - Overland Park - - $38,735 -
- Doris Greene - - Malawi - - Feldkirchen in Kärnten - - $63,542 - NombreLatitudLongitud
- Mason Porter - - Chile - - Gloucester - - $78,615 -
- Jon Porter - - Portugal - - Gloucester - - $98,615 -
-
-
-
-
-
-
-
-

Table on Plain Background

-

Here is a subtitle for this table

-
-
-
- - - - - - + - - - + + + - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +
- Name - - Country - - City - - Salary -
- Dakota Rice - - Niger -
- Oud-Turnhout - - $36,738 + {{wifi.id}}
- Minerva Hooper + {{wifi.latitud}} - Curaçao - - Sinaai-Waas - - $23,789 -
- Sage Rodriguez - - Netherlands - - Baileux - - $56,142 -
- Philip Chaney - - Korea, South - - Overland Park - - $38,735 -
- Doris Greene - - Malawi - - Feldkirchen in Kärnten - - $63,542 -
- Mason Porter - - Chile - - Gloucester - - $78,615 -
- Jon Porter - - Portugal - - Gloucester - - $98,615 + {{wifi.longitud}}
+
+
+
+
-
+ + \ No newline at end of file diff --git a/src/app/pages/table/table.component.ts b/src/app/pages/table/table.component.ts index bc747e50..85b1330b 100644 --- a/src/app/pages/table/table.component.ts +++ b/src/app/pages/table/table.component.ts @@ -1,41 +1,62 @@ import { Component, OnInit } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; -declare interface TableData { - headerRow: string[]; - dataRows: string[][]; +export interface WifiPoint { + id: number; + latitud: string; + longitud: string; } @Component({ selector: 'table-cmp', - moduleId: module.id, templateUrl: 'table.component.html' }) -export class TableComponent implements OnInit{ - public tableData1: TableData; - public tableData2: TableData; - ngOnInit(){ - this.tableData1 = { - headerRow: [ 'ID', 'Name', 'Country', 'City', 'Salary'], - dataRows: [ - ['1', 'Dakota Rice', 'Niger', 'Oud-Turnhout', '$36,738'], - ['2', 'Minerva Hooper', 'Curaçao', 'Sinaai-Waas', '$23,789'], - ['3', 'Sage Rodriguez', 'Netherlands', 'Baileux', '$56,142'], - ['4', 'Philip Chaney', 'Korea, South', 'Overland Park', '$38,735'], - ['5', 'Doris Greene', 'Malawi', 'Feldkirchen in Kärnten', '$63,542'], - ['6', 'Mason Porter', 'Chile', 'Gloucester', '$78,615'] - ] - }; - this.tableData2 = { - headerRow: [ 'ID', 'Name', 'Salary', 'Country', 'City' ], - dataRows: [ - ['1', 'Dakota Rice','$36,738', 'Niger', 'Oud-Turnhout' ], - ['2', 'Minerva Hooper', '$23,789', 'Curaçao', 'Sinaai-Waas'], - ['3', 'Sage Rodriguez', '$56,142', 'Netherlands', 'Baileux' ], - ['4', 'Philip Chaney', '$38,735', 'Korea, South', 'Overland Park' ], - ['5', 'Doris Greene', '$63,542', 'Malawi', 'Feldkirchen in Kärnten', ], - ['6', 'Mason Porter', '$78,615', 'Chile', 'Gloucester' ] - ] - }; +export class TableComponent implements OnInit { + + wifiPoints: WifiPoint[] = []; + filteredWifiPoints: WifiPoint[] = []; + + searchText: string = ''; + totalPoints = 0; + limit = 20; + + constructor(private http: HttpClient) { } + + ngOnInit() { + + this.http.get('https://wifi-cdmx-24918-default-rtdb.firebaseio.com/.json') + .subscribe(data => { + // Convertimos el objeto de Firebase a un array + const array = Object.values(data); + + this.wifiPoints = data; + this.totalPoints = data.length; + + // mostrar primeros registros + this.filteredWifiPoints = this.wifiPoints.slice(0, this.limit); + + }); + } -} + + search() { + + if (!this.searchText) { + + this.filteredWifiPoints = this.wifiPoints.slice(0, this.limit); + return; + + } + + this.filteredWifiPoints = this.wifiPoints + .filter(p => + p.id.toString().includes(this.searchText) || + p.latitud.toString().includes(this.searchText) || + p.longitud.toString().includes(this.searchText) + ) + .slice(0, this.limit); + + } + +} \ No newline at end of file diff --git a/src/app/shared/navbar/navbar.component.html b/src/app/shared/navbar/navbar.component.html index 185096d7..8c215f65 100644 --- a/src/app/shared/navbar/navbar.component.html +++ b/src/app/shared/navbar/navbar.component.html @@ -1,4 +1,3 @@ - + \ No newline at end of file diff --git a/src/app/sidebar/sidebar.component.html b/src/app/sidebar/sidebar.component.html index 04a15dc8..b4387485 100644 --- a/src/app/sidebar/sidebar.component.html +++ b/src/app/sidebar/sidebar.component.html @@ -1,21 +1,20 @@ - + + \ No newline at end of file diff --git a/src/app/sidebar/sidebar.component.ts b/src/app/sidebar/sidebar.component.ts index 3c327d3a..125f4471 100644 --- a/src/app/sidebar/sidebar.component.ts +++ b/src/app/sidebar/sidebar.component.ts @@ -9,14 +9,9 @@ export interface RouteInfo { } export const ROUTES: RouteInfo[] = [ - { path: '/dashboard', title: 'Dashboard', icon:'nc-bank', class: '' }, - { path: '/icons', title: 'Icons', icon:'nc-diamond', class: '' }, - { path: '/maps', title: 'Maps', icon:'nc-pin-3', class: '' }, - { path: '/notifications', title: 'Notifications', icon:'nc-bell-55', class: '' }, - { path: '/user', title: 'User Profile', icon:'nc-single-02', class: '' }, - { path: '/table', title: 'Table List', icon:'nc-tile-56', class: '' }, - { path: '/typography', title: 'Typography', icon:'nc-caps-small', class: '' }, - { path: '/upgrade', title: 'Upgrade to PRO', icon:'nc-spaceship', class: 'active-pro' }, + { path: '/dashboard', title: 'Dashboard Clustering', icon: 'nc-bank', class: '' }, + { path: '/maps', title: 'Maps (Score simple por proximidad)', icon: 'nc-pin-3', class: '' }, + { path: '/table', title: 'Table List', icon: 'nc-tile-56', class: '' }, ]; @Component({ diff --git a/src/assets/img/docs/filterClusters.png b/src/assets/img/docs/filterClusters.png new file mode 100644 index 00000000..7e63f43d Binary files /dev/null and b/src/assets/img/docs/filterClusters.png differ diff --git a/src/assets/img/docs/mapClustering.png b/src/assets/img/docs/mapClustering.png new file mode 100644 index 00000000..7c27a76e Binary files /dev/null and b/src/assets/img/docs/mapClustering.png differ diff --git a/src/assets/img/docs/mapScore.png b/src/assets/img/docs/mapScore.png new file mode 100644 index 00000000..49743ef3 Binary files /dev/null and b/src/assets/img/docs/mapScore.png differ diff --git a/src/assets/img/docs/mermaid-diagram.png b/src/assets/img/docs/mermaid-diagram.png new file mode 100644 index 00000000..2622c18d Binary files /dev/null and b/src/assets/img/docs/mermaid-diagram.png differ diff --git a/src/assets/img/docs/mermaid-diagram2.png b/src/assets/img/docs/mermaid-diagram2.png new file mode 100644 index 00000000..af379f45 Binary files /dev/null and b/src/assets/img/docs/mermaid-diagram2.png differ diff --git a/src/assets/img/docs/table.png b/src/assets/img/docs/table.png new file mode 100644 index 00000000..c6d4faf6 Binary files /dev/null and b/src/assets/img/docs/table.png differ diff --git a/src/assets/img/docs/wifi.png b/src/assets/img/docs/wifi.png new file mode 100644 index 00000000..8f964039 Binary files /dev/null and b/src/assets/img/docs/wifi.png differ diff --git a/src/environments/environment.ts b/src/environments/environment.ts index b7f639ae..439fee6e 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -4,5 +4,14 @@ // The list of which env maps to which file can be found in `.angular-cli.json`. export const environment = { - production: false -}; + production: false, + firebase: { + apiKey: "AIzaSyXXXX", + authDomain: "wifi-cdmx-24918.firebaseapp.com", + databaseURL: "https://wifi-cdmx-24918-default-rtdb.firebaseio.com", + projectId: "wifi-cdmx-24918", + storageBucket: "wifi-cdmx-24918.appspot.com", + messagingSenderId: "123456", + appId: "1:123456:web:abc" + } +}; \ No newline at end of file diff --git a/src/index.html b/src/index.html index 46edd05d..8f4f350e 100644 --- a/src/index.html +++ b/src/index.html @@ -18,31 +18,34 @@ - - - - - - - - - Paper Dashboard Angular by Creative Tim - - - - - - - - - - - - -
-
-
-
- - - + + + + + + + + + + Paper Dashboard Angular by Creative Tim + + + + + + + + + + + + + +
+
+
+
+ + + \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index ab3dea9e..d0c76d7d 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,10 @@ { "compileOnSave": false, "compilerOptions": { + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, "downlevelIteration": true, + "skipLibCheck": true, "importHelpers": true, "outDir": "./dist/out-tsc", "baseUrl": "src", @@ -10,6 +13,9 @@ "moduleResolution": "node", "emitDecoratorMetadata": true, "experimentalDecorators": true, + "types": [ + "google.maps" + ], "typeRoots": [ "node_modules/@types" ],