Small UI improvements, include total time tracking in Projects page. (#785)

This commit is contained in:
Brian Walczak 2026-01-13 22:44:18 -06:00 committed by GitHub
parent 880e06bdea
commit 9982746122
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 89 additions and 228 deletions

View file

@ -45,7 +45,7 @@
.filter .options-list {
/* not 200 cause the search thingie is 35.5 px high */
max-height: 164.5px;
max-height: 164.5px;
overflow-y: auto;
padding: 0;
margin: 0;
@ -155,61 +155,61 @@
display: none;
}
.filter .options-container {
background: white;
border: 1px solid #ddd;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.filter .options-container {
background: white;
border: 1px solid #ddd;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.filter .search-input {
border-bottom: 1px solid #eee;
color: var(--text-color);
}
.filter .search-input {
border-bottom: 1px solid #eee;
color: var(--text-color);
}
.filter .search-input::placeholder {
color: #999;
}
.filter .search-input::placeholder {
color: #999;
}
.filter .option {
color: var(--text-color);
}
.filter .option {
color: var(--text-color);
}
.filter .option:hover {
background-color: #f5f5f5;
}
.filter .option:hover {
background-color: #f5f5f5;
}
.filter .select-header-container {
border: 1px solid #ddd;
background-color: white;
}
.filter .select-header-container {
border: 1px solid #ddd;
background-color: white;
}
.filter .select-header {
color: #666;
}
.filter .select-header {
color: #666;
}
.filter .clear-button {
color: #666;
border-left: 1px solid #ddd;
}
.filter .clear-button {
color: #666;
border-left: 1px solid #ddd;
}
.filter .clear-button:hover {
color: #ff4444;
background-color: rgba(0, 0, 0, 0.05);
}
.filter .clear-button:hover {
color: #ff4444;
background-color: rgba(0, 0, 0, 0.05);
}
.filter .option input[type="checkbox"] {
border: 1px solid rgba(0, 0, 0, 0.2);
background: rgba(0, 0, 0, 0.05);
}
.filter .option input[type="checkbox"] {
border: 1px solid rgba(0, 0, 0, 0.2);
background: rgba(0, 0, 0, 0.05);
}
.filter .option input[type="checkbox"]:checked {
background: #3291ff;
border-color: #3291ff;
}
.filter .option input[type="checkbox"]:checked {
background: #3291ff;
border-color: #3291ff;
}
.filter .option input[type="checkbox"]:hover {
border-color: rgba(0, 0, 0, 0.4);
}
.filter .option input[type="checkbox"]:hover {
border-color: rgba(0, 0, 0, 0.4);
}
.dashboard-wrapper {
@ -234,9 +234,11 @@
0% {
opacity: 0.5;
}
50% {
opacity: 1;
}
100% {
opacity: 0.5;
}
@ -453,7 +455,7 @@
border-radius: 4px;
margin-top: 4px;
max-height: 200px;
overflow-y: hidden ;
overflow-y: hidden;
z-index: 1000;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
@ -496,11 +498,6 @@
font-size: 1.1rem;
}
.dropdown-arrow {
font-size: 0.8rem;
color: #666;
}
#dashboard-content {
min-height: 200px;
width: 100%;
@ -561,139 +558,4 @@
.date-inputs span {
color: #666;
font-size: 0.9rem;
}
.interval-dropdown {
position: relative;
display: inline-block;
}
.interval-dropdown-trigger {
padding: 0.5rem 1rem;
font-size: 1rem;
border: 1px solid #bbb;
border-radius: 4px;
background: #fff;
cursor: pointer;
min-width: 180px;
text-align: left;
display: flex;
align-items: center;
justify-content: space-between;
}
.interval-dropdown-menu {
position: absolute;
top: 110%;
left: 0;
min-width: 220px;
background: #fff;
border: 1px solid #bbb;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
z-index: 1000;
padding: 0.5rem 0.75rem;
}
.interval-options-list {
list-style: none;
margin: 0;
padding: 0;
}
.interval-options-list li {
padding: 0.4rem 0.2rem;
cursor: pointer;
border-radius: 3px;
font-size: 1rem;
}
.interval-options-list li:hover {
background: #f0f0f0;
}
.interval-custom-range {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-top: 0.5rem;
}
.interval-custom-range label {
font-size: 0.95rem;
display: flex;
align-items: center;
justify-content: space-between;
}
.interval-custom-range input[type="date"] {
margin-left: 0.5rem;
padding: 0.2rem 0.4rem;
font-size: 1rem;
}
.interval-custom-range button {
margin-top: 0.5rem;
padding: 0.4rem 0.8rem;
font-size: 1rem;
border-radius: 4px;
border: none;
background: #f5b041;
color: #222;
cursor: pointer;
transition: background 0.15s ease;
}
.interval-custom-range button:hover {
background: #f39c12;
}
.dropdown-arrow {
margin-left: 0.5rem;
font-size: 0.9em;
}
.interval-dropdown-trigger {
background: #23272f;
color: #f5f5f5;
border: 1px solid #444;
}
.interval-dropdown-menu {
background: #23272f;
color: #f5f5f5;
border: 1px solid #444;
box-shadow: 0 2px 8px rgba(0,0,0,0.4);
}
.interval-options-list li {
color: #f5f5f5;
}
.interval-options-list li:hover {
background: #2c313a;
}
.interval-custom-range label {
color: #f5f5f5;
}
.interval-custom-range input[type="date"] {
background: #181b20;
color: #f5f5f5;
border: 1px solid #444;
}
.interval-custom-range button {
background: #f5b041;
color: #23272f;
}
.interval-custom-range button:hover {
background: #f39c12;
}
.dropdown-arrow {
color: #f5f5f5;
}
.compact-options-list {
max-height: 220px;
overflow-y: auto;
font-size: 0.97rem;
padding-bottom: 0.2rem;
}
.compact-options-list li {
padding: 0.25rem 0.2rem;
font-size: 0.97rem;
}
.interval-dropdown-menu {
min-width: 210px;
padding: 0.3rem 0.5rem 0.5rem 0.5rem;
}
.interval-custom-range {
margin-top: 0.3rem;
padding-bottom: 0.1rem;
}
}

View file

@ -14,14 +14,14 @@
</div>
<% if current_user.github_uid.blank? %>
<div class="notice">
You can't tie your projects to GitHub until you connect your GitHub account.
<%= link_to "Sign in with GitHub", github_auth_path, class: "btn btn-primary" %>
<div class="text-red-400 mb-4">
Heads up! You can't link your projects to GitHub until you connect your GitHub account.
<%= link_to "Sign in with GitHub", github_auth_path, class: "btn btn-primary text-white underline" %>
</div>
<% end %>
<%= render "shared/interval_selector" %>
<%= turbo_frame_tag "project_durations", src: project_durations_static_pages_path(interval: params[:interval], from: params[:from], to: params[:to], show_archived: params[:show_archived]), target: "_top" do %>
<div class="loading-indicator">Loading projects...</div>
<p class="text-lg font-semibold text-gray-500 mt-6">Loading projects, please wait...</p>
<% end %>

View file

@ -1,27 +1,35 @@
<%= turbo_frame_tag "interval_selector" do %>
<div class="interval-dropdown">
<button type="button" class="interval-dropdown-trigger" onclick="toggleIntervalDropdown()">
<div class="relative inline-block w-full max-w-xs">
<button type="button" id="interval-dropdown-trigger" class="w-full px-4 py-2 bg-[#23272f] text-gray-100 border border-gray-700 rounded cursor-pointer text-left flex items-center justify-between shadow-lg" onclick="toggleIntervalDropdown()">
<span id="interval-dropdown-label">
<%= human_interval_name(params[:interval], from: params[:from], to: params[:to]) %>
</span>
<span class="dropdown-arrow">▼</span>
<svg viewBox="0 0 20 20" fill="currentColor" data-slot="icon" aria-hidden="true" class="w-5 h-5 text-gray-500">
<path d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" fill-rule="evenodd" />
</svg>
</button>
<div class="interval-dropdown-menu" id="interval-dropdown-menu" style="display: none;">
<ul class="interval-options-list compact-options-list">
<div id="interval-dropdown-menu" class="w-full px-3 py-2 mt-2 bg-[#23272f] text-gray-100 border border-gray-700 rounded-lg absolute z-[1000] shadow-lg" style="display: none;">
<ul class="list-none m-0 p-0 overflow-y-auto max-h-60">
<% TimeRangeFilterable::RANGES.each do |key, config| %>
<li onclick="selectInterval('<%= key %>', '<%= config[:human_name] %>')"><%= config[:human_name] %></li>
<li class="py-1.5 px-2 cursor-pointer rounded bg-transparent hover:bg-[#2c313a] transition" onclick="selectInterval('<%= key %>', '<%= config[:human_name] %>')"><%= config[:human_name] %></li>
<% end %>
<li onclick="selectInterval('', 'All Time')">All Time</li>
<li class="py-1.5 px-2 cursor-pointer rounded bg-transparent hover:bg-[#2c313a] transition" onclick="selectInterval('', 'All Time')">All Time</li>
</ul>
<hr>
<div class="interval-custom-range">
<label>Start:
<input type="date" id="custom-start" value="<%= params[:from] %>">
<hr class="my-2 border-gray-700">
<div class="flex flex-col gap-2 mt-2">
<label class="flex items-center justify-between">Start:
<input type="date" class="ml-2 py-1 px-2 bg-[#181b20] border border-gray-700" id="custom-start" value="<%= params[:from] %>">
</label>
<label>End:
<input type="date" id="custom-end" value="<%= params[:to] %>">
<label class="flex items-center justify-between">End:
<input type="date" class="ml-2 py-1 px-2 bg-[#181b20] border border-gray-700" id="custom-end" value="<%= params[:to] %>">
</label>
<button type="button" class="primary-button" onclick="applyCustomRange()">Apply</button>
<button type="button" class="px-3 py-2 mt-2 mb-1 rounded bg-[#f5b041] hover:bg-[#f39c12] text-gray-800 font-semibold transition cursor-pointer" onclick="applyCustomRange()">Apply</button>
</div>
</div>
</div>
@ -107,29 +115,11 @@
function closeDropdownOnClickOutside(e) {
const menu = document.getElementById('interval-dropdown-menu');
const trigger = document.querySelector('.interval-dropdown-trigger');
const trigger = document.getElementById('interval-dropdown-trigger');
if (!menu.contains(e.target) && !trigger.contains(e.target)) {
menu.style.display = 'none';
document.removeEventListener('mousedown', closeDropdownOnClickOutside);
}
}
</script>
<style>
/* Adding explicit styles for the Apply button */
.interval-custom-range .primary-button {
padding: 0.4rem 0.8rem;
margin: 0;
font-size: 1rem;
border-radius: 4px;
border: none;
background: var(--primary-color);
color: white;
cursor: pointer;
}
.interval-custom-range .primary-button:hover {
filter: brightness(1.1);
}
</style>
<% end %>

View file

@ -2,6 +2,15 @@
<%
max = project_durations.map { |p| p[:duration] }.max || 1
%>
<p class="text-lg text-white mt-6">
<% total_time = project_durations.sum { |p| p[:duration].to_i } %>
<% if total_time > 0 %>
You've spent <span class="font-semibold"><%= short_time_detailed(total_time) %></span> coding across all projects.
<% else %>
You haven't logged any time yet. Start coding!
<% end %>
</p>
<div class="grid grid-cols-[repeat(auto-fill,minmax(280px,1fr))] gap-6 mt-6">
<% project_durations.each do |project| %>
<% if current_user.github_uid.present? && project[:project].present? %>

View file

@ -1,14 +1,14 @@
<h1 class="text-3xl font-bold text-white mb-4">My Projects</h1>
<% if current_user.github_uid.blank? %>
<div class="notice">
You can't tie your projects to GitHub until you connect your GitHub account.
<%= link_to "Sign in with GitHub", github_auth_path, class: "btn btn-primary" %>
<div class="text-red-400 mb-4">
Heads up! You can't link your projects to GitHub until you connect your GitHub account.
<%= link_to "Sign in with GitHub", github_auth_path, class: "btn btn-primary text-white underline" %>
</div>
<% end %>
<%= render "shared/interval_selector" %>
<%= turbo_frame_tag "project_durations", src: project_durations_static_pages_path(interval: params[:interval], from: params[:from], to: params[:to]), target: "_top" do %>
<div class="loading-indicator">Loading projects...</div>
<p class="text-lg font-semibold text-gray-500 mt-6">Loading projects, please wait...</p>
<% end %>