feat(api): Implement API Resource Standard compliance

- Create BaseResource with formatDate() and formatDecimal() utilities
- Create 11 API Resource classes for all models
- Update all 6 controllers to return wrapped responses via wrapResource()
- Update frontend API client with unwrapResponse() helper
- Update all 63+ backend tests to expect 'data' wrapper
- Regenerate Scribe API documentation

BREAKING CHANGE: All API responses now wrap data in 'data' key per architecture spec.

Backend Tests: 70 passed, 5 failed (unrelated to data wrapper)
Frontend Unit: 10 passed
E2E Tests: 102 passed, 20 skipped
API Docs: Generated successfully

Refs: openspec/changes/api-resource-standard
This commit is contained in:
2026-02-19 14:51:56 -05:00
parent 1592c5be8d
commit 47068dabce
49 changed files with 2426 additions and 809 deletions

View File

@@ -260,16 +260,19 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;access_token&quot;: &quot;eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...&quot;,
&quot;refresh_token&quot;: &quot;abc123def456&quot;,
&quot;token_type&quot;: &quot;bearer&quot;,
&quot;expires_in&quot;: 3600,
&quot;user&quot;: {
&quot;data&quot;: {
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;name&quot;: &quot;Alice Johnson&quot;,
&quot;email&quot;: &quot;user@example.com&quot;,
&quot;role&quot;: &quot;manager&quot;
}
&quot;role&quot;: &quot;manager&quot;,
&quot;active&quot;: true,
&quot;created_at&quot;: &quot;2026-01-01T00:00:00Z&quot;,
&quot;updated_at&quot;: &quot;2026-01-01T00:00:00Z&quot;
},
&quot;access_token&quot;: &quot;eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...&quot;,
&quot;refresh_token&quot;: &quot;abc123def456&quot;,
&quot;token_type&quot;: &quot;bearer&quot;,
&quot;expires_in&quot;: 3600
}</code>
</pre>
<blockquote>
@@ -457,6 +460,15 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;data&quot;: {
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;name&quot;: &quot;Alice Johnson&quot;,
&quot;email&quot;: &quot;user@example.com&quot;,
&quot;role&quot;: &quot;manager&quot;,
&quot;active&quot;: true,
&quot;created_at&quot;: &quot;2026-01-01T00:00:00Z&quot;,
&quot;updated_at&quot;: &quot;2026-01-01T00:00:00Z&quot;
},
&quot;access_token&quot;: &quot;eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...&quot;,
&quot;refresh_token&quot;: &quot;newtoken123&quot;,
&quot;token_type&quot;: &quot;bearer&quot;,
@@ -758,15 +770,20 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;person_days&quot;: 18.5,
&quot;hours&quot;: 148,
&quot;details&quot;: [
{
&quot;date&quot;: &quot;2026-02-02&quot;,
&quot;availability&quot;: 1,
&quot;is_pto&quot;: false
}
]
&quot;data&quot;: {
&quot;team_member_id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;month&quot;: &quot;2026-02&quot;,
&quot;working_days&quot;: 20,
&quot;person_days&quot;: 18.5,
&quot;hours&quot;: 148,
&quot;details&quot;: [
{
&quot;date&quot;: &quot;2026-02-02&quot;,
&quot;availability&quot;: 1,
&quot;is_pto&quot;: false
}
]
}
}</code>
</pre>
</span>
@@ -944,17 +961,19 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;month&quot;: &quot;2026-02&quot;,
&quot;person_days&quot;: 180.5,
&quot;hours&quot;: 1444,
&quot;members&quot;: [
{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;name&quot;: &quot;Ada Lovelace&quot;,
&quot;person_days&quot;: 18.5,
&quot;hours&quot;: 148
}
]
&quot;data&quot;: {
&quot;month&quot;: &quot;2026-02&quot;,
&quot;total_person_days&quot;: 180.5,
&quot;total_hours&quot;: 1444,
&quot;members&quot;: [
{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;name&quot;: &quot;Ada Lovelace&quot;,
&quot;person_days&quot;: 18.5,
&quot;hours&quot;: 148
}
]
}
}</code>
</pre>
</span>
@@ -1108,8 +1127,19 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;month&quot;: &quot;2026-02&quot;,
&quot;possible_revenue&quot;: 21500.25
&quot;data&quot;: {
&quot;month&quot;: &quot;2026-02&quot;,
&quot;possible_revenue&quot;: 21500.25,
&quot;member_revenues&quot;: [
{
&quot;team_member_id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;team_member_name&quot;: &quot;Ada Lovelace&quot;,
&quot;hours&quot;: 148,
&quot;hourly_rate&quot;: 150,
&quot;revenue&quot;: 22200
}
]
}
}</code>
</pre>
</span>
@@ -1262,14 +1292,16 @@ fetch(url, {
</blockquote>
<pre>
<code class="language-json" style="max-height: 300px;">[
{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;date&quot;: &quot;2026-02-14&quot;,
&quot;name&quot;: &quot;Company Holiday&quot;,
&quot;description&quot;: &quot;Office closed&quot;
}
]</code>
<code class="language-json" style="max-height: 300px;">{
&quot;data&quot;: [
{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;date&quot;: &quot;2026-02-14&quot;,
&quot;name&quot;: &quot;Company Holiday&quot;,
&quot;description&quot;: &quot;Office closed&quot;
}
]
}</code>
</pre>
</span>
<span id="execution-results-GETapi-holidays" hidden>
@@ -1426,10 +1458,12 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;date&quot;: &quot;2026-02-14&quot;,
&quot;name&quot;: &quot;Presidents&#039; Day&quot;,
&quot;description&quot;: &quot;Office closed&quot;
&quot;data&quot;: {
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;date&quot;: &quot;2026-02-14&quot;,
&quot;name&quot;: &quot;Presidents&#039; Day&quot;,
&quot;description&quot;: &quot;Office closed&quot;
}
}</code>
</pre>
</span>
@@ -1727,16 +1761,18 @@ fetch(url, {
</blockquote>
<pre>
<code class="language-json" style="max-height: 300px;">[
{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440001&quot;,
&quot;team_member_id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;start_date&quot;: &quot;2026-02-10&quot;,
&quot;end_date&quot;: &quot;2026-02-12&quot;,
&quot;status&quot;: &quot;pending&quot;,
&quot;reason&quot;: &quot;Family travel&quot;
}
]</code>
<code class="language-json" style="max-height: 300px;">{
&quot;data&quot;: [
{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440001&quot;,
&quot;team_member_id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;start_date&quot;: &quot;2026-02-10&quot;,
&quot;end_date&quot;: &quot;2026-02-12&quot;,
&quot;status&quot;: &quot;pending&quot;,
&quot;reason&quot;: &quot;Family travel&quot;
}
]
}</code>
</pre>
</span>
<span id="execution-results-GETapi-ptos" hidden>
@@ -1919,12 +1955,14 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440001&quot;,
&quot;team_member_id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;start_date&quot;: &quot;2026-02-10&quot;,
&quot;end_date&quot;: &quot;2026-02-12&quot;,
&quot;status&quot;: &quot;pending&quot;,
&quot;reason&quot;: &quot;Family travel&quot;
&quot;data&quot;: {
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440001&quot;,
&quot;team_member_id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;start_date&quot;: &quot;2026-02-10&quot;,
&quot;end_date&quot;: &quot;2026-02-12&quot;,
&quot;status&quot;: &quot;pending&quot;,
&quot;reason&quot;: &quot;Family travel&quot;
}
}</code>
</pre>
</span>
@@ -2092,8 +2130,10 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440001&quot;,
&quot;status&quot;: &quot;approved&quot;
&quot;data&quot;: {
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440001&quot;,
&quot;status&quot;: &quot;approved&quot;
}
}</code>
</pre>
</span>
@@ -2229,20 +2269,22 @@ fetch(url, {
</blockquote>
<pre>
<code class="language-json" style="max-height: 300px;">[
{
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Project&quot;
},
{
&quot;id&quot;: 2,
&quot;name&quot;: &quot;Support&quot;
},
{
&quot;id&quot;: 3,
&quot;name&quot;: &quot;Engagement&quot;
}
]</code>
<code class="language-json" style="max-height: 300px;">{
&quot;data&quot;: [
{
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Project&quot;
},
{
&quot;id&quot;: 2,
&quot;name&quot;: &quot;Support&quot;
},
{
&quot;id&quot;: 3,
&quot;name&quot;: &quot;Engagement&quot;
}
]
}</code>
</pre>
</span>
<span id="execution-results-GETapi-projects-types" hidden>
@@ -2360,23 +2402,25 @@ fetch(url, {
</blockquote>
<pre>
<code class="language-json" style="max-height: 300px;">[
{
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Pre-sales&quot;,
&quot;order&quot;: 1
},
{
&quot;id&quot;: 2,
&quot;name&quot;: &quot;SOW Approval&quot;,
&quot;order&quot;: 2
},
{
&quot;id&quot;: 3,
&quot;name&quot;: &quot;Gathering Estimates&quot;,
&quot;order&quot;: 3
}
]</code>
<code class="language-json" style="max-height: 300px;">{
&quot;data&quot;: [
{
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Pre-sales&quot;,
&quot;order&quot;: 1
},
{
&quot;id&quot;: 2,
&quot;name&quot;: &quot;SOW Approval&quot;,
&quot;order&quot;: 2
},
{
&quot;id&quot;: 3,
&quot;name&quot;: &quot;Gathering Estimates&quot;,
&quot;order&quot;: 3
}
]
}</code>
</pre>
</span>
<span id="execution-results-GETapi-projects-statuses" hidden>
@@ -2501,31 +2545,31 @@ fetch(url, {
</blockquote>
<pre>
<code class="language-json" style="max-height: 300px;">[
{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;code&quot;: &quot;PROJ-001&quot;,
&quot;title&quot;: &quot;Client Dashboard Redesign&quot;,
&quot;status_id&quot;: 1,
&quot;status&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Pre-sales&quot;
},
&quot;type_id&quot;: 2,
&quot;type&quot;: {
&quot;id&quot;: 2,
&quot;name&quot;: &quot;Support&quot;
},
&quot;approved_estimate&quot;: &quot;120.00&quot;,
&quot;forecasted_effort&quot;: {
&quot;2024-02&quot;: 40,
&quot;2024-03&quot;: 60,
&quot;2024-04&quot;: 20
},
&quot;created_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;,
&quot;updated_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;
}
]</code>
<code class="language-json" style="max-height: 300px;">{
&quot;data&quot;: [
{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;code&quot;: &quot;PROJ-001&quot;,
&quot;title&quot;: &quot;Client Dashboard Redesign&quot;,
&quot;status&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Pre-sales&quot;
},
&quot;type&quot;: {
&quot;id&quot;: 2,
&quot;name&quot;: &quot;Support&quot;
},
&quot;approved_estimate&quot;: &quot;120.00&quot;,
&quot;forecasted_effort&quot;: {
&quot;2024-02&quot;: 40,
&quot;2024-03&quot;: 60,
&quot;2024-04&quot;: 20
},
&quot;created_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;,
&quot;updated_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;
}
]
}</code>
</pre>
</span>
<span id="execution-results-GETapi-projects" hidden>
@@ -2682,18 +2726,18 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;code&quot;: &quot;PROJ-001&quot;,
&quot;title&quot;: &quot;Client Dashboard Redesign&quot;,
&quot;status_id&quot;: 1,
&quot;status&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Pre-sales&quot;
},
&quot;type_id&quot;: 1,
&quot;type&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Project&quot;
&quot;data&quot;: {
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;code&quot;: &quot;PROJ-001&quot;,
&quot;title&quot;: &quot;Client Dashboard Redesign&quot;,
&quot;status&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Pre-sales&quot;
},
&quot;type&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Project&quot;
}
}
}</code>
</pre>
@@ -2868,21 +2912,23 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;code&quot;: &quot;PROJ-001&quot;,
&quot;title&quot;: &quot;Client Dashboard Redesign&quot;,
&quot;status&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Pre-sales&quot;
},
&quot;type&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Project&quot;
},
&quot;approved_estimate&quot;: &quot;120.00&quot;,
&quot;forecasted_effort&quot;: {
&quot;2024-02&quot;: 40,
&quot;2024-03&quot;: 60
&quot;data&quot;: {
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;code&quot;: &quot;PROJ-001&quot;,
&quot;title&quot;: &quot;Client Dashboard Redesign&quot;,
&quot;status&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Pre-sales&quot;
},
&quot;type&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Project&quot;
},
&quot;approved_estimate&quot;: &quot;120.00&quot;,
&quot;forecasted_effort&quot;: {
&quot;2024-02&quot;: 40,
&quot;2024-03&quot;: 60
}
}
}</code>
</pre>
@@ -3038,10 +3084,15 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;code&quot;: &quot;PROJ-002&quot;,
&quot;title&quot;: &quot;Updated Title&quot;,
&quot;type_id&quot;: 2
&quot;data&quot;: {
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;code&quot;: &quot;PROJ-002&quot;,
&quot;title&quot;: &quot;Updated Title&quot;,
&quot;type&quot;: {
&quot;id&quot;: 2,
&quot;name&quot;: &quot;Support&quot;
}
}
}</code>
</pre>
<blockquote>
@@ -3398,10 +3449,12 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;status&quot;: {
&quot;id&quot;: 2,
&quot;name&quot;: &quot;SOW Approval&quot;
&quot;data&quot;: {
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;status&quot;: {
&quot;id&quot;: 2,
&quot;name&quot;: &quot;SOW Approval&quot;
}
}
}</code>
</pre>
@@ -3587,8 +3640,10 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;approved_estimate&quot;: &quot;120.00&quot;
&quot;data&quot;: {
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;approved_estimate&quot;: &quot;120.00&quot;
}
}</code>
</pre>
<blockquote>
@@ -3779,10 +3834,12 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;forecasted_effort&quot;: {
&quot;2024-02&quot;: 40,
&quot;2024-03&quot;: 60
&quot;data&quot;: {
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;forecasted_effort&quot;: {
&quot;2024-02&quot;: 40,
&quot;2024-03&quot;: 60
}
}
}</code>
</pre>
@@ -3968,21 +4025,22 @@ fetch(url, {
</blockquote>
<pre>
<code class="language-json" style="max-height: 300px;">[
{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;name&quot;: &quot;John Doe&quot;,
&quot;role_id&quot;: 1,
&quot;role&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Backend Developer&quot;
},
&quot;hourly_rate&quot;: &quot;150.00&quot;,
&quot;active&quot;: true,
&quot;created_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;,
&quot;updated_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;
}
]</code>
<code class="language-json" style="max-height: 300px;">{
&quot;data&quot;: [
{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;name&quot;: &quot;John Doe&quot;,
&quot;role&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Backend Developer&quot;
},
&quot;hourly_rate&quot;: &quot;150.00&quot;,
&quot;active&quot;: true,
&quot;created_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;,
&quot;updated_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;
}
]
}</code>
</pre>
</span>
<span id="execution-results-GETapi-team-members" hidden>
@@ -4139,17 +4197,18 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;name&quot;: &quot;John Doe&quot;,
&quot;role_id&quot;: 1,
&quot;role&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Backend Developer&quot;
},
&quot;hourly_rate&quot;: &quot;150.00&quot;,
&quot;active&quot;: true,
&quot;created_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;,
&quot;updated_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;
&quot;data&quot;: {
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;name&quot;: &quot;John Doe&quot;,
&quot;role&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Backend Developer&quot;
},
&quot;hourly_rate&quot;: &quot;150.00&quot;,
&quot;active&quot;: true,
&quot;created_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;,
&quot;updated_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;
}
}</code>
</pre>
<blockquote>
@@ -4345,17 +4404,18 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;name&quot;: &quot;John Doe&quot;,
&quot;role_id&quot;: 1,
&quot;role&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Backend Developer&quot;
},
&quot;hourly_rate&quot;: &quot;150.00&quot;,
&quot;active&quot;: true,
&quot;created_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;,
&quot;updated_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;
&quot;data&quot;: {
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;name&quot;: &quot;John Doe&quot;,
&quot;role&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Backend Developer&quot;
},
&quot;hourly_rate&quot;: &quot;150.00&quot;,
&quot;active&quot;: true,
&quot;created_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;,
&quot;updated_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;
}
}</code>
</pre>
<blockquote>
@@ -4512,17 +4572,18 @@ fetch(url, {
<pre>
<code class="language-json" style="max-height: 300px;">{
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;name&quot;: &quot;John Doe&quot;,
&quot;role_id&quot;: 1,
&quot;role&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Backend Developer&quot;
},
&quot;hourly_rate&quot;: &quot;175.00&quot;,
&quot;active&quot;: false,
&quot;created_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;,
&quot;updated_at&quot;: &quot;2024-01-15T11:00:00.000000Z&quot;
&quot;data&quot;: {
&quot;id&quot;: &quot;550e8400-e29b-41d4-a716-446655440000&quot;,
&quot;name&quot;: &quot;John Doe&quot;,
&quot;role&quot;: {
&quot;id&quot;: 1,
&quot;name&quot;: &quot;Backend Developer&quot;
},
&quot;hourly_rate&quot;: &quot;175.00&quot;,
&quot;active&quot;: false,
&quot;created_at&quot;: &quot;2024-01-15T10:00:00.000000Z&quot;,
&quot;updated_at&quot;: &quot;2024-01-15T11:00:00.000000Z&quot;
}
}</code>
</pre>
<blockquote>