diff --git a/app/assets/images/person.svg b/app/assets/images/person.svg
new file mode 100644
index 000000000..1e29ed27d
--- /dev/null
+++ b/app/assets/images/person.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/assets/stylesheets/bubbles.css b/app/assets/stylesheets/bubbles.css
index 31021bf8a..f8a1fdd53 100644
--- a/app/assets/stylesheets/bubbles.css
+++ b/app/assets/stylesheets/bubbles.css
@@ -134,6 +134,29 @@
}
}
+ &.bubble__assignee {
+ aspect-ratio: 1;
+ inset: -1cqi auto auto 28cqi;
+ padding: 0.4em;
+
+ select {
+ inset: 0;
+ position: absolute;
+ }
+
+ @media (hover: hover) {
+ .windshield .bubble:hover & {
+ transform: translate(1rem, 0);
+ }
+ }
+ }
+
+ &.bubble__assignee--new {
+ .windshield & {
+ display: none;
+ }
+ }
+
&.bubble__attachment {
aspect-ratio: 1;
inset: -4cqi 24cqi auto auto;
@@ -199,7 +222,7 @@
white-space: nowrap;
z-index: 1;
- &:has(.input:not([type="file"], [type="date"])) {
+ &:has(.input:not([type="file"], [type="date"], select)) {
border-radius: 2em;
padding: 0.2em 0.5em;
diff --git a/app/controllers/assignments_controller.rb b/app/controllers/assignments_controller.rb
new file mode 100644
index 000000000..d21cbb59f
--- /dev/null
+++ b/app/controllers/assignments_controller.rb
@@ -0,0 +1,30 @@
+class AssignmentsController < ApplicationController
+ before_action :set_bubble, only: %i[ create update ]
+ before_action :set_assignment, only: :update
+
+ def create
+ @assignment = @bubble.assignments.build(assignment_params)
+ @assignment.save
+
+ redirect_to @bubble
+ end
+
+ def update
+ @assignment.update(assignment_params)
+ redirect_to @bubble
+ end
+
+ private
+
+ def assignment_params
+ params.require(:assignment).permit(:user_id)
+ end
+
+ def set_assignment
+ @assignment = @bubble.assignments.find(params[:id])
+ end
+
+ def set_bubble
+ @bubble = Bubble.find(params[:bubble_id])
+ end
+end
diff --git a/app/models/assignment.rb b/app/models/assignment.rb
new file mode 100644
index 000000000..4eed95605
--- /dev/null
+++ b/app/models/assignment.rb
@@ -0,0 +1,6 @@
+class Assignment < ApplicationRecord
+ belongs_to :user
+ belongs_to :bubble
+
+ validates :user_id, uniqueness: { scope: :bubble_id }
+end
diff --git a/app/models/bubble.rb b/app/models/bubble.rb
index a6841431b..f0d47395d 100644
--- a/app/models/bubble.rb
+++ b/app/models/bubble.rb
@@ -7,6 +7,9 @@ class Bubble < ApplicationRecord
has_many :taggings, dependent: :destroy
has_many :tags, through: :taggings
+ has_many :assignments, dependent: :destroy
+ has_many :assignees, through: :assignments, source: :user
+
has_one_attached :image, dependent: :purge_later
enum :color, %w[
diff --git a/app/models/user.rb b/app/models/user.rb
index 98f699bec..9a96b31c8 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,6 +1,9 @@
class User < ApplicationRecord
belongs_to :account
+ has_many :assignments
+ has_many :assigned, through: :assignments, source: :bubble
+
has_many :sessions, dependent: :destroy
has_secure_password validations: false
diff --git a/app/views/bubbles/_assignments.html.erb b/app/views/bubbles/_assignments.html.erb
new file mode 100644
index 000000000..4568e1769
--- /dev/null
+++ b/app/views/bubbles/_assignments.html.erb
@@ -0,0 +1,17 @@
+">
+ <%= form_with model: [bubble, Assignment.new], data: { controller: "form" } do |form| %>
+
+ <%= form.submit "Save", class: "btn", hidden: true %>
+ <% end %>
+
diff --git a/app/views/bubbles/_bubble.html.erb b/app/views/bubbles/_bubble.html.erb
index 7639f07d5..c87317320 100644
--- a/app/views/bubbles/_bubble.html.erb
+++ b/app/views/bubbles/_bubble.html.erb
@@ -14,6 +14,7 @@
<%= bubble.title %>
<% end %>
+ <%= render "bubbles/assignments", bubble: bubble %>
<%= render "bubbles/boosts", bubble: bubble %>
<%= render "bubbles/color", bubble: bubble %>
<%= render "bubbles/date", bubble: bubble %>
diff --git a/app/views/bubbles/_date.html.erb b/app/views/bubbles/_date.html.erb
index f67f885b1..647e30c56 100644
--- a/app/views/bubbles/_date.html.erb
+++ b/app/views/bubbles/_date.html.erb
@@ -2,7 +2,7 @@
<%= form_with model: bubble, data: { controller: "form" } do |form| %>
<% end %>
diff --git a/config/routes.rb b/config/routes.rb
index 2c775334b..7cafc501d 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -6,6 +6,7 @@ Rails.application.routes.draw do
resources :bubbles do
resource :image, controller: "bubbles/images"
+ resources :assignments
resources :boosts
resources :comments
resources :tags, shallow: true
diff --git a/db/migrate/20240917174301_create_assignments.rb b/db/migrate/20240917174301_create_assignments.rb
new file mode 100644
index 000000000..0c2fc5928
--- /dev/null
+++ b/db/migrate/20240917174301_create_assignments.rb
@@ -0,0 +1,10 @@
+class CreateAssignments < ActiveRecord::Migration[8.0]
+ def change
+ create_table :assignments do |t|
+ t.references :user, null: false, foreign_key: true
+ t.references :bubble, null: false, foreign_key: true
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 8cdd61d63..16ec490fa 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.0].define(version: 2024_09_13_172737) do
+ActiveRecord::Schema[8.0].define(version: 2024_09_17_174301) do
create_table "accounts", force: :cascade do |t|
t.string "name", null: false
t.datetime "created_at", null: false
@@ -46,6 +46,15 @@ ActiveRecord::Schema[8.0].define(version: 2024_09_13_172737) do
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
end
+ create_table "assignments", force: :cascade do |t|
+ t.integer "user_id", null: false
+ t.integer "bubble_id", null: false
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["bubble_id"], name: "index_assignments_on_bubble_id"
+ t.index ["user_id"], name: "index_assignments_on_user_id"
+ end
+
create_table "boosts", force: :cascade do |t|
t.string "body"
t.integer "creator_id", null: false
@@ -111,6 +120,8 @@ ActiveRecord::Schema[8.0].define(version: 2024_09_13_172737) do
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
+ add_foreign_key "assignments", "bubbles"
+ add_foreign_key "assignments", "users"
add_foreign_key "sessions", "users"
add_foreign_key "taggings", "bubbles"
add_foreign_key "taggings", "tags"
diff --git a/test/fixtures/assignments.yml b/test/fixtures/assignments.yml
new file mode 100644
index 000000000..ba61196f4
--- /dev/null
+++ b/test/fixtures/assignments.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ user: :kevin
+ bubble: one
+
+two:
+ user: :jz
+ bubble: two
diff --git a/test/fixtures/bubbles.yml b/test/fixtures/bubbles.yml
index 7cad9188f..1700e3862 100644
--- a/test/fixtures/bubbles.yml
+++ b/test/fixtures/bubbles.yml
@@ -3,13 +3,14 @@
one:
title: The logo isn't big enough
body: Make the logo bigger.
- color: '#AF2E1B'
+ color: '#ED8008'
creator: :david
+ due_on: <%= 3.days.from_now %>
two:
title: Layout is broken
body: The page scrolls horizontally on mobile devices.
- color: '#CC6324'
+ color: '#698F9C'
creator: :jz
three:
diff --git a/test/models/assignment_test.rb b/test/models/assignment_test.rb
new file mode 100644
index 000000000..ca8f09a34
--- /dev/null
+++ b/test/models/assignment_test.rb
@@ -0,0 +1,7 @@
+require "test_helper"
+
+class AssignmentTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end