Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 0 additions & 80 deletions .github/skills/socialite-development/SKILL.md

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
/public/storage
/storage/*.key
/storage/pail
/storage/api-docs/api-docs.json
storage/phpstan
/vendor
_ide_helper.php
Expand Down
224 changes: 224 additions & 0 deletions app/Http/Controllers/Api/V1/CategoryController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
<?php

namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use App\Models\Category;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use OpenApi\Attributes as OA;

class CategoryController extends Controller
{
#[OA\Get(
path: '/api/v1/categories',
operationId: 'categoryIndex',
summary: 'List categories',
description: 'Returns a paginated list of categories ordered by sort_order.',
tags: ['Category'],
parameters: [
new OA\Parameter(name: 'page', in: 'query', schema: new OA\Schema(type: 'integer')),
],
responses: [
new OA\Response(
response: 200,
description: 'Paginated list of categories',
content: new OA\JsonContent(
properties: [
new OA\Property(
property: 'data',
type: 'array',
items: new OA\Items(ref: '#/components/schemas/Category')
),
new OA\Property(property: 'current_page', type: 'integer', example: 1),
new OA\Property(property: 'last_page', type: 'integer', example: 1),
new OA\Property(property: 'per_page', type: 'integer', example: 20),
new OA\Property(property: 'total', type: 'integer', example: 5),
],
type: 'object'
)
),
]
)]
public function index(): JsonResponse
{
$categories = Category::query()
->orderBy('sort_order')
->orderBy('name')
->paginate(20);

return response()->json($categories, 200);
}

#[OA\Post(
path: '/api/v1/categories',
operationId: 'categoryStore',
summary: 'Create a category',
description: 'Creates a new developer category. The slug is auto-generated from the name when omitted.',
tags: ['Category'],
requestBody: new OA\RequestBody(
required: true,
content: new OA\JsonContent(
required: ['name'],
properties: [
new OA\Property(property: 'name', type: 'string', example: 'Backend'),
new OA\Property(property: 'slug', type: 'string', example: 'backend'),
new OA\Property(property: 'description', type: 'string', nullable: true, example: 'Server-side development'),
new OA\Property(property: 'is_active', type: 'boolean', example: true),
new OA\Property(property: 'sort_order', type: 'integer', example: 0),
]
)
),
responses: [
new OA\Response(
response: 201,
description: 'Category created',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'data', ref: '#/components/schemas/Category'),
],
type: 'object'
)
),
new OA\Response(response: 422, description: 'Validation error'),
]
)]
public function store(Request $request): JsonResponse
{
$validated = $request->validate([
'name' => ['required', 'string', 'max:255'],
'slug' => ['nullable', 'string', 'max:255', 'unique:categories,slug'],
'description' => ['nullable', 'string'],
'is_active' => ['sometimes', 'boolean'],
'sort_order' => ['sometimes', 'integer', 'min:0'],
]);

$category = Category::create($validated);

return response()->json(['data' => $category], 201);
}

#[OA\Get(
path: '/api/v1/categories/{category}',
operationId: 'categoryShow',
summary: 'Get a single category',
description: 'Returns a category by ID.',
tags: ['Category'],
parameters: [
new OA\Parameter(
name: 'category',
description: 'ID of the category to retrieve',
in: 'path',
required: true,
schema: new OA\Schema(type: 'integer', example: 1)
),
],
responses: [
new OA\Response(
response: 200,
description: 'Category retrieved successfully',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'data', ref: '#/components/schemas/Category'),
],
type: 'object'
)
),
new OA\Response(response: 404, description: 'Category not found'),
]
)]
public function show(Category $category): JsonResponse
{
return response()->json(['data' => $category], 200);
}

#[OA\Put(
path: '/api/v1/categories/{category}',
operationId: 'categoryUpdate',
summary: 'Update a category',
description: 'Updates an existing category.',
tags: ['Category'],
parameters: [
new OA\Parameter(
name: 'category',
description: 'ID of the category to update',
in: 'path',
required: true,
schema: new OA\Schema(type: 'integer', example: 1)
),
],
requestBody: new OA\RequestBody(
required: true,
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'name', type: 'string', example: 'Backend'),
new OA\Property(property: 'slug', type: 'string', example: 'backend'),
new OA\Property(property: 'description', type: 'string', nullable: true, example: 'Server-side development'),
new OA\Property(property: 'is_active', type: 'boolean', example: true),
new OA\Property(property: 'sort_order', type: 'integer', example: 0),
]
)
),
responses: [
new OA\Response(
response: 200,
description: 'Category updated successfully',
content: new OA\JsonContent(
properties: [
new OA\Property(property: 'data', ref: '#/components/schemas/Category'),
],
type: 'object'
)
),
new OA\Response(response: 404, description: 'Category not found'),
new OA\Response(response: 422, description: 'Validation error'),
]
)]
public function update(Request $request, Category $category): JsonResponse
{
$validated = $request->validate([
'name' => ['sometimes', 'required', 'string', 'max:255'],
'slug' => [
'nullable',
'string',
'max:255',
Rule::unique('categories', 'slug')->ignore($category->id),
],
'description' => ['nullable', 'string'],
'is_active' => ['sometimes', 'boolean'],
'sort_order' => ['sometimes', 'integer', 'min:0'],
]);

$category->update($validated);

return response()->json(['data' => $category], 200);
}

#[OA\Delete(
path: '/api/v1/categories/{category}',
operationId: 'categoryDestroy',
summary: 'Delete a category',
description: 'Soft-deletes a category.',
tags: ['Category'],
parameters: [
new OA\Parameter(
name: 'category',
description: 'ID of the category to delete',
in: 'path',
required: true,
schema: new OA\Schema(type: 'integer', example: 1)
),
],
responses: [
new OA\Response(response: 204, description: 'Category deleted successfully'),
new OA\Response(response: 404, description: 'Category not found'),
]
)]
public function destroy(Category $category): JsonResponse
{
$category->delete();

return response()->json(['data' => null, 'message' => 'Category deleted successfully'], 204);
}
}
8 changes: 4 additions & 4 deletions app/Http/Controllers/Api/V1/ProfileController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function __construct(private ProfileService $profileService)
path: '/api/v1/profiles',
operationId: 'profileIndex',
summary: 'List profiles',
description: 'Returns a paginated list of profiles, including their skills and projects.',
description: 'Returns a paginated list of profiles, including their skills, projects and category.',
tags: ['Profile'],
parameters: [
new OA\Parameter(
Expand Down Expand Up @@ -55,7 +55,7 @@ public function __construct(private ProfileService $profileService)
)]
public function index(): JsonResponse
{
$posts = Profile::with(['skills', 'projects'])->paginate(20);
$posts = Profile::with(['skills', 'projects', 'category'])->paginate(20);
return response()->json($posts, 200);
}

Expand Down Expand Up @@ -93,7 +93,7 @@ public function store(Request $request): JsonResponse
path: '/api/v1/profiles/{profile}',
operationId: 'profileShow',
summary: 'Get a single profile',
description: 'Returns a profile (route-model-bound by ID) along with its skills and projects.',
description: 'Returns a profile (route-model-bound by ID) along with its skills, projects and category.',
tags: ['Profile'],
parameters: [
new OA\Parameter(
Expand Down Expand Up @@ -129,7 +129,7 @@ public function store(Request $request): JsonResponse
)]
public function show(Profile $profile): JsonResponse
{
return response()->json(['data' => $profile->load(['skills', 'projects'])], 200);
return response()->json(['data' => $profile->load(['skills', 'projects', 'category'])], 200);
}


Expand Down
8 changes: 8 additions & 0 deletions app/Http/Requests/UpdateProfileRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
new OA\Property(property: 'bio', type: 'string', description: 'The profile bio', example: 'Product designer based in Lille.'),
new OA\Property(property: 'headline', type: 'string', description: 'The profile headline', example: 'Senior Product Designer'),
new OA\Property(property: 'location', type: 'string', description: 'The profile location', example: 'Lille, France'),
new OA\Property(
property: 'category_id',
type: 'integer',
nullable: true,
description: 'ID of the category to assign to this profile',
example: 1
),
new OA\Property(
property: 'skills',
description: 'Skills linked to this profile',
Expand Down Expand Up @@ -67,6 +74,7 @@ public function rules(): array
'bio' => ['nullable', 'string', 'max:2000'],
'headline' => ['nullable', 'string', 'max:2000'],
'location' => ['nullable', 'string', 'max:255'],
'category_id' => ['nullable', 'integer', 'exists:categories,id'],

// Skills
'skills' => ['sometimes', 'array'],
Expand Down
Loading
Loading