diff --git a/app/Http/Resources/V1/User/UserResource.php b/app/Http/Resources/V1/User/UserResource.php index ad2525c1..675380d3 100644 --- a/app/Http/Resources/V1/User/UserResource.php +++ b/app/Http/Resources/V1/User/UserResource.php @@ -28,6 +28,8 @@ class UserResource extends BaseResource 'name' => $this->resource->name, /** @var string $email Email of user */ 'email' => $this->resource->email, + /** @var string|null $pending_email Email address awaiting verification (set when the user has requested an email change but not yet verified the new address) */ + 'pending_email' => $this->resource->pending_email, /** @var string $profile_photo_url Profile photo URL */ 'profile_photo_url' => $this->resource->profile_photo_url, /** @var string $timezone Timezone (f.e. Europe/Berlin or America/New_York) */ diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 3efe037d..8ff220bd 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -28,6 +28,7 @@ class UserFactory extends Factory return [ 'name' => $this->faker->name(), 'email' => $this->faker->unique()->safeEmail(), + 'pending_email' => null, 'email_verified_at' => now(), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 'two_factor_secret' => null, diff --git a/resources/js/packages/api/src/index.ts b/resources/js/packages/api/src/index.ts index 888c4046..9e040e62 100644 --- a/resources/js/packages/api/src/index.ts +++ b/resources/js/packages/api/src/index.ts @@ -122,6 +122,9 @@ export type CreateInvoiceBody = ZodiosBodyByAlias export type UpdateInvoiceBody = ZodiosBodyByAlias; +export type User = ZodiosResponseByAlias['data']; +export type UpdateUserBody = ZodiosBodyByAlias; + const api = createApiClient('/api', { validate: 'none' }); export { createApiClient, api }; diff --git a/resources/js/packages/api/src/openapi.json.client.ts b/resources/js/packages/api/src/openapi.json.client.ts index 74a7ed21..f4ddd35a 100644 --- a/resources/js/packages/api/src/openapi.json.client.ts +++ b/resources/js/packages/api/src/openapi.json.client.ts @@ -688,11 +688,22 @@ const UserResource = z id: z.string(), name: z.string(), email: z.string(), + pending_email: z.union([z.string(), z.null()]), profile_photo_url: z.string(), timezone: z.string(), week_start: Weekday, }) .passthrough(); +const UserUpdateRequest = z + .object({ + name: z.string(), + email: z.string(), + photo: z.union([z.string(), z.null()]), + timezone: z.string(), + week_start: Weekday, + }) + .partial() + .passthrough(); const PersonalMembershipResource = z .object({ id: z.string(), @@ -764,6 +775,7 @@ export const schemas = { TimeEntryUpdateMultipleRequest, TimeEntryUpdateRequest, UserResource, + UserUpdateRequest, PersonalMembershipResource, }; @@ -4419,7 +4431,7 @@ The report is considered public if the `is_public` field is set to &#x method: 'get', path: '/v1/users/me', alias: 'getMe', - description: `This endpoint is independent of organization.`, + description: `This endpoint is independent of the organization.`, requestFormat: 'json', response: z.object({ data: UserResource }).passthrough(), errors: [ @@ -4435,6 +4447,79 @@ The report is considered public if the `is_public` field is set to &#x }, ], }, + { + method: 'put', + path: '/v1/users/:user', + alias: 'updateUser', + description: `This endpoint is independent of the organization.`, + requestFormat: 'json', + parameters: [ + { + name: 'body', + type: 'Body', + schema: UserUpdateRequest, + }, + { + name: 'user', + type: 'Path', + schema: z.string(), + }, + ], + response: z.object({ data: UserResource }).passthrough(), + errors: [ + { + status: 401, + description: `Unauthenticated`, + schema: z.object({ message: z.string() }).passthrough(), + }, + { + status: 403, + description: `Authorization error`, + schema: z.object({ message: z.string() }).passthrough(), + }, + { + status: 422, + description: `Validation error`, + schema: z + .object({ message: z.string(), errors: z.record(z.array(z.string())) }) + .passthrough(), + }, + ], + }, + { + method: 'post', + path: '/v1/users/:user/resend-email-verification', + alias: 'resendUserEmailVerification', + description: `This endpoint is independent of the organization.`, + requestFormat: 'json', + parameters: [ + { + name: 'user', + type: 'Path', + schema: z.string(), + }, + ], + response: z.void(), + errors: [ + { + status: 400, + description: `API exception`, + schema: z + .object({ error: z.boolean(), key: z.string(), message: z.string() }) + .passthrough(), + }, + { + status: 401, + description: `Unauthenticated`, + schema: z.object({ message: z.string() }).passthrough(), + }, + { + status: 403, + description: `Authorization error`, + schema: z.object({ message: z.string() }).passthrough(), + }, + ], + }, { method: 'get', path: '/v1/users/me/api-tokens', diff --git a/resources/js/types/models.d.ts b/resources/js/types/models.d.ts index 3b6fbcd5..f86bd0b4 100644 --- a/resources/js/types/models.d.ts +++ b/resources/js/types/models.d.ts @@ -63,6 +63,7 @@ export interface User { id: string; name: string; email: string; + pending_email: string | null; email_verified_at: string | null; password?: string; remember_token?: string | null; diff --git a/resources/js/types/models.ts b/resources/js/types/models.ts index 6744f8ea..cdef9c71 100644 --- a/resources/js/types/models.ts +++ b/resources/js/types/models.ts @@ -80,6 +80,7 @@ export interface User { id: string; name: string; email: string; + pending_email: string | null; email_verified_at: string | null; password?: string; remember_token?: string | null;