mirror of
https://github.com/SrIzan10/hc-harbor.git
synced 2026-05-01 10:45:21 +00:00
Add neighborhood post record updating by admins
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
class Admin::PostReviewsController < Admin::BaseController
|
||||
include ApplicationHelper # For short_time_simple, short_time_detailed helpers
|
||||
|
||||
before_action :set_post, only: [ :show ]
|
||||
before_action :set_post, only: [ :show, :update ]
|
||||
|
||||
def show
|
||||
# User related to the post
|
||||
@@ -18,7 +18,6 @@ class Admin::PostReviewsController < Admin::BaseController
|
||||
post_end_str = @post.airtable_fields["createdAt"]
|
||||
@total_post_hackatime_seconds = @post.airtable_fields["hackatimeTime"]&.to_i || 0
|
||||
|
||||
|
||||
if post_start_str.blank? || post_end_str.blank?
|
||||
raise ArgumentError, "Post start or end date is missing from Airtable. lastPost: '#{post_start_str}', createdAt: '#{post_end_str}'"
|
||||
end
|
||||
@@ -69,6 +68,10 @@ class Admin::PostReviewsController < Admin::BaseController
|
||||
all_heartbeats_for_user_in_review_window = all_heartbeats_for_user_in_review_window.select do |hb|
|
||||
selected_projects.include?(hb.project)
|
||||
end
|
||||
else
|
||||
all_heartbeats_for_user_in_review_window = all_heartbeats_for_user_in_review_window.select do |hb|
|
||||
@recommended_project_names.include?(hb.project)
|
||||
end
|
||||
end
|
||||
|
||||
@detailed_spans = []
|
||||
@@ -123,6 +126,29 @@ class Admin::PostReviewsController < Admin::BaseController
|
||||
@current_user_timezone = current_user.timezone
|
||||
end
|
||||
|
||||
def update
|
||||
approved_seconds = params[:approved_seconds].to_i
|
||||
comment = params[:comment]
|
||||
|
||||
fields = {
|
||||
"approved_time" => approved_seconds,
|
||||
"review_comment" => comment,
|
||||
"review_status" => "approved"
|
||||
}
|
||||
|
||||
if @post.push_to_airtable!(fields)
|
||||
flash[:notice] = "Time approved successfully"
|
||||
redirect_to admin_ysws_review_path(@post.app.ysws_submission.airtable_id)
|
||||
else
|
||||
flash[:alert] = "Failed to approve time"
|
||||
redirect_to admin_post_review_path(@post.airtable_id)
|
||||
end
|
||||
|
||||
rescue => e
|
||||
flash[:alert] = "Failed to approve time: #{e.message}"
|
||||
redirect_to admin_post_review_path(@post.airtable_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_post
|
||||
|
||||
@@ -16,4 +16,9 @@ class Neighborhood::App < ApplicationRecord
|
||||
return [] unless airtable_fields["hackatimeProjects"]&.any?
|
||||
Neighborhood::Project.where(airtable_id: airtable_fields["hackatimeProjects"])
|
||||
end
|
||||
|
||||
def ysws_submission
|
||||
return nil unless airtable_fields["YSWS Project Submission"]&.first
|
||||
Neighborhood::YswsSubmission.find_by(airtable_id: airtable_fields["YSWS Project Submission"].first)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,12 +3,32 @@ class Neighborhood::Post < ApplicationRecord
|
||||
|
||||
include HasTableSync
|
||||
|
||||
has_table_sync base: "appnsN4MzbnfMY0ai",
|
||||
table: "tbl0iKxglbySiEbB4",
|
||||
BASE_ID = "appnsN4MzbnfMY0ai"
|
||||
TABLE_ID = "tbl0iKxglbySiEbB4"
|
||||
|
||||
has_table_sync base: BASE_ID,
|
||||
table: TABLE_ID,
|
||||
pat: ENV["NEIGHBORHOOD_AIRTABLE_PAT"]
|
||||
|
||||
def app
|
||||
return nil unless airtable_fields["app"]&.first
|
||||
Neighborhood::App.find_by(airtable_id: airtable_fields["app"].first)
|
||||
end
|
||||
|
||||
def push_to_airtable!(fields)
|
||||
response = HTTP.patch(
|
||||
"https://api.airtable.com/v0/#{BASE_ID}/#{TABLE_ID}/#{airtable_id}",
|
||||
headers: {
|
||||
"Authorization" => "Bearer #{ENV["NEIGHBORHOOD_AIRTABLE_PAT"]}",
|
||||
"Content-Type" => "application/json"
|
||||
},
|
||||
json: { fields: fields }
|
||||
)
|
||||
new_fields = JSON.parse(response.body)["fields"]
|
||||
if response.status.success?
|
||||
update(airtable_fields: new_fields)
|
||||
else
|
||||
raise "Failed to push to Airtable: #{response.body}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
36
app/views/admin/post_reviews/_time_approval_form.html.erb
Normal file
36
app/views/admin/post_reviews/_time_approval_form.html.erb
Normal file
@@ -0,0 +1,36 @@
|
||||
<%# Time Approval Form %>
|
||||
<div id="time-approval-form" class="time-approval-form" style="position: fixed; bottom: 100px; right: 20px; background-color: rgba(30, 41, 59, 0.9); color: #E2E8F0; padding: 0.75rem 1rem; border-radius: 0.375rem; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); z-index: 1000; font-size: 0.9rem; min-width: 180px; text-align: center;">
|
||||
<%= form_with url: admin_post_review_path(post_id: @post.airtable_id), method: :patch, class: "approval-form", data: { turbo: true, turbo_method: :patch } do |f| %>
|
||||
<div style="margin-bottom: 0.75rem;">
|
||||
<%= f.text_area :comment, placeholder: "Optional comment...", class: "w-full p-2 rounded bg-slate-700 text-slate-200", style: "width: 100%; margin-bottom: 0.5rem; background-color: #1E293B; color: #E2E8F0; border: 1px solid #475569; border-radius: 0.25rem; padding: 0.5rem;" %>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 0.5rem; justify-content: center;">
|
||||
<%= f.hidden_field :approved_seconds, id: "approved-seconds" %>
|
||||
<%= f.submit "Approve All Time", class: "approve-all-btn", style: "background-color: #059669; color: white; padding: 0.5rem 1rem; border-radius: 0.25rem; border: none; cursor: pointer; font-size: 0.875rem;" %>
|
||||
<%= f.submit "Approve Selected", class: "approve-selected-btn", style: "background-color: #2563EB; color: white; padding: 0.5rem 1rem; border-radius: 0.25rem; border: none; cursor: pointer; font-size: 0.875rem;" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.addEventListener('turbo:load', function() {
|
||||
const form = document.querySelector('.approval-form');
|
||||
const approvedSecondsInput = document.getElementById('approved-seconds');
|
||||
const approveAllBtn = document.querySelector('.approve-all-btn');
|
||||
const approveSelectedBtn = document.querySelector('.approve-selected-btn');
|
||||
|
||||
// Update approved seconds when form is submitted
|
||||
form.addEventListener('submit', function(e) {
|
||||
if (e.submitter === approveAllBtn) {
|
||||
approvedSecondsInput.value = <%= @total_post_hackatime_seconds %>;
|
||||
} else if (e.submitter === approveSelectedBtn) {
|
||||
let selectedSeconds = 0;
|
||||
document.querySelectorAll('.span-selector-checkbox:checked').forEach(checkbox => {
|
||||
selectedSeconds += parseInt(checkbox.dataset.durationSeconds, 10) || 0;
|
||||
});
|
||||
approvedSecondsInput.value = selectedSeconds;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@@ -155,6 +155,10 @@
|
||||
<div class="alert-flash"><%= flash[:alert] %></div>
|
||||
<% end %>
|
||||
|
||||
<% if flash[:notice] %>
|
||||
<div class="notice-flash"><%= flash[:notice] %></div>
|
||||
<% end %>
|
||||
|
||||
<section class="post-details-section">
|
||||
<h2>Post Details</h2>
|
||||
<% if @user %> <p><strong>User:</strong> <%= render "shared/user_mention", user: @user %></p> <% end %>
|
||||
@@ -177,6 +181,18 @@
|
||||
<% end %>
|
||||
<p><strong>Airtable Link:</strong> <%= link_to "View on Airtable", @post.airtable_url, target: "_blank", class: "button-style" %></p>
|
||||
<p><strong>API Link:</strong> <%= link_to "View on stats endpoint", api_v1_path(username: @user.id, start_date: @post_start_display.iso8601, end_date: @post_end_display.iso8601, features: "projects") %></p>
|
||||
<p><strong>Created:</strong> <%= @post_start_display.strftime("%B %d, %Y at %I:%M %p") %></p>
|
||||
<p><strong>Last Post:</strong> <%= @post_end_display.strftime("%B %d, %Y at %I:%M %p") %></p>
|
||||
<p><strong>Total Time:</strong> <%= short_time_detailed(@total_post_hackatime_seconds) %></p>
|
||||
<% if @post.airtable_fields["approvedTime"].present? %>
|
||||
<p><strong>Approved Time:</strong> <%= short_time_detailed(@post.airtable_fields["approvedTime"]) %></p>
|
||||
<% end %>
|
||||
<% if @post.airtable_fields["reviewComment"].present? %>
|
||||
<p><strong>Review Comment:</strong> <%= @post.airtable_fields["reviewComment"] %></p>
|
||||
<% end %>
|
||||
<% if @post.airtable_fields["reviewStatus"].present? %>
|
||||
<p><strong>Status:</strong> <%= @post.airtable_fields["reviewStatus"] %></p>
|
||||
<% end %>
|
||||
</section>
|
||||
|
||||
<% if @unique_project_names.any? %>
|
||||
@@ -185,7 +201,7 @@
|
||||
<form id="project-filter-form" style="display: flex; flex-direction: column; gap: 1rem;">
|
||||
<div class="project-filters" style="display: flex; flex-wrap: wrap; gap: 0.5rem;">
|
||||
<% @unique_project_names.each do |project_name| %>
|
||||
<% is_selected = params[:projects].present? ? params[:projects].split(',').include?(project_name) : true %>
|
||||
<% is_selected = params[:projects].present? ? params[:projects].split(',').include?(project_name) : @recommended_project_names.include?(project_name) %>
|
||||
<% is_recommended = @recommended_project_names.include?(project_name) %>
|
||||
<label class="project-filter-label <%= 'recommended' if is_recommended %>"
|
||||
style="display: flex; align-items: center; background: <%= is_recommended ? '#1F2937' : '#374151' %>; padding: 0.25rem 0.5rem; border-radius: 0.25rem; cursor: pointer; border: 1px solid <%= is_recommended ? '#60A5FA' : 'transparent' %>;">
|
||||
@@ -340,8 +356,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="selected-time-summary">
|
||||
<div>
|
||||
<span id="selected-duration">0s</span> / <span id="total-post-duration"><%= short_time_detailed(@total_post_hackatime_seconds) %></span>
|
||||
</div>
|
||||
<div class="seconds">
|
||||
|
||||
@@ -22,6 +22,7 @@ Rails.application.routes.draw do
|
||||
get "timeline/leaderboard_users", to: "timeline#leaderboard_users"
|
||||
|
||||
get "post_reviews/:post_id", to: "post_reviews#show", as: :post_review
|
||||
patch "post_reviews/:post_id", to: "post_reviews#update"
|
||||
get "post_reviews/:post_id/date/:date", to: "post_reviews#show", as: :post_review_on_date
|
||||
|
||||
get "ysws_reviews/:record_id", to: "ysws_reviews#show", as: :ysws_review
|
||||
|
||||
Reference in New Issue
Block a user