├── .bowerrc ├── .editorconfig ├── .ember-cli ├── .github └── first-timers.yml ├── .gitignore ├── .jshintrc ├── .travis.yml ├── .watchmanconfig ├── LICENSE ├── README.md ├── app ├── account │ ├── route.js │ └── template.hbs ├── accounts │ ├── controller.js │ ├── route.js │ └── template.hbs ├── app.js ├── application │ └── template.hbs ├── components │ ├── .gitkeep │ ├── account-details │ │ └── template.hbs │ ├── new-user │ │ └── template.hbs │ └── show-password-toggle │ │ ├── component.js │ │ └── template.hbs ├── helpers │ └── .gitkeep ├── index.html ├── login │ ├── controller.js │ ├── route.js │ └── template.hbs ├── models │ └── .gitkeep ├── resolver.js ├── router.js ├── services │ └── hoodie-account-admin.js └── styles │ └── app.scss ├── bower.json ├── config └── environment.js ├── ember-cli-build.js ├── package.json ├── server ├── .jshintrc ├── index.js └── mocks │ └── whatever.js ├── testem.js ├── tests ├── .jshintrc ├── helpers │ ├── data-attribute-bindings.js │ ├── destroy-app.js │ ├── module-for-acceptance.js │ ├── resolver.js │ └── start-app.js ├── index.html ├── integration │ ├── .gitkeep │ └── components │ │ ├── account-details-test.js │ │ ├── new-user-test.js │ │ └── show-password-toggle-test.js ├── test-helper.js └── unit │ └── .gitkeep └── vendor └── .gitkeep /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components", 3 | "analytics": false 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.js] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | [*.hbs] 21 | insert_final_newline = false 22 | indent_style = space 23 | indent_size = 2 24 | 25 | [*.css] 26 | indent_style = space 27 | indent_size = 2 28 | 29 | [*.html] 30 | indent_style = space 31 | indent_size = 2 32 | 33 | [*.{diff,md}] 34 | trim_trailing_whitespace = false 35 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /.github/first-timers.yml: -------------------------------------------------------------------------------- 1 | repository: camp 2 | labels: 3 | - first-timers-only 4 | - hacktoberfest 5 | - available 6 | template: .github/FIRST_TIMERS_ISSUE_TEMPLATE.md -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | 7 | # dependencies 8 | /node_modules 9 | /bower_components 10 | 11 | # misc 12 | .hoodie 13 | /.sass-cache 14 | /connect.lock 15 | /coverage/* 16 | /libpeerconnection.log 17 | npm-debug.log 18 | testem.log 19 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "-Promise" 6 | ], 7 | "browser": true, 8 | "boss": true, 9 | "curly": true, 10 | "debug": false, 11 | "devel": true, 12 | "eqeqeq": true, 13 | "evil": true, 14 | "forin": false, 15 | "immed": false, 16 | "laxbreak": false, 17 | "newcap": true, 18 | "noarg": true, 19 | "noempty": false, 20 | "nonew": false, 21 | "nomen": false, 22 | "onevar": false, 23 | "plusplus": false, 24 | "regexp": false, 25 | "undef": true, 26 | "sub": true, 27 | "strict": false, 28 | "white": false, 29 | "eqnull": true, 30 | "esnext": true, 31 | "unused": true 32 | } 33 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | notifications: 4 | email: false 5 | node_js: 6 | - 6 7 | before_script: 8 | - npm prune 9 | - bower prune 10 | install: 11 | - npm install -g bower 12 | - npm install -g phantomjs-prebuilt 13 | - npm install 14 | - bower install 15 | after_success: 16 | - npm run semantic-release 17 | branches: 18 | except: 19 | - "/^v\\d+\\.\\d+\\.\\d+$/" 20 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2012-2017 the Hoodie Community and other contributors. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hoodie-admin 2 | 3 | > Hoodie Admin core module 4 | 5 | [![Build Status](https://travis-ci.org/hoodiehq/hoodie-admin.svg?branch=master)](https://travis-ci.org/hoodiehq/hoodie-admin) 6 | [![Dependency Status](https://david-dm.org/hoodiehq/hoodie-admin.svg)](https://david-dm.org/hoodiehq/hoodie-admin) 7 | [![devDependency Status](https://david-dm.org/hoodiehq/hoodie-admin/dev-status.svg)](https://david-dm.org/hoodiehq/hoodie-admin#info=devDependencies) 8 | 9 | This is the admin UI for [Hoodie](http://hood.ie) applications. It’s build with 10 | [Ember.js](http://emberjs.com/) and embraces Ember’s convention above Hoodie’s 11 | to make it as natural for Ember developers to contribute to it as possible. 12 | 13 | ## Prerequisites 14 | 15 | You will need the following things properly installed on your computer. 16 | 17 | * [Git](http://git-scm.com/) 18 | * [Node.js](http://nodejs.org/) (with NPM) 19 | * [Bower](http://bower.io/) 20 | * [Ember CLI](http://ember-cli.com/) 21 | * [PhantomJS](http://phantomjs.org/) 22 | 23 | ## Installation 24 | 25 | * `git clone ` this repository 26 | * change into the new directory 27 | * `npm install` 28 | * `bower install` 29 | 30 | ## Running / Development 31 | 32 | * `ember server` 33 | * Visit your app at [http://localhost:4200](http://localhost:4200). 34 | 35 | ### Code Generators 36 | 37 | Make use of the many generators for code, try `ember help generate` for more details 38 | 39 | ### Running Tests 40 | 41 | * `ember test` 42 | * `ember test --server` 43 | 44 | ### Building 45 | 46 | * `ember build` (development) 47 | * `ember build --environment production` (production) 48 | 49 | ### Deploying 50 | 51 | Specify what it takes to deploy your app. 52 | 53 | ## Further Reading / Useful Links 54 | 55 | * [ember.js](http://emberjs.com/) 56 | * [ember-cli](http://ember-cli.com/) 57 | * Development Browser Extensions 58 | * [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi) 59 | * [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/) 60 | 61 | ## Contributing 62 | 63 | Have a look at the Hoodie project's [contribution guidelines](https://github.com/hoodiehq/hoodie/blob/master/CONTRIBUTING.md). 64 | If you want to hang out you can join our [Hoodie Community Chat](http://hood.ie/chat/). 65 | 66 | ## License 67 | 68 | [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) 69 | -------------------------------------------------------------------------------- /app/account/route.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | const { 4 | Route, 5 | inject: { service }, 6 | get 7 | } = Ember; 8 | 9 | export default Route.extend({ 10 | accountAdmin: service('hoodie-account-admin'), 11 | 12 | model(params) { 13 | return get(this, 'accountAdmin.accounts').find(params.account_id); 14 | }, 15 | 16 | beforeModel(transition) { 17 | let isLoggedIn = get(this, 'accountAdmin').isSignedIn(); 18 | if (!isLoggedIn) { 19 | transition.abort(); 20 | this.transitionTo('login'); 21 | } 22 | }, 23 | 24 | actions: { 25 | deleteAccount(account) { 26 | if (confirm(`Are you sure you'd like to delete ${account.username}'s account?`)) { 27 | get(this, 'accountAdmin.accounts').remove(account.id) 28 | .then(() => { 29 | this.transitionTo('accounts'); 30 | }); 31 | } 32 | } 33 | } 34 | }); 35 | -------------------------------------------------------------------------------- /app/account/template.hbs: -------------------------------------------------------------------------------- 1 | {{link-to '<- Back to account list' 'accounts'}} 2 | 3 | {{#each-in model as |key value|}} 4 |

{{key}}: {{value}}

5 | {{/each-in}} 6 | 7 |

8 | 9 |

10 | -------------------------------------------------------------------------------- /app/accounts/controller.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | const { 4 | Controller, 5 | inject: { service }, 6 | get 7 | } = Ember; 8 | 9 | export default Controller.extend({ 10 | accountAdmin: service('hoodie-account-admin'), 11 | 12 | actions: { 13 | signOut() { 14 | get(this, 'accountAdmin').signOut().then(() => { 15 | this.transitionToRoute('login'); 16 | }); 17 | }, 18 | 19 | createUser(user) { 20 | get(this, 'accountAdmin.accounts').add(user) 21 | .then((user) => { 22 | this.transitionToRoute('account', user.id); 23 | }); 24 | } 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /app/accounts/route.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | const { 4 | Route, 5 | inject: { service }, 6 | get 7 | } = Ember; 8 | 9 | export default Route.extend({ 10 | accountAdmin: service('hoodie-account-admin'), 11 | model() { 12 | return get(this, 'accountAdmin.accounts').findAll(); 13 | }, 14 | 15 | beforeModel(transition) { 16 | let isLoggedIn = get(this, 'accountAdmin').isSignedIn(); 17 | if (!isLoggedIn) { 18 | transition.abort(); 19 | this.transitionTo('login'); 20 | } 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /app/accounts/template.hbs: -------------------------------------------------------------------------------- 1 |

Hello, {{accountAdmin.username}}!

2 |

3 | 4 | {{new-user createUser=(action 'createUser')}} 5 | 6 |

Here are the accounts:

7 | 14 | -------------------------------------------------------------------------------- /app/app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Resolver from './resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from './config/environment'; 5 | 6 | let App; 7 | 8 | Ember.MODEL_FACTORY_INJECTIONS = true; 9 | 10 | App = Ember.Application.extend({ 11 | modulePrefix: config.modulePrefix, 12 | podModulePrefix: config.podModulePrefix, 13 | Resolver 14 | }); 15 | 16 | loadInitializers(App, config.modulePrefix); 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /app/application/template.hbs: -------------------------------------------------------------------------------- 1 | {{outlet}} 2 | -------------------------------------------------------------------------------- /app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoodiehq/hoodie-admin/135ffa58df2d336c92ef32fc2b391ca5fc99fdeb/app/components/.gitkeep -------------------------------------------------------------------------------- /app/components/account-details/template.hbs: -------------------------------------------------------------------------------- 1 | {{account.username}} 2 | -------------------------------------------------------------------------------- /app/components/new-user/template.hbs: -------------------------------------------------------------------------------- 1 |
2 | 6 |
7 | 11 |
12 | 13 |
14 | -------------------------------------------------------------------------------- /app/components/show-password-toggle/component.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | const { 4 | Component 5 | } = Ember; 6 | 7 | export default Component.extend({ 8 | tagName: 'span', 9 | inputType: 'password' 10 | }); 11 | -------------------------------------------------------------------------------- /app/components/show-password-toggle/template.hbs: -------------------------------------------------------------------------------- 1 | {{input type=inputType value=value}} 2 | {{#if (eq inputType 'password')}} 3 | 6 | {{else}} 7 | 10 | {{/if}} 11 | 12 | -------------------------------------------------------------------------------- /app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoodiehq/hoodie-admin/135ffa58df2d336c92ef32fc2b391ca5fc99fdeb/app/helpers/.gitkeep -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Admin 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/login/controller.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | const { 4 | Controller, 5 | inject: { service }, 6 | get, 7 | set 8 | } = Ember; 9 | 10 | export default Controller.extend({ 11 | currentPasswordValue: 'secret', 12 | accountAdmin: service('hoodie-account-admin'), 13 | 14 | actions: { 15 | signIn(password) { 16 | get(this, 'accountAdmin').signIn({ 17 | username: 'admin', 18 | password: password 19 | }).then(() => { 20 | this.transitionToRoute('accounts'); 21 | }).catch((reason) => { 22 | set(this, 'currentError', reason.message); 23 | }); 24 | }, 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /app/login/route.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | const { 4 | Route 5 | } = Ember; 6 | 7 | export default Route.extend({ 8 | }); 9 | -------------------------------------------------------------------------------- /app/login/template.hbs: -------------------------------------------------------------------------------- 1 |

Login

2 | 3 | {{#if currentError}} 4 |

5 | {{currentError}} 6 |

7 | {{/if}} 8 | 9 |
10 | 13 | 14 | 15 |
16 | -------------------------------------------------------------------------------- /app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoodiehq/hoodie-admin/135ffa58df2d336c92ef32fc2b391ca5fc99fdeb/app/models/.gitkeep -------------------------------------------------------------------------------- /app/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember-resolver'; 2 | 3 | export default Resolver; 4 | -------------------------------------------------------------------------------- /app/router.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import config from './config/environment'; 3 | 4 | const Router = Ember.Router.extend({ 5 | location: config.locationType 6 | }); 7 | 8 | Router.map(function() { 9 | this.route('accounts', {path: '/'}); 10 | this.route('account', {path: '/account/:account_id'}); 11 | this.route('login'); 12 | }); 13 | 14 | export default Router; 15 | -------------------------------------------------------------------------------- /app/services/hoodie-account-admin.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import HoodieAdmin from 'npm:@hoodie/admin-client'; 3 | 4 | const { 5 | computed: { alias } 6 | } = Ember; 7 | 8 | export default Ember.Service.extend({ 9 | init() { 10 | this._super(...arguments); 11 | this.admin = new HoodieAdmin(); 12 | 13 | // for debugging only 14 | window.hoodieAdmin = this.admin; 15 | }, 16 | 17 | signIn({username, password}) { 18 | return this.admin.signIn({username, password}); 19 | }, 20 | 21 | signOut() { 22 | return this.admin.signOut(); 23 | }, 24 | 25 | isSignedIn() { 26 | return this.admin.isSignedIn(); 27 | }, 28 | 29 | username: alias('admin.username'), 30 | accounts: alias('admin.accounts') 31 | }); 32 | -------------------------------------------------------------------------------- /app/styles/app.scss: -------------------------------------------------------------------------------- 1 | html { 2 | background: #eee; 3 | font: 16px/1.5 Helvetica, Arial; 4 | } 5 | 6 | body { 7 | max-width: 800px; 8 | margin: 20px auto; 9 | padding: 20px; 10 | border: 1px solid #ddd; 11 | background: #fff; 12 | } 13 | 14 | button { 15 | padding: 4px 8px; 16 | background: #ddd; 17 | border: 1px solid #ccc; 18 | margin: 4px 0; 19 | cursor: pointer; 20 | } 21 | 22 | input { 23 | font: inherit; 24 | } 25 | 26 | .error { 27 | color: #c00; 28 | font-weight: bold; 29 | } 30 | 31 | button.danger { 32 | background-color: #FF0000; 33 | color: white; 34 | } 35 | 36 | button.danger:active { 37 | background-color: #CC0000; 38 | } 39 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "admin", 3 | "dependencies": { 4 | "ember": "~2.6.0-beta.2", 5 | "ember-cli-shims": "0.1.1", 6 | "ember-cli-test-loader": "0.2.2", 7 | "ember-qunit-notifications": "0.1.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 3 | module.exports = function(environment) { 4 | var ENV = { 5 | modulePrefix: 'admin', 6 | environment: environment, 7 | baseURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. 'with-controller': true 13 | } 14 | }, 15 | 16 | APP: { 17 | // Here you can pass flags/options to your application instance 18 | // when it is created 19 | } 20 | }; 21 | 22 | if (environment === 'development') { 23 | // ENV.APP.LOG_RESOLVER = true; 24 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 25 | // ENV.APP.LOG_TRANSITIONS = true; 26 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 27 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 28 | } 29 | 30 | if (environment === 'test') { 31 | // Testem prefers this... 32 | ENV.baseURL = '/'; 33 | ENV.locationType = 'none'; 34 | 35 | // keep test console output quieter 36 | ENV.APP.LOG_ACTIVE_GENERATION = false; 37 | ENV.APP.LOG_VIEW_LOOKUPS = false; 38 | 39 | ENV.APP.rootElement = '#ember-testing'; 40 | } 41 | 42 | if (environment === 'production') { 43 | ENV.baseURL = '/hoodie/admin/'; 44 | ENV.rootUrl = '/hoodie/admin/'; 45 | } 46 | 47 | return ENV; 48 | }; 49 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | /* global require, module */ 3 | var EmberApp = require('ember-cli/lib/broccoli/ember-app'); 4 | 5 | module.exports = function(defaults) { 6 | var app = new EmberApp(defaults, { 7 | 8 | }); 9 | 10 | // Use `app.import` to add additional libraries to the generated 11 | // output files. 12 | // 13 | // If you need to use different assets in different 14 | // environments, specify an object as the first parameter. That 15 | // object's keys should be the environment name and the values 16 | // should be the asset to use in that environment. 17 | // 18 | // If the library that you are including contains AMD or ES6 19 | // modules that you would like to import into your application 20 | // please specify an object with the list of modules as keys 21 | // along with the exports of each module as its value. 22 | 23 | return app.toTree(); 24 | }; 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hoodie/admin", 3 | "description": "Hoodie Admin core module", 4 | "directories": { 5 | "test": "tests" 6 | }, 7 | "scripts": { 8 | "build": "ember build", 9 | "start": "ember server", 10 | "test": "ember test", 11 | "presemantic-release": "ember build --environment=production", 12 | "semantic-release": "semantic-release pre && npm publish && semantic-release post" 13 | }, 14 | "files": [ 15 | "dist" 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/hoodiehq/hoodie-admin.git" 20 | }, 21 | "keywords": [ 22 | "hoodie", 23 | "account" 24 | ], 25 | "author": "The Hoodie Community and other contributors | http://hood.ie/", 26 | "license": "Apache-2.0", 27 | "publishConfig": { 28 | "access": "public" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/hoodiehq/hoodie-admin/issues" 32 | }, 33 | "homepage": "https://github.com/hoodiehq/hoodie-admin#readme", 34 | "devDependencies": { 35 | "@hoodie/admin-client": "1.0.2", 36 | "broccoli-asset-rev": "^2.4.2", 37 | "ember-ajax": "^2.0.1", 38 | "ember-browserify": "^1.1.9", 39 | "ember-cli": "2.11.1", 40 | "ember-cli-app-version": "^2.0.0", 41 | "ember-cli-babel": "^5.1.6", 42 | "ember-cli-dependency-checker": "^1.2.0", 43 | "ember-cli-htmlbars": "^1.0.3", 44 | "ember-cli-htmlbars-inline-precompile": "^0.3.1", 45 | "ember-cli-inject-live-reload": "^1.4.0", 46 | "ember-cli-jshint": "^2.0.1", 47 | "ember-cli-qunit": "^3.0.0", 48 | "ember-cli-release": "0.2.9", 49 | "ember-cli-sass": "^6.0.0", 50 | "ember-cli-sri": "^2.1.0", 51 | "ember-cli-uglify": "^1.2.0", 52 | "ember-data": "^2.6.0-beta.1", 53 | "ember-export-application-global": "^1.0.5", 54 | "ember-load-initializers": "^0.6.0", 55 | "ember-resolver": "^2.0.3", 56 | "ember-truth-helpers": "^1.2.0", 57 | "express": "^4.13.4", 58 | "glob": "^7.0.4", 59 | "hapi": "^16.0.0", 60 | "hoodie": "^24.0.0", 61 | "http-proxy-middleware": "^0.17.0", 62 | "loader.js": "^4.2.2", 63 | "morgan": "^1.7.0", 64 | "semantic-release": "^7.0.1" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /server/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true 3 | } 4 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | /* jshint node:true */ 2 | 3 | // To use it create some files under `mocks/` 4 | // e.g. `server/mocks/ember-hamsters.js` 5 | // 6 | // module.exports = function(app) { 7 | // app.get('/ember-hamsters', function(req, res) { 8 | // res.send('hello'); 9 | // }); 10 | // }; 11 | // 12 | 13 | var hoodie = require('hoodie'); 14 | var Hapi = require('hapi'); 15 | var url = require('url'); 16 | var proxy = require('http-proxy-middleware'); 17 | 18 | module.exports = function(app) { 19 | 20 | // Log proxy requests 21 | var morgan = require('morgan'); 22 | app.use(morgan('dev')); 23 | 24 | app.use('/hoodie', proxy({target: 'http://localhost:4201'})); 25 | 26 | var server = new Hapi.Server(); 27 | server.connection({ 28 | host: 'localhost', 29 | port: 4201 30 | }); 31 | 32 | server.register({ 33 | register: hoodie, 34 | options: { 35 | // https://github.com/hoodiehq/hoodie/#hapi-plugin 36 | } 37 | }, function (error) { 38 | if (error) { 39 | var stack = new Error().stack.split('\n').slice(2).join('\n'); 40 | return console.log('app', 'Failed to initialise:\n' + stack, error); 41 | } 42 | 43 | console.log('app', 'Starting'); 44 | 45 | server.start(function () { 46 | console.log('Your Hoodie server has started on ' + url.format({ 47 | protocol: 'http', 48 | hostname: 'localhost', 49 | port: 4201 50 | })); 51 | }); 52 | }); 53 | }; 54 | -------------------------------------------------------------------------------- /server/mocks/whatever.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | module.exports = function(app) { 3 | var express = require('express'); 4 | var whateverRouter = express.Router(); 5 | 6 | whateverRouter.get('/', function(req, res) { 7 | res.send({ 8 | 'whatever': [] 9 | }); 10 | }); 11 | 12 | whateverRouter.post('/', function(req, res) { 13 | res.status(201).end(); 14 | }); 15 | 16 | whateverRouter.get('/:id', function(req, res) { 17 | res.send({ 18 | 'whatever': { 19 | id: req.params.id 20 | } 21 | }); 22 | }); 23 | 24 | whateverRouter.put('/:id', function(req, res) { 25 | res.send({ 26 | 'whatever': { 27 | id: req.params.id 28 | } 29 | }); 30 | }); 31 | 32 | whateverRouter.delete('/:id', function(req, res) { 33 | res.status(204).end(); 34 | }); 35 | 36 | // The POST and PUT call will not contain a request body 37 | // because the body-parser is not included by default. 38 | // To use req.body, run: 39 | 40 | // npm install --save-dev body-parser 41 | 42 | // After installing, you need to `use` the body-parser for 43 | // this mock uncommenting the following line: 44 | // 45 | //app.use('/api/whatever', require('body-parser').json()); 46 | app.use('/api/whatever', whateverRouter); 47 | }; 48 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | /*jshint node:true*/ 2 | module.exports = { 3 | "framework": "qunit", 4 | "test_page": "tests/index.html?hidepassed", 5 | "disable_watching": true, 6 | "launch_in_ci": [ 7 | "PhantomJS" 8 | ], 9 | "launch_in_dev": [ 10 | "PhantomJS", 11 | "Chrome" 12 | ] 13 | }; 14 | -------------------------------------------------------------------------------- /tests/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "predef": [ 3 | "document", 4 | "window", 5 | "location", 6 | "setTimeout", 7 | "$", 8 | "-Promise", 9 | "define", 10 | "console", 11 | "visit", 12 | "exists", 13 | "fillIn", 14 | "click", 15 | "keyEvent", 16 | "triggerEvent", 17 | "find", 18 | "findWithAssert", 19 | "wait", 20 | "DS", 21 | "andThen", 22 | "currentURL", 23 | "currentPath", 24 | "currentRouteName" 25 | ], 26 | "node": false, 27 | "browser": false, 28 | "boss": true, 29 | "curly": true, 30 | "debug": false, 31 | "devel": false, 32 | "eqeqeq": true, 33 | "evil": true, 34 | "forin": false, 35 | "immed": false, 36 | "laxbreak": false, 37 | "newcap": true, 38 | "noarg": true, 39 | "noempty": false, 40 | "nonew": false, 41 | "nomen": false, 42 | "onevar": false, 43 | "plusplus": false, 44 | "regexp": false, 45 | "undef": true, 46 | "sub": true, 47 | "strict": false, 48 | "white": false, 49 | "eqnull": true, 50 | "esnext": true, 51 | "unused": true 52 | } 53 | -------------------------------------------------------------------------------- /tests/helpers/data-attribute-bindings.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | Ember.Component.reopen({ 4 | attributeBindings: ['data-test-selector'] 5 | }); 6 | -------------------------------------------------------------------------------- /tests/helpers/destroy-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | 3 | export default function destroyApp(application) { 4 | Ember.run(application, 'destroy'); 5 | } 6 | -------------------------------------------------------------------------------- /tests/helpers/module-for-acceptance.js: -------------------------------------------------------------------------------- 1 | import { module } from 'qunit'; 2 | import Ember from 'ember'; 3 | import startApp from '../helpers/start-app'; 4 | import destroyApp from '../helpers/destroy-app'; 5 | 6 | const { RSVP: { Promise } } = Ember; 7 | 8 | export default function(name, options = {}) { 9 | module(name, { 10 | beforeEach() { 11 | this.application = startApp(); 12 | 13 | if (options.beforeEach) { 14 | return options.beforeEach.apply(this, arguments); 15 | } 16 | }, 17 | 18 | afterEach() { 19 | let afterEach = options.afterEach && options.afterEach.apply(this, arguments); 20 | return Promise.resolve(afterEach).then(() => destroyApp(this.application)); 21 | } 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /tests/helpers/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from '../../resolver'; 2 | import config from '../../config/environment'; 3 | 4 | const resolver = Resolver.create(); 5 | 6 | resolver.namespace = { 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix 9 | }; 10 | 11 | export default resolver; 12 | -------------------------------------------------------------------------------- /tests/helpers/start-app.js: -------------------------------------------------------------------------------- 1 | import Ember from 'ember'; 2 | import Application from '../../app'; 3 | import config from '../../config/environment'; 4 | 5 | export default function startApp(attrs) { 6 | let application; 7 | 8 | let attributes = Ember.merge({}, config.APP); 9 | attributes = Ember.merge(attributes, attrs); // use defaults, but you can override; 10 | 11 | Ember.run(() => { 12 | application = Application.create(attributes); 13 | application.setupForTesting(); 14 | application.injectTestHelpers(); 15 | }); 16 | 17 | return application; 18 | } 19 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Admin Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {{content-for "body-footer"}} 32 | {{content-for "test-body-footer"}} 33 | 34 | 35 | -------------------------------------------------------------------------------- /tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoodiehq/hoodie-admin/135ffa58df2d336c92ef32fc2b391ca5fc99fdeb/tests/integration/.gitkeep -------------------------------------------------------------------------------- /tests/integration/components/account-details-test.js: -------------------------------------------------------------------------------- 1 | import { moduleForComponent, test } from 'ember-qunit'; 2 | import hbs from 'htmlbars-inline-precompile'; 3 | 4 | moduleForComponent('account-details', 'Integration | Component | account details', { 5 | integration: true 6 | }); 7 | 8 | test('it renders', function(assert) { 9 | this.set('account', { username: 'john_locke_42' }); 10 | 11 | this.render(hbs`{{account-details account=account}}`); 12 | 13 | assert.equal(this.$().text().trim(), 'john_locke_42'); 14 | }); 15 | -------------------------------------------------------------------------------- /tests/integration/components/new-user-test.js: -------------------------------------------------------------------------------- 1 | import { moduleForComponent, test } from 'ember-qunit'; 2 | import hbs from 'htmlbars-inline-precompile'; 3 | 4 | moduleForComponent('new-user', 'Integration | Component | new user', { 5 | integration: true 6 | }); 7 | 8 | test('it renders', function(assert) { 9 | assert.expect(1); 10 | 11 | const username = 'user'; 12 | const password = 'pass'; 13 | 14 | this.set('createUser', function(user) { 15 | assert.deepEqual(user, { 16 | username, 17 | password 18 | }); 19 | }); 20 | 21 | this.render(hbs`{{new-user createUser=(action createUser)}}`); 22 | 23 | this.$('[data-test-selector="new-user-name"]').val(username); 24 | this.$('[data-test-selector="new-user-name"]').trigger('change'); 25 | this.$('[data-test-selector="new-user-password"] input').val(password); 26 | this.$('[data-test-selector="new-user-password"] input').trigger('change'); 27 | this.$('[data-test-selector="new-user-submit"]').click(); 28 | }); 29 | -------------------------------------------------------------------------------- /tests/integration/components/show-password-toggle-test.js: -------------------------------------------------------------------------------- 1 | import { moduleForComponent, test } from 'ember-qunit'; 2 | import hbs from 'htmlbars-inline-precompile'; 3 | 4 | moduleForComponent('show-password-toggle', 'Integration | Component | show password toggle', { 5 | integration: true 6 | }); 7 | 8 | test('it toggles input type', function(assert) { 9 | assert.expect(2); 10 | 11 | this.render(hbs`{{show-password-toggle}}`); 12 | 13 | assert.equal(this.$('input').attr('type'), 'password'); 14 | 15 | this.$('[data-test-selector="password-visibility-toggle"]').click(); 16 | 17 | assert.equal(this.$('input').attr('type'), 'text'); 18 | }); 19 | 20 | test('it binds to `value` just like `{{input`', function(assert) { 21 | assert.expect(3); 22 | 23 | this.set('value', 'hello'); 24 | 25 | this.render(hbs`{{show-password-toggle value=value}}`); 26 | 27 | assert.equal(this.$('input').val(), 'hello'); 28 | 29 | this.$('input').val('hello world'); 30 | this.$('input').trigger('change'); 31 | 32 | assert.equal(this.get('value'), 'hello world'); 33 | 34 | this.$('[data-test-selector="password-visibility-toggle"]').click(); 35 | this.$('input').val('hello world!'); 36 | this.$('input').trigger('change'); 37 | 38 | assert.equal(this.get('value'), 'hello world!'); 39 | }); 40 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import resolver from './helpers/resolver'; 2 | import { 3 | setResolver 4 | } from 'ember-qunit'; 5 | import './helpers/data-attribute-bindings'; 6 | 7 | setResolver(resolver); 8 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoodiehq/hoodie-admin/135ffa58df2d336c92ef32fc2b391ca5fc99fdeb/tests/unit/.gitkeep -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoodiehq/hoodie-admin/135ffa58df2d336c92ef32fc2b391ca5fc99fdeb/vendor/.gitkeep --------------------------------------------------------------------------------