From de19e675c8a6efff766b57ba37bfeee42c621b14 Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Mon, 26 Jan 2026 14:00:27 +0630
Subject: [PATCH 01/22] added Project model with project_name and Task model
with title&status
---
app/models/project.rb | 2 ++
app/models/task.rb | 2 ++
db/migrate/20260126072711_create_projects.rb | 9 +++++++++
db/migrate/20260126072814_create_tasks.rb | 10 ++++++++++
test/fixtures/projects.yml | 7 +++++++
test/fixtures/tasks.yml | 9 +++++++++
test/models/project_test.rb | 7 +++++++
test/models/task_test.rb | 7 +++++++
8 files changed, 53 insertions(+)
create mode 100644 app/models/project.rb
create mode 100644 app/models/task.rb
create mode 100644 db/migrate/20260126072711_create_projects.rb
create mode 100644 db/migrate/20260126072814_create_tasks.rb
create mode 100644 test/fixtures/projects.yml
create mode 100644 test/fixtures/tasks.yml
create mode 100644 test/models/project_test.rb
create mode 100644 test/models/task_test.rb
diff --git a/app/models/project.rb b/app/models/project.rb
new file mode 100644
index 0000000..9c7fe3f
--- /dev/null
+++ b/app/models/project.rb
@@ -0,0 +1,2 @@
+class Project < ApplicationRecord
+end
diff --git a/app/models/task.rb b/app/models/task.rb
new file mode 100644
index 0000000..3c23424
--- /dev/null
+++ b/app/models/task.rb
@@ -0,0 +1,2 @@
+class Task < ApplicationRecord
+end
diff --git a/db/migrate/20260126072711_create_projects.rb b/db/migrate/20260126072711_create_projects.rb
new file mode 100644
index 0000000..fdfd571
--- /dev/null
+++ b/db/migrate/20260126072711_create_projects.rb
@@ -0,0 +1,9 @@
+class CreateProjects < ActiveRecord::Migration[8.1]
+ def change
+ create_table :projects do |t|
+ t.string :project_name
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20260126072814_create_tasks.rb b/db/migrate/20260126072814_create_tasks.rb
new file mode 100644
index 0000000..8ca7137
--- /dev/null
+++ b/db/migrate/20260126072814_create_tasks.rb
@@ -0,0 +1,10 @@
+class CreateTasks < ActiveRecord::Migration[8.1]
+ def change
+ create_table :tasks do |t|
+ t.string :title
+ t.string :status
+
+ t.timestamps
+ end
+ end
+end
diff --git a/test/fixtures/projects.yml b/test/fixtures/projects.yml
new file mode 100644
index 0000000..0f9d978
--- /dev/null
+++ b/test/fixtures/projects.yml
@@ -0,0 +1,7 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ project_name: MyString
+
+two:
+ project_name: MyString
diff --git a/test/fixtures/tasks.yml b/test/fixtures/tasks.yml
new file mode 100644
index 0000000..e41d61c
--- /dev/null
+++ b/test/fixtures/tasks.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ title: MyString
+ status: MyString
+
+two:
+ title: MyString
+ status: MyString
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
--
2.49.1
From e0ac034974abb481169ad41b8395ad0a3b6c38f7 Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Mon, 26 Jan 2026 14:36:16 +0630
Subject: [PATCH 02/22] migrated db and defined Proj & Task relationship
---
app/helpers/articles_helper.rb | 2 ++
app/models/project.rb | 1 +
app/models/task.rb | 1 +
app/views/articles/index.html.erb | 2 ++
db/schema.rb | 26 ++++++++++++++++++++++++++
5 files changed, 32 insertions(+)
create mode 100644 app/helpers/articles_helper.rb
create mode 100644 app/views/articles/index.html.erb
create mode 100644 db/schema.rb
diff --git a/app/helpers/articles_helper.rb b/app/helpers/articles_helper.rb
new file mode 100644
index 0000000..2968277
--- /dev/null
+++ b/app/helpers/articles_helper.rb
@@ -0,0 +1,2 @@
+module ArticlesHelper
+end
diff --git a/app/models/project.rb b/app/models/project.rb
index 9c7fe3f..16ed525 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1,2 +1,3 @@
class Project < ApplicationRecord
+ has_many :tasks
end
diff --git a/app/models/task.rb b/app/models/task.rb
index 3c23424..1d95805 100644
--- a/app/models/task.rb
+++ b/app/models/task.rb
@@ -1,2 +1,3 @@
class Task < ApplicationRecord
+ balongs_to :project
end
diff --git a/app/views/articles/index.html.erb b/app/views/articles/index.html.erb
new file mode 100644
index 0000000..ab9c552
--- /dev/null
+++ b/app/views/articles/index.html.erb
@@ -0,0 +1,2 @@
+Articles#index
+Find me in app/views/articles/index.html.erb
diff --git a/db/schema.rb b/db/schema.rb
new file mode 100644
index 0000000..2e3d7dd
--- /dev/null
+++ b/db/schema.rb
@@ -0,0 +1,26 @@
+# This file is auto-generated from the current state of the database. Instead
+# of editing this file, please use the migrations feature of Active Record to
+# incrementally modify your database, and then regenerate this schema definition.
+#
+# This file is the source Rails uses to define your schema when running `bin/rails
+# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
+# be faster and is potentially less error prone than running all of your
+# migrations from scratch. Old migrations may fail to apply correctly if those
+# migrations use external dependencies or application code.
+#
+# It's strongly recommended that you check this file into your version control system.
+
+ActiveRecord::Schema[8.1].define(version: 2026_01_26_072814) do
+ create_table "projects", force: :cascade do |t|
+ t.datetime "created_at", null: false
+ t.string "project_name"
+ t.datetime "updated_at", null: false
+ end
+
+ create_table "tasks", force: :cascade do |t|
+ t.datetime "created_at", null: false
+ t.string "status"
+ t.string "title"
+ t.datetime "updated_at", null: false
+ end
+end
--
2.49.1
From 754a76fd5d448546daa4138dc4ffc78fdbf8b2a3 Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Mon, 26 Jan 2026 15:05:12 +0630
Subject: [PATCH 03/22] db migration
---
db/migrate/20260126082816_add_description_to_projects.rb | 5 +++++
db/schema.rb | 3 ++-
2 files changed, 7 insertions(+), 1 deletion(-)
create mode 100644 db/migrate/20260126082816_add_description_to_projects.rb
diff --git a/db/migrate/20260126082816_add_description_to_projects.rb b/db/migrate/20260126082816_add_description_to_projects.rb
new file mode 100644
index 0000000..2692093
--- /dev/null
+++ b/db/migrate/20260126082816_add_description_to_projects.rb
@@ -0,0 +1,5 @@
+class AddDescriptionToProjects < ActiveRecord::Migration[8.1]
+ def change
+ add_column :projects, :description, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 2e3d7dd..7379b52 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,9 +10,10 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.1].define(version: 2026_01_26_072814) do
+ActiveRecord::Schema[8.1].define(version: 2026_01_26_082816) do
create_table "projects", force: :cascade do |t|
t.datetime "created_at", null: false
+ t.string "description"
t.string "project_name"
t.datetime "updated_at", null: false
end
--
2.49.1
From 6bbbc67a3cdd0d4e8bacf59efd6cb0077f906408 Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Mon, 26 Jan 2026 16:45:15 +0630
Subject: [PATCH 04/22] Deleted bots
---
.github/dependabot.yml | 12 ---
.github/workflows/ci.yml | 124 --------------------------
.kamal/hooks/docker-setup.sample | 3 -
.kamal/hooks/post-app-boot.sample | 3 -
.kamal/hooks/post-deploy.sample | 14 ---
.kamal/hooks/post-proxy-reboot.sample | 3 -
.kamal/hooks/pre-app-boot.sample | 3 -
.kamal/hooks/pre-build.sample | 51 -----------
.kamal/hooks/pre-connect.sample | 47 ----------
.kamal/hooks/pre-deploy.sample | 122 -------------------------
.kamal/hooks/pre-proxy-reboot.sample | 3 -
.kamal/secrets | 20 -----
12 files changed, 405 deletions(-)
delete mode 100644 .github/dependabot.yml
delete mode 100644 .github/workflows/ci.yml
delete mode 100755 .kamal/hooks/docker-setup.sample
delete mode 100755 .kamal/hooks/post-app-boot.sample
delete mode 100755 .kamal/hooks/post-deploy.sample
delete mode 100755 .kamal/hooks/post-proxy-reboot.sample
delete mode 100755 .kamal/hooks/pre-app-boot.sample
delete mode 100755 .kamal/hooks/pre-build.sample
delete mode 100755 .kamal/hooks/pre-connect.sample
delete mode 100755 .kamal/hooks/pre-deploy.sample
delete mode 100755 .kamal/hooks/pre-proxy-reboot.sample
delete mode 100644 .kamal/secrets
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index 83610cf..0000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,12 +0,0 @@
-version: 2
-updates:
-- package-ecosystem: bundler
- directory: "/"
- schedule:
- interval: weekly
- open-pull-requests-limit: 10
-- package-ecosystem: github-actions
- directory: "/"
- schedule:
- interval: weekly
- open-pull-requests-limit: 10
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 6824190..0000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,124 +0,0 @@
-name: CI
-
-on:
- pull_request:
- push:
- branches: [ main ]
-
-jobs:
- scan_ruby:
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v6
-
- - name: Set up Ruby
- uses: ruby/setup-ruby@v1
- with:
- bundler-cache: true
-
- - name: Scan for common Rails security vulnerabilities using static analysis
- run: bin/brakeman --no-pager
-
- - name: Scan for known security vulnerabilities in gems used
- run: bin/bundler-audit
-
- scan_js:
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout code
- uses: actions/checkout@v6
-
- - name: Set up Ruby
- uses: ruby/setup-ruby@v1
- with:
- bundler-cache: true
-
- - name: Scan for security vulnerabilities in JavaScript dependencies
- run: bin/importmap audit
-
- lint:
- runs-on: ubuntu-latest
- env:
- RUBOCOP_CACHE_ROOT: tmp/rubocop
- steps:
- - name: Checkout code
- uses: actions/checkout@v6
-
- - name: Set up Ruby
- uses: ruby/setup-ruby@v1
- with:
- bundler-cache: true
-
- - name: Prepare RuboCop cache
- uses: actions/cache@v4
- env:
- DEPENDENCIES_HASH: ${{ hashFiles('.ruby-version', '**/.rubocop.yml', '**/.rubocop_todo.yml', 'Gemfile.lock') }}
- with:
- path: ${{ env.RUBOCOP_CACHE_ROOT }}
- key: rubocop-${{ runner.os }}-${{ env.DEPENDENCIES_HASH }}-${{ github.ref_name == github.event.repository.default_branch && github.run_id || 'default' }}
- restore-keys: |
- rubocop-${{ runner.os }}-${{ env.DEPENDENCIES_HASH }}-
-
- - name: Lint code for consistent style
- run: bin/rubocop -f github
-
- test:
- runs-on: ubuntu-latest
-
- # services:
- # redis:
- # image: valkey/valkey:8
- # ports:
- # - 6379:6379
- # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
- steps:
- - name: Checkout code
- uses: actions/checkout@v6
-
- - name: Set up Ruby
- uses: ruby/setup-ruby@v1
- with:
- bundler-cache: true
-
- - name: Run tests
- env:
- RAILS_ENV: test
- # RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
- # REDIS_URL: redis://localhost:6379/0
- run: bin/rails db:test:prepare test
-
- system-test:
- runs-on: ubuntu-latest
-
- # services:
- # redis:
- # image: valkey/valkey:8
- # ports:
- # - 6379:6379
- # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
- steps:
- - name: Checkout code
- uses: actions/checkout@v6
-
- - name: Set up Ruby
- uses: ruby/setup-ruby@v1
- with:
- bundler-cache: true
-
- - name: Run System Tests
- env:
- RAILS_ENV: test
- # RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }}
- # REDIS_URL: redis://localhost:6379/0
- run: bin/rails db:test:prepare test:system
-
- - name: Keep screenshots from failed system tests
- uses: actions/upload-artifact@v4
- if: failure()
- with:
- name: screenshots
- path: ${{ github.workspace }}/tmp/screenshots
- if-no-files-found: ignore
diff --git a/.kamal/hooks/docker-setup.sample b/.kamal/hooks/docker-setup.sample
deleted file mode 100755
index 2fb07d7..0000000
--- a/.kamal/hooks/docker-setup.sample
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-echo "Docker set up on $KAMAL_HOSTS..."
diff --git a/.kamal/hooks/post-app-boot.sample b/.kamal/hooks/post-app-boot.sample
deleted file mode 100755
index 70f9c4b..0000000
--- a/.kamal/hooks/post-app-boot.sample
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-echo "Booted app version $KAMAL_VERSION on $KAMAL_HOSTS..."
diff --git a/.kamal/hooks/post-deploy.sample b/.kamal/hooks/post-deploy.sample
deleted file mode 100755
index fd364c2..0000000
--- a/.kamal/hooks/post-deploy.sample
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-
-# A sample post-deploy hook
-#
-# These environment variables are available:
-# KAMAL_RECORDED_AT
-# KAMAL_PERFORMER
-# KAMAL_VERSION
-# KAMAL_HOSTS
-# KAMAL_ROLES (if set)
-# KAMAL_DESTINATION (if set)
-# KAMAL_RUNTIME
-
-echo "$KAMAL_PERFORMER deployed $KAMAL_VERSION to $KAMAL_DESTINATION in $KAMAL_RUNTIME seconds"
diff --git a/.kamal/hooks/post-proxy-reboot.sample b/.kamal/hooks/post-proxy-reboot.sample
deleted file mode 100755
index 1435a67..0000000
--- a/.kamal/hooks/post-proxy-reboot.sample
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-echo "Rebooted kamal-proxy on $KAMAL_HOSTS"
diff --git a/.kamal/hooks/pre-app-boot.sample b/.kamal/hooks/pre-app-boot.sample
deleted file mode 100755
index 45f7355..0000000
--- a/.kamal/hooks/pre-app-boot.sample
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-echo "Booting app version $KAMAL_VERSION on $KAMAL_HOSTS..."
diff --git a/.kamal/hooks/pre-build.sample b/.kamal/hooks/pre-build.sample
deleted file mode 100755
index c5a5567..0000000
--- a/.kamal/hooks/pre-build.sample
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/bin/sh
-
-# A sample pre-build hook
-#
-# Checks:
-# 1. We have a clean checkout
-# 2. A remote is configured
-# 3. The branch has been pushed to the remote
-# 4. The version we are deploying matches the remote
-#
-# These environment variables are available:
-# KAMAL_RECORDED_AT
-# KAMAL_PERFORMER
-# KAMAL_VERSION
-# KAMAL_HOSTS
-# KAMAL_ROLES (if set)
-# KAMAL_DESTINATION (if set)
-
-if [ -n "$(git status --porcelain)" ]; then
- echo "Git checkout is not clean, aborting..." >&2
- git status --porcelain >&2
- exit 1
-fi
-
-first_remote=$(git remote)
-
-if [ -z "$first_remote" ]; then
- echo "No git remote set, aborting..." >&2
- exit 1
-fi
-
-current_branch=$(git branch --show-current)
-
-if [ -z "$current_branch" ]; then
- echo "Not on a git branch, aborting..." >&2
- exit 1
-fi
-
-remote_head=$(git ls-remote $first_remote --tags $current_branch | cut -f1)
-
-if [ -z "$remote_head" ]; then
- echo "Branch not pushed to remote, aborting..." >&2
- exit 1
-fi
-
-if [ "$KAMAL_VERSION" != "$remote_head" ]; then
- echo "Version ($KAMAL_VERSION) does not match remote HEAD ($remote_head), aborting..." >&2
- exit 1
-fi
-
-exit 0
diff --git a/.kamal/hooks/pre-connect.sample b/.kamal/hooks/pre-connect.sample
deleted file mode 100755
index 77744bd..0000000
--- a/.kamal/hooks/pre-connect.sample
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env ruby
-
-# A sample pre-connect check
-#
-# Warms DNS before connecting to hosts in parallel
-#
-# These environment variables are available:
-# KAMAL_RECORDED_AT
-# KAMAL_PERFORMER
-# KAMAL_VERSION
-# KAMAL_HOSTS
-# KAMAL_ROLES (if set)
-# KAMAL_DESTINATION (if set)
-# KAMAL_RUNTIME
-
-hosts = ENV["KAMAL_HOSTS"].split(",")
-results = nil
-max = 3
-
-elapsed = Benchmark.realtime do
- results = hosts.map do |host|
- Thread.new do
- tries = 1
-
- begin
- Socket.getaddrinfo(host, 0, Socket::AF_UNSPEC, Socket::SOCK_STREAM, nil, Socket::AI_CANONNAME)
- rescue SocketError
- if tries < max
- puts "Retrying DNS warmup: #{host}"
- tries += 1
- sleep rand
- retry
- else
- puts "DNS warmup failed: #{host}"
- host
- end
- end
-
- tries
- end
- end.map(&:value)
-end
-
-retries = results.sum - hosts.size
-nopes = results.count { |r| r == max }
-
-puts "Prewarmed %d DNS lookups in %.2f sec: %d retries, %d failures" % [ hosts.size, elapsed, retries, nopes ]
diff --git a/.kamal/hooks/pre-deploy.sample b/.kamal/hooks/pre-deploy.sample
deleted file mode 100755
index 05b3055..0000000
--- a/.kamal/hooks/pre-deploy.sample
+++ /dev/null
@@ -1,122 +0,0 @@
-#!/usr/bin/env ruby
-
-# A sample pre-deploy hook
-#
-# Checks the Github status of the build, waiting for a pending build to complete for up to 720 seconds.
-#
-# Fails unless the combined status is "success"
-#
-# These environment variables are available:
-# KAMAL_RECORDED_AT
-# KAMAL_PERFORMER
-# KAMAL_VERSION
-# KAMAL_HOSTS
-# KAMAL_COMMAND
-# KAMAL_SUBCOMMAND
-# KAMAL_ROLES (if set)
-# KAMAL_DESTINATION (if set)
-
-# Only check the build status for production deployments
-if ENV["KAMAL_COMMAND"] == "rollback" || ENV["KAMAL_DESTINATION"] != "production"
- exit 0
-end
-
-require "bundler/inline"
-
-# true = install gems so this is fast on repeat invocations
-gemfile(true, quiet: true) do
- source "https://rubygems.org"
-
- gem "octokit"
- gem "faraday-retry"
-end
-
-MAX_ATTEMPTS = 72
-ATTEMPTS_GAP = 10
-
-def exit_with_error(message)
- $stderr.puts message
- exit 1
-end
-
-class GithubStatusChecks
- attr_reader :remote_url, :git_sha, :github_client, :combined_status
-
- def initialize
- @remote_url = github_repo_from_remote_url
- @git_sha = `git rev-parse HEAD`.strip
- @github_client = Octokit::Client.new(access_token: ENV["GITHUB_TOKEN"])
- refresh!
- end
-
- def refresh!
- @combined_status = github_client.combined_status(remote_url, git_sha)
- end
-
- def state
- combined_status[:state]
- end
-
- def first_status_url
- first_status = combined_status[:statuses].find { |status| status[:state] == state }
- first_status && first_status[:target_url]
- end
-
- def complete_count
- combined_status[:statuses].count { |status| status[:state] != "pending"}
- end
-
- def total_count
- combined_status[:statuses].count
- end
-
- def current_status
- if total_count > 0
- "Completed #{complete_count}/#{total_count} checks, see #{first_status_url} ..."
- else
- "Build not started..."
- end
- end
-
- private
- def github_repo_from_remote_url
- url = `git config --get remote.origin.url`.strip.delete_suffix(".git")
- if url.start_with?("https://github.com/")
- url.delete_prefix("https://github.com/")
- elsif url.start_with?("git@github.com:")
- url.delete_prefix("git@github.com:")
- else
- url
- end
- end
-end
-
-
-$stdout.sync = true
-
-begin
- puts "Checking build status..."
-
- attempts = 0
- checks = GithubStatusChecks.new
-
- loop do
- case checks.state
- when "success"
- puts "Checks passed, see #{checks.first_status_url}"
- exit 0
- when "failure"
- exit_with_error "Checks failed, see #{checks.first_status_url}"
- when "pending"
- attempts += 1
- end
-
- exit_with_error "Checks are still pending, gave up after #{MAX_ATTEMPTS * ATTEMPTS_GAP} seconds" if attempts == MAX_ATTEMPTS
-
- puts checks.current_status
- sleep(ATTEMPTS_GAP)
- checks.refresh!
- end
-rescue Octokit::NotFound
- exit_with_error "Build status could not be found"
-end
diff --git a/.kamal/hooks/pre-proxy-reboot.sample b/.kamal/hooks/pre-proxy-reboot.sample
deleted file mode 100755
index 061f805..0000000
--- a/.kamal/hooks/pre-proxy-reboot.sample
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-echo "Rebooting kamal-proxy on $KAMAL_HOSTS..."
diff --git a/.kamal/secrets b/.kamal/secrets
deleted file mode 100644
index b3089d6..0000000
--- a/.kamal/secrets
+++ /dev/null
@@ -1,20 +0,0 @@
-# Secrets defined here are available for reference under registry/password, env/secret, builder/secrets,
-# and accessories/*/env/secret in config/deploy.yml. All secrets should be pulled from either
-# password manager, ENV, or a file. DO NOT ENTER RAW CREDENTIALS HERE! This file needs to be safe for git.
-
-# Example of extracting secrets from 1password (or another compatible pw manager)
-# SECRETS=$(kamal secrets fetch --adapter 1password --account your-account --from Vault/Item KAMAL_REGISTRY_PASSWORD RAILS_MASTER_KEY)
-# KAMAL_REGISTRY_PASSWORD=$(kamal secrets extract KAMAL_REGISTRY_PASSWORD ${SECRETS})
-# RAILS_MASTER_KEY=$(kamal secrets extract RAILS_MASTER_KEY ${SECRETS})
-
-# Example of extracting secrets from Rails credentials
-# KAMAL_REGISTRY_PASSWORD=$(rails credentials:fetch kamal.registry_password)
-
-# Use a GITHUB_TOKEN if private repositories are needed for the image
-# GITHUB_TOKEN=$(gh config get -h github.com oauth_token)
-
-# Grab the registry password from ENV
-# KAMAL_REGISTRY_PASSWORD=$KAMAL_REGISTRY_PASSWORD
-
-# Improve security by using a password manager. Never check config/master.key into git!
-RAILS_MASTER_KEY=$(cat config/master.key)
--
2.49.1
From f72af5d45f1d17e199ab8e1587189b1025cd2bc3 Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Mon, 26 Jan 2026 21:50:14 +0630
Subject: [PATCH 05/22] Added enum to Task model. Added project to project to
tasks. Changed Status data type in Tasks.
---
Gemfile | 2 ++
Gemfile.lock | 3 +++
app/models/task.rb | 4 +++-
db/migrate/20260126091632_change_status_type_in_tasks.rb | 6 ++++++
db/migrate/20260126094649_add_project_to_tasks.rb | 5 +++++
db/schema.rb | 8 ++++++--
6 files changed, 25 insertions(+), 3 deletions(-)
create mode 100644 db/migrate/20260126091632_change_status_type_in_tasks.rb
create mode 100644 db/migrate/20260126094649_add_project_to_tasks.rb
diff --git a/Gemfile b/Gemfile
index c4aca79..9a38f1f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -64,3 +64,5 @@ group :test do
gem "capybara"
gem "selenium-webdriver"
end
+
+gem "tailwindcss", "~> 0.1.1"
diff --git a/Gemfile.lock b/Gemfile.lock
index 6700a33..a486f73 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -351,6 +351,7 @@ GEM
stimulus-rails (1.3.4)
railties (>= 6.0.0)
stringio (3.2.0)
+ tailwindcss (0.1.1)
thor (1.5.0)
thruster (0.1.17)
thruster (0.1.17-aarch64-linux)
@@ -415,6 +416,7 @@ DEPENDENCIES
solid_queue
sqlite3 (>= 2.1)
stimulus-rails
+ tailwindcss (~> 0.1.1)
thruster
turbo-rails
tzinfo-data
@@ -552,6 +554,7 @@ CHECKSUMS
sshkit (1.25.0) sha256=c8c6543cdb60f91f1d277306d585dd11b6a064cb44eab0972827e4311ff96744
stimulus-rails (1.3.4) sha256=765676ffa1f33af64ce026d26b48e8ffb2e0b94e0f50e9119e11d6107d67cb06
stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
+ tailwindcss (0.1.1) sha256=cdd453b13267d6441cba86cc6bf2b91e06bf01171983d90701b8d9b634d3be75
thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
thruster (0.1.17) sha256=6f3f1de43e22f0162d81cbc363f45ee42a1b8460213856c1a899cbf0d3297235
thruster (0.1.17-aarch64-linux) sha256=1b3a34b2814185c2aeaf835b5ecff5348cdcf8e77809f7a092d46e4b962a16ba
diff --git a/app/models/task.rb b/app/models/task.rb
index 1d95805..d994ea3 100644
--- a/app/models/task.rb
+++ b/app/models/task.rb
@@ -1,3 +1,5 @@
class Task < ApplicationRecord
- balongs_to :project
+ belongs_to :project
+
+ enum :status, { to_do: 0, in_progress: 1, done: 2 }
end
diff --git a/db/migrate/20260126091632_change_status_type_in_tasks.rb b/db/migrate/20260126091632_change_status_type_in_tasks.rb
new file mode 100644
index 0000000..5ac5381
--- /dev/null
+++ b/db/migrate/20260126091632_change_status_type_in_tasks.rb
@@ -0,0 +1,6 @@
+class ChangeStatusTypeInTasks < ActiveRecord::Migration[8.1]
+ def change
+ remove_column :tasks, :status, :string
+ add_column :tasks, :status, :integer, default: 0, null: false
+ end
+end
diff --git a/db/migrate/20260126094649_add_project_to_tasks.rb b/db/migrate/20260126094649_add_project_to_tasks.rb
new file mode 100644
index 0000000..031681a
--- /dev/null
+++ b/db/migrate/20260126094649_add_project_to_tasks.rb
@@ -0,0 +1,5 @@
+class AddProjectToTasks < ActiveRecord::Migration[8.1]
+ def change
+ add_reference :tasks, :project, null: false, foreign_key: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 7379b52..2ea0ef3 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.1].define(version: 2026_01_26_082816) do
+ActiveRecord::Schema[8.1].define(version: 2026_01_26_094649) do
create_table "projects", force: :cascade do |t|
t.datetime "created_at", null: false
t.string "description"
@@ -20,8 +20,12 @@ ActiveRecord::Schema[8.1].define(version: 2026_01_26_082816) do
create_table "tasks", force: :cascade do |t|
t.datetime "created_at", null: false
- t.string "status"
+ t.integer "project_id", null: false
+ t.integer "status", default: 0, null: false
t.string "title"
t.datetime "updated_at", null: false
+ t.index ["project_id"], name: "index_tasks_on_project_id"
end
+
+ add_foreign_key "tasks", "projects"
end
--
2.49.1
From fd0a6fb40835379e62cd9f86080460504ff9378e Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Tue, 27 Jan 2026 10:18:48 +0630
Subject: [PATCH 06/22] Database seeding
---
db/seeds.rb | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/db/seeds.rb b/db/seeds.rb
index 4fbd6ed..24e2157 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -7,3 +7,43 @@
# ["Action", "Comedy", "Drama", "Horror"].each do |genre_name|
# MovieGenre.find_or_create_by!(name: genre_name)
# end
+# db/seeds.rb
+
+Task.destroy_all
+Project.destroy_all
+
+project1 = Project.create!(
+ project_name: "project one",
+ description: "This is project one"
+)
+
+project2 = Project.create!(
+ project_name: "project two",
+ description: "This is project two"
+)
+
+Task.create!(
+ title: "first task of project one",
+ status: :to_do,
+ project: project1
+)
+
+Task.create!(
+ title: "second task of project one",
+ status: :in_progress,
+ project: project1
+)
+
+Task.create!(
+ title: "third task of project one",
+ status: :done,
+ project: project1
+)
+
+Task.create!(
+ title: "first task of project two",
+ status: :to_do,
+ project: project2
+)
+
+puts "Seeding complete"
\ No newline at end of file
--
2.49.1
From 6c3180f203fb81c12381a5ea206705114d136765 Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Tue, 27 Jan 2026 11:43:00 +0630
Subject: [PATCH 07/22] Nested routes, Listed tasks associated with projects
---
app/controllers/projects_controller.rb | 22 ++++++++++++++++++++++
app/helpers/projects_helper.rb | 2 ++
app/views/projects/index.html.erb | 7 +++++++
app/views/projects/new.html.erb | 17 +++++++++++++++++
app/views/projects/show.html.erb | 10 ++++++++++
config/routes.rb | 7 +++++++
6 files changed, 65 insertions(+)
create mode 100644 app/controllers/projects_controller.rb
create mode 100644 app/helpers/projects_helper.rb
create mode 100644 app/views/projects/index.html.erb
create mode 100644 app/views/projects/new.html.erb
create mode 100644 app/views/projects/show.html.erb
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
new file mode 100644
index 0000000..1565773
--- /dev/null
+++ b/app/controllers/projects_controller.rb
@@ -0,0 +1,22 @@
+class ProjectsController < ApplicationController
+ def index
+ @projects = Project.all
+ end
+
+ def show
+ @project = Project.find(params[:id])
+ end
+
+ def new
+ @project = Project.new
+ end
+
+ def create
+ @project = Project.new(project_params)
+ if @project.save
+ redirect_to @project
+ else
+ render :new
+ end
+ end
+end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
new file mode 100644
index 0000000..db5c5ce
--- /dev/null
+++ b/app/helpers/projects_helper.rb
@@ -0,0 +1,2 @@
+module ProjectsHelper
+end
diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb
new file mode 100644
index 0000000..127fe18
--- /dev/null
+++ b/app/views/projects/index.html.erb
@@ -0,0 +1,7 @@
+Projects
+
+
+ <% @projects.each do |project| %>
+ <%= project.project_name %>
+ <% end %>
+
diff --git a/app/views/projects/new.html.erb b/app/views/projects/new.html.erb
new file mode 100644
index 0000000..e9feebd
--- /dev/null
+++ b/app/views/projects/new.html.erb
@@ -0,0 +1,17 @@
+New Project
+
+<%= form_with model: @project do |form| %>
+
+ <%= form.label :project_name %>
+ <%= form.text_field :project_name %>
+
+
+
+ <%= form.label :description %>
+ <%= form.text_area :description %>
+
+
+
+ <%= form.submit %>
+
+<% end %>
\ No newline at end of file
diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb
new file mode 100644
index 0000000..fbb2f53
--- /dev/null
+++ b/app/views/projects/show.html.erb
@@ -0,0 +1,10 @@
+<%= @project.project_name %>
+<%= @project.description %>
+
+
+<% @project.tasks.each do |task| %>
+ -
+ <%= task.title %> (<%= task.status %>)
+
+ <% end %>
+
\ No newline at end of file
diff --git a/config/routes.rb b/config/routes.rb
index 48254e8..a656a21 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,11 @@
Rails.application.routes.draw do
+
+ root "projects#index"
+
+ resources :projects
+
+
+
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
--
2.49.1
From ec595da8892473dd738f35c54473bc67927cbd13 Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Tue, 27 Jan 2026 16:50:25 +0630
Subject: [PATCH 08/22] Added New Task form
---
app/views/tasks/new.html.erb | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 app/views/tasks/new.html.erb
diff --git a/app/views/tasks/new.html.erb b/app/views/tasks/new.html.erb
new file mode 100644
index 0000000..890e5bb
--- /dev/null
+++ b/app/views/tasks/new.html.erb
@@ -0,0 +1,22 @@
+Add new task for "<%= @project.project_name %>"
+
+<%= form_with model: [@project, Task.new], local: true do |task| %>
+
+ <%= task.label :title %>
+ <%= task.text_field :title %>
+ <% @project.tasks.each do |t| %>
+ <% t.errors.full_messages_for(:title).each do |message| %>
+
<%= message %>
+ <% end %>
+ <% end %>
+
+
+
+ <%= task.label :status %>
+ <%= task.select :status, Task.statuses.keys %>
+
+
+
+ <%= task.submit "Create Task" %>
+
+<% end %>
\ No newline at end of file
--
2.49.1
From 142e14743acf683a4d82382e7d072fd119270c10 Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Tue, 27 Jan 2026 16:51:35 +0630
Subject: [PATCH 09/22] CRUD implementation for Projects and Tasks
---
app/controllers/projects_controller.rb | 26 +++++++++++++
app/controllers/tasks_controller.rb | 53 ++++++++++++++++++++++++++
2 files changed, 79 insertions(+)
create mode 100644 app/controllers/tasks_controller.rb
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 1565773..3cbf145 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -19,4 +19,30 @@ class ProjectsController < ApplicationController
render :new
end
end
+
+ def edit
+ @project = Project.find(params[:id])
+ end
+
+ def update
+ @project = Project.find(params[:id])
+ if @project.update(project_params)
+ redirect_to @project
+ else
+ render :edit
+ end
+ end
+
+ def destroy
+ @project = Project.find(params[:id])
+ @project.destroy
+ redirect_to projects_path
+ end
+
+
+ private
+
+ def project_params
+ params.require(:project).permit(:project_name, :description)
+ end
end
diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb
new file mode 100644
index 0000000..3e56d2d
--- /dev/null
+++ b/app/controllers/tasks_controller.rb
@@ -0,0 +1,53 @@
+class TasksController < ApplicationController
+ def index
+ @project = Project.find(params[:project_id])
+ @tasks = @project.tasks
+ end
+
+ def show
+ @project = Project.find(params[:project_id])
+ @task = @project.tasks.find(params[:id])
+ end
+
+ def new
+ @project = Project.find(params[:project_id])
+ @task = @project.tasks.new
+ end
+
+ def create
+ @project = Project.find(params[:project_id])
+ @task = @project.tasks.new(task_params)
+ if @task.save
+ redirect_to project_task_path(@project, @task)
+ else
+ render :new
+ end
+ end
+
+ def edit
+ @project = Project.find(params[:project_id])
+ @task = @project.tasks.find(params[:id])
+ end
+
+ def update
+ @project = Project.find(params[:project_id])
+ @task = @project.tasks.find(params[:id])
+ if @task.update(task_params)
+ redirect_to project_task_path(@project, @task)
+ else
+ render :edit
+ end
+ end
+
+ def destroy
+ @project = Project.find(params[:project_id])
+ @task = @project.tasks.find(params[:id])
+ @task.destroy
+ redirect_to project_tasks_path(@project)
+ end
+
+ private
+ def task_params
+ params.require(:task).permit(:title, :description, :status)
+ end
+end
--
2.49.1
From cfca2bec27f39816b22d7980327c06577d6f1bdb Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Tue, 27 Jan 2026 16:53:13 +0630
Subject: [PATCH 10/22] Configured route for Tasks
---
config/routes.rb | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/config/routes.rb b/config/routes.rb
index a656a21..d7c9dbc 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -2,7 +2,9 @@ Rails.application.routes.draw do
root "projects#index"
- resources :projects
+ resources :projects do
+ resources :tasks
+ end
--
2.49.1
From fbb09e40157f0d011334f1edb68ea6fa4c83fd00 Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Tue, 27 Jan 2026 16:55:07 +0630
Subject: [PATCH 11/22] Validate form data
---
app/models/project.rb | 4 +++-
app/models/task.rb | 2 ++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/app/models/project.rb b/app/models/project.rb
index 16ed525..1259445 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1,3 +1,5 @@
class Project < ApplicationRecord
- has_many :tasks
+ has_many :tasks, dependent: :destroy
+
+ validates :project_name, :description, presence: true
end
diff --git a/app/models/task.rb b/app/models/task.rb
index d994ea3..5f0de52 100644
--- a/app/models/task.rb
+++ b/app/models/task.rb
@@ -2,4 +2,6 @@ class Task < ApplicationRecord
belongs_to :project
enum :status, { to_do: 0, in_progress: 1, done: 2 }
+
+ validates :title, presence: true
end
--
2.49.1
From 910c1912e0eaa3f1cf1c21a45db2296cf3c308b2 Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Tue, 27 Jan 2026 16:57:50 +0630
Subject: [PATCH 12/22] Added CRUD links for projects/tasks and validation
display
---
app/views/projects/index.html.erb | 7 ++++++-
app/views/projects/new.html.erb | 6 ++++++
app/views/projects/show.html.erb | 8 +++++++-
3 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb
index 127fe18..6c15d46 100644
--- a/app/views/projects/index.html.erb
+++ b/app/views/projects/index.html.erb
@@ -2,6 +2,11 @@
<% @projects.each do |project| %>
- <%= project.project_name %>
+
+
+ <%= link_to "Destroy", project_path(project), data: {
+ turbo_method: :delete,
+ turbo_confirm: "Are you sure?"
+ } %>
<% end %>
diff --git a/app/views/projects/new.html.erb b/app/views/projects/new.html.erb
index e9feebd..62a2795 100644
--- a/app/views/projects/new.html.erb
+++ b/app/views/projects/new.html.erb
@@ -4,11 +4,17 @@
<%= form.label :project_name %>
<%= form.text_field :project_name %>
+ <% @project.errors.full_messages_for(:project_name).each do |message| %>
+
<%= message %>
+ <% end %>
<%= form.label :description %>
<%= form.text_area :description %>
+ <% @project.errors.full_messages_for(:description).each do |message| %>
+
<%= message %>
+ <% end %>
diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb
index fbb2f53..d44d6b8 100644
--- a/app/views/projects/show.html.erb
+++ b/app/views/projects/show.html.erb
@@ -6,5 +6,11 @@
<%= task.title %> (<%= task.status %>)
+ <%= link_to "Destroy", project_task_path(@project, task), data: {
+ turbo_method: :delete,
+ turbo_confirm: "Are you sure?"
+ } %>
<% end %>
-
\ No newline at end of file
+
+
+<%= link_to "Add Task", new_project_task_path(@project) %>
\ No newline at end of file
--
2.49.1
From 0f5d105be3497c55f32873d594c42062d3c46a05 Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Wed, 28 Jan 2026 14:12:24 +0630
Subject: [PATCH 13/22] Used Turbo Frames/Streams for adding/deleting tasks
without refresh entire browser
---
app/controllers/tasks_controller.rb | 65 +++++++-------------
app/helpers/tasks_helper.rb | 2 +
app/views/projects/index.html.erb | 3 +
app/views/projects/show.html.erb | 29 +++++----
app/views/tasks/_form.html.erb | 13 ++++
app/views/tasks/create.turbo_stream.erb | 13 ++++
app/views/tasks/destroy.turbo_stream.erb | 1 +
app/views/tasks/new.html.erb | 22 -------
test/controllers/projects_controller_test.rb | 8 +++
test/controllers/tasks_controller_test.rb | 7 +++
10 files changed, 87 insertions(+), 76 deletions(-)
create mode 100644 app/helpers/tasks_helper.rb
create mode 100644 app/views/tasks/_form.html.erb
create mode 100644 app/views/tasks/create.turbo_stream.erb
create mode 100644 app/views/tasks/destroy.turbo_stream.erb
delete mode 100644 app/views/tasks/new.html.erb
create mode 100644 test/controllers/projects_controller_test.rb
create mode 100644 test/controllers/tasks_controller_test.rb
diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb
index 3e56d2d..52b5f46 100644
--- a/app/controllers/tasks_controller.rb
+++ b/app/controllers/tasks_controller.rb
@@ -1,53 +1,34 @@
class TasksController < ApplicationController
- def index
- @project = Project.find(params[:project_id])
- @tasks = @project.tasks
- end
+ before_action :set_project
- def show
- @project = Project.find(params[:project_id])
- @task = @project.tasks.find(params[:id])
- end
+ def create
+ @task = @project.tasks.build(task_params)
- def new
- @project = Project.find(params[:project_id])
- @task = @project.tasks.new
- end
-
- def create
- @project = Project.find(params[:project_id])
- @task = @project.tasks.new(task_params)
- if @task.save
- redirect_to project_task_path(@project, @task)
+ if @task.save
+ respond_to do |format|
+ format.turbo_stream
+ format.html { redirect_to @project, notice: "Task created!" }
+ end
else
- render :new
+ respond_to do |format|
+ format.turbo_stream { render turbo_stream: turbo_stream.replace("new_task", partial: "tasks/form", locals: { project: @project, task: @task }) }
+ format.html { render "projects/show", status: :unprocessable_entity }
end
end
+ end
- def edit
- @project = Project.find(params[:project_id])
- @task = @project.tasks.find(params[:id])
- end
+ def destroy
+ @task = @project.tasks.find(params[:id])
+ @task.destroy
+ end
- def update
- @project = Project.find(params[:project_id])
- @task = @project.tasks.find(params[:id])
- if @task.update(task_params)
- redirect_to project_task_path(@project, @task)
- else
- render :edit
- end
- end
+ private
- def destroy
- @project = Project.find(params[:project_id])
- @task = @project.tasks.find(params[:id])
- @task.destroy
- redirect_to project_tasks_path(@project)
- end
+ def set_project
+ @project = Project.find(params[:project_id])
+ end
- private
- def task_params
- params.require(:task).permit(:title, :description, :status)
- end
+ def task_params
+ params.require(:task).permit(:title, :status)
+ end
end
diff --git a/app/helpers/tasks_helper.rb b/app/helpers/tasks_helper.rb
new file mode 100644
index 0000000..ce894d0
--- /dev/null
+++ b/app/helpers/tasks_helper.rb
@@ -0,0 +1,2 @@
+module TasksHelper
+end
diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb
index 6c15d46..6951f83 100644
--- a/app/views/projects/index.html.erb
+++ b/app/views/projects/index.html.erb
@@ -10,3 +10,6 @@
} %>
<% end %>
+
+
+<%= link_to "Add Project", new_project_path %>
\ No newline at end of file
diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb
index d44d6b8..1583b79 100644
--- a/app/views/projects/show.html.erb
+++ b/app/views/projects/show.html.erb
@@ -1,16 +1,21 @@
<%= @project.project_name %>
<%= @project.description %>
-
-<% @project.tasks.each do |task| %>
- -
- <%= task.title %> (<%= task.status %>)
-
- <%= link_to "Destroy", project_task_path(@project, task), data: {
- turbo_method: :delete,
- turbo_confirm: "Are you sure?"
- } %>
- <% end %>
-
+
+
+ <% @project.tasks.each do |task| %>
+ -
+ <%= task.title %> (<%= task.status %>)
+ <%= link_to "Destroy", project_task_path(@project, task), data: {
+ turbo_method: :delete,
+ turbo_confirm: "Are you sure?"
+ } %>
+
+ <% end %>
+
+
-<%= link_to "Add Task", new_project_task_path(@project) %>
\ No newline at end of file
+
+
+ <%= render partial: "tasks/form", locals: { project: @project, task: Task.new } %>
+
\ No newline at end of file
diff --git a/app/views/tasks/_form.html.erb b/app/views/tasks/_form.html.erb
new file mode 100644
index 0000000..f3c7ef7
--- /dev/null
+++ b/app/views/tasks/_form.html.erb
@@ -0,0 +1,13 @@
+<%= form_with model: [project, task], local: false do |f| %>
+
+ <%= f.label :title %>
+ <%= f.text_field :title %>
+
+
+
+ <%= f.label :status %>
+ <%= f.select :status, Task.statuses.keys %>
+
+
+ <%= f.submit "Create Task" %>
+<% end %>
diff --git a/app/views/tasks/create.turbo_stream.erb b/app/views/tasks/create.turbo_stream.erb
new file mode 100644
index 0000000..24cb5e2
--- /dev/null
+++ b/app/views/tasks/create.turbo_stream.erb
@@ -0,0 +1,13 @@
+<%= turbo_stream.append "tasks_list" do %>
+
+ <%= @task.title %> (<%= @task.status %>)
+ <%= link_to "Destroy", project_task_path(@project, @task), data: {
+ turbo_method: :delete,
+ turbo_confirm: "Are you sure?"
+ } %>
+
+<% end %>
+
+<%= turbo_stream.replace "new_task" do %>
+ <%= render partial: "tasks/form", locals: { project: @project, task: Task.new } %>
+<% end %>
diff --git a/app/views/tasks/destroy.turbo_stream.erb b/app/views/tasks/destroy.turbo_stream.erb
new file mode 100644
index 0000000..84da1cb
--- /dev/null
+++ b/app/views/tasks/destroy.turbo_stream.erb
@@ -0,0 +1 @@
+<%= turbo_stream.remove dom_id(@task) %>
\ No newline at end of file
diff --git a/app/views/tasks/new.html.erb b/app/views/tasks/new.html.erb
deleted file mode 100644
index 890e5bb..0000000
--- a/app/views/tasks/new.html.erb
+++ /dev/null
@@ -1,22 +0,0 @@
-Add new task for "<%= @project.project_name %>"
-
-<%= form_with model: [@project, Task.new], local: true do |task| %>
-
- <%= task.label :title %>
- <%= task.text_field :title %>
- <% @project.tasks.each do |t| %>
- <% t.errors.full_messages_for(:title).each do |message| %>
-
<%= message %>
- <% end %>
- <% end %>
-
-
-
- <%= task.label :status %>
- <%= task.select :status, Task.statuses.keys %>
-
-
-
- <%= task.submit "Create Task" %>
-
-<% end %>
\ No newline at end of file
diff --git a/test/controllers/projects_controller_test.rb b/test/controllers/projects_controller_test.rb
new file mode 100644
index 0000000..4305777
--- /dev/null
+++ b/test/controllers/projects_controller_test.rb
@@ -0,0 +1,8 @@
+require "test_helper"
+
+class ProjectsControllerTest < ActionDispatch::IntegrationTest
+ test "should get index" do
+ get projects_index_url
+ assert_response :success
+ end
+end
diff --git a/test/controllers/tasks_controller_test.rb b/test/controllers/tasks_controller_test.rb
new file mode 100644
index 0000000..a60ed4a
--- /dev/null
+++ b/test/controllers/tasks_controller_test.rb
@@ -0,0 +1,7 @@
+require "test_helper"
+
+class TasksControllerTest < ActionDispatch::IntegrationTest
+ # test "the truth" do
+ # assert true
+ # end
+end
--
2.49.1
From 7c9e164a9a77b27265e96553c7243f93fbfc008d Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Wed, 28 Jan 2026 14:59:13 +0630
Subject: [PATCH 14/22] Added complete button
---
app/controllers/tasks_controller.rb | 10 ++++++++++
app/views/projects/show.html.erb | 21 +++++++++++++--------
app/views/tasks/_task.html.erb | 16 ++++++++++++++++
app/views/tasks/complete.turbo_stream.erb | 10 ++++++++++
app/views/tasks/create.turbo_stream.erb | 8 +-------
config/routes.rb | 6 +++++-
6 files changed, 55 insertions(+), 16 deletions(-)
create mode 100644 app/views/tasks/_task.html.erb
create mode 100644 app/views/tasks/complete.turbo_stream.erb
diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb
index 52b5f46..203770d 100644
--- a/app/controllers/tasks_controller.rb
+++ b/app/controllers/tasks_controller.rb
@@ -22,6 +22,16 @@ class TasksController < ApplicationController
@task.destroy
end
+ def complete
+ @task = @project.tasks.find(params[:id])
+ @task.update(status: "done")
+
+ respond_to do |format|
+ format.turbo_stream
+ format.html { redirect_to @project }
+ end
+end
+
private
def set_project
diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb
index 1583b79..123e6c2 100644
--- a/app/views/projects/show.html.erb
+++ b/app/views/projects/show.html.erb
@@ -4,18 +4,23 @@
<% @project.tasks.each do |task| %>
- -
- <%= task.title %> (<%= task.status %>)
- <%= link_to "Destroy", project_task_path(@project, task), data: {
- turbo_method: :delete,
- turbo_confirm: "Are you sure?"
- } %>
-
+
+ -
+ <%= task.title %> (<%= task.status %>)
+
+ <%= link_to "Destroy", project_task_path(@project, task), data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %>
+
+ <% unless task.status == "done" %>
+ <%= button_to "Complete", complete_project_task_path(@project, task), method: :patch, form: { data: { turbo_frame: dom_id(task) } } %>
+ <% end %>
+
+
<% end %>
-
+
+ Add New Task
<%= render partial: "tasks/form", locals: { project: @project, task: Task.new } %>
\ No newline at end of file
diff --git a/app/views/tasks/_task.html.erb b/app/views/tasks/_task.html.erb
new file mode 100644
index 0000000..77098b5
--- /dev/null
+++ b/app/views/tasks/_task.html.erb
@@ -0,0 +1,16 @@
+
+
+ <%= task.title %> (<%= task.status %>)
+
+ <%= link_to "Destroy", project_task_path(task.project, task), data: {
+ turbo_method: :delete,
+ turbo_confirm: "Are you sure?"
+ } %>
+
+ <% unless task.status == "completed" %>
+ <%= button_to "Complete", complete_project_task_path(task.project, task),
+ method: :patch,
+ form: { data: { turbo_frame: dom_id(task) } } %>
+ <% end %>
+
+
diff --git a/app/views/tasks/complete.turbo_stream.erb b/app/views/tasks/complete.turbo_stream.erb
new file mode 100644
index 0000000..058bf2a
--- /dev/null
+++ b/app/views/tasks/complete.turbo_stream.erb
@@ -0,0 +1,10 @@
+<%= turbo_stream.replace dom_id(@task) do %>
+
+ <%= @task.title %> (<%= @task.status %>)
+
+ <%= link_to "Destroy", project_task_path(@project, @task), data: {
+ turbo_method: :delete,
+ turbo_confirm: "Are you sure?"
+ } %>
+
+<% end %>
diff --git a/app/views/tasks/create.turbo_stream.erb b/app/views/tasks/create.turbo_stream.erb
index 24cb5e2..f63494f 100644
--- a/app/views/tasks/create.turbo_stream.erb
+++ b/app/views/tasks/create.turbo_stream.erb
@@ -1,11 +1,5 @@
<%= turbo_stream.append "tasks_list" do %>
-
- <%= @task.title %> (<%= @task.status %>)
- <%= link_to "Destroy", project_task_path(@project, @task), data: {
- turbo_method: :delete,
- turbo_confirm: "Are you sure?"
- } %>
-
+ <%= render partial: "tasks/task", locals: { task: @task } %>
<% end %>
<%= turbo_stream.replace "new_task" do %>
diff --git a/config/routes.rb b/config/routes.rb
index d7c9dbc..fa9fbf6 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -3,7 +3,11 @@ Rails.application.routes.draw do
root "projects#index"
resources :projects do
- resources :tasks
+ resources :tasks do
+ member do
+ patch :complete
+ end
+ end
end
--
2.49.1
From e3da79930c0e2a177123394e4bc6f33b33aeb08d Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Wed, 28 Jan 2026 15:27:17 +0630
Subject: [PATCH 15/22] Render 'complete' button only if the task's status is
not 'done'.
---
app/controllers/tasks_controller.rb | 23 ++++++++++++++++-------
app/views/tasks/_task.html.erb | 17 ++++++++---------
2 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb
index 203770d..b4d7211 100644
--- a/app/controllers/tasks_controller.rb
+++ b/app/controllers/tasks_controller.rb
@@ -18,19 +18,28 @@ class TasksController < ApplicationController
end
def destroy
- @task = @project.tasks.find(params[:id])
+ @task = @project.tasks.find_by(id: params[:id])
+ return unless @task
+
+ task_dom_id = "task_#{@task.id}"
@task.destroy
+
+ respond_to do |format|
+ format.turbo_stream { render turbo_stream: turbo_stream.remove(task_dom_id) }
+ format.html { redirect_to @project }
+ end
end
+
def complete
- @task = @project.tasks.find(params[:id])
- @task.update(status: "done")
+ @task = @project.tasks.find(params[:id])
+ @task.update(status: "done")
- respond_to do |format|
- format.turbo_stream
- format.html { redirect_to @project }
+ respond_to do |format|
+ format.turbo_stream
+ format.html { redirect_to @project }
+ end
end
-end
private
diff --git a/app/views/tasks/_task.html.erb b/app/views/tasks/_task.html.erb
index 77098b5..750ef9e 100644
--- a/app/views/tasks/_task.html.erb
+++ b/app/views/tasks/_task.html.erb
@@ -2,15 +2,14 @@
<%= task.title %> (<%= task.status %>)
- <%= link_to "Destroy", project_task_path(task.project, task), data: {
- turbo_method: :delete,
- turbo_confirm: "Are you sure?"
- } %>
+ <%= link_to "Destroy", project_task_path(task.project, task),
+ data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %>
- <% unless task.status == "completed" %>
- <%= button_to "Complete", complete_project_task_path(task.project, task),
- method: :patch,
- form: { data: { turbo_frame: dom_id(task) } } %>
+ <% unless task.status == "done" %>
+ <%= button_to "Complete",
+ complete_project_task_path(task.project, task),
+ method: :patch,
+ form: { data: { turbo_frame: dom_id(task) } } %>
<% end %>
-
+
\ No newline at end of file
--
2.49.1
From 8a7d1716e268ac3f2a40ce93dbaf9b93a285cc4f Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Thu, 29 Jan 2026 11:13:04 +0630
Subject: [PATCH 16/22] Use find_by and task_dom_id to prevent errors when
deleting tasks.
---
app/controllers/tasks_controller.rb | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb
index 203770d..f72a3c2 100644
--- a/app/controllers/tasks_controller.rb
+++ b/app/controllers/tasks_controller.rb
@@ -18,8 +18,16 @@ class TasksController < ApplicationController
end
def destroy
- @task = @project.tasks.find(params[:id])
+ @task = @project.tasks.find_by(id: params[:id])
+ return unless @task
+
+ task_dom_id = "task_#{@task.id}"
@task.destroy
+
+ respond_to do |format|
+ format.turbo_stream { render turbo_stream: turbo_stream.remove(task_dom_id) }
+ format.html { redirect_to @project }
+ end
end
def complete
--
2.49.1
From 8a94b0795596e7a530d90a7157efb042c713aed6 Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Fri, 30 Jan 2026 09:30:32 +0630
Subject: [PATCH 17/22] Fixed turbo frame and stream bug
---
app/views/projects/show.html.erb | 26 +++++++++--------------
app/views/tasks/_task.html.erb | 21 +++++++++++-------
app/views/tasks/complete.turbo_stream.erb | 9 +-------
app/views/tasks/create.turbo_stream.erb | 6 +++---
4 files changed, 27 insertions(+), 35 deletions(-)
diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb
index 123e6c2..908d09d 100644
--- a/app/views/projects/show.html.erb
+++ b/app/views/projects/show.html.erb
@@ -1,26 +1,20 @@
<%= @project.project_name %>
<%= @project.description %>
-
+<%= turbo_frame_tag "tasks" do %>
- <% @project.tasks.each do |task| %>
-
- -
- <%= task.title %> (<%= task.status %>)
+ <% # First: pending or in-progress tasks %>
+ <%= render @project.tasks.reject { |t| t.status == "done" } %>
- <%= link_to "Destroy", project_task_path(@project, task), data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %>
-
- <% unless task.status == "done" %>
- <%= button_to "Complete", complete_project_task_path(@project, task), method: :patch, form: { data: { turbo_frame: dom_id(task) } } %>
- <% end %>
-
-
+ <% done_tasks = @project.tasks.select { |t| t.status == "done" } %>
+ <% if done_tasks.any? %>
+ <%= render done_tasks %>
<% end %>
-
+<% end %>
-
+<%= turbo_frame_tag "new_task" do %>
Add New Task
- <%= render partial: "tasks/form", locals: { project: @project, task: Task.new } %>
-
\ No newline at end of file
+ <%= render "tasks/form", project: @project, task: Task.new %>
+<% end %>
diff --git a/app/views/tasks/_task.html.erb b/app/views/tasks/_task.html.erb
index 750ef9e..7edd2e7 100644
--- a/app/views/tasks/_task.html.erb
+++ b/app/views/tasks/_task.html.erb
@@ -1,15 +1,20 @@
-
- <%= task.title %> (<%= task.status %>)
-
- <%= link_to "Destroy", project_task_path(task.project, task),
- data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %>
-
+
+ <%= task.title %>
+ <%= task.status.titleize %>
+
<% unless task.status == "done" %>
<%= button_to "Complete",
complete_project_task_path(task.project, task),
method: :patch,
- form: { data: { turbo_frame: dom_id(task) } } %>
+ data: { turbo_frame: dom_id(task) },
+ class: "task-btn complete-btn" %>
<% end %>
-
+
+ <%= link_to "Destroy",
+ project_task_path(task.project, task),
+ data: { turbo_method: :delete, turbo_confirm: "Are you sure?" },
+ class: "task-btn destroy-btn" %>
+
+
\ No newline at end of file
diff --git a/app/views/tasks/complete.turbo_stream.erb b/app/views/tasks/complete.turbo_stream.erb
index 058bf2a..34efcab 100644
--- a/app/views/tasks/complete.turbo_stream.erb
+++ b/app/views/tasks/complete.turbo_stream.erb
@@ -1,10 +1,3 @@
<%= turbo_stream.replace dom_id(@task) do %>
-
- <%= @task.title %> (<%= @task.status %>)
-
- <%= link_to "Destroy", project_task_path(@project, @task), data: {
- turbo_method: :delete,
- turbo_confirm: "Are you sure?"
- } %>
-
+ <%= render @task %>
<% end %>
diff --git a/app/views/tasks/create.turbo_stream.erb b/app/views/tasks/create.turbo_stream.erb
index f63494f..9ec72d4 100644
--- a/app/views/tasks/create.turbo_stream.erb
+++ b/app/views/tasks/create.turbo_stream.erb
@@ -1,7 +1,7 @@
-<%= turbo_stream.append "tasks_list" do %>
- <%= render partial: "tasks/task", locals: { task: @task } %>
+<%= turbo_stream.append "tasks" do %>
+ <%= render @task %>
<% end %>
<%= turbo_stream.replace "new_task" do %>
- <%= render partial: "tasks/form", locals: { project: @project, task: Task.new } %>
+ <%= render "tasks/form", project: @project, task: Task.new %>
<% end %>
--
2.49.1
From 7f6123a6e68cf02d7c7c23497adc96642408b49d Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Fri, 30 Jan 2026 09:38:32 +0630
Subject: [PATCH 18/22] Show Done tasks and Fixed turbo frame and stream bug
---
app/views/projects/show.html.erb | 26 +++++++++--------------
app/views/tasks/_task.html.erb | 21 +++++++++++-------
app/views/tasks/complete.turbo_stream.erb | 9 +-------
app/views/tasks/create.turbo_stream.erb | 6 +++---
4 files changed, 27 insertions(+), 35 deletions(-)
diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb
index 123e6c2..908d09d 100644
--- a/app/views/projects/show.html.erb
+++ b/app/views/projects/show.html.erb
@@ -1,26 +1,20 @@
<%= @project.project_name %>
<%= @project.description %>
-
+<%= turbo_frame_tag "tasks" do %>
- <% @project.tasks.each do |task| %>
-
- -
- <%= task.title %> (<%= task.status %>)
+ <% # First: pending or in-progress tasks %>
+ <%= render @project.tasks.reject { |t| t.status == "done" } %>
- <%= link_to "Destroy", project_task_path(@project, task), data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %>
-
- <% unless task.status == "done" %>
- <%= button_to "Complete", complete_project_task_path(@project, task), method: :patch, form: { data: { turbo_frame: dom_id(task) } } %>
- <% end %>
-
-
+ <% done_tasks = @project.tasks.select { |t| t.status == "done" } %>
+ <% if done_tasks.any? %>
+ <%= render done_tasks %>
<% end %>
-
+<% end %>
-
+<%= turbo_frame_tag "new_task" do %>
Add New Task
- <%= render partial: "tasks/form", locals: { project: @project, task: Task.new } %>
-
\ No newline at end of file
+ <%= render "tasks/form", project: @project, task: Task.new %>
+<% end %>
diff --git a/app/views/tasks/_task.html.erb b/app/views/tasks/_task.html.erb
index 750ef9e..7edd2e7 100644
--- a/app/views/tasks/_task.html.erb
+++ b/app/views/tasks/_task.html.erb
@@ -1,15 +1,20 @@
-
- <%= task.title %> (<%= task.status %>)
-
- <%= link_to "Destroy", project_task_path(task.project, task),
- data: { turbo_method: :delete, turbo_confirm: "Are you sure?" } %>
-
+
+ <%= task.title %>
+ <%= task.status.titleize %>
+
<% unless task.status == "done" %>
<%= button_to "Complete",
complete_project_task_path(task.project, task),
method: :patch,
- form: { data: { turbo_frame: dom_id(task) } } %>
+ data: { turbo_frame: dom_id(task) },
+ class: "task-btn complete-btn" %>
<% end %>
-
+
+ <%= link_to "Destroy",
+ project_task_path(task.project, task),
+ data: { turbo_method: :delete, turbo_confirm: "Are you sure?" },
+ class: "task-btn destroy-btn" %>
+
+
\ No newline at end of file
diff --git a/app/views/tasks/complete.turbo_stream.erb b/app/views/tasks/complete.turbo_stream.erb
index 058bf2a..34efcab 100644
--- a/app/views/tasks/complete.turbo_stream.erb
+++ b/app/views/tasks/complete.turbo_stream.erb
@@ -1,10 +1,3 @@
<%= turbo_stream.replace dom_id(@task) do %>
-
- <%= @task.title %> (<%= @task.status %>)
-
- <%= link_to "Destroy", project_task_path(@project, @task), data: {
- turbo_method: :delete,
- turbo_confirm: "Are you sure?"
- } %>
-
+ <%= render @task %>
<% end %>
diff --git a/app/views/tasks/create.turbo_stream.erb b/app/views/tasks/create.turbo_stream.erb
index f63494f..9ec72d4 100644
--- a/app/views/tasks/create.turbo_stream.erb
+++ b/app/views/tasks/create.turbo_stream.erb
@@ -1,7 +1,7 @@
-<%= turbo_stream.append "tasks_list" do %>
- <%= render partial: "tasks/task", locals: { task: @task } %>
+<%= turbo_stream.append "tasks" do %>
+ <%= render @task %>
<% end %>
<%= turbo_stream.replace "new_task" do %>
- <%= render partial: "tasks/form", locals: { project: @project, task: Task.new } %>
+ <%= render "tasks/form", project: @project, task: Task.new %>
<% end %>
--
2.49.1
From 85937a3fcd3c965d696e8d4540651073289a21c9 Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Fri, 30 Jan 2026 09:46:20 +0630
Subject: [PATCH 19/22] Make project list and its destroy buttons in 1 line
---
app/views/projects/index.html.erb | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb
index 6951f83..507054a 100644
--- a/app/views/projects/index.html.erb
+++ b/app/views/projects/index.html.erb
@@ -2,12 +2,13 @@
<% @projects.each do |project| %>
-
-
- <%= link_to "Destroy", project_path(project), data: {
- turbo_method: :delete,
- turbo_confirm: "Are you sure?"
- } %>
+ - <%= link_to project.project_name, project_path(project) %>
+
+ <%= link_to "Destroy", project_path(project), data: {
+ turbo_method: :delete,
+ turbo_confirm: "Are you sure?"
+ } %>
+
<% end %>
--
2.49.1
From 6716129d4a2b0cb302ff4a8645f3384e975c762b Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Fri, 30 Jan 2026 09:48:34 +0630
Subject: [PATCH 20/22] Added css for body & lists. Make done tasks obvious.
---
app/assets/stylesheets/application.css | 42 ++++++++++++++++++++------
1 file changed, 32 insertions(+), 10 deletions(-)
diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css
index fe93333..54cec86 100644
--- a/app/assets/stylesheets/application.css
+++ b/app/assets/stylesheets/application.css
@@ -1,10 +1,32 @@
-/*
- * This is a manifest file that'll be compiled into application.css.
- *
- * With Propshaft, assets are served efficiently without preprocessing steps. You can still include
- * application-wide styles in this file, but keep in mind that CSS precedence will follow the standard
- * cascading order, meaning styles declared later in the document or manifest will override earlier ones,
- * depending on specificity.
- *
- * Consider organizing styles into separate files for maintainability.
- */
+/* application.css */
+
+body {
+ font-family: sans-serif;
+ background-color: #f9f9f9;
+ margin: 18px;
+ padding: 0;
+}
+
+li {
+ border: 1px solid #ddd;
+ border-radius: 8px;
+ padding: 10px;
+ margin-bottom: 10px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.task-actions {
+ display: flex;
+ gap: 8px;
+}
+
+.done-task {
+ background-color: #f0f0f0;
+ color: #888;
+}
+
+.done-task .task-title {
+ text-decoration: line-through;
+}
\ No newline at end of file
--
2.49.1
From 4c30a133d364f618d5d00baec6dd4725266b3557 Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Fri, 30 Jan 2026 11:29:17 +0630
Subject: [PATCH 21/22] Remove 'done' from task form. Add newest to top. Move
done tasks to bottom
---
app/views/projects/show.html.erb | 18 ++++++++++--------
app/views/tasks/_form.html.erb | 4 +++-
app/views/tasks/_task.html.erb | 2 --
app/views/tasks/complete.turbo_stream.erb | 2 +-
app/views/tasks/create.turbo_stream.erb | 6 +++---
5 files changed, 17 insertions(+), 15 deletions(-)
diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb
index 908d09d..54609b0 100644
--- a/app/views/projects/show.html.erb
+++ b/app/views/projects/show.html.erb
@@ -2,19 +2,21 @@
<%= @project.description %>
<%= turbo_frame_tag "tasks" do %>
-
- <% # First: pending or in-progress tasks %>
- <%= render @project.tasks.reject { |t| t.status == "done" } %>
- <% done_tasks = @project.tasks.select { |t| t.status == "done" } %>
- <% if done_tasks.any? %>
- <%= render done_tasks %>
+
+ <% @project.tasks.where(status: %w[to_do in_progress]).each do |task| %>
+ <%= render partial: "tasks/task", locals: { task: task } %>
<% end %>
+
+
+ <% @project.tasks.where(status: "done").each do |task| %>
+ <%= render partial: "tasks/task", locals: { task: task } %>
+ <% end %>
+
+
<% end %>
<%= turbo_frame_tag "new_task" do %>
-
- Add New Task
<%= render "tasks/form", project: @project, task: Task.new %>
<% end %>
diff --git a/app/views/tasks/_form.html.erb b/app/views/tasks/_form.html.erb
index f3c7ef7..a6a22ea 100644
--- a/app/views/tasks/_form.html.erb
+++ b/app/views/tasks/_form.html.erb
@@ -1,3 +1,5 @@
+Add New Task
+
<%= form_with model: [project, task], local: false do |f| %>
<%= f.label :title %>
@@ -6,7 +8,7 @@
<%= f.label :status %>
- <%= f.select :status, Task.statuses.keys %>
+ <%= f.select :status, Task.statuses.keys.reject { |s| s == "done" } %>
<%= f.submit "Create Task" %>
diff --git a/app/views/tasks/_task.html.erb b/app/views/tasks/_task.html.erb
index 7edd2e7..efd7312 100644
--- a/app/views/tasks/_task.html.erb
+++ b/app/views/tasks/_task.html.erb
@@ -1,4 +1,3 @@
-
-
<%= task.title %>
<%= task.status.titleize %>
@@ -17,4 +16,3 @@
class: "task-btn destroy-btn" %>
-
\ No newline at end of file
diff --git a/app/views/tasks/complete.turbo_stream.erb b/app/views/tasks/complete.turbo_stream.erb
index 34efcab..7b6386f 100644
--- a/app/views/tasks/complete.turbo_stream.erb
+++ b/app/views/tasks/complete.turbo_stream.erb
@@ -1,3 +1,3 @@
<%= turbo_stream.replace dom_id(@task) do %>
- <%= render @task %>
+ <%= render partial: "tasks/task", locals: { task: @task } %>
<% end %>
diff --git a/app/views/tasks/create.turbo_stream.erb b/app/views/tasks/create.turbo_stream.erb
index 9ec72d4..67cfd7f 100644
--- a/app/views/tasks/create.turbo_stream.erb
+++ b/app/views/tasks/create.turbo_stream.erb
@@ -1,6 +1,6 @@
-<%= turbo_stream.append "tasks" do %>
- <%= render @task %>
-<% end %>
+<%= turbo_stream.prepend "active_tasks" do %>
+ <%= render partial: "tasks/task", locals: { task: @task } %>
+ <% end %>
<%= turbo_stream.replace "new_task" do %>
<%= render "tasks/form", project: @project, task: Task.new %>
--
2.49.1
From 5b298a1e5963cf1c43cb1f74e8b94dea782e314f Mon Sep 17 00:00:00 2001
From: KaungKaung
Date: Fri, 30 Jan 2026 12:04:49 +0630
Subject: [PATCH 22/22] Add done task percentage
---
app/views/projects/index.html.erb | 41 ++++++++++++++++++++++---------
1 file changed, 30 insertions(+), 11 deletions(-)
diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb
index 507054a..071eb3a 100644
--- a/app/views/projects/index.html.erb
+++ b/app/views/projects/index.html.erb
@@ -1,16 +1,35 @@
Projects
-
- <% @projects.each do |project| %>
- - <%= link_to project.project_name, project_path(project) %>
+
+
+ <% @projects.each do |project| %>
+ <% total_tasks = project.tasks.count %>
+ <% done_tasks_count = project.tasks.where(status: "done").count %>
- <%= link_to "Destroy", project_path(project), data: {
- turbo_method: :delete,
- turbo_confirm: "Are you sure?"
- } %>
-
- <% end %>
-
+
-
+
+ <%= link_to project.project_name, project_path(project) %>
+
+
+
+
+ <%= link_to "Destroy", project_path(project), data: {
+ turbo_method: :delete,
+ turbo_confirm: "Are you sure?"
+ } %>
+
+
+
+ <% if total_tasks.zero? %>
+ No tasks yet
+ <% else %>
+ Done tasks: <%= ((done_tasks_count.to_f / total_tasks) * 100).round %>%
+ <% end %>
+
+
+ <% end %>
+
+
-<%= link_to "Add Project", new_project_path %>
\ No newline at end of file
+<%= link_to "Add Project", new_project_path %>
--
2.49.1