workarea-product_videos 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +16 -0
  3. data/.eslintrc +27 -0
  4. data/.gitignore +19 -0
  5. data/.rails-rubocop.yml +119 -0
  6. data/.rubocop.yml +7 -0
  7. data/.scss-lint.yml +192 -0
  8. data/CHANGELOG.md +92 -0
  9. data/Gemfile +16 -0
  10. data/README.md +87 -0
  11. data/Rakefile +60 -0
  12. data/app/assets/images/workarea/admin/icons/video.svg +1 -0
  13. data/app/assets/images/workarea/storefront/icons/video_play.svg +1 -0
  14. data/app/assets/javascripts/workarea/storefront/product_videos/modules/vimeo_api.js +81 -0
  15. data/app/assets/javascripts/workarea/storefront/product_videos/modules/youtube_api.js +124 -0
  16. data/app/assets/stylesheets/workarea/storefront/product_videos/components/_product_video.scss +37 -0
  17. data/app/controllers/workarea/admin/catalog_product_videos_controller.rb +52 -0
  18. data/app/controllers/workarea/storefront/products_controller.decorator +19 -0
  19. data/app/models/workarea/catalog/product.decorator +7 -0
  20. data/app/models/workarea/catalog/product_video.rb +32 -0
  21. data/app/services/workarea/video_embed_parser.rb +54 -0
  22. data/app/view_models/workarea/storefront/product_video_view_model.rb +84 -0
  23. data/app/view_models/workarea/storefront/product_view_model.decorator +9 -0
  24. data/app/views/workarea/admin/catalog_product_videos/edit.html.haml +76 -0
  25. data/app/views/workarea/admin/catalog_product_videos/index.html.haml +46 -0
  26. data/app/views/workarea/admin/catalog_product_videos/new.html.haml +57 -0
  27. data/app/views/workarea/admin/catalog_products/_product_videos_card.html.haml +21 -0
  28. data/app/views/workarea/storefront/products/_video_link.html.haml +8 -0
  29. data/app/views/workarea/storefront/products/_video_thumbnail.html.haml +12 -0
  30. data/app/views/workarea/storefront/products/video.html.haml +3 -0
  31. data/bin/rails +20 -0
  32. data/config/initializers/appends.rb +37 -0
  33. data/config/initializers/product_videos.rb +5 -0
  34. data/config/initializers/workarea.rb +3 -0
  35. data/config/locales/en.yml +41 -0
  36. data/config/routes.rb +15 -0
  37. data/lib/workarea/product_videos/engine.rb +8 -0
  38. data/lib/workarea/product_videos/version.rb +5 -0
  39. data/lib/workarea/product_videos.rb +11 -0
  40. data/script/admin_ci +9 -0
  41. data/script/ci +11 -0
  42. data/script/core_ci +9 -0
  43. data/script/plugins_ci +9 -0
  44. data/script/storefront_ci +9 -0
  45. data/test/dummy/Rakefile +6 -0
  46. data/test/dummy/app/assets/config/manifest.js +4 -0
  47. data/test/dummy/app/assets/images/.keep +0 -0
  48. data/test/dummy/app/assets/javascripts/application.js +13 -0
  49. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  50. data/test/dummy/app/controllers/application_controller.rb +3 -0
  51. data/test/dummy/app/controllers/concerns/.keep +0 -0
  52. data/test/dummy/app/helpers/application_helper.rb +2 -0
  53. data/test/dummy/app/jobs/application_job.rb +2 -0
  54. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  55. data/test/dummy/app/models/concerns/.keep +0 -0
  56. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  57. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  58. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  59. data/test/dummy/bin/bundle +3 -0
  60. data/test/dummy/bin/rails +4 -0
  61. data/test/dummy/bin/rake +4 -0
  62. data/test/dummy/bin/setup +38 -0
  63. data/test/dummy/bin/update +29 -0
  64. data/test/dummy/bin/yarn +11 -0
  65. data/test/dummy/config/application.rb +29 -0
  66. data/test/dummy/config/boot.rb +5 -0
  67. data/test/dummy/config/cable.yml +10 -0
  68. data/test/dummy/config/environment.rb +5 -0
  69. data/test/dummy/config/environments/development.rb +54 -0
  70. data/test/dummy/config/environments/production.rb +91 -0
  71. data/test/dummy/config/environments/test.rb +44 -0
  72. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  73. data/test/dummy/config/initializers/assets.rb +14 -0
  74. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  75. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  76. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  77. data/test/dummy/config/initializers/inflections.rb +16 -0
  78. data/test/dummy/config/initializers/mime_types.rb +4 -0
  79. data/test/dummy/config/initializers/new_framework_defaults.rb +23 -0
  80. data/test/dummy/config/initializers/session_store.rb +3 -0
  81. data/test/dummy/config/initializers/workarea.rb +5 -0
  82. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  83. data/test/dummy/config/locales/en.yml +33 -0
  84. data/test/dummy/config/puma.rb +56 -0
  85. data/test/dummy/config/routes.rb +5 -0
  86. data/test/dummy/config/secrets.yml +32 -0
  87. data/test/dummy/config/spring.rb +6 -0
  88. data/test/dummy/config.ru +5 -0
  89. data/test/dummy/db/seeds.rb +2 -0
  90. data/test/dummy/lib/assets/.keep +0 -0
  91. data/test/dummy/log/.keep +0 -0
  92. data/test/dummy/package.json +5 -0
  93. data/test/factories/embed_codes.rb +15 -0
  94. data/test/integration/workarea/admin/product_videos_integration_test.rb +59 -0
  95. data/test/integration/workarea/storefront/product_videos_integration_test.rb +30 -0
  96. data/test/services/workarea/video_embedder_test.rb +27 -0
  97. data/test/system/workarea/admin/product_videos_system_test.rb +58 -0
  98. data/test/system/workarea/storefront/product_videos_system_test.rb +60 -0
  99. data/test/teaspoon_env.rb +6 -0
  100. data/test/test_helper.rb +10 -0
  101. data/test/view_models/workarea/storefront/product_video_view_model_test.rb +46 -0
  102. data/workarea-product_videos.gemspec +19 -0
  103. metadata +157 -0
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @namespace WORKAREA.vimeoApi
3
+ */
4
+ WORKAREA.registerModule('vimeoApi', (function () {
5
+ 'use strict';
6
+
7
+ var getAnalyticsLabel = function(iframe) {
8
+ var iframeSrc = iframe.attr('src').split('?')[0],
9
+ label = iframeSrc;
10
+
11
+ if (iframe.data('title')) {
12
+ label += ' (' + iframe.data('title') + ')';
13
+ } else if (iframe.attr('title')) {
14
+ label += ' (' + iframe.attr('title') + ')';
15
+ }
16
+
17
+ return label;
18
+ },
19
+
20
+ sendEvent = function (id, action) {
21
+ var iframe = $('#' + id),
22
+ label = getAnalyticsLabel(iframe);
23
+
24
+ WORKAREA.analytics.fireCallback(
25
+ 'vimeo',
26
+ {
27
+ 'eventCategory': 'Vimeo',
28
+ 'eventAction': action,
29
+ 'eventLabel': label,
30
+ 'eventValue': undefined
31
+ }
32
+ );
33
+ },
34
+
35
+ muteVideo = function (player) {
36
+ player.setVolume(0);
37
+ },
38
+
39
+ onPlay = function (playerData) {
40
+ sendEvent(playerData.player_id, 'Play Video');
41
+ },
42
+
43
+ onPause = function (playerData) {
44
+ sendEvent(playerData.player_id, 'Paused video');
45
+ },
46
+
47
+ onFinish = function (playerData) {
48
+ sendEvent(playerData.player_id, 'Completed video');
49
+ },
50
+
51
+ setupPlayer = function (index, iframe) {
52
+ var player = new Vimeo.Player(iframe),
53
+ playerData = $(iframe).data('vimeoApi');
54
+
55
+ if (playerData.mute === 'true') {
56
+ muteVideo(player);
57
+ }
58
+
59
+ player.on('play', _.partial(onPlay, playerData));
60
+ player.on('pause', _.partial(onPause, playerData));
61
+ player.on('finish', _.partial(onFinish, playerData));
62
+ },
63
+
64
+ injectSDK = _.once(function($vimeoIframes) {
65
+ $.getScript('https://player.vimeo.com/api/player.js', function() {
66
+ $vimeoIframes.each(setupPlayer);
67
+ });
68
+ }),
69
+
70
+ init = function ($scope) {
71
+ var $vimeoIframes = $('[data-vimeo-api]', $scope);
72
+
73
+ if (_.isEmpty($vimeoIframes)) { return; }
74
+
75
+ injectSDK($vimeoIframes);
76
+ };
77
+
78
+ return {
79
+ init: init
80
+ };
81
+ }()));
@@ -0,0 +1,124 @@
1
+ /**
2
+ * @namespace WORKAREA.youtubeApi
3
+ */
4
+ WORKAREA.registerModule('youtubeApi', (function () {
5
+ 'use strict';
6
+
7
+ var getAnalyticsLabel = function(iframe) {
8
+ var iframeSrc = iframe.attr('src').split('?')[0],
9
+ label = iframeSrc;
10
+
11
+ if (iframe.data('title')) {
12
+ label += ' (' + iframe.data('title') + ')';
13
+ } else if (iframe.attr('title')) {
14
+ label += ' (' + iframe.attr('title') + ')';
15
+ }
16
+
17
+ return label;
18
+ },
19
+
20
+ sendEvent = function (id, action) {
21
+ var iframe = $('#' + id),
22
+ label = getAnalyticsLabel(iframe);
23
+
24
+ WORKAREA.analytics.fireCallback(
25
+ 'youtube',
26
+ {
27
+ 'eventCategory': 'Youtube',
28
+ 'eventAction': action,
29
+ 'eventLabel': label,
30
+ 'eventValue': undefined
31
+ }
32
+ );
33
+ },
34
+
35
+ onPlay = function (playerId) {
36
+ sendEvent(playerId, 'Play Video');
37
+ },
38
+
39
+ onPause = function (playerId) {
40
+ sendEvent(playerId, 'Paused video');
41
+ },
42
+
43
+ onFinish = function (playerId) {
44
+ sendEvent(playerId, 'Completed video');
45
+ },
46
+
47
+ handlePlayerState = function(playerId, state) {
48
+ var stateMap = {
49
+ '-1': 'unstarted',
50
+ '0': 'ended',
51
+ '1': 'playing',
52
+ '2': 'paused',
53
+ '3': 'buffering',
54
+ '5': 'queued'
55
+ },
56
+ currentState = stateMap[state.data];
57
+
58
+ if (currentState === 'playing') {
59
+ onPlay(playerId);
60
+ } else if (currentState === 'paused') {
61
+ onPause(playerId);
62
+ } else if (currentState === 'ended') {
63
+ onFinish(playerId);
64
+ }
65
+ },
66
+
67
+ onPlayerReady = function (playerData, event) {
68
+ var player = event.target;
69
+
70
+ if (playerData.mute === 'true') {
71
+ player.mute();
72
+ }
73
+ },
74
+
75
+ setupPlayer = function (index, iframe) {
76
+ var playerData = $(iframe).data('youtubeApi'),
77
+ playerId = $(iframe).attr('id'),
78
+ player = new YT.Player(playerId, {
79
+ events: {
80
+ 'onReady': _.partial(onPlayerReady, playerData),
81
+ 'onStateChange': _.partial(handlePlayerState, playerId)
82
+ }
83
+ });
84
+
85
+ return player;
86
+ },
87
+
88
+ scriptLoaded = function ($scope) {
89
+ var $youtubeIframes = $('[data-youtube-api]', $scope);
90
+
91
+ $youtubeIframes.each(setupPlayer);
92
+ },
93
+
94
+ injectSDK = function () {
95
+ if ($('[data-youtube-api]').data('youtube-api-loaded')) {
96
+ return;
97
+ } else {
98
+ $.getScript('https://www.youtube.com/player_api');
99
+ }
100
+ },
101
+
102
+ init = function ($scope) {
103
+ var $youtubeIframes = $('[data-youtube-api]', $scope);
104
+
105
+ if (_.isEmpty($youtubeIframes)) { return; }
106
+
107
+ injectSDK();
108
+ };
109
+
110
+ return {
111
+ init: init,
112
+ scriptLoaded: scriptLoaded
113
+ };
114
+ }()));
115
+
116
+ /**
117
+ * onYouTubeIframeAPIReady is called by YouTube API
118
+ * This is executed when the API is ready to go.
119
+ */
120
+ /*eslint no-unused-vars: 0 */
121
+ function onYouTubeIframeAPIReady() {
122
+ $('[data-youtube-api]').data('youtube-api-loaded', true);
123
+ WORKAREA.youtubeApi.scriptLoaded($(document));
124
+ }
@@ -0,0 +1,37 @@
1
+ /*------------------------------------*\
2
+ #PRODUCT-VIDEO
3
+ \*------------------------------------*/
4
+
5
+
6
+ $product-video-padding: 56.25% !default; // 16:9 aspect
7
+ $product-video-width: 80vw !default;
8
+ $product-video-max-width: 1080px !default;
9
+
10
+
11
+ .product-video {
12
+ position: relative;
13
+ margin: 0 auto;
14
+ }
15
+
16
+ .product-video--thumbnail {}
17
+
18
+ .product-video__container {
19
+ position: relative;
20
+ padding-bottom: $product-video-padding;
21
+ width: $product-video-width;
22
+ max-width: $product-video-max-width;
23
+ }
24
+
25
+ .product-video__iframe {
26
+ position: absolute;
27
+ width: 100%;
28
+ height: 100%;
29
+ }
30
+
31
+ .product-video__dialog-link {}
32
+
33
+ .product-video__thumbnail-image {}
34
+
35
+ .product-video__play-icon {
36
+ @include center;
37
+ }
@@ -0,0 +1,52 @@
1
+ module Workarea
2
+ class Admin::CatalogProductVideosController < Admin::ApplicationController
3
+ required_permissions :catalog
4
+ before_action :find_product
5
+
6
+ def index
7
+ @videos = @product.videos
8
+ end
9
+
10
+ def new; end
11
+
12
+ def create
13
+ @video = @product.videos.build(params[:video])
14
+
15
+ if @video.save
16
+ flash[:success] = t("workarea.admin.catalog_product_videos.flash_messages.created")
17
+ redirect_to catalog_product_videos_path(@product)
18
+ else
19
+ render :index, status: :unprocessable_entity
20
+ end
21
+ end
22
+
23
+ def edit
24
+ @video = @product.videos.find(params[:id])
25
+ end
26
+
27
+ def update
28
+ @video = @product.videos.find(params[:id])
29
+
30
+ if @video.update_attributes(params[:video])
31
+ flash[:success] = t("workarea.admin.catalog_product_videos.flash_messages.updated")
32
+ else
33
+ flash[:error] = t("workarea.admin.catalog_product_videos.flash_messages.error")
34
+ end
35
+
36
+ redirect_to catalog_product_videos_path(@product)
37
+ end
38
+
39
+ def destroy
40
+ @product.videos.find(params[:id]).destroy
41
+ flash[:success] = t("workarea.admin.catalog_product_videos.flash_messages.deleted")
42
+ redirect_to catalog_product_videos_path(@product)
43
+ end
44
+
45
+ private
46
+
47
+ def find_product
48
+ model = Catalog::Product.find_by(slug: params[:catalog_product_id])
49
+ @product = Admin::ProductViewModel.wrap(model, view_model_options)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ module Workarea
2
+ decorate Storefront::ProductsController, with: :product_videos do
3
+ def video
4
+ model = Catalog::Product.find_by(id: params[:product_id])
5
+ raise InvalidDisplay unless model.active? || current_user.try(:admin?)
6
+
7
+ @product = Storefront::ProductViewModel.wrap(
8
+ model,
9
+ view_model_options
10
+ )
11
+
12
+ video = @product.videos.detect{ |v| v.id.to_s == params[:video_id] }
13
+ @video = Storefront::ProductVideoViewModel.wrap(
14
+ video,
15
+ view_model_options
16
+ )
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ module Workarea
2
+ decorate Catalog::Product, with: :product_videos do
3
+ decorated do
4
+ embeds_many :videos, class_name: 'Workarea::Catalog::ProductVideo'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,32 @@
1
+ module Workarea
2
+ module Catalog
3
+ class ProductVideo
4
+ include ApplicationDocument
5
+
6
+ field :embed_code, type: String
7
+ field :display_option, type: String, default: "dialog"
8
+ field :embed_id, type: String
9
+ field :url, type: String
10
+ field :host, type: String
11
+ field :aspect_ratio, type: String
12
+ field :thumbnail, type: String
13
+
14
+ embedded_in :product,
15
+ class_name: "Workarea::Catalog::Product",
16
+ inverse_of: :videos,
17
+ touch: true
18
+
19
+ validates :embed_code, presence: true
20
+ validates :display_option, presence: true
21
+ validates :embed_id, presence: true
22
+ validates :url, presence: true
23
+ validates :host, presence: true
24
+ validates :aspect_ratio, presence: true
25
+ before_validation :process_embed_code
26
+
27
+ def process_embed_code
28
+ VideoEmbedParser.new(self.embed_code).video_params.each { |key, value| self[key] = value }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,54 @@
1
+ module Workarea
2
+ class VideoEmbedParser
3
+ def initialize(embed_code, options = {})
4
+ @parsed_embed_code = Nokogiri::HTML.fragment(embed_code).children.first
5
+ @options = options
6
+ end
7
+
8
+ def video_params
9
+ {
10
+ embed_id: embed_id,
11
+ url: url,
12
+ host: uri.host,
13
+ aspect_ratio: aspect_ratio
14
+ }
15
+ end
16
+
17
+ private
18
+
19
+ def vimeo?
20
+ /vimeo/.match?(host)
21
+ end
22
+
23
+ def youtube?
24
+ /youtube/.match(host)
25
+ end
26
+
27
+ def url
28
+ @url ||= @parsed_embed_code.attribute("src").value
29
+ end
30
+
31
+ def uri
32
+ URI.parse(url)
33
+ end
34
+
35
+ def host
36
+ uri.host
37
+ end
38
+
39
+ def embed_id
40
+ id = uri.path.split("/").last
41
+ if youtube?
42
+ id = "youtube-#{id}"
43
+ end
44
+ id
45
+ end
46
+
47
+ def aspect_ratio
48
+ height = @parsed_embed_code.attribute("height").value
49
+ width = @parsed_embed_code.attribute("width").value
50
+ ratio = (height.to_f / width.to_f) * 100
51
+ ratio.round(2)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,84 @@
1
+ module Workarea
2
+ module Storefront
3
+ class ProductVideoViewModel < ApplicationViewModel
4
+ def iframe_attrs
5
+ attrs = {
6
+ id: model.embed_id,
7
+ frameborder: "0",
8
+ allowfullscreen: "",
9
+ container_style: "padding-bottom: #{model.aspect_ratio}%"
10
+ }
11
+
12
+ if vimeo?
13
+ attrs.merge(vimeo_attrs)
14
+ elsif youtube?
15
+ attrs.merge(youtube_attrs)
16
+ end
17
+ end
18
+
19
+ def thumbnail
20
+ return unless model.thumbnail.present?
21
+ @thumbnail ||= Workarea::Content::Asset.find_or_initialize_by(id: model.thumbnail)
22
+ end
23
+
24
+ private
25
+
26
+ def vimeo?
27
+ /vimeo/.match?(host)
28
+ end
29
+
30
+ def youtube?
31
+ /youtube/.match?(host)
32
+ end
33
+
34
+ def vimeo_attrs
35
+ {
36
+ src: "#{model.url}?#{vimeo_url_params}",
37
+ webkitallowfullscreen: "",
38
+ mozallowfullscreen: "",
39
+ data: {
40
+ vimeo_api: {
41
+ player_id: model.embed_id,
42
+ mute: @options[:mute]
43
+ }.to_json
44
+ }
45
+ }
46
+ end
47
+
48
+ def youtube_attrs
49
+ {
50
+ src: "#{url}?#{youtube_url_params}",
51
+ data: {
52
+ youtube_api: {
53
+ mute: @options[:mute]
54
+ }.to_json
55
+ }
56
+ }
57
+ end
58
+
59
+ def uri
60
+ URI.parse(model.url)
61
+ end
62
+
63
+ def vimeo_url_params
64
+ parsed_query = Rack::Utils.parse_query(uri.query)
65
+ parsed_query["api"] = 1
66
+ parsed_query["autoplay"] = @options[:autoplay]
67
+ parsed_query["loop"] = @options[:loop_playback]
68
+ parsed_query["player_id"] = model.embed_id
69
+
70
+ URI.encode_www_form(parsed_query)
71
+ end
72
+
73
+ def youtube_url_params
74
+ parsed_query = Rack::Utils.parse_query(uri.query)
75
+ parsed_query["enablejsapi"] = 1
76
+ parsed_query["autoplay"] = @options[:autoplay] ? 1 : 0
77
+ parsed_query["loop"] = @options[:loop_playback] ? 1 : 0
78
+ parsed_query["videoId"] = model.embed_id
79
+
80
+ URI.encode_www_form(parsed_query)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,9 @@
1
+ module Workarea
2
+ decorate Storefront::ProductViewModel, with: :product_videos do
3
+ def videos
4
+ @videos ||= Storefront::ProductVideoViewModel.wrap(
5
+ model.videos
6
+ )
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,76 @@
1
+ - @page_title = t('workarea.admin.catalog_product_videos.edit.title', product: @product.name)
2
+
3
+ .product-images.view
4
+ .view__header
5
+ .grid.grid--middle
6
+ .grid__cell.grid__cell--25
7
+ = render 'workarea/admin/releases/select'
8
+ .grid__cell.grid__cell--50
9
+ .view__heading
10
+ = link_to_index_for(@product)
11
+ %h1= link_to @product.name, url_for(@product)
12
+ .grid__cell.grid__cell--25
13
+ = render_aux_navigation_for(@product)
14
+
15
+ .view__container
16
+ = render_cards_for(@product, :videos)
17
+
18
+ .section
19
+ %h2.view__heading= t('workarea.admin.catalog_product_videos.edit.heading')
20
+
21
+ %h3= t('workarea.admin.catalog_product_videos.edit.extracted_data')
22
+ %table
23
+ %thead
24
+ %tr
25
+ %th= t('workarea.admin.catalog_product_videos.fields.video_id')
26
+ %th= t('workarea.admin.catalog_product_videos.fields.host')
27
+ %th= t('workarea.admin.catalog_product_videos.fields.url')
28
+ %th= t('workarea.admin.catalog_product_videos.fields.aspect_ratio')
29
+ %tbody
30
+ %tr
31
+ %td= @video.embed_id
32
+ %td= @video.host
33
+ %td= link_to @video.url, @video.url, target: '_blank', rel: 'noopener'
34
+ %td= @video.aspect_ratio
35
+ .grid
36
+ .grid__cell.grid__cell--33
37
+ = form_tag catalog_product_video_path(@product, @video), method: :patch, class: 'product-videos__action-group-item' do
38
+ .property.property--required
39
+ = label_tag 'video_embed_code', class: 'property__name' do
40
+ %span.property__text= t('workarea.admin.catalog_product_videos.fields.embed_code')
41
+ = text_area_tag 'video[embed_code]', @video.embed_code, class: 'text-box text-box--i18n text-box--multi-line', required: true
42
+ .property.property--required
43
+ = label_tag 'video_display_option', class: 'property__name' do
44
+ %span.property__text= t('workarea.admin.catalog_product_videos.fields.display_type')
45
+ = link_to '#display-type-info', data: { tooltip: '' } do
46
+ = inline_svg('workarea/admin/icons/help.svg', class: 'svg-icon svg-icon--small svg-icon--blue', title: t('workarea.admin.catalog_product_videos.fields.display_type'))
47
+ #display-type-info.tooltip-content
48
+ %p= t('workarea.admin.catalog_product_videos.fields.display_type_info_html')
49
+ = select_tag 'video[display_option]', options_for_select(Workarea.config.product_videos[:display_options], @video.display_option), id: 'video_display_option', required: true
50
+
51
+ .property
52
+ = label_tag 'video_thumbnail', class: 'property__name' do
53
+ %span.property__text= t('workarea.admin.catalog_product_videos.fields.thumbnail')
54
+
55
+ .asset-picker-field{ data: { asset_picker_field: '' } }
56
+ .asset-picker-field__cell
57
+ = hidden_field_tag 'video[thumbnail]', @video.thumbnail, id: 'video_thumbnail', class: 'text-box text-box--i18n'
58
+
59
+ %span{ data: { asset_name: true } }
60
+ - asset = Workarea::Content::Asset.find_or_initialize_by(id: @video.thumbnail)
61
+ = asset.name.presence || t('workarea.admin.content_blocks.asset.name_missing')
62
+
63
+ .asset-picker-field__cell= link_to t('workarea.admin.content_blocks.asset.select_an_asset'), insert_content_assets_path(file_type: 'image'), class: 'text-button', data: { asset_picker_field_open: '', turbolinks: false }
64
+ - if asset.name.present?
65
+ .asset-picker-field__cell= button_tag t('workarea.admin.content_blocks.asset.clear_asset'), type: 'button', value: 'clear_asset', id: nil, class: 'text-button text-button--destroy', data: { asset_picker_field_clear: '' }
66
+
67
+
68
+ .workflow-bar
69
+ .grid.grid--right.grid--middle
70
+ .grid__cell.grid__cell--20
71
+ = link_to t('workarea.admin.form.cancel'), url_for(@product), class: 'workflow-bar__button workflow-bar__button--delete'
72
+
73
+ .grid__cell.grid__cell--80
74
+ .grid.grid--auto.grid--right.grid--middle
75
+ .grid__cell
76
+ = button_tag t('workarea.admin.catalog_product_videos.edit.update_button'), value: 'edit_video', class: 'workflow-bar__button workflow-bar__button--update'
@@ -0,0 +1,46 @@
1
+ - @page_title = t('workarea.admin.catalog_product_videos.index.title', product: @product.name)
2
+
3
+ .product-images.view
4
+ .view__header
5
+ .grid.grid--middle
6
+ .grid__cell.grid__cell--25
7
+ = render 'workarea/admin/releases/select'
8
+ .grid__cell.grid__cell--50
9
+ .view__heading
10
+ = link_to_index_for(@product)
11
+ %h1= link_to @product.name, url_for(@product)
12
+ .grid__cell.grid__cell--25
13
+ = render_aux_navigation_for(@product)
14
+
15
+ .view__container
16
+ = render_cards_for(@product, :videos)
17
+
18
+ .section
19
+ %h2.view__heading= t('workarea.admin.catalog_product_videos.index.heading')
20
+ %table.index-table
21
+ %thead
22
+ %tr
23
+ %th= t('workarea.admin.catalog_product_videos.fields.video_id')
24
+ %th= t('workarea.admin.catalog_product_videos.fields.host')
25
+ %th= t('workarea.admin.catalog_product_videos.fields.display_type')
26
+ %th
27
+ %tbody
28
+ - @product.videos.each do |video|
29
+ %tr.index-table__row
30
+ %td= link_to video[:embed_id], edit_catalog_product_video_path(@product, video)
31
+ %td= video[:host]
32
+ %td= video.display_option
33
+ %td
34
+ = form_tag catalog_product_video_path(@product, video), method: 'delete', class: 'text-button text-button--destroy' do
35
+ = button_tag value: 'delete_video', class: 'text-button text-button--destroy' do
36
+ = inline_svg('workarea/admin/icons/delete.svg', class: 'text-button__icon', title: t('workarea.admin.actions.delete'))
37
+
38
+ .workflow-bar
39
+ .grid.grid--right.grid--middle
40
+ .grid__cell.grid__cell--20
41
+ = link_to t('workarea.admin.form.cancel'), url_for(@product), class: 'workflow-bar__button workflow-bar__button--delete'
42
+
43
+ .grid__cell.grid__cell--80
44
+ .grid.grid--auto.grid--right.grid--middle
45
+ .grid__cell
46
+ = link_to t('workarea.admin.catalog_product_videos.index.add_new'), new_catalog_product_video_path(@product), class: 'workflow-bar__button workflow-bar__button--create'
@@ -0,0 +1,57 @@
1
+ - @page_title = t('workarea.admin.catalog_product_videos.new.title', product: @product.name)
2
+
3
+ .product-images.view
4
+ .view__header
5
+ .grid.grid--middle
6
+ .grid__cell.grid__cell--25
7
+ = render 'workarea/admin/releases/select'
8
+ .grid__cell.grid__cell--50
9
+ .view__heading
10
+ = link_to_index_for(@product)
11
+ %h1= link_to @product.name, url_for(@product)
12
+ .grid__cell.grid__cell--25
13
+ = render_aux_navigation_for(@product)
14
+
15
+ .view__container
16
+ = render_cards_for(@product, :videos)
17
+
18
+ .section
19
+ %h2.view__heading= t('workarea.admin.catalog_product_videos.new.heading')
20
+ .grid
21
+ .grid__cell.grid__cell--33
22
+ = form_tag catalog_product_videos_path(@product), method: 'post', multipart: true do
23
+ .property.property--required
24
+ = label_tag 'video_embed_code', class: 'property__name' do
25
+ %span.property__text= t('workarea.admin.catalog_product_videos.fields.embed_code')
26
+ = text_area_tag 'video[embed_code]', nil, class: 'text-box text-box--i18n text-box--multi-line', required: true
27
+ .property.property--required
28
+ = label_tag 'video_display_option', class: 'property__name' do
29
+ %span.property__text= t('workarea.admin.catalog_product_videos.fields.display_type')
30
+ = link_to '#display-type-info', data: { tooltip: '' } do
31
+ = inline_svg('workarea/admin/icons/help.svg', class: 'svg-icon svg-icon--small svg-icon--blue', title: t('workarea.admin.catalog_product_videos.fields.display_type'))
32
+ #display-type-info.tooltip-content
33
+ %p= t('workarea.admin.catalog_product_videos.fields.display_type_info_html')
34
+ .value= select_tag 'video[display_option]', options_for_select(Workarea.config.product_videos[:display_options]), id: 'video_display_option', required: true
35
+
36
+ .property
37
+ = label_tag 'video_thumbnail', class: 'property__name' do
38
+ %span.property__text= t('workarea.admin.catalog_product_videos.fields.thumbnail')
39
+
40
+ .asset-picker-field{ data: { asset_picker_field: '' } }
41
+ .asset-picker-field__cell
42
+ = hidden_field_tag 'video[thumbnail]', nil, id: 'video_thumbnail', class: 'text-box text-box--i18n'
43
+
44
+ %span{ data: { asset_name: true } }
45
+ = t('workarea.admin.content_blocks.asset.name_missing')
46
+
47
+ .asset-picker-field__cell= link_to t('workarea.admin.content_blocks.asset.select_an_asset'), insert_content_assets_path(file_type: 'image'), class: 'text-button', data: { asset_picker_field_open: '', turbolinks: false }
48
+
49
+ .workflow-bar
50
+ .grid.grid--right.grid--middle
51
+ .grid__cell.grid__cell--20
52
+ = link_to t('workarea.admin.form.cancel'), url_for(@product), class: 'workflow-bar__button workflow-bar__button--delete'
53
+
54
+ .grid__cell.grid__cell--80
55
+ .grid.grid--auto.grid--right.grid--middle
56
+ .grid__cell
57
+ = button_tag t('workarea.admin.catalog_product_videos.new.button'), value: 'create_video', class: 'workflow-bar__button workflow-bar__button--create'