diff --git a/app/Http/Controllers/Admin/FeedbackController.php b/app/Http/Controllers/Admin/FeedbackController.php
index 942d625..f778961 100644
--- a/app/Http/Controllers/Admin/FeedbackController.php
+++ b/app/Http/Controllers/Admin/FeedbackController.php
@@ -53,9 +53,33 @@ public function index(Request $request)
]);
}
+ public function search(Request $request)
+ {
+ $search = $request->input('search');
+ $parent_id = $request->input('parent_id') ?? null;
+ if (!$request->has('search')) {
+ return response()->json([]);
+ }
+
+ $query = Post::where('merged_with_post', null);
+ $query->where('title', 'like', '%' . $search . '%');
+ if ($parent_id) {
+ $query->where('id', '!=', $parent_id);
+ }
+
+ $query->orderBy('vote', 'desc');
+
+ $response = $query->get();
+
+ return response()->json($response);
+ }
+
public function show(Post $post)
{
$post->load('creator', 'board', 'status', 'by');
+ if ($post->merged_with_post) {
+ $post->merged_with_post = Post::find($post->merged_with_post);
+ }
$boards = Board::select('id', 'name', 'posts', 'slug')->get();
$statuses = Status::select('id', 'name', 'color')->get();
@@ -141,10 +165,6 @@ public function update(Request $request, Post $post)
'body' => $request->input('comment') ?? '',
'status_id' => $request->input('status_id'),
]);
-
- if ($request->input('notify') === true) {
- $this->notify($post);
- }
}
$post->update([
@@ -152,6 +172,10 @@ public function update(Request $request, Post $post)
'status_id' => $request->input('status_id'),
]);
+ if ($request->input('notify') === true) {
+ $this->notify($post);
+ }
+
return redirect()->back()->with('success', 'Feedback updated successfully.');
}
@@ -195,4 +219,32 @@ public function addVote(Post $post, Request $request)
return redirect()->back()->with('success', 'Vote added successfully.');
}
+
+ public function merge(Request $request)
+ {
+ $request->validate([
+ 'post_id' => 'required|exists:posts,id',
+ 'merge_ids' => 'required|array|exists:posts,id',
+ 'status_id' => 'required|exists:statuses,id',
+ ]);
+
+ try {
+ $post = Post::where('id', $request->post_id);
+ $post->increment('comments', Post::whereIn('id', $request->merge_ids)->pluck( 'comments' )->sum() );
+ $post->increment('vote', Post::whereIn('id', $request->merge_ids)->pluck( 'vote' )->sum() );
+
+ Post::whereIn('id', $request->merge_ids)
+ ->update([
+ 'merged_with_post' => $request->post_id,
+ 'status_id' => $request->status_id,
+ 'comments' => 0,
+ 'vote' => 0,
+ ]);
+
+ return redirect()->back()->with('success', 'Posts merged successfully.');
+ } catch (\Exception $e) {
+ return redirect()->back()->with('error', 'Post not found.');
+
+ }
+ }
}
diff --git a/app/Http/Controllers/Frontend/BoardController.php b/app/Http/Controllers/Frontend/BoardController.php
index 8d82253..d2317b9 100644
--- a/app/Http/Controllers/Frontend/BoardController.php
+++ b/app/Http/Controllers/Frontend/BoardController.php
@@ -27,8 +27,8 @@ public function show(Request $request, Board $board)
$postsQuery = Post::where('board_id', $board->id);
$statuses = Status::select('id')
- ->inFrontend()
- ->get();
+ ->inFrontend()
+ ->get();
// Only show posts with statuses that are in the frontend
// or have no status (waiting to be reviewed)
@@ -47,12 +47,16 @@ public function show(Request $request, Board $board)
]);
}
+ if ($request->has('search') && $request->search) {
+ $postsQuery->where('title', 'like', '%' . $request->search . '%');
+ }
+
if ($request->has('sort') && in_array($request->sort, array_keys($sortFields))) {
$orderBy = $sortFields[$request->sort];
}
$postsQuery->orderBy($orderBy, $request->sort === 'oldest' ? 'asc' : 'desc');
- $posts = $postsQuery->cursorPaginate(20);
+ $posts = $postsQuery->paginate(20);
$data = [
'board' => $board,
diff --git a/app/Http/Controllers/Frontend/CommentController.php b/app/Http/Controllers/Frontend/CommentController.php
index d936b0a..806c76d 100644
--- a/app/Http/Controllers/Frontend/CommentController.php
+++ b/app/Http/Controllers/Frontend/CommentController.php
@@ -19,7 +19,13 @@ public function index(Request $request, Post $post)
$sort = $request->has('sort') && in_array($request->sort, ['latest', 'oldest']) ? $request->sort : 'oldest';
$orderBy = ($sort === 'latest') ? 'desc' : 'asc';
- $comments = $post->comments()->with('user', 'status')->orderBy('created_at', $orderBy)->get();
+ // Search if there are any merged posts with the post id
+ $mergedPost = Post::where('merged_with_post', $post->id)->get();
+ $postIds = $mergedPost->pluck('id')->push($post->id);
+ $comments = Comment::whereIn('post_id', $postIds)
+ ->with('user', 'status')
+ ->orderBy('created_at', $orderBy)
+ ->get();
// Group comments by parent_id
$groupedComments = $comments->groupBy('parent_id');
diff --git a/app/Http/Controllers/Frontend/PostController.php b/app/Http/Controllers/Frontend/PostController.php
index f59ce7c..269077d 100644
--- a/app/Http/Controllers/Frontend/PostController.php
+++ b/app/Http/Controllers/Frontend/PostController.php
@@ -18,6 +18,9 @@ public function show(Board $board, $post)
$post->load('creator');
$post->body = Formatting::transformBody($post->body);
+ if ($post->merged_with_post) {
+ $post->merged_with_post = Post::with('board')->find($post->merged_with_post);
+ }
$data = [
'post' => $post,
diff --git a/app/Models/Post.php b/app/Models/Post.php
index 7ef238d..eaa3639 100644
--- a/app/Models/Post.php
+++ b/app/Models/Post.php
@@ -23,7 +23,8 @@ class Post extends Model
'eta',
'impact',
'effort',
- 'created_by'
+ 'created_by',
+ 'merged_with_post',
];
protected static function boot()
@@ -63,7 +64,7 @@ public function board()
public function status()
{
- return $this->hasOne(Status::class, 'id', 'status_id');
+ return $this->hasOne(Status::class, 'id', 'status_id')->withDefault();
}
public function votes()
@@ -98,7 +99,7 @@ public function scopeWithVote($query)
'has_voted' => Vote::selectRaw('count(*)')
->whereColumn('post_id', 'posts.id')
->where('user_id', $userId)
- ->take(1)
+ ->take(1),
]);
}
}
diff --git a/database/migrations/2024_05_07_091836_add_archived_by_post_to_posts_table.php b/database/migrations/2024_05_07_091836_add_archived_by_post_to_posts_table.php
new file mode 100644
index 0000000..8716e37
--- /dev/null
+++ b/database/migrations/2024_05_07_091836_add_archived_by_post_to_posts_table.php
@@ -0,0 +1,28 @@
+foreignId('merged_with_post')->nullable()->constrained('posts')->onDelete('set null');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('posts', function (Blueprint $table) {
+ $table->dropConstrainedForeignId('merged_with_post');
+ });
+ }
+};
diff --git a/database/seeders/PostSeeder.php b/database/seeders/PostSeeder.php
index c76e104..fabdddc 100644
--- a/database/seeders/PostSeeder.php
+++ b/database/seeders/PostSeeder.php
@@ -26,5 +26,14 @@ public function run(): void
'created_by' => User::inRandomOrder()->first()->id,
]);
}
+
+ // foreach (range(1, 200) as $n) {
+ // Post::factory()->create([
+ // 'title' => 'post-' . $n,
+ // 'board_id' => $boards->first()->id,
+ // ]);
+
+ // sleep(1);
+ // }
}
}
diff --git a/phpunit.xml b/phpunit.xml
index bc86714..ed8872f 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -21,8 +21,8 @@
-
-
+
+
diff --git a/resources/js/Components/ActionMenu.tsx b/resources/js/Components/ActionMenu.tsx
new file mode 100644
index 0000000..8224df8
--- /dev/null
+++ b/resources/js/Components/ActionMenu.tsx
@@ -0,0 +1,56 @@
+import { Fragment } from 'react'
+import { Menu, Transition } from '@headlessui/react'
+import { EllipsisVerticalIcon } from '@heroicons/react/20/solid'
+import classNames from 'classnames';
+
+type ActionMenuProps = {
+ menuItems: {
+ label: string;
+ onClick: () => void;
+ }[];
+ menuName?: string;
+};
+
+export default function ActionMenu({ menuItems, menuName = 'Actions' }: ActionMenuProps) {
+ return (
+
+ )
+}
diff --git a/resources/js/Components/Comments.tsx b/resources/js/Components/Comments.tsx
index 40569d5..6792657 100644
--- a/resources/js/Components/Comments.tsx
+++ b/resources/js/Components/Comments.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useState } from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
import { Link, usePage } from '@inertiajs/react';
import CommentBox from './CommentBox';
@@ -16,29 +16,32 @@ const Comments: React.FC = ({ post }) => {
const [sort, setSort] = useState<'latest' | 'oldest'>('oldest');
const [isFetching, setIsFetching] = useState(false);
- const fetchComments = async () => {
- setIsFetching(true);
+ const fetchComments = useCallback( async () => {
+ setIsFetching(true);
- try {
- const response = await fetch(
- route('post.comments.index', {
- post: post.slug,
- sort: sort,
- })
- );
+ if (post.merged_with_post){
+ return;
+ }
+ try {
+ const response = await fetch(
+ route('post.comments.index', {
+ post: post.slug,
+ sort: sort,
+ })
+ );
- const data = await response.json();
- setIsFetching(false);
- setComments(data);
- } catch (error) {
- console.error('Error fetching comments:', error);
- setIsFetching(false);
- }
- };
+ const data = await response.json();
+ setIsFetching(false);
+ setComments(data);
+ } catch (error) {
+ console.error('Error fetching comments:', error);
+ setIsFetching(false);
+ }
+ }, [sort, post]);
useEffect(() => {
fetchComments();
- }, [sort]);
+ }, [sort, post]);
const appendToComments = (comment: CommentType) => {
setComments([...comments, comment]);
@@ -70,7 +73,7 @@ const Comments: React.FC = ({ post }) => {
Sort By