fix(ui): Team member form and status badge improvements
1. Status badge: Render HTML in DataTable cell using {@html}
2. Form alignment: Consistent horizontal layout for all fields
- Labels on left (w-28), inputs on right (flex-1)
- Added dollar sign prefix for hourly rate
- Wider modal (max-w-lg)
3. API docs: Regenerated with scribe:generate
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
# GENERATED. YOU SHOULDN'T MODIFY OR DELETE THIS FILE.
|
# GENERATED. YOU SHOULDN'T MODIFY OR DELETE THIS FILE.
|
||||||
# Scribe uses this file to know when you change something manually in your docs.
|
# Scribe uses this file to know when you change something manually in your docs.
|
||||||
.scribe/intro.md=63d14186b9cbbb0a80ee87cd913db091
|
.scribe/intro.md=4bf90470e636417926ae5d9227747d45
|
||||||
.scribe/auth.md=5c5a140c89034600ae349aede2a22ec8
|
.scribe/auth.md=9bee2b1ef8a238b2e58613fa636d5f39
|
||||||
@@ -1,7 +1,3 @@
|
|||||||
# Authenticating requests
|
# Authenticating requests
|
||||||
|
|
||||||
To authenticate requests, include an **`Authorization`** header with the value **`"Bearer Bearer {token}"`**.
|
This API is not authenticated.
|
||||||
|
|
||||||
All authenticated endpoints are marked with a `requires authentication` badge in the documentation below.
|
|
||||||
|
|
||||||
Get tokens from `POST /api/auth/login`, send access token as `Bearer {token}`, and renew with `POST /api/auth/refresh` before access token expiry.
|
|
||||||
|
|||||||
214
backend/.scribe/endpoints.cache/00.yaml
Normal file
214
backend/.scribe/endpoints.cache/00.yaml
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
## Autogenerated by Scribe. DO NOT MODIFY.
|
||||||
|
|
||||||
|
name: Authentication
|
||||||
|
description: |-
|
||||||
|
|
||||||
|
Endpoints for JWT authentication and session lifecycle.
|
||||||
|
endpoints:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- POST
|
||||||
|
uri: api/auth/login
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: Authentication
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for JWT authentication and session lifecycle.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Login and get tokens'
|
||||||
|
description: 'Authenticate with email and password to receive an access token and refresh token.'
|
||||||
|
authenticated: false
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters: []
|
||||||
|
cleanUrlParameters: []
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters:
|
||||||
|
email:
|
||||||
|
custom: []
|
||||||
|
name: email
|
||||||
|
description: 'User email address.'
|
||||||
|
required: true
|
||||||
|
example: user@example.com
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
password:
|
||||||
|
custom: []
|
||||||
|
name: password
|
||||||
|
description: 'User password.'
|
||||||
|
required: true
|
||||||
|
example: secret123
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanBodyParameters:
|
||||||
|
email: user@example.com
|
||||||
|
password: secret123
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: |-
|
||||||
|
{
|
||||||
|
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
|
||||||
|
"refresh_token": "abc123def456",
|
||||||
|
"token_type": "bearer",
|
||||||
|
"expires_in": 3600,
|
||||||
|
"user": {
|
||||||
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"name": "Alice Johnson",
|
||||||
|
"email": "user@example.com",
|
||||||
|
"role": "manager"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 401
|
||||||
|
content: '{"message":"Invalid credentials"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 403
|
||||||
|
content: '{"message":"Account is inactive"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 422
|
||||||
|
content: '{"errors":{"email":["The email field is required."],"password":["The password field is required."]}}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- POST
|
||||||
|
uri: api/auth/refresh
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: Authentication
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for JWT authentication and session lifecycle.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Refresh access token'
|
||||||
|
description: 'Exchange a valid refresh token for a new access token and refresh token pair.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters: []
|
||||||
|
cleanUrlParameters: []
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters:
|
||||||
|
refresh_token:
|
||||||
|
custom: []
|
||||||
|
name: refresh_token
|
||||||
|
description: 'Refresh token returned by login.'
|
||||||
|
required: true
|
||||||
|
example: abc123def456
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanBodyParameters:
|
||||||
|
refresh_token: abc123def456
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: |-
|
||||||
|
{
|
||||||
|
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
|
||||||
|
"refresh_token": "newtoken123",
|
||||||
|
"token_type": "bearer",
|
||||||
|
"expires_in": 3600
|
||||||
|
}
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 401
|
||||||
|
content: '{"message":"Invalid or expired refresh token"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- POST
|
||||||
|
uri: api/auth/logout
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: Authentication
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for JWT authentication and session lifecycle.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Logout current session'
|
||||||
|
description: 'Invalidate a refresh token and end the active authenticated session.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters: []
|
||||||
|
cleanUrlParameters: []
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters:
|
||||||
|
refresh_token:
|
||||||
|
custom: []
|
||||||
|
name: refresh_token
|
||||||
|
description: 'Optional refresh token to invalidate immediately.'
|
||||||
|
required: false
|
||||||
|
example: abc123def456
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanBodyParameters:
|
||||||
|
refresh_token: abc123def456
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: '{"message":"Logged out successfully"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
443
backend/.scribe/endpoints.cache/01.yaml
Normal file
443
backend/.scribe/endpoints.cache/01.yaml
Normal file
@@ -0,0 +1,443 @@
|
|||||||
|
## Autogenerated by Scribe. DO NOT MODIFY.
|
||||||
|
|
||||||
|
name: 'Team Members'
|
||||||
|
description: |-
|
||||||
|
|
||||||
|
Endpoints for managing team members.
|
||||||
|
endpoints:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- GET
|
||||||
|
uri: api/team-members
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: 'Team Members'
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for managing team members.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'List all team members'
|
||||||
|
description: 'Get a list of all team members with optional filtering by active status.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters: []
|
||||||
|
cleanUrlParameters: []
|
||||||
|
queryParameters:
|
||||||
|
active:
|
||||||
|
custom: []
|
||||||
|
name: active
|
||||||
|
description: 'Filter by active status.'
|
||||||
|
required: false
|
||||||
|
example: true
|
||||||
|
type: boolean
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanQueryParameters:
|
||||||
|
active: true
|
||||||
|
bodyParameters: []
|
||||||
|
cleanBodyParameters: []
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: |-
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"name": "John Doe",
|
||||||
|
"role_id": 1,
|
||||||
|
"role": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Backend Developer"
|
||||||
|
},
|
||||||
|
"hourly_rate": "150.00",
|
||||||
|
"active": true,
|
||||||
|
"created_at": "2024-01-15T10:00:00.000000Z",
|
||||||
|
"updated_at": "2024-01-15T10:00:00.000000Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- POST
|
||||||
|
uri: api/team-members
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: 'Team Members'
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for managing team members.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Create a new team member'
|
||||||
|
description: 'Create a new team member with name, role, and hourly rate.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters: []
|
||||||
|
cleanUrlParameters: []
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters:
|
||||||
|
name:
|
||||||
|
custom: []
|
||||||
|
name: name
|
||||||
|
description: 'Team member name.'
|
||||||
|
required: true
|
||||||
|
example: 'John Doe'
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
role_id:
|
||||||
|
custom: []
|
||||||
|
name: role_id
|
||||||
|
description: 'Role ID.'
|
||||||
|
required: true
|
||||||
|
example: 1
|
||||||
|
type: integer
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
hourly_rate:
|
||||||
|
custom: []
|
||||||
|
name: hourly_rate
|
||||||
|
description: 'Hourly rate (must be > 0).'
|
||||||
|
required: true
|
||||||
|
example: '150.00'
|
||||||
|
type: numeric
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
active:
|
||||||
|
custom: []
|
||||||
|
name: active
|
||||||
|
description: 'Active status (defaults to true).'
|
||||||
|
required: false
|
||||||
|
example: true
|
||||||
|
type: boolean
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanBodyParameters:
|
||||||
|
name: 'John Doe'
|
||||||
|
role_id: 1
|
||||||
|
hourly_rate: '150.00'
|
||||||
|
active: true
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 201
|
||||||
|
content: |-
|
||||||
|
{
|
||||||
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"name": "John Doe",
|
||||||
|
"role_id": 1,
|
||||||
|
"role": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Backend Developer"
|
||||||
|
},
|
||||||
|
"hourly_rate": "150.00",
|
||||||
|
"active": true,
|
||||||
|
"created_at": "2024-01-15T10:00:00.000000Z",
|
||||||
|
"updated_at": "2024-01-15T10:00:00.000000Z"
|
||||||
|
}
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 422
|
||||||
|
content: '{"message":"Validation failed","errors":{"name":["The name field is required."],"hourly_rate":["Hourly rate must be greater than 0"]}}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- GET
|
||||||
|
uri: 'api/team-members/{id}'
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: 'Team Members'
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for managing team members.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Get a single team member'
|
||||||
|
description: 'Get details of a specific team member by ID.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters:
|
||||||
|
id:
|
||||||
|
custom: []
|
||||||
|
name: id
|
||||||
|
description: 'Team member UUID.'
|
||||||
|
required: true
|
||||||
|
example: 550e8400-e29b-41d4-a716-446655440000
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanUrlParameters:
|
||||||
|
id: 550e8400-e29b-41d4-a716-446655440000
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters: []
|
||||||
|
cleanBodyParameters: []
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: |-
|
||||||
|
{
|
||||||
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"name": "John Doe",
|
||||||
|
"role_id": 1,
|
||||||
|
"role": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Backend Developer"
|
||||||
|
},
|
||||||
|
"hourly_rate": "150.00",
|
||||||
|
"active": true,
|
||||||
|
"created_at": "2024-01-15T10:00:00.000000Z",
|
||||||
|
"updated_at": "2024-01-15T10:00:00.000000Z"
|
||||||
|
}
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 404
|
||||||
|
content: '{"message":"Team member not found"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- PUT
|
||||||
|
- PATCH
|
||||||
|
uri: 'api/team-members/{id}'
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: 'Team Members'
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for managing team members.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Update a team member'
|
||||||
|
description: 'Update details of an existing team member.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters:
|
||||||
|
id:
|
||||||
|
custom: []
|
||||||
|
name: id
|
||||||
|
description: 'Team member UUID.'
|
||||||
|
required: true
|
||||||
|
example: 550e8400-e29b-41d4-a716-446655440000
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanUrlParameters:
|
||||||
|
id: 550e8400-e29b-41d4-a716-446655440000
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters:
|
||||||
|
name:
|
||||||
|
custom: []
|
||||||
|
name: name
|
||||||
|
description: 'Team member name.'
|
||||||
|
required: false
|
||||||
|
example: 'John Doe'
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
role_id:
|
||||||
|
custom: []
|
||||||
|
name: role_id
|
||||||
|
description: 'Role ID.'
|
||||||
|
required: false
|
||||||
|
example: 1
|
||||||
|
type: integer
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
hourly_rate:
|
||||||
|
custom: []
|
||||||
|
name: hourly_rate
|
||||||
|
description: 'Hourly rate (must be > 0).'
|
||||||
|
required: false
|
||||||
|
example: '175.00'
|
||||||
|
type: numeric
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
active:
|
||||||
|
custom: []
|
||||||
|
name: active
|
||||||
|
description: 'Active status.'
|
||||||
|
required: false
|
||||||
|
example: false
|
||||||
|
type: boolean
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanBodyParameters:
|
||||||
|
name: 'John Doe'
|
||||||
|
role_id: 1
|
||||||
|
hourly_rate: '175.00'
|
||||||
|
active: false
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: |-
|
||||||
|
{
|
||||||
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"name": "John Doe",
|
||||||
|
"role_id": 1,
|
||||||
|
"role": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Backend Developer"
|
||||||
|
},
|
||||||
|
"hourly_rate": "175.00",
|
||||||
|
"active": false,
|
||||||
|
"created_at": "2024-01-15T10:00:00.000000Z",
|
||||||
|
"updated_at": "2024-01-15T11:00:00.000000Z"
|
||||||
|
}
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 404
|
||||||
|
content: '{"message":"Team member not found"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 422
|
||||||
|
content: '{"message":"Validation failed","errors":{"hourly_rate":["Hourly rate must be greater than 0"]}}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- DELETE
|
||||||
|
uri: 'api/team-members/{id}'
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: 'Team Members'
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for managing team members.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Delete a team member'
|
||||||
|
description: 'Delete a team member. Cannot delete if member has allocations or actuals.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters:
|
||||||
|
id:
|
||||||
|
custom: []
|
||||||
|
name: id
|
||||||
|
description: 'Team member UUID.'
|
||||||
|
required: true
|
||||||
|
example: 550e8400-e29b-41d4-a716-446655440000
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanUrlParameters:
|
||||||
|
id: 550e8400-e29b-41d4-a716-446655440000
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters: []
|
||||||
|
cleanBodyParameters: []
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: '{"message":"Team member deleted successfully"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 404
|
||||||
|
content: '{"message":"Team member not found"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 422
|
||||||
|
content: '{"message":"Cannot delete team member with active allocations","suggestion":"Consider deactivating the team member instead"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 422
|
||||||
|
content: '{"message":"Cannot delete team member with historical data","suggestion":"Consider deactivating the team member instead"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
212
backend/.scribe/endpoints/00.yaml
Normal file
212
backend/.scribe/endpoints/00.yaml
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
name: Authentication
|
||||||
|
description: |-
|
||||||
|
|
||||||
|
Endpoints for JWT authentication and session lifecycle.
|
||||||
|
endpoints:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- POST
|
||||||
|
uri: api/auth/login
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: Authentication
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for JWT authentication and session lifecycle.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Login and get tokens'
|
||||||
|
description: 'Authenticate with email and password to receive an access token and refresh token.'
|
||||||
|
authenticated: false
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters: []
|
||||||
|
cleanUrlParameters: []
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters:
|
||||||
|
email:
|
||||||
|
custom: []
|
||||||
|
name: email
|
||||||
|
description: 'User email address.'
|
||||||
|
required: true
|
||||||
|
example: user@example.com
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
password:
|
||||||
|
custom: []
|
||||||
|
name: password
|
||||||
|
description: 'User password.'
|
||||||
|
required: true
|
||||||
|
example: secret123
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanBodyParameters:
|
||||||
|
email: user@example.com
|
||||||
|
password: secret123
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: |-
|
||||||
|
{
|
||||||
|
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
|
||||||
|
"refresh_token": "abc123def456",
|
||||||
|
"token_type": "bearer",
|
||||||
|
"expires_in": 3600,
|
||||||
|
"user": {
|
||||||
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"name": "Alice Johnson",
|
||||||
|
"email": "user@example.com",
|
||||||
|
"role": "manager"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 401
|
||||||
|
content: '{"message":"Invalid credentials"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 403
|
||||||
|
content: '{"message":"Account is inactive"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 422
|
||||||
|
content: '{"errors":{"email":["The email field is required."],"password":["The password field is required."]}}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- POST
|
||||||
|
uri: api/auth/refresh
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: Authentication
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for JWT authentication and session lifecycle.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Refresh access token'
|
||||||
|
description: 'Exchange a valid refresh token for a new access token and refresh token pair.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters: []
|
||||||
|
cleanUrlParameters: []
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters:
|
||||||
|
refresh_token:
|
||||||
|
custom: []
|
||||||
|
name: refresh_token
|
||||||
|
description: 'Refresh token returned by login.'
|
||||||
|
required: true
|
||||||
|
example: abc123def456
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanBodyParameters:
|
||||||
|
refresh_token: abc123def456
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: |-
|
||||||
|
{
|
||||||
|
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
|
||||||
|
"refresh_token": "newtoken123",
|
||||||
|
"token_type": "bearer",
|
||||||
|
"expires_in": 3600
|
||||||
|
}
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 401
|
||||||
|
content: '{"message":"Invalid or expired refresh token"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- POST
|
||||||
|
uri: api/auth/logout
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: Authentication
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for JWT authentication and session lifecycle.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Logout current session'
|
||||||
|
description: 'Invalidate a refresh token and end the active authenticated session.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters: []
|
||||||
|
cleanUrlParameters: []
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters:
|
||||||
|
refresh_token:
|
||||||
|
custom: []
|
||||||
|
name: refresh_token
|
||||||
|
description: 'Optional refresh token to invalidate immediately.'
|
||||||
|
required: false
|
||||||
|
example: abc123def456
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanBodyParameters:
|
||||||
|
refresh_token: abc123def456
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: '{"message":"Logged out successfully"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
441
backend/.scribe/endpoints/01.yaml
Normal file
441
backend/.scribe/endpoints/01.yaml
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
name: 'Team Members'
|
||||||
|
description: |-
|
||||||
|
|
||||||
|
Endpoints for managing team members.
|
||||||
|
endpoints:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- GET
|
||||||
|
uri: api/team-members
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: 'Team Members'
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for managing team members.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'List all team members'
|
||||||
|
description: 'Get a list of all team members with optional filtering by active status.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters: []
|
||||||
|
cleanUrlParameters: []
|
||||||
|
queryParameters:
|
||||||
|
active:
|
||||||
|
custom: []
|
||||||
|
name: active
|
||||||
|
description: 'Filter by active status.'
|
||||||
|
required: false
|
||||||
|
example: true
|
||||||
|
type: boolean
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanQueryParameters:
|
||||||
|
active: true
|
||||||
|
bodyParameters: []
|
||||||
|
cleanBodyParameters: []
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: |-
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"name": "John Doe",
|
||||||
|
"role_id": 1,
|
||||||
|
"role": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Backend Developer"
|
||||||
|
},
|
||||||
|
"hourly_rate": "150.00",
|
||||||
|
"active": true,
|
||||||
|
"created_at": "2024-01-15T10:00:00.000000Z",
|
||||||
|
"updated_at": "2024-01-15T10:00:00.000000Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- POST
|
||||||
|
uri: api/team-members
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: 'Team Members'
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for managing team members.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Create a new team member'
|
||||||
|
description: 'Create a new team member with name, role, and hourly rate.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters: []
|
||||||
|
cleanUrlParameters: []
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters:
|
||||||
|
name:
|
||||||
|
custom: []
|
||||||
|
name: name
|
||||||
|
description: 'Team member name.'
|
||||||
|
required: true
|
||||||
|
example: 'John Doe'
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
role_id:
|
||||||
|
custom: []
|
||||||
|
name: role_id
|
||||||
|
description: 'Role ID.'
|
||||||
|
required: true
|
||||||
|
example: 1
|
||||||
|
type: integer
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
hourly_rate:
|
||||||
|
custom: []
|
||||||
|
name: hourly_rate
|
||||||
|
description: 'Hourly rate (must be > 0).'
|
||||||
|
required: true
|
||||||
|
example: '150.00'
|
||||||
|
type: numeric
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
active:
|
||||||
|
custom: []
|
||||||
|
name: active
|
||||||
|
description: 'Active status (defaults to true).'
|
||||||
|
required: false
|
||||||
|
example: true
|
||||||
|
type: boolean
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanBodyParameters:
|
||||||
|
name: 'John Doe'
|
||||||
|
role_id: 1
|
||||||
|
hourly_rate: '150.00'
|
||||||
|
active: true
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 201
|
||||||
|
content: |-
|
||||||
|
{
|
||||||
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"name": "John Doe",
|
||||||
|
"role_id": 1,
|
||||||
|
"role": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Backend Developer"
|
||||||
|
},
|
||||||
|
"hourly_rate": "150.00",
|
||||||
|
"active": true,
|
||||||
|
"created_at": "2024-01-15T10:00:00.000000Z",
|
||||||
|
"updated_at": "2024-01-15T10:00:00.000000Z"
|
||||||
|
}
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 422
|
||||||
|
content: '{"message":"Validation failed","errors":{"name":["The name field is required."],"hourly_rate":["Hourly rate must be greater than 0"]}}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- GET
|
||||||
|
uri: 'api/team-members/{id}'
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: 'Team Members'
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for managing team members.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Get a single team member'
|
||||||
|
description: 'Get details of a specific team member by ID.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters:
|
||||||
|
id:
|
||||||
|
custom: []
|
||||||
|
name: id
|
||||||
|
description: 'Team member UUID.'
|
||||||
|
required: true
|
||||||
|
example: 550e8400-e29b-41d4-a716-446655440000
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanUrlParameters:
|
||||||
|
id: 550e8400-e29b-41d4-a716-446655440000
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters: []
|
||||||
|
cleanBodyParameters: []
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: |-
|
||||||
|
{
|
||||||
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"name": "John Doe",
|
||||||
|
"role_id": 1,
|
||||||
|
"role": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Backend Developer"
|
||||||
|
},
|
||||||
|
"hourly_rate": "150.00",
|
||||||
|
"active": true,
|
||||||
|
"created_at": "2024-01-15T10:00:00.000000Z",
|
||||||
|
"updated_at": "2024-01-15T10:00:00.000000Z"
|
||||||
|
}
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 404
|
||||||
|
content: '{"message":"Team member not found"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- PUT
|
||||||
|
- PATCH
|
||||||
|
uri: 'api/team-members/{id}'
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: 'Team Members'
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for managing team members.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Update a team member'
|
||||||
|
description: 'Update details of an existing team member.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters:
|
||||||
|
id:
|
||||||
|
custom: []
|
||||||
|
name: id
|
||||||
|
description: 'Team member UUID.'
|
||||||
|
required: true
|
||||||
|
example: 550e8400-e29b-41d4-a716-446655440000
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanUrlParameters:
|
||||||
|
id: 550e8400-e29b-41d4-a716-446655440000
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters:
|
||||||
|
name:
|
||||||
|
custom: []
|
||||||
|
name: name
|
||||||
|
description: 'Team member name.'
|
||||||
|
required: false
|
||||||
|
example: 'John Doe'
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
role_id:
|
||||||
|
custom: []
|
||||||
|
name: role_id
|
||||||
|
description: 'Role ID.'
|
||||||
|
required: false
|
||||||
|
example: 1
|
||||||
|
type: integer
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
hourly_rate:
|
||||||
|
custom: []
|
||||||
|
name: hourly_rate
|
||||||
|
description: 'Hourly rate (must be > 0).'
|
||||||
|
required: false
|
||||||
|
example: '175.00'
|
||||||
|
type: numeric
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
active:
|
||||||
|
custom: []
|
||||||
|
name: active
|
||||||
|
description: 'Active status.'
|
||||||
|
required: false
|
||||||
|
example: false
|
||||||
|
type: boolean
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanBodyParameters:
|
||||||
|
name: 'John Doe'
|
||||||
|
role_id: 1
|
||||||
|
hourly_rate: '175.00'
|
||||||
|
active: false
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: |-
|
||||||
|
{
|
||||||
|
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||||
|
"name": "John Doe",
|
||||||
|
"role_id": 1,
|
||||||
|
"role": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Backend Developer"
|
||||||
|
},
|
||||||
|
"hourly_rate": "175.00",
|
||||||
|
"active": false,
|
||||||
|
"created_at": "2024-01-15T10:00:00.000000Z",
|
||||||
|
"updated_at": "2024-01-15T11:00:00.000000Z"
|
||||||
|
}
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 404
|
||||||
|
content: '{"message":"Team member not found"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 422
|
||||||
|
content: '{"message":"Validation failed","errors":{"hourly_rate":["Hourly rate must be greater than 0"]}}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
httpMethods:
|
||||||
|
- DELETE
|
||||||
|
uri: 'api/team-members/{id}'
|
||||||
|
metadata:
|
||||||
|
custom: []
|
||||||
|
groupName: 'Team Members'
|
||||||
|
groupDescription: |-
|
||||||
|
|
||||||
|
Endpoints for managing team members.
|
||||||
|
subgroup: ''
|
||||||
|
subgroupDescription: ''
|
||||||
|
title: 'Delete a team member'
|
||||||
|
description: 'Delete a team member. Cannot delete if member has allocations or actuals.'
|
||||||
|
authenticated: true
|
||||||
|
deprecated: false
|
||||||
|
headers:
|
||||||
|
Content-Type: application/json
|
||||||
|
Accept: application/json
|
||||||
|
urlParameters:
|
||||||
|
id:
|
||||||
|
custom: []
|
||||||
|
name: id
|
||||||
|
description: 'Team member UUID.'
|
||||||
|
required: true
|
||||||
|
example: 550e8400-e29b-41d4-a716-446655440000
|
||||||
|
type: string
|
||||||
|
enumValues: []
|
||||||
|
exampleWasSpecified: true
|
||||||
|
nullable: false
|
||||||
|
deprecated: false
|
||||||
|
cleanUrlParameters:
|
||||||
|
id: 550e8400-e29b-41d4-a716-446655440000
|
||||||
|
queryParameters: []
|
||||||
|
cleanQueryParameters: []
|
||||||
|
bodyParameters: []
|
||||||
|
cleanBodyParameters: []
|
||||||
|
fileParameters: []
|
||||||
|
responses:
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 200
|
||||||
|
content: '{"message":"Team member deleted successfully"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 404
|
||||||
|
content: '{"message":"Team member not found"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 422
|
||||||
|
content: '{"message":"Cannot delete team member with active allocations","suggestion":"Consider deactivating the team member instead"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
-
|
||||||
|
custom: []
|
||||||
|
status: 422
|
||||||
|
content: '{"message":"Cannot delete team member with historical data","suggestion":"Consider deactivating the team member instead"}'
|
||||||
|
headers: []
|
||||||
|
description: ''
|
||||||
|
responseFields: []
|
||||||
|
auth: []
|
||||||
|
controller: null
|
||||||
|
method: null
|
||||||
|
route: null
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
Resource planning and capacity management API
|
|
||||||
|
|
||||||
<aside>
|
<aside>
|
||||||
<strong>Base URL</strong>: <code>http://localhost/api</code>
|
<strong>Base URL</strong>: <code>http://localhost</code>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
Authenticate by sending `Authorization: Bearer {access_token}` on protected endpoints.
|
This documentation aims to provide all the information you need to work with our API.
|
||||||
|
|
||||||
Access tokens are valid for 60 minutes. Use `/api/auth/refresh` with your refresh token to obtain a new access token and refresh token pair.
|
<aside>As you scroll, you'll see code examples for working with the API in different programming languages in the dark area to the right (or as part of the content on mobile).
|
||||||
|
You can switch the language used with the tabs at the top right (or from the nav menu at the top left on mobile).</aside>
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,32 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Knuckles\Scribe\Config\AuthIn;
|
||||||
|
use Knuckles\Scribe\Config\Defaults;
|
||||||
|
use Knuckles\Scribe\Extracting\Strategies;
|
||||||
|
|
||||||
|
use function Knuckles\Scribe\Config\configureStrategy;
|
||||||
|
use function Knuckles\Scribe\Config\removeStrategies;
|
||||||
|
|
||||||
// Only the most common configs are shown. See the https://scribe.knuckles.wtf/laravel/reference/config for all.
|
// Only the most common configs are shown. See the https://scribe.knuckles.wtf/laravel/reference/config for all.
|
||||||
|
|
||||||
return [
|
return [
|
||||||
// The HTML <title> for the generated documentation.
|
// The HTML <title> for the generated documentation.
|
||||||
'title' => 'Headroom API',
|
'title' => config('app.name').' API Documentation',
|
||||||
|
|
||||||
// A short description of your API. Will be included in the docs webpage, Postman collection and OpenAPI spec.
|
// A short description of your API. Will be included in the docs webpage, Postman collection and OpenAPI spec.
|
||||||
'description' => 'Resource planning and capacity management API',
|
'description' => '',
|
||||||
|
|
||||||
// Text to place in the "Introduction" section, right after the `description`. Markdown and HTML are supported.
|
// Text to place in the "Introduction" section, right after the `description`. Markdown and HTML are supported.
|
||||||
'intro_text' => <<<'INTRO'
|
'intro_text' => <<<'INTRO'
|
||||||
Authenticate by sending `Authorization: Bearer {access_token}` on protected endpoints.
|
This documentation aims to provide all the information you need to work with our API.
|
||||||
|
|
||||||
Access tokens are valid for 60 minutes. Use `/api/auth/refresh` with your refresh token to obtain a new access token and refresh token pair.
|
<aside>As you scroll, you'll see code examples for working with the API in different programming languages in the dark area to the right (or as part of the content on mobile).
|
||||||
|
You can switch the language used with the tabs at the top right (or from the nav menu at the top left on mobile).</aside>
|
||||||
INTRO,
|
INTRO,
|
||||||
|
|
||||||
// The base URL displayed in the docs.
|
// The base URL displayed in the docs.
|
||||||
// If you're using `laravel` type, you can set this to a dynamic string, like '{{ config("app.tenant_url") }}' to get a dynamic base URL.
|
// If you're using `laravel` type, you can set this to a dynamic string, like '{{ config("app.tenant_url") }}' to get a dynamic base URL.
|
||||||
'base_url' => rtrim(config('app.url'), '/').'/api',
|
'base_url' => config('app.url'),
|
||||||
|
|
||||||
// Routes to include in the docs
|
// Routes to include in the docs
|
||||||
'routes' => [
|
'routes' => [
|
||||||
@@ -38,7 +46,7 @@ return [
|
|||||||
|
|
||||||
// Exclude these routes even if they matched the rules above.
|
// Exclude these routes even if they matched the rules above.
|
||||||
'exclude' => [
|
'exclude' => [
|
||||||
'api/user',
|
// 'GET /health', 'admin.*'
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -64,7 +72,7 @@ return [
|
|||||||
|
|
||||||
// URL path to use for the docs endpoint (if `add_routes` is true).
|
// URL path to use for the docs endpoint (if `add_routes` is true).
|
||||||
// By default, `/docs` opens the HTML page, `/docs.postman` opens the Postman collection, and `/docs.openapi` the OpenAPI spec.
|
// By default, `/docs` opens the HTML page, `/docs.postman` opens the Postman collection, and `/docs.openapi` the OpenAPI spec.
|
||||||
'docs_url' => '/api/documentation',
|
'docs_url' => '/docs',
|
||||||
|
|
||||||
// Directory within `public` in which to store CSS and JS assets.
|
// Directory within `public` in which to store CSS and JS assets.
|
||||||
// By default, assets are stored in `public/vendor/scribe`.
|
// By default, assets are stored in `public/vendor/scribe`.
|
||||||
@@ -97,28 +105,28 @@ return [
|
|||||||
// How is your API authenticated? This information will be used in the displayed docs, generated examples and response calls.
|
// How is your API authenticated? This information will be used in the displayed docs, generated examples and response calls.
|
||||||
'auth' => [
|
'auth' => [
|
||||||
// Set this to true if ANY endpoints in your API use authentication.
|
// Set this to true if ANY endpoints in your API use authentication.
|
||||||
'enabled' => true,
|
'enabled' => false,
|
||||||
|
|
||||||
// Set this to true if your API should be authenticated by default. If so, you must also set `enabled` (above) to true.
|
// Set this to true if your API should be authenticated by default. If so, you must also set `enabled` (above) to true.
|
||||||
// You can then use @unauthenticated or @authenticated on individual endpoints to change their status from the default.
|
// You can then use @unauthenticated or @authenticated on individual endpoints to change their status from the default.
|
||||||
'default' => true,
|
'default' => false,
|
||||||
|
|
||||||
// Where is the auth value meant to be sent in a request?
|
// Where is the auth value meant to be sent in a request?
|
||||||
'in' => 'bearer',
|
'in' => AuthIn::BEARER->value,
|
||||||
|
|
||||||
// The name of the auth parameter (e.g. token, key, apiKey) or header (e.g. Authorization, Api-Key).
|
// The name of the auth parameter (e.g. token, key, apiKey) or header (e.g. Authorization, Api-Key).
|
||||||
'name' => 'Authorization',
|
'name' => 'key',
|
||||||
|
|
||||||
// The value of the parameter to be used by Scribe to authenticate response calls.
|
// The value of the parameter to be used by Scribe to authenticate response calls.
|
||||||
// This will NOT be included in the generated documentation. If empty, Scribe will use a random value.
|
// This will NOT be included in the generated documentation. If empty, Scribe will use a random value.
|
||||||
'use_value' => 'Bearer {token}',
|
'use_value' => env('SCRIBE_AUTH_KEY'),
|
||||||
|
|
||||||
// Placeholder your users will see for the auth parameter in the example requests.
|
// Placeholder your users will see for the auth parameter in the example requests.
|
||||||
// Set this to null if you want Scribe to use a random value as placeholder instead.
|
// Set this to null if you want Scribe to use a random value as placeholder instead.
|
||||||
'placeholder' => 'Bearer {token}',
|
'placeholder' => '{YOUR_AUTH_KEY}',
|
||||||
|
|
||||||
// Any extra authentication-related info for your users. Markdown and HTML are supported.
|
// Any extra authentication-related info for your users. Markdown and HTML are supported.
|
||||||
'extra_info' => 'Get tokens from `POST /api/auth/login`, send access token as `Bearer {token}`, and renew with `POST /api/auth/refresh` before access token expiry.',
|
'extra_info' => 'You can retrieve your token by visiting your dashboard and clicking <b>Generate API token</b>.',
|
||||||
],
|
],
|
||||||
|
|
||||||
// Example requests for each endpoint will be shown in each of these languages.
|
// Example requests for each endpoint will be shown in each of these languages.
|
||||||
@@ -205,18 +213,38 @@ return [
|
|||||||
// Use configureStrategy() to specify settings for a strategy in the list.
|
// Use configureStrategy() to specify settings for a strategy in the list.
|
||||||
// Use removeStrategies() to remove an included strategy.
|
// Use removeStrategies() to remove an included strategy.
|
||||||
'strategies' => [
|
'strategies' => [
|
||||||
'metadata' => [],
|
'metadata' => [
|
||||||
|
...Defaults::METADATA_STRATEGIES,
|
||||||
|
],
|
||||||
'headers' => [
|
'headers' => [
|
||||||
[
|
...Defaults::HEADERS_STRATEGIES,
|
||||||
|
Strategies\StaticData::withSettings(data: [
|
||||||
'Content-Type' => 'application/json',
|
'Content-Type' => 'application/json',
|
||||||
'Accept' => 'application/json',
|
'Accept' => 'application/json',
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
|
'urlParameters' => [
|
||||||
|
...Defaults::URL_PARAMETERS_STRATEGIES,
|
||||||
|
],
|
||||||
|
'queryParameters' => [
|
||||||
|
...Defaults::QUERY_PARAMETERS_STRATEGIES,
|
||||||
|
],
|
||||||
|
'bodyParameters' => [
|
||||||
|
...Defaults::BODY_PARAMETERS_STRATEGIES,
|
||||||
|
],
|
||||||
|
'responses' => configureStrategy(
|
||||||
|
Defaults::RESPONSES_STRATEGIES,
|
||||||
|
Strategies\Responses\ResponseCalls::withSettings(
|
||||||
|
only: ['GET *'],
|
||||||
|
// Recommended: disable debug mode in response calls to avoid error stack traces in responses
|
||||||
|
config: [
|
||||||
|
'app.debug' => false,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'responseFields' => [
|
||||||
|
...Defaults::RESPONSE_FIELDS_STRATEGIES,
|
||||||
],
|
],
|
||||||
'urlParameters' => [],
|
|
||||||
'queryParameters' => [],
|
|
||||||
'bodyParameters' => [],
|
|
||||||
'responses' => [],
|
|
||||||
'responseFields' => [],
|
|
||||||
],
|
],
|
||||||
|
|
||||||
// For response calls, API resource responses and transformer responses,
|
// For response calls, API resource responses and transformer responses,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -106,7 +106,7 @@
|
|||||||
{#each row.getVisibleCells() as cell}
|
{#each row.getVisibleCells() as cell}
|
||||||
<td>
|
<td>
|
||||||
{#if typeof cell.column.columnDef.cell === 'function'}
|
{#if typeof cell.column.columnDef.cell === 'function'}
|
||||||
{cell.column.columnDef.cell(cell.getContext())}
|
{@html cell.column.columnDef.cell(cell.getContext())}
|
||||||
{:else}
|
{:else}
|
||||||
{cell.getValue()}
|
{cell.getValue()}
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -248,7 +248,7 @@
|
|||||||
<!-- Create/Edit Modal -->
|
<!-- Create/Edit Modal -->
|
||||||
{#if showModal}
|
{#if showModal}
|
||||||
<div class="modal modal-open">
|
<div class="modal modal-open">
|
||||||
<div class="modal-box max-w-md">
|
<div class="modal-box max-w-lg">
|
||||||
<div class="flex justify-between items-center mb-4">
|
<div class="flex justify-between items-center mb-4">
|
||||||
<h3 class="font-bold text-lg">{editingMember ? 'Edit Team Member' : 'Add Team Member'}</h3>
|
<h3 class="font-bold text-lg">{editingMember ? 'Edit Team Member' : 'Add Team Member'}</h3>
|
||||||
<button class="btn btn-ghost btn-sm btn-circle" onclick={closeModal}>
|
<button class="btn btn-ghost btn-sm btn-circle" onclick={closeModal}>
|
||||||
@@ -264,27 +264,29 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<form onsubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
|
<form onsubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
|
||||||
<div class="form-control mb-4">
|
<!-- Name Field - Horizontal Layout -->
|
||||||
<label class="label" for="name">
|
<div class="form-control mb-4 flex flex-row items-center gap-4">
|
||||||
<span class="label-text">Name</span>
|
<label class="label w-28 shrink-0" for="name">
|
||||||
|
<span class="label-text font-medium">Name</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="name"
|
id="name"
|
||||||
class="input input-bordered"
|
class="input input-bordered flex-1"
|
||||||
bind:value={formData.name}
|
bind:value={formData.name}
|
||||||
placeholder="Enter name"
|
placeholder="Enter name"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-control mb-4">
|
<!-- Role Field - Horizontal Layout -->
|
||||||
<label class="label" for="role">
|
<div class="form-control mb-4 flex flex-row items-center gap-4">
|
||||||
<span class="label-text">Role</span>
|
<label class="label w-28 shrink-0" for="role">
|
||||||
|
<span class="label-text font-medium">Role</span>
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
id="role"
|
id="role"
|
||||||
class="select select-bordered"
|
class="select select-bordered flex-1"
|
||||||
bind:value={formData.role_id}
|
bind:value={formData.role_id}
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
@@ -294,14 +296,17 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-control mb-4">
|
<!-- Hourly Rate Field - Horizontal Layout -->
|
||||||
<label class="label" for="hourly_rate">
|
<div class="form-control mb-4 flex flex-row items-center gap-4">
|
||||||
<span class="label-text">Hourly Rate ($)</span>
|
<label class="label w-28 shrink-0" for="hourly_rate">
|
||||||
|
<span class="label-text font-medium">Hourly Rate</span>
|
||||||
</label>
|
</label>
|
||||||
|
<div class="flex-1 relative">
|
||||||
|
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-base-content/50">$</span>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
id="hourly_rate"
|
id="hourly_rate"
|
||||||
class="input input-bordered"
|
class="input input-bordered w-full pl-7"
|
||||||
bind:value={formData.hourly_rate}
|
bind:value={formData.hourly_rate}
|
||||||
placeholder="0.00"
|
placeholder="0.00"
|
||||||
min="0.01"
|
min="0.01"
|
||||||
@@ -309,15 +314,20 @@
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-control mb-6">
|
<!-- Active Status -->
|
||||||
<label class="label cursor-pointer justify-start gap-3">
|
<div class="form-control mb-6 flex flex-row items-center gap-4">
|
||||||
|
<label class="label w-28 shrink-0">
|
||||||
|
<span class="label-text font-medium">Status</span>
|
||||||
|
</label>
|
||||||
|
<label class="label cursor-pointer justify-start gap-3 flex-1">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="checkbox"
|
class="checkbox checkbox-sm"
|
||||||
bind:checked={formData.active}
|
bind:checked={formData.active}
|
||||||
/>
|
/>
|
||||||
<span class="label-text">Active</span>
|
<span class="label-text">{formData.active ? 'Active' : 'Inactive'}</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user