From 9906b291f148b04fa10507fb39636ab234c6e905 Mon Sep 17 00:00:00 2001 From: Niki Date: Wed, 11 Jun 2025 19:56:30 +0200 Subject: [PATCH] add models for Project, Task, Note, Category --- Gemfile.lock | 4 + README.md | 1 + app/models/contact.rb | 7 ++ app/models/note.rb | 3 + app/models/organization.rb | 2 + app/models/phone_number.rb | 11 +++ app/models/project.rb | 8 ++ app/models/project_category.rb | 3 + app/models/task.rb | 9 ++ app/models/user.rb | 3 + ...0250611163105_create_project_categories.rb | 11 +++ db/migrate/20250611163700_create_contacts.rb | 13 +++ .../20250611172549_create_phone_numbers.rb | 11 +++ db/migrate/20250611172728_create_projects.rb | 14 +++ db/migrate/20250611173502_create_tasks.rb | 15 ++++ db/migrate/20250611174428_create_notes.rb | 13 +++ db/schema.rb | 88 ++++++++++++++++++- test/fixtures/contacts.yml | 15 ++++ test/fixtures/notes.yml | 15 ++++ test/fixtures/phone_numbers.yml | 11 +++ test/fixtures/project_categories.yml | 11 +++ test/fixtures/projects.yml | 17 ++++ test/fixtures/tasks.yml | 19 ++++ test/models/contact_test.rb | 7 ++ test/models/note_test.rb | 7 ++ test/models/phone_number_test.rb | 7 ++ test/models/project_category_test.rb | 7 ++ test/models/project_test.rb | 7 ++ test/models/task_test.rb | 7 ++ 29 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 app/models/contact.rb create mode 100644 app/models/note.rb create mode 100644 app/models/phone_number.rb create mode 100644 app/models/project.rb create mode 100644 app/models/project_category.rb create mode 100644 app/models/task.rb create mode 100644 db/migrate/20250611163105_create_project_categories.rb create mode 100644 db/migrate/20250611163700_create_contacts.rb create mode 100644 db/migrate/20250611172549_create_phone_numbers.rb create mode 100644 db/migrate/20250611172728_create_projects.rb create mode 100644 db/migrate/20250611173502_create_tasks.rb create mode 100644 db/migrate/20250611174428_create_notes.rb create mode 100644 test/fixtures/contacts.yml create mode 100644 test/fixtures/notes.yml create mode 100644 test/fixtures/phone_numbers.yml create mode 100644 test/fixtures/project_categories.yml create mode 100644 test/fixtures/projects.yml create mode 100644 test/fixtures/tasks.yml create mode 100644 test/models/contact_test.rb create mode 100644 test/models/note_test.rb create mode 100644 test/models/phone_number_test.rb create mode 100644 test/models/project_category_test.rb create mode 100644 test/models/project_test.rb create mode 100644 test/models/task_test.rb diff --git a/Gemfile.lock b/Gemfile.lock index cea4c5e..b5a8f0e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -103,6 +103,9 @@ GEM irb (~> 1.10) reline (>= 0.3.8) dotenv (3.1.8) + dotenv-rails (3.1.8) + dotenv (= 3.1.8) + railties (>= 6.1) drb (2.2.3) ed25519 (1.4.0) erb (5.0.1) @@ -360,6 +363,7 @@ DEPENDENCIES brakeman capybara debug + dotenv-rails importmap-rails jbuilder kamal diff --git a/README.md b/README.md index 0e65f51..abe2260 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ postgres=# ALTER USER CREATEDB; ``` 6. `rails db:migrate`, then create a new org: + `rails tenant:create['Demo Corp','demo']` and `rails tenant:migrate` ```sh rails tenant:create[name,subdomain] # Create a new tenant (organization) diff --git a/app/models/contact.rb b/app/models/contact.rb new file mode 100644 index 0000000..60754bc --- /dev/null +++ b/app/models/contact.rb @@ -0,0 +1,7 @@ +class Contact < ApplicationRecord + has_many :phone_numbers + belongs_to :user + + validates :name, presence: true + validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }, allow_blank: true +end diff --git a/app/models/note.rb b/app/models/note.rb new file mode 100644 index 0000000..a29be63 --- /dev/null +++ b/app/models/note.rb @@ -0,0 +1,3 @@ +class Note < ApplicationRecord + belongs_to :project +end diff --git a/app/models/organization.rb b/app/models/organization.rb index 5e767ee..6d3b9d0 100644 --- a/app/models/organization.rb +++ b/app/models/organization.rb @@ -6,6 +6,8 @@ class Organization < ApplicationRecord after_create :create_schema before_destroy :drop_schema + has_many :users, dependent: :destroy + has_many :projects, through: :users def schema_name "org_#{id.to_s.gsub('-', '_')}" diff --git a/app/models/phone_number.rb b/app/models/phone_number.rb new file mode 100644 index 0000000..1862082 --- /dev/null +++ b/app/models/phone_number.rb @@ -0,0 +1,11 @@ +class PhoneNumber < ApplicationRecord + belongs_to :contact + enum :type, { + mobile: 0, + home: 1, + work: 2, + fax: 3, + other: 4 + }, _prefix: true + validates :number, presence: true, format: { with: /\A\+?[0-9\s\-()]+\z/, message: "must be a valid phone number" } +end diff --git a/app/models/project.rb b/app/models/project.rb new file mode 100644 index 0000000..41d943d --- /dev/null +++ b/app/models/project.rb @@ -0,0 +1,8 @@ +class Project < ApplicationRecord + belongs_to :user + belongs_to :project_category + has_many :contacts, through: :user + has_many :tasks + has_many :notes + enum business_type: { type1: 0, type2: 1 } +end diff --git a/app/models/project_category.rb b/app/models/project_category.rb new file mode 100644 index 0000000..5174ab5 --- /dev/null +++ b/app/models/project_category.rb @@ -0,0 +1,3 @@ +class ProjectCategory < ApplicationRecord + has_many :projects +end diff --git a/app/models/task.rb b/app/models/task.rb new file mode 100644 index 0000000..7c41321 --- /dev/null +++ b/app/models/task.rb @@ -0,0 +1,9 @@ +class Task < ApplicationRecord + belongs_to :creator, class_name: 'User' + belongs_to :reporter, class_name: 'User', optional: true + belongs_to :reportee, class_name: 'User', optional: true + belongs_to :project + enum priority: { low: 0, medium: 1, high: 2 } + enum status: { open: 0, in_progress: 1, closed: 2 } + enum type: { bug: 0, feature: 1, chore: 2 } +end diff --git a/app/models/user.rb b/app/models/user.rb index c88d5b0..57796aa 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,9 @@ class User < ApplicationRecord has_secure_password has_many :sessions, dependent: :destroy + belongs_to :organization, optional: false + has_many :projects + has_many :contacts normalizes :email_address, with: ->(e) { e.strip.downcase } end diff --git a/db/migrate/20250611163105_create_project_categories.rb b/db/migrate/20250611163105_create_project_categories.rb new file mode 100644 index 0000000..0ed4086 --- /dev/null +++ b/db/migrate/20250611163105_create_project_categories.rb @@ -0,0 +1,11 @@ +class CreateProjectCategories < ActiveRecord::Migration[8.0] + def change + create_table :project_categories do |t| + t.string :title + t.text :description + t.integer :order + + t.timestamps + end + end +end diff --git a/db/migrate/20250611163700_create_contacts.rb b/db/migrate/20250611163700_create_contacts.rb new file mode 100644 index 0000000..dbcc1c2 --- /dev/null +++ b/db/migrate/20250611163700_create_contacts.rb @@ -0,0 +1,13 @@ +class CreateContacts < ActiveRecord::Migration[8.0] + def change + create_table :contacts do |t| + t.string :name + t.string :email + t.string :address + t.float :geo_long + t.float :geo_lat + + t.timestamps + end + end +end diff --git a/db/migrate/20250611172549_create_phone_numbers.rb b/db/migrate/20250611172549_create_phone_numbers.rb new file mode 100644 index 0000000..6f591bd --- /dev/null +++ b/db/migrate/20250611172549_create_phone_numbers.rb @@ -0,0 +1,11 @@ +class CreatePhoneNumbers < ActiveRecord::Migration[8.0] + def change + create_table :phone_numbers do |t| + t.string :phone_number + t.integer :type + t.references :contact, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/migrate/20250611172728_create_projects.rb b/db/migrate/20250611172728_create_projects.rb new file mode 100644 index 0000000..8062dff --- /dev/null +++ b/db/migrate/20250611172728_create_projects.rb @@ -0,0 +1,14 @@ +class CreateProjects < ActiveRecord::Migration[8.0] + def change + create_table :projects do |t| + t.string :title + t.string :website + t.string :email + t.integer :business_type + t.references :user, null: false, foreign_key: true + t.references :project_category, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/migrate/20250611173502_create_tasks.rb b/db/migrate/20250611173502_create_tasks.rb new file mode 100644 index 0000000..96a0ca5 --- /dev/null +++ b/db/migrate/20250611173502_create_tasks.rb @@ -0,0 +1,15 @@ +class CreateTasks < ActiveRecord::Migration[8.0] + def change + create_table :tasks do |t| + t.string :title + t.text :description + t.integer :priority + t.integer :status + t.integer :type + t.date :due_date + t.references :user, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/migrate/20250611174428_create_notes.rb b/db/migrate/20250611174428_create_notes.rb new file mode 100644 index 0000000..62c9ec5 --- /dev/null +++ b/db/migrate/20250611174428_create_notes.rb @@ -0,0 +1,13 @@ +class CreateNotes < ActiveRecord::Migration[8.0] + def change + create_table :notes do |t| + t.string :title + t.text :description + t.boolean :pinned + t.boolean :archived + t.references :project, null: false, foreign_key: true + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 59db64e..c94fca2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,9 +10,74 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_06_11_155737) do +ActiveRecord::Schema[8.0].define(version: 2025_06_11_174428) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" + enable_extension "pgcrypto" + + create_table "contacts", force: :cascade do |t| + t.string "name" + t.string "email" + t.string "address" + t.float "geo_long" + t.float "geo_lat" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "notes", force: :cascade do |t| + t.string "title" + t.text "description" + t.boolean "pinned" + t.boolean "archived" + t.bigint "project_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["project_id"], name: "index_notes_on_project_id" + end + + create_table "organizations", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + t.string "name" + t.string "subdomain" + t.string "email_domain" + t.string "logo_url" + t.string "address" + t.string "website" + t.string "phone_number" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["subdomain"], name: "index_organizations_on_subdomain", unique: true + end + + create_table "phone_numbers", force: :cascade do |t| + t.string "phone_number" + t.integer "type" + t.bigint "contact_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["contact_id"], name: "index_phone_numbers_on_contact_id" + end + + create_table "project_categories", force: :cascade do |t| + t.string "title" + t.text "description" + t.integer "order" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "projects", force: :cascade do |t| + t.string "title" + t.string "website" + t.string "email" + t.integer "business_type" + t.bigint "user_id", null: false + t.bigint "project_category_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["project_category_id"], name: "index_projects_on_project_category_id" + t.index ["user_id"], name: "index_projects_on_user_id" + end create_table "sessions", force: :cascade do |t| t.bigint "user_id", null: false @@ -23,13 +88,34 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_11_155737) do t.index ["user_id"], name: "index_sessions_on_user_id" end + create_table "tasks", force: :cascade do |t| + t.string "title" + t.text "description" + t.integer "priority" + t.integer "status" + t.integer "type" + t.date "due_date" + t.bigint "user_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["user_id"], name: "index_tasks_on_user_id" + end + create_table "users", force: :cascade do |t| t.string "email_address", null: false t.string "password_digest", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.uuid "organization_id", null: false t.index ["email_address"], name: "index_users_on_email_address", unique: true + t.index ["organization_id"], name: "index_users_on_organization_id" end + add_foreign_key "notes", "projects" + add_foreign_key "phone_numbers", "contacts" + add_foreign_key "projects", "project_categories" + add_foreign_key "projects", "users" add_foreign_key "sessions", "users" + add_foreign_key "tasks", "users" + add_foreign_key "users", "organizations" end diff --git a/test/fixtures/contacts.yml b/test/fixtures/contacts.yml new file mode 100644 index 0000000..f463886 --- /dev/null +++ b/test/fixtures/contacts.yml @@ -0,0 +1,15 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + name: MyString + email: MyString + address: MyString + geo_long: 1.5 + geo_lat: 1.5 + +two: + name: MyString + email: MyString + address: MyString + geo_long: 1.5 + geo_lat: 1.5 diff --git a/test/fixtures/notes.yml b/test/fixtures/notes.yml new file mode 100644 index 0000000..c83669e --- /dev/null +++ b/test/fixtures/notes.yml @@ -0,0 +1,15 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + title: MyString + description: MyText + pinned: false + archived: false + project: one + +two: + title: MyString + description: MyText + pinned: false + archived: false + project: two diff --git a/test/fixtures/phone_numbers.yml b/test/fixtures/phone_numbers.yml new file mode 100644 index 0000000..e0b51e5 --- /dev/null +++ b/test/fixtures/phone_numbers.yml @@ -0,0 +1,11 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + phone_number: MyString + type: 1 + contact: one + +two: + phone_number: MyString + type: 1 + contact: two diff --git a/test/fixtures/project_categories.yml b/test/fixtures/project_categories.yml new file mode 100644 index 0000000..1566e9b --- /dev/null +++ b/test/fixtures/project_categories.yml @@ -0,0 +1,11 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + title: MyString + description: MyText + order: 1 + +two: + title: MyString + description: MyText + order: 1 diff --git a/test/fixtures/projects.yml b/test/fixtures/projects.yml new file mode 100644 index 0000000..dbcbc47 --- /dev/null +++ b/test/fixtures/projects.yml @@ -0,0 +1,17 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + title: MyString + website: MyString + email: MyString + business_type: 1 + user: one + project_category: one + +two: + title: MyString + website: MyString + email: MyString + business_type: 1 + user: two + project_category: two diff --git a/test/fixtures/tasks.yml b/test/fixtures/tasks.yml new file mode 100644 index 0000000..23eac3b --- /dev/null +++ b/test/fixtures/tasks.yml @@ -0,0 +1,19 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + title: MyString + description: MyText + priority: 1 + status: 1 + type: 1 + due_date: 2025-06-11 + user: one + +two: + title: MyString + description: MyText + priority: 1 + status: 1 + type: 1 + due_date: 2025-06-11 + user: two diff --git a/test/models/contact_test.rb b/test/models/contact_test.rb new file mode 100644 index 0000000..ad10c13 --- /dev/null +++ b/test/models/contact_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class ContactTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/note_test.rb b/test/models/note_test.rb new file mode 100644 index 0000000..a7cec16 --- /dev/null +++ b/test/models/note_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class NoteTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/phone_number_test.rb b/test/models/phone_number_test.rb new file mode 100644 index 0000000..7cb215e --- /dev/null +++ b/test/models/phone_number_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class PhoneNumberTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/project_category_test.rb b/test/models/project_category_test.rb new file mode 100644 index 0000000..1b3f2a8 --- /dev/null +++ b/test/models/project_category_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class ProjectCategoryTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/project_test.rb b/test/models/project_test.rb new file mode 100644 index 0000000..5df4ca4 --- /dev/null +++ b/test/models/project_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class ProjectTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/task_test.rb b/test/models/task_test.rb new file mode 100644 index 0000000..29982eb --- /dev/null +++ b/test/models/task_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class TaskTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end