motor-admin 0.3.17 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/channels/motor/application_cable/channel.rb +14 -0
- data/app/channels/motor/application_cable/connection.rb +27 -0
- data/app/channels/motor/notes_channel.rb +9 -0
- data/app/channels/motor/notifications_channel.rb +9 -0
- data/app/controllers/concerns/motor/current_user_method.rb +2 -0
- data/app/controllers/motor/note_tags_controller.rb +13 -0
- data/app/controllers/motor/notes_controller.rb +58 -0
- data/app/controllers/motor/notifications_controller.rb +33 -0
- data/app/controllers/motor/reminders_controller.rb +38 -0
- data/app/controllers/motor/run_queries_controller.rb +1 -1
- data/app/controllers/motor/send_alerts_controller.rb +2 -2
- data/app/controllers/motor/slack_conversations_controller.rb +11 -0
- data/app/controllers/motor/tags_controller.rb +1 -1
- data/app/controllers/motor/ui_controller.rb +9 -1
- data/app/controllers/motor/users_for_autocomplete_controller.rb +23 -0
- data/app/jobs/motor/alert_sending_job.rb +1 -1
- data/app/jobs/motor/notify_note_mentions_job.rb +9 -0
- data/app/jobs/motor/notify_reminder_job.rb +9 -0
- data/app/mailers/motor/alerts_mailer.rb +6 -29
- data/app/mailers/motor/application_mailer.rb +27 -1
- data/app/mailers/motor/notifications_mailer.rb +33 -0
- data/app/models/motor/note.rb +18 -0
- data/app/models/motor/note_tag.rb +7 -0
- data/app/models/motor/note_tag_tag.rb +8 -0
- data/app/models/motor/notification.rb +14 -0
- data/app/models/motor/reminder.rb +13 -0
- data/app/views/layouts/motor/application.html.erb +4 -1
- data/app/views/layouts/motor/mailer.html.erb +72 -0
- data/app/views/motor/alerts_mailer/alert_email.html.erb +52 -124
- data/app/views/motor/notifications_mailer/notify_mention_email.html.erb +28 -0
- data/app/views/motor/notifications_mailer/notify_reminder_email.html.erb +28 -0
- data/config/locales/el.yml +25 -0
- data/config/locales/en.yml +33 -2
- data/config/locales/es.yml +33 -2
- data/config/locales/pt.yml +33 -2
- data/config/routes.rb +9 -0
- data/lib/generators/motor/install_notes_generator.rb +22 -0
- data/lib/generators/motor/templates/install.rb +77 -0
- data/lib/generators/motor/templates/install_notes.rb +83 -0
- data/lib/generators/motor/upgrade_generator.rb +13 -6
- data/lib/motor/admin.rb +13 -1
- data/lib/motor/alerts/slack_sender.rb +74 -0
- data/lib/motor/alerts.rb +42 -0
- data/lib/motor/api_query/apply_scope.rb +1 -0
- data/lib/motor/build_schema/apply_permissions.rb +8 -0
- data/lib/motor/build_schema/defaults.rb +15 -1
- data/lib/motor/build_schema/load_from_rails.rb +4 -1
- data/lib/motor/build_schema.rb +7 -0
- data/lib/motor/configs/build_ui_app_tag.rb +73 -8
- data/lib/motor/configs.rb +3 -4
- data/lib/motor/notes/notify_mentions.rb +73 -0
- data/lib/motor/notes/notify_reminder.rb +49 -0
- data/lib/motor/notes/persist.rb +36 -0
- data/lib/motor/notes/reminders_scheduler.rb +39 -0
- data/lib/motor/notes/tags.rb +34 -0
- data/lib/motor/notes.rb +12 -0
- data/lib/motor/queries/run_query.rb +66 -3
- data/lib/motor/resources/fetch_configured_model.rb +19 -3
- data/lib/motor/resources.rb +1 -1
- data/lib/motor/slack/client.rb +62 -0
- data/lib/motor/slack.rb +16 -0
- data/lib/motor/version.rb +1 -1
- data/lib/motor.rb +19 -0
- data/ui/dist/{main-726aa7f6805676af4d21.css.gz → main-94c4b9cbc80e23602ab9.css.gz} +0 -0
- data/ui/dist/main-94c4b9cbc80e23602ab9.js.gz +0 -0
- data/ui/dist/manifest.json +5 -5
- metadata +36 -4
- data/ui/dist/main-726aa7f6805676af4d21.js.gz +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23e2cf343874057f5b68688df96c709e52a434f7287c8acde0ec87b8dc06cd2b
|
4
|
+
data.tar.gz: 9a0025bf24e25413509c73c762917bc880727f9780725a28035c3aa291ff04f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7af958fa0c4fddfe7271b1296d795ac6c2a15d4d55e8f9d27077b6fc8760e5ab3faa3374dee57f1f9cc74cb571c847380cfe781175cbfad2c90c881bdd8afffb
|
7
|
+
data.tar.gz: 8196ea433758da7378b4ae22d00a23db66b6ec77ec3c5af22043b57b392047cdb2d800af74b829ec75c8111803b3f529d92c9c2bceba696a61330eaa022db634
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class DummyChannel
|
5
|
+
def self.broadcast_to(*_args)
|
6
|
+
nil
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module ApplicationCable
|
11
|
+
class Channel < defined?(ActionCable) ? ActionCable::Channel::Base : Motor::DummyChannel
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
module ApplicationCable
|
5
|
+
class Connection < defined?(ActionCable) ? ActionCable::Connection::Base : Object
|
6
|
+
identified_by :current_user if defined?(ActionCable)
|
7
|
+
|
8
|
+
def connect
|
9
|
+
self.current_user = find_verified_user
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def find_verified_user
|
15
|
+
return unless env['warden']
|
16
|
+
|
17
|
+
if env['warden'].respond_to?(:admin_user)
|
18
|
+
env['warden'].admin_user
|
19
|
+
elsif env['warden'].respond_to?(:admin)
|
20
|
+
env['warden'].admin
|
21
|
+
else
|
22
|
+
env['warden'].user
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class NoteTagsController < ApiBaseController
|
5
|
+
wrap_parameters :data, except: %i[include fields]
|
6
|
+
|
7
|
+
load_and_authorize_resource :note_tag
|
8
|
+
|
9
|
+
def index
|
10
|
+
render json: { data: @note_tags.to_a }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class NotesController < ApiBaseController
|
5
|
+
wrap_parameters :data, except: %i[include fields]
|
6
|
+
|
7
|
+
load_and_authorize_resource :note, only: %i[index show update destroy]
|
8
|
+
before_action :build_note, only: :create
|
9
|
+
authorize_resource :note, only: :create
|
10
|
+
|
11
|
+
def index
|
12
|
+
@notes = Motor::ApiQuery.call(@notes.active.order(created_at: :asc), params)
|
13
|
+
|
14
|
+
render json: { data: Motor::ApiQuery::BuildJson.call(@notes, params, current_ability) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def show
|
18
|
+
render json: { data: Motor::ApiQuery::BuildJson.call(@note, params, current_ability) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def create
|
22
|
+
Motor::Notes::Persist.call(@note, current_user)
|
23
|
+
Motor::NotifyNoteMentionsJob.perform_later(@note, current_user)
|
24
|
+
|
25
|
+
render json: { data: Motor::ApiQuery::BuildJson.call(@note, params, current_ability) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def update
|
29
|
+
@note.assign_attributes(note_params)
|
30
|
+
|
31
|
+
Motor::Notes::Persist.call(@note, current_user)
|
32
|
+
Motor::NotifyNoteMentionsJob.perform_later(@note, current_user)
|
33
|
+
|
34
|
+
render json: { data: Motor::ApiQuery::BuildJson.call(@note, params, current_ability) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def destroy
|
38
|
+
@note.update!(deleted_at: Time.current)
|
39
|
+
|
40
|
+
Motor::NotesChannel.broadcast_to(
|
41
|
+
@note.values_at(:record_type, :record_id).join(':'),
|
42
|
+
['remove', @note.as_json(only: %i[id body created_at updated_at])]
|
43
|
+
)
|
44
|
+
|
45
|
+
head :ok
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def build_note
|
51
|
+
@note = Motor::Note.new(note_params.merge(author: current_user))
|
52
|
+
end
|
53
|
+
|
54
|
+
def note_params
|
55
|
+
params.require(:data).permit(:body, :record_id, :record_type)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class NotificationsController < ApiBaseController
|
5
|
+
wrap_parameters :data, except: %i[include fields]
|
6
|
+
|
7
|
+
load_and_authorize_resource :notification
|
8
|
+
|
9
|
+
def index
|
10
|
+
@notifications =
|
11
|
+
Motor::ApiQuery.call(@notifications.where(recipient: current_user).order(created_at: :desc), params)
|
12
|
+
|
13
|
+
render json: {
|
14
|
+
data: Motor::ApiQuery::BuildJson.call(@notifications, params, current_ability),
|
15
|
+
meta: Motor::ApiQuery::BuildMeta.call(@notifications, params)
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def update
|
20
|
+
@notification.update!(notification_params)
|
21
|
+
|
22
|
+
Motor::NotificationsChannel.broadcast_to(current_user, ['update', @notification.as_json(include: %i[record])])
|
23
|
+
|
24
|
+
render json: { data: Motor::ApiQuery::BuildJson.call(@notification, params, current_ability) }
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def notification_params
|
30
|
+
params.require(:data).permit(:status)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class RemindersController < ApiBaseController
|
5
|
+
wrap_parameters :data, except: %i[include fields]
|
6
|
+
|
7
|
+
load_and_authorize_resource :reminder
|
8
|
+
|
9
|
+
def create
|
10
|
+
@reminder.update!(author: current_user, recipient: current_user)
|
11
|
+
|
12
|
+
broadcast_note_update(@reminder.record)
|
13
|
+
|
14
|
+
render json: { data: Motor::ApiQuery::BuildJson.call(@reminder, params, current_ability) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def destroy
|
18
|
+
@reminder.destroy!
|
19
|
+
|
20
|
+
broadcast_note_update(@reminder.record)
|
21
|
+
|
22
|
+
head :ok
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def broadcast_note_update(note)
|
28
|
+
Motor::NotesChannel.broadcast_to(
|
29
|
+
note.values_at(:record_type, :record_id).join(':'),
|
30
|
+
['update', note.as_json(include: %i[author tags reminders])]
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def reminder_params
|
35
|
+
params.require(:data).permit(:record_id, :record_type, :scheduled_at)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -20,7 +20,7 @@ module Motor
|
|
20
20
|
private
|
21
21
|
|
22
22
|
def render_result
|
23
|
-
query_result = Queries::RunQuery.call(@query, variables_hash: variables_params,
|
23
|
+
query_result = Queries::RunQuery.call(@query, variables_hash: variables_params.to_unsafe_h,
|
24
24
|
limit: params[:limit].presence,
|
25
25
|
filters: filter_params)
|
26
26
|
|
@@ -8,7 +8,7 @@ module Motor
|
|
8
8
|
authorize_resource :alert, only: :create
|
9
9
|
|
10
10
|
def create
|
11
|
-
|
11
|
+
Motor::Alerts.send_alert(@alert)
|
12
12
|
|
13
13
|
head :ok
|
14
14
|
end
|
@@ -20,7 +20,7 @@ module Motor
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def alert_params
|
23
|
-
params.require(:data).permit(:query_id, :name, :to_emails)
|
23
|
+
params.require(:data).permit(:query_id, :name, :to_emails, :description, preferences: {})
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -4,7 +4,7 @@ module Motor
|
|
4
4
|
class UiController < ApplicationController
|
5
5
|
layout 'motor/application'
|
6
6
|
|
7
|
-
helper_method :current_user, :current_ability, :cache_keys
|
7
|
+
helper_method :current_user, :current_ability, :cache_keys, :custom_html
|
8
8
|
|
9
9
|
before_action :set_i18n_locale
|
10
10
|
|
@@ -30,6 +30,14 @@ module Motor
|
|
30
30
|
render :show
|
31
31
|
end
|
32
32
|
|
33
|
+
def custom_html
|
34
|
+
Motor::Admin.config.custom_html.presence || begin
|
35
|
+
configs = Motor::Configs::LoadFromCache.load_configs(cache_key: cache_keys[:configs])
|
36
|
+
|
37
|
+
configs.find { |c| c.key == 'ui.custom_html' }&.value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
33
41
|
def set_i18n_locale
|
34
42
|
configs = Motor::Configs::LoadFromCache.load_configs(cache_key: cache_keys[:configs])
|
35
43
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class UsersForAutocompleteController < ApiBaseController
|
5
|
+
def index
|
6
|
+
authorize!(:create, Motor::Note)
|
7
|
+
|
8
|
+
render json: { data: user_emails }
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def user_emails
|
14
|
+
user_class = Motor::AdminUser if defined?(Motor::AdminUser)
|
15
|
+
user_class ||= AdminUser if defined?(AdminUser)
|
16
|
+
user_class ||= User if defined?(User)
|
17
|
+
|
18
|
+
return user_class.distinct.limit(100).pluck(:email) if user_class
|
19
|
+
|
20
|
+
[]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -5,7 +5,7 @@ module Motor
|
|
5
5
|
def perform(alert)
|
6
6
|
Motor::AlertLock.create!(alert_id: alert.id, lock_timestamp: alert.cron.previous_time.to_i)
|
7
7
|
|
8
|
-
Motor::
|
8
|
+
Motor::Alerts.send_alert(alert)
|
9
9
|
rescue ActiveRecord::RecordNotUnique
|
10
10
|
nil
|
11
11
|
end
|
@@ -6,9 +6,10 @@ module Motor
|
|
6
6
|
SENDER_NOT_SET_ERROR_MESSAGE =
|
7
7
|
'Please specify your sender address via MOTOR_ALERTS_FROM_ADDRESS environment variable'
|
8
8
|
|
9
|
-
def alert_email(alert)
|
9
|
+
def alert_email(alert, email: nil)
|
10
10
|
@alert = alert
|
11
|
-
@query_result = Queries::RunQuery.call(alert.query
|
11
|
+
@query_result = Queries::RunQuery.call(alert.query,
|
12
|
+
variables_hash: { current_user_email: email })
|
12
13
|
|
13
14
|
return if @alert.preferences[:send_empty].blank? && @query_result.data.blank?
|
14
15
|
|
@@ -18,7 +19,7 @@ module Motor
|
|
18
19
|
|
19
20
|
mail(
|
20
21
|
from: from_address,
|
21
|
-
to: alert.to_emails,
|
22
|
+
to: email || alert.to_emails,
|
22
23
|
subject: alert.name.presence || @alert.query.name
|
23
24
|
)
|
24
25
|
end
|
@@ -29,34 +30,10 @@ module Motor
|
|
29
30
|
attachments["#{alert.name.presence || 'data'}.csv"] = generate_csv(query_result)
|
30
31
|
end
|
31
32
|
|
32
|
-
def generate_csv(
|
33
|
-
rows = [
|
33
|
+
def generate_csv(query_result)
|
34
|
+
rows = [query_result.columns.pluck(:name)] + query_result.data
|
34
35
|
|
35
36
|
rows.map(&:to_csv).join
|
36
37
|
end
|
37
|
-
|
38
|
-
def from_address
|
39
|
-
from = ENV['MOTOR_ALERTS_FROM_ADDRESS'].presence || ENV['MOTOR_EMAIL_ADDRESS'].presence
|
40
|
-
|
41
|
-
from ||= application_mailer_default_from
|
42
|
-
from ||= mailer_config_from_address
|
43
|
-
from ||= "reports@#{ENV['HOST'].delete_prefix('www.')}" if ENV['HOST'].present?
|
44
|
-
|
45
|
-
from ||= 'reports@example.com' if Rails.env.development?
|
46
|
-
|
47
|
-
from
|
48
|
-
end
|
49
|
-
|
50
|
-
def application_mailer_default_from
|
51
|
-
return if !defined?(::ApplicationMailer) || ::ApplicationMailer.default[:from].to_s.include?('example.com')
|
52
|
-
|
53
|
-
::ApplicationMailer.default[:from].presence
|
54
|
-
end
|
55
|
-
|
56
|
-
def mailer_config_from_address
|
57
|
-
return if Rails.application.config.action_mailer.default_url_options&.dig(:host).blank?
|
58
|
-
|
59
|
-
"reports@#{Rails.application.config.action_mailer.default_url_options[:host].delete_prefix('www.')}"
|
60
|
-
end
|
61
38
|
end
|
62
39
|
end
|
@@ -2,6 +2,32 @@
|
|
2
2
|
|
3
3
|
module Motor
|
4
4
|
class ApplicationMailer < ActionMailer::Base
|
5
|
-
layout 'mailer'
|
5
|
+
layout 'motor/mailer'
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def from_address
|
10
|
+
from = ENV['MOTOR_ALERTS_FROM_ADDRESS'].presence || ENV['MOTOR_EMAIL_ADDRESS'].presence
|
11
|
+
|
12
|
+
from ||= application_mailer_default_from
|
13
|
+
from ||= mailer_config_from_address
|
14
|
+
from ||= "reports@#{ENV['HOST'].delete_prefix('www.')}" if ENV['HOST'].present?
|
15
|
+
|
16
|
+
from ||= 'reports@example.com' if Rails.env.development?
|
17
|
+
|
18
|
+
from
|
19
|
+
end
|
20
|
+
|
21
|
+
def application_mailer_default_from
|
22
|
+
return if !defined?(::ApplicationMailer) || ::ApplicationMailer.default[:from].to_s.include?('example.com')
|
23
|
+
|
24
|
+
::ApplicationMailer.default[:from].presence
|
25
|
+
end
|
26
|
+
|
27
|
+
def mailer_config_from_address
|
28
|
+
return if Rails.application.config.action_mailer.default_url_options&.dig(:host).blank?
|
29
|
+
|
30
|
+
"reports@#{Rails.application.config.action_mailer.default_url_options[:host].delete_prefix('www.')}"
|
31
|
+
end
|
6
32
|
end
|
7
33
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class NotificationsMailer < ApplicationMailer
|
5
|
+
def notify_mention_email(notification)
|
6
|
+
@notification = notification
|
7
|
+
|
8
|
+
@note = notification.record
|
9
|
+
|
10
|
+
subject =
|
11
|
+
"#{@note.author&.email || 'Anonymous'} mentioned you in " \
|
12
|
+
"#{@note.record.class.model_name.human} ##{@note.record.id}"
|
13
|
+
|
14
|
+
mail(
|
15
|
+
from: from_address,
|
16
|
+
to: notification.recipient.email,
|
17
|
+
subject: subject
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def notify_reminder_email(notification)
|
22
|
+
@notification = notification
|
23
|
+
|
24
|
+
@note = notification.record.record
|
25
|
+
|
26
|
+
mail(
|
27
|
+
from: from_address,
|
28
|
+
to: notification.recipient.email,
|
29
|
+
subject: "Reminder for #{@note.record.class.model_name.human} ##{@note.record.id}"
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class Note < ::Motor::ApplicationRecord
|
5
|
+
audited
|
6
|
+
|
7
|
+
belongs_to :author, polymorphic: true, optional: true
|
8
|
+
belongs_to :record, polymorphic: true
|
9
|
+
|
10
|
+
has_many :note_tag_tags, dependent: :destroy
|
11
|
+
has_many :tags, through: :note_tag_tags, class_name: 'Motor::NoteTag'
|
12
|
+
|
13
|
+
has_many :reminders, as: :record, dependent: :destroy
|
14
|
+
has_many :notifications, as: :record, dependent: :destroy
|
15
|
+
|
16
|
+
scope :active, -> { where(deleted_at: nil) }
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class Notification < ::Motor::ApplicationRecord
|
5
|
+
STATUSES = [
|
6
|
+
PENDING = 'pending'
|
7
|
+
].freeze
|
8
|
+
|
9
|
+
belongs_to :record, polymorphic: true, optional: true
|
10
|
+
belongs_to :recipient, polymorphic: true
|
11
|
+
|
12
|
+
attribute :status, type: :string, default: PENDING
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class Reminder < ::Motor::ApplicationRecord
|
5
|
+
audited
|
6
|
+
|
7
|
+
belongs_to :record, polymorphic: true, optional: true
|
8
|
+
belongs_to :author, polymorphic: true
|
9
|
+
belongs_to :recipient, polymorphic: true
|
10
|
+
|
11
|
+
has_many :notifications, as: :record, dependent: :destroy
|
12
|
+
end
|
13
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html lang="<%= I18n.locale %>">
|
3
3
|
<head>
|
4
|
-
<title
|
4
|
+
<title><%= Motor.company_name %></title>
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
6
6
|
<link rel="stylesheet" media="all" href="<%= Motor::Assets.asset_path('main.css') %>">
|
7
7
|
<%= csrf_meta_tags %>
|
@@ -10,5 +10,8 @@
|
|
10
10
|
<body>
|
11
11
|
<%= yield %>
|
12
12
|
<script src="<%= Motor::Assets.asset_path('main.js') %>"></script>
|
13
|
+
<% if params[:controller] === 'motor/ui' %>
|
14
|
+
<%= raw(custom_html) %>
|
15
|
+
<% end %>
|
13
16
|
</body>
|
14
17
|
</html>
|
@@ -0,0 +1,72 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta name="viewport" content="width=device-width">
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
6
|
+
<style>
|
7
|
+
@media only screen and (max-width: 620px) {
|
8
|
+
table[class=body] h1 {
|
9
|
+
font-size: 28px !important;
|
10
|
+
margin-bottom: 10px !important;
|
11
|
+
}
|
12
|
+
table[class=body] p,
|
13
|
+
table[class=body] ul,
|
14
|
+
table[class=body] ol,
|
15
|
+
table[class=body] td,
|
16
|
+
table[class=body] span,
|
17
|
+
table[class=body] a {
|
18
|
+
font-size: 14px !important;
|
19
|
+
}
|
20
|
+
table[class=body] .wrapper,
|
21
|
+
table[class=body] .article {
|
22
|
+
padding: 10px !important;
|
23
|
+
}
|
24
|
+
table[class=body] .content {
|
25
|
+
padding: 0 !important;
|
26
|
+
}
|
27
|
+
table[class=body] .container {
|
28
|
+
padding: 0 !important;
|
29
|
+
width: 100% !important;
|
30
|
+
}
|
31
|
+
table[class=body] .main {
|
32
|
+
border-left-width: 0 !important;
|
33
|
+
border-radius: 0 !important;
|
34
|
+
border-right-width: 0 !important;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
@media all {
|
39
|
+
.ExternalClass {
|
40
|
+
width: 100%;
|
41
|
+
}
|
42
|
+
.ExternalClass,
|
43
|
+
.ExternalClass p,
|
44
|
+
.ExternalClass span,
|
45
|
+
.ExternalClass font,
|
46
|
+
.ExternalClass td,
|
47
|
+
.ExternalClass div {
|
48
|
+
line-height: 100%;
|
49
|
+
}
|
50
|
+
.apple-link a {
|
51
|
+
color: inherit !important;
|
52
|
+
font-family: inherit !important;
|
53
|
+
font-size: inherit !important;
|
54
|
+
font-weight: inherit !important;
|
55
|
+
line-height: inherit !important;
|
56
|
+
text-decoration: none !important;
|
57
|
+
}
|
58
|
+
#MessageViewBody a {
|
59
|
+
color: inherit;
|
60
|
+
text-decoration: none;
|
61
|
+
font-size: inherit;
|
62
|
+
font-family: inherit;
|
63
|
+
font-weight: inherit;
|
64
|
+
line-height: inherit;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
</style>
|
68
|
+
</head>
|
69
|
+
<body class="" style="background-color: #f9fbfc; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
|
70
|
+
<%= yield %>
|
71
|
+
</body>
|
72
|
+
</html>
|