discourse/app/assets/javascripts/admin/addon/templates/admin-badges/show.hbs
chapoi 2ca06ba236
DEV: form-kit
This PR introduces FormKit, a component-based form library designed to simplify form creation and management. This library provides a single `Form` component, various field components, controls, validation mechanisms, and customization options. Additionally, it includes helpers to facilitate testing and writing specifications for forms.

1. **Form Component**:
   - The main component that encapsulates form logic and structure.
   - Yields various utilities like `Field`, `Submit`, `Alert`, etc.

   **Example Usage**:
   ```gjs
   import Form from "discourse/form";

   <template>
     <Form as |form|>
       <form.Field
         @name="username"
         @title="Username"
         @validation="required"
         as |field|
       >
         <field.Input />
       </form.Field>

       <form.Field @name="age" @title="Age" as |field|>
         <field.Input @type="number" />
       </form.Field>

       <form.Submit />
     </Form>
   </template>
   ```

2. **Validation**:
   - Built-in validation rules such as `required`, `number`, `length`, and `url`.
   - Custom validation callbacks for more complex validation logic.

   **Example Usage**:
   ```javascript
   validateUsername(name, value, data, { addError }) {
     if (data.bar / 2 === value) {
       addError(name, "That's not how maths work.");
     }
   }
   ```

   ```hbs
   <form.Field @name="username" @validate={{this.validateUsername}} />
   ```

3. **Customization**:
   - Plugin outlets for extending form functionality.
   - Styling capabilities through propagated attributes.
   - Custom controls with properties provided by `form` and `field`.

   **Example Usage**:
   ```hbs
   <Form class="my-form" as |form|>
     <form.Field class="my-field" as |field|>
       <MyCustomControl id={{field.id}} @onChange={{field.set}} />
     </form.Field>
   </Form>
   ```

4. **Helpers for Testing**:
   - Test assertions for form and field validation.

   **Example usage**:
   ```javascript
   assert.form().hasErrors("the form shows errors");
   assert.form().field("foo").hasValue("bar", "user has set the value");
   ```

   - Helper for interacting with he form

   **Example usage**:
   ```javascript
   await formKit().field("foo").fillIn("bar");
   ```

5. **Page Object for System Specs**:
   - Page objects for interacting with forms in system specs.
   - Methods for submitting forms, checking alerts, and interacting with fields.

   **Example Usage**:
   ```ruby
   form = PageObjects::Components::FormKit.new(".my-form")
   form.submit
   expect(form).to have_an_alert("message")
   ```

   **Field Interactions**:
   ```ruby
   field = form.field("foo")
   expect(field).to have_value("bar")
   field.fill_in("bar")
   ```


6. **Collections handling**:
   - A specific component to handle array of objects

   **Example Usage**:
   ```gjs
    <Form @data={{hash foo=(array (hash bar=1) (hash bar=2))}} as |form|>
      <form.Collection @name="foo" as |collection|>
        <collection.Field @name="bar" @title="Bar" as |field|>
          <field.Input />
        </collection.Field>
      </form.Collection>
    </Form>
   ```
2024-07-17 11:59:35 +02:00

328 lines
8.7 KiB
Handlebars

<Form
@data={{this.formData}}
@onSubmit={{this.handleSubmit}}
@validate={{this.validateForm}}
class="badge-form current-badge content-body"
as |form data|
>
<h1 class="current-badge-header">
{{iconOrImage data}}
<span class="badge-display-name">{{data.name}}</span>
</h1>
{{#if this.readOnly}}
<form.Alert @icon="info-circle">
{{i18n "admin.badges.disable_system"}}
</form.Alert>
{{else}}
<form.Field
@name="enabled"
@disabled={{this.readOnly}}
@validation="required"
@title={{i18n "admin.badges.status"}}
as |field|
>
<field.Question
@yesLabel={{i18n "admin.badges.enabled"}}
@noLabel={{i18n "admin.badges.disabled"}}
/>
</form.Field>
{{/if}}
{{#if this.readOnly}}
<form.Container data-name="name" @title={{i18n "admin.badges.name"}}>
<span class="readonly-field">
{{this.model.name}}
</span>
<LinkTo
@route="adminSiteText.edit"
@models={{array (concat this.textCustomizationPrefix "name")}}
@query={{hash locale="en"}}
>
{{d-icon "pencil-alt"}}
</LinkTo>
</form.Container>
{{else}}
<form.Field
@title={{i18n "admin.badges.name"}}
@name="name"
@disabled={{this.readOnly}}
@validation="required"
as |field|
>
<field.Input />
</form.Field>
{{/if}}
<form.Section @title="Design">
<form.Field
@name="badge_type_id"
@title={{i18n "admin.badges.badge_type"}}
@disabled={{this.readOnly}}
as |field|
>
<field.Select as |select|>
{{#each this.badgeTypes as |badgeType|}}
<select.Option @value={{badgeType.id}}>
{{badgeType.name}}
</select.Option>
{{/each}}
</field.Select>
</form.Field>
<form.ConditionalContent
@activeName={{if data.image "upload-image" "choose-icon"}}
as |cc|
>
<cc.Conditions as |Condition|>
<Condition @name="choose-icon">
{{i18n "admin.badges.select_an_icon"}}
</Condition>
<Condition @name="upload-image">
{{i18n "admin.badges.upload_an_image"}}
</Condition>
</cc.Conditions>
<cc.Contents as |Content|>
<Content @name="choose-icon">
<form.Field
@title={{i18n "admin.badges.icon"}}
@showTitle={{false}}
@name="icon"
@onSet={{this.onSetIcon}}
as |field|
>
<field.Icon />
</form.Field>
</Content>
<Content @name="upload-image">
<form.Field
@name="image"
@showTitle={{false}}
@title={{i18n "admin.badges.image"}}
@onSet={{this.onSetImage}}
@onUnset={{this.onUnsetImage}}
as |field|
>
<field.Image />
</form.Field>
</Content>
</cc.Contents>
</form.ConditionalContent>
{{#if this.readOnly}}
<form.Container
data-name="description"
@title={{i18n "admin.badges.description"}}
>
<span class="readonly-field">
{{this.model.description}}
</span>
<LinkTo
@route="adminSiteText.edit"
@models={{array (concat this.textCustomizationPrefix "description")}}
@query={{hash locale="en"}}
>
{{d-icon "pencil-alt"}}
</LinkTo>
</form.Container>
{{else}}
<form.Field
@title={{i18n "admin.badges.description"}}
@name="description"
@disabled={{this.readOnly}}
as |field|
>
<field.Textarea />
</form.Field>
{{/if}}
{{#if this.readOnly}}
<form.Container
data-name="long_description"
@title={{i18n "admin.badges.long_description"}}
>
<span class="readonly-field">
{{this.model.long_description}}
</span>
<LinkTo
@route="adminSiteText.edit"
@models={{array
(concat this.textCustomizationPrefix "long_description")
}}
@query={{hash locale="en"}}
>
{{d-icon "pencil-alt"}}
</LinkTo>
</form.Container>
{{else}}
<form.Field
@name="long_description"
@title={{i18n "admin.badges.long_description"}}
@disabled={{this.readOnly}}
as |field|
>
<field.Textarea />
</form.Field>
{{/if}}
</form.Section>
{{#if this.siteSettings.enable_badge_sql}}
<form.Section @title="Query">
<form.Field
@name="query"
@title={{i18n "admin.badges.query"}}
@disabled={{this.readOnly}}
as |field|
>
<field.Code @lang="sql" />
</form.Field>
{{#if (this.hasQuery data.query)}}
<form.Container>
<form.Button
@isLoading={{this.preview_loading}}
@label="admin.badges.preview.link_text"
class="preview-badge"
@action={{fn this.showPreview data "false"}}
/>
<form.Button
@isLoading={{this.preview_loading}}
@label="admin.badges.preview.plan_text"
class="preview-badge-plan"
@action={{fn this.showPreview data "true"}}
/>
</form.Container>
<form.Field
@name="auto_revoke"
@disabled={{this.readOnly}}
@showTitle={{false}}
@title={{i18n "admin.badges.auto_revoke_label"}}
as |field|
>
<field.Checkbox>
{{i18n "admin.badges.auto_revoke"}}
</field.Checkbox>
</form.Field>
<form.Field
@name="target_posts"
@disabled={{this.readOnly}}
@title={{i18n "admin.badges.target_posts_label"}}
@showTitle={{false}}
as |field|
>
<field.Checkbox>
{{i18n "admin.badges.target_posts"}}
</field.Checkbox>
</form.Field>
<form.Field
@name="trigger"
@disabled={{this.readOnly}}
@validation="required"
@title={{i18n "admin.badges.trigger"}}
as |field|
>
<field.Select as |select|>
{{#each this.badgeTriggers as |badgeType|}}
<select.Option @value={{badgeType.id}}>
{{badgeType.name}}
</select.Option>
{{/each}}
</field.Select>
</form.Field>
{{/if}}
</form.Section>
{{/if}}
<form.Section @title="Settings">
<form.Field
@name="badge_grouping_id"
@disabled={{this.readOnly}}
@validation="required"
@title={{i18n "admin.badges.badge_grouping"}}
as |field|
>
<field.Menu @selection={{this.currentBadgeGrouping data}} as |menu|>
{{#each this.badgeGroupings as |grouping|}}
<menu.Item @value={{grouping.id}}>{{grouping.name}}</menu.Item>
{{/each}}
<menu.Divider />
<menu.Item @action={{route-action "editGroupings"}}>Add new group</menu.Item>
</field.Menu>
</form.Field>
<form.Field
@title={{i18n "admin.badges.allow_title_label"}}
@showTitle={{false}}
@name="allow_title"
as |field|
>
<field.Checkbox>{{i18n "admin.badges.allow_title"}}</field.Checkbox>
</form.Field>
<form.Field
@title={{i18n "admin.badges.multiple_grant_label"}}
@showTitle={{false}}
@name="multiple_grant"
@disabled={{this.readOnly}}
as |field|
>
<field.Checkbox>{{i18n "admin.badges.multiple_grant"}}</field.Checkbox>
</form.Field>
<form.Field
@title={{i18n "admin.badges.listable_label"}}
@showTitle={{false}}
@name="listable"
@disabled={{this.readOnly}}
as |field|
>
<field.Checkbox>{{i18n "admin.badges.listable"}}</field.Checkbox>
</form.Field>
<form.Field
@title={{i18n "admin.badges.show_posts_label"}}
@showTitle={{false}}
@name="show_posts"
@disabled={{this.readOnly}}
as |field|
>
<field.Checkbox>{{i18n "admin.badges.show_posts"}}</field.Checkbox>
</form.Field>
</form.Section>
<PluginOutlet
@name="admin-above-badge-buttons"
@outletArgs={{hash badge=this.buffered form=form}}
/>
<form.Actions>
<form.Submit />
{{#unless this.readOnly}}
<form.Button @action={{this.handleDelete}} class="btn-danger">
{{i18n "admin.badges.delete"}}
</form.Button>
{{/unless}}
</form.Actions>
{{#if this.grant_count}}
<div class="content-body current-badge-actions">
<div>
<LinkTo @route="badges.show" @model={{this}}>
{{html-safe
(i18n
"badges.awarded"
count=this.displayCount
number=(number this.displayCount)
)
}}
</LinkTo>
</div>
</div>
{{/if}}
</Form>