category done
parent
8a3fcadeb9
commit
42bad6da90
@ -0,0 +1,67 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use App\Models\Category;
|
||||||
|
|
||||||
|
class CategoryController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return inertia('Category', [
|
||||||
|
'categories' => Category::orderBy('created_at', 'desc')->paginate(5)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'name' => 'required|string|max:255',
|
||||||
|
'description' => 'required|string|max:255',
|
||||||
|
'amount' => 'required|numeric|max:999999999|min:1'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$category = Category::create([
|
||||||
|
'name' => $request->name,
|
||||||
|
'description' => $request->description,
|
||||||
|
'default_budget' => $request->amount
|
||||||
|
]);
|
||||||
|
|
||||||
|
$category->budgets()->create([
|
||||||
|
'budget' => $request->amount,
|
||||||
|
'start_date' => now()->toDateString(),
|
||||||
|
'end_date' => now()->endOfMonth()->toDateString(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return redirect()->route('categories');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Request $request, Category $category)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'name' => 'required|string|max:255',
|
||||||
|
'description' => 'required|string|max:255',
|
||||||
|
'amount' => 'required|numeric|max:999999999|min:1'
|
||||||
|
]);
|
||||||
|
|
||||||
|
$category->update([
|
||||||
|
'name' => $request->name,
|
||||||
|
'description' => $request->description,
|
||||||
|
'default_budget' => $request->amount
|
||||||
|
]);
|
||||||
|
|
||||||
|
$budget = $category->budgets()->whereDate('end_date', now()->endOfMonth()->toDateString());
|
||||||
|
$budget->update(['budget' => $request->amount]);
|
||||||
|
|
||||||
|
return redirect()->route('categories');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Category $category)
|
||||||
|
{
|
||||||
|
$category->budgets()->delete();
|
||||||
|
$category->delete();
|
||||||
|
|
||||||
|
return redirect()->route('categories');
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,2 @@
|
|||||||
*.sqlite*
|
*.sqlite*
|
||||||
|
!database.sqlite
|
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -0,0 +1,52 @@
|
|||||||
|
import { Link } from '@inertiajs/inertia-react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
const PageLink = ({ active, label, url }) => {
|
||||||
|
const className = classNames(
|
||||||
|
[
|
||||||
|
'mr-1 mb-1',
|
||||||
|
'px-4 py-3',
|
||||||
|
'border border-solid border-gray-300 rounded',
|
||||||
|
'text-sm',
|
||||||
|
'bg-white',
|
||||||
|
'hover:bg-white',
|
||||||
|
'focus:outline-none focus:border-indigo-700 focus:text-indigo-700'
|
||||||
|
],
|
||||||
|
{
|
||||||
|
'border-indigo-600 bg-indigo-600 text-white hover:bg-indigo-400': active
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Link className={className} href={url}>
|
||||||
|
<span dangerouslySetInnerHTML={{ __html: label }}></span>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Previous, if on first page
|
||||||
|
// Next, if on last page
|
||||||
|
// and dots, if exists (...)
|
||||||
|
const PageInactive = ({ label }) => {
|
||||||
|
const className = classNames(
|
||||||
|
'mr-1 mb-1 px-4 py-3 text-sm border rounded border-solid border-gray-300 text-gray'
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div className={className} dangerouslySetInnerHTML={{ __html: label }} />
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ({ links = [] }) => {
|
||||||
|
// dont render, if there's only 1 page (previous, 1, next)
|
||||||
|
if (links.length === 3) return null;
|
||||||
|
return (
|
||||||
|
<div className="flex flex-wrap mt-6 -mb-1">
|
||||||
|
{links.map(({ active, label, url }) => {
|
||||||
|
return url === null ? (
|
||||||
|
<PageInactive key={label} label={label} />
|
||||||
|
) : (
|
||||||
|
<PageLink key={label} label={label} active={active} url={url} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,180 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import Pagination from '@/Components/Pagination'
|
||||||
|
import Authenticated from '@/Layouts/Authenticated';
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
import { Head, useForm } from '@inertiajs/inertia-react';
|
||||||
|
|
||||||
|
export default function Category(props) {
|
||||||
|
const [category, setCategory] = useState(null)
|
||||||
|
const { data: categories , links } = props.categories
|
||||||
|
const { data, setData, errors, post, put, processing, delete: destroy } = useForm({
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
amount: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleChange = (e) => {
|
||||||
|
const key = e.target.id;
|
||||||
|
const value = e.target.value
|
||||||
|
setData(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
setCategory(null)
|
||||||
|
setData({
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
amount: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEdit = (category) => {
|
||||||
|
setCategory(category)
|
||||||
|
setData({
|
||||||
|
name: category.name,
|
||||||
|
description: category.description,
|
||||||
|
amount: category.default_budget
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDelete = (category) => {
|
||||||
|
destroy(route('categories.destroy', category), {
|
||||||
|
onBefore: () => confirm('Are you sure you want to delete this record?'),
|
||||||
|
onSuccess: () => Promise.all([
|
||||||
|
handleReset(),
|
||||||
|
toast.success('data has been deleted')
|
||||||
|
])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
if(category !== null) {
|
||||||
|
put(route('categories.update', category), {
|
||||||
|
onSuccess: () => Promise.all([
|
||||||
|
handleReset(),
|
||||||
|
toast.success('The Data has been changed')
|
||||||
|
])
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
post(route('categories.store'), {
|
||||||
|
onSuccess: () => Promise.all([
|
||||||
|
handleReset(),
|
||||||
|
toast.success('Data has been saved')
|
||||||
|
])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Authenticated
|
||||||
|
errors={props.errors}
|
||||||
|
header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">Category</h2>}
|
||||||
|
>
|
||||||
|
<Head title="Category" />
|
||||||
|
|
||||||
|
<div className="flex flex-col space-y-2 md:space-y-0 md:flex-row py-12">
|
||||||
|
<div className="w-full md:w-1/3 px-6 md:pl-8">
|
||||||
|
<div className="card bg-white">
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="label">
|
||||||
|
<span className="label-text">Name</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Name"
|
||||||
|
className={`input input-bordered ${errors.name ? 'input-error' : ''}`}
|
||||||
|
id="name"
|
||||||
|
value={data.name}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
<label className="label">
|
||||||
|
<span className="label-text-alt">
|
||||||
|
{errors.name}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="label">
|
||||||
|
<span className="label-text">Description</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Description"
|
||||||
|
className={`input input-bordered ${errors.description ? 'input-error' : ''}`}
|
||||||
|
id="description"
|
||||||
|
value={data.description}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
<label className="label">
|
||||||
|
<span className="label-text-alt">
|
||||||
|
{errors.description}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="label">
|
||||||
|
<span className="label-text">Amount</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
placeholder="Amount"
|
||||||
|
className={`input input-bordered ${errors.amount ? 'input-error' : ''}`}
|
||||||
|
id="amount"
|
||||||
|
value={data.amount}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
<label className="label">
|
||||||
|
<span className="label-text-alt">
|
||||||
|
{errors.amount}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div className="card-actions">
|
||||||
|
<button className={`btn btn-primary ${processing && 'animate-spin'}`} onClick={handleSubmit} disabled={processing}>Add</button>
|
||||||
|
<button className="btn btn-secondary" onClick={handleReset} disabled={processing}>Clear</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full md:w-2/3 px-6 md:pr-8">
|
||||||
|
<div className="card bg-white">
|
||||||
|
<div className="card-body">
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="table w-full table-zebra">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Amount</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className={processing ? "opacity-70" : ""}>
|
||||||
|
{categories?.map(category => (
|
||||||
|
<tr key={category.id}>
|
||||||
|
<th>{category.id}</th>
|
||||||
|
<td>{category.name}</td>
|
||||||
|
<td>{category.description}</td>
|
||||||
|
<td>{category.default_budget}</td>
|
||||||
|
<td>
|
||||||
|
<div className="btn btn-warning mx-1" onClick={() => handleEdit(category)}>Edit</div>
|
||||||
|
<div className="btn btn-error mx-1" onClick={() => handleDelete(category)}>Delete</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<Pagination links={links} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</Authenticated>
|
||||||
|
);
|
||||||
|
}
|
@ -1,22 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import Authenticated from '@/Layouts/Authenticated';
|
|
||||||
import { Head } from '@inertiajs/inertia-react';
|
|
||||||
|
|
||||||
export default function Dashboard(props) {
|
|
||||||
return (
|
|
||||||
<Authenticated
|
|
||||||
errors={props.errors}
|
|
||||||
header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">Dashboard</h2>}
|
|
||||||
>
|
|
||||||
<Head title="Dashboard" />
|
|
||||||
|
|
||||||
<div className="py-12">
|
|
||||||
<div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
|
||||||
<div className="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
|
||||||
<div className="p-6 bg-white border-b border-gray-200">You're logged in!</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Authenticated>
|
|
||||||
);
|
|
||||||
}
|
|
Loading…
Reference in New Issue