mirror of
https://github.com/System-End/hackatime.git
synced 2026-04-19 22:15:14 +00:00
archive projects (#770)
This commit is contained in:
parent
9cc68b881a
commit
7ebb1b2085
17 changed files with 226 additions and 20 deletions
|
|
@ -75,4 +75,32 @@
|
|||
.nav-mobile-slide.open {
|
||||
@apply translate-x-0;
|
||||
}
|
||||
|
||||
.project-toggle-group {
|
||||
@apply flex items-center gap-2 rounded-lg p-1;
|
||||
background-color: var(--color-darkless);
|
||||
}
|
||||
|
||||
.project-toggle-btn {
|
||||
@apply px-3 py-1 rounded transition-all duration-200 font-medium cursor-pointer;
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.project-toggle-btn:hover {
|
||||
color: var(--color-cyan);
|
||||
}
|
||||
|
||||
.project-toggle-btn.active {
|
||||
background-color: var(--color-primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.project-toggle-btn.active:hover {
|
||||
background-color: var(--color-primary);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.project-toggle-btn.inactive {
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -287,7 +287,8 @@ module Api
|
|||
last_heartbeat: stat.last_heartbeat,
|
||||
languages: stat.languages || [],
|
||||
repo: repo_mapping&.repo_url,
|
||||
repo_mapping_id: repo_mapping&.id
|
||||
repo_mapping_id: repo_mapping&.id,
|
||||
archived: repo_mapping&.archived? || false
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,19 +4,31 @@ module Api
|
|||
class ProjectsController < ApplicationController
|
||||
def index
|
||||
projects = time_per_project.map { |project, _|
|
||||
archived = archived_project_names.include?(project)
|
||||
next if archived && !include_archived?
|
||||
|
||||
{
|
||||
name: project,
|
||||
total_seconds: time_per_project[project] || 0,
|
||||
most_recent_heartbeat: most_recent_heartbeat_per_project[project] ? Time.at(most_recent_heartbeat_per_project[project]).strftime("%Y-%m-%dT%H:%M:%SZ") : nil,
|
||||
languages: languages_per_project[project] || []
|
||||
languages: languages_per_project[project] || [],
|
||||
archived: archived
|
||||
}
|
||||
}
|
||||
}.compact
|
||||
|
||||
render json: { projects: projects }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def include_archived?
|
||||
params[:include_archived] == "true"
|
||||
end
|
||||
|
||||
def archived_project_names
|
||||
@archived_project_names ||= current_user.project_repo_mappings.archived.pluck(:project_name)
|
||||
end
|
||||
|
||||
def time_per_project
|
||||
@time_per_project ||= current_user.heartbeats
|
||||
.with_valid_timestamps
|
||||
|
|
|
|||
|
|
@ -307,6 +307,7 @@ class Api::V1::StatsController < ApplicationController
|
|||
.pluck(:language)
|
||||
|
||||
repo = @user.project_repo_mappings.find_by(project_name: name)
|
||||
next if repo&.archived?
|
||||
|
||||
data << {
|
||||
name: name,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
class My::ProjectRepoMappingsController < ApplicationController
|
||||
before_action :ensure_current_user
|
||||
before_action :require_github_oauth, only: [ :edit, :update ]
|
||||
before_action :set_project_repo_mapping, only: [ :edit, :update ]
|
||||
before_action :set_project_repo_mapping_for_edit, only: [ :edit, :update ]
|
||||
before_action :set_project_repo_mapping, only: [ :archive, :unarchive ]
|
||||
|
||||
def index
|
||||
@project_repo_mappings = current_user.project_repo_mappings || []
|
||||
@project_repo_mappings = current_user.project_repo_mappings.active
|
||||
@interval = params[:interval] || "daily"
|
||||
@from = params[:from]
|
||||
@to = params[:to]
|
||||
|
|
@ -26,6 +27,16 @@ class My::ProjectRepoMappingsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def archive
|
||||
@project_repo_mapping.archive!
|
||||
redirect_to my_projects_path, notice: "Away it goes!"
|
||||
end
|
||||
|
||||
def unarchive
|
||||
@project_repo_mapping.unarchive!
|
||||
redirect_to my_projects_path(show_archived: true), notice: "Back from the dead!"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_current_user
|
||||
|
|
@ -39,13 +50,20 @@ class My::ProjectRepoMappingsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def set_project_repo_mapping
|
||||
def set_project_repo_mapping_for_edit
|
||||
decoded_project_name = CGI.unescape(params[:project_name])
|
||||
@project_repo_mapping = current_user.project_repo_mappings.find_or_initialize_by(
|
||||
project_name: decoded_project_name
|
||||
)
|
||||
end
|
||||
|
||||
def set_project_repo_mapping
|
||||
decoded_project_name = CGI.unescape(params[:project_name])
|
||||
@project_repo_mapping = current_user.project_repo_mappings.find_by!(
|
||||
project_name: decoded_project_name
|
||||
)
|
||||
end
|
||||
|
||||
def project_repo_mapping_params
|
||||
params.require(:project_repo_mapping).permit(:repo_url)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class ProfilesController < ApplicationController
|
|||
.first(5)
|
||||
.to_h
|
||||
|
||||
project_repo_mappings = @user.project_repo_mappings.index_by(&:project_name)
|
||||
project_repo_mappings = @user.project_repo_mappings.active.index_by(&:project_name)
|
||||
|
||||
@top_projects_month = @user.heartbeats
|
||||
.where("time >= ?", 1.month.ago.to_f)
|
||||
|
|
|
|||
|
|
@ -111,9 +111,18 @@ class StaticPagesController < ApplicationController
|
|||
def project_durations
|
||||
return unless current_user
|
||||
|
||||
@project_repo_mappings = current_user.project_repo_mappings.includes(:repository)
|
||||
cache_key = "user_#{current_user.id}_project_durations_#{params[:interval]}"
|
||||
show_archived = params[:show_archived] == "true"
|
||||
|
||||
if show_archived
|
||||
@project_repo_mappings = current_user.project_repo_mappings.archived.includes(:repository)
|
||||
else
|
||||
@project_repo_mappings = current_user.project_repo_mappings.active.includes(:repository)
|
||||
end
|
||||
archived_projects = current_user.project_repo_mappings.archived.pluck(:project_name)
|
||||
|
||||
cache_key = "user_#{current_user.id}_project_durations_#{params[:interval]}_v2"
|
||||
cache_key += "_#{params[:from]}_#{params[:to]}" if params[:interval] == "custom"
|
||||
cache_key += "_archived" if show_archived
|
||||
|
||||
project_durations = Rails.cache.fetch(cache_key, expires_in: 1.minute) do
|
||||
heartbeats = current_user.heartbeats.filter_by_time_range(params[:interval], params[:from], params[:to])
|
||||
|
|
@ -123,13 +132,22 @@ class StaticPagesController < ApplicationController
|
|||
mapping = @project_repo_mappings.find { |p| p.project_name == project }
|
||||
{
|
||||
project: project_labels.find { |p| p.project_key == project }&.label || project || "Unknown",
|
||||
project_key: project,
|
||||
repo_url: mapping&.repo_url,
|
||||
repository: mapping&.repository,
|
||||
has_mapping: mapping.present?,
|
||||
duration: duration
|
||||
}
|
||||
end.filter { |p| p[:duration].positive? }.sort_by { |p| p[:duration] }.reverse
|
||||
end
|
||||
render partial: "project_durations", locals: { project_durations: project_durations }
|
||||
|
||||
if show_archived
|
||||
project_durations = project_durations.select { |p| archived_projects.include?(p[:project_key]) }
|
||||
else
|
||||
project_durations = project_durations.reject { |p| archived_projects.include?(p[:project_key]) }
|
||||
end
|
||||
|
||||
render partial: "project_durations", locals: { project_durations: project_durations, show_archived: show_archived }
|
||||
end
|
||||
|
||||
def activity_graph
|
||||
|
|
@ -264,10 +282,12 @@ class StaticPagesController < ApplicationController
|
|||
# Load filter options and apply filters with caching
|
||||
Rails.cache.fetch(cache_key, expires_in: 5.minutes) do
|
||||
result = {}
|
||||
archived_projects = current_user.project_repo_mappings.archived.pluck(:project_name)
|
||||
# Load filter options
|
||||
Time.use_zone(current_user.timezone) do
|
||||
filters.each do |filter|
|
||||
group_by_time = current_user.heartbeats.group(filter).duration_seconds
|
||||
group_by_time = group_by_time.reject { |name, _| archived_projects.include?(name) } if filter == :project
|
||||
result[filter] = group_by_time.sort_by { |k, v| v }
|
||||
.reverse.map(&:first)
|
||||
.compact_blank
|
||||
|
|
@ -309,10 +329,9 @@ class StaticPagesController < ApplicationController
|
|||
result[:total_heartbeats] = filtered_heartbeats.count
|
||||
|
||||
filters.each do |filter|
|
||||
result["top_#{filter}"] = filtered_heartbeats.group(filter)
|
||||
.duration_seconds
|
||||
.max_by { |_, v| v }
|
||||
&.first
|
||||
stats = filtered_heartbeats.group(filter).duration_seconds
|
||||
stats = stats.reject { |name, _| archived_projects.include?(name) } if filter == :project
|
||||
result["top_#{filter}"] = stats.max_by { |_, v| v }&.first
|
||||
end
|
||||
|
||||
# Transform top editor, OS, and language names
|
||||
|
|
@ -324,6 +343,7 @@ class StaticPagesController < ApplicationController
|
|||
result[:project_durations] = filtered_heartbeats
|
||||
.group(:project)
|
||||
.duration_seconds
|
||||
.reject { |project, _| archived_projects.include?(project) }
|
||||
.sort_by { |_, duration| -duration }
|
||||
.first(10)
|
||||
.to_h unless result["singular_project"]
|
||||
|
|
@ -395,6 +415,7 @@ class StaticPagesController < ApplicationController
|
|||
.where(time: week_start.to_f..week_end.to_f)
|
||||
.group(:project)
|
||||
.duration_seconds
|
||||
.reject { |project, _| archived_projects.include?(project) }
|
||||
|
||||
result[:weekly_project_stats][week_start.to_date.iso8601] = week_stats
|
||||
end
|
||||
|
|
|
|||
3
app/jobs/cache/active_projects_job.rb
vendored
3
app/jobs/cache/active_projects_job.rb
vendored
|
|
@ -9,7 +9,8 @@ class Cache::ActiveProjectsJob < Cache::ActivityJob
|
|||
|
||||
def calculate
|
||||
# Get recent heartbeats with matching project_repo_mappings in a single SQL query
|
||||
ProjectRepoMapping.joins("INNER JOIN heartbeats ON heartbeats.project = project_repo_mappings.project_name AND heartbeats.user_id = project_repo_mappings.user_id")
|
||||
ProjectRepoMapping.active
|
||||
.joins("INNER JOIN heartbeats ON heartbeats.project = project_repo_mappings.project_name AND heartbeats.user_id = project_repo_mappings.user_id")
|
||||
.joins("INNER JOIN users ON users.id = heartbeats.user_id")
|
||||
.where("heartbeats.source_type = ?", Heartbeat.source_types[:direct_entry])
|
||||
.where("heartbeats.time > ?", 5.minutes.ago.to_f)
|
||||
|
|
|
|||
3
app/jobs/cache/currently_hacking_job.rb
vendored
3
app/jobs/cache/currently_hacking_job.rb
vendored
|
|
@ -23,7 +23,8 @@ class Cache::CurrentlyHackingJob < Cache::ActivityJob
|
|||
active_projects = {}
|
||||
users.each do |user|
|
||||
recent_heartbeat = recent_heartbeats[user.id]
|
||||
active_projects[user.id] = user.project_repo_mappings.find { |p| p.project_name == recent_heartbeat&.project }
|
||||
mapping = user.project_repo_mappings.find { |p| p.project_name == recent_heartbeat&.project }
|
||||
active_projects[user.id] = mapping&.archived? ? nil : mapping
|
||||
end
|
||||
|
||||
users = users.sort_by do |user|
|
||||
|
|
|
|||
|
|
@ -22,9 +22,25 @@ class ProjectRepoMapping < ApplicationRecord
|
|||
"<<LAST PROJECT>>"
|
||||
]
|
||||
|
||||
scope :active, -> { where(archived_at: nil) }
|
||||
scope :archived, -> { where.not(archived_at: nil) }
|
||||
scope :all_statuses, -> { unscoped.where(nil) }
|
||||
|
||||
after_create :create_repository_and_sync
|
||||
after_update :sync_repository_if_url_changed
|
||||
|
||||
def archive!
|
||||
update!(archived_at: Time.current)
|
||||
end
|
||||
|
||||
def unarchive!
|
||||
update!(archived_at: nil)
|
||||
end
|
||||
|
||||
def archived?
|
||||
archived_at.present?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def repo_host_supported
|
||||
|
|
|
|||
25
app/views/my/project_repo_mappings/_archive_modal.html.erb
Normal file
25
app/views/my/project_repo_mappings/_archive_modal.html.erb
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<%
|
||||
modal_id = "archive-project-modal-#{project[:project]&.parameterize || 'unknown'}"
|
||||
project_name = project[:project]
|
||||
%>
|
||||
|
||||
<%= render "shared/modal",
|
||||
modal_id: modal_id,
|
||||
title: "Archive #{project_name}?",
|
||||
description: "This project will be hidden from most stats and listings, however it will still be visible to you and any time logged will still count towards it. You can restore it anytime from the archived projects page.",
|
||||
icon_svg: '<path fill="currentColor" d="m20.54 5.23l-1.39-1.68C18.88 3.21 18.47 3 18 3H6c-.47 0-.88.21-1.16.55L3.46 5.23C3.17 5.57 3 6.02 3 6.5V19c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6.5c0-.48-.17-.93-.46-1.27m-8.89 11.92L6.5 12H10v-2h4v2h3.5l-5.15 5.15c-.19.19-.51.19-.7 0M5.12 5l.81-1h12l.94 1z"/>',
|
||||
max_width: "max-w-md",
|
||||
buttons: [
|
||||
{
|
||||
text: "Cancel",
|
||||
class: "border border-gray-600 text-gray-300 hover:bg-darkless",
|
||||
action: "click->modal#close"
|
||||
},
|
||||
{
|
||||
text: "Archive",
|
||||
class: "bg-primary text-white hover:bg-red font-medium",
|
||||
form: true,
|
||||
url: archive_my_project_repo_mapping_path(CGI.escape(project_name)),
|
||||
method: "patch"
|
||||
}
|
||||
] %>
|
||||
24
app/views/my/project_repo_mappings/_unarchive_modal.html.erb
Normal file
24
app/views/my/project_repo_mappings/_unarchive_modal.html.erb
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<%
|
||||
modal_id = "unarchive-project-modal-#{project[:project]&.parameterize || 'unknown'}"
|
||||
%>
|
||||
|
||||
<%= render "shared/modal",
|
||||
modal_id: modal_id,
|
||||
title: "Restore #{project[:project]}?",
|
||||
description: "This will restore the project to your main projects list and it will appear in stats again. This will not affect any time logged on this project.",
|
||||
icon_svg: '<path fill="currentColor" d="m20.55 5.22l-1.39-1.68A1.51 1.51 0 0 0 18 3H6c-.47 0-.88.21-1.15.55L3.46 5.22C3.17 5.57 3 6.01 3 6.5V19a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V6.5c0-.49-.17-.93-.45-1.28m-8.2 4.63L17.5 15H14v2h-4v-2H6.5l5.15-5.15c.19-.19.51-.19.7 0M5.12 5l.82-1h12l.93 1z"/>',
|
||||
max_width: "max-w-md",
|
||||
buttons: [
|
||||
{
|
||||
text: "Cancel",
|
||||
class: "border border-gray-600 text-gray-300 hover:bg-darkless",
|
||||
action: "click->modal#close"
|
||||
},
|
||||
{
|
||||
text: "Restore",
|
||||
class: "bg-primary text-white hover:bg-red font-medium",
|
||||
form: true,
|
||||
url: unarchive_my_project_repo_mapping_path(CGI.escape(project[:project_key] || project[:project])),
|
||||
method: "patch"
|
||||
}
|
||||
] %>
|
||||
|
|
@ -1,4 +1,17 @@
|
|||
<h1 class="text-3xl font-bold text-white mb-4">My Projects</h1>
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="flex items-center gap-4">
|
||||
<h1 class="text-3xl font-bold text-white">My Projects</h1>
|
||||
<% archived_count = current_user.project_repo_mappings.archived.count %>
|
||||
<% if archived_count > 0 %>
|
||||
<div class="project-toggle-group">
|
||||
<%= link_to "Active", my_projects_path(show_archived: false),
|
||||
class: "project-toggle-btn #{params[:show_archived] != 'true' ? 'active' : 'inactive'}" %>
|
||||
<%= link_to "Archived", my_projects_path(show_archived: true),
|
||||
class: "project-toggle-btn #{params[:show_archived] == 'true' ? 'active' : 'inactive'}" %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if current_user.github_uid.blank? %>
|
||||
<div class="notice">
|
||||
|
|
@ -9,6 +22,6 @@
|
|||
|
||||
<%= 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 %>
|
||||
<%= 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>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,13 @@
|
|||
<% project_durations.each do |project| %>
|
||||
<% if current_user.github_uid.present? && project[:project].present? %>
|
||||
<%= render "my/project_repo_mappings/edit_modal", project: project %>
|
||||
<% if project[:has_mapping] %>
|
||||
<% if show_archived %>
|
||||
<%= render "my/project_repo_mappings/unarchive_modal", project: project %>
|
||||
<% else %>
|
||||
<%= render "my/project_repo_mappings/archive_modal", project: project %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<div class="bg-dark border border-primary rounded-xl p-6 shadow-lg transition-all duration-300 flex flex-col gap-4 hover:border-primary/40 hover:-translate-y-1 hover:shadow-xl backdrop-blur-sm">
|
||||
<div class="flex justify-between items-start gap-3">
|
||||
|
|
@ -47,6 +54,29 @@
|
|||
<path fill="currentColor" d="M20.71 7.04c.39-.39.39-1.04 0-1.41l-2.34-2.34c-.37-.39-1.02-.39-1.41 0l-1.84 1.83l3.75 3.75M3 17.25V21h3.75L17.81 9.93l-3.75-3.75z" />
|
||||
</svg>
|
||||
</button>
|
||||
<% if project[:has_mapping] %>
|
||||
<% if show_archived %>
|
||||
<% unarchive_modal_id = "unarchive-project-modal-#{project[:project]&.parameterize || 'unknown'}" %>
|
||||
<button type="button"
|
||||
onclick="document.getElementById(<%= unarchive_modal_id.to_json %>).dispatchEvent(new CustomEvent('modal:open'))"
|
||||
class="p-2 rounded-lg bg-white/5 hover:bg-white/10 transition-colors duration-200 cursor-pointer"
|
||||
title="Restore project">
|
||||
<svg class="w-4 h-4 text-white/70" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="m20.55 5.22l-1.39-1.68A1.51 1.51 0 0 0 18 3H6c-.47 0-.88.21-1.15.55L3.46 5.22C3.17 5.57 3 6.01 3 6.5V19a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V6.5c0-.49-.17-.93-.45-1.28m-8.2 4.63L17.5 15H14v2h-4v-2H6.5l5.15-5.15c.19-.19.51-.19.7 0M5.12 5l.82-1h12l.93 1z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<% else %>
|
||||
<% archive_modal_id = "archive-project-modal-#{project[:project]&.parameterize || 'unknown'}" %>
|
||||
<button type="button"
|
||||
onclick="document.getElementById(<%= archive_modal_id.to_json %>).dispatchEvent(new CustomEvent('modal:open'))"
|
||||
class="p-2 rounded-lg bg-white/5 hover:bg-white/10 transition-colors duration-200 cursor-pointer"
|
||||
title="Archive project">
|
||||
<svg class="w-4 h-4 text-white/70" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="m20.54 5.23l-1.39-1.68C18.88 3.21 18.47 3 18 3H6c-.47 0-.88.21-1.16.55L3.46 5.23C3.17 5.57 3 6.02 3 6.5V19c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V6.5c0-.48-.17-.93-.46-1.27m-8.89 11.92L6.5 12H10v-2h4v2h3.5l-5.15 5.15c-.19.19-.51.19-.7 0M5.12 5l.81-1h12l.94 1z"/>
|
||||
</svg>
|
||||
</button>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -120,7 +120,12 @@ Rails.application.routes.draw do
|
|||
post "my/settings/rotate_api_key", to: "users#rotate_api_key", as: :my_settings_rotate_api_key
|
||||
|
||||
namespace :my do
|
||||
resources :project_repo_mappings, param: :project_name, only: [ :edit, :update ], constraints: { project_name: /.+/ }
|
||||
resources :project_repo_mappings, param: :project_name, only: [ :edit, :update ], constraints: { project_name: /.+/ } do
|
||||
member do
|
||||
patch :archive
|
||||
patch :unarchive
|
||||
end
|
||||
end
|
||||
# resource :mailing_address, only: [ :show, :edit ]
|
||||
# get "mailroom", to: "mailroom#index"
|
||||
resources :heartbeats, only: [] do
|
||||
|
|
@ -131,6 +136,8 @@ Rails.application.routes.draw do
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
get "deletion", to: "deletion_requests#show", as: :deletion
|
||||
post "deletion", to: "deletion_requests#create", as: :create_deletion
|
||||
delete "deletion", to: "deletion_requests#cancel", as: :cancel_deletion
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
class AddArchivedAtToProjectRepoMappings < ActiveRecord::Migration[8.1]
|
||||
def change
|
||||
add_column :project_repo_mappings, :archived_at, :datetime
|
||||
add_index :project_repo_mappings, [ :user_id, :archived_at ]
|
||||
end
|
||||
end
|
||||
4
db/schema.rb
generated
4
db/schema.rb
generated
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.1].define(version: 2026_01_03_141942) do
|
||||
ActiveRecord::Schema[8.1].define(version: 2026_01_05_230132) do
|
||||
create_schema "pganalyze"
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_catalog.plpgsql"
|
||||
|
|
@ -384,6 +384,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_01_03_141942) do
|
|||
end
|
||||
|
||||
create_table "project_repo_mappings", force: :cascade do |t|
|
||||
t.datetime "archived_at"
|
||||
t.datetime "created_at", null: false
|
||||
t.string "project_name", null: false
|
||||
t.string "repo_url", null: false
|
||||
|
|
@ -392,6 +393,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_01_03_141942) do
|
|||
t.bigint "user_id", null: false
|
||||
t.index ["project_name"], name: "index_project_repo_mappings_on_project_name"
|
||||
t.index ["repository_id"], name: "index_project_repo_mappings_on_repository_id"
|
||||
t.index ["user_id", "archived_at"], name: "index_project_repo_mappings_on_user_id_and_archived_at"
|
||||
t.index ["user_id", "project_name"], name: "index_project_repo_mappings_on_user_id_and_project_name", unique: true
|
||||
t.index ["user_id"], name: "index_project_repo_mappings_on_user_id"
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue