commit
0fe4916288
24 changed files with 202 additions and 31 deletions
1
Gemfile
1
Gemfile
|
@ -84,6 +84,7 @@ gem 'sib-api-v3-sdk'
|
||||||
gem 'skylight'
|
gem 'skylight'
|
||||||
gem 'spreadsheet_architect'
|
gem 'spreadsheet_architect'
|
||||||
gem 'strong_migrations' # lint database migrations
|
gem 'strong_migrations' # lint database migrations
|
||||||
|
gem 'turbo-rails'
|
||||||
gem 'typhoeus'
|
gem 'typhoeus'
|
||||||
gem 'warden'
|
gem 'warden'
|
||||||
gem 'webpacker'
|
gem 'webpacker'
|
||||||
|
|
|
@ -723,6 +723,8 @@ GEM
|
||||||
timecop (0.9.4)
|
timecop (0.9.4)
|
||||||
timeout (0.1.1)
|
timeout (0.1.1)
|
||||||
ttfunk (1.7.0)
|
ttfunk (1.7.0)
|
||||||
|
turbo-rails (0.8.3)
|
||||||
|
rails (>= 6.0.0)
|
||||||
typhoeus (1.4.0)
|
typhoeus (1.4.0)
|
||||||
ethon (>= 0.9.0)
|
ethon (>= 0.9.0)
|
||||||
tzinfo (2.0.4)
|
tzinfo (2.0.4)
|
||||||
|
@ -899,6 +901,7 @@ DEPENDENCIES
|
||||||
spring-commands-rspec
|
spring-commands-rspec
|
||||||
strong_migrations
|
strong_migrations
|
||||||
timecop
|
timecop
|
||||||
|
turbo-rails
|
||||||
typhoeus
|
typhoeus
|
||||||
vcr
|
vcr
|
||||||
warden
|
warden
|
||||||
|
|
|
@ -22,3 +22,7 @@ a {
|
||||||
|
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
turbo-events {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ class RootController < ApplicationController
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html { redirect_back(fallback_location: root_path) }
|
format.html { redirect_back(fallback_location: root_path) }
|
||||||
format.js { render js: helpers.remove_element('#outdated-browser-banner') }
|
format.turbo_stream
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
35
app/helpers/turbo_stream_helper.rb
Normal file
35
app/helpers/turbo_stream_helper.rb
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
module TurboStreamHelper
|
||||||
|
def turbo_stream
|
||||||
|
TagBuilder.new(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
class TagBuilder < Turbo::Streams::TagBuilder
|
||||||
|
def dispatch(type, detail)
|
||||||
|
append_all('turbo-events', partial: 'layouts/turbo_event', locals: { type: type, detail: detail })
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(target, delay: nil)
|
||||||
|
dispatch('dom:mutation', { action: :show, target: target, delay: delay }.compact)
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_all(targets, delay: nil)
|
||||||
|
dispatch('dom:mutation', { action: :show, targets: targets, delay: delay }.compact)
|
||||||
|
end
|
||||||
|
|
||||||
|
def hide(target, delay: nil)
|
||||||
|
dispatch('dom:mutation', { action: :hide, target: target, delay: delay }.compact)
|
||||||
|
end
|
||||||
|
|
||||||
|
def hide_all(targets, delay: nil)
|
||||||
|
dispatch('dom:mutation', { action: :hide, targets: targets, delay: delay }.compact)
|
||||||
|
end
|
||||||
|
|
||||||
|
def focus(target)
|
||||||
|
dispatch('dom:mutation', { action: :focus, target: target })
|
||||||
|
end
|
||||||
|
|
||||||
|
def focus_all(targets)
|
||||||
|
dispatch('dom:mutation', { action: :focus, targets: targets })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
91
app/javascript/controllers/turbo_event_controller.ts
Normal file
91
app/javascript/controllers/turbo_event_controller.ts
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
import { Controller } from '@hotwired/stimulus';
|
||||||
|
import invariant from 'tiny-invariant';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
type Detail = Record<string, unknown>;
|
||||||
|
|
||||||
|
export class TurboEventController extends Controller {
|
||||||
|
static values = {
|
||||||
|
type: String,
|
||||||
|
detail: Object
|
||||||
|
};
|
||||||
|
|
||||||
|
declare readonly typeValue: string;
|
||||||
|
declare readonly detailValue: Detail;
|
||||||
|
|
||||||
|
connect(): void {
|
||||||
|
this.globalDispatch(this.typeValue, this.detailValue);
|
||||||
|
this.element.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
private globalDispatch(type: string, detail: Detail): void {
|
||||||
|
this.dispatch(type, {
|
||||||
|
detail,
|
||||||
|
prefix: '',
|
||||||
|
target: document.documentElement
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MutationAction = z.enum(['show', 'hide', 'focus']);
|
||||||
|
type MutationAction = z.infer<typeof MutationAction>;
|
||||||
|
const Mutation = z.union([
|
||||||
|
z.object({
|
||||||
|
action: MutationAction,
|
||||||
|
delay: z.number().optional(),
|
||||||
|
target: z.string()
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
action: MutationAction,
|
||||||
|
delay: z.number().optional(),
|
||||||
|
targets: z.string()
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
type Mutation = z.infer<typeof Mutation>;
|
||||||
|
|
||||||
|
addEventListener('dom:mutation', (event) => {
|
||||||
|
const detail = (event as CustomEvent).detail;
|
||||||
|
const mutation = Mutation.parse(detail);
|
||||||
|
mutate(mutation);
|
||||||
|
});
|
||||||
|
|
||||||
|
const Mutations: Record<MutationAction, (mutation: Mutation) => void> = {
|
||||||
|
hide: (mutation) => {
|
||||||
|
for (const element of findElements(mutation)) {
|
||||||
|
element.classList.add('hidden');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
show: (mutation) => {
|
||||||
|
for (const element of findElements(mutation)) {
|
||||||
|
element.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
focus: (mutation) => {
|
||||||
|
for (const element of findElements(mutation)) {
|
||||||
|
element.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function mutate(mutation: Mutation) {
|
||||||
|
const fn = Mutations[mutation.action];
|
||||||
|
invariant(fn, `Could not find mutation ${mutation.action}`);
|
||||||
|
if (mutation.delay) {
|
||||||
|
setTimeout(() => fn(mutation), mutation.delay);
|
||||||
|
} else {
|
||||||
|
fn(mutation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function findElements<Element extends HTMLElement = HTMLElement>(
|
||||||
|
mutation: Mutation
|
||||||
|
): Element[] {
|
||||||
|
if ('target' in mutation) {
|
||||||
|
const element = document.querySelector<Element>(`#${mutation.target}`);
|
||||||
|
invariant(element, `Could not find element with id ${mutation.target}`);
|
||||||
|
return [element];
|
||||||
|
} else if ('targets' in mutation) {
|
||||||
|
return [...document.querySelectorAll<Element>(mutation.targets)];
|
||||||
|
}
|
||||||
|
invariant(false, 'Could not find element');
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import Rails from '@rails/ujs';
|
||||||
import * as ActiveStorage from '@rails/activestorage';
|
import * as ActiveStorage from '@rails/activestorage';
|
||||||
import 'whatwg-fetch'; // window.fetch polyfill
|
import 'whatwg-fetch'; // window.fetch polyfill
|
||||||
import { Application } from '@hotwired/stimulus';
|
import { Application } from '@hotwired/stimulus';
|
||||||
|
import { Turbo } from '@hotwired/turbo-rails';
|
||||||
|
|
||||||
import '../shared/page-update-event';
|
import '../shared/page-update-event';
|
||||||
import '../shared/activestorage/ujs';
|
import '../shared/activestorage/ujs';
|
||||||
|
@ -17,6 +18,7 @@ import {
|
||||||
ReactController,
|
ReactController,
|
||||||
registerComponents
|
registerComponents
|
||||||
} from '../controllers/react_controller';
|
} from '../controllers/react_controller';
|
||||||
|
import { TurboEventController } from '../controllers/turbo_event_controller';
|
||||||
|
|
||||||
import '../new_design/dropdown';
|
import '../new_design/dropdown';
|
||||||
import '../new_design/form-validation';
|
import '../new_design/form-validation';
|
||||||
|
@ -89,9 +91,11 @@ const DS = {
|
||||||
// Start Rails helpers
|
// Start Rails helpers
|
||||||
Rails.start();
|
Rails.start();
|
||||||
ActiveStorage.start();
|
ActiveStorage.start();
|
||||||
|
Turbo.session.drive = false;
|
||||||
|
|
||||||
const Stimulus = Application.start();
|
const Stimulus = Application.start();
|
||||||
Stimulus.register('react', ReactController);
|
Stimulus.register('react', ReactController);
|
||||||
|
Stimulus.register('turbo-event', TurboEventController);
|
||||||
|
|
||||||
// Expose globals
|
// Expose globals
|
||||||
window.DS = window.DS || DS;
|
window.DS = window.DS || DS;
|
||||||
|
|
|
@ -33,7 +33,7 @@ module MailTemplateConcern
|
||||||
module ClassMethods
|
module ClassMethods
|
||||||
def default_for_procedure(procedure)
|
def default_for_procedure(procedure)
|
||||||
template_name = default_template_name_for_procedure(procedure)
|
template_name = default_template_name_for_procedure(procedure)
|
||||||
rich_body = ActionController::Base.new.render_to_string(template: template_name)
|
rich_body = ActionController::Base.render template: template_name
|
||||||
trix_rich_body = rich_body.gsub(/(?<!^|[.-])(?<!<\/strong>)\n/, '')
|
trix_rich_body = rich_body.gsub(/(?<!^|[.-])(?<!<\/strong>)\n/, '')
|
||||||
new(subject: const_get(:DEFAULT_SUBJECT), rich_body: trix_rich_body, procedure: procedure)
|
new(subject: const_get(:DEFAULT_SUBJECT), rich_body: trix_rich_body, procedure: procedure)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
= form_for procedure.administrateurs.new(user: User.new),
|
= form_for procedure.administrateurs.new(user: User.new),
|
||||||
url: { controller: 'procedure_administrateurs' },
|
url: { controller: 'procedure_administrateurs' },
|
||||||
html: { class: 'form', id: "procedure-#{procedure.id}-new_administrateur" } ,
|
html: { class: 'form', id: "new_administrateur" },
|
||||||
remote: true do |f|
|
data: { turbo: true } do |f|
|
||||||
= f.label :email do
|
= f.label :email do
|
||||||
Ajouter un administrateur
|
Ajouter un administrateur
|
||||||
%p.notice Renseignez l’email d’un administrateur déjà enregistré sur #{APPLICATION_NAME} pour lui permettre de modifier « #{procedure.libelle} ».
|
%p.notice Renseignez l’email d’un administrateur déjà enregistré sur #{APPLICATION_NAME} pour lui permettre de modifier « #{procedure.libelle} ».
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
%tr{ id: "procedure-#{@procedure.id}-administrateur-#{administrateur.id}" }
|
%tr{ id: dom_id(administrateur) }
|
||||||
%td= administrateur.email
|
%td= administrateur.email
|
||||||
%td= try_format_datetime(administrateur.created_at)
|
%td= try_format_datetime(administrateur.created_at)
|
||||||
%td= administrateur.registration_state
|
%td= administrateur.registration_state
|
||||||
|
@ -6,8 +6,8 @@
|
||||||
- if administrateur == current_administrateur
|
- if administrateur == current_administrateur
|
||||||
C’est vous !
|
C’est vous !
|
||||||
- else
|
- else
|
||||||
= link_to 'Retirer',
|
= button_to 'Retirer',
|
||||||
admin_procedure_administrateur_path(@procedure, administrateur),
|
admin_procedure_administrateur_path(procedure, administrateur),
|
||||||
method: :delete,
|
method: :delete,
|
||||||
'data-confirm': "Retirer « #{administrateur.email} » des administrateurs de « #{@procedure.libelle} » ?",
|
class: 'button',
|
||||||
remote: true
|
form: { data: { turbo: true, turbo_confirm: "Retirer « #{administrateur.email} » des administrateurs de « #{procedure.libelle} » ?" } }
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
= render_flash(sticky: true)
|
|
||||||
- if @administrateur
|
|
||||||
= append_to_element("#procedure-#{@procedure.id}-administrateurs",
|
|
||||||
partial: 'administrateur',
|
|
||||||
locals: { administrateur: @administrateur })
|
|
||||||
= render_to_element("#procedure-#{@procedure.id}-new_administrateur",
|
|
||||||
partial: 'add_admin_form',
|
|
||||||
outer: true,
|
|
||||||
locals: { procedure: @procedure })
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
- if @administrateur.present?
|
||||||
|
= turbo_stream.append "administrateurs", partial: 'administrateur', locals: { procedure: @procedure, administrateur: @administrateur }
|
||||||
|
= turbo_stream.replace "new_administrateur", partial: 'add_admin_form', locals: { procedure: @procedure }
|
|
@ -1,4 +0,0 @@
|
||||||
= render_flash(sticky: true)
|
|
||||||
- if @administrateur
|
|
||||||
= remove_element("#procedure-#{@procedure.id}-administrateur-#{@administrateur.id}")
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
- if @administrateur.present?
|
||||||
|
= turbo_stream.remove(@administrateur)
|
|
@ -10,8 +10,8 @@
|
||||||
%th= 'Adresse email'
|
%th= 'Adresse email'
|
||||||
%th= 'Enregistré le'
|
%th= 'Enregistré le'
|
||||||
%th= 'État'
|
%th= 'État'
|
||||||
%tbody{ id: "procedure-#{@procedure.id}-administrateurs" }
|
%tbody#administrateurs
|
||||||
= render partial: 'administrateur', collection: @procedure.administrateurs.order('users.email')
|
= render partial: 'administrateur', collection: @procedure.administrateurs.order('users.email'), locals: { procedure: @procedure }
|
||||||
%tfoot
|
%tfoot
|
||||||
%tr
|
%tr
|
||||||
%th{ colspan: 4 }
|
%th{ colspan: 4 }
|
||||||
|
|
|
@ -16,6 +16,6 @@
|
||||||
%br
|
%br
|
||||||
Certaines parties du site ne fonctionneront pas correctement.
|
Certaines parties du site ne fonctionneront pas correctement.
|
||||||
.site-banner-actions
|
.site-banner-actions
|
||||||
= button_to 'Ignorer', dismiss_outdated_browser_path, method: :post, remote: true, class: 'button btn', title: 'Ne plus afficher cet avertissement pendant une semaine'
|
= button_to 'Ignorer', dismiss_outdated_browser_path, method: :post, form: { data: { turbo: true } }, class: 'button btn', title: 'Ne plus afficher cet avertissement pendant une semaine'
|
||||||
%a.btn.button.primary{ href: "https://browser-update.org/fr/update.html", target: "_blank", rel: "noopener" }
|
%a.btn.button.primary{ href: "https://browser-update.org/fr/update.html", target: "_blank", rel: "noopener" }
|
||||||
Mettre à jour mon navigateur
|
Mettre à jour mon navigateur
|
||||||
|
|
5
app/views/layouts/_turbo_event.html.haml
Normal file
5
app/views/layouts/_turbo_event.html.haml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
%turbo-event{ data: {
|
||||||
|
controller: 'turbo-event',
|
||||||
|
turbo_event_type_value: type,
|
||||||
|
turbo_event_detail_value: detail.to_json
|
||||||
|
} }
|
|
@ -41,3 +41,5 @@
|
||||||
= content_for(:footer)
|
= content_for(:footer)
|
||||||
|
|
||||||
= yield :charts_js
|
= yield :charts_js
|
||||||
|
|
||||||
|
%turbo-events
|
||||||
|
|
6
app/views/layouts/application.turbo_stream.haml
Normal file
6
app/views/layouts/application.turbo_stream.haml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
- if flash.any?
|
||||||
|
= turbo_stream.replace 'flash_messages', partial: 'layouts/flash_messages'
|
||||||
|
= turbo_stream.hide 'flash_messages', delay: 10000
|
||||||
|
- flash.clear
|
||||||
|
|
||||||
|
= yield
|
|
@ -0,0 +1 @@
|
||||||
|
= turbo_stream.remove('outdated-browser-banner')
|
|
@ -5,6 +5,7 @@
|
||||||
"@headlessui/react": "^1.5.0",
|
"@headlessui/react": "^1.5.0",
|
||||||
"@heroicons/react": "^1.0.6",
|
"@heroicons/react": "^1.0.6",
|
||||||
"@hotwired/stimulus": "^3.0.1",
|
"@hotwired/stimulus": "^3.0.1",
|
||||||
|
"@hotwired/turbo-rails": "^7.1.1",
|
||||||
"@mapbox/mapbox-gl-draw": "^1.3.0",
|
"@mapbox/mapbox-gl-draw": "^1.3.0",
|
||||||
"@popperjs/core": "^2.11.4",
|
"@popperjs/core": "^2.11.4",
|
||||||
"@rails/actiontext": "^6.1.4-1",
|
"@rails/actiontext": "^6.1.4-1",
|
||||||
|
@ -42,7 +43,8 @@
|
||||||
"use-debounce": "^5.2.0",
|
"use-debounce": "^5.2.0",
|
||||||
"webpack": "^4.46.0",
|
"webpack": "^4.46.0",
|
||||||
"webpack-cli": "^3.3.12",
|
"webpack-cli": "^3.3.12",
|
||||||
"whatwg-fetch": "^3.0.0"
|
"whatwg-fetch": "^3.0.0",
|
||||||
|
"zod": "^3.14.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@2fd/graphdoc": "^2.4.0",
|
"@2fd/graphdoc": "^2.4.0",
|
||||||
|
|
|
@ -2,6 +2,7 @@ describe Administrateurs::ProcedureAdministrateursController, type: :controller
|
||||||
let(:signed_in_admin) { create(:administrateur) }
|
let(:signed_in_admin) { create(:administrateur) }
|
||||||
let(:other_admin) { create(:administrateur) }
|
let(:other_admin) { create(:administrateur) }
|
||||||
let(:procedure) { create(:procedure, administrateurs: [signed_in_admin, other_admin]) }
|
let(:procedure) { create(:procedure, administrateurs: [signed_in_admin, other_admin]) }
|
||||||
|
render_views
|
||||||
|
|
||||||
before do
|
before do
|
||||||
sign_in(signed_in_admin.user)
|
sign_in(signed_in_admin.user)
|
||||||
|
@ -9,7 +10,7 @@ describe Administrateurs::ProcedureAdministrateursController, type: :controller
|
||||||
|
|
||||||
describe '#destroy' do
|
describe '#destroy' do
|
||||||
subject do
|
subject do
|
||||||
delete :destroy, params: { procedure_id: procedure.id, id: admin_to_remove.id }, format: :js, xhr: true
|
delete :destroy, params: { procedure_id: procedure.id, id: admin_to_remove.id }, format: :turbo_stream
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when removing another admin' do
|
context 'when removing another admin' do
|
||||||
|
@ -17,8 +18,8 @@ describe Administrateurs::ProcedureAdministrateursController, type: :controller
|
||||||
|
|
||||||
it 'removes the admin from the procedure' do
|
it 'removes the admin from the procedure' do
|
||||||
subject
|
subject
|
||||||
expect(response.status).to eq(200)
|
expect(response).to have_http_status(:ok)
|
||||||
expect(flash[:notice]).to be_present
|
expect(subject.body).to include('alert-success')
|
||||||
expect(admin_to_remove.procedures.reload).not_to include(procedure)
|
expect(admin_to_remove.procedures.reload).not_to include(procedure)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -28,8 +29,8 @@ describe Administrateurs::ProcedureAdministrateursController, type: :controller
|
||||||
|
|
||||||
it 'denies the right for an admin to remove itself' do
|
it 'denies the right for an admin to remove itself' do
|
||||||
subject
|
subject
|
||||||
expect(response.status).to eq(200)
|
expect(response).to have_http_status(:ok)
|
||||||
expect(flash[:alert]).to be_present
|
expect(subject.body).to include('alert-danger')
|
||||||
expect(admin_to_remove.procedures.reload).to include(procedure)
|
expect(admin_to_remove.procedures.reload).to include(procedure)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
describe DevisePopulatedResource, type: :controller do
|
describe DevisePopulatedResource, type: :controller do
|
||||||
controller(Devise::PasswordsController) do
|
controller(Devise::PasswordsController) do
|
||||||
include DevisePopulatedResource
|
include DevisePopulatedResource
|
||||||
|
layout false
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
|
|
23
yarn.lock
23
yarn.lock
|
@ -1274,6 +1274,19 @@
|
||||||
resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.0.1.tgz#141f15645acaa3b133b7c247cad58ae252ffae85"
|
resolved "https://registry.yarnpkg.com/@hotwired/stimulus/-/stimulus-3.0.1.tgz#141f15645acaa3b133b7c247cad58ae252ffae85"
|
||||||
integrity sha512-oHsJhgY2cip+K2ED7vKUNd2P+BEswVhrCYcJ802DSsblJFv7mPFVk3cQKvm2vHgHeDVdnj7oOKrBbzp1u8D+KA==
|
integrity sha512-oHsJhgY2cip+K2ED7vKUNd2P+BEswVhrCYcJ802DSsblJFv7mPFVk3cQKvm2vHgHeDVdnj7oOKrBbzp1u8D+KA==
|
||||||
|
|
||||||
|
"@hotwired/turbo-rails@^7.1.1":
|
||||||
|
version "7.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@hotwired/turbo-rails/-/turbo-rails-7.1.1.tgz#35c03b92b5c86f0137ed08bef843d955ec9bbe83"
|
||||||
|
integrity sha512-ZXpxUjCfkdbuXfoGrsFK80qsVzACs8xCfie9rt2jMTSN6o1olXVA0Nrk8u02yNEwSiVJm/4QSOa8cUcMj6VQjg==
|
||||||
|
dependencies:
|
||||||
|
"@hotwired/turbo" "^7.1.0"
|
||||||
|
"@rails/actioncable" "^7.0"
|
||||||
|
|
||||||
|
"@hotwired/turbo@^7.1.0":
|
||||||
|
version "7.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@hotwired/turbo/-/turbo-7.1.0.tgz#27e44e0e3dc5bd1d4bda0766d579cf5a14091cd7"
|
||||||
|
integrity sha512-Q8kGjqwPqER+CtpQudbH+3Zgs2X4zb6pBAlr6NsKTXadg45pAOvxI9i4QpuHbwSzR2+x87HUm+rot9F/Pe8rxA==
|
||||||
|
|
||||||
"@humanwhocodes/config-array@^0.5.0":
|
"@humanwhocodes/config-array@^0.5.0":
|
||||||
version "0.5.0"
|
version "0.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9"
|
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9"
|
||||||
|
@ -1949,6 +1962,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.4.tgz#d8c7b8db9226d2d7664553a0741ad7d0397ee503"
|
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.4.tgz#d8c7b8db9226d2d7664553a0741ad7d0397ee503"
|
||||||
integrity sha512-q/ytXxO5NKvyT37pmisQAItCFqA7FD/vNb8dgaJy3/630Fsc+Mz9/9f2SziBoIZ30TJooXyTwZmhi1zjXmObYg==
|
integrity sha512-q/ytXxO5NKvyT37pmisQAItCFqA7FD/vNb8dgaJy3/630Fsc+Mz9/9f2SziBoIZ30TJooXyTwZmhi1zjXmObYg==
|
||||||
|
|
||||||
|
"@rails/actioncable@^7.0":
|
||||||
|
version "7.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@rails/actioncable/-/actioncable-7.0.2.tgz#69a6d999f4087e0537dd38fe0963db1f4305d650"
|
||||||
|
integrity sha512-G26maXW1Kx0LxQdmNNuNjQlRO/QlXNr3QfuwKiOKb5FZQGYe2OwtHTGXBAjSoiu4dW36XYMT/+L1Wo1Yov4ZXA==
|
||||||
|
|
||||||
"@rails/actiontext@^6.1.4-1":
|
"@rails/actiontext@^6.1.4-1":
|
||||||
version "6.1.4"
|
version "6.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@rails/actiontext/-/actiontext-6.1.4.tgz#ed8c7d2b68d66205301f4538ce65d04c48031f6b"
|
resolved "https://registry.yarnpkg.com/@rails/actiontext/-/actiontext-6.1.4.tgz#ed8c7d2b68d66205301f4538ce65d04c48031f6b"
|
||||||
|
@ -14014,3 +14032,8 @@ zip-stream@^4.1.0:
|
||||||
archiver-utils "^2.1.0"
|
archiver-utils "^2.1.0"
|
||||||
compress-commons "^4.1.0"
|
compress-commons "^4.1.0"
|
||||||
readable-stream "^3.6.0"
|
readable-stream "^3.6.0"
|
||||||
|
|
||||||
|
zod@^3.14.4:
|
||||||
|
version "3.14.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/zod/-/zod-3.14.4.tgz#e678fe9e5469f4663165a5c35c8f3dc66334a5d6"
|
||||||
|
integrity sha512-U9BFLb2GO34Sfo9IUYp0w3wJLlmcyGoMd75qU9yf+DrdGA4kEx6e+l9KOkAlyUO0PSQzZCa3TR4qVlcmwqSDuw==
|
||||||
|
|
Loading…
Reference in a new issue