attestation_template v2: edit / update
This commit is contained in:
parent
f6001c8eae
commit
af53a9b9db
5 changed files with 215 additions and 1 deletions
16
app/assets/stylesheets/attestation_template_2_edit.scss
Normal file
16
app/assets/stylesheets/attestation_template_2_edit.scss
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#attestation-edit {
|
||||||
|
.mention {
|
||||||
|
border: 1px solid var(--text-default-grey);
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
border: 1px solid #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tiptap {
|
||||||
|
padding: 8px;
|
||||||
|
|
||||||
|
min-height: 300px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,9 +9,22 @@ module Administrateurs
|
||||||
render layout: 'attestation'
|
render layout: 'attestation'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
@attestation_template
|
||||||
|
.update(json_body: editor_params)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def retrieve_attestation_template
|
def retrieve_attestation_template
|
||||||
@attestation_template = @procedure.attestation_template || @procedure.build_attestation_template
|
@attestation_template = @procedure.attestation_template || @procedure.build_attestation_template
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def editor_params
|
||||||
|
params.permit(content: [:type, content: [:type, :text, attrs: [:id, :label]]])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
175
app/javascript/controllers/attestation_controller.ts
Normal file
175
app/javascript/controllers/attestation_controller.ts
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
import { ApplicationController } from './application_controller';
|
||||||
|
import { Editor } from '@tiptap/core';
|
||||||
|
import StarterKit from '@tiptap/starter-kit';
|
||||||
|
import Mention from '@tiptap/extension-mention';
|
||||||
|
import tippy from 'tippy.js';
|
||||||
|
import { httpRequest } from '@utils';
|
||||||
|
|
||||||
|
export class AttestationController extends ApplicationController {
|
||||||
|
static values = {
|
||||||
|
tags: Array,
|
||||||
|
url: String
|
||||||
|
};
|
||||||
|
|
||||||
|
static targets = ['editor', 'bold'];
|
||||||
|
|
||||||
|
declare readonly tagsValue: string[];
|
||||||
|
declare readonly urlValue: string;
|
||||||
|
declare editor: Editor;
|
||||||
|
declare editorTarget: HTMLElement;
|
||||||
|
declare boldTarget: HTMLButtonElement;
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
const conf = {
|
||||||
|
element: this.editorTarget,
|
||||||
|
editorProps: {
|
||||||
|
attributes: {
|
||||||
|
class: 'fr-input'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
extensions: [
|
||||||
|
StarterKit,
|
||||||
|
Mention.configure({
|
||||||
|
HTMLAttributes: {
|
||||||
|
class: 'mention'
|
||||||
|
},
|
||||||
|
suggestion: {
|
||||||
|
items: ({ query }) => {
|
||||||
|
return this.tagsValue
|
||||||
|
.filter((item) =>
|
||||||
|
item.toLowerCase().startsWith(query.toLowerCase())
|
||||||
|
)
|
||||||
|
.slice(0, 5);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: () => {
|
||||||
|
let popup: { setProps: Function, hide: Function, destroy: Function }[];
|
||||||
|
let div: HTMLElement;
|
||||||
|
let selectedIndex = 0;
|
||||||
|
let items: string[];
|
||||||
|
let command: (props: object) => void;
|
||||||
|
|
||||||
|
const makeList = () => {
|
||||||
|
return items
|
||||||
|
.map((item, i) => {
|
||||||
|
if (i == selectedIndex) {
|
||||||
|
return `<li class='selected'>${item}</li>`;
|
||||||
|
} else {
|
||||||
|
return `<li>${item}</li>`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join('');
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
onStart: (props) => {
|
||||||
|
items = props.items;
|
||||||
|
command = props.command;
|
||||||
|
|
||||||
|
div = document.createElement('UL');
|
||||||
|
div.innerHTML = makeList();
|
||||||
|
|
||||||
|
if (!props.clientRect) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let body: Element = document.body;
|
||||||
|
|
||||||
|
popup = tippy(body, {
|
||||||
|
getReferenceClientRect: props.clientRect,
|
||||||
|
appendTo: () => this.element,
|
||||||
|
content: div,
|
||||||
|
showOnCreate: true,
|
||||||
|
interactive: true,
|
||||||
|
trigger: 'manual',
|
||||||
|
placement: 'bottom-start'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onUpdate(props) {
|
||||||
|
command = props.command;
|
||||||
|
items = props.items;
|
||||||
|
|
||||||
|
div.innerHTML = makeList();
|
||||||
|
|
||||||
|
if (!props.clientRect) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
popup[0].setProps({
|
||||||
|
getReferenceClientRect: props.clientRect
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onKeyDown(props) {
|
||||||
|
if (props.event.key === 'Escape') {
|
||||||
|
popup[0].hide();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.event.key === 'ArrowDown') {
|
||||||
|
selectedIndex = (selectedIndex + 1) % items.length;
|
||||||
|
div.innerHTML = makeList();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.event.key === 'ArrowUp') {
|
||||||
|
selectedIndex =
|
||||||
|
(selectedIndex + items.length - 1) % items.length;
|
||||||
|
div.innerHTML = makeList();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.event.key === 'Enter') {
|
||||||
|
const item = items[selectedIndex];
|
||||||
|
|
||||||
|
if (item) {
|
||||||
|
command({ id: item });
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
onExit() {
|
||||||
|
popup[0].destroy();
|
||||||
|
div.remove();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
content:
|
||||||
|
'<p>La situation de M. <span data-type="mention" data-id="nom"></span> dont la demande de logement social</p>'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.editor = new Editor(conf);
|
||||||
|
|
||||||
|
this.editor.on('transaction', () => {
|
||||||
|
this.boldTarget.disabled = !this.editor
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleBold()
|
||||||
|
.run();
|
||||||
|
|
||||||
|
if (this.editor.isActive('bold')) {
|
||||||
|
this.boldTarget.classList.add('fr-btn--secondary');
|
||||||
|
} else {
|
||||||
|
this.boldTarget.classList.remove('fr-btn--secondary');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bold() {
|
||||||
|
this.editor.chain().focus().toggleBold().run();
|
||||||
|
}
|
||||||
|
|
||||||
|
send() {
|
||||||
|
const json = this.editor.getJSON();
|
||||||
|
httpRequest(this.urlValue, { method: 'put', json }).json();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
#attestation-edit.fr-container.mt-2{
|
||||||
|
data: {
|
||||||
|
controller: 'attestation',
|
||||||
|
attestation_tags_value: @attestation_template.tags.map { _1[:libelle] },
|
||||||
|
attestation_url_value: admin_procedure_attestation_template_v2_path(@procedure)} }
|
||||||
|
|
||||||
|
%button.fr-btn{ data: { action: 'click->attestation#bold', attestation_target: 'bold' } } Gras
|
||||||
|
.editor.mt-2{ data: { attestation_target: 'editor' } }
|
||||||
|
|
||||||
|
%button.fr-btn.mt-2{ data: { action: 'click->attestation#send' } } Envoyer
|
|
@ -626,7 +626,7 @@ Rails.application.routes.draw do
|
||||||
get 'add_champ_engagement_juridique'
|
get 'add_champ_engagement_juridique'
|
||||||
end
|
end
|
||||||
|
|
||||||
resource :attestation_template_v2, only: [:show]
|
resource :attestation_template_v2, only: [:show, :edit, :update]
|
||||||
|
|
||||||
resource :dossier_submitted_message, only: [:edit, :update, :create]
|
resource :dossier_submitted_message, only: [:edit, :update, :create]
|
||||||
# ADDED TO ACCESS IT FROM THE IFRAME
|
# ADDED TO ACCESS IT FROM THE IFRAME
|
||||||
|
|
Loading…
Reference in a new issue