Enhance tender management with recommended tenders endpoint and search form update
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
- Added a new endpoint in the `tender` handler for retrieving AI-ranked tender recommendations for a company, improving the functionality of the admin panel. - Updated the `SearchForm` to include a query parameter for `only_active_deadlines`, allowing for more flexible search options. - Enhanced API documentation for the new endpoint to provide clear usage instructions and expected parameters. This update improves the tender management system by providing administrators with better tools for accessing relevant tender information.
This commit is contained in:
@@ -73,6 +73,7 @@ func RegisterAdminRoutes(e *echo.Echo, userHandler *user.Handler, companyHandler
|
|||||||
companiesGP.Use(userHandler.AuthMiddleware())
|
companiesGP.Use(userHandler.AuthMiddleware())
|
||||||
companiesGP.POST("", companyHandler.Create)
|
companiesGP.POST("", companyHandler.Create)
|
||||||
companiesGP.GET("", companyHandler.Search)
|
companiesGP.GET("", companyHandler.Search)
|
||||||
|
companiesGP.GET("/:id/recommended-tenders", tenderHandler.AdminRecommendTenders)
|
||||||
companiesGP.GET("/:id", companyHandler.Get)
|
companiesGP.GET("/:id", companyHandler.Get)
|
||||||
companiesGP.PUT("/:id", companyHandler.Update)
|
companiesGP.PUT("/:id", companyHandler.Update)
|
||||||
companiesGP.DELETE("/:id", companyHandler.Delete)
|
companiesGP.DELETE("/:id", companyHandler.Delete)
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ type SearchForm struct {
|
|||||||
BuyerOrganizationID *string `query:"buyer_organization_id" valid:"optional"`
|
BuyerOrganizationID *string `query:"buyer_organization_id" valid:"optional"`
|
||||||
NoticePublicationID *string `query:"notice_publication_id" valid:"optional"`
|
NoticePublicationID *string `query:"notice_publication_id" valid:"optional"`
|
||||||
Languages []string `query:"languages" valid:"optional"`
|
Languages []string `query:"languages" valid:"optional"`
|
||||||
OnlyActiveDeadlines bool `query:"-" valid:"optional"`
|
OnlyActiveDeadlines bool `query:"only_active_deadlines" valid:"optional"`
|
||||||
Language *string `query:"lang" valid:"optional"`
|
Language *string `query:"lang" valid:"optional"`
|
||||||
DocumentsScraped bool `query:"documents_scraped" valid:"optional"`
|
DocumentsScraped bool `query:"documents_scraped" valid:"optional"`
|
||||||
// ContractFolderIDsWithDocuments is populated by the service from MinIO when DocumentsScraped is true.
|
// ContractFolderIDsWithDocuments is populated by the service from MinIO when DocumentsScraped is true.
|
||||||
|
|||||||
@@ -198,6 +198,44 @@ func (h *TenderHandler) Search(c echo.Context) error {
|
|||||||
return response.SuccessWithMeta(c, tenders, tenders.Metadata, "Tenders retrieved successfully")
|
return response.SuccessWithMeta(c, tenders, tenders.Metadata, "Tenders retrieved successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AdminRecommendTenders returns AI-ranked tender recommendations for a company (Web Panel)
|
||||||
|
// @Summary Get recommended tenders for a company
|
||||||
|
// @Description Retrieve AI-ranked tenders with full tender details for the given company. Unlike the customer API, active-only filters are not applied unless requested via query parameters.
|
||||||
|
// @Tags Admin-Companies
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path string true "Company ID"
|
||||||
|
// @Param limit query int false "Number of items per page (default: 10, max: 100)"
|
||||||
|
// @Param offset query int false "Number of items to skip (default: 0)"
|
||||||
|
// @Param lang query string false "Response language code (defaults to en)"
|
||||||
|
// @Param status query []string false "Filter by status (comma-separated)"
|
||||||
|
// @Param only_active_deadlines query bool false "When true, exclude tenders past their deadline"
|
||||||
|
// @Success 200 {object} response.APIResponse{data=SearchResponse}
|
||||||
|
// @Failure 400 {object} response.APIResponse
|
||||||
|
// @Failure 404 {object} response.APIResponse
|
||||||
|
// @Failure 503 {object} response.APIResponse
|
||||||
|
// @Failure 500 {object} response.APIResponse
|
||||||
|
// @Security BearerAuth
|
||||||
|
// @Router /admin/v1/companies/{id}/recommended-tenders [get]
|
||||||
|
func (h *TenderHandler) AdminRecommendTenders(c echo.Context) error {
|
||||||
|
companyID := strings.TrimSpace(c.Param("id"))
|
||||||
|
if companyID == "" {
|
||||||
|
return response.BadRequest(c, "Company ID is required", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
form, err := response.Parse[SearchForm](c)
|
||||||
|
if err != nil {
|
||||||
|
return response.BadRequest(c, "Invalid request format", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := govalidator.ValidateStruct(form); err != nil {
|
||||||
|
return response.ValidationError(c, err.Error(), "")
|
||||||
|
}
|
||||||
|
|
||||||
|
form.CompanyID = &companyID
|
||||||
|
|
||||||
|
return h.serveRecommendTenders(c, form)
|
||||||
|
}
|
||||||
|
|
||||||
// *****************************************************
|
// *****************************************************
|
||||||
// * PUBLIC HANDLERS *
|
// * PUBLIC HANDLERS *
|
||||||
// *****************************************************
|
// *****************************************************
|
||||||
@@ -319,6 +357,10 @@ func (h *TenderHandler) RecommendTenders(c echo.Context) error {
|
|||||||
form.CompanyID = &customerCompanyID
|
form.CompanyID = &customerCompanyID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return h.serveRecommendTenders(c, form)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *TenderHandler) serveRecommendTenders(c echo.Context, form *SearchForm) error {
|
||||||
pagination, err := response.NewPagination(c)
|
pagination, err := response.NewPagination(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.PaginationBadRequest(c, err)
|
return response.PaginationBadRequest(c, err)
|
||||||
|
|||||||
Reference in New Issue
Block a user