diff --git a/app/authenticators/auth-token.js b/app/authenticators/auth-token.js index 4accc3619284cabb6b0e6160d84300e47c99d671..8a00242ffeb8429d1deda1beed7e661b8f36a0c8 100644 --- a/app/authenticators/auth-token.js +++ b/app/authenticators/auth-token.js @@ -27,7 +27,7 @@ export default class AuthToken extends Base { async authenticate(username, password) { let response, error; - response = await fetch(`${base_url()}/auth-token/`, { + response = await fetch(`${base_url()}/auth/login/`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/app/components/alert/success.hbs b/app/components/alert/success.hbs new file mode 100644 index 0000000000000000000000000000000000000000..ed92e0db4194453e19c1d7c0e8f72110bc64bc79 --- /dev/null +++ b/app/components/alert/success.hbs @@ -0,0 +1,4 @@ +<div class="alert alert-success alert-dismissible fade show" role="alert"> + {{@message}} + <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> +</div> \ No newline at end of file diff --git a/app/components/commander/index.hbs b/app/components/commander/index.hbs index 45ed1cbc3537f6ddda90e365a6daf5ed0b624a11..fc80a2af2bd4fe226b0e458c24b702f473254d28 100644 --- a/app/components/commander/index.hbs +++ b/app/components/commander/index.hbs @@ -1,4 +1,4 @@ -<div class="panel commander col m-2 p-2" +<div class="panel commander col m-2 p-2 user-select-none" {{droppable onDrop=this.onDrop onDragOver=this.onDragOver }} diff --git a/app/components/tag/new.hbs b/app/components/tag/new.hbs index e1fd42f21552752effd658be48ebe0874b10c55c..51aacff83851b6816c9e30aee65f75cd03c22355 100644 --- a/app/components/tag/new.hbs +++ b/app/components/tag/new.hbs @@ -12,65 +12,65 @@ </div> </div> - <div class="row mb-2"> - <div class="col-md-3"> - <label for="name" class="form-label">Name</label> - <Input - id="name" - @type="text" - @value={{this.new_name}} - class="form-control" - /> - <div class="d-flex flex-row my-1"> - <Input id="fg_color" @type="color" @value={{this.new_fg_color}} class="form-control form-control-color" /> - <Input id="bg_color" @type="color" @value={{this.new_bg_color}} class="form-control form-control-color" /> - </div> - </div> - <div class="col-md-1"> - <label class="form-check-label" for="is_pinned">Is Pinned?</label> - <div> - <Input - @type="checkbox" - @checked={{this.new_pinned}} - class="form-check-input" id="is_pinned" /> - </div> + <div class="row mb-2"> + <div class="col-md-3"> + <label for="name" class="form-label">Name</label> + <Input + id="name" + @type="text" + @value={{this.new_name}} + class="form-control" + /> + <div class="d-flex flex-row my-1"> + <Input id="fg_color" @type="color" @value={{this.new_fg_color}} class="form-control form-control-color" /> + <Input id="bg_color" @type="color" @value={{this.new_bg_color}} class="form-control form-control-color" /> </div> - <div class="col-md-5"> - <label for="description" class="form-label">Description</label> + </div> + <div class="col-md-1"> + <label class="form-check-label" for="is_pinned">Is Pinned?</label> + <div> <Input - id="description" - @type="text" - @value={{this.new_description}} - class="form-control" - /> + @type="checkbox" + @checked={{this.new_pinned}} + class="form-check-input" id="is_pinned" /> </div> - <div class="col-md-3"> - <label for="action" class="form-label">Action</label> - <div> + </div> + <div class="col-md-5"> + <label for="description" class="form-label">Description</label> + <Input + id="description" + @type="text" + @value={{this.new_description}} + class="form-control" + /> + </div> + <div class="col-md-3"> + <label for="action" class="form-label">Action</label> + <div> + <button + type="button" + {{on "click" this.onCancel}} + class="btn btn-secondary"> + Cancel + </button> + {{#if this.new_name }} <button type="button" - {{on "click" this.onCancel}} - class="btn btn-secondary"> - Cancel + {{on "click" this.onCreate}} + class="btn btn-success"> + Create Tag </button> - {{#if this.new_name }} - <button - type="button" - {{on "click" this.onCreate}} - class="btn btn-success"> - Create Tag - </button> - {{else}} - <button - type="button" - {{on "click" this.onCreate}} - disabled - class="btn btn-success"> - Create Tag - </button> - {{/if}} - </div> + {{else}} + <button + type="button" + {{on "click" this.onCreate}} + disabled + class="btn btn-success"> + Create Tag + </button> + {{/if}} </div> </div> + </div> </form> {{/if}} \ No newline at end of file diff --git a/app/components/token/new.hbs b/app/components/token/new.hbs new file mode 100644 index 0000000000000000000000000000000000000000..7cda1e5b945f77392977176858e6ac42d9bade48 --- /dev/null +++ b/app/components/token/new.hbs @@ -0,0 +1,47 @@ +{{#if this.message_with_token}} + <Alert::Success @message={{this.message_with_token}} /> +{{/if}} + +<Button::New @onClick={{this.onToggleNew}} class="add-token" /> + +{{#if this.form_visible}} + <form> + <div class="row mb-2"> + <div class="col-md-3"> + <label for="name" class="form-label">Expiry</label> + <div class="input-group mb-3"> + <input type="number" + min="1" + max="365" + class="form-control" + value="{{this.new_expiry}}" + {{on 'change' this.onNumericChange}} > + <select class="form-select" {{on 'change' this.onScaleChange}} > + <option selected value="1">hour(s)</option> + <option value="24">day(s)</option> + <option value="744">month(s)</option> + </select> + </div> + </div> + + <div class="col-md-3"> + <label for="action" class="form-label">Action</label> + <div> + <button + type="button" + {{on "click" this.onCancel}} + class="btn btn-secondary"> + Cancel + </button> + + <button + type="button" + {{on "click" this.onCreate}} + class="btn btn-success"> + Create Token + </button> + </div> + </div> + </div> + </form> +{{/if}} \ No newline at end of file diff --git a/app/components/token/new.js b/app/components/token/new.js new file mode 100644 index 0000000000000000000000000000000000000000..1ec419b5a4d16abd026930b4c52e64993dec4e35 --- /dev/null +++ b/app/components/token/new.js @@ -0,0 +1,85 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; +import { service } from '@ember/service'; + + +export default class NewTokenComponent extends Component { + /* + Component to create new token. + + It consists from a button labeled 'new' which + when clicked will toggle a 'new token' form. + */ + + new_expiry_numeric = 1; + new_expiry_scale = 1; + @service store; + + // initially only 'new' button is visible + @tracked form_visible = false; + @tracked message_with_token; + + @action + onToggleNew() { + this.form_visible = !this.form_visible; + } + + get new_expiry() { + /* + Returns token expiry value, in hours. + */ + let numeric, scale; + + // the numeric value e.g. 3 + numeric = parseInt(this.new_expiry_numeric); + // when user chooses hours, scale := 1 + // when user chooses days, scale := 24 + // when user chooses months, scale := 744 + scale = parseInt(this.new_expiry_scale); + + return numeric * scale; + } + + @action + onCreate() { + let that = this; + + this.store.createRecord('token', { + expiry_hours: this.new_expiry + }).save().then(model => { + let msg; + + msg = "Please remember the token as it won't be displayed again: "; + msg += model.token; + + that.message_with_token = msg; + }); + + this._empty_form(); + } + + @action + onCancel() { + this._empty_form(); + } + + @action + onNumericChange(event) { + this.new_expiry_numeric = parseInt(event.target.value); + } + + @action + onScaleChange(event) { + this.new_expiry_scale = parseInt(event.target.value); + } + + _empty_form() { + /* + Resets the form to initial state + */ + this.new_expiry_scale = 1; + this.new_expiry_numeric = 1; + this.form_visible = false; + } +} diff --git a/app/components/token/table_row.hbs b/app/components/token/table_row.hbs new file mode 100644 index 0000000000000000000000000000000000000000..15066130c0143d3c4db55521406a93f652d4e090 --- /dev/null +++ b/app/components/token/table_row.hbs @@ -0,0 +1,14 @@ +<tr> + <td> + {{truncatechars @token.digest}} + </td> + <td> + {{@token.expiry}} + </td> + <td>{{@token.created}}</td> + <td> + <button class="btn btn-link" type="button" {{on "click" (fn this.onRemove @token)}}> + Remove + </button> + </td> +</tr> \ No newline at end of file diff --git a/app/components/token/table_row.js b/app/components/token/table_row.js new file mode 100644 index 0000000000000000000000000000000000000000..0fee1aa66cf1297c528483458b7dd4569e457b2f --- /dev/null +++ b/app/components/token/table_row.js @@ -0,0 +1,13 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { service } from '@ember/service'; + + +export default class TokenTableRowComponent extends Component { + @service store; + + @action + async onRemove(token) { + await token.destroyRecord(); + } +} diff --git a/app/helpers/truncatechars.js b/app/helpers/truncatechars.js new file mode 100644 index 0000000000000000000000000000000000000000..be2f4cef43497ae9278e1f866af5bb3582e4a3f5 --- /dev/null +++ b/app/helpers/truncatechars.js @@ -0,0 +1,20 @@ +import { helper } from '@ember/component/helper'; + + +export function truncatechars([str]) { + /* + Truncates a string if it is longer than the specified number of characters. + Truncated strings will end with a translatable ellipsis character ('…'). + */ + if (!str) { + return ''; + } + + if (str.length <= 10) { + return str; + } + + return `${str.substring(0, 10)}...`; +} + +export default helper(truncatechars); diff --git a/app/models/token.js b/app/models/token.js new file mode 100644 index 0000000000000000000000000000000000000000..4ddcb30494f16baac9bebca8d96fde6a37365fa8 --- /dev/null +++ b/app/models/token.js @@ -0,0 +1,12 @@ +import Model, { attr } from '@ember-data/model'; + + +class TokenModel extends Model { + @attr expiry_hours; + @attr expiry; + @attr digest; + @attr token; + @attr created; +} + +export default TokenModel; diff --git a/app/router.js b/app/router.js index 35b438d2df0b73d96e673c69f48d0c426415ac58..c9e0b990e067058f50e3472dd5390adf017c7a7f 100644 --- a/app/router.js +++ b/app/router.js @@ -16,6 +16,8 @@ Router.map(function () { this.route('tags'); + this.route('tokens'); + this.route('automates', function () { this.route('add'); this.route('edit', { path: '/:automate_id/edit' }); @@ -44,10 +46,7 @@ Router.map(function () { this.route('section', { path: '/:section_name' }); }); - this.route('tokens', function() { - this.route('add'); - this.route('index', { path: '/' }); - }); + }); this.route('login'); diff --git a/app/routes/authenticated/tags.js b/app/routes/authenticated/tags.js index 67f9b06ab7684cf18b30f2a8ab136ad8cb46ed7f..0a6b8d5c41955c04b33e8ff8943c00107a5c5b60 100644 --- a/app/routes/authenticated/tags.js +++ b/app/routes/authenticated/tags.js @@ -1,4 +1,4 @@ -import { inject as service } from '@ember/service'; +import { service } from '@ember/service'; import BaseRoute from 'papermerge/base/routing'; diff --git a/app/routes/authenticated/tokens.js b/app/routes/authenticated/tokens.js new file mode 100644 index 0000000000000000000000000000000000000000..924d608ebfa0c244f06a5710a5ba189ff2a65bad --- /dev/null +++ b/app/routes/authenticated/tokens.js @@ -0,0 +1,11 @@ +import { service } from '@ember/service'; +import BaseRoute from 'papermerge/base/routing'; + + +export default class TokensRoute extends BaseRoute { + @service store; + + async model() { + return this.store.findAll('token'); + } +} diff --git a/app/styles/app.scss b/app/styles/app.scss index 28733cb9f2d60e4d2781f1d46fc95870576d2b05..ceac81d4642f09e51c7f0f8ff3ec5ea88d98f871 100644 --- a/app/styles/app.scss +++ b/app/styles/app.scss @@ -14,8 +14,10 @@ body { background-color: #e9ecef; - // make node title text unselectable - user-select: none; +} + +.user-select-none { + user-select: none; } main { diff --git a/app/templates/authenticated/tokens.hbs b/app/templates/authenticated/tokens.hbs new file mode 100644 index 0000000000000000000000000000000000000000..b22ac27eeb8c961caa79ae66f51adaf27bd2721e --- /dev/null +++ b/app/templates/authenticated/tokens.hbs @@ -0,0 +1,7 @@ +<Token::New /> + +<Table @titles={{array 'Digest' 'Expiry' 'Created' 'Action' }}> + {{#each @model as |token|}} + <Token::TableRow @token={{token}} /> + {{/each}} +</Table> diff --git a/app/templates/authenticated/tokens/add.hbs b/app/templates/authenticated/tokens/add.hbs deleted file mode 100644 index a319583b787f290640b8f796d95982887e4438c9..0000000000000000000000000000000000000000 --- a/app/templates/authenticated/tokens/add.hbs +++ /dev/null @@ -1 +0,0 @@ -Add \ No newline at end of file diff --git a/app/templates/authenticated/tokens/index.hbs b/app/templates/authenticated/tokens/index.hbs deleted file mode 100644 index 9f7cda8ed5fcaff95eb3e5566a6609dc61696f6b..0000000000000000000000000000000000000000 --- a/app/templates/authenticated/tokens/index.hbs +++ /dev/null @@ -1 +0,0 @@ -Index \ No newline at end of file