mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-08 08:12:17 +01:00
Added time entry api endpoints; Increased phpstan level; renamed to solidtime
This commit is contained in:
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
trait PasswordValidationRules
|
||||
@@ -11,7 +12,7 @@ trait PasswordValidationRules
|
||||
/**
|
||||
* Get the validation rules used to validate passwords.
|
||||
*
|
||||
* @return array<int, \Illuminate\Contracts\Validation\Rule|array|string>
|
||||
* @return array<int, Rule|string>
|
||||
*/
|
||||
protected function passwordRules(): array
|
||||
{
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace App\Actions\Jetstream;
|
||||
use App\Models\Organization;
|
||||
use App\Models\User;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Laravel\Jetstream\Contracts\AddsTeamMembers;
|
||||
@@ -55,7 +56,7 @@ class AddOrganizationMember implements AddsTeamMembers
|
||||
/**
|
||||
* Get the validation rules for adding a team member.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
|
||||
* @return array<string, array<Rule|string>>
|
||||
*/
|
||||
protected function rules(): array
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Models\Organization;
|
||||
use App\Models\OrganizationInvitation;
|
||||
use App\Models\User;
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
@@ -59,7 +60,7 @@ class InviteOrganizationMember implements InvitesTeamMembers
|
||||
/**
|
||||
* Get the validation rules for inviting a team member.
|
||||
*
|
||||
* @return array<string, ValidationRule|array|string>
|
||||
* @return array<string, array<ValidationRule|Rule|string>>
|
||||
*/
|
||||
protected function rules(Organization $organization): array
|
||||
{
|
||||
|
||||
24
app/Exceptions/ApiException.php
Normal file
24
app/Exceptions/ApiException.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ApiException extends Exception
|
||||
{
|
||||
/**
|
||||
* Render the exception into an HTTP response.
|
||||
*/
|
||||
public function render(Request $request): JsonResponse
|
||||
{
|
||||
return response()
|
||||
->json([
|
||||
'error' => true,
|
||||
'message' => $this->getMessage(),
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
9
app/Exceptions/TimeEntryStillRunning.php
Normal file
9
app/Exceptions/TimeEntryStillRunning.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
class TimeEntryStillRunning extends ApiException
|
||||
{
|
||||
}
|
||||
@@ -25,15 +25,16 @@ class OrganizationResource extends Resource
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->columns(1)
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('name')
|
||||
->label('Name')
|
||||
->required()
|
||||
->maxLength(255),
|
||||
Forms\Components\Toggle::make('Is personal?')
|
||||
Forms\Components\Toggle::make('personal_team')
|
||||
->label('Is personal?')
|
||||
->required(),
|
||||
Forms\Components\Select::make('owner_id')
|
||||
Forms\Components\Select::make('user_id')
|
||||
->relationship(name: 'owner', titleAttribute: 'email')
|
||||
->searchable(['name', 'email'])
|
||||
->required(),
|
||||
@@ -47,7 +48,8 @@ class OrganizationResource extends Resource
|
||||
Tables\Columns\TextColumn::make('name')
|
||||
->searchable()
|
||||
->sortable(),
|
||||
Tables\Columns\ToggleColumn::make('is_personal')
|
||||
Tables\Columns\IconColumn::make('personal_team')
|
||||
->boolean()
|
||||
->label('Is personal?')
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('owner.email')
|
||||
|
||||
@@ -25,6 +25,7 @@ class UserResource extends Resource
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->columns(1)
|
||||
->schema([
|
||||
Forms\Components\TextInput::make('id')
|
||||
->label('ID')
|
||||
|
||||
@@ -11,11 +11,22 @@ use App\Http\Resources\V1\Project\ProjectResource;
|
||||
use App\Models\Organization;
|
||||
use App\Models\Project;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
class ProjectController extends Controller
|
||||
{
|
||||
protected function checkPermission(Organization $organization, string $permission, ?Project $project = null): void
|
||||
{
|
||||
parent::checkPermission($organization, $permission);
|
||||
if ($project !== null && $project->organization_id !== $organization->id) {
|
||||
throw new AuthorizationException('Project does not belong to organization');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get projects
|
||||
*
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function index(Organization $organization): JsonResource
|
||||
@@ -29,17 +40,22 @@ class ProjectController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project
|
||||
*
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function show(Organization $organization, Project $project): JsonResource
|
||||
{
|
||||
$this->checkPermission($organization, 'projects:view');
|
||||
$this->checkPermission($organization, 'projects:view', $project);
|
||||
|
||||
$project->load('organization');
|
||||
|
||||
return new ProjectResource($project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create project
|
||||
*
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function store(Organization $organization, ProjectStoreRequest $request): JsonResource
|
||||
@@ -55,11 +71,13 @@ class ProjectController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Update project
|
||||
*
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function update(Organization $organization, Project $project, ProjectUpdateRequest $request): JsonResource
|
||||
{
|
||||
$this->checkPermission($organization, 'projects:update');
|
||||
$this->checkPermission($organization, 'projects:update', $project);
|
||||
$project->name = $request->input('name');
|
||||
$project->color = $request->input('color');
|
||||
$project->save();
|
||||
@@ -68,13 +86,17 @@ class ProjectController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete project
|
||||
*
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function destroy(Organization $organization, Project $project): JsonResource
|
||||
public function destroy(Organization $organization, Project $project): JsonResponse
|
||||
{
|
||||
$this->checkPermission($organization, 'projects:delete');
|
||||
$this->checkPermission($organization, 'projects:delete', $project);
|
||||
|
||||
$project->delete();
|
||||
|
||||
return new ProjectResource($project);
|
||||
return response()
|
||||
->json(null, 204);
|
||||
}
|
||||
}
|
||||
|
||||
126
app/Http/Controllers/Api/V1/TimeEntryController.php
Normal file
126
app/Http/Controllers/Api/V1/TimeEntryController.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Controllers\Api\V1;
|
||||
|
||||
use App\Exceptions\TimeEntryStillRunning;
|
||||
use App\Http\Requests\V1\TimeEntry\TimeEntryIndexRequest;
|
||||
use App\Http\Requests\V1\TimeEntry\TimeEntryStoreRequest;
|
||||
use App\Http\Requests\V1\TimeEntry\TimeEntryUpdateRequest;
|
||||
use App\Http\Resources\V1\TimeEntry\TimeEntryCollection;
|
||||
use App\Http\Resources\V1\TimeEntry\TimeEntryResource;
|
||||
use App\Models\Organization;
|
||||
use App\Models\TimeEntry;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class TimeEntryController extends Controller
|
||||
{
|
||||
protected function checkPermission(Organization $organization, string $permission, ?TimeEntry $timeEntry = null): void
|
||||
{
|
||||
parent::checkPermission($organization, $permission);
|
||||
if ($timeEntry !== null && $timeEntry->organization_id !== $organization->getKey()) {
|
||||
throw new AuthorizationException('Time entry does not belong to organization');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time entries
|
||||
*
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function index(Organization $organization, TimeEntryIndexRequest $request): JsonResource
|
||||
{
|
||||
if ($request->has('user_id') && $request->get('user_id') === Auth::id()) {
|
||||
$this->checkPermission($organization, 'time-entries:view:own');
|
||||
} else {
|
||||
$this->checkPermission($organization, 'time-entries:view:all');
|
||||
}
|
||||
|
||||
$timeEntriesQuery = TimeEntry::query()
|
||||
->whereBelongsTo($organization, 'organization');
|
||||
|
||||
if ($request->has('before')) {
|
||||
|
||||
}
|
||||
|
||||
if ($request->has('after')) {
|
||||
|
||||
}
|
||||
|
||||
if ($request->has('user_id')) {
|
||||
$timeEntriesQuery->where('user_id', $request->input('user_id'));
|
||||
}
|
||||
|
||||
$timeEntries = $timeEntriesQuery->get();
|
||||
|
||||
return new TimeEntryCollection($timeEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create time entry
|
||||
*
|
||||
* @throws AuthorizationException|TimeEntryStillRunning
|
||||
*/
|
||||
public function store(Organization $organization, TimeEntryStoreRequest $request): JsonResource
|
||||
{
|
||||
if ($request->get('user_id') === Auth::id()) {
|
||||
$this->checkPermission($organization, 'time-entries:create:own');
|
||||
} else {
|
||||
$this->checkPermission($organization, 'time-entries:create:all');
|
||||
}
|
||||
|
||||
if ($request->get('end') === null && TimeEntry::where('user_id', $request->get('user_id'))->where('end', null)->exists()) {
|
||||
// TODO: documentation
|
||||
throw new TimeEntryStillRunning('User already has an active time entry');
|
||||
}
|
||||
|
||||
$timeEntry = new TimeEntry();
|
||||
$timeEntry->fill($request->validated());
|
||||
$timeEntry->organization()->associate($organization);
|
||||
$timeEntry->save();
|
||||
|
||||
return new TimeEntryResource($timeEntry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update time entry
|
||||
*
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function update(Organization $organization, TimeEntry $timeEntry, TimeEntryUpdateRequest $request): JsonResource
|
||||
{
|
||||
if ($timeEntry->user_id === Auth::id() && $request->get('user_id') === Auth::id()) {
|
||||
$this->checkPermission($organization, 'time-entries:update:own', $timeEntry);
|
||||
} else {
|
||||
$this->checkPermission($organization, 'time-entries:update:all', $timeEntry);
|
||||
}
|
||||
|
||||
$timeEntry->fill($request->validated());
|
||||
$timeEntry->save();
|
||||
|
||||
return new TimeEntryResource($timeEntry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete time entry
|
||||
*
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public function destroy(Organization $organization, TimeEntry $timeEntry): JsonResponse
|
||||
{
|
||||
if ($timeEntry->user_id === Auth::id()) {
|
||||
$this->checkPermission($organization, 'time-entries:delete:own', $timeEntry);
|
||||
} else {
|
||||
$this->checkPermission($organization, 'time-entries:delete:all', $timeEntry);
|
||||
}
|
||||
|
||||
$timeEntry->delete();
|
||||
|
||||
return response()
|
||||
->json(null, 204);
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,8 @@ class HandleInertiaRequests extends Middleware
|
||||
* Defines the props that are shared by default.
|
||||
*
|
||||
* @see https://inertiajs.com/shared-data
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function share(Request $request): array
|
||||
{
|
||||
|
||||
@@ -26,5 +26,6 @@ class TrustProxies extends Middleware
|
||||
Request::HEADER_X_FORWARDED_HOST |
|
||||
Request::HEADER_X_FORWARDED_PORT |
|
||||
Request::HEADER_X_FORWARDED_PROTO |
|
||||
Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||
Request::HEADER_X_FORWARDED_AWS_ELB |
|
||||
Request::HEADER_X_FORWARDED_TRAEFIK;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class ProjectStoreRequest extends FormRequest
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, ValidationRule|array<mixed>|string>
|
||||
* @return array<string, array<string|ValidationRule>>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ class ProjectUpdateRequest extends FormRequest
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, ValidationRule|array<mixed>|string>
|
||||
* @return array<string, array<string|ValidationRule>>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
|
||||
51
app/Http/Requests/V1/TimeEntry/TimeEntryIndexRequest.php
Normal file
51
app/Http/Requests/V1/TimeEntry/TimeEntryIndexRequest.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\V1\TimeEntry;
|
||||
|
||||
use App\Models\Organization;
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Korridor\LaravelModelValidationRules\Rules\ExistsEloquent;
|
||||
|
||||
/**
|
||||
* @property Organization $organization
|
||||
*/
|
||||
class TimeEntryIndexRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, array<string|ValidationRule>>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'user_id' => [
|
||||
'string',
|
||||
'uuid',
|
||||
new ExistsEloquent(User::class, null, function (Builder $builder): Builder {
|
||||
/** @var Builder<User> $builder */
|
||||
return $builder->whereHas('organizations', function (Builder $builder) {
|
||||
/** @var Builder<Organization> $builder */
|
||||
return $builder->whereKey($this->organization->getKey());
|
||||
});
|
||||
}),
|
||||
],
|
||||
'before' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'date_format:Y-m-d',
|
||||
'before:after',
|
||||
],
|
||||
'after' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'date_format:Y-m-d',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
83
app/Http/Requests/V1/TimeEntry/TimeEntryStoreRequest.php
Normal file
83
app/Http/Requests/V1/TimeEntry/TimeEntryStoreRequest.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\V1\TimeEntry;
|
||||
|
||||
use App\Models\Organization;
|
||||
use App\Models\Tag;
|
||||
use App\Models\Task;
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Korridor\LaravelModelValidationRules\Rules\ExistsEloquent;
|
||||
|
||||
/**
|
||||
* @property Organization $organization Organization from model binding
|
||||
*/
|
||||
class TimeEntryStoreRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, array<string|ValidationRule>>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'user_id' => [
|
||||
'required',
|
||||
'string',
|
||||
'uuid',
|
||||
new ExistsEloquent(User::class, null, function (Builder $builder): Builder {
|
||||
/** @var Builder<User> $builder */
|
||||
return $builder->whereHas('organizations', function (Builder $builder) {
|
||||
/** @var Builder<Organization> $builder */
|
||||
return $builder->whereKey($this->organization->getKey());
|
||||
});
|
||||
}),
|
||||
],
|
||||
'task_id' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'uuid',
|
||||
new ExistsEloquent(Task::class, null, function (Builder $builder): Builder {
|
||||
/** @var Builder<Task> $builder */
|
||||
return $builder->whereBelongsTo($this->organization, 'organization');
|
||||
}),
|
||||
],
|
||||
// Start of time entry (ISO 8601 format, UTC timezone)
|
||||
'start' => [
|
||||
'required',
|
||||
'date', // TODO
|
||||
],
|
||||
// End of time entry (ISO 8601 format, UTC timezone)
|
||||
'end' => [
|
||||
'required',
|
||||
'nullable',
|
||||
'date', // TODO
|
||||
'after:start',
|
||||
],
|
||||
// Description of time entry
|
||||
'description' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'max:255',
|
||||
],
|
||||
// List of tag IDs
|
||||
'tags' => [
|
||||
'nullable',
|
||||
'array',
|
||||
],
|
||||
'tags.*' => [
|
||||
'string',
|
||||
'uuid',
|
||||
new ExistsEloquent(Tag::class, null, function (Builder $builder): Builder {
|
||||
/** @var Builder<Tag> $builder */
|
||||
return $builder->whereBelongsTo($this->organization, 'organization');
|
||||
}),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
70
app/Http/Requests/V1/TimeEntry/TimeEntryUpdateRequest.php
Normal file
70
app/Http/Requests/V1/TimeEntry/TimeEntryUpdateRequest.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Requests\V1\TimeEntry;
|
||||
|
||||
use App\Models\Organization;
|
||||
use App\Models\Tag;
|
||||
use App\Models\Task;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Korridor\LaravelModelValidationRules\Rules\ExistsEloquent;
|
||||
|
||||
/**
|
||||
* @property Organization $organization Organization from model binding
|
||||
*/
|
||||
class TimeEntryUpdateRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, array<string|ValidationRule>>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'task_id' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'uuid',
|
||||
new ExistsEloquent(Task::class, null, function (Builder $builder): Builder {
|
||||
/** @var Builder<Task> $builder */
|
||||
return $builder->whereBelongsTo($this->organization, 'organization');
|
||||
}),
|
||||
],
|
||||
// Start of time entry (ISO 8601 format, UTC timezone)
|
||||
'start' => [
|
||||
'required',
|
||||
'date', // TODO
|
||||
],
|
||||
// End of time entry (ISO 8601 format, UTC timezone)
|
||||
'end' => [
|
||||
'required',
|
||||
'nullable',
|
||||
'date', // TODO
|
||||
'after:start',
|
||||
],
|
||||
// Description of time entry
|
||||
'description' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'max:255',
|
||||
],
|
||||
// List of tag IDs
|
||||
'tags' => [
|
||||
'nullable',
|
||||
'array',
|
||||
],
|
||||
'tags.*' => [
|
||||
'string',
|
||||
'uuid',
|
||||
new ExistsEloquent(Tag::class, null, function (Builder $builder): Builder {
|
||||
/** @var Builder<Tag> $builder */
|
||||
return $builder->whereBelongsTo($this->organization, 'organization');
|
||||
}),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
17
app/Http/Resources/V1/TimeEntry/TimeEntryCollection.php
Normal file
17
app/Http/Resources/V1/TimeEntry/TimeEntryCollection.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\V1\TimeEntry;
|
||||
|
||||
use Illuminate\Http\Resources\Json\ResourceCollection;
|
||||
|
||||
class TimeEntryCollection extends ResourceCollection
|
||||
{
|
||||
/**
|
||||
* The resource that this resource collects.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $collects = TimeEntryResource::class;
|
||||
}
|
||||
28
app/Http/Resources/V1/TimeEntry/TimeEntryResource.php
Normal file
28
app/Http/Resources/V1/TimeEntry/TimeEntryResource.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Http\Resources\V1\TimeEntry;
|
||||
|
||||
use App\Models\TimeEntry;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/**
|
||||
* @property TimeEntry $resource
|
||||
*/
|
||||
class TimeEntryResource extends JsonResource
|
||||
{
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @return array<string, string|boolean|integer>
|
||||
*/
|
||||
public function toArray(Request $request): array
|
||||
{
|
||||
return [
|
||||
/** @var string $id ID of time entry */
|
||||
'id' => $this->resource->id,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,8 @@ class OrganizationInvitation extends JetstreamTeamInvitation
|
||||
|
||||
/**
|
||||
* Get the organization that the invitation belongs to.
|
||||
*
|
||||
* @return BelongsTo<Organization, OrganizationInvitation>
|
||||
*/
|
||||
public function organization(): BelongsTo
|
||||
{
|
||||
@@ -44,6 +46,8 @@ class OrganizationInvitation extends JetstreamTeamInvitation
|
||||
|
||||
/**
|
||||
* Get the organization that the invitation belongs to.
|
||||
*
|
||||
* @return BelongsTo<Organization, OrganizationInvitation>
|
||||
*/
|
||||
public function team(): BelongsTo
|
||||
{
|
||||
|
||||
@@ -19,9 +19,13 @@ use Illuminate\Support\Carbon;
|
||||
* @property Carbon|null $end
|
||||
* @property bool $billable
|
||||
* @property array $tags
|
||||
* @property string $user_id
|
||||
* @property-read User $user
|
||||
* @property string $organization_id
|
||||
* @property-read Organization $organization
|
||||
* @property string|null $project_id
|
||||
* @property-read Project|null $project
|
||||
* @property string|null $task_id
|
||||
* @property-read Task|null $task
|
||||
*
|
||||
* @method static TimeEntryFactory factory()
|
||||
|
||||
@@ -13,9 +13,15 @@ use App\Models\Tag;
|
||||
use App\Models\Task;
|
||||
use App\Models\TimeEntry;
|
||||
use App\Models\User;
|
||||
use Dedoc\Scramble\Scramble;
|
||||
use Dedoc\Scramble\Support\Generator\OpenApi;
|
||||
use Dedoc\Scramble\Support\Generator\SecurityScheme;
|
||||
use Dedoc\Scramble\Support\Generator\SecuritySchemes\OAuthFlow;
|
||||
use Filament\Forms\Components\Section;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
@@ -55,5 +61,19 @@ class AppServiceProvider extends ServiceProvider
|
||||
Section::configureUsing(function (Section $section): void {
|
||||
$section->columns(1);
|
||||
}, null, true);
|
||||
|
||||
Scramble::extendOpenApi(function (OpenApi $openApi) {
|
||||
$openApi->secure(
|
||||
SecurityScheme::oauth2()
|
||||
->flow('authorizationCode', function (OAuthFlow $flow) {
|
||||
$flow
|
||||
->authorizationUrl('https://solidtime.test/oauth/authorize');
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
if (config('app.force_https', false) || App::isProduction()) {
|
||||
URL::forceScheme('https');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,14 @@ class JetstreamServiceProvider extends ServiceProvider
|
||||
'projects:create',
|
||||
'projects:update',
|
||||
'projects:delete',
|
||||
'time-entries:view:all',
|
||||
'time-entries:create:all',
|
||||
'time-entries:update:all',
|
||||
'time-entries:delete:all',
|
||||
'time-entries:view:own',
|
||||
'time-entries:create:own',
|
||||
'time-entries:update:own',
|
||||
'time-entries:delete:own',
|
||||
])->description('Administrator users can perform any action.');
|
||||
|
||||
Jetstream::role('manager', 'Manager', [
|
||||
@@ -63,13 +71,22 @@ class JetstreamServiceProvider extends ServiceProvider
|
||||
'projects:create',
|
||||
'projects:update',
|
||||
'projects:delete',
|
||||
'time-entries:view:all',
|
||||
'time-entries:create:all',
|
||||
'time-entries:update:all',
|
||||
'time-entries:delete:all',
|
||||
'time-entries:view:own',
|
||||
'time-entries:create:own',
|
||||
'time-entries:update:own',
|
||||
'time-entries:delete:own',
|
||||
])->description('Editor users have the ability to read, create, and update.');
|
||||
|
||||
Jetstream::role('employee', 'Employee', [
|
||||
'projects:view',
|
||||
'projects:create',
|
||||
'projects:update',
|
||||
'projects:delete',
|
||||
'time-entries:view:own',
|
||||
'time-entries:create:own',
|
||||
'time-entries:update:own',
|
||||
'time-entries:delete:own',
|
||||
])->description('Editor users have the ability to read, create, and update.');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user