Add Contact Management Functionality

- Introduced a new contact management feature, including the ability to create, retrieve, search, update, and delete contact messages.
- Implemented the Contact entity, repository, service, and handler layers following Clean Architecture principles.
- Added API endpoints for contact operations in Swagger documentation, ensuring comprehensive API specifications.
- Enhanced error handling and validation for contact data, improving robustness and user experience.
- Updated the main application to initialize the contact repository and service, integrating them into the existing system.
This commit is contained in:
n.nakhostin
2025-10-25 18:15:39 +03:30
parent 889a56a9f9
commit 875447ac58
11 changed files with 2151 additions and 10 deletions
+492
View File
@@ -1043,6 +1043,337 @@ const docTemplate = `{
}
}
},
"/admin/v1/contacts": {
"get": {
"description": "Search contacts with filters and pagination (admin only)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"contacts"
],
"summary": "Search contacts",
"parameters": [
{
"type": "string",
"description": "Search query",
"name": "q",
"in": "query"
},
{
"enum": [
"pending",
"in_progress",
"resolved",
"closed"
],
"type": "string",
"description": "Contact status",
"name": "status",
"in": "query"
},
{
"type": "integer",
"description": "Date from (Unix timestamp)",
"name": "date_from",
"in": "query"
},
{
"type": "integer",
"description": "Date to (Unix timestamp)",
"name": "date_to",
"in": "query"
},
{
"maximum": 100,
"minimum": 1,
"type": "integer",
"default": 20,
"description": "Number of contacts per page (1-100)",
"name": "limit",
"in": "query"
},
{
"minimum": 0,
"type": "integer",
"default": 0,
"description": "Number of contacts to skip for pagination",
"name": "offset",
"in": "query"
},
{
"enum": [
"full_name",
"email",
"created_at",
"updated_at",
"status"
],
"type": "string",
"default": "created_at",
"description": "Field to sort by",
"name": "sort_by",
"in": "query"
},
{
"enum": [
"asc",
"desc"
],
"type": "string",
"default": "desc",
"description": "Sort order",
"name": "sort_order",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.APIResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/contact.ContactListResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/admin/v1/contacts/stats": {
"get": {
"description": "Get contact statistics by status (admin only)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"contacts"
],
"summary": "Get contact statistics",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.APIResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/contact.ContactStats"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/admin/v1/contacts/{id}": {
"get": {
"description": "Retrieve a contact message by its ID (admin only)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"contacts"
],
"summary": "Get contact by ID",
"parameters": [
{
"type": "string",
"description": "Contact ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.APIResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/contact.ContactResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"delete": {
"description": "Delete a contact message by ID (admin only)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"contacts"
],
"summary": "Delete contact",
"parameters": [
{
"type": "string",
"description": "Contact ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/admin/v1/contacts/{id}/status": {
"put": {
"description": "Update the status of a contact message (admin only)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"contacts"
],
"summary": "Update contact status",
"parameters": [
{
"type": "string",
"description": "Contact ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Status update information",
"name": "status",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/contact.UpdateContactStatusForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/admin/v1/customers": {
"get": {
"security": [
@@ -4699,6 +5030,64 @@ const docTemplate = `{
}
}
},
"/api/v1/contacts": {
"post": {
"description": "Create a new contact message from a user",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"contacts"
],
"summary": "Create a new contact message",
"parameters": [
{
"description": "Contact information",
"name": "contact",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/contact.CreateContactForm"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.APIResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/contact.ContactResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/api/v1/feedback": {
"get": {
"security": [
@@ -7369,6 +7758,109 @@ const docTemplate = `{
}
}
},
"contact.ContactListResponse": {
"type": "object",
"properties": {
"contacts": {
"type": "array",
"items": {
"$ref": "#/definitions/contact.ContactResponse"
}
}
}
},
"contact.ContactResponse": {
"type": "object",
"properties": {
"admin_notes": {
"type": "string"
},
"created_at": {
"type": "integer"
},
"email": {
"type": "string"
},
"full_name": {
"type": "string"
},
"id": {
"type": "string"
},
"message": {
"type": "string"
},
"phone": {
"type": "string"
},
"resolved_at": {
"type": "integer"
},
"resolved_by": {
"type": "string"
},
"status": {
"type": "string"
},
"updated_at": {
"type": "integer"
}
}
},
"contact.ContactStats": {
"type": "object",
"properties": {
"closed": {
"type": "integer"
},
"in_progress": {
"type": "integer"
},
"pending": {
"type": "integer"
},
"resolved": {
"type": "integer"
},
"total": {
"type": "integer"
}
}
},
"contact.CreateContactForm": {
"type": "object",
"properties": {
"email": {
"type": "string",
"example": "john.doe@example.com"
},
"full_name": {
"type": "string",
"example": "John Doe"
},
"message": {
"type": "string",
"example": "I have a question about your services"
},
"phone": {
"type": "string",
"example": "+1234567890"
}
}
},
"contact.UpdateContactStatusForm": {
"type": "object",
"properties": {
"admin_notes": {
"type": "string",
"example": "Customer inquiry handled"
},
"status": {
"type": "string",
"example": "in_progress"
}
}
},
"customer.AssignCompaniesForm": {
"type": "object",
"properties": {
+492
View File
@@ -1035,6 +1035,337 @@
}
}
},
"/admin/v1/contacts": {
"get": {
"description": "Search contacts with filters and pagination (admin only)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"contacts"
],
"summary": "Search contacts",
"parameters": [
{
"type": "string",
"description": "Search query",
"name": "q",
"in": "query"
},
{
"enum": [
"pending",
"in_progress",
"resolved",
"closed"
],
"type": "string",
"description": "Contact status",
"name": "status",
"in": "query"
},
{
"type": "integer",
"description": "Date from (Unix timestamp)",
"name": "date_from",
"in": "query"
},
{
"type": "integer",
"description": "Date to (Unix timestamp)",
"name": "date_to",
"in": "query"
},
{
"maximum": 100,
"minimum": 1,
"type": "integer",
"default": 20,
"description": "Number of contacts per page (1-100)",
"name": "limit",
"in": "query"
},
{
"minimum": 0,
"type": "integer",
"default": 0,
"description": "Number of contacts to skip for pagination",
"name": "offset",
"in": "query"
},
{
"enum": [
"full_name",
"email",
"created_at",
"updated_at",
"status"
],
"type": "string",
"default": "created_at",
"description": "Field to sort by",
"name": "sort_by",
"in": "query"
},
{
"enum": [
"asc",
"desc"
],
"type": "string",
"default": "desc",
"description": "Sort order",
"name": "sort_order",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.APIResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/contact.ContactListResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/admin/v1/contacts/stats": {
"get": {
"description": "Get contact statistics by status (admin only)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"contacts"
],
"summary": "Get contact statistics",
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.APIResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/contact.ContactStats"
}
}
}
]
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/admin/v1/contacts/{id}": {
"get": {
"description": "Retrieve a contact message by its ID (admin only)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"contacts"
],
"summary": "Get contact by ID",
"parameters": [
{
"type": "string",
"description": "Contact ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.APIResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/contact.ContactResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"delete": {
"description": "Delete a contact message by ID (admin only)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"contacts"
],
"summary": "Delete contact",
"parameters": [
{
"type": "string",
"description": "Contact ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/admin/v1/contacts/{id}/status": {
"put": {
"description": "Update the status of a contact message (admin only)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"contacts"
],
"summary": "Update contact status",
"parameters": [
{
"type": "string",
"description": "Contact ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Status update information",
"name": "status",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/contact.UpdateContactStatusForm"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/admin/v1/customers": {
"get": {
"security": [
@@ -4691,6 +5022,64 @@
}
}
},
"/api/v1/contacts": {
"post": {
"description": "Create a new contact message from a user",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"contacts"
],
"summary": "Create a new contact message",
"parameters": [
{
"description": "Contact information",
"name": "contact",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/contact.CreateContactForm"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"allOf": [
{
"$ref": "#/definitions/response.APIResponse"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/contact.ContactResponse"
}
}
}
]
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/api/v1/feedback": {
"get": {
"security": [
@@ -7361,6 +7750,109 @@
}
}
},
"contact.ContactListResponse": {
"type": "object",
"properties": {
"contacts": {
"type": "array",
"items": {
"$ref": "#/definitions/contact.ContactResponse"
}
}
}
},
"contact.ContactResponse": {
"type": "object",
"properties": {
"admin_notes": {
"type": "string"
},
"created_at": {
"type": "integer"
},
"email": {
"type": "string"
},
"full_name": {
"type": "string"
},
"id": {
"type": "string"
},
"message": {
"type": "string"
},
"phone": {
"type": "string"
},
"resolved_at": {
"type": "integer"
},
"resolved_by": {
"type": "string"
},
"status": {
"type": "string"
},
"updated_at": {
"type": "integer"
}
}
},
"contact.ContactStats": {
"type": "object",
"properties": {
"closed": {
"type": "integer"
},
"in_progress": {
"type": "integer"
},
"pending": {
"type": "integer"
},
"resolved": {
"type": "integer"
},
"total": {
"type": "integer"
}
}
},
"contact.CreateContactForm": {
"type": "object",
"properties": {
"email": {
"type": "string",
"example": "john.doe@example.com"
},
"full_name": {
"type": "string",
"example": "John Doe"
},
"message": {
"type": "string",
"example": "I have a question about your services"
},
"phone": {
"type": "string",
"example": "+1234567890"
}
}
},
"contact.UpdateContactStatusForm": {
"type": "object",
"properties": {
"admin_notes": {
"type": "string",
"example": "Customer inquiry handled"
},
"status": {
"type": "string",
"example": "in_progress"
}
}
},
"customer.AssignCompaniesForm": {
"type": "object",
"properties": {
+319
View File
@@ -302,6 +302,75 @@ definitions:
updated_at:
type: integer
type: object
contact.ContactListResponse:
properties:
contacts:
items:
$ref: '#/definitions/contact.ContactResponse'
type: array
type: object
contact.ContactResponse:
properties:
admin_notes:
type: string
created_at:
type: integer
email:
type: string
full_name:
type: string
id:
type: string
message:
type: string
phone:
type: string
resolved_at:
type: integer
resolved_by:
type: string
status:
type: string
updated_at:
type: integer
type: object
contact.ContactStats:
properties:
closed:
type: integer
in_progress:
type: integer
pending:
type: integer
resolved:
type: integer
total:
type: integer
type: object
contact.CreateContactForm:
properties:
email:
example: john.doe@example.com
type: string
full_name:
example: John Doe
type: string
message:
example: I have a question about your services
type: string
phone:
example: "+1234567890"
type: string
type: object
contact.UpdateContactStatusForm:
properties:
admin_notes:
example: Customer inquiry handled
type: string
status:
example: in_progress
type: string
type: object
customer.AssignCompaniesForm:
properties:
companies:
@@ -1999,6 +2068,221 @@ paths:
summary: Toggle category publish status
tags:
- Admin-Company-Categories
/admin/v1/contacts:
get:
consumes:
- application/json
description: Search contacts with filters and pagination (admin only)
parameters:
- description: Search query
in: query
name: q
type: string
- description: Contact status
enum:
- pending
- in_progress
- resolved
- closed
in: query
name: status
type: string
- description: Date from (Unix timestamp)
in: query
name: date_from
type: integer
- description: Date to (Unix timestamp)
in: query
name: date_to
type: integer
- default: 20
description: Number of contacts per page (1-100)
in: query
maximum: 100
minimum: 1
name: limit
type: integer
- default: 0
description: Number of contacts to skip for pagination
in: query
minimum: 0
name: offset
type: integer
- default: created_at
description: Field to sort by
enum:
- full_name
- email
- created_at
- updated_at
- status
in: query
name: sort_by
type: string
- default: desc
description: Sort order
enum:
- asc
- desc
in: query
name: sort_order
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.APIResponse'
- properties:
data:
$ref: '#/definitions/contact.ContactListResponse'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Search contacts
tags:
- contacts
/admin/v1/contacts/{id}:
delete:
consumes:
- application/json
description: Delete a contact message by ID (admin only)
parameters:
- description: Contact ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.APIResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Delete contact
tags:
- contacts
get:
consumes:
- application/json
description: Retrieve a contact message by its ID (admin only)
parameters:
- description: Contact ID
in: path
name: id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.APIResponse'
- properties:
data:
$ref: '#/definitions/contact.ContactResponse'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Get contact by ID
tags:
- contacts
/admin/v1/contacts/{id}/status:
put:
consumes:
- application/json
description: Update the status of a contact message (admin only)
parameters:
- description: Contact ID
in: path
name: id
required: true
type: string
- description: Status update information
in: body
name: status
required: true
schema:
$ref: '#/definitions/contact.UpdateContactStatusForm'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.APIResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"404":
description: Not Found
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Update contact status
tags:
- contacts
/admin/v1/contacts/stats:
get:
consumes:
- application/json
description: Get contact statistics by status (admin only)
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/response.APIResponse'
- properties:
data:
$ref: '#/definitions/contact.ContactStats'
type: object
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Get contact statistics
tags:
- contacts
/admin/v1/customers:
get:
consumes:
@@ -4314,6 +4598,41 @@ paths:
summary: Get my company profile
tags:
- Company
/api/v1/contacts:
post:
consumes:
- application/json
description: Create a new contact message from a user
parameters:
- description: Contact information
in: body
name: contact
required: true
schema:
$ref: '#/definitions/contact.CreateContactForm'
produces:
- application/json
responses:
"201":
description: Created
schema:
allOf:
- $ref: '#/definitions/response.APIResponse'
- properties:
data:
$ref: '#/definitions/contact.ContactResponse'
type: object
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Create a new contact message
tags:
- contacts
/api/v1/feedback:
get:
consumes: