Two month of work
This commit is contained in:
@@ -11,14 +11,14 @@ class NoteController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
* @param Request $request
|
||||
* @param string $modelType The type of the model (e.g., 'Customer', 'Invoice')
|
||||
* @param string $modelId The ID of the model
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function index(Request $request, $modelId)
|
||||
public function index(Request $request, string $modelType, int $modelId)
|
||||
{
|
||||
$modelType = $request->route()->parameter('modelType');
|
||||
if (!$modelType) {
|
||||
$modelType = $request->route()->getAction('modelType');
|
||||
}
|
||||
$model = app("App\\Models\\" . ucfirst($modelType))::findOrFail($modelId);
|
||||
$model = app("App\\Models\\" . $modelType)::findOrFail($modelId);
|
||||
|
||||
// Lade alle Notizen des Modells mit der Benutzerbeziehung
|
||||
$notes = $model->notes()->with('user')->orderBy('created_at', 'desc')->get();
|
||||
@@ -34,24 +34,30 @@ public function index(Request $request, $modelId)
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request, $modelId)
|
||||
public function store(Request $request)
|
||||
{
|
||||
$modelType = $request->route()->parameter('modelType');
|
||||
if (!$modelType) {
|
||||
$modelType = $request->route()->getAction('modelType');
|
||||
}
|
||||
|
||||
$validatedData = $request->validate([
|
||||
'userId' => 'required|integer|exists:users,id',
|
||||
'text' => 'required|string',
|
||||
'userId' => 'required|integer'
|
||||
'noteableId' => 'required|integer',
|
||||
'noteableType' => 'required|string',
|
||||
'createdAt' => 'sometimes|date|nullable'
|
||||
]);
|
||||
|
||||
// Convert camelCase to snake_case
|
||||
$snakeCaseData = ApiDataTransformer::camelToSnake($validatedData);
|
||||
|
||||
$model = app("App\\Models\\" . ucfirst($modelType))::findOrFail($modelId);
|
||||
$note = new Note($validatedData);
|
||||
$note->user_id = $validatedData['userId'];
|
||||
$model = app("App\\Models\\" . $snakeCaseData['noteable_type'])::findOrFail($snakeCaseData['noteable_id']);
|
||||
|
||||
// Create a new Note instance
|
||||
$note = new Note($snakeCaseData);
|
||||
|
||||
// Set the created_at field if it is provided
|
||||
if (isset($snakeCaseData['created_at'])) {
|
||||
$note->created_at = $snakeCaseData['created_at'];
|
||||
}
|
||||
|
||||
$model->notes()->save($note);
|
||||
|
||||
return response()->json($this->single($note->id), 201);
|
||||
@@ -64,22 +70,6 @@ public static function single($id)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Note $note)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Note $note)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\PipelineLane;
|
||||
use App\Models\PipelineItem;
|
||||
use App\Support\ApiDataTransformer;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class PipelineController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$lanes = PipelineLane::with(['items' => function ($q) {
|
||||
$q->withCount(['notes' => function ($query) {
|
||||
$query->where('notable_type', 'App\Models\PipelineItem');
|
||||
}])->orderBy('position');
|
||||
}])->orderBy('position')->get();
|
||||
|
||||
return $lanes->map(function ($lane) {
|
||||
return ApiDataTransformer::snakeToCamel($lane->toArray());
|
||||
});
|
||||
}
|
||||
|
||||
public function show()
|
||||
{
|
||||
return Inertia::render('Pipeline', [
|
||||
'pipeline' => $this->index(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update positions in bulk. Expect array of objects with camelCase keys: id, stage, position
|
||||
*/
|
||||
public function updatePositions(Request $request)
|
||||
{
|
||||
$payload = $request->all();
|
||||
|
||||
if (!is_array($payload)) {
|
||||
return response()->json(['message' => 'Invalid payload'], 422);
|
||||
}
|
||||
|
||||
$items = array_map(function ($item) {
|
||||
return ApiDataTransformer::camelToSnake((array)$item);
|
||||
}, $payload);
|
||||
|
||||
$validator = Validator::make(['items' => $items], [
|
||||
'items.*.id' => 'required|integer|exists:pipeline_items,id',
|
||||
'items.*.pipeline_lane_id' => 'required|integer|exists:pipeline_lanes,id',
|
||||
'items.*.title' => 'string',
|
||||
'items.*.position' => 'required|integer|min:0',
|
||||
'items.*.expected_revenue' => 'decimal',
|
||||
'items.*.description' => 'string',
|
||||
]);
|
||||
|
||||
if ($validator->fails()) {
|
||||
return response()->json(['errors' => $validator->errors()], 422);
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
foreach ($items as $it) {
|
||||
PipelineItem::where('id', $it['id'])->update([
|
||||
'pipeline_lane_id' => $it['pipeline_lane_id'],
|
||||
'position' => $it['position'],
|
||||
]);
|
||||
}
|
||||
DB::commit();
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
Log::error('Failed to update pipeline positions: ' . $e->getMessage());
|
||||
return response()->json(['message' => 'Failed to update positions'], 500);
|
||||
}
|
||||
|
||||
return response()->json(['status' => 'ok']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\PipelineItem;
|
||||
use App\Support\ApiDataTransformer;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PipelineItemController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$pipelineItems = PipelineItem::withCount(['notes' => function ($query) {
|
||||
$query->where('notable_type', 'App\Models\PipelineItem');
|
||||
}])->orderBy('position')->get();
|
||||
|
||||
return ApiDataTransformer::snakeToCamel($pipelineItems->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function single(int $id)
|
||||
{
|
||||
$pipelineItem = PipelineItem::withCount(['notes' => function ($query) {
|
||||
$query->where('notable_type', 'App\Models\PipelineItem');
|
||||
}])->orderBy('position')->findOrFail($id);
|
||||
|
||||
return ApiDataTransformer::snakeToCamel($pipelineItem->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'pipeline_lane_id' => 'required|integer|exists:pipeline_lanes,id',
|
||||
'title' => 'required|string',
|
||||
'position' => 'required|integer|min:0',
|
||||
'expected_revenue' => 'nullable|numeric',
|
||||
'due_date' => 'nullable|date',
|
||||
'description' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$pipelineItem = PipelineItem::create($validatedData);
|
||||
|
||||
return ApiDataTransformer::snakeToCamel($pipelineItem->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, PipelineItem $pipelineItem)
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'pipeline_lane_id' => 'sometimes|integer|exists:pipeline_lanes,id',
|
||||
'title' => 'sometimes|string',
|
||||
'position' => 'sometimes|integer|min:0',
|
||||
'expected_revenue' => 'nullable|numeric',
|
||||
'due_date' => 'nullable|date',
|
||||
'description' => 'nullable|string',
|
||||
]);
|
||||
|
||||
$pipelineItem->update($validatedData);
|
||||
|
||||
return ApiDataTransformer::snakeToCamel($pipelineItem->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function delete(int $id)
|
||||
{
|
||||
PipelineItem::findOrFail($id)->delete();
|
||||
return response()->noContent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Inertia\Inertia;
|
||||
use App\Models\Timesheet;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Support\ApiDataTransformer;
|
||||
|
||||
class TimesheetController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$timesheets = Timesheet::with(['entries' => function ($query) {
|
||||
$query->selectRaw('
|
||||
timesheet_id,
|
||||
SUM(hours) as total_hours,
|
||||
SUM(CASE WHEN billed = 1 THEN hours ELSE 0 END) as hours_billed,
|
||||
MIN(date) as earliest_date,
|
||||
MAX(date) as latest_date
|
||||
')
|
||||
->groupBy('timesheet_id');
|
||||
}])->get();
|
||||
|
||||
// Sort timesheets by the updated_at timestamp of the latest entry
|
||||
$timesheets = $timesheets->sortByDesc(function ($timesheet) {
|
||||
$latestEntry = $timesheet->entries()->latest('updated_at')->first();
|
||||
return $latestEntry ? $latestEntry->updated_at : $timesheet->updated_at;
|
||||
})->values();
|
||||
|
||||
$snakeCaseData = $timesheets->map(function ($timesheet) {
|
||||
$entryStats = $timesheet->entries->first();
|
||||
|
||||
return [
|
||||
'id' => $timesheet->id,
|
||||
'title' => $timesheet->title,
|
||||
'total_hours' => $entryStats->total_hours ?? 0,
|
||||
'hours_billed' => $entryStats->hours_billed ?? 0,
|
||||
'earliest_date' => $entryStats->earliest_date ?? null,
|
||||
'latest_date' => $entryStats->latest_date ?? null,
|
||||
'created_at' => $timesheet->created_at->toISOString(),
|
||||
'updated_at' => $timesheet->updated_at->toISOString(),
|
||||
];
|
||||
});
|
||||
|
||||
return ApiDataTransformer::snakeToCamel($snakeCaseData->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'title' => 'required|string|max:255',
|
||||
]);
|
||||
|
||||
$snakeCaseData = Timesheet::create($validated);
|
||||
return ApiDataTransformer::snakeToCamel($snakeCaseData->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Timesheet $timesheet)
|
||||
{
|
||||
return Inertia::render('Timesheets', [
|
||||
'timesheetData' => $this->index()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Timesheet $timesheet)
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'title' => 'required|string|max:255',
|
||||
]);
|
||||
|
||||
// Convert camelCase to snake_case
|
||||
$snakeCaseData = ApiDataTransformer::camelToSnake($validatedData);
|
||||
|
||||
$timesheet->update($snakeCaseData);
|
||||
|
||||
return $timesheet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Timesheet $timesheet)
|
||||
{
|
||||
$timesheet->delete();
|
||||
|
||||
return response()->noContent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\TimesheetEntry;
|
||||
use App\Models\Timesheet;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Support\ApiDataTransformer;
|
||||
|
||||
class TimesheetEntryController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$entries = TimesheetEntry::with(['timesheet', 'user'])->orderBy('date', 'asc')->get();
|
||||
|
||||
return $entries->map(function ($entry) {
|
||||
return ApiDataTransformer::snakeToCamel($entry->toArray());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all entries for a specific timesheet
|
||||
*/
|
||||
public function getEntriesForTimesheet(Timesheet $timesheet)
|
||||
{
|
||||
$entries = $timesheet->entries()->with('user')->orderBy('date', 'asc')->get();
|
||||
|
||||
return $entries->map(function ($entry) {
|
||||
return ApiDataTransformer::snakeToCamel($entry->toArray());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'timesheetId' => 'required|exists:timesheets,id',
|
||||
'date' => 'required|date',
|
||||
'userId' => 'required|exists:users,id',
|
||||
'description' => 'nullable|string',
|
||||
'hours' => 'required|numeric',
|
||||
'billed' => 'sometimes|boolean',
|
||||
]);
|
||||
|
||||
// Convert camelCase to snake_case
|
||||
$snakeCaseData = ApiDataTransformer::camelToSnake($validatedData);
|
||||
|
||||
$entry = TimesheetEntry::create($snakeCaseData);
|
||||
$entry = TimesheetEntry::with(['timesheet', 'user'])->find($entry->id);
|
||||
return ApiDataTransformer::snakeToCamel($entry->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(TimesheetEntry $timesheetEntry)
|
||||
{
|
||||
$entry = $timesheetEntry->load(['timesheet', 'user']);
|
||||
return ApiDataTransformer::snakeToCamel($entry->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, TimesheetEntry $timesheetEntry)
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'timesheetId' => 'sometimes|exists:timesheets,id',
|
||||
'date' => 'sometimes|date',
|
||||
'userId' => 'sometimes|exists:users,id',
|
||||
'description' => 'nullable|string',
|
||||
'hours' => 'sometimes|numeric',
|
||||
'billed' => 'sometimes|boolean',
|
||||
]);
|
||||
// Convert camelCase to snake_case
|
||||
$snakeCaseData = ApiDataTransformer::camelToSnake($validatedData);
|
||||
|
||||
$timesheetEntry->update($snakeCaseData);
|
||||
|
||||
return ApiDataTransformer::snakeToCamel($timesheetEntry->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(TimesheetEntry $timesheetEntry)
|
||||
{
|
||||
$timesheetEntry->delete();
|
||||
return response()->noContent();
|
||||
}
|
||||
|
||||
public function toggleBilled(TimesheetEntry $timesheetEntry)
|
||||
{
|
||||
$timesheetEntry = $timesheetEntry->update(['billed' => !$timesheetEntry->billed]);
|
||||
return ApiDataTransformer::snakeToCamel($timesheetEntry->toArray());
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ class TodoController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$todos = Todo::with('type')->get();
|
||||
$todos = Todo::with('type')->orderBy('due_date')->get();
|
||||
return ApiDataTransformer::snakeToCamel($todos->toArray());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user