Skip to content

Commit

Permalink
added board management
Browse files Browse the repository at this point in the history
  • Loading branch information
tareq1988 committed Jan 20, 2024
1 parent 45b5856 commit f797ea8
Show file tree
Hide file tree
Showing 11 changed files with 506 additions and 27 deletions.
116 changes: 116 additions & 0 deletions app/Http/Controllers/Admin/BoardController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
<?php

namespace App\Http\Controllers\Admin;

use App\Models\Board;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class BoardController extends Controller
{
public function index()
{
$data = [
'boards' => Board::all(),
];

return inertia('Admin/Boards/Index', $data);
}

public function show(Board $board)
{
$data = [
'board' => $board,
];

return inertia('Admin/Boards/Show', $data);
}

public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
]);

Board::create([
'name' => $request->name,
'slug' => Str::slug($request->name),
'privacy' => 'public',
'allow_posts' => true,
'order' => Board::count() + 1,
'settings' => [
'form' => [
'heading' => 'Suggest a feature',
'description' => 'What would you like to see in the future?',
'button' => 'Feature title',
'fields' => [
'title' => [
'label' => 'Title',
'placeholder' => 'Enter a short title',
],
'details' => [
'label' => 'Details',
'placeholder' => 'Enter a detailed description of your feature request',
],
],
]
],
]);

return redirect()->back()->with('success', 'Board created.');
}

public function update(Request $request, Board $board)
{
$request->validate([
'name' => 'required|string|max:255',
'slug' => 'required|string|max:255',
'privacy' => 'required|string|in:public,private',
'allow_posts' => 'required|boolean',
'settings' => 'required|array',
'settings.heading' => 'required|string|max:255',
'settings.description' => 'required|string|max:255',
'settings.button' => 'required|string|max:50',
'settings.fields.*.label' => 'required|string|max:100',
'settings.fields.*.placeholder' => 'required|string|max:100',
]);

$board->update([
'name' => $request->name,
'slug' => $request->slug,
'privacy' => $request->privacy,
'allow_posts' => $request->allow_posts,
'settings' => [
'form' => [
'heading' => $request->settings['heading'],
'description' => $request->settings['description'],
'button' => $request->settings['button'],
'fields' => [
'title' => [
'label' => $request->settings['fields']['title']['label'],
'placeholder' => $request->settings['fields']['title']['placeholder'],
],
'details' => [
'label' => $request->settings['fields']['details']['label'],
'placeholder' => $request->settings['fields']['details']['placeholder'],
],
],
]
],
]);

return redirect()->route('admin.boards.show', $board)->with('success', 'Board updated.');
}

public function destroy(Board $board)
{
if ($board->posts()->count() > 0) {
return redirect()->back()->with('error', 'Board has posts. Delete posts first.');
}

$board->delete();

return redirect()->route('admin.boards.index')->with('success', 'Board deleted.');
}
}
3 changes: 2 additions & 1 deletion app/Models/Board.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class Board extends Model
];

protected $casts = [
'settings' => 'array'
'settings' => 'array',
'allow_posts' => 'boolean',
];

public function getRouteKeyName()
Expand Down
34 changes: 24 additions & 10 deletions resources/js/Layouts/AuthenticatedLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default function Authenticated({
<div className="flex justify-between h-16">
<div className="flex">
<div className="shrink-0 flex items-center">
<Link href={route('dashboard')}>
<Link href="/admin">
<img
alt={`${appName} Logo`}
src={appLogo || '/images/logo.svg'}
Expand All @@ -35,17 +35,17 @@ export default function Authenticated({

<div className="hidden space-x-8 sm:-my-px sm:ms-10 sm:flex">
<NavLink
href={route('dashboard')}
active={route().current('dashboard')}
href={route('admin.feedbacks.index')}
active={route().current('admin.feedbacks.index')}
>
Dashboard
Feedbacks
</NavLink>

<NavLink
href={route('admin.feedbacks.index')}
active={route().current('admin.feedbacks.index')}
href={route('admin.boards.index')}
active={route().current('admin.boards.index')}
>
Feedback
Boards
</NavLink>

<NavLink
Expand Down Expand Up @@ -155,10 +155,24 @@ export default function Authenticated({
>
<div className="pt-2 pb-3 space-y-1">
<ResponsiveNavLink
href={route('dashboard')}
active={route().current('dashboard')}
href={route('admin.feedbacks.index')}
active={route().current('admin.feedbacks.index')}
>
Feedbacks
</ResponsiveNavLink>

<ResponsiveNavLink
href={route('admin.boards.index')}
active={route().current('admin.boards.index')}
>
Boards
</ResponsiveNavLink>

<ResponsiveNavLink
href={route('admin.statuses.index')}
active={route().current('admin.statuses.index')}
>
Dashboard
Status
</ResponsiveNavLink>
</div>

Expand Down
102 changes: 102 additions & 0 deletions resources/js/Pages/Admin/Boards/Index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React, { useState } from 'react';
import { Head, Link, useForm } from '@inertiajs/react';
import {
Button,
Modal,
ModalActions,
ModalBody,
ModalHeader,
SwitchInput,
TextField,
} from '@wedevs/tail-react';

import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { PlusIcon } from '@heroicons/react/24/outline';
import { BoardType } from '@/types';

type Props = {
boards: BoardType[];
};

const BoardsIndex = ({ boards }: Props) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const form = useForm({
name: '',
});

const createBoard = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

form.post(route('admin.boards.store'), {
preserveScroll: true,
onSuccess: () => {
setIsModalOpen(false);
form.reset();
},
});
};

return (
<AuthenticatedLayout
header={
<div className="flex justify-between">
<h2 className="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
Boards
</h2>

<Button
variant="primary"
className="inline-flex"
onClick={() => setIsModalOpen(true)}
>
<PlusIcon className="mr-2 h-5 w-5" aria-hidden="true" />
<span>Add Board</span>
</Button>
</div>
}
>
<Head title="Boards" />
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
{boards.map((board) => (
<Link
key={board.id}
className="rounded-lg bg-white shadow"
href={route('admin.boards.show', [board])}
>
<div className="space-x-6 p-6">
<div className="flex items-center justify-between">
<span className="font-medium">{board.name}</span>
<span className="text-sm">{board.posts}</span>
</div>
</div>
</Link>
))}
</div>

<Modal isOpen={isModalOpen} onClose={() => setIsModalOpen(false)}>
<form onSubmit={createBoard}>
<ModalHeader>Add Board</ModalHeader>
<ModalBody>
<TextField
label="Name"
value={form.data.name}
onChange={(value) => form.setData('name', value)}
/>
</ModalBody>
<ModalActions>
<Button variant="primary">Add Board</Button>
<Button
variant="secondary"
onClick={() => setIsModalOpen(false)}
className="mr-2"
>
Cancel
</Button>
</ModalActions>
</form>
</Modal>
</AuthenticatedLayout>
);
};

export default BoardsIndex;
Loading

0 comments on commit f797ea8

Please sign in to comment.