Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions app/admin/references.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
ActiveAdmin.register CoPlan::Reference, as: "Reference" do
permit_params :plan_id, :key, :url, :title, :reference_type, :source, :target_plan_id

index do
selectable_column
id_column
column :plan
column :key
column :url
column :title
column :reference_type
column :source
column :target_plan_id
column :created_at
actions
end

show do
attributes_table do
row :id
row :plan
row :key
row :url
row :title
row :reference_type
row :source
row :target_plan_id
row :created_at
row :updated_at
end
end
end
22 changes: 22 additions & 0 deletions db/migrate/20260410182145_create_coplan_references.co_plan.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# This migration comes from co_plan (originally 20260410000000)
class CreateCoplanReferences < ActiveRecord::Migration[8.1]
def change
create_table :coplan_references, id: { type: :string, limit: 36 } do |t|
t.string :plan_id, limit: 36, null: false
t.string :key
t.string :url, null: false
t.string :title
t.string :reference_type, null: false
t.string :source, null: false
t.string :target_plan_id, limit: 36
t.timestamps
end

add_index :coplan_references, [:plan_id, :key], unique: true
add_index :coplan_references, [:plan_id, :url], unique: true
add_index :coplan_references, :target_plan_id
add_index :coplan_references, :source
add_foreign_key :coplan_references, :coplan_plans, column: :plan_id
add_foreign_key :coplan_references, :coplan_plans, column: :target_plan_id
end
end
34 changes: 26 additions & 8 deletions db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

187 changes: 187 additions & 0 deletions engine/app/assets/stylesheets/coplan/application.css
Original file line number Diff line number Diff line change
Expand Up @@ -1872,6 +1872,193 @@ body:has(.comment-toolbar) .main-content {
margin-top: var(--space-sm);
}

/* Plan tabs */
.plan-tabs__nav {
display: flex;
gap: var(--space-xs);
margin-bottom: calc(-1px);
position: relative;
z-index: 1;
}

.plan-tabs__tab {
padding: var(--space-sm) var(--space-lg);
font-size: var(--text-sm);
font-weight: 500;
color: var(--color-text-muted);
text-decoration: none;
border: 1px solid transparent;
border-bottom: none;
border-radius: var(--radius) var(--radius) 0 0;
transition: color 0.15s, background 0.15s;
cursor: pointer;
}

.plan-tabs__tab:hover {
color: var(--color-text);
background: var(--color-bg);
text-decoration: none;
}

.plan-tabs__tab--active {
color: var(--color-text);
background: var(--color-surface);
border-color: var(--color-border);
font-weight: 600;
}

.plan-tabs__count {
font-size: 0.75rem;
color: var(--color-text-muted);
font-weight: 400;
}

.plan-tabs__panel--hidden {
display: none;
}

/* References section */
.references-section {
padding: var(--space-lg);
}

.references-list {
list-style: none;
padding: 0;
margin: 0;
}

.references-list__item {
display: flex;
align-items: flex-start;
gap: var(--space-sm);
padding: var(--space-sm) 0;
border-bottom: 1px solid var(--color-border);
}

.references-list__item:last-child {
border-bottom: none;
}

.references-list__icon {
flex-shrink: 0;
font-size: var(--text-lg);
line-height: 1.4;
}

.references-list__content {
flex: 1;
min-width: 0;
}

.references-list__link {
display: block;
word-break: break-all;
font-size: var(--text-sm);
}

.references-list__meta {
display: flex;
align-items: center;
gap: var(--space-xs);
margin-top: 2px;
}

.references-list__source {
font-size: 0.75rem;
color: var(--color-text-muted);
font-style: italic;
}

.badge--ref-type {
font-size: 0.7rem;
padding: 1px 6px;
border-radius: 4px;
background: var(--color-bg);
color: var(--color-text-muted);
border: 1px solid var(--color-border);
}

.badge--ref-key {
font-size: 0.7rem;
padding: 1px 6px;
border-radius: 4px;
background: var(--color-surface);
color: var(--color-primary);
border: 1px solid var(--color-primary);
font-family: var(--font-mono, monospace);
}

.references-list__remove {
flex-shrink: 0;
background: none;
border: none;
color: var(--color-text-muted);
cursor: pointer;
padding: 2px 6px;
font-size: var(--text-sm);
border-radius: var(--radius);
transition: color 0.15s, background 0.15s;
}

.references-list__remove:hover {
color: var(--color-danger);
background: var(--color-bg);
}

.references-section__header {
margin-bottom: var(--space-md);
}

.references-add summary {
cursor: pointer;
list-style: none;
}

.references-add summary::-webkit-details-marker {
display: none;
}

.references-add__form {
margin-top: var(--space-sm);
display: flex;
align-items: flex-end;
gap: var(--space-xs);
flex-wrap: wrap;
}

.references-add__form .form-group {
margin-bottom: 0;
flex: 1;
min-width: 140px;
}

.references-add__form input[type="text"] {
width: 100%;
padding: 6px var(--space-sm);
font-size: var(--text-sm);
font-family: var(--font-sans);
border: 1px solid var(--color-border);
border-radius: var(--radius);
background: var(--color-input-bg);
color: var(--color-text);
transition: border-color 0.15s;
}

.references-add__form input[type="text"]:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px var(--color-focus-ring);
}

.references-add__form input[type="text"]::placeholder {
color: var(--color-text-muted);
}

.references-add__form .btn {
flex-shrink: 0;
}

/* Hide sidebar on small screens */
@media (max-width: 1024px) {
.content-nav {
Expand Down
49 changes: 42 additions & 7 deletions engine/app/controllers/coplan/api/v1/plans_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,18 @@ def index
def show
render json: plan_json(@plan).merge(
current_content: @plan.current_content,
current_revision: @plan.current_revision
current_revision: @plan.current_revision,
references: @plan.references.map { |r|
{
id: r.id,
key: r.key,
url: r.url,
title: r.title,
reference_type: r.reference_type,
source: r.source,
target_plan_id: r.target_plan_id
}
}
)
end

Expand All @@ -33,12 +44,26 @@ def create
end
end

plan = Plans::Create.call(
title: params[:title],
content: params[:content] || "",
user: current_user,
plan_type_id: plan_type&.id
)
plan = nil
ActiveRecord::Base.transaction do
plan = Plans::Create.call(
title: params[:title],
content: params[:content] || "",
user: current_user,
plan_type_id: plan_type&.id
)

if params[:references].is_a?(Array)
params[:references].each do |ref_params|
next unless ref_params[:url].present?
ref_type = ref_params[:reference_type].presence || Reference.classify_url(ref_params[:url])
ref = plan.references.find_or_initialize_by(url: ref_params[:url])
ref.assign_attributes(key: ref_params[:key], title: ref_params[:title], reference_type: ref_type, source: "explicit")
ref.save!
end
end
end

render json: plan_json(plan).merge(
current_content: plan.current_content,
current_revision: plan.current_revision
Expand Down Expand Up @@ -68,6 +93,16 @@ def update
Plans::TriggerAutomatedReviews.call(plan: @plan, new_status: permitted[:status], triggered_by: current_user)
end

if params[:references].is_a?(Array)
params[:references].each do |ref_params|
next unless ref_params[:url].present?
ref_type = ref_params[:reference_type].presence || Reference.classify_url(ref_params[:url])
ref = @plan.references.find_or_initialize_by(url: ref_params[:url])
ref.assign_attributes(key: ref_params[:key], title: ref_params[:title], reference_type: ref_type, source: "explicit")
ref.save!
end
end

render json: plan_json(@plan).merge(
current_content: @plan.current_content,
current_revision: @plan.current_revision
Expand Down
Loading
Loading