Compare commits
21 Commits
mission-co
...
main
Author | SHA1 | Date | |
---|---|---|---|
5dce77c29d | |||
03e09c74a0 | |||
da51a073cc | |||
1953ba9d7f | |||
![]() |
bea90af59d | ||
c1c90f4db1 | |||
![]() |
bcddb802b7 | ||
edfb7227cf | |||
![]() |
b20078a115 | ||
71562e8143 | |||
![]() |
6478760b8d | ||
5cb4d14343 | |||
397a0f34ff | |||
4496deeef5 | |||
de87b6c46b | |||
eabba2109a | |||
1586f88986 | |||
c42eb4e576 | |||
29b3fca80c | |||
d236e459cd | |||
ff8918a1d4 |
1
Gemfile
1
Gemfile
@ -54,5 +54,4 @@ gem 'devise', '~> 4.9'
|
|||||||
|
|
||||||
gem 'wicked_pdf', '~> 2.8'
|
gem 'wicked_pdf', '~> 2.8'
|
||||||
|
|
||||||
gem 'mission_control-jobs'
|
|
||||||
gem 'rqrcode', '~> 3.1'
|
gem 'rqrcode', '~> 3.1'
|
||||||
|
32
Gemfile.lock
32
Gemfile.lock
@ -76,7 +76,7 @@ GEM
|
|||||||
rails (>= 6.0)
|
rails (>= 6.0)
|
||||||
addressable (2.8.7)
|
addressable (2.8.7)
|
||||||
public_suffix (>= 2.0.2, < 7.0)
|
public_suffix (>= 2.0.2, < 7.0)
|
||||||
annotaterb (4.16.0)
|
annotaterb (4.17.0)
|
||||||
activerecord (>= 6.0.0)
|
activerecord (>= 6.0.0)
|
||||||
activesupport (>= 6.0.0)
|
activesupport (>= 6.0.0)
|
||||||
ast (2.4.3)
|
ast (2.4.3)
|
||||||
@ -196,16 +196,6 @@ GEM
|
|||||||
mini_mime (1.1.5)
|
mini_mime (1.1.5)
|
||||||
mini_portile2 (2.8.9)
|
mini_portile2 (2.8.9)
|
||||||
minitest (5.25.5)
|
minitest (5.25.5)
|
||||||
mission_control-jobs (1.0.2)
|
|
||||||
actioncable (>= 7.1)
|
|
||||||
actionpack (>= 7.1)
|
|
||||||
activejob (>= 7.1)
|
|
||||||
activerecord (>= 7.1)
|
|
||||||
importmap-rails (>= 1.2.1)
|
|
||||||
irb (~> 1.13)
|
|
||||||
railties (>= 7.1)
|
|
||||||
stimulus-rails
|
|
||||||
turbo-rails
|
|
||||||
money (6.19.0)
|
money (6.19.0)
|
||||||
i18n (>= 0.6.4, <= 2)
|
i18n (>= 0.6.4, <= 2)
|
||||||
msgpack (1.7.5)
|
msgpack (1.7.5)
|
||||||
@ -301,7 +291,7 @@ GEM
|
|||||||
zeitwerk (~> 2.6)
|
zeitwerk (~> 2.6)
|
||||||
rainbow (3.1.1)
|
rainbow (3.1.1)
|
||||||
rake (13.3.0)
|
rake (13.3.0)
|
||||||
rdoc (6.14.1)
|
rdoc (6.14.2)
|
||||||
erb
|
erb
|
||||||
psych (>= 4.0.0)
|
psych (>= 4.0.0)
|
||||||
react-rails (3.2.1)
|
react-rails (3.2.1)
|
||||||
@ -310,7 +300,7 @@ GEM
|
|||||||
execjs
|
execjs
|
||||||
railties (>= 3.2)
|
railties (>= 3.2)
|
||||||
tilt
|
tilt
|
||||||
redis (5.4.0)
|
redis (5.4.1)
|
||||||
redis-client (>= 0.22.0)
|
redis-client (>= 0.22.0)
|
||||||
redis-client (0.23.2)
|
redis-client (0.23.2)
|
||||||
connection_pool
|
connection_pool
|
||||||
@ -357,7 +347,7 @@ GEM
|
|||||||
rswag-ui (2.16.0)
|
rswag-ui (2.16.0)
|
||||||
actionpack (>= 5.2, < 8.1)
|
actionpack (>= 5.2, < 8.1)
|
||||||
railties (>= 5.2, < 8.1)
|
railties (>= 5.2, < 8.1)
|
||||||
rubocop (1.77.0)
|
rubocop (1.78.0)
|
||||||
json (~> 2.3)
|
json (~> 2.3)
|
||||||
language_server-protocol (~> 3.17.0.2)
|
language_server-protocol (~> 3.17.0.2)
|
||||||
lint_roller (~> 1.1.0)
|
lint_roller (~> 1.1.0)
|
||||||
@ -394,7 +384,7 @@ GEM
|
|||||||
securerandom (0.4.1)
|
securerandom (0.4.1)
|
||||||
shoulda-matchers (6.5.0)
|
shoulda-matchers (6.5.0)
|
||||||
activesupport (>= 5.2.0)
|
activesupport (>= 5.2.0)
|
||||||
solid_queue (1.1.5)
|
solid_queue (1.2.0)
|
||||||
activejob (>= 7.1)
|
activejob (>= 7.1)
|
||||||
activerecord (>= 7.1)
|
activerecord (>= 7.1)
|
||||||
concurrent-ruby (>= 1.3.1)
|
concurrent-ruby (>= 1.3.1)
|
||||||
@ -466,7 +456,6 @@ DEPENDENCIES
|
|||||||
jsonapi-rails
|
jsonapi-rails
|
||||||
letter_opener_web
|
letter_opener_web
|
||||||
license_finder
|
license_finder
|
||||||
mission_control-jobs
|
|
||||||
money
|
money
|
||||||
pg (~> 1.1)
|
pg (~> 1.1)
|
||||||
pluck_to_hash
|
pluck_to_hash
|
||||||
@ -508,7 +497,7 @@ CHECKSUMS
|
|||||||
activesupport (8.0.2) sha256=8565cddba31b900cdc17682fd66ecd020441e3eef320a9930285394e8c07a45e
|
activesupport (8.0.2) sha256=8565cddba31b900cdc17682fd66ecd020441e3eef320a9930285394e8c07a45e
|
||||||
acts_as_tenant (1.0.1) sha256=6944e4d64533337938a8817a6b4ff9b11189c9dcc0b1333bb89f3821a4c14c53
|
acts_as_tenant (1.0.1) sha256=6944e4d64533337938a8817a6b4ff9b11189c9dcc0b1333bb89f3821a4c14c53
|
||||||
addressable (2.8.7) sha256=462986537cf3735ab5f3c0f557f14155d778f4b43ea4f485a9deb9c8f7c58232
|
addressable (2.8.7) sha256=462986537cf3735ab5f3c0f557f14155d778f4b43ea4f485a9deb9c8f7c58232
|
||||||
annotaterb (4.16.0) sha256=3ed087a925b306036139e4191b38044390bcfc4561adece8f779f9d5ee87ca3c
|
annotaterb (4.17.0) sha256=f0338f8aaadd5c47fa3deaccb560a54abcdde29aca6f69f4b94726ea9256b4bd
|
||||||
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
||||||
babel-source (5.8.35) sha256=79ef222a9dcb867ac2efa3b0da35b4bcb15a4bfa67b6b2dcbf1e9a29104498d9
|
babel-source (5.8.35) sha256=79ef222a9dcb867ac2efa3b0da35b4bcb15a4bfa67b6b2dcbf1e9a29104498d9
|
||||||
babel-transpiler (0.7.0) sha256=4c06f4ad9e8e1cabe94f99e11df2f140bb72aca9ba067dbb49dc14d9b98d1570
|
babel-transpiler (0.7.0) sha256=4c06f4ad9e8e1cabe94f99e11df2f140bb72aca9ba067dbb49dc14d9b98d1570
|
||||||
@ -569,7 +558,6 @@ CHECKSUMS
|
|||||||
mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
|
mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
|
||||||
mini_portile2 (2.8.9) sha256=0cd7c7f824e010c072e33f68bc02d85a00aeb6fce05bb4819c03dfd3c140c289
|
mini_portile2 (2.8.9) sha256=0cd7c7f824e010c072e33f68bc02d85a00aeb6fce05bb4819c03dfd3c140c289
|
||||||
minitest (5.25.5) sha256=391b6c6cb43a4802bfb7c93af1ebe2ac66a210293f4a3fb7db36f2fc7dc2c756
|
minitest (5.25.5) sha256=391b6c6cb43a4802bfb7c93af1ebe2ac66a210293f4a3fb7db36f2fc7dc2c756
|
||||||
mission_control-jobs (1.0.2) sha256=0f9e115afdd77e3b10a1988870b9b32bdc99350a952c7a85a3b3f440bf473eb4
|
|
||||||
money (6.19.0) sha256=ec936fa1e42f2783719241ed9fd52725d0efa628f928feea1eb5c37d5de7daf3
|
money (6.19.0) sha256=ec936fa1e42f2783719241ed9fd52725d0efa628f928feea1eb5c37d5de7daf3
|
||||||
msgpack (1.7.5) sha256=ffb04979f51e6406823c03abe50e1da2c825c55a37dee138518cdd09d9d3aea8
|
msgpack (1.7.5) sha256=ffb04979f51e6406823c03abe50e1da2c825c55a37dee138518cdd09d9d3aea8
|
||||||
multi_xml (0.7.1) sha256=4fce100c68af588ff91b8ba90a0bb3f0466f06c909f21a32f4962059140ba61b
|
multi_xml (0.7.1) sha256=4fce100c68af588ff91b8ba90a0bb3f0466f06c909f21a32f4962059140ba61b
|
||||||
@ -610,9 +598,9 @@ CHECKSUMS
|
|||||||
railties (8.0.2) sha256=0d7c3f40c49ba74980f1bac1d4bb153a9331c5ee8a9631d89c7bf79db82e5cf9
|
railties (8.0.2) sha256=0d7c3f40c49ba74980f1bac1d4bb153a9331c5ee8a9631d89c7bf79db82e5cf9
|
||||||
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
||||||
rake (13.3.0) sha256=96f5092d786ff412c62fde76f793cc0541bd84d2eb579caa529aa8a059934493
|
rake (13.3.0) sha256=96f5092d786ff412c62fde76f793cc0541bd84d2eb579caa529aa8a059934493
|
||||||
rdoc (6.14.1) sha256=905efa796cd296ef252af4fb31fe41c073dee894de6aad715821f335c632516b
|
rdoc (6.14.2) sha256=9fdd44df130f856ae70cc9a264dfd659b9b40de369b16581f4ab746e42439226
|
||||||
react-rails (3.2.1) sha256=2235db0b240517596b1cb3e26177ab5bc64d3a56579b0415ee242b1691f81f64
|
react-rails (3.2.1) sha256=2235db0b240517596b1cb3e26177ab5bc64d3a56579b0415ee242b1691f81f64
|
||||||
redis (5.4.0) sha256=798900d869418a9fc3977f916578375b45c38247a556b61d58cba6bb02f7d06b
|
redis (5.4.1) sha256=b5e675b57ad22b15c9bcc765d5ac26f60b675408af916d31527af9bd5a81faae
|
||||||
redis-client (0.23.2) sha256=e33bab6682c8155cfef95e6dd296936bb9c2981a89fb578ace27a076fa2836fa
|
redis-client (0.23.2) sha256=e33bab6682c8155cfef95e6dd296936bb9c2981a89fb578ace27a076fa2836fa
|
||||||
regexp_parser (2.10.0) sha256=cb6f0ddde88772cd64bff1dbbf68df66d376043fe2e66a9ef77fcb1b0c548c61
|
regexp_parser (2.10.0) sha256=cb6f0ddde88772cd64bff1dbbf68df66d376043fe2e66a9ef77fcb1b0c548c61
|
||||||
reline (0.6.1) sha256=1afcc9d7cb1029cdbe780d72f2f09251ce46d3780050f3ec39c3ccc6b60675fb
|
reline (0.6.1) sha256=1afcc9d7cb1029cdbe780d72f2f09251ce46d3780050f3ec39c3ccc6b60675fb
|
||||||
@ -629,7 +617,7 @@ CHECKSUMS
|
|||||||
rswag-api (2.16.0) sha256=b653f7bd92e98be18b01ab4525d88950d7b0960e293a99f856b9efcee3ae6074
|
rswag-api (2.16.0) sha256=b653f7bd92e98be18b01ab4525d88950d7b0960e293a99f856b9efcee3ae6074
|
||||||
rswag-specs (2.16.0) sha256=8ba26085c408b0bd2ed21dc8015c80f417c7d34c63720ab7133c2549b5bd2a91
|
rswag-specs (2.16.0) sha256=8ba26085c408b0bd2ed21dc8015c80f417c7d34c63720ab7133c2549b5bd2a91
|
||||||
rswag-ui (2.16.0) sha256=a1f49e927dceda92e6e6e7c1000f1e217ee66c565f69e28131dc98b33cd3a04f
|
rswag-ui (2.16.0) sha256=a1f49e927dceda92e6e6e7c1000f1e217ee66c565f69e28131dc98b33cd3a04f
|
||||||
rubocop (1.77.0) sha256=1f360b4575ef7a124be27b0dfffa227a2b2d9420d22d4fd8bf179d702bcc88c0
|
rubocop (1.78.0) sha256=8b74a6f912eb4fd3e6878851f7f7f45dcad8c7185c34250d4f952b0ee80d6bc0
|
||||||
rubocop-ast (1.45.1) sha256=94042e49adc17f187ba037b33f941ba7398fede77cdf4bffafba95190a473a3e
|
rubocop-ast (1.45.1) sha256=94042e49adc17f187ba037b33f941ba7398fede77cdf4bffafba95190a473a3e
|
||||||
rubocop-factory_bot (2.27.1) sha256=9d744b5916778c1848e5fe6777cc69855bd96548853554ec239ba9961b8573fe
|
rubocop-factory_bot (2.27.1) sha256=9d744b5916778c1848e5fe6777cc69855bd96548853554ec239ba9961b8573fe
|
||||||
rubocop-rails (2.32.0) sha256=9fcc623c8722fe71e835e99c4a18b740b5b0d3fb69915d7f0777f00794b30490
|
rubocop-rails (2.32.0) sha256=9fcc623c8722fe71e835e99c4a18b740b5b0d3fb69915d7f0777f00794b30490
|
||||||
@ -640,7 +628,7 @@ CHECKSUMS
|
|||||||
rubyzip (2.3.2) sha256=3f57e3935dc2255c414484fbf8d673b4909d8a6a57007ed754dde39342d2373f
|
rubyzip (2.3.2) sha256=3f57e3935dc2255c414484fbf8d673b4909d8a6a57007ed754dde39342d2373f
|
||||||
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
||||||
shoulda-matchers (6.5.0) sha256=ef6b572b2bed1ac4aba6ab2c5ff345a24b6d055a93a3d1c3bfc86d9d499e3f44
|
shoulda-matchers (6.5.0) sha256=ef6b572b2bed1ac4aba6ab2c5ff345a24b6d055a93a3d1c3bfc86d9d499e3f44
|
||||||
solid_queue (1.1.5) sha256=bae0c9d76310f4953ebc57466f2e8c78703a0fbf4b89d25756c23c88f9b6df9b
|
solid_queue (1.2.0) sha256=482ac5305cbe91ebf845627caec493fda8545bf22b18205df01afb80999e28de
|
||||||
sprockets (4.2.1) sha256=951b13dd2f2fcae840a7184722689a803e0ff9d2702d902bd844b196da773f97
|
sprockets (4.2.1) sha256=951b13dd2f2fcae840a7184722689a803e0ff9d2702d902bd844b196da773f97
|
||||||
sprockets-rails (3.5.2) sha256=a9e88e6ce9f8c912d349aa5401509165ec42326baf9e942a85de4b76dbc4119e
|
sprockets-rails (3.5.2) sha256=a9e88e6ce9f8c912d349aa5401509165ec42326baf9e942a85de4b76dbc4119e
|
||||||
stimulus-rails (1.3.4) sha256=765676ffa1f33af64ce026d26b48e8ffb2e0b94e0f50e9119e11d6107d67cb06
|
stimulus-rails (1.3.4) sha256=765676ffa1f33af64ce026d26b48e8ffb2e0b94e0f50e9119e11d6107d67cb06
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
set_current_tenant_through_filter
|
set_current_tenant_through_filter
|
||||||
before_action :set_tenant, unless: -> { params[:controller].start_with?('mission_control/') }
|
before_action :set_tenant
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
after_action :set_csrf_cookie
|
after_action :set_csrf_cookie
|
||||||
|
|
||||||
|
@ -22,7 +22,13 @@ class GuestsController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
guest = Guest.find(params[:id]).update!(guest_params)
|
guest = Guest.find(params[:id])
|
||||||
|
guest.update!(guest_params)
|
||||||
|
|
||||||
|
if !user_signed_in? && guest.saved_change_to_status?
|
||||||
|
AdminMailer.with(guest_id: guest.id).attendance_change_email.deliver_later
|
||||||
|
end
|
||||||
|
|
||||||
render json: guest.as_json(GUEST_PARAMS), status: :ok
|
render json: guest.as_json(GUEST_PARAMS), status: :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -26,6 +26,12 @@ class InvitationsController < ApplicationController
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def email
|
||||||
|
AdminMailer.with(wedding_id: ActsAsTenant.current_tenant.id).invitations_pdf_email.deliver_later
|
||||||
|
|
||||||
|
head :ok
|
||||||
|
end
|
||||||
|
|
||||||
def sheet; end
|
def sheet; end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
45
app/mailers/admin_mailer.rb
Normal file
45
app/mailers/admin_mailer.rb
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Copyright (C) 2024-2025 LibreWeddingPlanner contributors
|
||||||
|
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AdminMailer < ApplicationMailer
|
||||||
|
def attendance_change_email
|
||||||
|
@guest = Guest.find(params[:guest_id])
|
||||||
|
ActsAsTenant.with_tenant(@guest.wedding) do
|
||||||
|
mail(
|
||||||
|
to: recipients,
|
||||||
|
subject: I18n.t(
|
||||||
|
'admin_mailer.attendance_change_email.subject',
|
||||||
|
name: @guest.name,
|
||||||
|
status: I18n.t("active_record.attributes.guest/status.#{@guest.status}")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def invitations_pdf_email
|
||||||
|
ActsAsTenant.with_tenant(Wedding.find(params[:wedding_id])) do
|
||||||
|
invitations = Invitation.includes(:guests).all
|
||||||
|
|
||||||
|
pdf_html = ActionController::Base.new.render_to_string(
|
||||||
|
template: 'invitations/sheet',
|
||||||
|
layout: 'pdf',
|
||||||
|
locals: { invitations: }
|
||||||
|
)
|
||||||
|
pdf = WickedPdf.new.pdf_from_string(pdf_html)
|
||||||
|
|
||||||
|
attachments["invitations_#{Time.current.strftime('%Y%m%d_%H%M%S')}.pdf"] = pdf
|
||||||
|
|
||||||
|
mail(
|
||||||
|
to: recipients,
|
||||||
|
subject: I18n.t('admin_mailer.invitations_pdf_email.subject')
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def recipients
|
||||||
|
ActsAsTenant.current_tenant.users.pluck(:email)
|
||||||
|
end
|
||||||
|
end
|
@ -3,6 +3,16 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ApplicationMailer < ActionMailer::Base
|
class ApplicationMailer < ActionMailer::Base
|
||||||
default from: 'from@example.com'
|
class << self
|
||||||
|
private
|
||||||
|
|
||||||
|
def default_from
|
||||||
|
File.read('/run/secrets/smtp_user_name').strip
|
||||||
|
rescue Errno::ENOENT
|
||||||
|
'development@example.com'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
default from: default_from
|
||||||
layout 'mailer'
|
layout 'mailer'
|
||||||
end
|
end
|
||||||
|
@ -23,5 +23,6 @@ class Wedding < ApplicationRecord
|
|||||||
has_many :guests, dependent: :delete_all
|
has_many :guests, dependent: :delete_all
|
||||||
has_many :groups, dependent: :delete_all
|
has_many :groups, dependent: :delete_all
|
||||||
has_many :invitations, dependent: :delete_all
|
has_many :invitations, dependent: :delete_all
|
||||||
|
has_many :users, dependent: :delete_all
|
||||||
has_one :website, dependent: :destroy
|
has_one :website, dependent: :destroy
|
||||||
end
|
end
|
||||||
|
@ -14,17 +14,18 @@ module Tables
|
|||||||
|
|
||||||
attr_accessor :tables, :min_per_table, :max_per_table, :hierarchy
|
attr_accessor :tables, :min_per_table, :max_per_table, :hierarchy
|
||||||
|
|
||||||
def initialize(min_per_table:, max_per_table:)
|
def initialize(min_per_table:, max_per_table:, hierarchy: AffinityGroupsHierarchy.new)
|
||||||
@min_per_table = min_per_table
|
@min_per_table = min_per_table
|
||||||
@max_per_table = max_per_table
|
@max_per_table = max_per_table
|
||||||
@hierarchy = AffinityGroupsHierarchy.new
|
@hierarchy = hierarchy
|
||||||
@tables = []
|
@tables = []
|
||||||
end
|
end
|
||||||
|
|
||||||
def random_distribution(people)
|
def random_distribution(people, random: Random.new)
|
||||||
min_tables = (people.count * 1.0 / @max_per_table).ceil
|
min_tables = (people.count * 1.0 / @max_per_table).ceil
|
||||||
max_tables = (people.count * 1.0 / @min_per_table).ceil
|
max_tables = (people.count * 1.0 / @min_per_table).ceil
|
||||||
@tables = people.in_groups(rand(min_tables..max_tables), false)
|
table_size = random.rand(min_tables..max_tables)
|
||||||
|
@tables = people.in_groups(table_size, false)
|
||||||
.map { |group| Table.new(group) }
|
.map { |group| Table.new(group) }
|
||||||
.each { |table| table.min_per_table = @min_per_table }
|
.each { |table| table.min_per_table = @min_per_table }
|
||||||
.each { |table| table.max_per_table = @max_per_table }
|
.each { |table| table.max_per_table = @max_per_table }
|
||||||
@ -41,7 +42,8 @@ module Tables
|
|||||||
end
|
end
|
||||||
|
|
||||||
def deep_dup
|
def deep_dup
|
||||||
self.class.new(min_per_table: @min_per_table, max_per_table: @max_per_table).tap do |new_distribution|
|
self.class.new(min_per_table: @min_per_table, max_per_table: @max_per_table,
|
||||||
|
hierarchy: @hierarchy).tap do |new_distribution|
|
||||||
new_distribution.tables = @tables.map(&:dup)
|
new_distribution.tables = @tables.map(&:dup)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
17
app/views/admin_mailer/attendance_change_email.html.erb
Normal file
17
app/views/admin_mailer/attendance_change_email.html.erb
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<%# Copyright (C) 2024-2025 LibreWeddingPlanner contributors %>
|
||||||
|
|
||||||
|
<p><%= I18n.t('admin_mailer.greeting') %>,</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<%= I18n.t('admin_mailer.attendance_change_email.paragraph_1', name: @guest.name) %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<strong><%= I18n.t("active_record.attributes.guest.status") %>:</strong> <%= I18n.t("active_record.attributes.guest/status.#{@guest.status}") %>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<%= I18n.t("admin_mailer.attendance_change_email.notify_on_updates") %>
|
||||||
|
</p>
|
9
app/views/admin_mailer/attendance_change_email.text.erb
Normal file
9
app/views/admin_mailer/attendance_change_email.text.erb
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<%# Copyright (C) 2024-2025 LibreWeddingPlanner contributors %>
|
||||||
|
|
||||||
|
<%= I18n.t('admin_mailer.greeting') %>,
|
||||||
|
|
||||||
|
<%= I18n.t('admin_mailer.attendance_change_email.paragraph_1', name: @guest.name) %>
|
||||||
|
|
||||||
|
- <%= I18n.t("active_record.attributes.guest.status") %>: <%= I18n.t("active_record.attributes.guest/status.#{@guest.status}") %>
|
||||||
|
|
||||||
|
<%= I18n.t("admin_mailer.attendance_change_email.notify_on_updates") %>
|
7
app/views/admin_mailer/invitations_pdf_email.html.erb
Normal file
7
app/views/admin_mailer/invitations_pdf_email.html.erb
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<%# Copyright (C) 2024-2025 LibreWeddingPlanner contributors %>
|
||||||
|
|
||||||
|
<p><%= I18n.t('admin_mailer.greeting') %>,</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<%= I18n.t('admin_mailer.invitations_pdf_email.paragraph_1') %>
|
||||||
|
</p>
|
5
app/views/admin_mailer/invitations_pdf_email.txt.erb
Normal file
5
app/views/admin_mailer/invitations_pdf_email.txt.erb
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<%# Copyright (C) 2024-2025 LibreWeddingPlanner contributors %>
|
||||||
|
|
||||||
|
<%= I18n.t('admin_mailer.greeting') %>,
|
||||||
|
|
||||||
|
<%= I18n.t('admin_mailer.invitations_pdf_email.paragraph_1') %>
|
@ -1 +1 @@
|
|||||||
TOtkAra/cchKwT7oQqMVOrryboJ8sR1YjkXawjui9HUV8WvnVg56w5BJMiwTlKGwtOaDoLiZVRTmE2ROud6GIOqXu1fj4bJ1hgobeK5MdEbJPJ6kRoD6p9y29CaBuH6I8V4ynJlGJwhOe0Tlob+tn72ECk3bHKN0ZUGAUrGk9Br9DJhtzLhkFN2bawEDHIVcbJpTOsLKqSIsk6aKX/2AfnOZ5Xo9njjSF4lYnetItL/XKWD01ErZ3XLENuOkGyCI29F5lsDVR0WMdr8n3Cx1iVyn5N0pZ55jAEuy6aY1/Q0f/BYEwAYOJPRvC/gPc/vVqVEmnVHJBKsez8hOxQ3Wm3taAcnOFXTLfufp6oDqdVTBgUYiWpfvK/hEeCyOOf478zRFIkONr1IBbIxkLFCTW6ZJKPSa61AG8TinYWTdwJ9UtPErs4b1r8/P2eU2PgNp3XzXhuyNog3lGlwvH5H/P1vncWJ+C2PDAgoqBPlScS0CU82HH5vxsHcOsIBRKB9vRYS8hOiE0134wYLNpDei1fUslllme2GX1L3mfP4Iz3lwJruXiuOO8tbsxuJv+GVN4kvjPhf9Fqfvs8KHZ7F35XLvJdTgg9TYfdW/cUxiPggokM8Mn3VvArf28zUj10alrofkwONFtlsE3U+rs4DXrawnNTn2V69F1PFDHTTTcQh87wnbSNKIuQ==--mN3rf6F43fMXSAgl--GIEBnzwQcaKYHaPxe6O68w==
|
OiXHHR1FtwOH/WOGpbJYiNbTvOgucDw80NPTRUiyDYosdk5M++4XvMfMQV77fR023FtStN+xagLpXH+F2ey6Mod5EQKCyAGvKz/fNinSNmNoIlDzs4OEM1ry2o/guZ9Y30XwwEjmz4R9wtN+iXVsUh1ig0QyfPuD7vavbv4N66KDRHeAeMHmkXKampzAp0RNaybS4xPWHl56zaFCGHqQSjnrLzWJi7Q0nlG2VTm+gbjRf2zAkLssSXKfux3lUnWGDcQi7CfrjkaSd9Ltp2qO+UxOFyEkjKKI2O3Jd1sReikpLIOae/Q1C9HwhlDvVIKvXTh7BUMgS4tuutzo+TypbgX/7liYSxS7B6fKkxsHrWkfMEo5Rx4YRLZrHqwGBkSKPsFyX7fcUBgjYnEBm2dmgYFduEHs--K9NZ9weZIzDBbOnk--LSi8wdpxZJtxqvaNy24qWQ==
|
@ -78,8 +78,6 @@ Rails.application.configure do
|
|||||||
# Raise error when a before_action's only/except options reference missing actions
|
# Raise error when a before_action's only/except options reference missing actions
|
||||||
config.action_controller.raise_on_missing_callback_actions = true
|
config.action_controller.raise_on_missing_callback_actions = true
|
||||||
|
|
||||||
config.mission_control.jobs.http_basic_auth_enabled = false
|
|
||||||
|
|
||||||
config.hosts << "libre-wedding-planner.app.localhost"
|
config.hosts << "libre-wedding-planner.app.localhost"
|
||||||
Rails.application.routes.default_url_options[:host] = "libre-wedding-planner.app.localhost"
|
Rails.application.routes.default_url_options[:host] = "libre-wedding-planner.app.localhost"
|
||||||
end
|
end
|
||||||
|
@ -79,7 +79,8 @@ Rails.application.configure do
|
|||||||
port: File.read("/run/secrets/smtp_port").strip.to_i,
|
port: File.read("/run/secrets/smtp_port").strip.to_i,
|
||||||
user_name: File.read("/run/secrets/smtp_user_name").strip,
|
user_name: File.read("/run/secrets/smtp_user_name").strip,
|
||||||
password: File.read("/run/secrets/smtp_password").strip,
|
password: File.read("/run/secrets/smtp_password").strip,
|
||||||
authentication: File.read("/run/secrets/smtp_authentication").strip.to_sym
|
authentication: File.read("/run/secrets/smtp_authentication").strip.to_sym,
|
||||||
|
tls: true
|
||||||
}
|
}
|
||||||
rescue Errno::ENOENT
|
rescue Errno::ENOENT
|
||||||
{}
|
{}
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
# Copyright (C) 2024-2025 LibreWeddingPlanner contributors
|
# Copyright (C) 2024-2025 LibreWeddingPlanner contributors
|
||||||
|
|
||||||
ActsAsTenant.configure do |config|
|
ActsAsTenant.configure do |config|
|
||||||
config.require_tenant = lambda do
|
config.require_tenant = !Rails.env.test?
|
||||||
return false if Rails.env.test?
|
|
||||||
if $request_env.present?
|
|
||||||
return false if $request_env["REQUEST_PATH"].start_with?("/jobs/")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Rails.application.console do
|
Rails.application.console do
|
||||||
|
@ -1,31 +1,22 @@
|
|||||||
# Files in the config/locales directory are used for internationalization and
|
|
||||||
# are automatically loaded by Rails. If you want to use locales other than
|
|
||||||
# English, add the necessary files in this directory.
|
|
||||||
#
|
|
||||||
# To use the locales, use `I18n.t`:
|
|
||||||
#
|
|
||||||
# I18n.t "hello"
|
|
||||||
#
|
|
||||||
# In views, this is aliased to just `t`:
|
|
||||||
#
|
|
||||||
# <%= t("hello") %>
|
|
||||||
#
|
|
||||||
# To use a different locale, set it with `I18n.locale`:
|
|
||||||
#
|
|
||||||
# I18n.locale = :es
|
|
||||||
#
|
|
||||||
# This would use the information in config/locales/es.yml.
|
|
||||||
#
|
|
||||||
# To learn more about the API, please read the Rails Internationalization guide
|
|
||||||
# at https://guides.rubyonrails.org/i18n.html.
|
|
||||||
#
|
|
||||||
# Be aware that YAML interprets the following case-insensitive strings as
|
|
||||||
# booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings
|
|
||||||
# must be quoted to be interpreted as strings. For example:
|
|
||||||
#
|
|
||||||
# en:
|
|
||||||
# "yes": yup
|
|
||||||
# enabled: "ON"
|
|
||||||
|
|
||||||
en:
|
en:
|
||||||
hello: "Hello world"
|
hello: "Hello world"
|
||||||
|
active_record:
|
||||||
|
attributes:
|
||||||
|
guest:
|
||||||
|
status: Status
|
||||||
|
guest/status:
|
||||||
|
considered: Considered
|
||||||
|
invited: Invited
|
||||||
|
confirmed: Confirmed
|
||||||
|
declined: Declined
|
||||||
|
tentative: Tentative
|
||||||
|
|
||||||
|
admin_mailer:
|
||||||
|
greeting: "Dear user"
|
||||||
|
attendance_change_email:
|
||||||
|
subject: "%{name} has changed their attendance status: %{status}"
|
||||||
|
paragraph_1: "The guest %{name} has changed their attendance for the wedding."
|
||||||
|
notify_on_updates: "You will be notified of any further changes to their attendance status."
|
||||||
|
invitations_pdf_email:
|
||||||
|
subject: "Your wedding invitations are ready"
|
||||||
|
paragraph_1: "Your wedding invitations are ready. Please, find them attached to this email."
|
||||||
|
@ -11,7 +11,6 @@ Rails.application.routes.draw do
|
|||||||
|
|
||||||
mount Rswag::Ui::Engine => '/api-docs'
|
mount Rswag::Ui::Engine => '/api-docs'
|
||||||
mount Rswag::Api::Engine => '/api-docs'
|
mount Rswag::Api::Engine => '/api-docs'
|
||||||
mount MissionControl::Jobs::Engine, at: "/jobs"
|
|
||||||
|
|
||||||
scope ":slug", constraints: { slug: Wedding::SLUG_REGEX } do
|
scope ":slug", constraints: { slug: Wedding::SLUG_REGEX } do
|
||||||
devise_for :users, skip: [:registration, :session, :confirmation]
|
devise_for :users, skip: [:registration, :session, :confirmation]
|
||||||
@ -43,7 +42,9 @@ Rails.application.routes.draw do
|
|||||||
|
|
||||||
resources :tables_arrangements, only: %i[index show create]
|
resources :tables_arrangements, only: %i[index show create]
|
||||||
resources :summary, only: :index
|
resources :summary, only: :index
|
||||||
resources :invitations, only: %i[show index create update destroy]
|
resources :invitations, only: %i[show index create update destroy] do
|
||||||
|
post :email, on: :collection
|
||||||
|
end
|
||||||
|
|
||||||
root to: redirect("/%{slug}")
|
root to: redirect("/%{slug}")
|
||||||
end
|
end
|
||||||
|
29
lib/tasks/vns.rake
Normal file
29
lib/tasks/vns.rake
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
namespace :vns do
|
||||||
|
desc 'Benchmarks the efficiency of the VNS implementation'
|
||||||
|
task benchmark: :environment do
|
||||||
|
ActsAsTenant.with_tenant(Wedding.first) do
|
||||||
|
Rails.logger.info "There are #{Guest.potential.count} potential guests"
|
||||||
|
|
||||||
|
engine = VNS::Engine.new
|
||||||
|
|
||||||
|
engine.add_perturbation(Tables::Swap)
|
||||||
|
engine.add_perturbation(Tables::Shift)
|
||||||
|
|
||||||
|
hierarchy = AffinityGroupsHierarchy.new
|
||||||
|
initial_solution = Tables::Distribution.new(min_per_table: 8, max_per_table: 10, hierarchy:)
|
||||||
|
|
||||||
|
random = Random.new(561_163)
|
||||||
|
initial_solution.random_distribution(Guest.potential.shuffle(random:), random:)
|
||||||
|
|
||||||
|
engine.initial_solution = initial_solution
|
||||||
|
|
||||||
|
engine.target_function(&:discomfort)
|
||||||
|
|
||||||
|
solution = Rails.benchmark('VNS Benchmarking') { engine.run }
|
||||||
|
|
||||||
|
Rails.logger.info "Best solution found with discomfort: #{solution.discomfort}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -12,11 +12,6 @@ server {
|
|||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
}
|
}
|
||||||
|
|
||||||
location /jobs/ {
|
|
||||||
proxy_pass http://backend:3000/jobs/;
|
|
||||||
proxy_set_header Host $http_host;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /captcha/v2/media/ {
|
location /captcha/v2/media/ {
|
||||||
proxy_pass http://libre-captcha:8888/v2/media/;
|
proxy_pass http://libre-captcha:8888/v2/media/;
|
||||||
proxy_set_header Host $http_host;
|
proxy_set_header Host $http_host;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user