Skip to content

Commit

Permalink
Task Type functionality added and enhance UI
Browse files Browse the repository at this point in the history
  • Loading branch information
Mayank Infyom committed Oct 20, 2023
1 parent 5b595b0 commit 337e90d
Show file tree
Hide file tree
Showing 19 changed files with 412 additions and 140 deletions.
17 changes: 14 additions & 3 deletions app/Http/Controllers/TaskController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Http\Controllers;

use App\Models\Task;
use App\Models\Type;
use Illuminate\Http\Request;
use Inertia\Inertia;

Expand All @@ -12,14 +13,16 @@ class TaskController extends Controller
public function index(){


$tasks = Task::select(['id', 'title', 'description', 'order', 'completed', 'section']);
$tasks = Task::with('type:id,name,color')->select(['id', 'title', 'description', 'order', 'completed', 'section','type_id']);
$allTasks = $tasks->get();
$types = Type::all();
$currentTasks = $allTasks->where('completed',false)->groupBy('section')->toArray();
$completedTasks = $tasks->where('completed',1)->get()->toArray();

return Inertia::render('Todo/Todo', [
return Inertia::render('Todo/Main', [
'tasks' => $currentTasks,
'completedTasks' => $completedTasks
'completedTasks' => $completedTasks,
'types' => $types
]);
}

Expand Down Expand Up @@ -48,4 +51,12 @@ public function destroy($id)

return to_route('todo.index');
}

public function assetsIndex(){

$types = Type::withCount('tasks')->get();
return Inertia::render('Assets/Assets', [
'types' => $types
]);
}
}
20 changes: 20 additions & 0 deletions app/Http/Controllers/TypeController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Http\Controllers;

use App\Models\Type;
use Illuminate\Http\Request;

class TypeController extends Controller
{
public function store(Request $request){
$request->validate([
'name' => 'required',
'color' => 'required',
]);

Type::create($request->all());

return to_route('assets.index');
}
}
6 changes: 6 additions & 0 deletions app/Models/Task.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,11 @@ class Task extends Model
'order',
'completed',
'section',
'type_id'
];

public function type()
{
return $this->belongsTo(Type::class);
}
}
18 changes: 18 additions & 0 deletions app/Models/Type.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Type extends Model
{
use HasFactory;

protected $fillable = ['name','color'];

public function tasks()
{
return $this->hasMany(Task::class);
}
}
29 changes: 29 additions & 0 deletions database/migrations/2023_10_20_060419_create_types_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('types', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('color');
$table->timestamps();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('types');
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('tasks', function (Blueprint $table) {
$table->foreignId('type_id')->nullable()->constrained('types');
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('tasks', function (Blueprint $table) {
$table->dropForeign(['type_id']);
$table->dropColumn('type_id');
});
}
};
11 changes: 11 additions & 0 deletions resources/js/Layouts/Layout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from "react";
import Navbar from "./Navbar";

export default function Layout({children}) {
return (
<div className="">
<Navbar/>
<div className="main-content mt-5">{children}</div>
</div>
)
}
29 changes: 29 additions & 0 deletions resources/js/Layouts/Navbar.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Link } from "@inertiajs/react";
import React from "react";

export default function Navbar() {
return (
<nav className="navbar navbar-expand-lg navbar-light bg-light sticky-top">
<div className="container-fluid">
<a className="navbar-brand fs-3" href="/todo">Inertia Reactive PMS</a>
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarSupportedContent">
<ul className="navbar-nav me-auto mb-2 mb-lg-0">
<li className="nav-item">
<Link className="nav-link active" aria-current="page" href="/todo">Home</Link>
</li>
<li className="nav-item">
<Link className="nav-link" href="/assets">Assets</Link>
</li>
</ul>
<form className="d-flex">
<input className="form-control me-2" type="search" placeholder="Search" aria-label="Search" />
<button className="btn btn-outline-success" type="submit">Search</button>
</form>
</div>
</div>
</nav>
)
}
21 changes: 21 additions & 0 deletions resources/js/Pages/Assets/Assets.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from "react";
import Types from "./Types";
import { Head } from "@inertiajs/react";

export default function Assets(props) {

const { types } = props;
return (
<div className="container">
<Head title="IR PMS - Assets" />
<h1 className="text-center text-success">Assets</h1>
<div className="row">
<div className="col-4">
<Types types={types}/>
</div>
</div>


</div>
)
}
66 changes: 66 additions & 0 deletions resources/js/Pages/Assets/Types.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { faSquarePlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useForm } from "@inertiajs/react";
import React, { useEffect, useState } from "react";

export default function Types(props) {
const { types } = props;
const [ showNewType, setShowNewType ] = useState(false);
const { data, setData, post, processing, errors , reset , clearErrors } = useForm({
name: "",
color: ""
});

useEffect(() => {
setTimeout(() => {
clearErrors();
},5000);
},[errors]);

const saveType = (e) => {
e.preventDefault();
post('/types',{
onSuccess: () => {
reset();
setShowNewType(false)
}
});

}


return (
<>
<h5 className="text-center text-success">Types <span className="ms-2" role="button" onClick={() => setShowNewType(!showNewType)}><FontAwesomeIcon className="text-success" icon={faSquarePlus}/></span></h5>
<div className=" border rounded p-2">
{ showNewType &&
<form onSubmit={saveType}>
<div className="d-flex align-items-center justify-content-center">
<div className="card w-100">
<div className="card-body py-2">
<div className="d-flex align-items-center row ">
<div className="col-9">
<input type="text" name="name" id="name" className="form-control" value={data.name} onChange={e => setData('name',e.target.value)} required/>
</div>
<div className="col-3 px-2">
<input type="color" name="name" id="name" className="form-control p-0" value={data.color} onChange={e => setData('color',e.target.value)}/>
</div>
</div>
</div>
</div>
<button className="btn text-success ms-2 fs-5" disabled={processing}>Add</button>
</div>
{ errors.name && <span className="text-danger">{errors.name}</span> }
</form>
}

<div className="m-2 d-flex justify-content-center flex-wrap">
{ types.map((type,index) => (
<span className="badge m-2" style={{ backgroundColor : type.color }} key={index}><h6 className="mb-0">{type.name + "(" + type.tasks_count + ")"}</h6></span>
)) }
</div>
</div>
</>
)

}
5 changes: 4 additions & 1 deletion resources/js/Pages/Keys.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export const AllSection = {
"backlog" : 0,
'inProgress' : 1,
'done' : 2
'done' : 2,
0 : "Backlog",
1 : "In Progress",
2 : "Done"
};
3 changes: 1 addition & 2 deletions resources/js/Pages/Todo/AddTask.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { router } from "@inertiajs/react";
import React, { useState } from "react";
import {AllSection} from "../Keys.jsx"

export default function AddTask(props) {
const { section,hideNewTaskCard } = props;
Expand All @@ -9,7 +8,7 @@ export default function AddTask(props) {
hideNewTaskCard();
router.post('/todo', {
title:task,
section : AllSection[section]
section : section
});
}

Expand Down
38 changes: 38 additions & 0 deletions resources/js/Pages/Todo/Main.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { useState } from "react";
import { AllSection } from "../Keys";
import { Head, router } from "@inertiajs/react";
import Sections from "./Sections/Sections";
import Completed from "./Sections/Completed";

export default function Main(props) {

const { tasks,completedTasks } = props;
const [ draggedTaskId, setDraggedTaskId ] = useState();

const onDragOver = (event) => {
event.preventDefault();
}

const onDrop = (event, section ) => {
event.preventDefault();
router.put(`/todo/${draggedTaskId}`, {
section : section
});
}


return (
<div className="container">
<Head title="IR PMS - Home" />
<h1 className="text-center text-success">Todo List</h1>
<div className="row">
<Sections tasks={tasks} setDraggedTaskId={setDraggedTaskId} section={AllSection.backlog} sectionName="Backlog" onDragOver={onDragOver} onDrop={onDrop}/>
<Sections tasks={tasks} setDraggedTaskId={setDraggedTaskId} section={AllSection.inProgress} sectionName="In Progress" onDragOver={onDragOver} onDrop={onDrop}/>
<Sections tasks={tasks} setDraggedTaskId={setDraggedTaskId} section={AllSection.done} sectionName="Done" onDragOver={onDragOver} onDrop={onDrop}/>

{/* Completed Task */}
<Completed completedTasks={completedTasks}/>
</div>
</div>
);
}
19 changes: 19 additions & 0 deletions resources/js/Pages/Todo/Sections/Completed.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";
import Task from "../Task";

export default function Completed(props)
{
const { completedTasks } = props;
return (
<>
{completedTasks.length > 0 &&
<div className="m-2 border-top pt-4 shadow shadow-md" >
<h4 className="text-success" role="button">Completed</h4>
{completedTasks.map((task) => {
return <Task key={task.id} task={task} isCompleted={true}/>
})}
</div>
}
</>
)
}
27 changes: 27 additions & 0 deletions resources/js/Pages/Todo/Sections/Sections.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React, { useState } from "react";
import Task from "../Task";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faSquarePlus } from "@fortawesome/free-solid-svg-icons";
import AddTask from "../AddTask";

export default function Sections(props) {

const { tasks, setDraggedTaskId ,section ,sectionName , onDrop , onDragOver} = props;

const [ addNewTask , setAddNewTask ] = useState({ [section] : false });
const showNewTask = (section, show = false) => {
setAddNewTask({
[section] : show
})
}

return (
<div className="m-2 shadow shadow-md py-3" onDrop={event => onDrop(event, section)} onDragOver={(event => onDragOver(event))}>
<h4 className=" text-success" role="button">{sectionName} <span className="ms-2" onClick={() => showNewTask(section, !addNewTask[section])}><FontAwesomeIcon className="text-success" icon={faSquarePlus} /></span></h4>
{addNewTask[section] && <AddTask section={section} hideNewTaskCard={showNewTask} />}
{tasks[section]? tasks[section].map((task) => {
return <Task key={task.id} task={task} currentSection={section} draggedTaskId={setDraggedTaskId} />
}) : <strong className="d-flex justify-content-center py-3 fs-5">No Task Here</strong>}
</div>
);
}
Loading

0 comments on commit 337e90d

Please sign in to comment.