Merge from docusealco/wip

pull/402/head 2.2.3
Alex Turchyn 3 weeks ago committed by GitHub
commit dd5b6a6aa6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -51,6 +51,7 @@ import ToggleClasses from './elements/toggle_classes'
import AutosizeField from './elements/autosize_field' import AutosizeField from './elements/autosize_field'
import GoogleDriveFilePicker from './elements/google_drive_file_picker' import GoogleDriveFilePicker from './elements/google_drive_file_picker'
import OpenModal from './elements/open_modal' import OpenModal from './elements/open_modal'
import BarChart from './elements/bar_chart'
import * as TurboInstantClick from './lib/turbo_instant_click' import * as TurboInstantClick from './lib/turbo_instant_click'
@ -140,6 +141,7 @@ safeRegisterElement('toggle-classes', ToggleClasses)
safeRegisterElement('autosize-field', AutosizeField) safeRegisterElement('autosize-field', AutosizeField)
safeRegisterElement('google-drive-file-picker', GoogleDriveFilePicker) safeRegisterElement('google-drive-file-picker', GoogleDriveFilePicker)
safeRegisterElement('open-modal', OpenModal) safeRegisterElement('open-modal', OpenModal)
safeRegisterElement('bar-chart', BarChart)
safeRegisterElement('template-builder', class extends HTMLElement { safeRegisterElement('template-builder', class extends HTMLElement {
connectedCallback () { connectedCallback () {

@ -0,0 +1,50 @@
export default class extends HTMLElement {
connectedCallback () {
this.chartLabels = JSON.parse(this.dataset.labels || '[]')
this.chartDatasets = JSON.parse(this.dataset.datasets || '[]')
this.initChart()
}
disconnectedCallback () {
if (this.chartInstance) {
this.chartInstance.destroy()
this.chartInstance = null
}
}
async initChart () {
const { default: Chart } = await import(/* webpackChunkName: "chartjs" */ 'chart.js/auto')
const canvas = this.querySelector('canvas')
const ctx = canvas.getContext('2d')
this.chartInstance = new Chart(ctx, {
type: 'bar',
data: {
labels: this.chartLabels,
datasets: this.chartDatasets
},
options: {
responsive: true,
maintainAspectRatio: true,
animation: false,
scales: {
y: {
beginAtZero: true,
grace: '20%',
ticks: {
precision: 0
}
}
},
plugins: {
legend: {
display: false
}
}
}
})
}
}

@ -47,6 +47,7 @@ class ProcessSubmitterCompletionJob
completed_submitter.assign_attributes( completed_submitter.assign_attributes(
submission_id: submitter.submission_id, submission_id: submitter.submission_id,
account_id: submission.account_id, account_id: submission.account_id,
is_first: !CompletedSubmitter.exists?(submission: submitter.submission_id, is_first: true),
template_id: submission.template_id, template_id: submission.template_id,
source: submission.source, source: submission.source,
sms_count: sms_events.sum { |e| e.data['segments'] || 1 }, sms_count: sms_events.sum { |e| e.data['segments'] || 1 },

@ -60,6 +60,10 @@ class Account < ApplicationRecord
linked_account_account&.testing? linked_account_account&.testing?
end end
def tz_info
@tz_info ||= TZInfo::Timezone.get(ActiveSupport::TimeZone::MAPPING[timezone] || timezone)
end
def default_template_folder def default_template_folder
super || build_default_template_folder(name: TemplateFolder::DEFAULT_NAME, super || build_default_template_folder(name: TemplateFolder::DEFAULT_NAME,
author_id: users.minimum(:id)).tap(&:save!) author_id: users.minimum(:id)).tap(&:save!)

@ -6,6 +6,7 @@
# #
# id :bigint not null, primary key # id :bigint not null, primary key
# completed_at :datetime not null # completed_at :datetime not null
# is_first :boolean
# sms_count :integer not null # sms_count :integer not null
# source :string not null # source :string not null
# verification_method :string # verification_method :string
@ -18,7 +19,9 @@
# #
# Indexes # Indexes
# #
# index_completed_submitters_on_account_id (account_id) # index_completed_submitters_account_id_completed_at_is_first (account_id,completed_at) WHERE (is_first = true)
# index_completed_submitters_on_account_id_and_completed_at (account_id,completed_at)
# index_completed_submitters_on_submission_id (submission_id) UNIQUE WHERE (is_first = true)
# index_completed_submitters_on_submitter_id (submitter_id) UNIQUE # index_completed_submitters_on_submitter_id (submitter_id) UNIQUE
# #
class CompletedSubmitter < ApplicationRecord class CompletedSubmitter < ApplicationRecord

@ -10,17 +10,21 @@
# event_type :string not null # event_type :string not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# account_id :bigint
# submission_id :bigint not null # submission_id :bigint not null
# submitter_id :bigint # submitter_id :bigint
# #
# Indexes # Indexes
# #
# index_submission_events_on_account_id (account_id)
# index_submission_events_on_created_at (created_at) # index_submission_events_on_created_at (created_at)
# index_submission_events_on_submission_id (submission_id) # index_submission_events_on_submission_id (submission_id)
# index_submission_events_on_submitter_id (submitter_id) # index_submission_events_on_submitter_id (submitter_id)
# index_submissions_events_on_sms_event_types (account_id,created_at) WHERE ((event_type)::text = ANY ((ARRAY['send_sms'::character varying, 'send_2fa_sms'::character varying])::text[]))
# #
# Foreign Keys # Foreign Keys
# #
# fk_rails_... (account_id => accounts.id)
# fk_rails_... (submission_id => submissions.id) # fk_rails_... (submission_id => submissions.id)
# fk_rails_... (submitter_id => submitters.id) # fk_rails_... (submitter_id => submitters.id)
# #
@ -35,6 +39,7 @@ class SubmissionEvent < ApplicationRecord
serialize :data, coder: JSON serialize :data, coder: JSON
before_validation :set_submission_id, on: :create before_validation :set_submission_id, on: :create
before_validation :set_account_id, on: :create
enum :event_type, { enum :event_type, {
send_email: 'send_email', send_email: 'send_email',
@ -63,4 +68,8 @@ class SubmissionEvent < ApplicationRecord
def set_submission_id def set_submission_id
self.submission_id = submitter&.submission_id self.submission_id = submitter&.submission_id
end end
def set_account_id
self.account_id = submitter&.account_id
end
end end

@ -66,7 +66,7 @@
<% end %> <% end %>
<% if !Docuseal.demo? && can?(:manage, EncryptedConfig) && (current_user != true_user || !current_account.linked_account_account) %> <% if !Docuseal.demo? && can?(:manage, EncryptedConfig) && (current_user != true_user || !current_account.linked_account_account) %>
<li> <li>
<%= link_to Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premises" }.to_query}", class: 'text-base hover:bg-base-300', data: { prefetch: false } do %> <%= content_for(:pro_link) || link_to(Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}/plans") : "#{Docuseal::CLOUD_URL}/sign_up?#{{ redir: "#{Docuseal::CONSOLE_URL}/on_premises" }.to_query}", class: 'text-base hover:bg-base-300', data: { turbo: false }) do %>
<%= t('plans') %> <%= t('plans') %>
<span class="badge badge-warning"><%= t('pro') %></span> <span class="badge badge-warning"><%= t('pro') %></span>
<% end %> <% end %>
@ -74,13 +74,13 @@
<% end %> <% end %>
<% if !Docuseal.demo? && can?(:manage, EncryptedConfig) && (current_user == true_user || current_account.testing?) %> <% if !Docuseal.demo? && can?(:manage, EncryptedConfig) && (current_user == true_user || current_account.testing?) %>
<li> <li>
<%= link_to Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}#{'/test' if current_account.testing?}/api") : "#{Docuseal::CONSOLE_URL}/on_premises", class: 'text-base hover:bg-base-300', data: { prefetch: false } do %> <%= link_to Docuseal.multitenant? ? console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}#{'/test' if current_account.testing?}/api") : "#{Docuseal::CONSOLE_URL}/on_premises", class: 'text-base hover:bg-base-300', data: { turbo: false } do %>
<% if Docuseal.multitenant? %> API <% else %> <%= t('console') %> <% end %> <% if Docuseal.multitenant? %> API <% else %> <%= t('console') %> <% end %>
<% end %> <% end %>
</li> </li>
<% if Docuseal.multitenant? %> <% if Docuseal.multitenant? %>
<li> <li>
<%= link_to console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}#{'/test' if current_account.testing?}/embedding/form"), class: 'text-base hover:bg-base-300', data: { prefetch: false } do %> <%= link_to console_redirect_index_path(redir: "#{Docuseal::CONSOLE_URL}#{'/test' if current_account.testing?}/embedding/form"), class: 'text-base hover:bg-base-300', data: { turbo: false } do %>
<%= t('embedding') %> <%= t('embedding') %>
<% end %> <% end %>
</li> </li>

@ -23,6 +23,7 @@ en: &en
add_from_google_drive: Add from Google Drive add_from_google_drive: Add from Google Drive
or_add_from: Or add from or_add_from: Or add from
upload_a_new_document: Upload a New Document upload_a_new_document: Upload a New Document
billing: Billing
hi_there: Hi there hi_there: Hi there
pro: Pro pro: Pro
thanks: Thanks thanks: Thanks
@ -843,6 +844,23 @@ en: &en
tablet: Tablet tablet: Tablet
reset_default: Reset default reset_default: Reset default
send_signature_request_email: Send signature request email send_signature_request_email: Send signature request email
last_3_months: Last 3 months
last_6_months: Last 6 months
last_year: Last year
all_time: All time
everyone: Everyone
daily: Daily
weekly: Weekly
monthly: Monthly
api: API
embed: Embed
bulk: Bulk
invite: Invite
api_and_embed: API and Embed
period: Period
reports: Reports
completed_submissions: Completed submissions
sms: SMS
submission_sources: submission_sources:
api: API api: API
bulk: Bulk Send bulk: Bulk Send
@ -949,10 +967,12 @@ en: &en
range_without_total: "%{from}-%{to} events" range_without_total: "%{from}-%{to} events"
es: &es es: &es
billing: Facturación
add_from_google_drive: Agregar desde Google Drive add_from_google_drive: Agregar desde Google Drive
or_add_from: O agregar desde or_add_from: O agregar desde
upload_a_new_document: Subir nuevo documento upload_a_new_document: Subir nuevo documento
use_direct_file_attachment_links_in_the_documents: Usar enlaces directos de archivos adjuntos en los documentos use_direct_file_attachment_links_in_the_documents: Usar enlaces directos de archivos adjuntos en los documentos
click_here_to_send_a_reset_password_email_html: '<label class="link" for="resend_password_button">Haz clic aquí</label> para enviar un correo electrónico de restablecimiento de contraseña.'
enabled: Habilitado enabled: Habilitado
disabled: Deshabilitado disabled: Deshabilitado
expirable_file_download_links: Enlaces de descarga de archivos con vencimiento expirable_file_download_links: Enlaces de descarga de archivos con vencimiento
@ -1594,6 +1614,7 @@ es: &es
image_field: Campo de Imagen image_field: Campo de Imagen
file_field: Campo de Archivo file_field: Campo de Archivo
select_field: Campo de Selección select_field: Campo de Selección
checkbox_field: Campo de Casilla
multiple_field: Campo Múltiple multiple_field: Campo Múltiple
radio_field: Campo de Grupo Radio radio_field: Campo de Grupo Radio
cells_field: Campo de Celdas cells_field: Campo de Celdas
@ -1770,6 +1791,23 @@ es: &es
tablet: Tableta tablet: Tableta
reset_default: Restablecer por defecto reset_default: Restablecer por defecto
send_signature_request_email: Enviar correo de solicitud de firma send_signature_request_email: Enviar correo de solicitud de firma
last_3_months: Últimos 3 meses
last_6_months: Últimos 6 meses
last_year: Último año
all_time: Todo el tiempo
everyone: Todos
daily: Diario
weekly: Semanal
monthly: Mensual
api: API
embed: Integrar
bulk: Masivo
invite: Invitación
api_and_embed: API e Integrar
period: Período
reports: Informes
completed_submissions: Envíos completados
sms: SMS
submission_sources: submission_sources:
api: API api: API
bulk: Envío masivo bulk: Envío masivo
@ -1876,6 +1914,7 @@ es: &es
range_without_total: "%{from}-%{to} eventos" range_without_total: "%{from}-%{to} eventos"
it: &it it: &it
billing: Fatturazione
add_from_google_drive: Aggiungi da Google Drive add_from_google_drive: Aggiungi da Google Drive
or_add_from: Oppure aggiungi da or_add_from: Oppure aggiungi da
upload_a_new_document: Carica nuovo documento upload_a_new_document: Carica nuovo documento
@ -1909,6 +1948,7 @@ it: &it
edit_per_party: Modifica per partito edit_per_party: Modifica per partito
signed: Firmato signed: Firmato
reply_to: Rispondi a reply_to: Rispondi a
partially_completed: Parzialmente completato
pending_by_me: In sospeso da me pending_by_me: In sospeso da me
add: Aggiungi add: Aggiungi
adding: Aggiungendo adding: Aggiungendo
@ -2354,6 +2394,7 @@ it: &it
upload_signature: Carica firma upload_signature: Carica firma
integration: Integrazione integration: Integrazione
admin: Amministratore admin: Amministratore
tenant_admin: Amministratore tenant
editor: Editor editor: Editor
viewer: Visualizzatore viewer: Visualizzatore
member: Membro member: Membro
@ -2698,6 +2739,23 @@ it: &it
tablet: Tablet tablet: Tablet
reset_default: Reimposta predefinito reset_default: Reimposta predefinito
send_signature_request_email: Invia email di richiesta firma send_signature_request_email: Invia email di richiesta firma
last_3_months: Ultimi 3 mesi
last_6_months: Ultimi 6 mesi
last_year: Ultimo anno
all_time: Tutto il tempo
everyone: Tutti
daily: Giornaliero
weekly: Settimanale
monthly: Mensile
api: API
embed: Incorporare
bulk: Massivo
invite: Invito
api_and_embed: API e Incorporare
period: Periodo
reports: Rapporti
completed_submissions: Invii completati
sms: SMS
submission_sources: submission_sources:
api: API api: API
bulk: Invio massivo bulk: Invio massivo
@ -2804,6 +2862,7 @@ it: &it
range_without_total: "%{from}-%{to} eventi" range_without_total: "%{from}-%{to} eventi"
fr: &fr fr: &fr
billing: Facturation
add_from_google_drive: Ajouter depuis Google Drive add_from_google_drive: Ajouter depuis Google Drive
or_add_from: Ou ajouter depuis or_add_from: Ou ajouter depuis
upload_a_new_document: Téléverser un nouveau document upload_a_new_document: Téléverser un nouveau document
@ -3623,6 +3682,24 @@ fr: &fr
tablet: Tablette tablet: Tablette
reset_default: Réinitialiser par défaut reset_default: Réinitialiser par défaut
send_signature_request_email: Envoyer un e-mail de demande de signature send_signature_request_email: Envoyer un e-mail de demande de signature
last_month: Mois dernier
last_3_months: 3 derniers mois
last_6_months: 6 derniers mois
last_year: Année dernière
all_time: Tout le temps
everyone: Tout le monde
daily: Quotidien
weekly: Hebdomadaire
monthly: Mensuel
api: API
embed: Intégrer
bulk: En masse
invite: Invitation
api_and_embed: API et Intégrer
period: Période
reports: Rapports
completed_submissions: Soumissions terminées
sms: SMS
submission_sources: submission_sources:
api: API api: API
bulk: Envoi en masse bulk: Envoi en masse
@ -3729,6 +3806,7 @@ fr: &fr
range_without_total: "%{from}-%{to} événements" range_without_total: "%{from}-%{to} événements"
pt: &pt pt: &pt
billing: Pagamentos
add_from_google_drive: Adicionar do Google Drive add_from_google_drive: Adicionar do Google Drive
or_add_from: Ou adicionar de or_add_from: Ou adicionar de
upload_a_new_document: Enviar novo documento upload_a_new_document: Enviar novo documento
@ -4552,6 +4630,23 @@ pt: &pt
tablet: Tablet tablet: Tablet
reset_default: Redefinir para padrão reset_default: Redefinir para padrão
send_signature_request_email: Enviar e-mail de solicitação de assinatura send_signature_request_email: Enviar e-mail de solicitação de assinatura
last_3_months: Últimos 3 meses
last_6_months: Últimos 6 meses
last_year: Último ano
all_time: Todo o tempo
everyone: Todos
daily: Diário
weekly: Semanal
monthly: Mensal
api: API
embed: Incorporar
bulk: Em massa
invite: Convite
api_and_embed: API e Incorporar
period: Período
reports: Relatórios
completed_submissions: Envios concluídos
sms: SMS
submission_sources: submission_sources:
api: API api: API
bulk: Envio em massa bulk: Envio em massa
@ -4658,6 +4753,7 @@ pt: &pt
range_without_total: "%{from}-%{to} eventos" range_without_total: "%{from}-%{to} eventos"
de: &de de: &de
billing: Abrechnung
add_from_google_drive: Aus Google Drive hinzufügen add_from_google_drive: Aus Google Drive hinzufügen
or_add_from: Oder hinzufügen aus or_add_from: Oder hinzufügen aus
upload_a_new_document: Neues Dokument hochladen upload_a_new_document: Neues Dokument hochladen
@ -5481,6 +5577,23 @@ de: &de
tablet: Tablet tablet: Tablet
reset_default: Standard zurücksetzen reset_default: Standard zurücksetzen
send_signature_request_email: Signaturanfrage-E-Mail senden send_signature_request_email: Signaturanfrage-E-Mail senden
last_3_months: Letzte 3 Monate
last_6_months: Letzte 6 Monate
last_year: Letztes Jahr
all_time: Gesamte Zeit
everyone: Alle
daily: Täglich
weekly: Wöchentlich
monthly: Monatlich
api: API
embed: Einbetten
bulk: Massenversand
invite: Einladung
api_and_embed: API und Einbetten
period: Zeitraum
reports: Berichte
completed_submissions: Abgeschlossene Übermittlungen
sms: SMS
submission_sources: submission_sources:
api: API api: API
bulk: Massenversand bulk: Massenversand
@ -5951,6 +6064,7 @@ he:
your_email_could_not_be_reached_this_may_happen_if_there_was_a_typo_in_your_address_or_if_your_mailbox_is_not_available_please_contact_support_email_to_log_in: לא ניתן היה לגשת לדוא"ל שלך. ייתכן שזה קרה עקב שגיאת כתיב בכתובת או אם תיבת הדואר אינה זמינה. אנא פנה ל־support@docuseal.com כדי להתחבר. your_email_could_not_be_reached_this_may_happen_if_there_was_a_typo_in_your_address_or_if_your_mailbox_is_not_available_please_contact_support_email_to_log_in: לא ניתן היה לגשת לדוא"ל שלך. ייתכן שזה קרה עקב שגיאת כתיב בכתובת או אם תיבת הדואר אינה זמינה. אנא פנה ל־support@docuseal.com כדי להתחבר.
nl: &nl nl: &nl
billing: Facturatie
add_from_google_drive: Toevoegen vanuit Google Drive add_from_google_drive: Toevoegen vanuit Google Drive
or_add_from: Of toevoegen vanuit or_add_from: Of toevoegen vanuit
upload_a_new_document: Nieuw document uploaden upload_a_new_document: Nieuw document uploaden
@ -6771,6 +6885,23 @@ nl: &nl
tablet: Tablet tablet: Tablet
reset_default: Standaard herstellen reset_default: Standaard herstellen
send_signature_request_email: E-mail met handtekeningaanvraag verzenden send_signature_request_email: E-mail met handtekeningaanvraag verzenden
last_3_months: Afgelopen 3 maanden
last_6_months: Afgelopen 6 maanden
last_year: Afgelopen jaar
all_time: Altijd
everyone: Iedereen
daily: Dagelijks
weekly: Wekelijks
monthly: Maandelijks
api: API
embed: Insluiten
bulk: Bulk
invite: Uitnodiging
api_and_embed: API en Insluiten
period: Periode
reports: Rapporten
completed_submissions: Voltooide inzendingen
sms: SMS
submission_sources: submission_sources:
api: API api: API
bulk: Bulkverzending bulk: Bulkverzending

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddAccountIdToSubmissionEvents < ActiveRecord::Migration[8.0]
def change
add_reference :submission_events, :account, null: true, foreign_key: true, index: true
end
end

@ -0,0 +1,14 @@
# frozen_string_literal: true
class AddIsFirstToCompletedSubmitters < ActiveRecord::Migration[8.0]
def change
# rubocop:disable Rails/ThreeStateBooleanColumn
add_column :completed_submitters, :is_first, :boolean
# rubocop:enable Rails/ThreeStateBooleanColumn
add_index :completed_submitters, %i[account_id completed_at],
where: 'is_first = TRUE',
name: 'index_completed_submitters_account_id_completed_at_is_first'
add_index :completed_submitters, :submission_id, unique: true, where: 'is_first = TRUE'
end
end

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddCompletedAtIndexToCompletedSubmitters < ActiveRecord::Migration[8.0]
def change
add_index :completed_submitters, %i[account_id completed_at],
name: 'index_completed_submitters_on_account_id_and_completed_at'
remove_index :completed_submitters, :account_id
end
end

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddSendSmsSend2faSmsIndex < ActiveRecord::Migration[8.0]
def change
add_index :submission_events, %i[account_id created_at],
where: "event_type IN ('send_sms', 'send_2fa_sms')",
name: 'index_submissions_events_on_sms_event_types'
end
end

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_09_22_053744) do ActiveRecord::Schema[8.0].define(version: 2025_11_21_113910) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "btree_gin" enable_extension "btree_gin"
enable_extension "plpgsql" enable_extension "plpgsql"
@ -118,7 +118,10 @@ ActiveRecord::Schema[8.0].define(version: 2025_09_22_053744) do
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.string "verification_method" t.string "verification_method"
t.index ["account_id"], name: "index_completed_submitters_on_account_id" t.boolean "is_first"
t.index ["account_id", "completed_at"], name: "index_completed_submitters_account_id_completed_at_is_first", where: "(is_first = true)"
t.index ["account_id", "completed_at"], name: "index_completed_submitters_on_account_id_and_completed_at"
t.index ["submission_id"], name: "index_completed_submitters_on_submission_id", unique: true, where: "(is_first = true)"
t.index ["submitter_id"], name: "index_completed_submitters_on_submitter_id", unique: true t.index ["submitter_id"], name: "index_completed_submitters_on_submitter_id", unique: true
end end
@ -293,6 +296,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_09_22_053744) do
t.datetime "event_timestamp", null: false t.datetime "event_timestamp", null: false
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.bigint "account_id"
t.index ["account_id", "created_at"], name: "index_submissions_events_on_sms_event_types", where: "((event_type)::text = ANY ((ARRAY['send_sms'::character varying, 'send_2fa_sms'::character varying])::text[]))"
t.index ["account_id"], name: "index_submission_events_on_account_id"
t.index ["created_at"], name: "index_submission_events_on_created_at" t.index ["created_at"], name: "index_submission_events_on_created_at"
t.index ["submission_id"], name: "index_submission_events_on_submission_id" t.index ["submission_id"], name: "index_submission_events_on_submission_id"
t.index ["submitter_id"], name: "index_submission_events_on_submitter_id" t.index ["submitter_id"], name: "index_submission_events_on_submitter_id"
@ -506,6 +512,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_09_22_053744) do
add_foreign_key "oauth_access_grants", "users", column: "resource_owner_id" add_foreign_key "oauth_access_grants", "users", column: "resource_owner_id"
add_foreign_key "oauth_access_tokens", "oauth_applications", column: "application_id" add_foreign_key "oauth_access_tokens", "oauth_applications", column: "application_id"
add_foreign_key "oauth_access_tokens", "users", column: "resource_owner_id" add_foreign_key "oauth_access_tokens", "users", column: "resource_owner_id"
add_foreign_key "submission_events", "accounts"
add_foreign_key "submission_events", "submissions" add_foreign_key "submission_events", "submissions"
add_foreign_key "submission_events", "submitters" add_foreign_key "submission_events", "submitters"
add_foreign_key "submissions", "templates" add_foreign_key "submissions", "templates"

@ -20,4 +20,14 @@ module SubmissionEvents
**data **data
}.compact_blank) }.compact_blank)
end end
def populate_account_id
Account.find_each do |account|
ids = account.submissions.pluck(:id)
ids.each_slice(10_000).each do |batch|
SubmissionEvent.where(submission_id: batch).update_all(account_id: account.id)
end
end
end
end end

@ -3,6 +3,7 @@
module Submissions module Submissions
class TimestampHandler class TimestampHandler
HASH_ALGORITHM = 'SHA256' HASH_ALGORITHM = 'SHA256'
TIMEOUT = 10
TimestampError = Class.new(StandardError) TimestampError = Class.new(StandardError)
@ -32,6 +33,8 @@ module Submissions
uri = Addressable::URI.parse(tsa_url) uri = Addressable::URI.parse(tsa_url)
conn = Faraday.new(uri.origin) do |c| conn = Faraday.new(uri.origin) do |c|
c.options.read_timeout = TIMEOUT
c.options.open_timeout = TIMEOUT
c.basic_auth(uri.user, uri.password) if uri.password.present? c.basic_auth(uri.user, uri.password) if uri.password.present?
end end

@ -251,4 +251,20 @@ module Submitters
true true
end end
def populate_completed_is_first
Account.find_each do |account|
submissions_index = {}
CompletedSubmitter.where(account_id: account.id).order(:account_id, :completed_at).each do |cs|
submissions_index[cs.submission_id] ||= cs.submitter_id
cs.update_columns(is_first: submissions_index[cs.submission_id] == cs.submitter_id)
rescue ActiveRecord::RecordNotUnique
CompletedSubmitter.where(submission_id: cs.submission_id).update_all(is_first: false)
cs.update_columns(is_first: submissions_index[cs.submission_id] == cs.submitter_id)
end
end
end
end end

@ -22,7 +22,7 @@ module TimeUtils
DEFAULT_DATE_FORMAT_US = 'MM/DD/YYYY' DEFAULT_DATE_FORMAT_US = 'MM/DD/YYYY'
DEFAULT_DATE_FORMAT = 'DD/MM/YYYY' DEFAULT_DATE_FORMAT = 'DD/MM/YYYY'
US_TIMEZONES = %w[EST CST MST PST HST AKDT].freeze US_TIMEZONES = %w[EST EDT CST CDT MST MDT PST PDT HST HDT AKST AKDT].freeze
module_function module_function

@ -20,6 +20,7 @@
"babel-plugin-dynamic-import-node": "^2.3.3", "babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-macros": "^3.1.0", "babel-plugin-macros": "^3.1.0",
"canvas-confetti": "^1.6.0", "canvas-confetti": "^1.6.0",
"chart.js": "^4.5.1",
"codemirror": "^6.0.2", "codemirror": "^6.0.2",
"compression-webpack-plugin": "10.0.0", "compression-webpack-plugin": "10.0.0",
"css-loader": "^6.7.3", "css-loader": "^6.7.3",

@ -1355,6 +1355,11 @@
"@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14" "@jridgewell/sourcemap-codec" "^1.4.14"
"@kurkle/color@^0.3.0":
version "0.3.4"
resolved "https://registry.yarnpkg.com/@kurkle/color/-/color-0.3.4.tgz#4d4ff677e1609214fc71c580125ddddd86abcabf"
integrity sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==
"@leichtgewicht/ip-codec@^2.0.1": "@leichtgewicht/ip-codec@^2.0.1":
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
@ -2341,6 +2346,13 @@ chalk@^4.0, chalk@^4.0.0, chalk@^4.1.0:
ansi-styles "^4.1.0" ansi-styles "^4.1.0"
supports-color "^7.1.0" supports-color "^7.1.0"
chart.js@^4.5.1:
version "4.5.1"
resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-4.5.1.tgz#19dd1a9a386a3f6397691672231cb5fc9c052c35"
integrity sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==
dependencies:
"@kurkle/color" "^0.3.0"
"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3: "chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3:
version "3.5.3" version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"

Loading…
Cancel
Save