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)