Skip to content

Commit

Permalink
Merge pull request #5 from weDevsOfficial/feature/post-as-user
Browse files Browse the repository at this point in the history
feat: post on behalf of an user
  • Loading branch information
tareq1988 authored Feb 22, 2024
2 parents e5bd9b9 + 3675e47 commit fb9006d
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 6 deletions.
19 changes: 17 additions & 2 deletions app/Http/Controllers/Admin/PostsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,29 @@ public function store(Request $request)
'body' => 'required',
'status_id' => 'required|exists:statuses,id',
'board_id' => 'required|exists:boards,id',
'behalf_id' => 'sometimes|exists:users,id',
]);

$post = Post::create([
$args = [
'title' => $request->title,
'body' => $request->body,
'status_id' => $request->status_id,
'board_id' => $request->board_id,
]);
];

if ($request->behalf_id) {
$args['created_by'] = $request->behalf_id;
$args['by'] = auth()->id();
}

$post = Post::create($args);

if ($post) {
$post->votes()->create([
'user_id' => $post->created_by,
'board_id' => $post->board_id,
]);
}

return redirect()->route('admin.feedbacks.show', $post)->with('success', 'Post created successfully');
}
Expand Down
23 changes: 23 additions & 0 deletions app/Http/Controllers/Admin/UserSearchController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace App\Http\Controllers\Admin;

use App\Models\User;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class UserSearchController extends Controller
{
public function search(Request $request)
{
$query = $request->input('query');

$users = User::where('name', 'LIKE', "%$query%")
->orWhere('email', 'LIKE', "%$query%")
->get()
->take(10)
->makeVisible('email');

return response()->json($users);
}
}
2 changes: 1 addition & 1 deletion app/Models/Post.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ protected static function boot()

$post->slug = $count ? "{$slug}-{$count}" : $slug;

if (auth()->id()) {
if (auth()->id() && ! $post->created_by) {
$post->created_by = auth()->id();
}
});
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@headlessui/react": "^1.4.2",
"@inertiajs/react": "^1.0.0",
"@tailwindcss/forms": "^0.5.3",
"@types/lodash": "^4.14.202",
"@types/node": "^18.13.0",
"@types/react": "^18.0.28",
"@types/react-color": "^3.0.11",
Expand All @@ -30,6 +31,7 @@
"@radix-ui/react-popover": "^1.0.7",
"@wedevs/tail-react": "^0.3.3",
"classnames": "^2.5.1",
"lodash.debounce": "^4.0.8",
"react-color": "^2.19.3"
},
"config": {
Expand Down
178 changes: 177 additions & 1 deletion resources/js/Pages/Admin/Feedbacks/Create.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { useState } from 'react';
import { useForm } from '@inertiajs/react';
import { Combobox } from '@headlessui/react';
import {
Button,
Modal,
Expand All @@ -10,6 +11,10 @@ import {
TextField,
Textarea,
} from '@wedevs/tail-react';
import classNames from 'classnames';
import { CheckIcon, PlusIcon, XMarkIcon } from '@heroicons/react/24/outline';
import { debounce } from 'lodash';
import { User } from '@/types';

type Option = {
value: string;
Expand All @@ -24,18 +29,26 @@ type Props = {
boards: Option[];
};

type SearchProps = {
onSelect: (user: any) => void;
onCreate: () => void;
onClear: () => void;
};

const CreateModal = ({
showModal,
setShowModal,
onSubmit,
statuses,
boards,
}: Props) => {
const [showUserForm, setShowUserForm] = useState(false);
const form = useForm({
title: '',
body: '',
board_id: '',
status_id: '',
behalf_id: 0,
});

const statusOptions = statuses.map((status) => ({
Expand Down Expand Up @@ -69,6 +82,17 @@ const CreateModal = ({
<form onSubmit={handleSubmit}>
<ModalHeader>Create Feedback</ModalHeader>
<ModalBody>
<div className="mb-4 border p-3 rounded">
<div className="block text-sm font-medium leading-6 text-gray-900 mb-2">
Post on behalf of a user (optional)
</div>
<UserSearchDropdown
onCreate={() => setShowUserForm(true)}
onSelect={(user) => form.setData('behalf_id', user.id)}
onClear={() => form.setData('behalf_id', 0)}
/>
</div>

<TextField
label="Title"
name="title"
Expand Down Expand Up @@ -121,8 +145,160 @@ const CreateModal = ({
</Button>
</ModalActions>
</form>

<Modal isOpen={showUserForm} onClose={() => setShowUserForm(false)}>
<form>
<ModalHeader>Create User</ModalHeader>
<ModalBody>
<TextField
label="Name"
placeholder="Enter a name"
value={''}
onChange={function (value: string): void {
throw new Error('Function not implemented.');
}}
/>
<TextField
label="Email"
placeholder="Enter an email"
value={''}
onChange={function (value: string): void {
throw new Error('Function not implemented.');
}}
/>
</ModalBody>
<ModalActions>
<Button type="submit" variant="primary">
Create
</Button>
<Button onClick={() => setShowUserForm(false)} variant="secondary">
Cancel
</Button>
</ModalActions>
</form>
</Modal>
</Modal>
);
};

export default CreateModal;

const UserSearchDropdown = ({ onSelect, onCreate, onClear }: SearchProps) => {
const [selectedUser, setSelectedUser] = useState<User | null>(null);
const [users, setUsers] = useState<User[]>([]);
const [query, setQuery] = useState('');

const fetchUsers = async (searchTerm: string) => {
const response = await fetch(
route('admin.users.search', { query: searchTerm })
);

return response.json();
};

const handleSelect = (user: any) => {
setSelectedUser(user);
onSelect(user);
};

const handleClear = () => {
setSelectedUser(null);
onClear();
};

const handleSearch = debounce((e: React.KeyboardEvent<HTMLInputElement>) => {
setQuery((e.target as HTMLInputElement).value);
fetchUsers((e.target as HTMLInputElement).value).then((fetchedUsers) =>
setUsers(fetchedUsers)
);
}, 300);

return (
<div className="relative z-10">
{selectedUser === null && (
<Combobox value={selectedUser} onChange={handleSelect}>
<Combobox.Input
className="w-full px-3 py-2 text-sm border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500"
placeholder="Search for a user by name or email"
onKeyUp={handleSearch}
/>
<Combobox.Options className="absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm">
{users.length === 0 && query !== '' ? (
<div className="relative cursor-pointer select-none py-2 px-4 text-gray-700">
<button
type="button"
className="inline-flex items-center"
onClick={() => onCreate()}
>
<PlusIcon className="h-4 w-4 inline-block mr-1.5" />
Create New User
</button>
</div>
) : (
users.map((user) => (
<Combobox.Option
key={user.id}
value={user}
className={({ active }) =>
`relative cursor-pointer select-none py-2 pl-10 pr-4 ${
active ? 'bg-indigo-100 text-gray-700' : 'text-gray-900'
}`
}
>
{({ selected, active }) => (
<>
<div className="flex items-center">
<img
src={user.avatar}
alt={user.name}
className="h-6 w-6 flex-shrink-0 rounded-full"
/>
<span
className={classNames(
'ml-3 truncate',
selected && 'font-semibold'
)}
>
{user.name} ({user.email})
</span>
</div>

{selected ? (
<span
className={`absolute inset-y-0 left-0 flex items-center pl-3 ${
active ? 'text-white' : 'text-indigo-600'
}`}
>
<CheckIcon className="h-5 w-5" aria-hidden="true" />
</span>
) : null}
</>
)}
</Combobox.Option>
))
)}
</Combobox.Options>
</Combobox>
)}

{selectedUser && (
<div className="mt-2 flex items-center justify-between bg-indigo-50 px-2 py-2 rounded border">
<div className="flex">
<img
src={selectedUser.avatar}
alt={selectedUser.name}
className="h-6 w-6 flex-shrink-0 rounded-full mr-2"
/>
<span className="block font-medium text-sm">
{selectedUser.name}
</span>
</div>

<button className="" onClick={handleClear}>
<XMarkIcon className="h-4 w-4" />
</button>
</div>
)}
</div>
);
};
7 changes: 5 additions & 2 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Application;
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\Admin\UserController;
use App\Http\Controllers\Admin\PostsController;
use App\Http\Controllers\Admin\StatusController;
use App\Http\Controllers\Frontend\HomeController;
use App\Http\Controllers\Frontend\PostController;
use App\Http\Controllers\Admin\FeedbackController;
use App\Http\Controllers\Frontend\BoardController;
use App\Http\Controllers\Admin\DashboardController;
use App\Http\Controllers\Admin\UserSearchController;
use App\Http\Controllers\Frontend\CommentController;
use App\Http\Controllers\Admin\BoardController as AdminBoardController;
use App\Http\Controllers\Admin\PostsController;
use App\Http\Controllers\Admin\UserController;

/*
|--------------------------------------------------------------------------
Expand Down Expand Up @@ -58,6 +59,8 @@
Route::post('/users', [UserController::class, 'store'])->name('admin.users.store');
Route::delete('/users/{user}', [UserController::class, 'destroy'])->name('admin.users.destroy');
Route::put('/users/{user}', [UserController::class, 'update'])->name('admin.users.update');

Route::get('/search-users', [UserSearchController::class, 'search'])->name('admin.users.search');
});

Route::middleware(['auth', 'verified'])->group(function () {
Expand Down
10 changes: 10 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,11 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==

"@types/lodash@^4.14.202":
version "4.14.202"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.202.tgz#f09dbd2fb082d507178b2f2a5c7e74bd72ff98f8"
integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==

"@types/node@^18.13.0":
version "18.19.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.5.tgz#4b23a9ab8ab7dafebb57bcbaf5c3d8d04f9d8cac"
Expand Down Expand Up @@ -1892,6 +1897,11 @@ lodash-es@^4.17.15:
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==

lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==

lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
Expand Down

0 comments on commit fb9006d

Please sign in to comment.