diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b75b2b4..23983da 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -6,27 +6,33 @@ labels: bug assignees: '' --- -**Describe the bug** +# Describe the bug A clear and concise description of what the bug is. -**To Reproduce** +# To Reproduce Steps to reproduce the behavior: 1. Go to '...' 2. Click on '...' 3. Scroll down to '...' 4. See error -**Expected behavior** +# Expected behavior A clear and concise description of what you expected to happen. -**Screenshots or logs** +# Screenshots or logs If applicable, add screenshots or terminal logs to help explain your problem. -**Environment (please complete the following information):** +# Environment (please complete the following information): - OS: [e.g. Android 14 / iOS 17 / Ubuntu 22.04] - App version: [e.g. Android v2.0 / iOS 2.0] - Node version: [e.g. 20] - Device/emulator: [e.g. Pixel 6 / iPhone 12] -**Additional context** +# Additional context Add any other context about the problem here. + +# Guidelines +- If you want to work on this bug, please comment on this issue first. +- Once someone has been assigned or has claimed the issue, no one else should start working on it. +- If the issue is not completed, you must provide a progress update within 2 days. +- If no update is provided within 2 days, the issue will be considered available for others to take. diff --git a/BizBuch API.yaml b/BizBuch API.yaml deleted file mode 100644 index 7cb180b..0000000 --- a/BizBuch API.yaml +++ /dev/null @@ -1,2233 +0,0 @@ -openapi: 3.0.3 -info: - title: BizBuch API - version: 1.0.0 - description: API Documentation for BizBuch -paths: - /activity/{id}/read/: - post: - operationId: activity_read_create - parameters: - - in: path - name: id - schema: - type: integer - required: true - tags: - - activity - security: - - jwtAuth: [] - responses: - '200': - description: No response body - /activity/log/: - get: - operationId: activity_log_list - description: Activity log of the authenticated user (actor-based). - tags: - - activity - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ActivityLog' - description: '' - /activity/notifications/: - get: - operationId: activity_notifications_list - tags: - - activity - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Activity' - description: '' - /activity/read-all/: - post: - operationId: activity_read_all_create - tags: - - activity - security: - - jwtAuth: [] - responses: - '200': - description: No response body - /auth/forgot-password/: - post: - operationId: auth_forgot_password_create - tags: - - auth - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ForgotPasswordRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/ForgotPasswordRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/ForgotPasswordRequest' - required: true - security: - - jwtAuth: [] - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/ForgotPassword' - description: '' - /auth/login/: - post: - operationId: auth_login_create - tags: - - auth - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/LoginRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/LoginRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/LoginRequest' - required: true - security: - - jwtAuth: [] - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/Login' - description: '' - /auth/register/: - post: - operationId: auth_register_create - tags: - - auth - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/RegisterRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/RegisterRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/RegisterRequest' - required: true - security: - - jwtAuth: [] - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/Register' - description: '' - /auth/resend-otp/: - post: - operationId: auth_resend_otp_create - tags: - - auth - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ResendOTPRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/ResendOTPRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/ResendOTPRequest' - required: true - security: - - jwtAuth: [] - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/ResendOTP' - description: '' - /auth/reset-password/: - post: - operationId: auth_reset_password_create - tags: - - auth - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ResetPasswordRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/ResetPasswordRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/ResetPasswordRequest' - required: true - security: - - jwtAuth: [] - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/ResetPassword' - description: '' - /auth/validate-token/: - post: - operationId: auth_validate_token_create - tags: - - auth - security: - - jwtAuth: [] - - {} - responses: - '200': - description: No response body - /auth/verify-otp/: - post: - operationId: auth_verify_otp_create - tags: - - auth - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/VerifyOTPRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/VerifyOTPRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/VerifyOTPRequest' - required: true - security: - - jwtAuth: [] - - {} - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/VerifyOTP' - description: '' - /chat/conversations/: - get: - operationId: chat_conversations_list - description: Get all conversations for the authenticated user with preview of - last message - summary: List user's conversations - tags: - - Chat - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ConversationList' - description: '' - /chat/conversations/{conversation_id}/: - get: - operationId: chat_conversations_retrieve - description: Get detailed view of a conversation with all messages - summary: Get conversation details - parameters: - - in: path - name: conversation_id - schema: - type: integer - required: true - tags: - - Chat - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/Conversation' - description: '' - /chat/conversations/{conversation_id}/messages/: - get: - operationId: chat_conversations_messages_list - description: Get paginated list of messages in a conversation. Each message - includes a 'type' field indicating 'sent' or 'received'. - summary: List messages in conversation - parameters: - - in: path - name: conversation_id - schema: - type: integer - required: true - - in: query - name: page - schema: - type: integer - description: Page number - - in: query - name: page_size - schema: - type: integer - description: 'Number of messages per page (default: 50)' - tags: - - Chat - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/Message' - description: '' - /chat/conversations/{conversation_id}/read/: - post: - operationId: chat_conversations_read_create - description: Mark specific messages or all messages in a conversation as read - summary: Mark messages as read - parameters: - - in: path - name: conversation_id - schema: - type: integer - required: true - tags: - - Chat - requestBody: - content: - application/json: - schema: - type: object - properties: - message_ids: - type: array - items: - type: integer - description: List of message IDs to mark as read (optional) - mark_all: - type: boolean - description: Mark all messages in conversation as read - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - description: Messages marked as read - description: '' - '400': - content: - application/json: - schema: - description: Bad request - description: '' - '404': - content: - application/json: - schema: - description: Conversation not found - description: '' - /chat/conversations/start/: - post: - operationId: chat_conversations_start_create - description: Start a new conversation with a user or get the existing one - summary: Start or get conversation - tags: - - Chat - requestBody: - content: - application/json: - schema: - type: object - properties: - user_id: - type: integer - description: ID of the user to start conversation with - required: - - user_id - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/Conversation' - description: '' - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/Conversation' - description: '' - '400': - content: - application/json: - schema: - description: Bad request - description: '' - '404': - content: - application/json: - schema: - description: User not found - description: '' - /chat/send/: - post: - operationId: chat_send_create - description: Send a message to another user. Creates conversation if it doesn't - exist. - summary: Send a message - tags: - - Chat - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/MessageCreateRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/MessageCreateRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/MessageCreateRequest' - required: true - security: - - jwtAuth: [] - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/Message' - description: '' - '400': - content: - application/json: - schema: - description: Bad request - description: '' - '404': - content: - application/json: - schema: - description: User not found - description: '' - /onboarding/status/: - get: - operationId: onboarding_status_retrieve - tags: - - onboarding - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/OnboardingStatus' - description: '' - /onboarding/topics/: - get: - operationId: onboarding_topics_retrieve - tags: - - onboarding - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/Topic' - description: '' - /onboarding/topics/submit/: - post: - operationId: onboarding_topics_submit_create - tags: - - onboarding - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/TopicSelectionRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/TopicSelectionRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/TopicSelectionRequest' - required: true - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/TopicSelection' - description: '' - /posts/: - get: - operationId: posts_list - tags: - - posts - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/PostModel' - description: '' - post: - operationId: posts_create - tags: - - posts - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PostModelRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PostModelRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PostModelRequest' - security: - - jwtAuth: [] - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/PostModel' - description: '' - /posts/{id}/: - get: - operationId: posts_retrieve - parameters: - - in: path - name: id - schema: - type: integer - required: true - tags: - - posts - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/PostModel' - description: '' - put: - operationId: posts_update - parameters: - - in: path - name: id - schema: - type: integer - required: true - tags: - - posts - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PostModelRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PostModelRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PostModelRequest' - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/PostModel' - description: '' - patch: - operationId: posts_partial_update - parameters: - - in: path - name: id - schema: - type: integer - required: true - tags: - - posts - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PatchedPostModelRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PatchedPostModelRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PatchedPostModelRequest' - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/PostModel' - description: '' - delete: - operationId: posts_destroy - parameters: - - in: path - name: id - schema: - type: integer - required: true - tags: - - posts - security: - - jwtAuth: [] - responses: - '204': - description: No response body - /posts/{post_id}/comments/: - get: - operationId: posts_comments_list - parameters: - - in: path - name: post_id - schema: - type: integer - required: true - tags: - - Comments - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/PostComment' - description: '' - post: - operationId: posts_comments_create - parameters: - - in: path - name: post_id - schema: - type: integer - required: true - tags: - - Comments - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PostCommentRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PostCommentRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PostCommentRequest' - required: true - security: - - jwtAuth: [] - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/PostComment' - description: '' - /posts/{post_id}/likes/: - get: - operationId: posts_likes_list - parameters: - - in: path - name: post_id - schema: - type: integer - required: true - tags: - - Likes - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/PostLike' - description: '' - post: - operationId: posts_likes_create - parameters: - - in: path - name: post_id - schema: - type: integer - required: true - tags: - - Likes - security: - - jwtAuth: [] - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/PostLike' - description: '' - /posts/{post_id}/report/: - post: - operationId: posts_report_create - parameters: - - in: path - name: post_id - schema: - type: integer - required: true - tags: - - Reports - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PostReportRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PostReportRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PostReportRequest' - required: true - security: - - jwtAuth: [] - responses: - '201': - content: - application/json: - schema: - $ref: '#/components/schemas/PostReport' - description: '' - /posts/comments/{id}/: - get: - operationId: posts_comments_retrieve - parameters: - - in: path - name: id - schema: - type: integer - required: true - tags: - - Comments - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/PostComment' - description: '' - put: - operationId: posts_comments_update - parameters: - - in: path - name: id - schema: - type: integer - required: true - tags: - - Comments - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PostCommentRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PostCommentRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PostCommentRequest' - required: true - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/PostComment' - description: '' - patch: - operationId: posts_comments_partial_update - parameters: - - in: path - name: id - schema: - type: integer - required: true - tags: - - Comments - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PatchedPostCommentRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PatchedPostCommentRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PatchedPostCommentRequest' - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/PostComment' - description: '' - delete: - operationId: posts_comments_destroy - parameters: - - in: path - name: id - schema: - type: integer - required: true - tags: - - Comments - security: - - jwtAuth: [] - responses: - '204': - description: No response body - /posts/likes/{id}/: - delete: - operationId: posts_likes_destroy - parameters: - - in: path - name: id - schema: - type: integer - required: true - tags: - - Likes - security: - - jwtAuth: [] - responses: - '204': - description: No response body - /profiles/: - get: - operationId: profiles_list - parameters: - - name: search - required: false - in: query - description: A search term. - schema: - type: string - tags: - - profiles - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ProfileCompact' - description: '' - /profiles/{id}/follow/: - post: - operationId: profiles_follow_create - description: Follow a user profile - parameters: - - in: path - name: id - schema: - type: integer - required: true - tags: - - Follows - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/FollowResponse' - description: '' - /profiles/{id}/followers/: - get: - operationId: profiles_followers_list - parameters: - - in: path - name: id - schema: - type: integer - required: true - tags: - - Follows - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ProfileCompact' - description: '' - /profiles/{id}/following/: - get: - operationId: profiles_following_list - parameters: - - in: path - name: id - schema: - type: integer - required: true - tags: - - Follows - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ProfileCompact' - description: '' - /profiles/{id}/unfollow/: - post: - operationId: profiles_unfollow_create - description: Unfollow a user profile - parameters: - - in: path - name: id - schema: - type: integer - required: true - tags: - - Follows - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/FollowResponse' - description: '' - /profiles/{user_id}/: - get: - operationId: profiles_retrieve - parameters: - - in: path - name: user_id - schema: - type: integer - required: true - tags: - - profiles - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/Profile' - description: '' - /profiles/connections/: - get: - operationId: profiles_connections_list - description: |- - Get the authenticated user's connections (followers + following). - - Returns a list of profiles that the user is connected with. - tags: - - profiles - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/ProfileCompact' - description: '' - /profiles/me/: - get: - operationId: profiles_me_retrieve - tags: - - profiles - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/Profile' - description: '' - patch: - operationId: profiles_me_partial_update - tags: - - profiles - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PatchedProfileUpdateRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PatchedProfileUpdateRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PatchedProfileUpdateRequest' - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/ProfileUpdate' - description: '' - /uploads/presign/: - post: - operationId: uploads_presign_create - tags: - - uploads - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/PresignUploadRequestRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/PresignUploadRequestRequest' - multipart/form-data: - schema: - $ref: '#/components/schemas/PresignUploadRequestRequest' - required: true - security: - - jwtAuth: [] - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/PresignUploadRequest' - description: '' -components: - schemas: - AccountTypeEnum: - enum: - - personal - - business - - creator - type: string - description: |- - * `personal` - personal - * `business` - business - * `creator` - creator - Activity: - type: object - properties: - id: - type: integer - readOnly: true - actor_username: - type: string - readOnly: true - verb: - type: string - maxLength: 50 - is_read: - type: boolean - created_at: - type: string - format: date-time - readOnly: true - required: - - actor_username - - created_at - - id - - verb - ActivityLog: - type: object - properties: - id: - type: integer - readOnly: true - verb: - type: string - maxLength: 50 - target_type: - type: string - nullable: true - readOnly: true - target_id: - type: integer - nullable: true - readOnly: true - created_at: - type: string - format: date-time - readOnly: true - required: - - created_at - - id - - target_id - - target_type - - verb - BlankEnum: - enum: - - '' - Conversation: - type: object - description: Detailed serializer for a single conversation. - properties: - id: - type: integer - readOnly: true - participants: - type: array - items: - $ref: '#/components/schemas/ConversationParticipant' - readOnly: true - messages: - type: array - items: - $ref: '#/components/schemas/Message' - readOnly: true - created_at: - type: string - format: date-time - readOnly: true - updated_at: - type: string - format: date-time - readOnly: true - required: - - created_at - - id - - messages - - participants - - updated_at - ConversationList: - type: object - description: Serializer for listing conversations with preview. - properties: - id: - type: integer - readOnly: true - other_participant: - type: string - readOnly: true - last_message: - type: string - readOnly: true - unread_count: - type: string - readOnly: true - created_at: - type: string - format: date-time - readOnly: true - updated_at: - type: string - format: date-time - readOnly: true - required: - - created_at - - id - - last_message - - other_participant - - unread_count - - updated_at - ConversationParticipant: - type: object - description: Serializer for conversation participants. - properties: - id: - type: integer - username: - type: string - first_name: - type: string - last_name: - type: string - headline: - type: string - nullable: true - required: - - first_name - - headline - - id - - last_name - - username - EmploymentTypeEnum: - enum: - - full-time - - part-time - - self-employed - - freelance - - contract - - internship - - apprenticeship - - seasonal - type: string - description: |- - * `full-time` - Full-time - * `part-time` - Part-time - * `self-employed` - Self-employed - * `freelance` - Freelance - * `contract` - Contract - * `internship` - Internship - * `apprenticeship` - Apprenticeship - * `seasonal` - Seasonal - EndMonthEnum: - enum: - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - 10 - - 11 - - 12 - type: integer - description: |- - * `1` - 1 - * `2` - 2 - * `3` - 3 - * `4` - 4 - * `5` - 5 - * `6` - 6 - * `7` - 7 - * `8` - 8 - * `9` - 9 - * `10` - 10 - * `11` - 11 - * `12` - 12 - FollowResponse: - type: object - properties: - detail: - type: string - required: - - detail - ForgotPassword: - type: object - properties: - email: - type: string - format: email - required: - - email - ForgotPasswordRequest: - type: object - properties: - email: - type: string - format: email - minLength: 1 - required: - - email - LocationTypeEnum: - enum: - - current - - previous - - hometown - - other - type: string - description: |- - * `current` - Current - * `previous` - Previous - * `hometown` - Hometown - * `other` - Other - Login: - type: object - properties: - username: - type: string - required: - - username - LoginRequest: - type: object - properties: - username: - type: string - minLength: 1 - password: - type: string - writeOnly: true - minLength: 1 - required: - - password - - username - Message: - type: object - description: Serializer for Message model. - properties: - id: - type: integer - readOnly: true - conversation: - type: integer - sender_id: - type: integer - readOnly: true - sender_username: - type: string - readOnly: true - sender_first_name: - type: string - readOnly: true - sender_last_name: - type: string - readOnly: true - content: - type: string - timestamp: - type: string - format: date-time - readOnly: true - is_read: - type: boolean - readOnly: true - read_at: - type: string - format: date-time - readOnly: true - nullable: true - required: - - content - - conversation - - id - - is_read - - read_at - - sender_first_name - - sender_id - - sender_last_name - - sender_username - - timestamp - MessageCreateRequest: - type: object - description: Serializer for creating a new message. - properties: - recipient_id: - type: integer - content: - type: string - minLength: 1 - maxLength: 5000 - required: - - content - - recipient_id - NullEnum: - enum: - - null - OnboardingStatus: - type: object - properties: - completed: - type: boolean - required: - - completed - PatchedPostCommentRequest: - type: object - properties: - content: - type: string - minLength: 1 - PatchedPostModelRequest: - type: object - properties: - content: - type: string - poll: - nullable: true - location: - type: string - maxLength: 255 - feeling: - type: string - maxLength: 100 - privacy: - type: string - minLength: 1 - maxLength: 50 - PatchedProfileUpdateRequest: - type: object - description: |- - Serializer for updating profile and user information. - Separates read and write operations - follows Single Responsibility Principle. - properties: - display_name: - type: string - minLength: 1 - maxLength: 150 - bio: - type: string - avatar: - type: string - nullable: true - minLength: 1 - headline: - type: string - maxLength: 255 - current_position: - type: string - maxLength: 255 - company: - type: string - maxLength: 255 - industry: - type: string - maxLength: 100 - company_logo: - type: string - nullable: true - minLength: 1 - cover_image: - type: string - nullable: true - minLength: 1 - phone: - type: string - maxLength: 20 - website: - type: string - format: uri - linkedin_url: - type: string - format: uri - twitter_url: - type: string - format: uri - account_type: - $ref: '#/components/schemas/AccountTypeEnum' - open_to_work: - type: boolean - open_to_hire: - type: boolean - is_public: - type: boolean - PostComment: - type: object - properties: - id: - type: integer - readOnly: true - post: - type: integer - readOnly: true - user: - type: string - description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_ - only. - readOnly: true - content: - type: string - created_at: - type: string - format: date-time - readOnly: true - required: - - content - - created_at - - id - - post - - user - PostCommentRequest: - type: object - properties: - content: - type: string - minLength: 1 - required: - - content - PostLike: - type: object - properties: - id: - type: integer - readOnly: true - user: - type: string - description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_ - only. - readOnly: true - post: - type: integer - readOnly: true - created_at: - type: string - format: date-time - readOnly: true - required: - - created_at - - id - - post - - user - PostModel: - type: object - properties: - id: - type: integer - readOnly: true - author: - type: string - description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_ - only. - readOnly: true - image_url: - type: string - readOnly: true - content: - type: string - poll: - nullable: true - location: - type: string - maxLength: 255 - feeling: - type: string - maxLength: 100 - privacy: - type: string - maxLength: 50 - created_at: - type: string - format: date-time - readOnly: true - likes_count: - type: integer - readOnly: true - comments_count: - type: integer - readOnly: true - shares_count: - type: integer - readOnly: true - required: - - author - - comments_count - - created_at - - id - - image_url - - likes_count - - shares_count - PostModelRequest: - type: object - properties: - content: - type: string - poll: - nullable: true - location: - type: string - maxLength: 255 - feeling: - type: string - maxLength: 100 - privacy: - type: string - minLength: 1 - maxLength: 50 - PostReport: - type: object - properties: - id: - type: integer - readOnly: true - reported_by: - type: string - description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_ - only. - readOnly: true - post: - type: integer - readOnly: true - reason: - type: string - created_at: - type: string - format: date-time - readOnly: true - required: - - created_at - - id - - post - - reason - - reported_by - PostReportRequest: - type: object - properties: - reason: - type: string - minLength: 1 - required: - - reason - PresignUploadRequest: - type: object - properties: - contentType: - type: string - maxLength: 100 - required: - - contentType - PresignUploadRequestRequest: - type: object - properties: - contentType: - type: string - minLength: 1 - maxLength: 100 - required: - - contentType - Profile: - type: object - description: |- - Main profile response serializer with key fields. - Follows Dependency Inversion Principle - depends on ProfileStatsService. - properties: - id: - type: integer - display_name: - type: string - username: - type: string - bio: - type: string - avatar: - type: string - readOnly: true - headline: - type: string - nullable: true - current_position: - type: string - nullable: true - company: - type: string - nullable: true - company_logo: - type: string - readOnly: true - industry: - type: string - nullable: true - locations: - type: array - items: - $ref: '#/components/schemas/ProfileLocation' - readOnly: true - phone: - type: string - nullable: true - website: - type: string - format: uri - nullable: true - linkedin_url: - type: string - format: uri - nullable: true - twitter_url: - type: string - format: uri - nullable: true - connections_count: - type: string - readOnly: true - followers_count: - type: string - readOnly: true - following_count: - type: string - readOnly: true - posts_count: - type: string - readOnly: true - is_connected: - type: string - readOnly: true - is_following: - type: string - readOnly: true - mutual_connections_count: - type: string - readOnly: true - is_verified: - type: boolean - is_premium: - type: boolean - account_type: - type: string - cover_image: - type: string - readOnly: true - joined_date: - type: string - format: date-time - skills: - type: array - items: - type: string - educations: - type: array - items: - $ref: '#/components/schemas/ProfileEducation' - readOnly: true - work_experiences: - type: array - items: - $ref: '#/components/schemas/ProfileWorkExperience' - readOnly: true - open_to_work: - type: boolean - open_to_hire: - type: boolean - required: - - account_type - - avatar - - bio - - company - - company_logo - - connections_count - - cover_image - - current_position - - display_name - - educations - - followers_count - - following_count - - headline - - id - - industry - - is_connected - - is_following - - is_premium - - is_verified - - joined_date - - linkedin_url - - locations - - mutual_connections_count - - open_to_hire - - open_to_work - - phone - - posts_count - - skills - - twitter_url - - username - - website - - work_experiences - ProfileCompact: - type: object - description: |- - Lightweight profile serializer for list views and embeds. - Follows Single Responsibility - only essential fields for performance. - properties: - id: - type: integer - display_name: - type: string - username: - type: string - avatar: - type: string - readOnly: true - headline: - type: string - nullable: true - company: - type: string - nullable: true - is_verified: - type: boolean - followers_count: - type: string - readOnly: true - required: - - avatar - - company - - display_name - - followers_count - - headline - - id - - is_verified - - username - ProfileEducation: - type: object - description: Serializer for user profile education - properties: - id: - type: integer - readOnly: true - name: - type: string - maxLength: 255 - degrees: {} - duration: - type: string - nullable: true - maxLength: 100 - start_year: - type: integer - maximum: 9223372036854775807 - minimum: -9223372036854775808 - format: int64 - nullable: true - end_year: - type: integer - maximum: 9223372036854775807 - minimum: -9223372036854775808 - format: int64 - nullable: true - is_current: - type: boolean - school_logo: - type: string - readOnly: true - description: - type: string - created_at: - type: string - format: date-time - readOnly: true - updated_at: - type: string - format: date-time - readOnly: true - required: - - created_at - - id - - name - - school_logo - - updated_at - ProfileLocation: - type: object - description: Serializer for user profile locations - properties: - id: - type: integer - readOnly: true - location: - type: string - maxLength: 255 - is_primary: - type: boolean - location_type: - $ref: '#/components/schemas/LocationTypeEnum' - created_at: - type: string - format: date-time - readOnly: true - updated_at: - type: string - format: date-time - readOnly: true - required: - - created_at - - id - - location - - updated_at - ProfileUpdate: - type: object - description: |- - Serializer for updating profile and user information. - Separates read and write operations - follows Single Responsibility Principle. - properties: - display_name: - type: string - maxLength: 150 - bio: - type: string - avatar: - type: string - nullable: true - headline: - type: string - maxLength: 255 - current_position: - type: string - maxLength: 255 - company: - type: string - maxLength: 255 - industry: - type: string - maxLength: 100 - company_logo: - type: string - nullable: true - cover_image: - type: string - nullable: true - phone: - type: string - maxLength: 20 - website: - type: string - format: uri - linkedin_url: - type: string - format: uri - twitter_url: - type: string - format: uri - account_type: - $ref: '#/components/schemas/AccountTypeEnum' - open_to_work: - type: boolean - open_to_hire: - type: boolean - is_public: - type: boolean - ProfileWorkExperience: - type: object - description: Serializer for user profile work experience - properties: - id: - type: integer - readOnly: true - company_name: - type: string - maxLength: 255 - job_title: - type: string - maxLength: 255 - location: - type: string - nullable: true - maxLength: 255 - employment_type: - nullable: true - oneOf: - - $ref: '#/components/schemas/EmploymentTypeEnum' - - $ref: '#/components/schemas/BlankEnum' - - $ref: '#/components/schemas/NullEnum' - start_year: - type: integer - maximum: 9223372036854775807 - minimum: -9223372036854775808 - format: int64 - start_month: - nullable: true - minimum: -9223372036854775808 - maximum: 9223372036854775807 - oneOf: - - $ref: '#/components/schemas/StartMonthEnum' - - $ref: '#/components/schemas/NullEnum' - end_year: - type: integer - maximum: 9223372036854775807 - minimum: -9223372036854775808 - format: int64 - nullable: true - end_month: - nullable: true - minimum: -9223372036854775808 - maximum: 9223372036854775807 - oneOf: - - $ref: '#/components/schemas/EndMonthEnum' - - $ref: '#/components/schemas/NullEnum' - is_current: - type: boolean - company_logo: - type: string - readOnly: true - description: - type: string - skills: {} - created_at: - type: string - format: date-time - readOnly: true - updated_at: - type: string - format: date-time - readOnly: true - required: - - company_logo - - company_name - - created_at - - id - - job_title - - start_year - - updated_at - Register: - type: object - properties: - username: - type: string - description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_ - only. - maxLength: 150 - email: - type: string - format: email - first_name: - type: string - maxLength: 150 - last_name: - type: string - maxLength: 150 - required: - - email - - username - RegisterRequest: - type: object - properties: - username: - type: string - minLength: 1 - description: Required. 150 characters or fewer. Letters, digits and @/./+/-/_ - only. - maxLength: 150 - email: - type: string - format: email - minLength: 1 - password: - type: string - writeOnly: true - minLength: 1 - description: Use a strong password - confirm_password: - type: string - writeOnly: true - minLength: 1 - description: Retype the password - first_name: - type: string - minLength: 1 - maxLength: 150 - last_name: - type: string - minLength: 1 - maxLength: 150 - recaptcha_token: - type: string - writeOnly: true - minLength: 1 - required: - - confirm_password - - email - - password - - username - ResendOTP: - type: object - properties: - email: - type: string - format: email - required: - - email - ResendOTPRequest: - type: object - properties: - email: - type: string - format: email - minLength: 1 - required: - - email - ResetPassword: - type: object - properties: - email: - type: string - format: email - otp: - type: string - maxLength: 6 - required: - - email - - otp - ResetPasswordRequest: - type: object - properties: - email: - type: string - format: email - minLength: 1 - otp: - type: string - minLength: 1 - maxLength: 6 - new_password: - type: string - writeOnly: true - minLength: 1 - confirm_password: - type: string - writeOnly: true - minLength: 1 - required: - - confirm_password - - email - - new_password - - otp - StartMonthEnum: - enum: - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - 10 - - 11 - - 12 - type: integer - description: |- - * `1` - 1 - * `2` - 2 - * `3` - 3 - * `4` - 4 - * `5` - 5 - * `6` - 6 - * `7` - 7 - * `8` - 8 - * `9` - 9 - * `10` - 10 - * `11` - 11 - * `12` - 12 - Topic: - type: object - properties: - id: - type: integer - readOnly: true - name: - type: string - maxLength: 100 - required: - - id - - name - TopicSelection: - type: object - properties: - topic_ids: - type: array - items: - type: integer - required: - - topic_ids - TopicSelectionRequest: - type: object - properties: - topic_ids: - type: array - items: - type: integer - required: - - topic_ids - VerifyOTP: - type: object - properties: - email: - type: string - format: email - otp: - type: string - maxLength: 6 - required: - - email - - otp - VerifyOTPRequest: - type: object - properties: - email: - type: string - format: email - minLength: 1 - otp: - type: string - minLength: 1 - maxLength: 6 - required: - - email - - otp - securitySchemes: - jwtAuth: - type: http - scheme: bearer - bearerFormat: JWT diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a605c86..2043bb7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,6 +46,76 @@ pod install npm test ``` +## Backend setup (Docker) + +The backend services run via Docker Compose. This includes the Django API, PostgreSQL, Redis, and MinIO (S3-compatible storage). + +### Prerequisites +- Docker and Docker Compose installed + +### Quick start + +1. Start all services: + +```bash +docker compose up -d +``` + +2. Check logs: + +```bash +docker compose logs -f web +``` + +3. Stop services: + +```bash +docker compose down +``` + +### Available services + +| Service | URL | Description | +|---------------|------------------------------|--------------------------------| +| Backend API | http://localhost:8000 | Django REST API | +| pgAdmin | http://localhost:5050 | PostgreSQL admin UI | +| MinIO Console | http://localhost:9001 | S3-compatible storage UI | +| RedisInsight | http://localhost:5540 | Redis management UI | + +### Environment variables + +Key environment variables in `docker-compose.yml`: + +| Variable | Default | Description | +|---------------------------|---------|------------------------------------------| +| `DEBUG` | `True` | Enable Django debug mode | +| `OTP_VERIFICATION_ENABLED`| `False` | Set to `True` to require email OTP | +| `EMAIL_HOST_USER` | - | Gmail address for sending OTP emails | +| `EMAIL_HOST_PASSWORD` | - | Gmail app password (not regular password)| + +### Pull latest backend image + +```bash +docker compose pull web +docker compose up -d --force-recreate web +``` + +### Create a admin user + +```bash +docker exec -it bizbuch_backend python manage.py createsuperuser +``` + +### Database management + +Access PostgreSQL via pgAdmin at http://localhost:5050 (credentials: `admin@admin.com` / `admin`). + +Or via command line: + +```bash +docker exec -it bizbuch-db-1 psql -U postgres -d bizbuch +``` + ## Code style - Project uses `eslint` and `prettier`. Run `npm run lint` before opening a PR. - Keep changes focused and include tests for new behavior when feasible. diff --git a/README.md b/README.md index b0ebbcd..559f491 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Unlike traditional social platforms where a single company owns the servers, dat --- - + ## ✨ Key Idea diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..582cb01 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,169 @@ +services: + web: + image: ujjwalkar/bizbuch-backend:latest + container_name: bizbuch_backend + restart: always + command: > + sh -c " + python manage.py migrate && + python manage.py collectstatic --noinput && + daphne -b 0.0.0.0 -p 8000 --access-log - --verbosity 1 mysite.asgi:application + " + ports: + - "8000:8000" # ✅ only required exposed port + environment: + - DEBUG=True + - OTP_VERIFICATION_ENABLED=False # Set to True to enable OTP verification + - EMAIL_HOST_USER=your_email@example.com # Your email here + - EMAIL_HOST_PASSWORD=your_email_password_here # Create app password from here https://myaccount.google.com/apppasswords + + # Postgres + - DB_HOST=db + - DB_PORT=5432 + - POSTGRES_DB=bizbuch + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + + # Redis + - REDIS_HOST=redis + + # S3 (MinIO) + - AWS_ACCESS_KEY_ID=bizbuch_admin + - AWS_SECRET_ACCESS_KEY=bizbuch_secret_key + - AWS_S3_ENDPOINT_URL=http://minio:9000 + - AWS_S3_BUCKET=bizbuch + - AWS_DEFAULT_REGION=us-east-1 + - AWS_S3_USE_SSL=False + + depends_on: + db: + condition: service_healthy + redis: + condition: service_started + minio: + condition: service_started + minio_init: + condition: service_completed_successfully + networks: + bizbuch_network: + ipv4_address: 172.28.0.10 + + db: + image: postgres:15 + restart: always + environment: + POSTGRES_DB: bizbuch + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + # Optional: expose Postgres port only if needed for external access + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres -d bizbuch"] + interval: 5s + timeout: 3s + retries: 20 + networks: + bizbuch_network: + ipv4_address: 172.28.0.11 + + redis: + image: redis:7-alpine + restart: always + # Optional: expose Redis port only if needed for external access + # ports: + # - "6379:6379" + networks: + bizbuch_network: + ipv4_address: 172.28.0.12 + + redisinsight: + image: redis/redisinsight:latest + container_name: redisinsight + restart: always + ports: + - "5540:5540" + depends_on: + - redis + networks: + bizbuch_network: + ipv4_address: 172.28.0.17 + + minio: + image: minio/minio:latest + container_name: minio + restart: always + environment: + MINIO_ROOT_USER: bizbuch_admin + MINIO_ROOT_PASSWORD: bizbuch_secret_key + command: server /data --console-address ":9001" + volumes: + - minio_data:/data + + # Optional: expose MinIO ports only if needed for UI/debugging + ports: + - "9000:9000" + - "9001:9001" + networks: + bizbuch_network: + ipv4_address: 172.28.0.13 + + minio_init: + image: minio/mc:latest + depends_on: + - minio + restart: "no" + entrypoint: > + /bin/sh -c " + until (/usr/bin/mc alias set myminio http://minio:9000 bizbuch_admin bizbuch_secret_key) do sleep 2; done; + /usr/bin/mc mb -p myminio/bizbuch || true; + /usr/bin/mc anonymous set download myminio/bizbuch || true; + exit 0; + " + networks: + bizbuch_network: + ipv4_address: 172.28.0.14 + + pgadmin: + image: dpage/pgadmin4:latest + container_name: pgadmin + restart: always + environment: + PGADMIN_DEFAULT_EMAIL: admin@admin.com + PGADMIN_DEFAULT_PASSWORD: admin + PGADMIN_CONFIG_SERVER_MODE: "False" + PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False" + ports: + - "5050:80" + volumes: + - ./pgadmin/servers.json:/pgadmin4/servers.json:ro + depends_on: + - db + networks: + bizbuch_network: + ipv4_address: 172.28.0.16 + + # Uncomment the following section to enable Cloudflare Tunnel + tunnel: + image: cloudflare/cloudflared:latest + command: tunnel --no-autoupdate --url http://web:8000 + restart: always + depends_on: + - web + networks: + bizbuch_network: + ipv4_address: 172.28.0.15 + +volumes: + postgres_data: + minio_data: + +networks: + bizbuch_network: + driver: bridge + ipam: + config: + - subnet: 172.28.0.0/16 + gateway: 172.28.0.1 diff --git a/package-lock.json b/package-lock.json index 08aaefb..bec599f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10498,9 +10498,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "11.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", - "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "version": "11.2.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.5.tgz", + "integrity": "sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" diff --git a/src/assets/screenshots/Screenshot_4.png b/src/assets/screenshots/Screenshot_4.png new file mode 100644 index 0000000..7e78c39 Binary files /dev/null and b/src/assets/screenshots/Screenshot_4.png differ diff --git a/src/assets/screenshots/Screenshot_5.png b/src/assets/screenshots/Screenshot_5.png new file mode 100644 index 0000000..c1dcd5b Binary files /dev/null and b/src/assets/screenshots/Screenshot_5.png differ diff --git a/src/assets/screenshots/Screenshot_6.png b/src/assets/screenshots/Screenshot_6.png new file mode 100644 index 0000000..ec32112 Binary files /dev/null and b/src/assets/screenshots/Screenshot_6.png differ diff --git a/src/assets/screenshots/Screenshot_7.png b/src/assets/screenshots/Screenshot_7.png new file mode 100644 index 0000000..049141e Binary files /dev/null and b/src/assets/screenshots/Screenshot_7.png differ diff --git a/src/assets/screenshots/Screenshot_8.png b/src/assets/screenshots/Screenshot_8.png new file mode 100644 index 0000000..1454de1 Binary files /dev/null and b/src/assets/screenshots/Screenshot_8.png differ diff --git a/src/data/repositories/ProfileRepository.ts b/src/data/repositories/ProfileRepository.ts index f1bb6f4..fe607a9 100644 --- a/src/data/repositories/ProfileRepository.ts +++ b/src/data/repositories/ProfileRepository.ts @@ -21,7 +21,7 @@ export class ProfileRepository implements IProfileRepository { } async addWorkExperience(data: AddWorkExperienceData): Promise { - await postAuth("profiles/me/work-experiences/", data); + await postAuth("profiles/me/work-experience/", data); } async addEducation(data: AddEducationData): Promise { diff --git a/src/presentation/components/templates/BottomNavigationTemplate.tsx b/src/presentation/components/templates/BottomNavigationTemplate.tsx index 60ee32b..f94d5af 100644 --- a/src/presentation/components/templates/BottomNavigationTemplate.tsx +++ b/src/presentation/components/templates/BottomNavigationTemplate.tsx @@ -1,6 +1,7 @@ import React, { useMemo, useCallback } from 'react'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { getFocusedRouteNameFromRoute, RouteProp } from '@react-navigation/native'; +import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { TabIcon } from '../atoms/TabIcon'; import { TAB_ROUTES } from '../../navigation/TabRoutes'; import { theme } from '../../theme'; @@ -25,18 +26,26 @@ const HIDDEN_TAB_BAR_SCREENS = [ * Tab bar style configuration * Single Responsibility: Compute tab bar visibility and styling */ -const getTabBarStyle = (route: RouteProp>) => { +const getTabBarStyle = ( + route: RouteProp>, + bottomInset: number +) => { const routeName = getFocusedRouteNameFromRoute(route); if (routeName && HIDDEN_TAB_BAR_SCREENS.includes(routeName as typeof HIDDEN_TAB_BAR_SCREENS[number])) { return { display: 'none' as const }; } + // Use safe area bottom inset to ensure tab bar is visible above home indicator + // Minimum padding ensures proper spacing even on devices without notches + const safeBottomPadding = Math.max(bottomInset, bottomNavigation.paddingBottom); + return { backgroundColor: theme.colors.white, borderTopColor: theme.colors.border, paddingTop: bottomNavigation.paddingTop, - paddingBottom: bottomNavigation.paddingBottom, + paddingBottom: safeBottomPadding, + height: 56 + safeBottomPadding, // Fixed height for consistency }; }; @@ -48,6 +57,8 @@ const getTabBarStyle = (route: RouteProp>) => * SOLID: Dependency Inversion - Uses TabIcon atom abstraction */ export const BottomNavigationTemplate: React.FC = () => { + const insets = useSafeAreaInsets(); + const getTabBarIcon = useCallback( (tabKey: string) => ({ focused, color }: { focused: boolean; color: string }) => { @@ -67,14 +78,14 @@ export const BottomNavigationTemplate: React.FC = () => { headerShown: false, tabBarActiveTintColor: theme.colors.primary, tabBarInactiveTintColor: theme.colors.gray600, - tabBarStyle: getTabBarStyle(route), + tabBarStyle: getTabBarStyle(route, insets.bottom), tabBarLabelStyle: { fontSize: bottomNavigation.labelFontSize, fontWeight: bottomNavigation.labelFontWeight, }, tabBarIcon: getTabBarIcon(route.name), }), - [getTabBarIcon], + [getTabBarIcon, insets.bottom], ); return (