-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
406 additions
and
3 deletions.
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
class BlogPostsController < ApplicationController | ||
before_action :set_blog_post, only: %i[show edit update destroy] | ||
before_action :require_admin!, only: %i[new edit create update destroy] | ||
# before_action :authenticate_user!, only: %i[new create edit update destroy] | ||
|
||
# GET /blog_posts | ||
def index | ||
@blog_posts = BlogPost.where(draft: false).order(created_at: :asc) | ||
@drafts = BlogPost.where(draft: true).order(created_at: :desc) | ||
end | ||
|
||
# GET /blog_posts/slug | ||
def show | ||
end | ||
|
||
# GET /blog_posts/new | ||
def new | ||
@blog_post = BlogPost.new | ||
|
||
end | ||
|
||
# GET /blog_posts/slug/edit | ||
def edit | ||
end | ||
|
||
# POST /blog_posts | ||
def create | ||
@blog_post = BlogPost.new(blog_post_params) | ||
|
||
if @blog_post.save | ||
redirect_to blog_post_path(@blog_post.slug), notice: "Blog post was successfully created." | ||
else | ||
render :new, status: :unprocessable_entity | ||
end | ||
end | ||
|
||
# PATCH/PUT /blog_posts/slug | ||
def update | ||
@blog_post.slug = params[:blog_post][:slug] | ||
if @blog_post.save && @blog_post.update(blog_post_params) | ||
redirect_to blog_post_path(@blog_post.slug), notice: "Blog post was successfully updated." | ||
else | ||
render :edit, status: :unprocessable_entity | ||
end | ||
end | ||
|
||
# DELETE /blog_posts/1 | ||
def destroy | ||
@blog_post.destroy | ||
redirect_to blog_posts_url, notice: "Blog post was successfully destroyed." | ||
end | ||
|
||
private | ||
|
||
# Use callbacks to share common setup or constraints between actions. | ||
def set_blog_post | ||
slug = params[:blog_post].present? ? params[:blog_post][:slug] : params[:slug] | ||
@blog_post = BlogPost.find_by!(slug: slug) | ||
end | ||
|
||
# Only allow a list of trusted parameters through, but add :body, and use slug instead of id in the URL. | ||
def blog_post_params | ||
params.require(:blog_post).permit(:title, :slug, :description, :body, :cover_image, :draft) | ||
end | ||
|
||
def require_admin! | ||
unless current_user&.admin? | ||
redirect_to root_path, alert: "You are not authorized to view this page." | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
class BlogPost < ApplicationRecord | ||
has_one_attached :cover_image | ||
has_rich_text :body | ||
validates :slug, uniqueness: true | ||
before_validation :generate_unique_slug | ||
validates_presence_of :title, :slug, :body, :description | ||
|
||
def to_param | ||
slug | ||
end | ||
|
||
private | ||
|
||
def generate_unique_slug | ||
if new_record? || slug_changed? | ||
base_slug = slug.blank? ? title.parameterize : slug | ||
other = self.class.where("slug LIKE ?", "#{base_slug}%") | ||
self.slug = if other.exists? | ||
"#{base_slug}-#{other.count + 1}" | ||
else | ||
base_slug | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<div id="<%= dom_id blog_post %>"> | ||
<article class="flex flex-col items-start justify-between hover:opacity-80 transition-opacity duration-300 ease-in-out" > | ||
|
||
<%= link_to blog_post_path(blog_post) do %> | ||
|
||
<div class="relative w-full"> | ||
<%= image_tag blog_post.cover_image, class:"aspect-[16/9] w-full rounded-2xl bg-gray-100 object-cover" if blog_post.cover_image.attached? %> | ||
<div class="absolute inset-0 rounded-2xl ring-1 ring-inset ring-gray-900/10"></div> | ||
</div> | ||
<div class="w-full"> | ||
<div class="text-xs -mt-8 ml-auto text-right w-full pr-2.5"> | ||
<time class="relative z-10 rounded-full bg-gray-50 px-3 py-1.5 font-medium text-gray-600 border" datetime="<%= blog_post.updated_at.strftime('%m-%d-%Y') %>" class="text-gray-500"><%= blog_post.created_at.strftime('%B %d, %Y') %></time> | ||
</div> | ||
<div class="group relative"> | ||
<h2 class="mt-9 text-lg font-semibold leading-6 text-gray-900 group-hover:text-gray-600"> | ||
<%= blog_post.title %> | ||
</h2> | ||
<p class="mt-3 line-clamp-3 text-sm leading-6 text-gray-600"><%= blog_post.description %></p> | ||
</div> | ||
|
||
<% end %> | ||
</article> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
<%= form_with(model: blog_post) do |form| %> | ||
<% if blog_post.errors.any? %> | ||
<div class="bg-red-100 text-red-700 p-4 rounded mb-4"> | ||
<h2><%= pluralize(blog_post.errors.count, "error") %> prohibited this blog_post from being saved:</h2> | ||
|
||
<ul> | ||
<% blog_post.errors.each do |error| %> | ||
<li><%= error.full_message %></li> | ||
<% end %> | ||
</ul> | ||
</div> | ||
<% end %> | ||
<div class="flex flex-col space-y-6"> | ||
<% unless turbo_native_app? %> | ||
<% end %> | ||
<div class="bg-white border shadow px-4 py-5 lg:rounded-lg sm:p-6"> | ||
<div> | ||
|
||
<div class="mt-5 md:mt-0 md:col-span-2"> | ||
<div class="space-y-6"> | ||
<div class="sm:col-span-3"> | ||
<%= form.label :slug, class: "block text-sm font-medium text-gray-700" %> | ||
<div class="mt-1 flex rounded-md shadow-sm"> | ||
<span class="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm"> | ||
https://yt.careers/blog/ | ||
</span> | ||
<%= form.text_field :slug, required: true, class: "flex-1 block w-full min-w-0 rounded-none rounded-r-md sm:text-sm border-gray-300 focus:border-primary-500 focus:ring focus:ring-primary-500 focus:ring-opacity-50", placeholder: "how-to-grow-a-saas" %> | ||
</div> | ||
</div> | ||
|
||
<%# checkbox to consider this post a draft %> | ||
<div class="sm:col-span-3"> | ||
<div class="flex items-start"> | ||
<div class="flex items-center h-5"> | ||
<%= form.check_box :draft, class: "focus:ring-primary-500 h-4 w-4 text-primary-600 border-gray-300 rounded" %> | ||
</div> | ||
<div class="ml-3 text-sm"> | ||
<%= form.label :draft, class: "font-medium text-gray-700" %> | ||
<p class="text-gray-500">Drafts are not visible to the public</p> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div class="sm:col-span-3"> | ||
<%= form.label :title, class: "block text-sm font-medium text-gray-700" %> | ||
<%= form.text_field :title, required: true, class: "mt-1 focus:border-primary-500 focus:ring focus:ring-primary-500 focus:ring-opacity-50 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md", placeholder: "Title of your blog post. (65 characters max)", maxlength: 65 %> | ||
</div> | ||
|
||
<div class="sm:col-span-3"> | ||
<%= form.label :description, class: "block text-sm font-medium text-gray-700" %> | ||
<%= form.text_area :description, required: true, rows: 2 , class: "mt-1 focus:border-primary-500 focus:ring focus:ring-primary-500 focus:ring-opacity-50 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md", placeholder: "A snippet of text designed to summarise a page. Though not critically important, an ideal length would be 70-155 characters.", maxlength: 155 %> | ||
</div> | ||
|
||
<div class="col-span-full"> | ||
<label for="cover_image" class="block text-sm font-medium leading-6 text-gray-900">Cover photo</label> | ||
<div id="drop_zone" class="mt-2 flex justify-center rounded-lg border border-dashed border-gray-900/25 px-6 py-10"> | ||
<div class="text-center"> | ||
<%= image_tag @blog_post.cover_image, id: 'cover_image_preview', data: { url: url_for(@blog_post.cover_image) } if @blog_post.cover_image.attached? %> | ||
<img id="cover_image_preview" src="#" alt="Cover Image Preview" class="hidden"/> | ||
<div class="mt-4 flex text-sm leading-6 text-gray-600 justify-center"> | ||
<label for="cover_image" class="relative cursor-pointer rounded-md bg-white font-semibold text-gray-600 focus-within:outline-none focus-within:ring-2 focus-within:ring-gray-600 focus-within:ring-offset-2 hover:text-gray-500"> | ||
<span>Upload a file</span> | ||
<%= form.file_field :cover_image, id: 'cover_image', class: 'sr-only' %> | ||
</label> | ||
<p class="pl-1">or drag and drop</p> | ||
</div> | ||
<p class="text-xs leading-5 text-gray-600">PNG, JPG, WEBP up to 1MB (1200 × 630 px recommended)</p> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div class="field relative border rounded-md mt-2 p-2 min-h-[175px] border-gray-300 group"> | ||
<%= form.rich_text_area :body, placeholder: "Write your blog post...", required: true, class:"group-focus:ring-gray-500 group-focus:border-gray-500" %> | ||
</div> | ||
|
||
<div class="flex justify-center items-center mt-4"> | ||
<%= form.submit "Save", class:"group relative w-full flex justify-center py-2.5 px-4 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none cursor-pointer"%> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<% end %> | ||
|
||
<script> | ||
// Preview the cover image | ||
document.getElementById('cover_image').addEventListener('change', function(e) { | ||
var file = e.target.files[0]; | ||
var reader = new FileReader(); | ||
|
||
reader.onloadend = function() { | ||
document.getElementById('cover_image_preview').src = reader.result; | ||
document.getElementById('cover_image_preview').style.display = 'block'; | ||
} | ||
|
||
if (file) { | ||
reader.readAsDataURL(file); | ||
} else { | ||
document.getElementById('cover_image_preview').src = ""; | ||
} | ||
}); | ||
|
||
document.addEventListener('DOMContentLoaded', function() { | ||
var coverImagePreview = document.getElementById('cover_image_preview'); | ||
if (coverImagePreview) { | ||
coverImagePreview.src = coverImagePreview.dataset.url; | ||
coverImagePreview.style.display = 'opacity-100'; | ||
} | ||
}); | ||
|
||
var dropZone = document.getElementById('drop_zone'); | ||
var fileInput = document.getElementById('cover_image'); | ||
|
||
dropZone.addEventListener('dragover', function(e) { | ||
e.preventDefault(); | ||
dropZone.classList.add('bg-gray-100'); | ||
}); | ||
|
||
dropZone.addEventListener('dragleave', function(e) { | ||
e.preventDefault(); | ||
dropZone.classList.remove('bg-gray-100'); | ||
}); | ||
|
||
dropZone.addEventListener('drop', function(e) { | ||
e.preventDefault(); | ||
dropZone.classList.remove('bg-gray-100'); | ||
|
||
var files = e.dataTransfer.files; | ||
if (files.length > 0) { | ||
fileInput.files = files; | ||
// Trigger the 'change' event to update the preview | ||
var event = new Event('change'); | ||
fileInput.dispatchEvent(event); | ||
} | ||
}); | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<% meta title: "Edit blog post", description: "Edit blog post." %> | ||
|
||
<h1 class="text-xl text-center font-bold leading-tight text-gray-900 mx-4 lg:mx-0 mt-8 lg:mt-16">Edit blog post</h1> | ||
|
||
<div class="flex justify-center flex-col space-y-3 mt-4 text-center"> | ||
<%= link_to "Show blog post", blog_post_path(@blog_post.slug), class:"mx-auto w-fit shrink-0 mt-4 md:mt-0 inline-flex items-center justify-center sm:justify-start px-4 py-2 border border-gray-300 shadow-sm text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500" %> | ||
<%= link_to "Back to blog posts", blog_posts_path, class:"mx-auto w-fit shrink-0 mt-4 md:mt-0 inline-flex items-center justify-center sm:justify-start px-4 py-2 border border-gray-300 shadow-sm text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500" %> | ||
</div> | ||
|
||
<div class="max-w-2xl mx-auto mt-8"> | ||
<%= render "form", blog_post: @blog_post %> | ||
</div> | ||
|
||
<div class="flex justify-center flex-col space-y-3 mt-4 mb-8 text-center"> | ||
<%= link_to "Show blog post", blog_post_path(@blog_post.slug), class:"mx-auto w-fit shrink-0 mt-4 md:mt-0 inline-flex items-center justify-center sm:justify-start px-4 py-2 border border-gray-300 shadow-sm text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500" %> | ||
<%= link_to "Back to blog posts", blog_posts_path, class:"mx-auto w-fit shrink-0 mt-4 md:mt-0 inline-flex items-center justify-center sm:justify-start px-4 py-2 border border-gray-300 shadow-sm text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500" %> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<% meta title: "Blog posts", description: "All blog posts from #{ENV['COMPANY_NAME']}." %> | ||
<%# Title %> | ||
<div class="mt-16 mx-auto max-w-3xl px-4 sm:mt-24"> | ||
<div class="text-center"> | ||
<h1 class="text-2xl tracking-tight font-extrabold sm:text-3xl md:text-4xl font-display text-gray-800"> | ||
<span class="block">Blog posts</span> | ||
</h1> | ||
<p class="mt-3 max-w-md mx-auto text-base text-gray-500 sm:text-lg md:mt-5 md:text-xl md:max-w-3xl"> | ||
All <%= ENV['COMPANY_NAME'] %> blog posts. | ||
</p> | ||
</div> | ||
</div> | ||
|
||
<% if current_user&.admin? %> | ||
<%# New blog post button, only accessibly by admins %> | ||
<div class="mt-8 mx-auto max-w-fit"> | ||
<div class="group relative w-full flex justify-center py-2.5 px-4 border border-transparent text-sm leading-5 font-medium rounded-md text-white bg-primary-600 hover:bg-primary-700 focus:outline-none"> | ||
<div class="text-base flex justify-center items-center"> | ||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 -ml-1 mr-2" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"> | ||
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z" clip-rule="evenodd"></path> | ||
</svg> | ||
<%= link_to new_blog_post_path, class: "focus:outline-none" do %> | ||
<span class="absolute inset-0" aria-hidden="true"></span> | ||
New blog post | ||
<% end %> | ||
</div> | ||
</div> | ||
</div> | ||
<% end %> | ||
|
||
<div class="font-inter antialiased text-gray-800 tracking-tight my-16"> | ||
<main class="grow"> | ||
<section> | ||
<div class="mx-auto max-w-7xl px-6 lg:px-8"> | ||
<div class="mx-auto mt-16 grid max-w-2xl grid-cols-1 gap-x-8 gap-y-20 lg:mx-0 lg:max-w-none lg:grid-cols-3"> | ||
<%# Blog post list %> | ||
<% @blog_posts.each do |blog_post| %> | ||
<%= render blog_post %> | ||
<% end %> | ||
</div> | ||
</div> | ||
|
||
<% if current_user && current_user.admin? && @drafts.any? %> | ||
<div class="mx-auto max-w-7xl px-6 lg:px-8 mt-16 border border-dashed bg-white py-12"> | ||
<p class="text-2xl tracking-tight font-extrabold sm:text-2xl md:text-3xl font-display text-gray-800"> | ||
Drafts | ||
</p> | ||
<div class="mx-auto mt-8 grid max-w-2xl grid-cols-1 gap-x-8 gap-y-20 lg:mx-0 lg:max-w-none lg:grid-cols-3"> | ||
<%# Drafts %> | ||
<% @drafts.each do |blog_post| %> | ||
<%= render blog_post %> | ||
<% end %> | ||
</div> | ||
</div> | ||
<% end %> | ||
</section> | ||
</main> | ||
|
||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<% meta title: "Create a new blog post", description: "New blog post for #{ENV['COMPANY_NAME']}." %> | ||
|
||
<h1 class="text-xl text-center font-bold leading-tight text-gray-900 mx-4 lg:mx-0 mt-8 lg:mt-16">New blog post</h1> | ||
|
||
<div class="flex justify-center flex-col space-y-3 mt-4 text-center"> | ||
<%= link_to "Back to blog posts", blog_posts_path, class:"mx-auto w-fit shrink-0 mt-4 md:mt-0 inline-flex items-center justify-center sm:justify-start px-4 py-2 border border-gray-300 shadow-sm text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500" %> | ||
</div> | ||
|
||
<div class="max-w-2xl mx-auto mt-8"> | ||
<%= render "form", blog_post: @blog_post %> | ||
</div> | ||
|
||
<div class="flex justify-center flex-col space-y-3 mt-4 mb-8 text-center"> | ||
<%= link_to "Back to blog posts", blog_posts_path, class:"mx-auto w-fit shrink-0 mt-4 md:mt-0 inline-flex items-center justify-center sm:justify-start px-4 py-2 border border-gray-300 shadow-sm text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500" %> | ||
</div> |
Oops, something went wrong.