rename template

This commit is contained in:
Pete Matsyburka committed 2026-09-23 10:59:54 +03:00
1 parent 9678cd1286
commit ca009252ac
8 files changed
+159 -34

No files matched your search

+42 -26
View File
@@ -13,6 +13,7 @@ import NativeAction from './elements/native_action'
import NativeSearch from './elements/native_search' import NativeSearch from './elements/native_search'
import NativeModal from './elements/native_modal' import NativeModal from './elements/native_modal'
import NativeEvent from './elements/native_event' import NativeEvent from './elements/native_event'
import FormPrompt from './elements/form_prompt'
import InfiniteScroll from './elements/infinite_scroll' import InfiniteScroll from './elements/infinite_scroll'
import ModalButton from './elements/modal_button' import ModalButton from './elements/modal_button'
import FileDropzone from './elements/file_dropzone' import FileDropzone from './elements/file_dropzone'
@@ -128,6 +129,7 @@ safeRegisterElement('native-action', NativeAction)
safeRegisterElement('native-search', NativeSearch) safeRegisterElement('native-search', NativeSearch)
safeRegisterElement('native-modal', NativeModal) safeRegisterElement('native-modal', NativeModal)
safeRegisterElement('native-event', NativeEvent) safeRegisterElement('native-event', NativeEvent)
safeRegisterElement('form-prompt', FormPrompt)
safeRegisterElement('infinite-scroll', InfiniteScroll) safeRegisterElement('infinite-scroll', InfiniteScroll)
safeRegisterElement('modal-button', ModalButton) safeRegisterElement('modal-button', ModalButton)
safeRegisterElement('file-dropzone', FileDropzone) safeRegisterElement('file-dropzone', FileDropzone)
@@ -186,6 +188,7 @@ safeRegisterElement('date-placeholder', DatePlaceholder)
safeRegisterElement('template-builder', class extends HTMLElement { safeRegisterElement('template-builder', class extends HTMLElement {
connectedCallback () { connectedCallback () {
document.addEventListener('turbo:submit-end', this.onSubmit) document.addEventListener('turbo:submit-end', this.onSubmit)
document.addEventListener('template-builder:update', this.onSheetSubmit)
document.addEventListener('turbo:before-cache', this.onBeforeCache) document.addEventListener('turbo:before-cache', this.onBeforeCache)
this.appElem = document.createElement('div') this.appElem = document.createElement('div')
@@ -238,36 +241,48 @@ safeRegisterElement('template-builder', class extends HTMLElement {
this.appendChild(this.appElem) this.appendChild(this.appElem)
} }
onSubmit = (e) => { onSubmit = async (e) => {
if (e.detail.success) { const form = e.detail.formSubmission?.formElement
if (e.detail?.formSubmission?.formElement?.id === 'submitters_form') {
e.detail.fetchResponse.response.json().then((data) => {
this.component.template.submitters = data.submitters
})
}
if (e.detail?.formSubmission?.formElement?.action?.endsWith('/prefillable_fields')) { if (!e.detail.success || !form) return
e.detail.fetchResponse.response.text().then((data) => {
const doc = new DOMParser().parseFromString(data, 'text/html')
const fragment = doc.querySelector('turbo-stream template').content
const prefillableUuidsIndex = {} const data = {}
fragment.querySelectorAll('[name="field_uuid"]').forEach((field) => { new FormData(form).forEach((value, key) => { data[key] = value })
prefillableUuidsIndex[field.value] = true
})
this.component.template.fields.forEach((field) => { this.applySubmission(form.id, form.action, await e.detail.fetchResponse.responseText, data)
if (prefillableUuidsIndex[field.uuid]) { }
field.prefillable = true
field.readonly = true onSheetSubmit = (e) => {
} else if (field.prefillable) { this.applySubmission(e.detail.form, e.detail.action, e.detail.body, {})
delete field.prefillable }
delete field.readonly
} applySubmission (formId, action, body, data) {
}) if (formId === 'submitters_form') {
}) this.component.template.submitters = JSON.parse(body).submitters
} } else if (action.endsWith('/prefillable_fields')) {
const doc = new DOMParser().parseFromString(body, 'text/html')
const fragment = doc.querySelector('turbo-stream template').content
const prefillableUuidsIndex = {}
fragment.querySelectorAll('[name="field_uuid"]').forEach((field) => {
prefillableUuidsIndex[field.value] = true
})
this.component.template.fields.forEach((field) => {
if (prefillableUuidsIndex[field.uuid]) {
field.prefillable = true
field.readonly = true
} else if (field.prefillable) {
delete field.prefillable
delete field.readonly
}
})
} else if (data['template[name]']) {
this.component.template.name = data['template[name]']
document.title = data['template[name]']
} }
} }
@@ -278,6 +293,7 @@ safeRegisterElement('template-builder', class extends HTMLElement {
disconnectedCallback () { disconnectedCallback () {
document.removeEventListener('turbo:submit-end', this.onSubmit) document.removeEventListener('turbo:submit-end', this.onSubmit)
document.removeEventListener('template-builder:update', this.onSheetSubmit)
document.removeEventListener('turbo:before-cache', this.onBeforeCache) document.removeEventListener('turbo:before-cache', this.onBeforeCache)
this.app?.unmount() this.app?.unmount()
+33
View File
@@ -0,0 +1,33 @@
export default class extends HTMLElement {
connectedCallback () {
this.addEventListener('click', this.prompt)
document.addEventListener('turbo:submit-end', this.onSubmitEnd)
}
disconnectedCallback () {
this.removeEventListener('click', this.prompt)
document.removeEventListener('turbo:submit-end', this.onSubmitEnd)
}
get form () {
return this.querySelector('form')
}
prompt = () => {
const label = this.form.querySelector('label')
const input = this.form.querySelector(`#${CSS.escape(label.htmlFor)}`)
const value = window.prompt(label.textContent.trim(), input.value)?.trim()
if (!value || value === input.value) return
input.value = value
this.form.requestSubmit()
}
onSubmitEnd = (e) => {
if (e.detail.success && e.detail.formSubmission?.formElement === this.form && this.dataset.refresh !== 'false') {
window.Turbo.visit(window.location.href, { action: 'replace' })
}
}
}
+20 -5
View File
@@ -1,16 +1,27 @@
export default class extends HTMLElement { export default class extends HTMLElement {
connectedCallback () { connectedCallback () {
this.addEventListener('click', this.dispatch) if (this.dataset.on === 'submit') {
document.addEventListener('turbo:submit-end', this.onSubmitEnd)
} else {
this.addEventListener('click', this.dispatch)
}
} }
disconnectedCallback () { disconnectedCallback () {
document.removeEventListener('turbo:submit-end', this.onSubmitEnd)
this.removeEventListener('click', this.dispatch) this.removeEventListener('click', this.dispatch)
} }
dispatch = async () => { onSubmitEnd = async (e) => {
const bridge = window.webkit?.messageHandlers?.modal const form = e.detail.formSubmission?.formElement
if (!bridge || this.dataset.loading) return if (!e.detail.success || !form || !this.contains(form)) return
this.post(JSON.stringify({ form: form.id, action: form.action, body: await e.detail.fetchResponse.responseText }))
}
dispatch = async () => {
if (this.dataset.loading) return
let detail = this.dataset.detail || 'null' let detail = this.dataset.detail || 'null'
@@ -30,6 +41,10 @@ export default class extends HTMLElement {
} }
} }
bridge.postMessage({ action: 'dispatch', name: this.dataset.name, detail }) this.post(detail)
}
post (detail) {
window.webkit?.messageHandlers?.modal?.postMessage({ action: 'dispatch', name: this.dataset.name, detail, dismiss: this.dataset.dismiss || 'true' })
} }
} }
+1
View File
@@ -13,6 +13,7 @@ export default class extends HTMLElement {
if (e.detail.formSubmission?.formElement?.method === 'get') return if (e.detail.formSubmission?.formElement?.method === 'get') return
if (this.dataset.closeAfterSubmit === 'false') return if (this.dataset.closeAfterSubmit === 'false') return
if (e.detail.formSubmission?.formElement?.dataset?.closeOnSubmit === 'false') return if (e.detail.formSubmission?.formElement?.dataset?.closeOnSubmit === 'false') return
if (e.detail.formSubmission?.formElement?.closest('native-event[data-on="submit"]')) return
window.webkit?.messageHandlers?.modal?.postMessage({ action: 'submitted' }) window.webkit?.messageHandlers?.modal?.postMessage({ action: 'submitted' })
} }
@@ -103,6 +103,42 @@
name="buttons" name="buttons"
/> />
<template v-else> <template v-else>
<form-prompt
v-if="nativeApp && editable"
data-refresh="false"
>
<native-action
data-placement="title"
:data-label="t('name')"
>
<form
class="hidden"
method="post"
:action="`/templates/${template.id}`"
>
<input
type="hidden"
name="_method"
value="put"
autocomplete="off"
>
<input
type="hidden"
name="authenticity_token"
:value="authenticityToken"
autocomplete="off"
>
<label for="template_name_prompt">{{ t('name') }}</label>
<input
id="template_name_prompt"
type="hidden"
name="template[name]"
:value="template.name"
autocomplete="off"
>
</form>
</native-action>
</form-prompt>
<native-action <native-action
v-if="withSendButton && nativeApp" v-if="withSendButton && nativeApp"
:data-label="t('send')" :data-label="t('send')"
+7
View File
@@ -1,4 +1,5 @@
const en = { const en = {
name: 'Name',
fixed: 'Fixed', fixed: 'Fixed',
default: 'Default', default: 'Default',
save_as_custom_field: 'Save as custom field', save_as_custom_field: 'Save as custom field',
@@ -243,6 +244,7 @@ const en = {
} }
const es = { const es = {
name: 'Nombre',
fixed: 'Fijo', fixed: 'Fijo',
default: 'Predeterminado', default: 'Predeterminado',
save_as_custom_field: 'Guardar como personalizado', save_as_custom_field: 'Guardar como personalizado',
@@ -487,6 +489,7 @@ const es = {
} }
const it = { const it = {
name: 'Nome',
fixed: 'Fisso', fixed: 'Fisso',
default: 'Predefinito', default: 'Predefinito',
save_as_custom_field: 'Salva come personalizzato', save_as_custom_field: 'Salva come personalizzato',
@@ -731,6 +734,7 @@ const it = {
} }
const pt = { const pt = {
name: 'Nome',
fixed: 'Fixo', fixed: 'Fixo',
default: 'Padrão', default: 'Padrão',
save_as_custom_field: 'Salvar como personalizado', save_as_custom_field: 'Salvar como personalizado',
@@ -975,6 +979,7 @@ const pt = {
} }
const fr = { const fr = {
name: 'Nom',
fixed: 'Fixe', fixed: 'Fixe',
default: 'Par défaut', default: 'Par défaut',
save_as_custom_field: 'Enregistrer comme personnalisé', save_as_custom_field: 'Enregistrer comme personnalisé',
@@ -1219,6 +1224,7 @@ const fr = {
} }
const de = { const de = {
name: 'Name',
fixed: 'Fest', fixed: 'Fest',
default: 'Standard', default: 'Standard',
save_as_custom_field: 'Als benutzerdefiniert speichern', save_as_custom_field: 'Als benutzerdefiniert speichern',
@@ -1463,6 +1469,7 @@ const de = {
} }
const nl = { const nl = {
name: 'Naam',
fixed: 'Vast', fixed: 'Vast',
default: 'Standaard', default: 'Standaard',
save_as_custom_field: 'Opslaan als aangepast', save_as_custom_field: 'Opslaan als aangepast',
+14 -1
View File
@@ -2,8 +2,21 @@
<div class="relative flex items-start justify-between w-full space-x-0"> <div class="relative flex items-start justify-between w-full space-x-0">
<div> <div>
<div id="template_title" class="flex"> <div id="template_title" class="flex">
<% name_html = capture do %><% template.name.split(/(_)/).each do |item| %><%= item %><wbr><% end %><% end %>
<h1 class="text-3xl md:text-[2em] font-semibold" style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;"> <h1 class="text-3xl md:text-[2em] font-semibold" style="overflow: hidden; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;">
<% template.name.split(/(_)/).each do |item| %><%= item %><wbr><% end %> <% if turbo_native_app? && !template.archived_at? && can?(:update, template) %>
<form-prompt>
<native-action data-placement="title" data-label="<%= t('name') %>">
<%= form_for template, url: template_path(template), method: :put, html: { class: 'hidden' } do |f| %>
<%= f.label :name, t('name') %>
<%= f.hidden_field :name %>
<% end %>
</native-action>
<%= name_html %>
</form-prompt>
<% else %>
<%= name_html %>
<% end %>
<% if template.archived_at? %> <% if template.archived_at? %>
<span class="ml-1 badge badge-outline badge-lg align-middle hidden md:inline-flex"><%= t('archived') %></span> <span class="ml-1 badge badge-outline badge-lg align-middle hidden md:inline-flex"><%= t('archived') %></span>
<% end %> <% end %>
@@ -63,7 +63,9 @@
<%= t('form_preferences') %> <%= t('form_preferences') %>
</div> </div>
<div class="collapse-content"> <div class="collapse-content">
<%= render 'templates_prefillable_fields/form', template: @template %> <native-event data-on="submit" data-name="template-builder:update" data-dismiss="false" class="block">
<%= render 'templates_prefillable_fields/form', template: @template %>
</native-event>
<%= form_for @template, url: template_preferences_path(@template), method: :post, html: { autocomplete: 'off', class: 'mt-1' }, data: { close_on_submit: false } do |f| %> <%= form_for @template, url: template_preferences_path(@template), method: :post, html: { autocomplete: 'off', class: 'mt-1' }, data: { close_on_submit: false } do |f| %>
<toggle-on-submit data-element-id="form_saved_alert"></toggle-on-submit> <toggle-on-submit data-element-id="form_saved_alert"></toggle-on-submit>
<% configs = AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_COMPLETED_EMAIL_KEY).value %> <% configs = AccountConfigs.find_or_initialize_for_key(current_account, AccountConfig::SUBMITTER_COMPLETED_EMAIL_KEY).value %>
@@ -145,7 +147,9 @@
</div> </div>
<% if show_recipients %> <% if show_recipients %>
<div id="recipients" class="hidden mt-2 mb-4 px-5 pt-1 pb-2"> <div id="recipients" class="hidden mt-2 mb-4 px-5 pt-1 pb-2">
<%= render 'recipients', template: @template %> <native-event data-on="submit" data-name="template-builder:update" class="block">
<%= render 'recipients', template: @template %>
</native-event>
</div> </div>
<% end %> <% end %>
<% if show_api %> <% if show_api %>