feat: update approval workflows
This commit is contained in:
parent
9ba425540e
commit
df77b1c576
|
|
@ -5,6 +5,12 @@ import (
|
||||||
"netidhub-saas-be/app/module/approval_workflows/repository"
|
"netidhub-saas-be/app/module/approval_workflows/repository"
|
||||||
"netidhub-saas-be/app/module/approval_workflows/service"
|
"netidhub-saas-be/app/module/approval_workflows/service"
|
||||||
|
|
||||||
|
// articleCategoriesRepo "netidhub-saas-be/app/module/article_categories/repository"
|
||||||
|
// clientApprovalSettingsRepo "netidhub-saas-be/app/module/client_approval_settings/repository"
|
||||||
|
// userLevelsRepo "netidhub-saas-be/app/module/user_levels/repository"
|
||||||
|
// userRolesRepo "netidhub-saas-be/app/module/user_roles/repository"
|
||||||
|
// usersRepo "netidhub-saas-be/app/module/users/repository"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"go.uber.org/fx"
|
"go.uber.org/fx"
|
||||||
)
|
)
|
||||||
|
|
@ -20,6 +26,12 @@ var NewApprovalWorkflowsModule = fx.Options(
|
||||||
// register repository of ApprovalWorkflows module
|
// register repository of ApprovalWorkflows module
|
||||||
fx.Provide(repository.NewApprovalWorkflowsRepository),
|
fx.Provide(repository.NewApprovalWorkflowsRepository),
|
||||||
|
|
||||||
|
// fx.Provide(clientApprovalSettingsRepo.NewClientApprovalSettingsRepository),
|
||||||
|
// fx.Provide(usersRepo.NewUsersRepository),
|
||||||
|
// fx.Provide(userLevelsRepo.NewUserLevelsRepository),
|
||||||
|
// fx.Provide(userRolesRepo.NewUserRolesRepository),
|
||||||
|
// fx.Provide(articleCategoriesRepo.NewArticleCategoriesRepository),
|
||||||
|
|
||||||
// register service of ApprovalWorkflows module
|
// register service of ApprovalWorkflows module
|
||||||
fx.Provide(service.NewApprovalWorkflowsService),
|
fx.Provide(service.NewApprovalWorkflowsService),
|
||||||
|
|
||||||
|
|
@ -52,6 +64,7 @@ func (_i *ApprovalWorkflowsRouter) RegisterApprovalWorkflowsRoutes() {
|
||||||
router.Post("/", approvalWorkflowsController.Save)
|
router.Post("/", approvalWorkflowsController.Save)
|
||||||
router.Post("/with-steps", approvalWorkflowsController.SaveWithSteps)
|
router.Post("/with-steps", approvalWorkflowsController.SaveWithSteps)
|
||||||
router.Post("/with-client-settings", approvalWorkflowsController.SaveWithClientSettings)
|
router.Post("/with-client-settings", approvalWorkflowsController.SaveWithClientSettings)
|
||||||
|
router.Post("/comprehensive-details", approvalWorkflowsController.GetComprehensiveDetails)
|
||||||
router.Put("/:id", approvalWorkflowsController.Update)
|
router.Put("/:id", approvalWorkflowsController.Update)
|
||||||
router.Put("/:id/with-steps", approvalWorkflowsController.UpdateWithSteps)
|
router.Put("/:id/with-steps", approvalWorkflowsController.UpdateWithSteps)
|
||||||
router.Put("/:id/set-default", approvalWorkflowsController.SetDefault)
|
router.Put("/:id/set-default", approvalWorkflowsController.SetDefault)
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ type ApprovalWorkflowsController interface {
|
||||||
SaveWithSteps(c *fiber.Ctx) error
|
SaveWithSteps(c *fiber.Ctx) error
|
||||||
UpdateWithSteps(c *fiber.Ctx) error
|
UpdateWithSteps(c *fiber.Ctx) error
|
||||||
SaveWithClientSettings(c *fiber.Ctx) error
|
SaveWithClientSettings(c *fiber.Ctx) error
|
||||||
|
GetComprehensiveDetails(c *fiber.Ctx) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewApprovalWorkflowsController(approvalWorkflowsService service.ApprovalWorkflowsService, log zerolog.Logger) ApprovalWorkflowsController {
|
func NewApprovalWorkflowsController(approvalWorkflowsService service.ApprovalWorkflowsService, log zerolog.Logger) ApprovalWorkflowsController {
|
||||||
|
|
@ -521,3 +522,37 @@ func (_i *approvalWorkflowsController) SaveWithClientSettings(c *fiber.Ctx) erro
|
||||||
Data: responseData,
|
Data: responseData,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetComprehensiveDetails ApprovalWorkflows
|
||||||
|
// @Summary Get comprehensive approval workflow details
|
||||||
|
// @Description API for getting comprehensive details of approval workflow including steps, client settings, and related data
|
||||||
|
// @Tags ApprovalWorkflows
|
||||||
|
// @Security Bearer
|
||||||
|
// @Param Authorization header string true "Insert the Authorization"
|
||||||
|
// @Param req body request.ComprehensiveWorkflowDetailRequest true "Workflow detail request"
|
||||||
|
// @Success 200 {object} response.Response
|
||||||
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
// @Failure 401 {object} response.UnauthorizedError
|
||||||
|
// @Failure 500 {object} response.InternalServerError
|
||||||
|
// @Router /approval-workflows/comprehensive-details [post]
|
||||||
|
func (_i *approvalWorkflowsController) GetComprehensiveDetails(c *fiber.Ctx) error {
|
||||||
|
req := new(request.ComprehensiveWorkflowDetailRequest)
|
||||||
|
if err := utilVal.ParseAndValidate(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get authToken from context
|
||||||
|
authToken := c.Get("Authorization")
|
||||||
|
|
||||||
|
// Get comprehensive workflow details
|
||||||
|
details, err := _i.approvalWorkflowsService.GetComprehensiveWorkflowDetails(authToken, req.WorkflowId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return utilRes.Resp(c, utilRes.Response{
|
||||||
|
Success: true,
|
||||||
|
Messages: utilRes.Messages{"Comprehensive workflow details retrieved successfully"},
|
||||||
|
Data: details,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
package mapper
|
package mapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"netidhub-saas-be/app/database/entity"
|
"netidhub-saas-be/app/database/entity"
|
||||||
approvalWorkflowStepsMapper "netidhub-saas-be/app/module/approval_workflow_steps/mapper"
|
approvalWorkflowStepsMapper "netidhub-saas-be/app/module/approval_workflow_steps/mapper"
|
||||||
approvalWorkflowStepsRepository "netidhub-saas-be/app/module/approval_workflow_steps/repository"
|
approvalWorkflowStepsRepository "netidhub-saas-be/app/module/approval_workflow_steps/repository"
|
||||||
approvalWorkflowStepsResponse "netidhub-saas-be/app/module/approval_workflow_steps/response"
|
approvalWorkflowStepsResponse "netidhub-saas-be/app/module/approval_workflow_steps/response"
|
||||||
res "netidhub-saas-be/app/module/approval_workflows/response"
|
res "netidhub-saas-be/app/module/approval_workflows/response"
|
||||||
usersRepository "netidhub-saas-be/app/module/users/repository"
|
usersRepository "netidhub-saas-be/app/module/users/repository"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ApprovalWorkflowsResponseMapper(
|
func ApprovalWorkflowsResponseMapper(
|
||||||
|
|
@ -39,6 +40,12 @@ func ApprovalWorkflowsResponseMapper(
|
||||||
isActive = *approvalWorkflowsReq.IsActive
|
isActive = *approvalWorkflowsReq.IsActive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert steps to interface{} slice
|
||||||
|
stepsInterface := make([]interface{}, len(workflowStepsArr))
|
||||||
|
for i, step := range workflowStepsArr {
|
||||||
|
stepsInterface[i] = step
|
||||||
|
}
|
||||||
|
|
||||||
approvalWorkflowsRes = &res.ApprovalWorkflowsResponse{
|
approvalWorkflowsRes = &res.ApprovalWorkflowsResponse{
|
||||||
ID: approvalWorkflowsReq.ID,
|
ID: approvalWorkflowsReq.ID,
|
||||||
Name: approvalWorkflowsReq.Name,
|
Name: approvalWorkflowsReq.Name,
|
||||||
|
|
@ -47,7 +54,7 @@ func ApprovalWorkflowsResponseMapper(
|
||||||
CreatedBy: 0, // Default value since entity doesn't have CreatedBy field
|
CreatedBy: 0, // Default value since entity doesn't have CreatedBy field
|
||||||
CreatedAt: approvalWorkflowsReq.CreatedAt,
|
CreatedAt: approvalWorkflowsReq.CreatedAt,
|
||||||
UpdatedAt: approvalWorkflowsReq.UpdatedAt,
|
UpdatedAt: approvalWorkflowsReq.UpdatedAt,
|
||||||
Steps: workflowStepsArr,
|
Steps: stepsInterface,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,6 +89,12 @@ func ApprovalWorkflowsWithStepsResponseMapper(
|
||||||
isActive = *approvalWorkflowsReq.IsActive
|
isActive = *approvalWorkflowsReq.IsActive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert steps to interface{} slice
|
||||||
|
stepsInterface := make([]interface{}, len(workflowStepsArr))
|
||||||
|
for i, step := range workflowStepsArr {
|
||||||
|
stepsInterface[i] = step
|
||||||
|
}
|
||||||
|
|
||||||
approvalWorkflowsRes = &res.ApprovalWorkflowsWithStepsResponse{
|
approvalWorkflowsRes = &res.ApprovalWorkflowsWithStepsResponse{
|
||||||
ID: approvalWorkflowsReq.ID,
|
ID: approvalWorkflowsReq.ID,
|
||||||
Name: approvalWorkflowsReq.Name,
|
Name: approvalWorkflowsReq.Name,
|
||||||
|
|
@ -90,7 +103,7 @@ func ApprovalWorkflowsWithStepsResponseMapper(
|
||||||
CreatedBy: 0, // Default value since entity doesn't have CreatedBy field
|
CreatedBy: 0, // Default value since entity doesn't have CreatedBy field
|
||||||
CreatedAt: approvalWorkflowsReq.CreatedAt,
|
CreatedAt: approvalWorkflowsReq.CreatedAt,
|
||||||
UpdatedAt: approvalWorkflowsReq.UpdatedAt,
|
UpdatedAt: approvalWorkflowsReq.UpdatedAt,
|
||||||
Steps: workflowStepsArr,
|
Steps: stepsInterface,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -299,3 +299,8 @@ func (req ApprovalWorkflowsQueryRequestContext) ToParamRequest() ApprovalWorkflo
|
||||||
IsDefault: isDefault,
|
IsDefault: isDefault,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ComprehensiveWorkflowDetailRequest - Request for getting comprehensive workflow details
|
||||||
|
type ComprehensiveWorkflowDetailRequest struct {
|
||||||
|
WorkflowId uint `json:"workflowId" validate:"required"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
package response
|
package response
|
||||||
|
|
||||||
import (
|
import "time"
|
||||||
approvalWorkflowStepsResponse "netidhub-saas-be/app/module/approval_workflow_steps/response"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
// ApprovalWorkflowsResponse - Basic workflow response
|
||||||
type ApprovalWorkflowsResponse struct {
|
type ApprovalWorkflowsResponse struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
@ -13,11 +11,10 @@ type ApprovalWorkflowsResponse struct {
|
||||||
CreatedBy uint `json:"createdBy"`
|
CreatedBy uint `json:"createdBy"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
Steps []interface{} `json:"steps"`
|
||||||
// Relations
|
|
||||||
Steps []*approvalWorkflowStepsResponse.ApprovalWorkflowStepsResponse `json:"steps,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApprovalWorkflowsWithStepsResponse - Workflow response with steps
|
||||||
type ApprovalWorkflowsWithStepsResponse struct {
|
type ApprovalWorkflowsWithStepsResponse struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
@ -26,11 +23,10 @@ type ApprovalWorkflowsWithStepsResponse struct {
|
||||||
CreatedBy uint `json:"createdBy"`
|
CreatedBy uint `json:"createdBy"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
Steps []interface{} `json:"steps"`
|
||||||
// Relations
|
|
||||||
Steps []*approvalWorkflowStepsResponse.ApprovalWorkflowStepsResponse `json:"steps"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ApprovalWorkflowsSummaryResponse - Summary workflow response
|
||||||
type ApprovalWorkflowsSummaryResponse struct {
|
type ApprovalWorkflowsSummaryResponse struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
@ -39,10 +35,133 @@ type ApprovalWorkflowsSummaryResponse struct {
|
||||||
StepCount int `json:"stepCount"`
|
StepCount int `json:"stepCount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApprovalWorkflowsStatsResponse struct {
|
// ComprehensiveWorkflowDetailResponse - Comprehensive response for workflow details
|
||||||
TotalWorkflows int `json:"totalWorkflows"`
|
type ComprehensiveWorkflowDetailResponse struct {
|
||||||
ActiveWorkflows int `json:"activeWorkflows"`
|
Workflow WorkflowDetailInfo `json:"workflow"`
|
||||||
InactiveWorkflows int `json:"inactiveWorkflows"`
|
Steps []WorkflowStepDetailInfo `json:"steps"`
|
||||||
TotalSteps int `json:"totalSteps"`
|
ClientSettings ClientApprovalSettingsDetail `json:"clientSettings"`
|
||||||
AverageStepsPerFlow int `json:"averageStepsPerFlow"`
|
RelatedData RelatedDataInfo `json:"relatedData"`
|
||||||
|
Statistics WorkflowStatisticsInfo `json:"statistics"`
|
||||||
|
LastUpdated time.Time `json:"lastUpdated"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WorkflowDetailInfo - Detailed workflow information
|
||||||
|
type WorkflowDetailInfo struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description *string `json:"description"`
|
||||||
|
IsDefault *bool `json:"isDefault"`
|
||||||
|
IsActive *bool `json:"isActive"`
|
||||||
|
RequiresApproval *bool `json:"requiresApproval"`
|
||||||
|
AutoPublish *bool `json:"autoPublish"`
|
||||||
|
ClientId *string `json:"clientId"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
|
||||||
|
// Additional workflow info
|
||||||
|
TotalSteps int `json:"totalSteps"`
|
||||||
|
ActiveSteps int `json:"activeSteps"`
|
||||||
|
HasBranches bool `json:"hasBranches"`
|
||||||
|
MaxStepOrder int `json:"maxStepOrder"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WorkflowStepDetailInfo - Detailed step information
|
||||||
|
type WorkflowStepDetailInfo struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
WorkflowId uint `json:"workflowId"`
|
||||||
|
StepOrder int `json:"stepOrder"`
|
||||||
|
StepName string `json:"stepName"`
|
||||||
|
RequiredUserLevelId uint `json:"requiredUserLevelId"`
|
||||||
|
RequiredUserLevelName string `json:"requiredUserLevelName"`
|
||||||
|
CanSkip *bool `json:"canSkip"`
|
||||||
|
AutoApproveAfterHours *int `json:"autoApproveAfterHours"`
|
||||||
|
IsActive *bool `json:"isActive"`
|
||||||
|
|
||||||
|
// Multi-branch support fields
|
||||||
|
ParentStepId *uint `json:"parentStepId"`
|
||||||
|
ParentStepName *string `json:"parentStepName"`
|
||||||
|
ConditionType *string `json:"conditionType"`
|
||||||
|
ConditionValue *string `json:"conditionValue"`
|
||||||
|
IsParallel *bool `json:"isParallel"`
|
||||||
|
BranchName *string `json:"branchName"`
|
||||||
|
BranchOrder *int `json:"branchOrder"`
|
||||||
|
|
||||||
|
// Additional step info
|
||||||
|
HasChildren bool `json:"hasChildren"`
|
||||||
|
IsFirstStep bool `json:"isFirstStep"`
|
||||||
|
IsLastStep bool `json:"isLastStep"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientApprovalSettingsDetail - Detailed client approval settings
|
||||||
|
type ClientApprovalSettingsDetail struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
ClientId string `json:"clientId"`
|
||||||
|
RequiresApproval *bool `json:"requiresApproval"`
|
||||||
|
DefaultWorkflowId *uint `json:"defaultWorkflowId"`
|
||||||
|
DefaultWorkflowName *string `json:"defaultWorkflowName"`
|
||||||
|
AutoPublishArticles *bool `json:"autoPublishArticles"`
|
||||||
|
ApprovalExemptUsers []uint `json:"approvalExemptUsers"`
|
||||||
|
ApprovalExemptRoles []uint `json:"approvalExemptRoles"`
|
||||||
|
ApprovalExemptCategories []uint `json:"approvalExemptCategories"`
|
||||||
|
RequireApprovalFor []string `json:"requireApprovalFor"`
|
||||||
|
SkipApprovalFor []string `json:"skipApprovalFor"`
|
||||||
|
IsActive *bool `json:"isActive"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
|
||||||
|
// Additional client settings info
|
||||||
|
ExemptUsersDetails []UserDetailInfo `json:"exemptUsersDetails"`
|
||||||
|
ExemptRolesDetails []UserRoleDetailInfo `json:"exemptRolesDetails"`
|
||||||
|
ExemptCategoriesDetails []CategoryDetailInfo `json:"exemptCategoriesDetails"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RelatedDataInfo - Related data information
|
||||||
|
type RelatedDataInfo struct {
|
||||||
|
UserLevels []UserLevelDetailInfo `json:"userLevels"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WorkflowStatisticsInfo - Workflow statistics
|
||||||
|
type WorkflowStatisticsInfo struct {
|
||||||
|
TotalArticlesProcessed int `json:"totalArticlesProcessed"`
|
||||||
|
PendingArticles int `json:"pendingArticles"`
|
||||||
|
ApprovedArticles int `json:"approvedArticles"`
|
||||||
|
RejectedArticles int `json:"rejectedArticles"`
|
||||||
|
AverageProcessingTime int `json:"averageProcessingTime"` // in hours
|
||||||
|
MostActiveStep string `json:"mostActiveStep"`
|
||||||
|
LastUsedAt *string `json:"lastUsedAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserLevelDetailInfo - User level detail information
|
||||||
|
type UserLevelDetailInfo struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
AliasName string `json:"aliasName"`
|
||||||
|
LevelNumber int `json:"levelNumber"`
|
||||||
|
IsApprovalActive *bool `json:"isApprovalActive"`
|
||||||
|
IsActive *bool `json:"isActive"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserDetailInfo - User detail information
|
||||||
|
type UserDetailInfo struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Fullname string `json:"fullname"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
UserLevelId uint `json:"userLevelId"`
|
||||||
|
UserLevelName string `json:"userLevelName"`
|
||||||
|
IsActive *bool `json:"isActive"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserRoleDetailInfo - User role detail information
|
||||||
|
type UserRoleDetailInfo struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
RoleName string `json:"roleName"`
|
||||||
|
IsActive *bool `json:"isActive"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CategoryDetailInfo - Category detail information
|
||||||
|
type CategoryDetailInfo struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
CategoryName string `json:"categoryName"`
|
||||||
|
IsActive *bool `json:"isActive"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,13 @@ import (
|
||||||
stepRepo "netidhub-saas-be/app/module/approval_workflow_steps/repository"
|
stepRepo "netidhub-saas-be/app/module/approval_workflow_steps/repository"
|
||||||
"netidhub-saas-be/app/module/approval_workflows/repository"
|
"netidhub-saas-be/app/module/approval_workflows/repository"
|
||||||
"netidhub-saas-be/app/module/approval_workflows/request"
|
"netidhub-saas-be/app/module/approval_workflows/request"
|
||||||
|
"netidhub-saas-be/app/module/approval_workflows/response"
|
||||||
|
articleApprovalFlowsRepo "netidhub-saas-be/app/module/article_approval_flows/repository"
|
||||||
|
articleCategoriesRepo "netidhub-saas-be/app/module/article_categories/repository"
|
||||||
clientApprovalSettingsRepo "netidhub-saas-be/app/module/client_approval_settings/repository"
|
clientApprovalSettingsRepo "netidhub-saas-be/app/module/client_approval_settings/repository"
|
||||||
|
userLevelsRepo "netidhub-saas-be/app/module/user_levels/repository"
|
||||||
|
userLevelsReq "netidhub-saas-be/app/module/user_levels/request"
|
||||||
|
userRolesRepo "netidhub-saas-be/app/module/user_roles/repository"
|
||||||
usersRepo "netidhub-saas-be/app/module/users/repository"
|
usersRepo "netidhub-saas-be/app/module/users/repository"
|
||||||
"netidhub-saas-be/utils/paginator"
|
"netidhub-saas-be/utils/paginator"
|
||||||
utilSvc "netidhub-saas-be/utils/service"
|
utilSvc "netidhub-saas-be/utils/service"
|
||||||
|
|
@ -22,6 +28,10 @@ type approvalWorkflowsService struct {
|
||||||
ApprovalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository
|
ApprovalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository
|
||||||
ClientApprovalSettingsRepository clientApprovalSettingsRepo.ClientApprovalSettingsRepository
|
ClientApprovalSettingsRepository clientApprovalSettingsRepo.ClientApprovalSettingsRepository
|
||||||
UsersRepository usersRepo.UsersRepository
|
UsersRepository usersRepo.UsersRepository
|
||||||
|
UserLevelsRepository userLevelsRepo.UserLevelsRepository
|
||||||
|
UserRolesRepository userRolesRepo.UserRolesRepository
|
||||||
|
ArticleCategoriesRepository articleCategoriesRepo.ArticleCategoriesRepository
|
||||||
|
ArticleApprovalFlowsRepository articleApprovalFlowsRepo.ArticleApprovalFlowsRepository
|
||||||
Log zerolog.Logger
|
Log zerolog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,6 +61,9 @@ type ApprovalWorkflowsService interface {
|
||||||
|
|
||||||
// Comprehensive workflow creation with client settings
|
// Comprehensive workflow creation with client settings
|
||||||
CreateWorkflowWithClientSettings(authToken string, req request.CreateApprovalWorkflowWithClientSettingsRequest) (workflow *entity.ApprovalWorkflows, clientSettings *entity.ClientApprovalSettings, err error)
|
CreateWorkflowWithClientSettings(authToken string, req request.CreateApprovalWorkflowWithClientSettingsRequest) (workflow *entity.ApprovalWorkflows, clientSettings *entity.ClientApprovalSettings, err error)
|
||||||
|
|
||||||
|
// Comprehensive workflow details
|
||||||
|
GetComprehensiveWorkflowDetails(authToken string, workflowId uint) (details *response.ComprehensiveWorkflowDetailResponse, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewApprovalWorkflowsService(
|
func NewApprovalWorkflowsService(
|
||||||
|
|
@ -58,6 +71,10 @@ func NewApprovalWorkflowsService(
|
||||||
approvalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository,
|
approvalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository,
|
||||||
clientApprovalSettingsRepository clientApprovalSettingsRepo.ClientApprovalSettingsRepository,
|
clientApprovalSettingsRepository clientApprovalSettingsRepo.ClientApprovalSettingsRepository,
|
||||||
usersRepository usersRepo.UsersRepository,
|
usersRepository usersRepo.UsersRepository,
|
||||||
|
userLevelsRepository userLevelsRepo.UserLevelsRepository,
|
||||||
|
userRolesRepository userRolesRepo.UserRolesRepository,
|
||||||
|
articleCategoriesRepository articleCategoriesRepo.ArticleCategoriesRepository,
|
||||||
|
articleApprovalFlowsRepository articleApprovalFlowsRepo.ArticleApprovalFlowsRepository,
|
||||||
log zerolog.Logger,
|
log zerolog.Logger,
|
||||||
) ApprovalWorkflowsService {
|
) ApprovalWorkflowsService {
|
||||||
return &approvalWorkflowsService{
|
return &approvalWorkflowsService{
|
||||||
|
|
@ -65,6 +82,10 @@ func NewApprovalWorkflowsService(
|
||||||
ApprovalWorkflowStepsRepository: approvalWorkflowStepsRepository,
|
ApprovalWorkflowStepsRepository: approvalWorkflowStepsRepository,
|
||||||
ClientApprovalSettingsRepository: clientApprovalSettingsRepository,
|
ClientApprovalSettingsRepository: clientApprovalSettingsRepository,
|
||||||
UsersRepository: usersRepository,
|
UsersRepository: usersRepository,
|
||||||
|
UserLevelsRepository: userLevelsRepository,
|
||||||
|
UserRolesRepository: userRolesRepository,
|
||||||
|
ArticleCategoriesRepository: articleCategoriesRepository,
|
||||||
|
ArticleApprovalFlowsRepository: articleApprovalFlowsRepository,
|
||||||
Log: log,
|
Log: log,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -713,3 +734,270 @@ func (_i *approvalWorkflowsService) CreateWorkflowWithClientSettings(authToken s
|
||||||
|
|
||||||
return workflow, clientSettings, nil
|
return workflow, clientSettings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetComprehensiveWorkflowDetails retrieves comprehensive workflow details including all related data
|
||||||
|
func (_i *approvalWorkflowsService) GetComprehensiveWorkflowDetails(authToken string, workflowId uint) (details *response.ComprehensiveWorkflowDetailResponse, err error) {
|
||||||
|
// Extract clientId from authToken
|
||||||
|
var clientId *uuid.UUID
|
||||||
|
if authToken != "" {
|
||||||
|
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepository, authToken)
|
||||||
|
if user != nil && user.ClientId != nil {
|
||||||
|
clientId = user.ClientId
|
||||||
|
_i.Log.Info().Interface("clientId", clientId).Msg("Extracted clientId from auth token")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if clientId == nil {
|
||||||
|
return nil, errors.New("clientId not found in auth token")
|
||||||
|
}
|
||||||
|
|
||||||
|
_i.Log.Info().
|
||||||
|
Uint("workflowId", workflowId).
|
||||||
|
Interface("clientId", clientId).
|
||||||
|
Msg("Getting comprehensive workflow details")
|
||||||
|
|
||||||
|
// Get workflow
|
||||||
|
workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, workflowId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get workflow: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if workflow == nil {
|
||||||
|
return nil, errors.New("workflow not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get workflow steps
|
||||||
|
steps, err := _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, workflowId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get workflow steps: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get client approval settings
|
||||||
|
clientSettings, err := _i.ClientApprovalSettingsRepository.FindByClientId(*clientId)
|
||||||
|
if err != nil {
|
||||||
|
_i.Log.Warn().Err(err).Msg("Failed to get client approval settings")
|
||||||
|
// Don't return error, just log warning
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build comprehensive response
|
||||||
|
details = &response.ComprehensiveWorkflowDetailResponse{
|
||||||
|
LastUpdated: workflow.UpdatedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build workflow detail info
|
||||||
|
workflowDetail := response.WorkflowDetailInfo{
|
||||||
|
ID: workflow.ID,
|
||||||
|
Name: workflow.Name,
|
||||||
|
Description: workflow.Description,
|
||||||
|
IsDefault: workflow.IsDefault,
|
||||||
|
IsActive: workflow.IsActive,
|
||||||
|
RequiresApproval: workflow.RequiresApproval,
|
||||||
|
AutoPublish: workflow.AutoPublish,
|
||||||
|
CreatedAt: workflow.CreatedAt,
|
||||||
|
UpdatedAt: workflow.UpdatedAt,
|
||||||
|
TotalSteps: len(steps),
|
||||||
|
MaxStepOrder: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
if workflow.ClientId != nil {
|
||||||
|
clientIdStr := workflow.ClientId.String()
|
||||||
|
workflowDetail.ClientId = &clientIdStr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate workflow statistics
|
||||||
|
activeSteps := 0
|
||||||
|
hasBranches := false
|
||||||
|
for _, step := range steps {
|
||||||
|
if step.IsActive != nil && *step.IsActive {
|
||||||
|
activeSteps++
|
||||||
|
}
|
||||||
|
if step.ParentStepId != nil {
|
||||||
|
hasBranches = true
|
||||||
|
}
|
||||||
|
if step.StepOrder > workflowDetail.MaxStepOrder {
|
||||||
|
workflowDetail.MaxStepOrder = step.StepOrder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
workflowDetail.ActiveSteps = activeSteps
|
||||||
|
workflowDetail.HasBranches = hasBranches
|
||||||
|
details.Workflow = workflowDetail
|
||||||
|
|
||||||
|
// Build step details
|
||||||
|
stepDetails := make([]response.WorkflowStepDetailInfo, len(steps))
|
||||||
|
for i, step := range steps {
|
||||||
|
stepDetail := response.WorkflowStepDetailInfo{
|
||||||
|
ID: step.ID,
|
||||||
|
WorkflowId: step.WorkflowId,
|
||||||
|
StepOrder: step.StepOrder,
|
||||||
|
StepName: step.StepName,
|
||||||
|
RequiredUserLevelId: step.RequiredUserLevelId,
|
||||||
|
CanSkip: step.CanSkip,
|
||||||
|
AutoApproveAfterHours: step.AutoApproveAfterHours,
|
||||||
|
IsActive: step.IsActive,
|
||||||
|
ParentStepId: step.ParentStepId,
|
||||||
|
ConditionType: step.ConditionType,
|
||||||
|
ConditionValue: step.ConditionValue,
|
||||||
|
IsParallel: step.IsParallel,
|
||||||
|
BranchName: step.BranchName,
|
||||||
|
BranchOrder: step.BranchOrder,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user level name
|
||||||
|
userLevel, err := _i.UserLevelsRepository.FindOne(clientId, step.RequiredUserLevelId)
|
||||||
|
if err == nil && userLevel != nil {
|
||||||
|
stepDetail.RequiredUserLevelName = userLevel.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get parent step name
|
||||||
|
if step.ParentStepId != nil {
|
||||||
|
for _, parentStep := range steps {
|
||||||
|
if parentStep.ID == *step.ParentStepId {
|
||||||
|
stepDetail.ParentStepName = &parentStep.StepName
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if step has children
|
||||||
|
hasChildren := false
|
||||||
|
for _, childStep := range steps {
|
||||||
|
if childStep.ParentStepId != nil && *childStep.ParentStepId == step.ID {
|
||||||
|
hasChildren = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stepDetail.HasChildren = hasChildren
|
||||||
|
|
||||||
|
// Check if first/last step
|
||||||
|
stepDetail.IsFirstStep = step.StepOrder == 1
|
||||||
|
stepDetail.IsLastStep = step.StepOrder == workflowDetail.MaxStepOrder
|
||||||
|
|
||||||
|
stepDetails[i] = stepDetail
|
||||||
|
}
|
||||||
|
|
||||||
|
details.Steps = stepDetails
|
||||||
|
|
||||||
|
// Build client settings detail
|
||||||
|
if clientSettings != nil {
|
||||||
|
clientSettingsDetail := response.ClientApprovalSettingsDetail{
|
||||||
|
ID: clientSettings.ID,
|
||||||
|
ClientId: clientSettings.ClientId.String(),
|
||||||
|
RequiresApproval: clientSettings.RequiresApproval,
|
||||||
|
DefaultWorkflowId: clientSettings.DefaultWorkflowId,
|
||||||
|
AutoPublishArticles: clientSettings.AutoPublishArticles,
|
||||||
|
ApprovalExemptUsers: clientSettings.ApprovalExemptUsers,
|
||||||
|
ApprovalExemptRoles: clientSettings.ApprovalExemptRoles,
|
||||||
|
ApprovalExemptCategories: clientSettings.ApprovalExemptCategories,
|
||||||
|
RequireApprovalFor: clientSettings.RequireApprovalFor,
|
||||||
|
SkipApprovalFor: clientSettings.SkipApprovalFor,
|
||||||
|
IsActive: clientSettings.IsActive,
|
||||||
|
CreatedAt: clientSettings.CreatedAt,
|
||||||
|
UpdatedAt: clientSettings.UpdatedAt,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set default workflow name
|
||||||
|
if clientSettings.DefaultWorkflowId != nil {
|
||||||
|
defaultWorkflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, *clientSettings.DefaultWorkflowId)
|
||||||
|
if err == nil && defaultWorkflow != nil {
|
||||||
|
clientSettingsDetail.DefaultWorkflowName = &defaultWorkflow.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get exempt users details
|
||||||
|
exemptUsersDetails := make([]response.UserDetailInfo, 0)
|
||||||
|
for _, userId := range clientSettings.ApprovalExemptUsers {
|
||||||
|
user, err := _i.UsersRepository.FindOne(clientId, userId)
|
||||||
|
if err == nil && user != nil {
|
||||||
|
userLevel, _ := _i.UserLevelsRepository.FindOne(clientId, user.UserLevelId)
|
||||||
|
userLevelName := ""
|
||||||
|
if userLevel != nil {
|
||||||
|
userLevelName = userLevel.Name
|
||||||
|
}
|
||||||
|
exemptUsersDetails = append(exemptUsersDetails, response.UserDetailInfo{
|
||||||
|
ID: user.ID,
|
||||||
|
Username: user.Username,
|
||||||
|
Fullname: user.Fullname,
|
||||||
|
Email: user.Email,
|
||||||
|
UserLevelId: user.UserLevelId,
|
||||||
|
UserLevelName: userLevelName,
|
||||||
|
IsActive: user.IsActive,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clientSettingsDetail.ExemptUsersDetails = exemptUsersDetails
|
||||||
|
|
||||||
|
// Get exempt roles details
|
||||||
|
exemptRolesDetails := make([]response.UserRoleDetailInfo, 0)
|
||||||
|
for _, roleId := range clientSettings.ApprovalExemptRoles {
|
||||||
|
role, err := _i.UserRolesRepository.FindOne(roleId)
|
||||||
|
if err == nil && role != nil {
|
||||||
|
exemptRolesDetails = append(exemptRolesDetails, response.UserRoleDetailInfo{
|
||||||
|
ID: role.ID,
|
||||||
|
RoleName: role.Name,
|
||||||
|
IsActive: role.IsActive,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clientSettingsDetail.ExemptRolesDetails = exemptRolesDetails
|
||||||
|
|
||||||
|
// Get exempt categories details
|
||||||
|
exemptCategoriesDetails := make([]response.CategoryDetailInfo, 0)
|
||||||
|
for _, categoryId := range clientSettings.ApprovalExemptCategories {
|
||||||
|
category, err := _i.ArticleCategoriesRepository.FindOne(clientId, categoryId)
|
||||||
|
if err == nil && category != nil {
|
||||||
|
exemptCategoriesDetails = append(exemptCategoriesDetails, response.CategoryDetailInfo{
|
||||||
|
ID: category.ID,
|
||||||
|
CategoryName: category.Title,
|
||||||
|
IsActive: category.IsActive,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clientSettingsDetail.ExemptCategoriesDetails = exemptCategoriesDetails
|
||||||
|
|
||||||
|
details.ClientSettings = clientSettingsDetail
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build related data
|
||||||
|
relatedData := response.RelatedDataInfo{}
|
||||||
|
|
||||||
|
// Get all user levels for this client
|
||||||
|
userLevels, _, err := _i.UserLevelsRepository.GetAll(clientId, userLevelsReq.UserLevelsQueryRequest{
|
||||||
|
Pagination: &paginator.Pagination{},
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
userLevelDetails := make([]response.UserLevelDetailInfo, len(userLevels))
|
||||||
|
for i, level := range userLevels {
|
||||||
|
userLevelDetails[i] = response.UserLevelDetailInfo{
|
||||||
|
ID: level.ID,
|
||||||
|
Name: level.Name,
|
||||||
|
AliasName: level.AliasName,
|
||||||
|
LevelNumber: level.LevelNumber,
|
||||||
|
IsApprovalActive: level.IsApprovalActive,
|
||||||
|
IsActive: level.IsActive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
relatedData.UserLevels = userLevelDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get workflow statistics (simplified - would need more complex queries in real implementation)
|
||||||
|
statistics := response.WorkflowStatisticsInfo{
|
||||||
|
TotalArticlesProcessed: 0, // Would need to query article_approval_flows
|
||||||
|
PendingArticles: 0, // Would need to query active flows
|
||||||
|
ApprovedArticles: 0, // Would need to query completed flows
|
||||||
|
RejectedArticles: 0, // Would need to query rejected flows
|
||||||
|
AverageProcessingTime: 0, // Would need to calculate from flow durations
|
||||||
|
MostActiveStep: "", // Would need to analyze step activity
|
||||||
|
LastUsedAt: nil, // Would need to query last flow creation
|
||||||
|
}
|
||||||
|
|
||||||
|
details.Statistics = statistics
|
||||||
|
details.RelatedData = relatedData
|
||||||
|
|
||||||
|
_i.Log.Info().
|
||||||
|
Uint("workflowId", workflowId).
|
||||||
|
Int("stepsCount", len(steps)).
|
||||||
|
Msg("Comprehensive workflow details retrieved successfully")
|
||||||
|
|
||||||
|
return details, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,456 @@
|
||||||
|
# Comprehensive Workflow Detail API
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
API endpoint untuk mendapatkan detail lengkap dari Approval Workflow beserta semua data terkait termasuk steps, client settings, dan informasi pendukung lainnya.
|
||||||
|
|
||||||
|
## Endpoint
|
||||||
|
```
|
||||||
|
POST /approval-workflows/comprehensive-details
|
||||||
|
```
|
||||||
|
|
||||||
|
## Request
|
||||||
|
|
||||||
|
### Headers
|
||||||
|
```
|
||||||
|
Authorization: Bearer <access_token>
|
||||||
|
Content-Type: application/json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Request Body
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"workflowId": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Request Structure
|
||||||
|
```go
|
||||||
|
type ComprehensiveWorkflowDetailRequest struct {
|
||||||
|
WorkflowId uint `json:"workflowId" validate:"required"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response
|
||||||
|
|
||||||
|
### Success Response (200)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"messages": ["Comprehensive workflow details retrieved successfully"],
|
||||||
|
"data": {
|
||||||
|
"workflow": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Standard Article Approval",
|
||||||
|
"description": "Standard approval workflow for articles",
|
||||||
|
"isDefault": true,
|
||||||
|
"isActive": true,
|
||||||
|
"requiresApproval": true,
|
||||||
|
"autoPublish": false,
|
||||||
|
"clientId": "123e4567-e89b-12d3-a456-426614174000",
|
||||||
|
"createdAt": "2024-01-01T00:00:00Z",
|
||||||
|
"updatedAt": "2024-01-01T00:00:00Z",
|
||||||
|
"totalSteps": 3,
|
||||||
|
"activeSteps": 3,
|
||||||
|
"hasBranches": false,
|
||||||
|
"maxStepOrder": 3
|
||||||
|
},
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"workflowId": 1,
|
||||||
|
"stepOrder": 1,
|
||||||
|
"stepName": "Content Review",
|
||||||
|
"requiredUserLevelId": 2,
|
||||||
|
"requiredUserLevelName": "Editor",
|
||||||
|
"canSkip": false,
|
||||||
|
"autoApproveAfterHours": null,
|
||||||
|
"isActive": true,
|
||||||
|
"parentStepId": null,
|
||||||
|
"parentStepName": null,
|
||||||
|
"conditionType": null,
|
||||||
|
"conditionValue": null,
|
||||||
|
"isParallel": false,
|
||||||
|
"branchName": null,
|
||||||
|
"branchOrder": null,
|
||||||
|
"hasChildren": false,
|
||||||
|
"isFirstStep": true,
|
||||||
|
"isLastStep": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"workflowId": 1,
|
||||||
|
"stepOrder": 2,
|
||||||
|
"stepName": "Final Approval",
|
||||||
|
"requiredUserLevelId": 3,
|
||||||
|
"requiredUserLevelName": "Manager",
|
||||||
|
"canSkip": false,
|
||||||
|
"autoApproveAfterHours": null,
|
||||||
|
"isActive": true,
|
||||||
|
"parentStepId": null,
|
||||||
|
"parentStepName": null,
|
||||||
|
"conditionType": null,
|
||||||
|
"conditionValue": null,
|
||||||
|
"isParallel": false,
|
||||||
|
"branchName": null,
|
||||||
|
"branchOrder": null,
|
||||||
|
"hasChildren": false,
|
||||||
|
"isFirstStep": false,
|
||||||
|
"isLastStep": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"clientSettings": {
|
||||||
|
"id": 1,
|
||||||
|
"clientId": "123e4567-e89b-12d3-a456-426614174000",
|
||||||
|
"requiresApproval": true,
|
||||||
|
"defaultWorkflowId": 1,
|
||||||
|
"defaultWorkflowName": "Standard Article Approval",
|
||||||
|
"autoPublishArticles": false,
|
||||||
|
"approvalExemptUsers": [1, 2],
|
||||||
|
"approvalExemptRoles": [1],
|
||||||
|
"approvalExemptCategories": [1, 2],
|
||||||
|
"requireApprovalFor": ["articles", "news"],
|
||||||
|
"skipApprovalFor": ["announcements"],
|
||||||
|
"isActive": true,
|
||||||
|
"createdAt": "2024-01-01T00:00:00Z",
|
||||||
|
"updatedAt": "2024-01-01T00:00:00Z",
|
||||||
|
"exemptUsersDetails": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"username": "admin",
|
||||||
|
"fullname": "Administrator",
|
||||||
|
"email": "admin@example.com",
|
||||||
|
"userLevelId": 1,
|
||||||
|
"userLevelName": "Super Admin",
|
||||||
|
"isActive": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exemptRolesDetails": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"roleName": "Super Admin",
|
||||||
|
"isActive": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"exemptCategoriesDetails": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"categoryName": "Announcements",
|
||||||
|
"isActive": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"relatedData": {
|
||||||
|
"userLevels": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Super Admin",
|
||||||
|
"aliasName": "super_admin",
|
||||||
|
"levelNumber": 1,
|
||||||
|
"isApprovalActive": true,
|
||||||
|
"isActive": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Editor",
|
||||||
|
"aliasName": "editor",
|
||||||
|
"levelNumber": 2,
|
||||||
|
"isApprovalActive": true,
|
||||||
|
"isActive": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"statistics": {
|
||||||
|
"totalArticlesProcessed": 0,
|
||||||
|
"pendingArticles": 0,
|
||||||
|
"approvedArticles": 0,
|
||||||
|
"rejectedArticles": 0,
|
||||||
|
"averageProcessingTime": 0,
|
||||||
|
"mostActiveStep": "",
|
||||||
|
"lastUsedAt": null
|
||||||
|
},
|
||||||
|
"lastUpdated": "2024-01-01T00:00:00Z"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response Structure
|
||||||
|
|
||||||
|
### ComprehensiveWorkflowDetailResponse
|
||||||
|
```go
|
||||||
|
type ComprehensiveWorkflowDetailResponse struct {
|
||||||
|
Workflow WorkflowDetailInfo `json:"workflow"`
|
||||||
|
Steps []WorkflowStepDetailInfo `json:"steps"`
|
||||||
|
ClientSettings ClientApprovalSettingsDetail `json:"clientSettings"`
|
||||||
|
RelatedData RelatedDataInfo `json:"relatedData"`
|
||||||
|
Statistics WorkflowStatisticsInfo `json:"statistics"`
|
||||||
|
LastUpdated time.Time `json:"lastUpdated"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### WorkflowDetailInfo
|
||||||
|
```go
|
||||||
|
type WorkflowDetailInfo struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description *string `json:"description"`
|
||||||
|
IsDefault *bool `json:"isDefault"`
|
||||||
|
IsActive *bool `json:"isActive"`
|
||||||
|
RequiresApproval *bool `json:"requiresApproval"`
|
||||||
|
AutoPublish *bool `json:"autoPublish"`
|
||||||
|
ClientId *string `json:"clientId"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
|
||||||
|
// Additional workflow info
|
||||||
|
TotalSteps int `json:"totalSteps"`
|
||||||
|
ActiveSteps int `json:"activeSteps"`
|
||||||
|
HasBranches bool `json:"hasBranches"`
|
||||||
|
MaxStepOrder int `json:"maxStepOrder"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### WorkflowStepDetailInfo
|
||||||
|
```go
|
||||||
|
type WorkflowStepDetailInfo struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
WorkflowId uint `json:"workflowId"`
|
||||||
|
StepOrder int `json:"stepOrder"`
|
||||||
|
StepName string `json:"stepName"`
|
||||||
|
RequiredUserLevelId uint `json:"requiredUserLevelId"`
|
||||||
|
RequiredUserLevelName string `json:"requiredUserLevelName"`
|
||||||
|
CanSkip *bool `json:"canSkip"`
|
||||||
|
AutoApproveAfterHours *int `json:"autoApproveAfterHours"`
|
||||||
|
IsActive *bool `json:"isActive"`
|
||||||
|
|
||||||
|
// Multi-branch support fields
|
||||||
|
ParentStepId *uint `json:"parentStepId"`
|
||||||
|
ParentStepName *string `json:"parentStepName"`
|
||||||
|
ConditionType *string `json:"conditionType"`
|
||||||
|
ConditionValue *string `json:"conditionValue"`
|
||||||
|
IsParallel *bool `json:"isParallel"`
|
||||||
|
BranchName *string `json:"branchName"`
|
||||||
|
BranchOrder *int `json:"branchOrder"`
|
||||||
|
|
||||||
|
// Additional step info
|
||||||
|
HasChildren bool `json:"hasChildren"`
|
||||||
|
IsFirstStep bool `json:"isFirstStep"`
|
||||||
|
IsLastStep bool `json:"isLastStep"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### ClientApprovalSettingsDetail
|
||||||
|
```go
|
||||||
|
type ClientApprovalSettingsDetail struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
ClientId string `json:"clientId"`
|
||||||
|
RequiresApproval *bool `json:"requiresApproval"`
|
||||||
|
DefaultWorkflowId *uint `json:"defaultWorkflowId"`
|
||||||
|
DefaultWorkflowName *string `json:"defaultWorkflowName"`
|
||||||
|
AutoPublishArticles *bool `json:"autoPublishArticles"`
|
||||||
|
ApprovalExemptUsers []uint `json:"approvalExemptUsers"`
|
||||||
|
ApprovalExemptRoles []uint `json:"approvalExemptRoles"`
|
||||||
|
ApprovalExemptCategories []uint `json:"approvalExemptCategories"`
|
||||||
|
RequireApprovalFor []string `json:"requireApprovalFor"`
|
||||||
|
SkipApprovalFor []string `json:"skipApprovalFor"`
|
||||||
|
IsActive *bool `json:"isActive"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
|
||||||
|
// Detailed exempt information
|
||||||
|
ExemptUsersDetails []UserDetailInfo `json:"exemptUsersDetails"`
|
||||||
|
ExemptRolesDetails []UserRoleDetailInfo `json:"exemptRolesDetails"`
|
||||||
|
ExemptCategoriesDetails []CategoryDetailInfo `json:"exemptCategoriesDetails"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### RelatedDataInfo
|
||||||
|
```go
|
||||||
|
type RelatedDataInfo struct {
|
||||||
|
UserLevels []UserLevelDetailInfo `json:"userLevels"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### WorkflowStatisticsInfo
|
||||||
|
```go
|
||||||
|
type WorkflowStatisticsInfo struct {
|
||||||
|
TotalArticlesProcessed int `json:"totalArticlesProcessed"`
|
||||||
|
PendingArticles int `json:"pendingArticles"`
|
||||||
|
ApprovedArticles int `json:"approvedArticles"`
|
||||||
|
RejectedArticles int `json:"rejectedArticles"`
|
||||||
|
AverageProcessingTime int `json:"averageProcessingTime"`
|
||||||
|
MostActiveStep string `json:"mostActiveStep"`
|
||||||
|
LastUsedAt *string `json:"lastUsedAt"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Responses
|
||||||
|
|
||||||
|
### 400 Bad Request
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"messages": ["Validation failed: workflowId is required"],
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 401 Unauthorized
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"messages": ["Unauthorized access"],
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 404 Not Found
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"messages": ["Workflow not found"],
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 500 Internal Server Error
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"messages": ["Internal server error"],
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### 1. Comprehensive Workflow Information
|
||||||
|
- Detail lengkap workflow termasuk metadata dan statistik
|
||||||
|
- Informasi step dengan hierarki dan kondisi
|
||||||
|
- Status aktif/inaktif dan konfigurasi approval
|
||||||
|
|
||||||
|
### 2. Detailed Step Information
|
||||||
|
- Informasi lengkap setiap step dalam workflow
|
||||||
|
- Nama user level yang diperlukan untuk approval
|
||||||
|
- Konfigurasi skip, auto-approve, dan kondisi
|
||||||
|
- Dukungan multi-branch workflow
|
||||||
|
|
||||||
|
### 3. Client Settings Integration
|
||||||
|
- Pengaturan approval global untuk client
|
||||||
|
- Daftar user, role, dan kategori yang dikecualikan
|
||||||
|
- Informasi detail untuk setiap exemption
|
||||||
|
- Workflow default yang digunakan
|
||||||
|
|
||||||
|
### 4. Related Data Support
|
||||||
|
- Daftar user level yang tersedia
|
||||||
|
- Data pendukung untuk konfigurasi workflow
|
||||||
|
- Informasi terkait untuk referensi
|
||||||
|
|
||||||
|
### 5. Statistics Information
|
||||||
|
- Statistik penggunaan workflow (placeholder untuk implementasi future)
|
||||||
|
- Informasi performa dan aktivitas
|
||||||
|
- Data untuk monitoring dan analisis
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
|
||||||
|
### 1. Workflow Management Dashboard
|
||||||
|
- Menampilkan detail lengkap workflow untuk administrator
|
||||||
|
- Monitoring status dan konfigurasi workflow
|
||||||
|
- Analisis performa dan penggunaan
|
||||||
|
|
||||||
|
### 2. Workflow Configuration
|
||||||
|
- Mengedit workflow dengan informasi lengkap
|
||||||
|
- Mengatur step dan kondisi approval
|
||||||
|
- Konfigurasi exemption dan pengaturan client
|
||||||
|
|
||||||
|
### 3. Approval Process Monitoring
|
||||||
|
- Melihat status artikel dalam proses approval
|
||||||
|
- Tracking progress melalui step workflow
|
||||||
|
- Monitoring bottleneck dan performa
|
||||||
|
|
||||||
|
### 4. Client Settings Management
|
||||||
|
- Mengatur pengaturan approval global
|
||||||
|
- Mengelola exemption untuk user dan kategori
|
||||||
|
- Konfigurasi workflow default
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
### 1. Performance Considerations
|
||||||
|
- API menggunakan single query dengan join untuk efisiensi
|
||||||
|
- Data related diambil secara terpisah untuk fleksibilitas
|
||||||
|
- Caching dapat diimplementasikan untuk data yang jarang berubah
|
||||||
|
|
||||||
|
### 2. Security
|
||||||
|
- Menggunakan authentication token untuk akses
|
||||||
|
- Validasi clientId dari token untuk multi-tenant
|
||||||
|
- Authorization check untuk akses workflow
|
||||||
|
|
||||||
|
### 3. Error Handling
|
||||||
|
- Graceful handling untuk data yang tidak ditemukan
|
||||||
|
- Logging untuk debugging dan monitoring
|
||||||
|
- Rollback mechanism untuk operasi yang gagal
|
||||||
|
|
||||||
|
### 4. Extensibility
|
||||||
|
- Struktur response dapat diperluas untuk kebutuhan future
|
||||||
|
- Statistics dapat diimplementasikan dengan query yang lebih kompleks
|
||||||
|
- Support untuk workflow yang lebih kompleks
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
### Required Repositories
|
||||||
|
- `ApprovalWorkflowsRepository`
|
||||||
|
- `ApprovalWorkflowStepsRepository`
|
||||||
|
- `ClientApprovalSettingsRepository`
|
||||||
|
- `UsersRepository`
|
||||||
|
- `UserLevelsRepository`
|
||||||
|
- `UserRolesRepository`
|
||||||
|
- `ArticleCategoriesRepository`
|
||||||
|
- `ArticleApprovalFlowsRepository`
|
||||||
|
|
||||||
|
### Required Services
|
||||||
|
- `ApprovalWorkflowsService`
|
||||||
|
- `ClientApprovalSettingsService`
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
```go
|
||||||
|
func TestGetComprehensiveWorkflowDetails(t *testing.T) {
|
||||||
|
// Test cases for comprehensive workflow details
|
||||||
|
// Test with valid workflow ID
|
||||||
|
// Test with invalid workflow ID
|
||||||
|
// Test with missing client settings
|
||||||
|
// Test with empty steps
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
```go
|
||||||
|
func TestComprehensiveWorkflowDetailsAPI(t *testing.T) {
|
||||||
|
// Test API endpoint
|
||||||
|
// Test authentication
|
||||||
|
// Test response structure
|
||||||
|
// Test error handling
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
### 1. Real-time Statistics
|
||||||
|
- Implementasi query untuk statistik real-time
|
||||||
|
- Monitoring performa workflow
|
||||||
|
- Analytics dan reporting
|
||||||
|
|
||||||
|
### 2. Workflow Templates
|
||||||
|
- Template workflow yang dapat digunakan kembali
|
||||||
|
- Import/export workflow configuration
|
||||||
|
- Workflow marketplace
|
||||||
|
|
||||||
|
### 3. Advanced Branching
|
||||||
|
- Conditional workflow berdasarkan konten
|
||||||
|
- Dynamic step generation
|
||||||
|
- AI-powered workflow optimization
|
||||||
|
|
||||||
|
### 4. Integration Features
|
||||||
|
- Webhook untuk notifikasi
|
||||||
|
- API untuk external system integration
|
||||||
|
- Workflow automation triggers
|
||||||
|
|
@ -0,0 +1,412 @@
|
||||||
|
# Frontend Form Generation Prompt
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Generate comprehensive React/TypeScript forms for two main functionalities based on backend API specifications:
|
||||||
|
|
||||||
|
1. **Approval Workflow Settings Form** (API: `/approval-workflows/with-client-settings`)
|
||||||
|
2. **User Levels Management Form** (API: `/user-levels`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. APPROVAL WORKFLOW SETTINGS FORM
|
||||||
|
|
||||||
|
### API Endpoint
|
||||||
|
```
|
||||||
|
POST /approval-workflows/with-client-settings
|
||||||
|
```
|
||||||
|
|
||||||
|
### Request Structure
|
||||||
|
```typescript
|
||||||
|
interface CreateApprovalWorkflowWithClientSettingsRequest {
|
||||||
|
// Workflow details
|
||||||
|
name: string; // required
|
||||||
|
description: string; // required
|
||||||
|
isActive?: boolean;
|
||||||
|
isDefault?: boolean;
|
||||||
|
requiresApproval?: boolean;
|
||||||
|
autoPublish?: boolean;
|
||||||
|
steps: ApprovalWorkflowStepRequest[]; // required, min 1
|
||||||
|
|
||||||
|
// Client approval settings
|
||||||
|
clientApprovalSettings: ClientApprovalSettingsRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApprovalWorkflowStepRequest {
|
||||||
|
stepOrder: number; // required
|
||||||
|
stepName: string; // required
|
||||||
|
requiredUserLevelId: number; // required
|
||||||
|
canSkip?: boolean;
|
||||||
|
autoApproveAfterHours?: number;
|
||||||
|
isActive?: boolean;
|
||||||
|
|
||||||
|
// Multi-branch support fields
|
||||||
|
parentStepId?: number;
|
||||||
|
conditionType?: string; // 'user_level', 'user_level_hierarchy', 'always', 'custom'
|
||||||
|
conditionValue?: string; // JSON string for conditions
|
||||||
|
isParallel?: boolean;
|
||||||
|
branchName?: string;
|
||||||
|
branchOrder?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ClientApprovalSettingsRequest {
|
||||||
|
requiresApproval?: boolean;
|
||||||
|
autoPublishArticles?: boolean;
|
||||||
|
approvalExemptUsers: number[];
|
||||||
|
approvalExemptRoles: number[];
|
||||||
|
approvalExemptCategories: number[];
|
||||||
|
requireApprovalFor: string[];
|
||||||
|
skipApprovalFor: string[];
|
||||||
|
isActive?: boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### CURL Request
|
||||||
|
```
|
||||||
|
curl -X 'POST' \
|
||||||
|
'https://kontenhumas.com/api/approval-workflows/with-client-settings' \
|
||||||
|
-H 'accept: application/json' \
|
||||||
|
-H 'Authorization: Bearer eyJhbGciOiJSU....' \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{
|
||||||
|
"autoPublish": true,
|
||||||
|
"clientApprovalSettings": {
|
||||||
|
"approvalExemptCategories": [],
|
||||||
|
"approvalExemptRoles": [],
|
||||||
|
"approvalExemptUsers": [],
|
||||||
|
"autoPublishArticles": true,
|
||||||
|
"isActive": true,
|
||||||
|
"requireApprovalFor": [],
|
||||||
|
"requiresApproval": true,
|
||||||
|
"skipApprovalFor": []
|
||||||
|
},
|
||||||
|
"description": "Test",
|
||||||
|
"isActive": true,
|
||||||
|
"isDefault": true,
|
||||||
|
"name": "Multi-Branch Jurnalis Approval",
|
||||||
|
"requiresApproval": true,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"stepOrder": 1,
|
||||||
|
"stepName": "Single Approval",
|
||||||
|
"requiredUserLevelId": 12,
|
||||||
|
"conditionType": "user_level_hierarchy",
|
||||||
|
"conditionValue": "{\"applies_to_levels\": [13]}",
|
||||||
|
"branchName": "Single Approval"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Form Requirements
|
||||||
|
|
||||||
|
#### **Section 1: Workflow Basic Information**
|
||||||
|
- **Workflow Name** (Text Input, Required)
|
||||||
|
- Label: "Workflow Name"
|
||||||
|
- Placeholder: "e.g., Standard Article Approval"
|
||||||
|
- Validation: Required, min 3 characters
|
||||||
|
|
||||||
|
- **Description** (Textarea, Required)
|
||||||
|
- Label: "Workflow Description"
|
||||||
|
- Placeholder: "Describe the purpose and process of this workflow"
|
||||||
|
- Validation: Required, min 10 characters
|
||||||
|
|
||||||
|
- **Workflow Settings** (Checkbox Group)
|
||||||
|
- ☐ Is Active (default: true)
|
||||||
|
- ☐ Set as Default Workflow
|
||||||
|
- ☐ Requires Approval (default: true)
|
||||||
|
- ☐ Auto Publish After Approval (default: false)
|
||||||
|
|
||||||
|
#### **Section 2: Workflow Steps** (Dynamic Array)
|
||||||
|
- **Add Step Button** - Allow adding multiple steps
|
||||||
|
- **Step Configuration** (for each step):
|
||||||
|
- **Step Order** (Number Input, Required)
|
||||||
|
- Label: "Step Order"
|
||||||
|
- Default: Auto-increment (1, 2, 3...)
|
||||||
|
- Validation: Required, unique within workflow
|
||||||
|
|
||||||
|
- **Step Name** (Text Input, Required)
|
||||||
|
- Label: "Step Name"
|
||||||
|
- Placeholder: "e.g., Editor Review, Manager Approval"
|
||||||
|
- Validation: Required, min 3 characters
|
||||||
|
|
||||||
|
- **Required User Level** (Select Dropdown, Required)
|
||||||
|
- Label: "Required User Level"
|
||||||
|
- Options: Fetch from `/user-levels` API
|
||||||
|
- Display: `{name} (Level {levelNumber})`
|
||||||
|
- Validation: Required
|
||||||
|
|
||||||
|
- **Step Settings** (Checkbox Group)
|
||||||
|
- ☐ Can Skip This Step
|
||||||
|
- ☐ Is Active (default: true)
|
||||||
|
|
||||||
|
- **Auto Approval** (Number Input, Optional)
|
||||||
|
- Label: "Auto Approve After Hours"
|
||||||
|
- Placeholder: "Leave empty for manual approval"
|
||||||
|
- Help Text: "Automatically approve after specified hours"
|
||||||
|
|
||||||
|
- **Advanced Settings** (Collapsible Section)
|
||||||
|
- **Parent Step** (Select Dropdown, Optional)
|
||||||
|
- Label: "Parent Step (for branching)"
|
||||||
|
- Options: Previous steps in workflow
|
||||||
|
- Help Text: "Select parent step for parallel branches"
|
||||||
|
|
||||||
|
- **Condition Type** (Select Dropdown, Optional)
|
||||||
|
- Label: "Condition Type"
|
||||||
|
- Options: ['always', 'user_level', 'user_level_hierarchy', 'custom']
|
||||||
|
- Default: 'always'
|
||||||
|
|
||||||
|
- **Condition Value** (Text Input, Optional)
|
||||||
|
- Label: "Condition Value (JSON)"
|
||||||
|
- Placeholder: '{"minLevel": 3}'
|
||||||
|
- Help Text: "JSON string for custom conditions"
|
||||||
|
|
||||||
|
- **Branch Settings** (Checkbox Group)
|
||||||
|
- ☐ Is Parallel Step
|
||||||
|
- **Branch Name** (Text Input, Optional)
|
||||||
|
- Label: "Branch Name"
|
||||||
|
- Placeholder: "e.g., technical, content"
|
||||||
|
- **Branch Order** (Number Input, Optional)
|
||||||
|
- Label: "Branch Order"
|
||||||
|
|
||||||
|
- **Step Actions**
|
||||||
|
- Move Up/Down buttons
|
||||||
|
- Delete Step button
|
||||||
|
- Duplicate Step button
|
||||||
|
|
||||||
|
#### **Section 3: Client Approval Settings**
|
||||||
|
- **Global Approval Settings** (Checkbox Group)
|
||||||
|
- ☐ Requires Approval (default: true)
|
||||||
|
- ☐ Auto Publish Articles (default: false)
|
||||||
|
- ☐ Is Active (default: true)
|
||||||
|
|
||||||
|
- **Exemptions** (Multi-select sections)
|
||||||
|
- **Exempt Users** (Multi-select)
|
||||||
|
- Label: "Users Exempt from Approval"
|
||||||
|
- Options: Fetch from `/users` API
|
||||||
|
- Display: `{fullname} ({username})`
|
||||||
|
- Searchable: Yes
|
||||||
|
|
||||||
|
- **Exempt Roles** (Multi-select)
|
||||||
|
- Label: "Roles Exempt from Approval"
|
||||||
|
- Options: Fetch from `/user-roles` API
|
||||||
|
- Display: `{roleName}`
|
||||||
|
- Searchable: Yes
|
||||||
|
|
||||||
|
- **Exempt Categories** (Multi-select)
|
||||||
|
- Label: "Categories Exempt from Approval"
|
||||||
|
- Options: Fetch from `/article-categories` API
|
||||||
|
- Display: `{categoryName}`
|
||||||
|
- Searchable: Yes
|
||||||
|
|
||||||
|
- **Content Type Rules** (Dynamic Arrays)
|
||||||
|
- **Require Approval For** (Tag Input)
|
||||||
|
- Label: "Content Types Requiring Approval"
|
||||||
|
- Placeholder: "e.g., news, article, blog"
|
||||||
|
- Help Text: "Press Enter to add content type"
|
||||||
|
|
||||||
|
- **Skip Approval For** (Tag Input)
|
||||||
|
- Label: "Content Types Skipping Approval"
|
||||||
|
- Placeholder: "e.g., announcement, update"
|
||||||
|
- Help Text: "Press Enter to add content type"
|
||||||
|
|
||||||
|
### Form Features
|
||||||
|
- **Real-time Validation**
|
||||||
|
- **Step-by-step Wizard** (Optional)
|
||||||
|
- **Preview Mode** - Show workflow visualization
|
||||||
|
- **Save as Draft** functionality
|
||||||
|
- **Form Reset** with confirmation
|
||||||
|
- **Auto-save** every 30 seconds
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. USER LEVELS MANAGEMENT FORM
|
||||||
|
|
||||||
|
### API Endpoint
|
||||||
|
```
|
||||||
|
POST /user-levels
|
||||||
|
```
|
||||||
|
|
||||||
|
### Request Structure
|
||||||
|
```typescript
|
||||||
|
interface UserLevelsCreateRequest {
|
||||||
|
name: string; // required
|
||||||
|
aliasName: string; // required
|
||||||
|
levelNumber: number; // required
|
||||||
|
parentLevelId?: number;
|
||||||
|
provinceId?: number;
|
||||||
|
group?: string;
|
||||||
|
isApprovalActive?: boolean;
|
||||||
|
isActive?: boolean;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Form Requirements
|
||||||
|
|
||||||
|
#### **Section 1: Basic Information**
|
||||||
|
- **Level Name** (Text Input, Required)
|
||||||
|
- Label: "Level Name"
|
||||||
|
- Placeholder: "e.g., Senior Editor, Manager, Publisher"
|
||||||
|
- Validation: Required, min 3 characters, unique
|
||||||
|
|
||||||
|
- **Alias Name** (Text Input, Required)
|
||||||
|
- Label: "Alias Name"
|
||||||
|
- Placeholder: "e.g., SENIOR_EDITOR, MANAGER, PUBLISHER"
|
||||||
|
- Validation: Required, uppercase, unique
|
||||||
|
- Help Text: "Short identifier for system use"
|
||||||
|
|
||||||
|
- **Level Number** (Number Input, Required)
|
||||||
|
- Label: "Level Number"
|
||||||
|
- Placeholder: "e.g., 1, 2, 3"
|
||||||
|
- Validation: Required, unique, positive integer
|
||||||
|
- Help Text: "Higher number = higher authority"
|
||||||
|
|
||||||
|
#### **Section 2: Hierarchy Settings**
|
||||||
|
- **Parent Level** (Select Dropdown, Optional)
|
||||||
|
- Label: "Parent Level"
|
||||||
|
- Options: Fetch from `/user-levels` API
|
||||||
|
- Display: `{name} (Level {levelNumber})`
|
||||||
|
- Help Text: "Select parent level for hierarchy"
|
||||||
|
|
||||||
|
- **Province** (Select Dropdown, Optional)
|
||||||
|
- Label: "Province"
|
||||||
|
- Options: Fetch from `/provinces` API
|
||||||
|
- Display: `{provinceName}`
|
||||||
|
- Help Text: "Geographic scope for this level"
|
||||||
|
|
||||||
|
- **Group** (Text Input, Optional)
|
||||||
|
- Label: "Group"
|
||||||
|
- Placeholder: "e.g., Editorial, Management, Technical"
|
||||||
|
- Help Text: "Group classification for organization"
|
||||||
|
|
||||||
|
#### **Section 3: Approval Settings**
|
||||||
|
- **Approval Settings** (Checkbox Group)
|
||||||
|
- ☐ Is Approval Active (default: true)
|
||||||
|
- Help Text: "Users with this level can participate in approval process"
|
||||||
|
- ☐ Is Active (default: true)
|
||||||
|
- Help Text: "Level is available for assignment"
|
||||||
|
|
||||||
|
### Form Features
|
||||||
|
- **Level Number Validation** - Prevent duplicate level numbers
|
||||||
|
- **Hierarchy Visualization** - Show level hierarchy tree
|
||||||
|
- **Bulk Operations** - Create multiple levels at once
|
||||||
|
- **Import/Export** - CSV import/export functionality
|
||||||
|
- **Level Preview** - Show how level fits in hierarchy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. COMMON FORM COMPONENTS
|
||||||
|
|
||||||
|
### **Form Layout**
|
||||||
|
- **Responsive Design** - Mobile-first approach
|
||||||
|
- **Multi-step Wizard** - For complex forms
|
||||||
|
- **Tabbed Interface** - For different sections
|
||||||
|
- **Collapsible Sections** - For advanced settings
|
||||||
|
|
||||||
|
### **Validation**
|
||||||
|
- **Real-time Validation** - Show errors as user types
|
||||||
|
- **Cross-field Validation** - Validate relationships between fields
|
||||||
|
- **Server-side Validation** - Handle API validation errors
|
||||||
|
- **Custom Validation Rules** - Business logic validation
|
||||||
|
|
||||||
|
### **User Experience**
|
||||||
|
- **Auto-complete** - For select fields
|
||||||
|
- **Search Functionality** - For large option lists
|
||||||
|
- **Drag & Drop** - For reordering steps/items
|
||||||
|
- **Keyboard Navigation** - Full keyboard support
|
||||||
|
- **Accessibility** - ARIA labels, screen reader support
|
||||||
|
|
||||||
|
### **Data Management**
|
||||||
|
- **Form State Management** - Redux/Zustand
|
||||||
|
- **Local Storage** - Save draft forms
|
||||||
|
- **API Integration** - Axios/Fetch with error handling
|
||||||
|
- **Loading States** - Spinners, skeleton screens
|
||||||
|
- **Success/Error Messages** - Toast notifications
|
||||||
|
|
||||||
|
### **Styling Requirements**
|
||||||
|
- **Design System** - Consistent with existing app
|
||||||
|
- **Dark/Light Mode** - Theme support
|
||||||
|
- **Custom Components** - Reusable form components
|
||||||
|
- **Animation** - Smooth transitions
|
||||||
|
- **Icons** - Meaningful icons for actions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. IMPLEMENTATION NOTES
|
||||||
|
|
||||||
|
### **API Integration**
|
||||||
|
```typescript
|
||||||
|
// Example API calls
|
||||||
|
const createWorkflow = async (data: CreateApprovalWorkflowWithClientSettingsRequest) => {
|
||||||
|
const response = await fetch('/api/approval-workflows/with-client-settings', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
});
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
const createUserLevel = async (data: UserLevelsCreateRequest) => {
|
||||||
|
const response = await fetch('/api/user-levels', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
});
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### **Form Libraries**
|
||||||
|
- **React Hook Form** - For form management
|
||||||
|
- **Yup/Zod** - For validation
|
||||||
|
- **React Select** - For advanced select components
|
||||||
|
- **React DnD** - For drag and drop
|
||||||
|
- **React Query** - For API state management
|
||||||
|
|
||||||
|
### **Component Structure**
|
||||||
|
```
|
||||||
|
components/
|
||||||
|
├── forms/
|
||||||
|
│ ├── ApprovalWorkflowForm/
|
||||||
|
│ │ ├── index.tsx
|
||||||
|
│ │ ├── WorkflowBasicInfo.tsx
|
||||||
|
│ │ ├── WorkflowSteps.tsx
|
||||||
|
│ │ ├── ClientSettings.tsx
|
||||||
|
│ │ └── StepForm.tsx
|
||||||
|
│ └── UserLevelsForm/
|
||||||
|
│ ├── index.tsx
|
||||||
|
│ ├── BasicInfo.tsx
|
||||||
|
│ ├── HierarchySettings.tsx
|
||||||
|
│ └── ApprovalSettings.tsx
|
||||||
|
├── common/
|
||||||
|
│ ├── FormField.tsx
|
||||||
|
│ ├── DynamicArray.tsx
|
||||||
|
│ ├── MultiSelect.tsx
|
||||||
|
│ └── ValidationMessage.tsx
|
||||||
|
```
|
||||||
|
|
||||||
|
### **State Management**
|
||||||
|
```typescript
|
||||||
|
// Form state structure
|
||||||
|
interface FormState {
|
||||||
|
workflowForm: {
|
||||||
|
data: CreateApprovalWorkflowWithClientSettingsRequest;
|
||||||
|
errors: Record<string, string>;
|
||||||
|
isValid: boolean;
|
||||||
|
isSubmitting: boolean;
|
||||||
|
};
|
||||||
|
userLevelsForm: {
|
||||||
|
data: UserLevelsCreateRequest;
|
||||||
|
errors: Record<string, string>;
|
||||||
|
isValid: boolean;
|
||||||
|
isSubmitting: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This prompt provides comprehensive specifications for generating both forms with all necessary fields, validation rules, user experience considerations, and implementation details based on the backend API structures.
|
||||||
|
|
@ -1706,6 +1706,64 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/approval-workflows/comprehensive-details": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "API for getting comprehensive details of approval workflow including steps, client settings, and related data",
|
||||||
|
"tags": [
|
||||||
|
"ApprovalWorkflows"
|
||||||
|
],
|
||||||
|
"summary": "Get comprehensive approval workflow details",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Insert the Authorization",
|
||||||
|
"name": "Authorization",
|
||||||
|
"in": "header",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Workflow detail request",
|
||||||
|
"name": "req",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.ComprehensiveWorkflowDetailRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.BadRequestError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.UnauthorizedError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.InternalServerError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/approval-workflows/default": {
|
"/approval-workflows/default": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
|
@ -17344,6 +17402,17 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"request.ComprehensiveWorkflowDetailRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"workflowId"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"workflowId": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"request.CreateApprovalWorkflowStepsRequest": {
|
"request.CreateApprovalWorkflowStepsRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|
|
||||||
|
|
@ -1695,6 +1695,64 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/approval-workflows/comprehensive-details": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"Bearer": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "API for getting comprehensive details of approval workflow including steps, client settings, and related data",
|
||||||
|
"tags": [
|
||||||
|
"ApprovalWorkflows"
|
||||||
|
],
|
||||||
|
"summary": "Get comprehensive approval workflow details",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Insert the Authorization",
|
||||||
|
"name": "Authorization",
|
||||||
|
"in": "header",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Workflow detail request",
|
||||||
|
"name": "req",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.ComprehensiveWorkflowDetailRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.Response"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad Request",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.BadRequestError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.UnauthorizedError"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/response.InternalServerError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/approval-workflows/default": {
|
"/approval-workflows/default": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
|
|
@ -17333,6 +17391,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"request.ComprehensiveWorkflowDetailRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"workflowId"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"workflowId": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"request.CreateApprovalWorkflowStepsRequest": {
|
"request.CreateApprovalWorkflowStepsRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
|
|
|
||||||
|
|
@ -802,6 +802,13 @@ definitions:
|
||||||
description: Custom settings
|
description: Custom settings
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
request.ComprehensiveWorkflowDetailRequest:
|
||||||
|
properties:
|
||||||
|
workflowId:
|
||||||
|
type: integer
|
||||||
|
required:
|
||||||
|
- workflowId
|
||||||
|
type: object
|
||||||
request.CreateApprovalWorkflowStepsRequest:
|
request.CreateApprovalWorkflowStepsRequest:
|
||||||
properties:
|
properties:
|
||||||
approverRoleId:
|
approverRoleId:
|
||||||
|
|
@ -3031,6 +3038,44 @@ paths:
|
||||||
summary: Update ApprovalWorkflows with steps
|
summary: Update ApprovalWorkflows with steps
|
||||||
tags:
|
tags:
|
||||||
- ApprovalWorkflows
|
- ApprovalWorkflows
|
||||||
|
/approval-workflows/comprehensive-details:
|
||||||
|
post:
|
||||||
|
description: API for getting comprehensive details of approval workflow including
|
||||||
|
steps, client settings, and related data
|
||||||
|
parameters:
|
||||||
|
- description: Insert the Authorization
|
||||||
|
in: header
|
||||||
|
name: Authorization
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- description: Workflow detail request
|
||||||
|
in: body
|
||||||
|
name: req
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/request.ComprehensiveWorkflowDetailRequest'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.Response'
|
||||||
|
"400":
|
||||||
|
description: Bad Request
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.BadRequestError'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.UnauthorizedError'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/response.InternalServerError'
|
||||||
|
security:
|
||||||
|
- Bearer: []
|
||||||
|
summary: Get comprehensive approval workflow details
|
||||||
|
tags:
|
||||||
|
- ApprovalWorkflows
|
||||||
/approval-workflows/default:
|
/approval-workflows/default:
|
||||||
get:
|
get:
|
||||||
description: API for getting default ApprovalWorkflows
|
description: API for getting default ApprovalWorkflows
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue