diff --git a/app/assets/stylesheets/admin.bootstrap.scss b/app/assets/stylesheets/admin.bootstrap.scss
index b8fe9689..871486a2 100644
--- a/app/assets/stylesheets/admin.bootstrap.scss
+++ b/app/assets/stylesheets/admin.bootstrap.scss
@@ -1,4 +1,3 @@
-
@import 'admin/components/colors.scss';
@import 'admin/components/buttons.scss';
@@ -8,3 +7,7 @@
// Custom
@import 'admin/header.scss';
+
+// Slim Select custom
+@import 'slim-select/dist/slimselect.css';
+@import 'admin/custom_slim_select.scss';
diff --git a/app/assets/stylesheets/admin/custom_slim_select.scss b/app/assets/stylesheets/admin/custom_slim_select.scss
new file mode 100644
index 00000000..ef87cb4c
--- /dev/null
+++ b/app/assets/stylesheets/admin/custom_slim_select.scss
@@ -0,0 +1,36 @@
+.ss-content {
+ padding-right: 0.75rem;
+ background-image: none;
+
+ .ss-list {
+ height: 150px;
+
+ .ss-option {
+ color: var(--bs-body-color);
+
+ &:not(.ss-disabled) {
+ &.ss-selected {
+ background-color: $primary;
+ }
+
+ &:first-child {
+ color: var(--bs-secondary-color);
+ }
+ }
+
+ &:hover {
+ color: var(--bs-body-color);
+ background-color: $primary;
+ }
+ &:first-child {
+ &.ss-selected {
+ background-color: $primary-bg-subtle;
+ }
+ }
+ }
+ }
+
+ .ss-search input:focus {
+ box-shadow: $box-shadow-sm;
+ }
+}
diff --git a/app/controllers/admin/events_controller.rb b/app/controllers/admin/events_controller.rb
index 1f612adf..7629e771 100644
--- a/app/controllers/admin/events_controller.rb
+++ b/app/controllers/admin/events_controller.rb
@@ -16,9 +16,9 @@ def create
@event = Event.create(event_params)
if @event.save
- redirect_to admin_events_path, notice: 'Event has been created successfully'
+ redirect_to edit_admin_event_path(@event), notice: 'Event was successfully created'
else
- render :new, alert: 'Please review'
+ render :new, status: :unprocessable_entity
end
end
@@ -30,9 +30,9 @@ def update
render_not_found unless @event
if @event.update(event_params)
- redirect_to admin_events_path
+ redirect_to admin_events_path, notice: 'Event was successfully updated'
else
- render :edit
+ render :edit, notice: 'Error updating event'
end
end
@@ -46,6 +46,36 @@ def destroy
end
end
+ #GET /admin/events/set_talk/1
+ def set_talk
+ @talk = Talk.new
+ @url = generate_talk_admin_events_path(talk_id: 'create_talk')
+
+ return unless params[:talk_id] != 'new_talk'
+ talk_id = params[:talk_id]
+ @talk = Talk.new(event_params['talks_attributes'][talk_id].except(:_destroy))
+ @url = generate_talk_admin_events_path(talk_id: talk_id)
+
+ end
+
+ #POST /admin/events/get_talk/1
+ def generate_talk
+ @talk = Talk.new(talk_params)
+ talk_id = @talk.object_id
+
+ if params[:talk_id] != 'create_talk'
+ talk_id = params[:talk_id]
+ @talk.id = params[:talk_id]
+ end
+
+ if @talk.valid?
+ render 'admin/events/form/_card_talk_new', locals: { talk: @talk, talk_id: talk_id }
+ else
+ @url = generate_talk_admin_events_path(talk_id: talk_id)
+ render :set_talk, status: :bad_request
+ end
+ end
+
private
def authorize_event
@@ -53,17 +83,30 @@ def authorize_event
end
def set_event
- @event = Event.includes(:talks).find_by(id: params[:id])
+ @event = Event.includes(talks: :speaker).find_by(id: params[:id])
end
def event_params
params.require(:event).permit(
+ :id,
:title,
:location,
:description,
:date,
:type,
:panel_video_link,
+ talks_attributes: Talk.attribute_names.map(&:to_sym).push(:_destroy),
+ )
+ end
+
+ def talk_params
+ params.require(:talk).permit(
+ :talk_description,
+ :talk_title,
+ :talk_video_link,
+ :speaker_id,
+ :event_id,
+ :_destroy,
)
end
end
diff --git a/app/controllers/admin/speakers_controller.rb b/app/controllers/admin/speakers_controller.rb
index abcb7e11..596c930a 100644
--- a/app/controllers/admin/speakers_controller.rb
+++ b/app/controllers/admin/speakers_controller.rb
@@ -6,7 +6,7 @@ class SpeakersController < AdminController
# GET /admin/speakers
def index
- @speakers = Speaker.ordered_by_name
+ @speakers = Speaker.all
end
# GET /admin/speakers/1
diff --git a/app/javascript/admin.js b/app/javascript/admin.js
index b895ce1b..188d979f 100644
--- a/app/javascript/admin.js
+++ b/app/javascript/admin.js
@@ -3,3 +3,6 @@
import './controllers';
import * as bootstrap from 'bootstrap';
import '@hotwired/turbo-rails';
+import './turbo_streams';
+
+window.bootstrap = bootstrap;
diff --git a/app/javascript/controllers/bs_modal_controller.js b/app/javascript/controllers/bs_modal_controller.js
new file mode 100644
index 00000000..736fec66
--- /dev/null
+++ b/app/javascript/controllers/bs_modal_controller.js
@@ -0,0 +1,15 @@
+import { Controller } from '@hotwired/stimulus';
+
+// Connects to data-controller="bs-modal"
+export default class extends Controller {
+ connect() {
+ // eslint-disable-next-line no-undef
+ this.modal = new bootstrap.Modal(this.element);
+
+ this.modal.show();
+ }
+
+ disconnect() {
+ this.modal.hide();
+ }
+}
diff --git a/app/javascript/controllers/event_form_remove_talk_controller.js b/app/javascript/controllers/event_form_remove_talk_controller.js
new file mode 100644
index 00000000..202b49d6
--- /dev/null
+++ b/app/javascript/controllers/event_form_remove_talk_controller.js
@@ -0,0 +1,20 @@
+import { Controller } from '@hotwired/stimulus';
+
+// Connects to data-controller="event-form-remove-talk"
+export default class extends Controller {
+ removeRecord(event) {
+ let talkId = event.currentTarget.dataset.talkId;
+ let talkPersisted = event.currentTarget.dataset.persisted === 'true';
+ let talk = document.getElementById(`talk-${talkId}`);
+ console.log(talkPersisted);
+
+ if (talkPersisted) {
+ console.log('to be hidden');
+ let removeRecordElement = talk.querySelector('.remove_record');
+ removeRecordElement.value = '1';
+ talk.className += ' d-none';
+ } else {
+ talk.remove();
+ }
+ }
+}
diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js
index 0c92444d..7ce7e345 100644
--- a/app/javascript/controllers/index.js
+++ b/app/javascript/controllers/index.js
@@ -4,5 +4,17 @@
import { application } from './application';
+import BsModalController from './bs_modal_controller';
+application.register('bs-modal', BsModalController);
+
import HelloController from './hello_controller';
application.register('hello', HelloController);
+
+import EventFormRemoveTalkController from './event_form_remove_talk_controller';
+application.register('event-form-remove-talk', EventFormRemoveTalkController);
+
+import SlimSelectController from './slim_select_controller';
+application.register('slim-select', SlimSelectController);
+
+import TurboController from './turbo_controller';
+application.register('turbo', TurboController);
diff --git a/app/javascript/controllers/slim_select_controller.js b/app/javascript/controllers/slim_select_controller.js
new file mode 100644
index 00000000..9a23977a
--- /dev/null
+++ b/app/javascript/controllers/slim_select_controller.js
@@ -0,0 +1,19 @@
+import { Controller } from '@hotwired/stimulus';
+import SlimSelect from 'slim-select';
+
+// Connects to data-controller="slim-select"
+export default class extends Controller {
+ connect() {
+ this.select = new SlimSelect({
+ select: this.element,
+ settings: {
+ openPosition: 'up',
+ },
+ });
+ document.querySelector('svg.ss-arrow').remove();
+ }
+
+ disconnect() {
+ this.select.destroy();
+ }
+}
diff --git a/app/javascript/controllers/turbo_controller.js b/app/javascript/controllers/turbo_controller.js
new file mode 100644
index 00000000..bbd59ccc
--- /dev/null
+++ b/app/javascript/controllers/turbo_controller.js
@@ -0,0 +1,36 @@
+import { Controller } from '@hotwired/stimulus';
+import { Turbo } from '@hotwired/turbo-rails';
+// Connects to data-controller="turbo"
+export default class extends Controller {
+ initialize() {
+ this.element.setAttribute('data-action', 'click->turbo#click');
+ }
+ click(e) {
+ e.preventDefault();
+
+ if (this.element.hasAttribute('href')) {
+ // If href is present, get its value
+ this.url = this.element.getAttribute('href');
+ } else if (this.element.hasAttribute('data-href')) {
+ // If href is not present, check for data-href attribute
+ this.url = this.element.getAttribute('data-href');
+ } else if (this.element.hasAttribute('formaction')) {
+ // If href is not present, check for formaction attribute
+ // breakpoint here
+
+ this.url = this.element.getAttribute('formaction');
+ }
+ if (this.url) {
+ fetch(this.url, {
+ headers: {
+ Accept: 'text/vnd.turbo-stream.html',
+ },
+ })
+ .then((r) => r.text())
+ .then((html) => Turbo.renderStreamMessage(html))
+ .catch((err) => console.log(err));
+ } else {
+ console.log('No URL found');
+ }
+ }
+}
diff --git a/app/javascript/turbo_streams/index.js b/app/javascript/turbo_streams/index.js
new file mode 100644
index 00000000..33230825
--- /dev/null
+++ b/app/javascript/turbo_streams/index.js
@@ -0,0 +1 @@
+import './update_children_or_append';
diff --git a/app/javascript/turbo_streams/update_children_or_append.js b/app/javascript/turbo_streams/update_children_or_append.js
new file mode 100644
index 00000000..1b074a6f
--- /dev/null
+++ b/app/javascript/turbo_streams/update_children_or_append.js
@@ -0,0 +1,43 @@
+import { StreamActions } from '@hotwired/turbo';
+
+StreamActions.update_children_or_append = function () {
+ // Custom duplicateChildren function
+ function duplicateChildren(existingChildren, newChildrenIds) {
+ return existingChildren
+ .filter((existingChild) => newChildrenIds.includes(existingChild.id))
+ .map((existingChild) => {
+ const templateChild = this.templateContent.querySelector(`#${existingChild.id}`);
+ return { targetChild: existingChild, templateChild };
+ });
+ }
+
+ // Check if the template content has any children
+ if (this.duplicateChildren.length > 0) {
+ // Get existing children with IDs
+ const existingChildren = this.targetElements
+ .flatMap((e) => [...e.children])
+ .filter((c) => !!c.id);
+
+ // Get new children IDs
+ const newChildrenIds = [...(this.templateContent?.children || [])]
+ .filter((c) => !!c.id)
+ .map((c) => c.id);
+
+ // Get the array of duplicated children with targetChild and templateChild properties using the custom function
+ const duplicatedChildren = duplicateChildren.call(this, existingChildren, newChildrenIds);
+
+ duplicatedChildren.forEach(({ targetChild, templateChild }) => {
+ if (targetChild && templateChild) {
+ const newElement = templateChild.cloneNode(true);
+ templateChild.remove();
+ targetChild.replaceWith(newElement);
+
+ // Access the new element here
+ // console.log(newElement);
+ }
+ });
+ } else {
+ // Append new children
+ this.targetElements.forEach((e) => e.append(this.templateContent));
+ }
+};
diff --git a/app/models/event.rb b/app/models/event.rb
index f4a58a53..779f1f1c 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -18,5 +18,7 @@ class Event < ApplicationRecord
has_many :talks, dependent: :destroy
has_many :speakers, through: :talks
+ accepts_nested_attributes_for :talks, allow_destroy: true
+
validates :title, :location, :date, presence: true
end
diff --git a/app/models/speaker.rb b/app/models/speaker.rb
index ff681899..36d6d42e 100644
--- a/app/models/speaker.rb
+++ b/app/models/speaker.rb
@@ -28,7 +28,7 @@ class Speaker < ApplicationRecord
before_validation :format_links
- scope :ordered_by_name, -> { order(:name) }
+ default_scope { order(name: :asc) }
def validate_social_media_brand
return if links.blank? || links.keys.all? { |key| SOCIAL_MEDIA_LINKS.include?(key) }
diff --git a/app/models/talk.rb b/app/models/talk.rb
index 301c537b..dbcaa40d 100644
--- a/app/models/talk.rb
+++ b/app/models/talk.rb
@@ -25,5 +25,9 @@
#
class Talk < ApplicationRecord
belongs_to :speaker
- belongs_to :event
+ belongs_to :event, optional: true
+
+ validates :talk_title, :talk_description, presence: true
+
+ default_scope { order(id: :asc) }
end
diff --git a/app/policies/event_policy.rb b/app/policies/event_policy.rb
index 58e2518d..9c79f9dd 100644
--- a/app/policies/event_policy.rb
+++ b/app/policies/event_policy.rb
@@ -23,4 +23,12 @@ def update?
def destroy?
user.admin?
end
+
+ def set_talk?
+ create?
+ end
+
+ def generate_talk?
+ create?
+ end
end
diff --git a/app/views/admin/events/_form.html.erb b/app/views/admin/events/_form.html.erb
index 74105c8e..c8565ed4 100644
--- a/app/views/admin/events/_form.html.erb
+++ b/app/views/admin/events/_form.html.erb
@@ -1,31 +1,51 @@
- <%= form_for [:admin, event.becomes(Event)] do |f| %>
- <%= render 'layouts/admin/form_errors', object: f.object %>
-
- <%= f.text_field :title, class: "form-control" %>
- <%= f.label :title %>
+<%= form_with model: [:admin, event.becomes(Event)] do |f| %>
+ <%= render 'layouts/admin/form_errors', object: f.object %>
+
+ <%= f.text_field :title, class: "form-control shadow-sm" %>
+ <%= f.label :title %>
+
+
+ <%= f.text_field :location, class: "form-control shadow-sm" %>
+ <%= f.label :location %>
+
+
+ <%= f.text_area :description, rows: 40, class: "form-control shadow-sm", style: "height: 100px" %>
+ <%= f.label :description %>
+
+
+ <%= f.label :date, class: "d-block form-label" %>
+ <%= f.datetime_select :date, {}, { class: "form-select shadow-sm d-inline-block w-auto" } %>
+
+
+ <%= f.select :type, ["Meetup", "Panel"], {}, { class: "form-select shadow-sm" } %>
+ <%= f.label :type, "Type" %>
+
+
+ <%= f.text_field :panel_video_link, class: "form-control shadow-sm" %>
+ <%= f.label :panel_video_link, "Video link" %>
+
+
+
+ <%=f.submit "Save", class: "btn btn-primary btn-lg shadow-sm" %>
+ <%=link_to "Cancel", admin_events_path, class: "btn btn-outline-primary btn-lg shadow-sm" %>
+
+<% end %>
diff --git a/app/views/admin/events/edit.html.erb b/app/views/admin/events/edit.html.erb
index 3b8fdfd0..637b04ed 100644
--- a/app/views/admin/events/edit.html.erb
+++ b/app/views/admin/events/edit.html.erb
@@ -1,6 +1,6 @@
-
+
Edit event
- <%=render 'form', event: @event %>
+ <%= render "form", event: @event %>
diff --git a/app/views/admin/events/form/_card_talk.html.erb b/app/views/admin/events/form/_card_talk.html.erb
new file mode 100644
index 00000000..a25eae65
--- /dev/null
+++ b/app/views/admin/events/form/_card_talk.html.erb
@@ -0,0 +1,69 @@
+<%= tag.div id: "talk-#{talk_id}", class: "col-12 col-md-6" do %>
+
+
+
+
+ <%= fields_for "event[talks_attributes][#{talk_id}]", talk do |ff| %>
+ <%= ff.hidden_field :talk_title %>
+ <%= ff.hidden_field :talk_description %>
+ <%= ff.hidden_field :talk_video_link %>
+ <%= ff.hidden_field :speaker_id %>
+ <% if talk.persisted? %>
+ <%= ff.hidden_field :_destroy, { class: "remove_record" } %>
+ <% end %>
+ <% end %>
+
+
+
+
+ <%=image_tag talk.speaker.image_url, class:"rounded-circle object-fit-cover border border-3 border-primary shadow", height: 53, width: 53, alt:"Profile photo for #{talk.speaker.name}" %>
+
+
+
<%= talk.talk_title %>
+ <%= talk.speaker.name %>
+
+
+
+
+
<%= talk.talk_description %>
+
+
+
+
+
+
+
+ <% if talk.talk_video_link.present? %>
+ Video:
+ <%= link_to "Youtube", talk.talk_video_link, class:"text-nowrap", target: "_blank", rel: "noopener noreferrer" %>
+ <% else %>
+ (No video provided)
+ <% end %>
+
+
+
+ <%= button_tag type: :submit, formmethod: "get", formaction: set_talk_admin_events_path(talk_id: talk_id), class: "btn btn-info", data: { turbo_stream: "true" } do %>
+
+ <% end %>
+ <%=button_tag type: "button", class: "btn btn-outline-secondary", data: { action: "event-form-remove-talk#removeRecord", talk_id: talk_id, persisted: talk.persisted? } do %>
+
+ <% end %>
+
+
+
+
+
+
+
+<% end %>
+<% if talk.persisted? %>
+
+<% end %>
diff --git a/app/views/admin/events/form/_card_talk_new.turbo_stream.erb b/app/views/admin/events/form/_card_talk_new.turbo_stream.erb
new file mode 100644
index 00000000..f2cd240f
--- /dev/null
+++ b/app/views/admin/events/form/_card_talk_new.turbo_stream.erb
@@ -0,0 +1,8 @@
+
+
+ <%= render "admin/events/form/card_talk", talk: talk, talk_id: talk_id %>
+ <% if talk.persisted? %>
+
+ <% end %>
+
+
diff --git a/app/views/admin/events/index.html.erb b/app/views/admin/events/index.html.erb
index 52cba037..f148d7d7 100644
--- a/app/views/admin/events/index.html.erb
+++ b/app/views/admin/events/index.html.erb
@@ -25,7 +25,6 @@
<% @events.each do |event| %>
-
<%= event.id %> |
<%= event.title %> |
@@ -36,7 +35,7 @@
<%= truncate_html(event.description) %> |
<%= link_to 'Edit', edit_admin_event_path(event) %>
- <%= button_to "Delete", admin_event_url(event), form_class: "", class: "link-primary border-0 bg-transparent text-decoration-underline", method: :delete %>
+ <%= button_to "Delete", admin_event_url(event), form_class: "", class: "link-primary border-0 bg-transparent text-decoration-underline", method: :delete, data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %>
|
<% end %>
diff --git a/app/views/admin/events/new.html.erb b/app/views/admin/events/new.html.erb
index 4f4c1d0a..f9c18747 100644
--- a/app/views/admin/events/new.html.erb
+++ b/app/views/admin/events/new.html.erb
@@ -1,6 +1,6 @@
-
+
New Event
- <%= render 'form', event: @event %>
+ <%= render "form", event: @event %>
diff --git a/app/views/admin/events/set_talk.turbo_stream.erb b/app/views/admin/events/set_talk.turbo_stream.erb
new file mode 100644
index 00000000..b948d119
--- /dev/null
+++ b/app/views/admin/events/set_talk.turbo_stream.erb
@@ -0,0 +1,38 @@
+<%= turbo_stream.append "remote_modal" do %>
+ <%= turbo_frame_tag :remote_modal, target: :_top do %>
+
+
+ <%= form_with model: @talk, url: @url, method: :post, class: "modal-content" do |f| %>
+
+
+ <%= f.hidden_field :event_id, value: @talk.event_id %>
+ <%= render 'layouts/admin/form_errors', object: @talk %>
+
+ <%= f.text_field :talk_title, class: "form-control" %>
+ <%= f.label :talk_title, "Title" %>
+
+
+ <%= f.text_area :talk_description, class: "form-control", style: "height: 100px" %>
+ <%= f.label :talk_description, "Description" %>
+
+
+ <%= f.text_field :talk_video_link, class: "form-control" %>
+ <%= f.label :talk_video_link, "Video link" %>
+
+
+ <%= f.select :speaker_id, Speaker.pluck(:name, :id), { include_blank: "Select one speaker" }, { data: { controller: "slim-select" }, class: "form-select form-control #{'is-invalid' if f.object.errors[:speaker].any?}" } %>
+ <%= f.label :speaker_id, "Speaker" %>
+
+
+
+ <% end %>
+
+
+ <% end %>
+<% end %>
diff --git a/app/views/admin/speakers/_form.html.erb b/app/views/admin/speakers/_form.html.erb
index 5592d23d..1941b348 100644
--- a/app/views/admin/speakers/_form.html.erb
+++ b/app/views/admin/speakers/_form.html.erb
@@ -1,19 +1,19 @@
<%= form_for [:admin, speaker] do |f| %>
<%= render "layouts/admin/form_errors", object: f.object %>
- <%= f.text_field :name, class: "form-control" %>
+ <%= f.text_field :name, class: "form-control shadow-sm" %>
<%= f.label :name %>
- <%= f.text_area :bio, rows: 40, class: "form-control", style: "height: 200px" %>
+ <%= f.text_area :bio, rows: 40, class: "form-control shadow-sm", style: "height: 200px" %>
<%= f.label :bio %>
- <%= f.text_field :tagline, class: "form-control" %>
+ <%= f.text_field :tagline, class: "form-control shadow-sm" %>
<%= f.label :tagline %>
- <%= f.url_field :image_url, class: "form-control" %>
+ <%= f.url_field :image_url, class: "form-control shadow-sm" %>
<%= f.label :image_url, "Image link" %>
@@ -30,7 +30,7 @@
<%= f.label link.to_sym, class: "input-group-text" %>
- <%= f.text_field link.to_sym, class: "form-control", placeholder: "https://socialmedia.url", aria: { label: link.capitalize, describedby: link } %>
+ <%= f.text_field link.to_sym, class: "form-control shadow-sm", placeholder: "https://socialmedia.url", aria: { label: link.capitalize, describedby: link } %>
<% end %>
diff --git a/app/views/layouts/admin.html.erb b/app/views/layouts/admin.html.erb
index 6632583b..69e716f3 100644
--- a/app/views/layouts/admin.html.erb
+++ b/app/views/layouts/admin.html.erb
@@ -18,11 +18,12 @@
<%= stylesheet_link_tag "admin" %>
<%= favicon_link_tag asset_path('favicon.ico') %>
-
+
<%= render "layouts/admin/header" %>
<%= render "layouts/admin/flashes" %>
<%= yield %>
+ <%= turbo_frame_tag "remote_modal" %>
diff --git a/app/views/layouts/admin/_flashes.html.erb b/app/views/layouts/admin/_flashes.html.erb
index d6958919..464860e7 100644
--- a/app/views/layouts/admin/_flashes.html.erb
+++ b/app/views/layouts/admin/_flashes.html.erb
@@ -1,5 +1,5 @@
<% if flash.any? %>
-
+
<% flash.each do |type, msg| %>
- <%= msg %>
diff --git a/app/views/layouts/admin/_form_errors.html.erb b/app/views/layouts/admin/_form_errors.html.erb
index 70081809..81bbe001 100644
--- a/app/views/layouts/admin/_form_errors.html.erb
+++ b/app/views/layouts/admin/_form_errors.html.erb
@@ -1,6 +1,6 @@
<%- if object.errors.any? %>
-
<%= pluralize(object.errors.count, "error") %> prohibited this event from being saved:
+
Could not be saved because of <%= pluralize(object.errors.count, "error") %>:
<% object.errors.full_messages.each do |msg| %>
- <%= msg %>
diff --git a/config/routes.rb b/config/routes.rb
index ecc63a36..ca389d9e 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -9,10 +9,15 @@
registration: 'register',
}
- namespace :admin, constraints: { format: 'html' } do
+ namespace :admin do
get 'dashboard', to: 'dashboard#show'
resources :speakers, only: %i[index new create edit update]
- resources :events, only: %i[index new create edit update destroy]
+ resources :events, only: %i[index new create edit update destroy] do
+ collection do
+ get 'set_talk/:talk_id', action: :set_talk, as: :set_talk
+ post 'generate_talk/:talk_id', action: :generate_talk, as: :generate_talk
+ end
+ end
end
get '/sponsor-us', to: redirect('/partner-with-us')
diff --git a/package.json b/package.json
index ec467de3..c9e7b1da 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,7 @@
"react-helmet": "^6.1.0",
"sass": "^1.60.0",
"sass-loader": "^13.2.0",
+ "slim-select": "^2.6.0",
"style-loader": "^3.3.1",
"tailwindcss": "^3.3.1",
"terser-webpack-plugin": "^5.3.6",
diff --git a/spec/controllers/admin/events_controller_spec.rb b/spec/controllers/admin/events_controller_spec.rb
index 9097870b..9f2ea520 100644
--- a/spec/controllers/admin/events_controller_spec.rb
+++ b/spec/controllers/admin/events_controller_spec.rb
@@ -146,7 +146,7 @@
},
}
- expect(response).to redirect_to(admin_events_path)
+ expect(response).to redirect_to edit_admin_event_path(Event.last)
end
end
end
diff --git a/spec/factories/talks.rb b/spec/factories/talks.rb
new file mode 100644
index 00000000..1e2b851d
--- /dev/null
+++ b/spec/factories/talks.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+FactoryBot.define do
+ factory :talk do
+ association :speaker
+ association :event, factory: :event, strategy: :build
+ talk_title { Faker::Lorem.sentence }
+ talk_description { Faker::Lorem.paragraph }
+
+ trait :with_event do
+ association :event
+ end
+ end
+end
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index 76818e8d..6078c2f2 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -26,5 +26,6 @@
it { is_expected.to validate_presence_of(:title) }
it { is_expected.to validate_presence_of(:location) }
it { is_expected.to validate_presence_of(:date) }
+ it { is_expected.to accept_nested_attributes_for(:talks).allow_destroy(true) }
end
end
diff --git a/spec/models/speaker_spec.rb b/spec/models/speaker_spec.rb
index 986ce407..f195005f 100644
--- a/spec/models/speaker_spec.rb
+++ b/spec/models/speaker_spec.rb
@@ -31,7 +31,7 @@
it 'orders speakers by name' do
speaker_b = create(:speaker, name: 'B Speaker')
speaker_a = create(:speaker, name: 'A Speaker')
- expect(Speaker.ordered_by_name).to eq([speaker_a, speaker_b])
+ expect(Speaker.all).to eq([speaker_a, speaker_b])
end
end
context 'when links are not present' do
diff --git a/spec/models/talk_spec.rb b/spec/models/talk_spec.rb
index 73e8042e..820d05ba 100644
--- a/spec/models/talk_spec.rb
+++ b/spec/models/talk_spec.rb
@@ -27,6 +27,22 @@
RSpec.describe Talk, type: :model do
describe 'associations' do
it { is_expected.to belong_to(:speaker) }
- it { is_expected.to belong_to(:event) }
+ it { is_expected.to belong_to(:event).optional }
+ end
+
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:talk_title) }
+ it { is_expected.to validate_presence_of(:talk_description) }
+ end
+
+ describe 'scopes' do
+ describe 'default_scope' do
+ it 'orders talks by id in ascending order' do
+ talk1 = create(:talk, id: 2)
+ talk2 = create(:talk, id: 1)
+
+ expect(Talk.all).to eq([talk2, talk1])
+ end
+ end
end
end
diff --git a/spec/system/managing_events_spec.rb b/spec/system/managing_events_spec.rb
index 841439d1..e05825b6 100644
--- a/spec/system/managing_events_spec.rb
+++ b/spec/system/managing_events_spec.rb
@@ -27,8 +27,8 @@
click_on 'Save'
- expect(page).to have_current_path(admin_events_path)
- expect(page).to have_text('Event has been created successfully')
+ expect(page).to have_current_path(edit_admin_event_path(Event.last))
+ expect(page).to have_text('Event was successfully created')
expect(Event.last.title).to eq(new_event.title)
end
end
diff --git a/spec/system/managing_speakers_spec.rb b/spec/system/managing_speakers_spec.rb
index e6cfeb47..729ea442 100644
--- a/spec/system/managing_speakers_spec.rb
+++ b/spec/system/managing_speakers_spec.rb
@@ -26,9 +26,10 @@
fill_in 'Image link', with: new_speaker.image_url
click_on 'Save'
-
expect(page).to have_text('Speaker was successfully created.')
- expect(page).to have_current_path(edit_admin_speaker_url(Speaker.last.id))
+ expect(page).to have_current_path(
+ edit_admin_speaker_url(Speaker.find_by(id: Speaker.maximum(:id)).id),
+ )
end
end
diff --git a/yarn.lock b/yarn.lock
index bbc06e20..71b2e1bc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7190,6 +7190,11 @@ slice-ansi@^5.0.0:
ansi-styles "^6.0.0"
is-fullwidth-code-point "^4.0.0"
+slim-select@^2.6.0:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/slim-select/-/slim-select-2.6.0.tgz#d588aedaaf18c74f6508d62ec7ef3a5533de37f0"
+ integrity sha512-ubIyC+IOaLpq+Ls5Eo5sXq2QSPfAGNkfKKqHzUuTSKvaDKkCn+4rK+GrBqvnpXk5YYwidluSCG0X6pwJlOKhtw==
+
sockjs@^0.3.24:
version "0.3.24"
resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce"