Skip to content

Commit 8bed1e2

Browse files
committed
Create topics
1 parent 5b094df commit 8bed1e2

File tree

7 files changed

+184
-6
lines changed

7 files changed

+184
-6
lines changed

app/controllers/topics_controller.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,26 @@ def show
2323
])
2424
render inertia: { topic: }
2525
end
26+
27+
def new
28+
topic = Topic.new(category_id: params.dig(:topic, :category_id))
29+
30+
render inertia: { topic: }
31+
end
32+
33+
def create
34+
topic = current_user.topics.build(topic_params)
35+
36+
if topic.save(context: :form_submission)
37+
redirect_to topic, notice: "Topic created successfully"
38+
else
39+
redirect_to new_topic_path, inertia: { errors: topic.errors }
40+
end
41+
end
42+
43+
private
44+
45+
def topic_params
46+
params.require(:topic).permit(:title, :category_id, messages_attributes: [ :body ])
47+
end
2648
end

app/frontend/pages/categories/show.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Link } from "@inertiajs/react";
12
import TopicsTable from "../../components/TopicsTable"
23
import AppLayout from "../../layouts/AppLayout"
34
import { Category } from "../../types"
@@ -7,7 +8,17 @@ function CategoriesShow({ category }: { category: Category }) {
78

89
return (
910
<AppLayout>
10-
<h1 className="text-2xl font-bold text-gray-900 mb-6">Category: {category.name}</h1>
11+
<div className="flex items-center justify-between mb-6">
12+
<h1 className="text-2xl font-bold text-gray-900">Category: {category.name}</h1>
13+
<Link
14+
href={'/topics/new'}
15+
data={{topic: { category_id: category.id }}}
16+
className="bg-sky-600 hover:bg-sky-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors shadow inline-block text-center"
17+
>
18+
New Topic
19+
</Link>
20+
</div>
21+
1122
<div className="max-w-7xl mx-auto">
1223
<div className="bg-white shadow-sm rounded-lg overflow-hidden">
1324
<TopicsTable topics={topics} />
@@ -17,4 +28,4 @@ function CategoriesShow({ category }: { category: Category }) {
1728
)
1829
}
1930

20-
export default CategoriesShow
31+
export default CategoriesShow

app/frontend/pages/my_topics/index.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Head } from '@inertiajs/react'
1+
import { Head, Link } from '@inertiajs/react'
22
import AppLayout from '../../layouts/AppLayout'
33
import { Topic } from '../../types'
44
import TopicsTable from '../../components/TopicsTable'
@@ -8,6 +8,16 @@ function MyTopicsIndex({ topics }: { topics: Topic[] }) {
88
<AppLayout>
99
<Head title="My Topics - Pups & Pourovers" />
1010

11+
<div className="flex items-center justify-between mb-6">
12+
<h1 className="text-2xl font-bold text-gray-900">My Topics</h1>
13+
<Link
14+
href="/topics/new"
15+
className="bg-sky-600 hover:bg-sky-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors shadow inline-block text-center"
16+
>
17+
New Topic
18+
</Link>
19+
</div>
20+
1121
<div className="max-w-7xl mx-auto">
1222
<div className="bg-white shadow-sm rounded-lg overflow-hidden">
1323
<TopicsTable topics={topics} />

app/frontend/pages/topics/index.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Head } from '@inertiajs/react'
1+
import { Head, Link } from '@inertiajs/react'
22
import AppLayout from '../../layouts/AppLayout'
33
import { Topic } from '../../types'
44
import TopicsTable from '../../components/TopicsTable'
@@ -9,6 +9,14 @@ function TopicsIndex({ topics }: { topics: Topic[] }) {
99
<Head title="Topics - Pups & Pourovers" />
1010

1111
<div className="max-w-7xl mx-auto">
12+
<div className="flex justify-end mb-6">
13+
<Link
14+
href="/topics/new"
15+
className="bg-sky-600 hover:bg-sky-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors shadow inline-block text-center"
16+
>
17+
New Topic
18+
</Link>
19+
</div>
1220
<div className="bg-white shadow-sm rounded-lg overflow-hidden">
1321
<TopicsTable topics={topics} />
1422
</div>

app/frontend/pages/topics/new.tsx

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import {Head, Form, usePage} from '@inertiajs/react'
2+
import AppLayout from '../../layouts/AppLayout'
3+
import LexicalRichTextEditor from '../../components/LexicalRichTextEditor'
4+
5+
interface TopicFormProps {
6+
topic?: {
7+
title?: string
8+
category_id?: number
9+
messages_attributes?: Array<{ body: string }>
10+
}
11+
}
12+
13+
const TopicsNew = ({topic}: TopicFormProps) => {
14+
const {categories} = usePage().props
15+
16+
return (
17+
<AppLayout>
18+
<Head title="New Topic - Pups & Pourovers"/>
19+
20+
<div className="max-w-4xl mx-auto">
21+
<div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-6">
22+
<h1 className="text-2xl font-bold text-gray-900 mb-6">New Topic</h1>
23+
<Form
24+
action="/topics"
25+
method="post"
26+
disableWhileProcessing
27+
className="inert:opacity-50 inert:pointer-events-none"
28+
>
29+
{({errors, processing}) => (
30+
<>
31+
<div className="mb-6">
32+
<label htmlFor="title" className="block text-sm font-medium text-gray-700 mb-2">
33+
Topic Title
34+
</label>
35+
<input
36+
type="text"
37+
id="title"
38+
name="topic[title]"
39+
defaultValue={topic?.title}
40+
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-sky-500 focus:border-sky-500"
41+
placeholder="Enter topic title..."
42+
/>
43+
{(errors.title) && (
44+
<p className="mt-1 text-sm text-red-600">
45+
Title {errors.title}
46+
</p>
47+
)}
48+
</div>
49+
50+
<div className="mb-6">
51+
<label htmlFor="category_id" className="block text-sm font-medium text-gray-700 mb-2">
52+
Category
53+
</label>
54+
<select
55+
id="category_id"
56+
name="topic[category_id]"
57+
defaultValue={topic?.category_id}
58+
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-sky-500 focus:border-sky-500"
59+
>
60+
<option value="">Select a category...</option>
61+
{categories.map((category) => (
62+
<option key={category.id} value={category.id}>
63+
{category.name}
64+
</option>
65+
))}
66+
</select>
67+
{(errors.category) && (
68+
<p className="mt-1 text-sm text-red-600">
69+
Category {errors.category}
70+
</p>
71+
)}
72+
</div>
73+
74+
<div className="mb-6">
75+
<label className="block text-sm font-medium text-gray-700 mb-2">
76+
Message
77+
</label>
78+
<div className="mb-2">
79+
<LexicalRichTextEditor
80+
name="topic[messages_attributes][0][body]"
81+
placeholder="Write your message..."
82+
/>
83+
</div>
84+
{(errors['messages[0].body']) && (
85+
<p className="mt-1 text-sm text-red-600">
86+
{errors['messages[0].body']}
87+
</p>
88+
)}
89+
{(errors.messages) && (
90+
<p className="mt-1 text-sm text-red-600">
91+
Messages {errors.messages}
92+
</p>
93+
)}
94+
</div>
95+
96+
<div className="flex items-center justify-end gap-4">
97+
<button
98+
type="submit"
99+
disabled={processing}
100+
className="px-6 py-3 bg-sky-600 text-white font-medium rounded-lg hover:bg-sky-700 focus:outline-none focus:ring-2 focus:ring-sky-500 focus:ring-offset-2 transition-colors duration-150 disabled:opacity-50 disabled:cursor-not-allowed"
101+
>
102+
{processing ? 'Creating Topic...' : 'Create Topic'}
103+
</button>
104+
</div>
105+
</>
106+
)}
107+
</Form>
108+
</div>
109+
</div>
110+
</AppLayout>
111+
)
112+
}
113+
114+
export default TopicsNew

app/models/topic.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
class Topic < ApplicationRecord
2-
has_many :messages, -> { order(created_at: :asc) }, dependent: :destroy
2+
has_many :messages, -> { order(created_at: :asc) }, dependent: :destroy, index_errors: true
33
belongs_to :user, counter_cache: true
44
belongs_to :category, counter_cache: true
55

6+
accepts_nested_attributes_for :messages, reject_if: :all_blank
7+
68
validates :title, presence: true
9+
validates :category_id, presence: true
10+
11+
validates :messages, presence: true, on: :form_submission
12+
13+
before_validation :assign_user_to_messages, on: [ :create, :form_submission ]
14+
15+
private
16+
17+
def assign_user_to_messages
18+
messages.each { |message| message.user_id ||= user_id }
19+
end
720
end

config/routes.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# root "posts#index"
1717
root "landing#index"
1818

19-
resources :topics, only: [ :index, :show ] do
19+
resources :topics, only: [ :index, :show, :new, :create ] do
2020
resources :messages, only: [ :create ]
2121
end
2222
resources :categories, only: [ :show ]

0 commit comments

Comments
 (0)