From a7a49fc4112d29b52a101894a4cc94bb3174ee66 Mon Sep 17 00:00:00 2001 From: Mazyar Date: Tue, 23 Jun 2026 13:09:27 +0330 Subject: [PATCH] Enhance tender management with recommended tenders endpoint and search form update - 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. --- cmd/web/router/routes.go | 1 + internal/tender/form.go | 2 +- internal/tender/handler.go | 42 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/cmd/web/router/routes.go b/cmd/web/router/routes.go index 296955b..91e15c9 100644 --- a/cmd/web/router/routes.go +++ b/cmd/web/router/routes.go @@ -73,6 +73,7 @@ func RegisterAdminRoutes(e *echo.Echo, userHandler *user.Handler, companyHandler companiesGP.Use(userHandler.AuthMiddleware()) companiesGP.POST("", companyHandler.Create) companiesGP.GET("", companyHandler.Search) + companiesGP.GET("/:id/recommended-tenders", tenderHandler.AdminRecommendTenders) companiesGP.GET("/:id", companyHandler.Get) companiesGP.PUT("/:id", companyHandler.Update) companiesGP.DELETE("/:id", companyHandler.Delete) diff --git a/internal/tender/form.go b/internal/tender/form.go index d3d4efa..7a712e0 100644 --- a/internal/tender/form.go +++ b/internal/tender/form.go @@ -60,7 +60,7 @@ type SearchForm struct { BuyerOrganizationID *string `query:"buyer_organization_id" valid:"optional"` NoticePublicationID *string `query:"notice_publication_id" 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"` DocumentsScraped bool `query:"documents_scraped" valid:"optional"` // ContractFolderIDsWithDocuments is populated by the service from MinIO when DocumentsScraped is true. diff --git a/internal/tender/handler.go b/internal/tender/handler.go index 231cbf9..b4212ae 100644 --- a/internal/tender/handler.go +++ b/internal/tender/handler.go @@ -198,6 +198,44 @@ func (h *TenderHandler) Search(c echo.Context) error { 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 * // ***************************************************** @@ -319,6 +357,10 @@ func (h *TenderHandler) RecommendTenders(c echo.Context) error { form.CompanyID = &customerCompanyID } + return h.serveRecommendTenders(c, form) +} + +func (h *TenderHandler) serveRecommendTenders(c echo.Context, form *SearchForm) error { pagination, err := response.NewPagination(c) if err != nil { return response.PaginationBadRequest(c, err)