diff --git a/app/controllers/api/events_controller.rb b/app/controllers/api/events_controller.rb
index 45eba931..ad347997 100644
--- a/app/controllers/api/events_controller.rb
+++ b/app/controllers/api/events_controller.rb
@@ -2,8 +2,19 @@
module Api
class EventsController < ApplicationController
+
+ def show
+ event = Event.find(params[:id])
+
+ if event.present?
+ render status: 200, json: { data: event.as_json(include: %i[event_speakers speakers]) }
+ else
+ render status: 404, json: { data: 'No event found' }
+ end
+ end
+
def past
- events = Event.where('date < ?', DateTime.current).order(date: :desc)
+ events = Event.includes([:speakers]).by_event_date.where('date < ?', DateTime.current)
events_by_date =
events
.group_by { |event| event.date.year }
@@ -11,21 +22,21 @@ def past
events_by_year.group_by { |event| event.date.strftime('%B') } # group by month name
end
- render status: 200, json: { data: events_by_date.as_json(include: [:event_speakers, :speakers]) }
+ render status: 200, json: { data: events_by_date.as_json(include: %i[event_speakers speakers]) }
end
- def past_by_month
- event_date = DateTime.new(params[:year].to_i, params[:month].to_i)
- event = Event.where(date: event_date..event_date.end_of_month).first
- if event.present?
- render status: 200, json: { data: event.as_json(include: [:event_speakers, :speakers]) }
+ def by_month
+ events_by_month = Event.includes([:speakers]).by_event_date.by_month_year(params[:month].to_i, params[:year].to_i)
+
+ if events_by_month.present?
+ render status: 200, json: { data: events_by_month.as_json(include: %i[event_speakers speakers]) }
else
render status: 404, json: { data: 'No events found' }
end
end
def upcoming
- events = Event.where('date > ?', DateTime.current).order(date: :asc)
+ events = Event.includes([:speakers]).by_event_date.where('date > ?', DateTime.current)
events_by_date =
events
.group_by { |event| event.date.year }
diff --git a/app/controllers/site_controller.rb b/app/controllers/site_controller.rb
index fc479346..ec79f56b 100644
--- a/app/controllers/site_controller.rb
+++ b/app/controllers/site_controller.rb
@@ -12,6 +12,6 @@ def jobs; end
def jobs_authenticate; end
def donate; end
-
- def past_meetup; end
+
+ def meetup; end
end
diff --git a/app/javascript/components/PageTitleWithContainer.jsx b/app/javascript/components/PageTitleWithContainer.jsx
index 58edadc4..61f48495 100644
--- a/app/javascript/components/PageTitleWithContainer.jsx
+++ b/app/javascript/components/PageTitleWithContainer.jsx
@@ -4,7 +4,7 @@ import PageTitle from 'components/PageTitle';
const PageTitleWithContainer = ({ text }) => {
return (
-
+
);
diff --git a/app/javascript/components/layout/Footer.jsx b/app/javascript/components/layout/Footer.jsx
index e41b8389..92eb08de 100644
--- a/app/javascript/components/layout/Footer.jsx
+++ b/app/javascript/components/layout/Footer.jsx
@@ -50,7 +50,7 @@ const Footer = () => (
Job Board
- Past Meetups
+ Meetups
diff --git a/app/javascript/components/layout/Header.jsx b/app/javascript/components/layout/Header.jsx
index 6ff2a0ae..74e47daf 100644
--- a/app/javascript/components/layout/Header.jsx
+++ b/app/javascript/components/layout/Header.jsx
@@ -57,12 +57,13 @@ const Header = () => {
Job Board
-
- Past Meetups
-
+
Sponsor Us
+
+ Meetups
+
diff --git a/app/javascript/components/past_meetups/SpeakerBiosBlock.jsx b/app/javascript/components/meetup/SpeakerBiosBlock.jsx
similarity index 100%
rename from app/javascript/components/past_meetups/SpeakerBiosBlock.jsx
rename to app/javascript/components/meetup/SpeakerBiosBlock.jsx
diff --git a/app/javascript/components/past_meetups/SpeakerVideoBlock.jsx b/app/javascript/components/meetup/SpeakerVideoBlock.jsx
similarity index 94%
rename from app/javascript/components/past_meetups/SpeakerVideoBlock.jsx
rename to app/javascript/components/meetup/SpeakerVideoBlock.jsx
index 2a45dddf..79dd1c65 100644
--- a/app/javascript/components/past_meetups/SpeakerVideoBlock.jsx
+++ b/app/javascript/components/meetup/SpeakerVideoBlock.jsx
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
-import VideoBlock from 'components/past_meetups/VideoBlock';
+import VideoBlock from 'components/meetup/VideoBlock';
export default function SpeakerVideoBlock({ speaker, eventSpeaker }) {
const { id, image_url: imageUrl, name, tagline } = speaker;
diff --git a/app/javascript/components/past_meetups/VideoBlock.jsx b/app/javascript/components/meetup/VideoBlock.jsx
similarity index 100%
rename from app/javascript/components/past_meetups/VideoBlock.jsx
rename to app/javascript/components/meetup/VideoBlock.jsx
diff --git a/app/javascript/components/pages/PastMeetup.jsx b/app/javascript/components/pages/Meetup.jsx
similarity index 50%
rename from app/javascript/components/pages/PastMeetup.jsx
rename to app/javascript/components/pages/Meetup.jsx
index e207544f..b5afb573 100644
--- a/app/javascript/components/pages/PastMeetup.jsx
+++ b/app/javascript/components/pages/Meetup.jsx
@@ -1,95 +1,34 @@
import React, { useEffect, useState } from 'react';
import { Helmet } from 'react-helmet';
-import PropTypes from 'prop-types';
-import { getPastMeetup } from '../../datasources';
+import { getMeetup } from '../../datasources';
import SharedLayout from 'components/layout/SharedLayout';
-import PageTitleWithContainer from 'components/PageTitle';
-import SpeakersList from '../SpeakersList';
-import Microphone from '../icons/Microphone';
-import LoadingSpinner from 'components/LoadingSpinner';
-import 'stylesheets/page';
-import 'stylesheets/meetup';
-
-const VideoBlock = ({ videoUrl, title }) => {
- if (!videoUrl) {
- return null;
- }
- return (
-
- ;
-
- );
-};
-VideoBlock.propTypes = {
- videoUrl: PropTypes.string,
- title: PropTypes.string,
-};
-
-const SpeakerBiosBlock = ({ speakers }) => {
- return (
-
-
-
-
About the speakers
-
-
- {speakers?.map(({ id, bio }) => (
-
{bio}
- ))}
-
-
- );
-};
+import PageTitleWithContainer from 'components/PageTitleWithContainer';
+import SpeakersList from 'components/SpeakersList';
+import LoadingSpinner from 'components/LoadingSpinner';
-SpeakerBiosBlock.propTypes = {
- speakers: PropTypes.arrayOf(PropTypes.object),
-};
+import SpeakerVideoBlock from 'components/meetup/SpeakerVideoBlock';
+import SpeakerBiosBlock from 'components/meetup/SpeakerBiosBlock';
+import VideoBlock from 'components/meetup/VideoBlock';
-const SpeakerVideoBlock = ({ speaker, eventSpeaker }) => {
- const { id, image_url: imageUrl, name, tagline } = speaker;
- const {
- talk_title: title,
- talk_description: description,
- talk_video_link: videoLink,
- } = eventSpeaker;
- return (
- <>
-
-
-
-
-
-
{description}
- >
- );
-};
+import 'stylesheets/page';
+import 'stylesheets/meetup';
-SpeakerVideoBlock.propTypes = {
- speaker: PropTypes.object,
- eventSpeaker: PropTypes.object,
-};
+const Meetup = () => {
+ const id_event = window.id_event;
-const PastMeetup = () => {
- const year = window.year;
- const month = window.month;
- const eventDate = new Date(year, Number(month - 1));
- const monthName = eventDate.toLocaleDateString('en-US', { month: 'long' });
const [loading, setLoading] = useState(true);
const [meetup, setMeetup] = useState({});
useEffect(() => {
const fetchData = async () => {
- const data = await getPastMeetup(year, month);
+ const data = await getMeetup(id_event);
setMeetup(data);
setLoading(false);
};
fetchData();
- }, [year, month]);
+ }, [id_event]);
const {
title,
@@ -98,15 +37,14 @@ const PastMeetup = () => {
event_speakers: eventSpeakers,
panel_video_link: panelVideoUrl,
} = meetup;
+
return (
<>
{`${title} | WNB.rb`}
-
+
{loading ? (
@@ -137,7 +75,7 @@ const PastMeetup = () => {
})}
>
)}
-
@@ -148,4 +86,4 @@ const PastMeetup = () => {
);
};
-export default PastMeetup;
+export default Meetup;
diff --git a/app/javascript/components/pages/Meetups.jsx b/app/javascript/components/pages/Meetups.jsx
index 03b48f6d..b967dc01 100644
--- a/app/javascript/components/pages/Meetups.jsx
+++ b/app/javascript/components/pages/Meetups.jsx
@@ -1,97 +1,17 @@
import React, { useEffect, useState } from 'react';
import { Helmet } from 'react-helmet';
-import PropTypes from 'prop-types';
+
import { getPastMeetups } from '../../datasources';
import SharedLayout from 'components/layout/SharedLayout';
import LoadingSpinner from 'components/LoadingSpinner';
import PageTitleWithContainer from 'components/PageTitleWithContainer';
-import 'stylesheets/page';
-import 'stylesheets/meetup';
-
-const YearSection = ({ children, year }) => (
-
-);
-YearSection.propTypes = {
- year: PropTypes.string,
- children: PropTypes.node,
-};
-
-const MonthSection = ({ children, month }) => (
-
-
-
- {month}
-
-
-
-
-
-);
-
-MonthSection.propTypes = {
- month: PropTypes.string,
- children: PropTypes.node,
-};
+import MonthSection from 'components/meetups/MonthSection';
+import YearSection from 'components/meetups/YearSection';
+import MeetupCard from 'components/meetups/MeetupCard';
-const Meetup = ({ speakers, title = '', event_speakers, year, month }) => {
- const eventWithSpeaker = event_speakers.map((talk) => {
- const speaker = speakers.find((speak) => speak.id === talk.speaker_id);
- return { ...talk, speaker };
- });
- return (
-
-
-
-
{title}
- {eventWithSpeaker.length > 0 &&
- eventWithSpeaker.map(({ id, talk_title, speaker }) => (
-
-
- {talk_title}
-
-
-
-
-
- {speaker.name}
-
-
- {speaker.tagline}
-
-
-
-
- ))}
-
-
-
-
- );
-};
-
-Meetup.propTypes = {
- speakers: PropTypes.array,
- title: PropTypes.string,
- event_speakers: PropTypes.array,
- year: PropTypes.string,
- month: PropTypes.string,
-};
+import 'stylesheets/page';
+import 'stylesheets/meetup';
const Meetups = () => {
const [loading, setLoading] = useState(true);
@@ -109,10 +29,10 @@ const Meetups = () => {
return (
<>
- Past Meetups | WNB.rb
+ Meetups | WNB.rb
-
+
{loading ? (
) : (
@@ -136,8 +56,9 @@ const Meetups = () => {
const numericMonth =
new Date(date).getMonth() + 1;
return (
- {
+ const year = window.year;
+ const month = window.month;
+ const eventDate = new Date(year, Number(month - 1));
+ const monthName = eventDate.toLocaleDateString('en-US', { month: 'long' });
+ const [loading, setLoading] = useState(true);
+ const [meetupsList, setMeetupsList] = useState({});
+ useEffect(() => {
+ const fetchData = async () => {
+ const data = await getMonthlyMeetups(year, month);
+ setMeetupsList(Object.entries(data));
+ setLoading(false);
+ };
+
+ fetchData();
+ }, [year, month]);
+ window.meetupsList = meetupsList;
+
+ return (
+ <>
+
+ {`${year}-${month} Meetups | WNB.rb`}
+
+
+
+ {loading ? (
+
+ ) : (
+
+ {meetupsList.length > 0 ? (
+ meetupsList.reverse().map((meetup) => {
+ return (
+ <>
+
+ >
+ );
+ })
+ ) : (
+ <>
+
+ No Meetups for this month
+
+ >
+ )}
+
+ )}
+
+ >
+ );
+};
+
+export default MonthlyMeetups;
diff --git a/app/javascript/datasources/index.js b/app/javascript/datasources/index.js
index a307606d..5f3c1239 100644
--- a/app/javascript/datasources/index.js
+++ b/app/javascript/datasources/index.js
@@ -75,8 +75,14 @@ const productionDonationAmounts = [
{ value: 1000, link: 'https://buy.stripe.com/8wMeVAbl710mdEY009' },
];
-export const getPastMeetup = async (year, month) => {
+export const getMonthlyMeetups = async (year, month) => {
const result = await fetch(`${API_ROOT}/events/${year}/${month}`);
const json = await result.json();
return json.data;
};
+
+export const getMeetup = async (id) => {
+ const result = await fetch(`${API_ROOT}/events/${id}`);
+ const json = await result.json();
+ return json.data;
+};
diff --git a/app/javascript/packs/past_meetup.jsx b/app/javascript/packs/meetup.jsx
similarity index 71%
rename from app/javascript/packs/past_meetup.jsx
rename to app/javascript/packs/meetup.jsx
index 2a2e16e5..4ebf580a 100644
--- a/app/javascript/packs/past_meetup.jsx
+++ b/app/javascript/packs/meetup.jsx
@@ -1,10 +1,10 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
-import PastMeetup from '../components/pages/PastMeetup';
+import Meetup from '../components/pages/Meetup';
document.addEventListener('DOMContentLoaded', () => {
const container = document.getElementById('root');
const root = createRoot(container);
- root.render();
+ root.render();
});
diff --git a/app/javascript/packs/monthly_meetups.jsx b/app/javascript/packs/monthly_meetups.jsx
new file mode 100644
index 00000000..4d09fc5d
--- /dev/null
+++ b/app/javascript/packs/monthly_meetups.jsx
@@ -0,0 +1,11 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+
+import MonthlyMeetups from '../components/pages/MonthlyMeetups';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const body = document.createElement('div');
+ body.style = 'min-height: 100vh';
+
+ ReactDOM.render(, document.body.appendChild(body));
+});
diff --git a/app/javascript/stylesheets/header.scss b/app/javascript/stylesheets/header.scss
index ea015f7b..5dca4f86 100644
--- a/app/javascript/stylesheets/header.scss
+++ b/app/javascript/stylesheets/header.scss
@@ -43,6 +43,12 @@ header {
}
}
+ &.highlight {
+ a {
+ color: #f56960
+ }
+ }
+
&.is-active {
a {
@apply text-gray-500 font-bold md:font-normal;
diff --git a/app/javascript/stylesheets/meetup.scss b/app/javascript/stylesheets/meetup.scss
index 3581a0b8..0516226b 100644
--- a/app/javascript/stylesheets/meetup.scss
+++ b/app/javascript/stylesheets/meetup.scss
@@ -1,35 +1,39 @@
.meetups__month--name {
- width: 128px;
+ width: 128px;
}
.meetups__month:first-of-type > .meetups__card--border {
- margin-top: 80px;
+ margin-top: 80px;
}
.meetups__month:last-of-type > .meetups__card--border {
- align-self: flex-start;
- padding-top: 50px;
- line-height: 0;
+ align-self: flex-start;
+ padding-top: 50px;
+ line-height: 0;
}
.meetups__month:first-of-type > .meetups__month--name,
.meetups__month:last-of-type > .meetups__month--name {
- margin-top: 50px;
+ margin-top: 50px;
}
.meetups__card--border::before {
- display: block;
- position: relative;
- left: -10px;
- font-size: 50px;
- color: #d1d5db;
- content: '•';
+ display: block;
+ position: relative;
+ left: -10px;
+ font-size: 50px;
+ color: #d1d5db;
+ content: '•';
+}
+
+.meetup__item li div:first-of-type {
+ max-width: 100%;
}
.meetups__month:first-of-type > .meetups__card--border::before {
- top: -28px;
+ top: -28px;
}
.meetups__month:last-of-type > .meetups__card--border::before {
- top: 14px;
+ top: 14px;
}
diff --git a/app/javascript/stylesheets/page-title.scss b/app/javascript/stylesheets/page-title.scss
index c81cfc90..2c657dc4 100644
--- a/app/javascript/stylesheets/page-title.scss
+++ b/app/javascript/stylesheets/page-title.scss
@@ -1,7 +1,6 @@
.page-title {
@apply relative
min-h-[10rem];
- /* overflow-auto; */
}
.ruby-outline {
@@ -9,15 +8,14 @@
}
.title-container {
- @apply pt-10 pl-20;
+ @apply pt-10 pl-10 md:pl-20;
}
.title-text {
@apply text-wnbrb-pink-default
text-5xl
sm:text-6xl
- font-black
- md:whitespace-nowrap;
+ font-black;
-webkit-text-stroke-width: 2px;
-webkit-text-stroke-color: white;
diff --git a/app/models/event.rb b/app/models/event.rb
index ee29f3a4..63a18902 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -5,4 +5,13 @@ class Event < ApplicationRecord
has_many :speakers, through: :event_speakers
validates :title, :location, :date, presence: true
+
+ scope :by_event_date, -> { order(date: :desc) }
+
+ scope :by_month_year,
+ ->(month, year) {
+ first_day = Date.new(year, month, 1)
+ last_day = Date.new(year, month, 1).next_month - 1.second
+ where(date: first_day..last_day)
+ }
end
diff --git a/app/views/site/meetup.html.erb b/app/views/site/meetup.html.erb
new file mode 100644
index 00000000..d3747c9b
--- /dev/null
+++ b/app/views/site/meetup.html.erb
@@ -0,0 +1,6 @@
+<%= javascript_pack_tag 'meetup' %>
+<%= stylesheet_pack_tag 'meetup' %>
+
+<%= javascript_tag do %>
+ var id_event = <%= raw(params[:id]) %>
+<% end %>
diff --git a/app/views/site/past_meetup.erb b/app/views/site/monthly_meetups.html.erb
similarity index 51%
rename from app/views/site/past_meetup.erb
rename to app/views/site/monthly_meetups.html.erb
index ab07506e..bb3e37d3 100644
--- a/app/views/site/past_meetup.erb
+++ b/app/views/site/monthly_meetups.html.erb
@@ -1,6 +1,6 @@
-<%= javascript_pack_tag 'past_meetup' %>
-<%= stylesheet_pack_tag "past_meetup" %>
+<%= javascript_pack_tag 'monthly_meetups' %>
+<%= stylesheet_pack_tag 'monthly_meetups' %>
<%= javascript_tag do %>
var year = <%= raw(params[:year]) %>
var month = <%= raw(params[:month]) %>
-<% end %>
\ No newline at end of file
+<% end %>
diff --git a/config/routes.rb b/config/routes.rb
index 8870ffda..d5f85ba1 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -8,22 +8,26 @@
sign_up: 'new',
registration: 'register',
}
+
namespace :admin do
get 'dashboard', to: 'dashboard#show'
end
+
get '/sponsor-us', to: 'site#sponsor_us'
get '/meetups', to: 'site#meetups'
+ get '/meetups/:id', to: 'site#meetup'
+
get '/jobs', to: 'site#jobs'
get '/jobs/authenticate', to: 'site#jobs_authenticate'
get '/donate', to: 'site#donate'
- get '/meetups/:year/:month', to: 'site#past_meetup'
root 'site#home'
+
namespace :api do
- resources :events, only: [:none] do
+ resources :events, only: [:show] do
collection do
get 'past'
- get '/:year/:month', to: 'events#past_by_month'
+ get '/:year/:month', to: 'events#by_month'
get 'upcoming'
end
end
diff --git a/spec/controllers/api/events_controller_spec.rb b/spec/controllers/api/events_controller_spec.rb
index 53239dfd..059a1e79 100644
--- a/spec/controllers/api/events_controller_spec.rb
+++ b/spec/controllers/api/events_controller_spec.rb
@@ -87,20 +87,20 @@
end
end
- describe 'GET #past_by_month' do
+ describe 'GET #by_month' do
before do
Meetup.create(title: 'August event', location: 'virtual', date: DateTime.new(2021, 8, 1))
end
it 'returns one event for a given month' do
- get :past_by_month, params: { year: '2021', month: '8' }
+ get :by_month, params: { year: '2021', month: '8' }
expect(response).to have_http_status(200)
body = JSON.parse(response.body)
- expect(body['data']['title']).to eq('August event')
+ expect(body['data'][0]['title']).to eq('August event')
end
it 'returns a 404 when no event exists for that month' do
- get :past_by_month, params: { year: '2021', month: '11' }
+ get :by_month, params: { year: '2021', month: '11' }
expect(response).to have_http_status(404)
end
end