Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 31 additions & 37 deletions ui/packages/consul-ui/app/components/child-selector/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,37 @@
{{yield}}
{{#if (not @disabled)}}
{{yield to="create"}}
<label class="type-text">
<span>{{yield to="label"}}</span>
{{!-- {{#if isOpen}} --}}
<DataSource
@src={{uri '/${partition}/${nspace}/${dc}/${type}'
(hash
partition=@partition
nspace=@nspace
dc=@dc
type=(pluralize this.type)
)
}}
@onchange={{action "setAllOptions" value="data"}}
/>
{{!-- {{/if}} --}}
<DataCollection
@type={{this.type}}
@sort='Name:asc'
@filters={{hash
searchproperties=(array 'Name')
}}
@items={{this.options}}
as |collection|>
<Hds::Form::SuperSelect::Single::Field
@searchEnabled={{true}}
@onFilter={{action collection.search}}
@options={{sort-by 'Name:asc' this.options}}
@placeholder={{@placeholder}}
@onChange={{action "change" "items[]" @items}}
@label="Select {{this.type}}"
@isOptional={{true}}
@searchField="Name"
as |F|>
<F.Options>{{F.options.Name}}</F.Options>
</Hds::Form::SuperSelect::Single::Field>
</DataCollection>
</label>
<DataSource
@src={{uri '/${partition}/${nspace}/${dc}/${type}'
(hash
partition=@partition
nspace=@nspace
dc=@dc
type=(pluralize this.type)
)
}}
@onchange={{action "setAllOptions" value="data"}}
/>
<DataCollection
@type={{this.type}}
@sort='Name:asc'
@filters={{hash
searchproperties=(array 'Name')
}}
@items={{this.options}}
as |collection|>
<Hds::Form::SuperSelect::Single::Field
@searchEnabled={{true}}
@onFilter={{action collection.search}}
@options={{sort-by 'Name:asc' this.options}}
@placeholder={{@placeholder}}
@onChange={{action "change" "items[]" @items}}
@searchField="Name"
as |F|>
<F.Label>{{yield to="label"}}</F.Label>
<F.Options>{{F.options.Name}}</F.Options>
</Hds::Form::SuperSelect::Single::Field>
</DataCollection>
{{/if}}
{{#if (gt @items.length 0)}}
{{yield (action "remove") to="set"}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
{{on 'input' @onChange}}
as |F|
>
<F.Label>Name</F.Label>
<F.Label>Description</F.Label>
</Hds::Form::TextInput::Field>
<label class='type-text'>
<Hds::CodeEditor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,77 @@
<fieldset
disabled={{if (not (can "write token" item=@item)) "disabled"}}
>
{{#if @create }}
<div class="type-toggle">
<label>
<input type="checkbox" name="Local" checked={{if @local 'checked' }} onchange={{@onChange}} />
<span>Restrict this token to a local datacenter?</span>
</label>
<em>Local tokens get set in the Raft store of the local DC and do not ever get transmitted to the primary DC or replicated to any other DC.</em>
</div>
{{/if}}
<label class="type-text">
<span>Name</span>
<input type="text" name="Name" value={{@item.Name}} oninput={{@onChange}} />
</label>
<label class="type-text validate-optional">
<span>Description (Optional)</span>
<textarea name="Description" oninput={{@onChange}}>{{@item.Description}}</textarea>
</label>
{{! Read-only fields (AccessorID, Token, Scope) — edit mode only }}
{{#unless @create}}
<Hds::Form::MaskedInput::Field
readonly
@isContentMasked={{false}}
@hasCopyButton={{true}}
@value={{@item.AccessorID}}
as |F|
>
<F.Label>AccessorID</F.Label>
</Hds::Form::MaskedInput::Field>

<Hds::Form::MaskedInput::Field
readonly
@isContentMasked={{true}}
@hasCopyButton={{true}}
@value={{@item.SecretID}}
as |F|
>
<F.Label>Token</F.Label>
</Hds::Form::MaskedInput::Field>

{{#unless (token/is-legacy @item)}}
<Hds::Form::TextInput::Field
readonly
@value={{if @item.Local 'local' 'global'}}
as |F|
>
<F.Label>Scope</F.Label>
</Hds::Form::TextInput::Field>
{{/unless}}
{{/unless}}

{{! Local-scope toggle — create mode only }}
{{#if @create}}
<Hds::Form::Toggle::Field
name="Local"
checked={{if @local true false}}
{{on "change" @onChange}}
as |F|
>
<F.Label>Restrict this token to a local datacenter?</F.Label>
<F.HelperText>
Local tokens get set in the Raft store of the local DC and do not ever
get transmitted to the primary DC or replicated to any other DC.
</F.HelperText>
</Hds::Form::Toggle::Field>
{{/if}}

<Hds::Form::TextInput::Field
@value={{@item.Name}}
name="Name"
{{on "input" @onChange}}
as |F|
>
<F.Label>Name</F.Label>
</Hds::Form::TextInput::Field>

<Hds::Form::Textarea::Field
@value={{@item.Description}}
name="Description"
@isOptional={{true}}
{{on "input" @onChange}}
as |F|
>
<F.Label>Description</F.Label>
</Hds::Form::Textarea::Field>
</fieldset>

<Hds::Separator />

<fieldset id="roles">
<RoleSelector
@showHeader={{true}}
Expand All @@ -34,6 +87,9 @@
@items={{@item.Roles}}
/>
</fieldset>

<Hds::Separator />

<fieldset id="policies">
<PolicySelector
@showHeader={{true}}
Expand All @@ -43,4 +99,4 @@
@nspace={{@nspace}}
@items={{@item.Policies}}
/>
</fieldset>
</fieldset>
57 changes: 39 additions & 18 deletions ui/packages/consul-ui/app/components/consul/token/form/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
SPDX-License-Identifier: BUSL-1.1
}}

<form>
<form class="consul-token-form">
{{!-- fieldsets already converted to components --}}
{{#if (not (token/is-legacy @item))}}
<Consul::Token::Fieldsets
Expand All @@ -24,7 +24,7 @@
/>
{{/if}}

<div>
<div class="consul-token-form__actions">
<Hds::ButtonSet>
{{#if (and @create (can "create tokens")) }}
<Hds::Button
Expand All @@ -50,22 +50,43 @@
type='reset'
{{on 'click' @onCancel}}
/>

{{# if (and (not @create) (can "delete token" item=@item token=@token) ) }}
<ConfirmationDialog @message="Are you sure you want to delete this Token?">
<:action as |confirm|>
<Hds::Button
@text='Delete'
@color='critical'
{{on 'click' confirm}}
data-test-delete
/>
</:action>
<:dialog as |execute cancel message|>
<DeleteConfirmation @message={{message}} @execute={{queue execute (fn @onDelete @item)}} @cancel={{cancel}} />
</:dialog>
</ConfirmationDialog>
{{/if}}
</Hds::ButtonSet>

{{#if (and (not @create) (can "delete token" item=@item token=@token))}}
<Hds::Button
@text='Delete'
@color='critical'
{{on 'click' this.openDeleteModal}}
data-test-delete
/>
{{/if}}
</div>

{{#if this.showDeleteModal}}
<Hds::Modal
@color='critical'
@onClose={{this.closeDeleteModal}}
as |M|
>
<M.Header @icon='trash'>Confirm delete</M.Header>
<M.Body>
<p>Are you sure you want to delete this token?</p>
</M.Body>
<M.Footer as |F|>
<Hds::ButtonSet>
<Hds::Button
@text='Delete'
@color='critical'
data-test-confirm-delete
{{on 'click' this.confirmDelete}}
/>
<Hds::Button
@text='Cancel'
@color='secondary'
{{on 'click' F.close}}
/>
</Hds::ButtonSet>
</M.Footer>
</Hds::Modal>
{{/if}}
</form>
36 changes: 36 additions & 0 deletions ui/packages/consul-ui/app/components/consul/token/form/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/

import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

/**
* Consul::Token::Form
*
* Owns the Delete confirmation modal state. All other actions (Save, Cancel,
* Delete invoke) are delegated to the parent via @onCreate / @onUpdate /
* @onCancel / @onDelete args.
*/
export default class ConsulTokenForm extends Component {
// true while the delete-confirmation modal is open
@tracked showDeleteModal = false;

@action
openDeleteModal() {
this.showDeleteModal = true;
}

@action
closeDeleteModal() {
this.showDeleteModal = false;
}

@action
confirmDelete() {
this.showDeleteModal = false;
this.args.onDelete(this.args.item);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/

// Token create/edit form layout.
// Mirrors the nspaces form pattern: flex column with 32px gap between each
// top-level child (fieldsets, separators, button row). The separators
// themselves carry 24px margin on each side via hds-separator--spacing-24,
// so the 32px form gap + 24px separator margin matches the Figma rhythm.
.consul-token-form {
display: flex;
flex-direction: column;
// All section-level spacing is driven by this single gap value.
// The separators' built-in margins are reset to zero below so they
// don't add extra space on top of the gap.
gap: 32px;

// Reset HDS separator margins so the form gap is the sole source of spacing.
> .hds-separator {
margin-top: 0;
margin-bottom: 0;
}

// All top-level fieldsets: flex column so HDS form fields stack with a 24px gap.
// Covers the main token fields, #roles, and #policies sections.
> fieldset {
border: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 24px;
}
&__actions {
display: flex;
justify-content: space-between;
align-items: center;
}
}
Loading
Loading