kontenhumas-be/docs/ENHANCED_APPROVAL_PRACTICAL...

11 KiB

Enhanced Approval Actions - Practical Usage Guide

🎯 Overview

Panduan praktis untuk menggunakan Enhanced Approval Actions API yang mendukung 3 jenis action: Approve, Revision, dan Reject.

🔄 Action Types & Behavior

Action Behavior Use Case Result
approve Naik ke step berikutnya Content sudah sesuai Workflow berlanjut
revision Mundur 1 step Content perlu diperbaiki Workflow mundur 1 step
reject Balik ke Draft Content tidak sesuai Workflow dihentikan, artikel jadi draft

🧪 Practical Examples

Scenario 1: Normal Approval Flow

Step 1: Editor Review

curl -X POST "http://localhost:8080/api/article-approval-flows/articles/10/approve" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer EDITOR_TOKEN" \
  -d '{
    "action": "approve",
    "message": "Content is well-written and follows our guidelines"
  }'

Response:

{
  "success": true,
  "messages": ["Article successfully approve through active approval flow"],
  "data": {
    "article_id": 10,
    "flow_id": 1,
    "action": "approve",
    "current_step": 2,
    "current_branch": "Final_Approval",
    "workflow_id": 1
  }
}

Step 2: Senior Editor Review

curl -X POST "http://localhost:8080/api/article-approval-flows/articles/10/approve" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer SENIOR_EDITOR_TOKEN" \
  -d '{
    "action": "approve",
    "message": "Final approval granted. Ready for publication."
  }'

Result: Article published successfully! 🎉


Scenario 2: Request Update Flow

Step 1: Editor Review

curl -X POST "http://localhost:8080/api/article-approval-flows/articles/11/approve" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer EDITOR_TOKEN" \
  -d '{
    "action": "approve",
    "message": "Content is good, moving to next level"
  }'

Step 2: Senior Editor Review (Request Revision)

curl -X POST "http://localhost:8080/api/article-approval-flows/articles/11/approve" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer SENIOR_EDITOR_TOKEN" \
  -d '{
    "action": "revision",
    "message": "Please add more examples in section 3 and improve the conclusion"
  }'

Response:

{
  "success": true,
  "messages": ["Article successfully revision through active approval flow"],
  "data": {
    "article_id": 11,
    "flow_id": 2,
    "action": "revision",
    "current_step": 1,
    "current_branch": "Branch_A",
    "workflow_id": 1
  }
}

Result: Article kembali ke step 1 untuk revisi! 📝

Step 3: After Revision, Approve Again

curl -X POST "http://localhost:8080/api/article-approval-flows/articles/11/approve" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer EDITOR_TOKEN" \
  -d '{
    "action": "approve",
    "message": "Revisions completed, content improved significantly"
  }'

Scenario 3: Reject Flow

Step 1: Editor Review (Reject)

curl -X POST "http://localhost:8080/api/article-approval-flows/articles/12/approve" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer EDITOR_TOKEN" \
  -d '{
    "action": "reject",
    "message": "Content does not meet our quality standards. Please rewrite with better research and clearer structure."
  }'

Response:

{
  "success": true,
  "messages": ["Article successfully reject through active approval flow"],
  "data": {
    "article_id": 12,
    "flow_id": 3,
    "action": "reject",
    "current_step": 1,
    "current_branch": "Branch_A",
    "workflow_id": 1
  }
}

Result: Article dikembalikan ke status Draft!


🔍 Database State Changes

After Approve:

-- Article Approval Flows
UPDATE article_approval_flows 
SET current_step = 2, 
    updated_at = NOW() 
WHERE id = 1;

-- Article Approval Step Logs
INSERT INTO article_approval_step_logs 
(approval_flow_id, step_order, action, action_by_id, message, created_at) 
VALUES (1, 1, 'approve', 5, 'Content is well-written', NOW());

After Revision:

-- Article Approval Flows
UPDATE article_approval_flows 
SET current_step = 1, 
    revision_requested = true,
    revision_message = 'Please add more examples',
    updated_at = NOW() 
WHERE id = 2;

-- Article Approval Step Logs
INSERT INTO article_approval_step_logs 
(approval_flow_id, step_order, action, action_by_id, message, created_at) 
VALUES (2, 2, 'revision', 6, 'Please add more examples', NOW());

After Reject:

-- Article Approval Flows
UPDATE article_approval_flows 
SET status_id = 3, 
    rejection_reason = 'Content does not meet quality standards',
    completed_at = NOW(),
    updated_at = NOW() 
WHERE id = 3;

-- Articles
UPDATE articles 
SET status_id = 1, 
    is_draft = true,
    workflow_id = NULL,
    current_approval_step = NULL,
    updated_at = NOW() 
WHERE id = 12;

-- Article Approval Step Logs
INSERT INTO article_approval_step_logs 
(approval_flow_id, step_order, action, action_by_id, message, created_at) 
VALUES (3, 1, 'reject', 5, 'Content does not meet quality standards', NOW());

🎯 Frontend Integration Examples

React Component:

import React, { useState } from 'react';

const ApprovalActions = ({ articleId, onActionComplete }) => {
  const [action, setAction] = useState('approve');
  const [message, setMessage] = useState('');

  const handleSubmit = async () => {
    try {
      const response = await fetch(`/api/article-approval-flows/articles/${articleId}/approve`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${token}`
        },
        body: JSON.stringify({ action, message })
      });

      const result = await response.json();
      
      if (result.success) {
        onActionComplete(result.data);
        alert(`Article ${action} successfully!`);
      } else {
        alert(`Error: ${result.messages[0]}`);
      }
    } catch (error) {
      alert('Failed to process approval action');
    }
  };

  return (
    <div className="approval-actions">
      <h3>Approval Actions</h3>
      
      <div className="action-buttons">
        <button 
          className={action === 'approve' ? 'active' : ''}
          onClick={() => setAction('approve')}
        >
           Approve
        </button>
        
        <button 
          className={action === 'revision' ? 'active' : ''}
          onClick={() => setAction('revision')}
        >
          📝 Revision
        </button>
        
        <button 
          className={action === 'reject' ? 'active' : ''}
          onClick={() => setAction('reject')}
        >
           Reject
        </button>
      </div>

      <textarea
        placeholder="Enter your message..."
        value={message}
        onChange={(e) => setMessage(e.target.value)}
      />

      <button onClick={handleSubmit}>
        Submit {action}
      </button>
    </div>
  );
};

Vue.js Component:

<template>
  <div class="approval-actions">
    <h3>Approval Actions</h3>
    
    <div class="action-buttons">
      <button 
        v-for="actionType in actions" 
        :key="actionType.value"
        :class="{ active: selectedAction === actionType.value }"
        @click="selectedAction = actionType.value"
      >
        {{ actionType.icon }} {{ actionType.label }}
      </button>
    </div>

    <textarea
      v-model="message"
      placeholder="Enter your message..."
    />

    <button @click="submitAction" :disabled="!selectedAction">
      Submit {{ selectedAction }}
    </button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      selectedAction: 'approve',
      message: '',
      actions: [
        { value: 'approve', label: 'Approve', icon: '✅' },
        { value: 'revision', label: 'Revision', icon: '📝' },
        { value: 'reject', label: 'Reject', icon: '❌' }
      ]
    }
  },
  methods: {
    async submitAction() {
      try {
        const response = await fetch(`/api/article-approval-flows/articles/${this.articleId}/approve`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${this.token}`
          },
          body: JSON.stringify({ 
            action: this.selectedAction, 
            message: this.message 
          })
        });

        const result = await response.json();
        
        if (result.success) {
          this.$emit('action-complete', result.data);
          alert(`Article ${this.selectedAction} successfully!`);
        } else {
          alert(`Error: ${result.messages[0]}`);
        }
      } catch (error) {
        alert('Failed to process approval action');
      }
    }
  }
}
</script>

🚨 Error Handling Examples

Invalid Action:

curl -X POST "http://localhost:8080/api/article-approval-flows/articles/123/approve" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "action": "invalid_action",
    "message": "Test"
  }'

Response:

{
  "success": false,
  "messages": ["Validation failed: [Action must be one of: approve, revision, reject]"]
}

Missing Action:

curl -X POST "http://localhost:8080/api/article-approval-flows/articles/123/approve" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{
    "message": "Test"
  }'

Response:

{
  "success": false,
  "messages": ["Validation failed: [Action is required]"]
}

📊 Testing Scenarios

Test Case 1: Complete Approval Flow

  1. Create article dengan user level 5
  2. Editor (level 3) approve → step 2
  3. Senior Editor (level 2) approve → published

Test Case 2: Revision Flow

  1. Create article dengan user level 5
  2. Editor (level 3) approve → step 2
  3. Senior Editor (level 2) revision → step 1
  4. Editor (level 3) approve again → step 2
  5. Senior Editor (level 2) approve → published

Test Case 3: Reject Flow

  1. Create article dengan user level 5
  2. Editor (level 3) reject → article jadi draft
  3. Workflow dihentikan

🎉 Summary

Enhanced Approval Actions API memberikan:

  • 3 Action Types: Approve, Revision, Reject
  • Flexible Workflow: Support untuk berbagai skenario
  • Better UX: User bisa memberikan feedback yang spesifik
  • Quality Control: Reject option untuk content yang tidak sesuai
  • Revision Support: Revision untuk perbaikan tanpa menghentikan workflow
  • Audit Trail: Semua action dicatat dalam logs

Sekarang approver memiliki kontrol penuh atas workflow dengan 3 pilihan action yang berbeda! 🚀