diff --git a/.github/workflows/backmerge-to-develop.yml b/.github/workflows/backmerge-to-develop.yml
new file mode 100644
index 0000000..8906ac1
--- /dev/null
+++ b/.github/workflows/backmerge-to-develop.yml
@@ -0,0 +1,203 @@
+name: Backmerge to Develop
+
+on:
+ pull_request:
+ types: [closed]
+ branches:
+ - main
+
+jobs:
+ backmerge:
+ # Only run if PR was merged (not just closed) and came from release/* or hotfix/* branch
+ if: |
+ github.event.pull_request.merged == true &&
+ (startsWith(github.event.pull_request.head.ref, 'release/') ||
+ startsWith(github.event.pull_request.head.ref, 'hotfix/'))
+
+ runs-on: ubuntu-latest
+
+ permissions:
+ contents: write
+ pull-requests: write
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # Fetch all history for all branches
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Configure Git
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+
+ - name: Extract version from branch name
+ id: version
+ run: |
+ BRANCH_NAME="${{ github.event.pull_request.head.ref }}"
+ echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
+
+ if [[ $BRANCH_NAME == release/v* ]]; then
+ VERSION=$(echo $BRANCH_NAME | sed 's/release\/v//')
+ TYPE="release"
+ elif [[ $BRANCH_NAME == hotfix/v* ]]; then
+ VERSION=$(echo $BRANCH_NAME | sed 's/hotfix\/v//')
+ TYPE="hotfix"
+ fi
+
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
+ echo "type=$TYPE" >> $GITHUB_OUTPUT
+
+ - name: Check if develop branch exists
+ id: check_develop
+ run: |
+ if git ls-remote --heads origin develop | grep -q develop; then
+ echo "exists=true" >> $GITHUB_OUTPUT
+ else
+ echo "exists=false" >> $GITHUB_OUTPUT
+ echo "โ ๏ธ Develop branch does not exist. Skipping backmerge."
+ fi
+
+ - name: Create backmerge pull request
+ if: steps.check_develop.outputs.exists == 'true'
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ # Create a unique branch name for the backmerge
+ BACKMERGE_BRANCH="backmerge/main-to-develop-$(date +%Y%m%d-%H%M%S)"
+
+ # Fetch and checkout develop
+ git fetch origin develop:develop
+
+ # Create new branch from develop
+ git checkout -b $BACKMERGE_BRANCH develop
+
+ # Merge main into the backmerge branch
+ echo "Attempting to merge main into $BACKMERGE_BRANCH..."
+
+ # Try to merge main
+ if git merge origin/main --no-edit; then
+ echo "โ
Merge successful, no conflicts"
+
+ # Push the backmerge branch
+ git push origin $BACKMERGE_BRANCH
+
+ # Create PR using GitHub CLI
+ PR_TITLE="chore: backmerge v${{ steps.version.outputs.version }} to develop"
+
+ cat > pr_body.md << 'PREOF'
+ ## Automated Backmerge
+
+ This PR automatically backmerges changes from `main` to `develop` branch.
+
+ ### Source
+ - **Original PR:** #${{ github.event.pull_request.number }}
+ - **Title:** ${{ github.event.pull_request.title }}
+ - **Merged by:** @${{ github.event.pull_request.merged_by.login }}
+ - **Merged at:** ${{ github.event.pull_request.merged_at }}
+
+ ### Branch Information
+ - **Source branch:** `${{ github.event.pull_request.head.ref }}`
+ - **Type:** ${{ github.event.pull_request.head.ref }}
+
+ ### Actions Required
+ - ๐ **Review Required** - This PR needs approval before merging
+ - โ
Ensure all CI/CD checks pass
+ - โ
Review changes for any conflicts or issues
+ - โ
Merge after approval
+
+ ---
+ *This PR was automatically created by GitHub Actions workflow.*
+ PREOF
+
+ # Create PR with review requirements
+ # Uncomment --draft if you want PRs to be created as draft
+ # Add --reviewer "username1,username2" to auto-assign reviewers
+ gh pr create \
+ --base develop \
+ --head $BACKMERGE_BRANCH \
+ --title "$PR_TITLE" \
+ --body-file pr_body.md \
+ --label "chore,auto-generated" \
+ --no-maintainer-edit
+
+ echo "โ
Pull request created successfully"
+
+ else
+ echo "โ Merge conflicts detected"
+
+ # Reset merge
+ git merge --abort
+
+ # Push the branch anyway for manual resolution
+ git push origin $BACKMERGE_BRANCH
+
+ # Create PR with conflict warning
+ PR_TITLE="chore: backmerge v${{ steps.version.outputs.version }} to develop (conflicts)"
+
+ cat > pr_conflict_body.md << 'PREOF'
+ ## โ ๏ธ Automated Backmerge with Conflicts
+
+ This PR attempts to backmerge changes from `main` to `develop` branch, but **conflicts were detected**.
+
+ ### Source
+ - **Original PR:** #${{ github.event.pull_request.number }}
+ - **Title:** ${{ github.event.pull_request.title }}
+ - **Merged by:** @${{ github.event.pull_request.merged_by.login }}
+ - **Merged at:** ${{ github.event.pull_request.merged_at }}
+
+ ### Branch Information
+ - **Source branch:** `${{ github.event.pull_request.head.ref }}`
+ - **Type:** ${{ github.event.pull_request.head.ref }}
+
+ ### โ ๏ธ Manual Actions Required
+ 1. Checkout the branch locally
+ 2. Merge `main` and resolve conflicts manually
+ 3. Push the resolved changes
+ 4. Request review and merge this PR
+
+ ```bash
+ git fetch origin
+ git checkout -b ${BACKMERGE_BRANCH} origin/${BACKMERGE_BRANCH}
+ git merge origin/main
+ # Resolve conflicts in your editor
+ git add .
+ git commit
+ git push origin ${BACKMERGE_BRANCH}
+ ```
+
+ ---
+ *This PR was automatically created by GitHub Actions workflow. Manual conflict resolution is required.*
+ PREOF
+
+ gh pr create \
+ --base develop \
+ --head $BACKMERGE_BRANCH \
+ --title "$PR_TITLE" \
+ --body-file pr_conflict_body.md \
+ --label "chore,auto-generated,has-conflicts" \
+ --no-maintainer-edit
+
+ echo "โ ๏ธ Pull request created with conflict warning"
+
+ # Exit with error to mark the job as failed
+ exit 1
+ fi
+
+ - name: Summary
+ if: always()
+ run: |
+ echo "## Backmerge Summary" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "- **Source PR:** #${{ github.event.pull_request.number }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **Source Branch:** \`${{ github.event.pull_request.head.ref }}\`" >> $GITHUB_STEP_SUMMARY
+ echo "- **Target Branch:** \`develop\`" >> $GITHUB_STEP_SUMMARY
+ echo "- **Triggered by:** @${{ github.event.pull_request.merged_by.login }}" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ if [ "${{ steps.check_develop.outputs.exists }}" == "false" ]; then
+ echo "โ ๏ธ **Status:** Skipped - develop branch does not exist" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "โ
**Status:** Backmerge PR created" >> $GITHUB_STEP_SUMMARY
+ fi
\ No newline at end of file
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..0487caa
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,414 @@
+name: Deploy Documentation to Production
+
+on:
+ push:
+ tags:
+ - 'v*.*.*' # Triggers on version tags like v1.0.0, v2.1.3, etc.
+
+jobs:
+ build-and-deploy:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '18'
+ cache: 'npm'
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v2
+ with:
+ version: 8
+
+ - name: Get version from tag
+ run: |
+ echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
+ echo "Deploying documentation version: ${GITHUB_REF#refs/tags/}"
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Build documentation site
+ run: |
+ echo "๐ฆ Building Vue.js documentation site..."
+ pnpm run build
+
+ echo "โ
Build completed successfully"
+ echo "๐ Build output:"
+ ls -la dist/
+
+ - name: Prepare deployment package
+ run: |
+ echo "๐ Preparing deployment package..."
+
+ # Create deployment directory
+ mkdir -p deploy-package
+
+ # Copy built files
+ cp -r dist/* deploy-package/
+
+ # Copy essential server files
+ cp .htaccess deploy-package/
+ cp public/api-down.html deploy-package/
+
+ # Copy favicon if exists
+ cp public/favicon.ico deploy-package/ 2>/dev/null || echo "โ ๏ธ favicon.ico not found, skipping"
+
+ # Ensure logos are included
+ mkdir -p deploy-package/assets
+ cp public/sulteng-*.webp deploy-package/assets/ 2>/dev/null || echo "โน๏ธ Logos already in dist or not found"
+
+ # Create deployment info
+ cat > deploy-package/DEPLOY_INFO.txt << EOF
+ PICO SulTeng COVID-19 API Documentation
+ Version: ${{ env.VERSION }}
+ Deployed: $(date -u '+%Y-%m-%d %H:%M:%S UTC')
+ Built with: Vue.js + Vite
+ Target: Hostinger Shared Hosting
+ EOF
+
+ echo "โ
Deployment package prepared"
+ echo "๐ Package contents:"
+ ls -la deploy-package/
+ echo "๐ Package size: $(du -sh deploy-package | cut -f1)"
+
+ - name: Setup SSH Agent
+ uses: webfactory/ssh-agent@v0.8.0
+ with:
+ ssh-private-key: ${{ secrets.DEPLOY_SSH_KEY }}
+ log-public-key: false
+
+ - name: Add server to known hosts
+ run: |
+ mkdir -p ~/.ssh
+ echo "Adding ${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PORT }} to known hosts..."
+ ssh-keyscan -H -p ${{ secrets.DEPLOY_PORT }} ${{ secrets.DEPLOY_HOST }} >> ~/.ssh/known_hosts
+
+ - name: Test SSH connection
+ run: |
+ echo "Testing SSH connection to ${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PORT }}..."
+ ssh -p ${{ secrets.DEPLOY_PORT }} -o ConnectTimeout=10 -o BatchMode=yes ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} 'echo "SSH connection successful"'
+
+ - name: Deploy to production server
+ run: |
+ echo "๐ Starting deployment of documentation ${{ env.VERSION }} to production..."
+
+ # Create temporary deployment archive
+ tar -czf docs-${{ env.VERSION }}.tar.gz -C deploy-package .
+
+ # Upload deployment package
+ echo "๐ค Uploading deployment package..."
+ scp -P ${{ secrets.DEPLOY_PORT }} docs-${{ env.VERSION }}.tar.gz ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:/tmp/
+
+ # Execute deployment script on remote server
+ ssh -p ${{ secrets.DEPLOY_PORT }} ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} << 'EOF'
+ set -e
+
+ DEPLOY_PATH="${{ secrets.DOCS_DEPLOY_PATH }}"
+ VERSION="${{ env.VERSION }}"
+ TEMP_ARCHIVE="/tmp/docs-${VERSION}.tar.gz"
+ BACKUP_DIR=""
+
+ echo "๐ Checking deployment environment..."
+ echo "Deploy path: $DEPLOY_PATH"
+
+ # Create deployment directory if it doesn't exist
+ mkdir -p "$DEPLOY_PATH"
+ cd "$DEPLOY_PATH"
+
+ echo "๐ Current directory contents:"
+ ls -la
+
+ echo "๐พ Creating backup of current deployment..."
+ if [ -f "index.html" ]; then
+ BACKUP_DIR="../docs-backup-$(date +%Y%m%d_%H%M%S)"
+ mkdir -p "$BACKUP_DIR"
+ cp -r * "$BACKUP_DIR/" 2>/dev/null || echo "โ ๏ธ Some files couldn't be backed up"
+ echo "โ
Backup created at: $BACKUP_DIR"
+ echo "BACKUP_DIR=$BACKUP_DIR" > /tmp/backup_path.txt
+ else
+ echo "โน๏ธ No existing deployment found, skipping backup"
+ echo "BACKUP_DIR=" > /tmp/backup_path.txt
+ fi
+
+ echo "๐ฆ Extracting new deployment..."
+ tar -xzf "$TEMP_ARCHIVE" -C .
+
+ echo "๐ง Setting proper permissions..."
+ find . -type f -name "*.html" -exec chmod 644 {} \;
+ find . -type f -name "*.css" -exec chmod 644 {} \;
+ find . -type f -name "*.js" -exec chmod 644 {} \;
+ find . -type f -name "*.json" -exec chmod 644 {} \;
+ find . -type f -name ".htaccess" -exec chmod 644 {} \;
+ find . -type d -exec chmod 755 {} \;
+
+ echo "๐งน Cleaning up temporary files..."
+ rm -f "$TEMP_ARCHIVE"
+
+ echo "โ
New deployment files deployed!"
+ echo "๐ Deployed files:"
+ ls -la | head -20
+ EOF
+
+ - name: Verify deployment and handle rollback
+ run: |
+ echo "๐ Verifying deployment..."
+ sleep 5 # Give the site time to propagate
+
+ HEALTH_CHECK_PASSED=false
+
+ # Check if the documentation site is accessible
+ if [ -n "${{ secrets.DOCS_URL }}" ]; then
+ echo "Testing documentation site at: ${{ secrets.DOCS_URL }}"
+
+ # Perform multiple health checks
+ CHECKS_PASSED=0
+ TOTAL_CHECKS=3
+
+ for i in $(seq 1 $TOTAL_CHECKS); do
+ echo "Health check attempt $i/$TOTAL_CHECKS..."
+
+ # Check if site responds with 200
+ if curl -f -s -I "${{ secrets.DOCS_URL }}" | head -n1 | grep -q "200 OK"; then
+ echo "โ
HTTP status check passed"
+
+ # Check if it's serving the Vue.js app content
+ if curl -f -s "${{ secrets.DOCS_URL }}" | grep -q "PICO SulTeng\|pico-api-docs"; then
+ echo "โ
Content verification passed"
+ CHECKS_PASSED=$((CHECKS_PASSED + 1))
+ else
+ echo "โ Content verification failed"
+ fi
+ else
+ echo "โ HTTP status check failed"
+ fi
+
+ if [ $i -lt $TOTAL_CHECKS ]; then
+ sleep 3
+ fi
+ done
+
+ # Determine if health check passed (majority of checks must pass)
+ if [ $CHECKS_PASSED -ge 2 ]; then
+ HEALTH_CHECK_PASSED=true
+ echo "โ
Health checks passed ($CHECKS_PASSED/$TOTAL_CHECKS)"
+ else
+ HEALTH_CHECK_PASSED=false
+ echo "โ Health checks failed ($CHECKS_PASSED/$TOTAL_CHECKS)"
+ fi
+ else
+ echo "โน๏ธ No documentation URL configured - skipping health check"
+ HEALTH_CHECK_PASSED=true # Assume success if no URL to test
+ fi
+
+ # Handle success/failure
+ ssh -p ${{ secrets.DEPLOY_PORT }} ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} << EOF
+ # Read backup path from previous step
+ if [ -f "/tmp/backup_path.txt" ]; then
+ BACKUP_DIR=\$(grep "BACKUP_DIR=" /tmp/backup_path.txt | cut -d'=' -f2)
+ else
+ BACKUP_DIR=""
+ fi
+
+ if [ "$HEALTH_CHECK_PASSED" = "true" ]; then
+ echo "๐ Deployment verification successful!"
+
+ # Clean up backup if it exists
+ if [ -n "\$BACKUP_DIR" ] && [ -d "\$BACKUP_DIR" ]; then
+ echo "๐งน Removing backup directory: \$BACKUP_DIR"
+ rm -rf "\$BACKUP_DIR"
+ echo "โ
Backup cleaned up successfully"
+ fi
+
+ # Clean up backup path file
+ rm -f /tmp/backup_path.txt
+
+ echo "โ
Deployment ${{ env.VERSION }} completed successfully!"
+
+ else
+ echo "โ Deployment verification failed! Rolling back..."
+
+ if [ -n "\$BACKUP_DIR" ] && [ -d "\$BACKUP_DIR" ]; then
+ DEPLOY_PATH="${{ secrets.DOCS_DEPLOY_PATH }}"
+ cd "\$DEPLOY_PATH"
+
+ echo "๐ Restoring from backup: \$BACKUP_DIR"
+
+ # Remove failed deployment
+ rm -rf ./* .[^.]* 2>/dev/null || true
+
+ # Restore backup
+ cp -r "\$BACKUP_DIR"/* . 2>/dev/null || echo "โ ๏ธ Some backup files couldn't be restored"
+ cp -r "\$BACKUP_DIR"/.[^.]* . 2>/dev/null || true
+
+ echo "โ
Rollback completed - previous version restored"
+
+ # Keep backup for investigation
+ echo "๐ Backup preserved for investigation: \$BACKUP_DIR"
+
+ else
+ echo "โ No backup available for rollback!"
+ echo "โ ๏ธ Manual intervention required"
+ fi
+
+ # Clean up backup path file
+ rm -f /tmp/backup_path.txt
+
+ echo "โ Deployment ${{ env.VERSION }} failed and rolled back"
+ exit 1
+ fi
+ EOF
+
+ # Exit with error if health check failed
+ if [ "$HEALTH_CHECK_PASSED" = "false" ]; then
+ echo "โ Deployment failed health checks and was rolled back"
+ exit 1
+ fi
+
+ - name: Create deployment summary
+ run: |
+ echo "## ๐ Documentation Deployment Summary" >> $GITHUB_STEP_SUMMARY
+ echo "- **Version**: ${{ env.VERSION }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **Target**: ${{ secrets.DOCS_DEPLOY_PATH }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **Status**: โ
Deployed successfully" >> $GITHUB_STEP_SUMMARY
+ echo "- **Site**: ${{ secrets.DOCS_URL || 'URL not configured' }}" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "### ๐ What was deployed" >> $GITHUB_STEP_SUMMARY
+ echo "- Vue.js documentation site" >> $GITHUB_STEP_SUMMARY
+ echo "- API proxy configuration (.htaccess)" >> $GITHUB_STEP_SUMMARY
+ echo "- Maintenance page (api-down.html)" >> $GITHUB_STEP_SUMMARY
+ echo "- Static assets and images" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "### โ
Next Steps" >> $GITHUB_STEP_SUMMARY
+ echo "1. Verify site functionality at ${{ secrets.DOCS_URL }}" >> $GITHUB_STEP_SUMMARY
+ echo "2. Test Vue Router navigation" >> $GITHUB_STEP_SUMMARY
+ echo "3. Verify API proxy is working (if backend is running)" >> $GITHUB_STEP_SUMMARY
+
+ create-release:
+ runs-on: ubuntu-latest
+ needs: build-and-deploy
+ if: needs.build-and-deploy.result == 'success'
+ permissions:
+ contents: write
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Get version and release info
+ id: release_info
+ run: |
+ VERSION=${GITHUB_REF#refs/tags/}
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
+
+ # Get the previous tag for changelog
+ PREVIOUS_TAG=$(git tag --sort=-version:refname | grep -A1 "^${VERSION}$" | tail -n1)
+ if [ -z "$PREVIOUS_TAG" ] || [ "$PREVIOUS_TAG" = "$VERSION" ]; then
+ PREVIOUS_TAG=$(git tag --sort=-version:refname | head -n2 | tail -n1)
+ fi
+ echo "previous_tag=$PREVIOUS_TAG" >> $GITHUB_OUTPUT
+
+ - name: Generate release notes
+ id: release_notes
+ run: |
+ VERSION=${{ steps.release_info.outputs.version }}
+ PREVIOUS_TAG=${{ steps.release_info.outputs.previous_tag }}
+
+ # Create release notes
+ cat > release_notes.md << 'EOF'
+ ## ๐ PICO SulTeng COVID-19 API Documentation ${{ steps.release_info.outputs.version }}
+
+ **Deployment**: โ
Successfully deployed to production
+ **Site**: ${{ secrets.DOCS_URL || 'Documentation site' }}
+
+ ### ๐ What's New
+
+ EOF
+
+ # Get commits since last tag
+ if [ -n "$PREVIOUS_TAG" ]; then
+ echo "Changes since $PREVIOUS_TAG:" >> release_notes.md
+ echo "" >> release_notes.md
+
+ git log --pretty=format:"- %s" "${PREVIOUS_TAG}..${VERSION}" | \
+ grep -v "Merge branch\|Merge pull request" | \
+ head -20 >> release_notes.md
+ else
+ echo "- Initial documentation release" >> release_notes.md
+ fi
+
+ # Add deployment details
+ cat >> release_notes.md << 'EOF'
+
+ ### ๐ Deployment Details
+
+ - **Built with**: Vue.js 3 + Vite + TypeScript
+ - **Features**: Responsive design, bilingual support (ID/EN), API integration
+ - **Deployment Time**: $(date -u '+%Y-%m-%d %H:%M:%S UTC')
+ - **Server**: Hostinger Shared Hosting
+
+ ### ๐ Quick Links
+
+ - [Documentation Site](${{ secrets.DOCS_URL || '#' }})
+ - [API Health Check](${{ secrets.DOCS_URL || 'https://pico-api.banuacoder.com' }}/api/v1/health)
+ - [Repository](https://github.com/banua-coder/pico-api-docs)
+
+ ### ๐ฑ Features Included
+
+ - ๐ Bilingual support (Indonesian/English)
+ - ๐ Interactive API documentation
+ - ๐ป Responsive design for all devices
+ - ๐ Real-time API integration
+ - ๐ COVID-19 data visualization
+ - ๐ผ๏ธ Official Central Sulawesi branding
+ EOF
+
+ # Set output for GitHub Actions
+ echo 'RELEASE_NOTES<> $GITHUB_OUTPUT
+ cat release_notes.md >> $GITHUB_OUTPUT
+ echo 'EOF' >> $GITHUB_OUTPUT
+
+ - name: Create GitHub Release
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ VERSION=${{ steps.release_info.outputs.version }}
+
+ # Create the release
+ gh release create "$VERSION" \
+ --title "๐ Documentation $VERSION" \
+ --notes "${{ steps.release_notes.outputs.RELEASE_NOTES }}" \
+ --target main
+
+ notification:
+ runs-on: ubuntu-latest
+ needs: [build-and-deploy, create-release]
+ if: always()
+
+ steps:
+ - name: Notify deployment status
+ run: |
+ DEPLOY_STATUS="${{ needs.build-and-deploy.result }}"
+ RELEASE_STATUS="${{ needs.create-release.result }}"
+
+ if [ "$DEPLOY_STATUS" == "success" ]; then
+ echo "โ
Documentation deployment successful for ${{ github.ref_name }}"
+ echo "๐ Site should be available at: ${{ secrets.DOCS_URL }}"
+
+ if [ "$RELEASE_STATUS" == "success" ]; then
+ echo "โ
GitHub release created successfully"
+ else
+ echo "โ ๏ธ GitHub release creation failed, but deployment succeeded"
+ fi
+ else
+ echo "โ Documentation deployment failed for ${{ github.ref_name }}"
+ exit 1
+ fi
\ No newline at end of file
diff --git a/.github/workflows/release-automation.yml b/.github/workflows/release-automation.yml
new file mode 100644
index 0000000..422a1ca
--- /dev/null
+++ b/.github/workflows/release-automation.yml
@@ -0,0 +1,218 @@
+name: Release Automation
+
+on:
+ create:
+
+jobs:
+ prepare-release:
+ if: |
+ github.event.ref_type == 'branch' &&
+ (startsWith(github.event.ref, 'release/v') || startsWith(github.event.ref, 'hotfix/v'))
+
+ runs-on: ubuntu-latest
+
+ permissions:
+ contents: write
+ pull-requests: write
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # Need full history for changelog generation
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Setup Ruby
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: '3.2'
+
+ - name: Configure Git
+ run: |
+ git config user.name "github-actions[bot]"
+ git config user.email "github-actions[bot]@users.noreply.github.com"
+
+ - name: Extract version from branch name
+ id: extract_version
+ run: |
+ BRANCH_NAME="${{ github.event.ref }}"
+ # Extract version from branch name (release/v1.2.3 or hotfix/v1.2.3)
+ VERSION=$(echo "$BRANCH_NAME" | sed 's/.*\/v//')
+
+ if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+ echo "โ Invalid version format: $VERSION"
+ echo "Version must be in format x.y.z"
+ exit 1
+ fi
+
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
+ echo "branch_type=$(echo "$BRANCH_NAME" | cut -d'/' -f1)" >> $GITHUB_OUTPUT
+ echo "โ
Extracted version: $VERSION"
+
+ - name: Update package.json version
+ run: |
+ VERSION="${{ steps.extract_version.outputs.version }}"
+
+ # Update version in package.json
+ if [ -f "package.json" ]; then
+ # Use jq to update version if available, otherwise use sed
+ if command -v jq >/dev/null 2>&1; then
+ jq ".version = \"$VERSION\"" package.json > package.json.tmp && mv package.json.tmp package.json
+ else
+ sed -i.bak "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" package.json && rm package.json.bak
+ fi
+ echo "โ
Updated package.json to version $VERSION"
+ else
+ echo "โ ๏ธ package.json not found, skipping version update"
+ fi
+
+ - name: Generate changelog
+ id: changelog
+ run: |
+ VERSION="${{ steps.extract_version.outputs.version }}"
+
+ # Generate changelog using the Ruby script
+ echo "Generating changelog for version $VERSION..."
+
+ # Create CHANGELOG.md if it doesn't exist
+ if [ ! -f "CHANGELOG.md" ]; then
+ echo "# Changelog" > CHANGELOG.md
+ echo "" >> CHANGELOG.md
+ echo "All notable changes to this project will be documented in this file." >> CHANGELOG.md
+ echo "" >> CHANGELOG.md
+ fi
+
+ # Generate changelog for this version
+ ruby scripts/generate_changelog.rb \
+ --version "$VERSION" \
+ --output "CHANGELOG_NEW.md"
+
+ # Prepend new changelog to existing one
+ if [ -f "CHANGELOG_NEW.md" ]; then
+ # Add separator
+ echo "" >> CHANGELOG_NEW.md
+ echo "---" >> CHANGELOG_NEW.md
+ echo "" >> CHANGELOG_NEW.md
+
+ # Append existing changelog (skip first 3 lines if they're the header)
+ if [ -f "CHANGELOG.md" ]; then
+ tail -n +4 CHANGELOG.md >> CHANGELOG_NEW.md 2>/dev/null || cat CHANGELOG.md >> CHANGELOG_NEW.md
+ fi
+
+ mv CHANGELOG_NEW.md CHANGELOG.md
+ echo "โ
Changelog generated successfully"
+ else
+ echo "โ Failed to generate changelog"
+ exit 1
+ fi
+
+ # Save changelog content for PR body
+ echo "changelog<> $GITHUB_OUTPUT
+ ruby scripts/generate_changelog.rb --version "$VERSION" | head -50 >> $GITHUB_OUTPUT
+ echo "EOF" >> $GITHUB_OUTPUT
+
+ - name: Commit changes
+ id: commit
+ run: |
+ VERSION="${{ steps.extract_version.outputs.version }}"
+ BRANCH_TYPE="${{ steps.extract_version.outputs.branch_type }}"
+
+ # Create a new branch for the release preparation
+ PREP_BRANCH="chore/prepare-${BRANCH_TYPE}-v${VERSION}"
+ git checkout -b "$PREP_BRANCH"
+
+ # Stage changes
+ git add -A
+
+ # Check if there are changes to commit
+ if git diff --staged --quiet; then
+ echo "No changes to commit"
+ echo "has_changes=false" >> $GITHUB_OUTPUT
+ else
+ # Commit with conventional commit message
+ if [ "$BRANCH_TYPE" = "release" ]; then
+ git commit -m "chore(release): prepare release v$VERSION"
+ else
+ git commit -m "fix(hotfix): prepare hotfix v$VERSION"
+ fi
+
+ # Push the new branch
+ git push origin "$PREP_BRANCH"
+
+ echo "has_changes=true" >> $GITHUB_OUTPUT
+ echo "prep_branch=$PREP_BRANCH" >> $GITHUB_OUTPUT
+ echo "โ
Changes committed and pushed to $PREP_BRANCH"
+ fi
+
+ - name: Create Pull Request
+ if: steps.commit.outputs.has_changes == 'true'
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ VERSION="${{ steps.extract_version.outputs.version }}"
+ BRANCH_TYPE="${{ steps.extract_version.outputs.branch_type }}"
+ PREP_BRANCH="${{ steps.commit.outputs.prep_branch }}"
+ SOURCE_BRANCH="${{ github.event.ref }}"
+
+ # Determine target branch (should be the release branch)
+ TARGET_BRANCH="$SOURCE_BRANCH"
+
+ if [ "$BRANCH_TYPE" = "release" ]; then
+ PR_TITLE="chore(release): prepare release v$VERSION"
+ LABELS="release,auto-generated"
+ else
+ PR_TITLE="fix(hotfix): prepare hotfix v$VERSION"
+ LABELS="hotfix,auto-generated,priority:high"
+ fi
+
+ # Create PR body
+ cat > pr_body.md << 'PREOF'
+ ## ๐ Prepare ${{ steps.extract_version.outputs.branch_type == 'release' && 'Release' || 'Hotfix' }} v${{ steps.extract_version.outputs.version }}
+
+ This PR prepares the following changes for v${{ steps.extract_version.outputs.version }}:
+
+ ### ๐ Changes Included
+ - โ
Version bumped to v${{ steps.extract_version.outputs.version }}
+ - โ
Changelog updated
+
+ ### ๐ Changelog Preview
+
+ ${{ steps.changelog.outputs.changelog }}
+
+ ### โ๏ธ Next Steps
+ 1. Review and merge this PR into `${{ github.event.ref }}`
+ 2. Create a PR from `${{ github.event.ref }}` to `main`
+ 3. After merging to `main`, a tag `v${{ steps.extract_version.outputs.version }}` will be created
+ 4. A GitHub release will be created with the changelog
+ 5. Changes will be backmerged to `develop` branch
+
+ ---
+ *This PR was automatically generated when the ${{ github.event.ref }} branch was created.*
+ PREOF
+
+ # Create the pull request from prep branch to release/hotfix branch
+ gh pr create \
+ --base "$TARGET_BRANCH" \
+ --head "$PREP_BRANCH" \
+ --title "$PR_TITLE" \
+ --body-file pr_body.md \
+ --label "$LABELS"
+
+ echo "โ
Pull request created successfully"
+
+ - name: Summary
+ if: always()
+ run: |
+ echo "## Release Preparation Summary" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "- **Branch:** ${{ github.event.ref }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **Type:** ${{ steps.extract_version.outputs.branch_type }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **Version:** v${{ steps.extract_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
+ echo "- **Changes Committed:** ${{ steps.commit.outputs.has_changes == 'true' && 'โ
Yes' || 'โ No' }}" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ if [ "${{ steps.commit.outputs.has_changes }}" == "true" ]; then
+ echo "โ
**Status:** Pull request created for v${{ steps.extract_version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "โ ๏ธ **Status:** No changes needed" >> $GITHUB_STEP_SUMMARY
+ fi
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index ebb2677..70a1ee8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -104,4 +104,4 @@ temp/
# Claude Code specific
.claude
-CLAUDE.md
\ No newline at end of file
+CLAUDE.mdimage.png
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..fd9e364
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,101 @@
+# Changelog for v1.0.0
+
+Generated on 2025-09-08
+All changes
+
+---
+
+## โจ Features
+
+- implement complete i18n translations for documentation sections (
+c24cc38)
+- redesign Documentation page with sidebar layout (
+7b7f287)
+- implement comprehensive national endpoint documentation (
+94f6aab)
+- add MIT license and API source code reference (
+95ed65b)
+- create reusable Navigation component with mobile support (
+49f042a)
+- implement comprehensive responsive design and mobile navigation (
+073591f)
+- add comprehensive deployment system with health checks and rollback (
+ecafbf5)
+- include version in backmerge PR titles (
+a2f029c)
+- add changelog generation script and release automation workflow (
+05d2b27)
+- add htaccess with api proxy and spa routing configuration (
+fe0d0e3)
+- add partner logos and maintenance page (
+fd23051)
+- implement vue spa with modern hero, features, and responsive design (
+cee2d15)
+- add html entry point with comprehensive seo meta tags (
+1fc22b6)
+
+## ๐ Bug Fixes
+
+- create separate branch for release preparation to avoid conflicts (c197f16f)
+- handle multiline commit messages in changelog generation for first release (
+5edb93c)
+- update API response examples to match actual response structure (
+3d09518)
+- update branding and contact links (
+3f1961d)
+- resolve CSS conflict in DataSources component (
+136a545)
+- update data source URLs to correct endpoints (
+25ee3b8)
+- update backmerge PR titles to use conventional commit style (
+99f59c7)
+
+## ๐ Documentation
+
+- update README with MIT license and API source references (
+17c22a2)
+
+## ๐ Style
+
+- add trailing newlines to all code files (
+71dcbee)
+
+## ๐ฆ Build System
+
+- add vite, typescript, and tailwind configuration (
+9876bed)
+
+## ๐ท CI/CD
+
+- add github action workflow for automatic backmerge to develop branch (
+8f7f494)
+
+## ๐ง Chores
+
+- Fix LaTeX rendering and clean up duplicate i18n setup (
+cc77722)
+- Add LaTeX rendering for mathematical formulas in Rt calculation (
+495256f)
+- Fix glossary section by removing broken i18n implementation (
+031088b)
+- Fix i18n implementation in GlossarySection component (
+0aff2ef)
+- Add Rt formula, references, and Vue i18n internationalization (
+98933e0)
+- Add Indonesian COVID-19 terminology to glossary (
+2ea7fde)
+- Refactor Documentation into subcomponents and add Glossary (
+5eed45c)
+- Fix mobile layout spacing and improve Coming Soon styling (
+0685343)
+- initial project setup with gitignore and readme (
+2960bc5)
+
+---
+
+## ๐ Statistics
+
+- Total commits: 33
+---
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ad9b30a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 Fajrian Aidil Pratama
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
index ae286e2..c00760d 100644
--- a/README.md
+++ b/README.md
@@ -114,6 +114,7 @@ The site integrates with PICO SulTeng API:
- **Base URL**: `https://pico-api.banuacoder.com/api/v1`
- **Documentation**: `https://pico-api.banuacoder.com/swagger/index.html`
- **Health Check**: `https://pico-api.banuacoder.com/api/v1/health`
+- **Source Code**: `https://github.com/banua-coder/pico-api-go`
## Available Scripts
@@ -154,9 +155,11 @@ pnpm run vue-tsc # Type check without emit
## License
-This project is licensed under the ISC License.
+This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
---
**Created for PICO SulTeng API**
-Banua Coders โข Central Sulawesi COVID-19 Data API
\ No newline at end of file
+[Banua Coder](https://banuacoder.com) โข Central Sulawesi COVID-19 Data API
+
+**Developer**: [Fajrian Aidil Pratama](https://github.com/ryanaidilp)
diff --git a/deploy.sh b/deploy.sh
new file mode 100755
index 0000000..dae0082
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,139 @@
+#!/bin/bash
+
+# Deployment script for PICO SulTeng COVID-19 API Documentation
+# Prepares files for deployment to Hostinger shared hosting
+
+echo "๐ Preparing PICO API Documentation for deployment..."
+
+# Build the Vue.js application
+echo "๐ฆ Building Vue.js application..."
+pnpm run build
+
+if [ ! -d "dist" ]; then
+ echo "โ Build failed - dist directory not found"
+ exit 1
+fi
+
+# Create deployment directory
+echo "๐ Creating deployment directory..."
+rm -rf deploy
+mkdir -p deploy
+
+# Copy built files
+echo "๐ Copying built files..."
+cp -r dist/* deploy/
+
+# Copy essential server files
+echo "๐ง Copying server configuration files..."
+cp .htaccess deploy/
+cp public/api-down.html deploy/
+cp public/favicon.ico deploy/ 2>/dev/null || echo "โ ๏ธ favicon.ico not found, skipping"
+
+# Copy logos and assets that might not be in dist
+echo "๐ผ๏ธ Ensuring all assets are included..."
+mkdir -p deploy/assets
+cp -r public/sulteng-*.webp deploy/assets/ 2>/dev/null || echo "โ ๏ธ Logo files not found in public, checking dist"
+
+# Create deployment info file
+cat > deploy/DEPLOY_INFO.txt << EOF
+PICO SulTeng COVID-19 API Documentation
+Deployment prepared on: $(date)
+Built with: Vue.js + Vite
+Target: Hostinger Shared Hosting
+
+Deployment Structure:
+- All files in this directory should be uploaded to public_html/
+- .htaccess handles Vue Router routing and API proxy
+- api-down.html is the maintenance page for API downtime
+
+Post-deployment steps:
+1. Ensure .htaccess is in the document root
+2. Verify Vue router is working by testing routes
+3. Test API proxy functionality
+4. Check that all assets load correctly
+EOF
+
+# Note: No server startup needed - this is a static website served by Apache
+
+# Create health check for the static site
+cat > deploy/health-check.sh << 'EOF'
+#!/bin/bash
+# Health check for the documentation site
+
+echo "๐ Checking documentation site health..."
+
+# Check if main files exist
+if [ -f "index.html" ]; then
+ echo "โ
index.html found"
+else
+ echo "โ index.html missing"
+ exit 1
+fi
+
+if [ -f ".htaccess" ]; then
+ echo "โ
.htaccess found"
+else
+ echo "โ ๏ธ .htaccess missing - Vue routing may not work"
+fi
+
+if [ -f "api-down.html" ]; then
+ echo "โ
api-down.html found"
+else
+ echo "โ ๏ธ api-down.html missing - API maintenance page unavailable"
+fi
+
+echo "๐ Site files:"
+ls -la *.html *.js *.css 2>/dev/null | head -10
+
+echo "โ
Documentation site health check complete"
+EOF
+
+chmod +x deploy/health-check.sh
+
+# Verify deployment package
+echo "๐ Verifying deployment package..."
+cd deploy
+
+# Check essential files
+ESSENTIAL_FILES=("index.html" ".htaccess" "api-down.html")
+MISSING_FILES=()
+
+for file in "${ESSENTIAL_FILES[@]}"; do
+ if [ ! -f "$file" ]; then
+ MISSING_FILES+=("$file")
+ fi
+done
+
+if [ ${#MISSING_FILES[@]} -eq 0 ]; then
+ echo "โ
All essential files present"
+else
+ echo "โ ๏ธ Missing files: ${MISSING_FILES[*]}"
+fi
+
+cd ..
+
+# Display deployment summary
+echo ""
+echo "โ
Deployment package ready in ./deploy/ directory"
+echo ""
+echo "๐ Deployment Summary:"
+echo "- Vue.js app built successfully"
+echo "- Static files prepared for upload"
+echo "- Apache .htaccess configured for SPA routing"
+echo "- API maintenance page included"
+echo "- Total files: $(find deploy -type f | wc -l)"
+echo "- Package size: $(du -sh deploy | cut -f1)"
+echo ""
+echo "๐ค Next steps:"
+echo "1. Upload all files in ./deploy/ to your Hostinger public_html/"
+echo "2. Ensure .htaccess is in the document root"
+echo "3. Test the site at your domain"
+echo "4. Verify API proxy is working (if backend is running)"
+echo ""
+echo "๐ Expected URLs:"
+echo "- Documentation: https://your-domain.com/"
+echo "- API Proxy: https://your-domain.com/api/v1/"
+echo "- Maintenance: https://your-domain.com/api-down.html"
+echo ""
+echo "๐ Files ready for upload:"
+ls -la deploy/ | head -20
\ No newline at end of file
diff --git a/image.png b/image.png
new file mode 100644
index 0000000..062ce7a
Binary files /dev/null and b/image.png differ
diff --git a/package.json b/package.json
index 7c40773..e5eb540 100644
--- a/package.json
+++ b/package.json
@@ -27,8 +27,9 @@
},
"dependencies": {
"aos": "^2.3.4",
+ "katex": "^0.16.22",
"vue": "^3.5.21",
- "vue-i18n": "^9.14.5",
+ "vue-i18n": "9",
"vue-router": "^4.5.1"
}
}
diff --git a/postcss.config.js b/postcss.config.js
index e99ebc2..2e7af2b 100644
--- a/postcss.config.js
+++ b/postcss.config.js
@@ -3,4 +3,4 @@ export default {
tailwindcss: {},
autoprefixer: {},
},
-}
\ No newline at end of file
+}
diff --git a/public/api-down.html b/public/api-down.html
index b33216a..ebc6775 100644
--- a/public/api-down.html
+++ b/public/api-down.html
@@ -181,13 +181,13 @@ Need immediate access?
-
- Contact Banua Coders
+ Contact Banua Coder
'โจ Features',
+ 'fix' => '๐ Bug Fixes',
+ 'docs' => '๐ Documentation',
+ 'style' => '๐ Style',
+ 'refactor' => 'โป๏ธ Code Refactoring',
+ 'perf' => 'โก Performance Improvements',
+ 'test' => 'โ
Tests',
+ 'build' => '๐ฆ Build System',
+ 'ci' => '๐ท CI/CD',
+ 'chore' => '๐ง Chores',
+ 'revert' => 'โช Reverts'
+ }.freeze
+
+ BREAKING_CHANGE_HEADER = '๐ฅ BREAKING CHANGES'
+
+ def initialize(options = {})
+ @from_tag = options[:from_tag]
+ @to_ref = options[:to_ref] || 'HEAD'
+ @version = options[:version]
+ @output_file = options[:output_file]
+ @include_merge_commits = options[:include_merge_commits] || false
+ end
+
+ def generate
+ # Get the latest tag if not specified
+ @from_tag ||= get_latest_tag
+
+ if @from_tag.nil? || @from_tag.empty?
+ puts "No previous tags found. Generating changelog from beginning."
+ @from_tag = nil
+ end
+
+ commits = get_commits
+ grouped_commits = group_commits_by_type(commits)
+ changelog = format_changelog(grouped_commits)
+
+ if @output_file
+ write_to_file(changelog)
+ else
+ puts changelog
+ end
+
+ changelog
+ end
+
+ private
+
+ def get_latest_tag
+ `git describe --tags --abbrev=0 2>/dev/null`.strip
+ rescue
+ nil
+ end
+
+ def get_commits
+ range = @from_tag ? "#{@from_tag}..#{@to_ref}" : @to_ref
+ merge_flag = @include_merge_commits ? '' : '--no-merges'
+
+ # Get commit information in a parseable format using a unique delimiter
+ delimiter = '|||DELIMITER|||'
+ format = "%H#{delimiter}%s#{delimiter}%b#{delimiter}%an#{delimiter}%ae#{delimiter}%ad"
+ commits_raw = `git log #{merge_flag} --format="#{format}#{delimiter}END" --date=short #{range}`
+
+ commits = []
+ commits_raw.split("#{delimiter}END").each do |commit_block|
+ next if commit_block.strip.empty?
+
+ parts = commit_block.split(delimiter)
+ next unless parts && parts.length >= 6
+
+ commits << {
+ hash: parts[0][0..7], # Short hash
+ subject: parts[1] || '',
+ body: parts[2] || '',
+ author: parts[3] || '',
+ email: parts[4] || '',
+ date: parts[5] || ''
+ }
+ end
+
+ commits
+ end
+
+ def group_commits_by_type(commits)
+ grouped = {
+ breaking: [],
+ types: Hash.new { |h, k| h[k] = [] }
+ }
+
+ commits.each do |commit|
+ # Parse conventional commit format
+ if commit[:subject] =~ /^(\w+)(?:\(([^)]+)\))?: (.+)$/
+ type = $1
+ scope = $2
+ description = $3
+
+ commit_info = {
+ hash: commit[:hash],
+ scope: scope,
+ description: description,
+ author: commit[:author],
+ date: commit[:date]
+ }
+
+ # Check for breaking changes
+ if commit[:subject].include?('!:') || commit[:body].to_s.downcase.include?('breaking change')
+ grouped[:breaking] << commit_info
+ end
+
+ # Group by type
+ if COMMIT_TYPES.key?(type)
+ grouped[:types][type] << commit_info
+ else
+ grouped[:types]['chore'] << commit_info
+ end
+ else
+ # Non-conventional commits go to chore
+ grouped[:types]['chore'] << {
+ hash: commit[:hash],
+ description: commit[:subject],
+ author: commit[:author],
+ date: commit[:date]
+ }
+ end
+ end
+
+ grouped
+ end
+
+ def format_changelog(grouped_commits)
+ lines = []
+
+ # Header
+ if @version
+ lines << "# Changelog for v#{@version}"
+ else
+ lines << "# Changelog"
+ end
+
+ lines << ""
+ lines << "Generated on #{Date.today.strftime('%Y-%m-%d')}"
+
+ if @from_tag
+ lines << "Changes since #{@from_tag}"
+ else
+ lines << "All changes"
+ end
+
+ lines << ""
+ lines << "---"
+ lines << ""
+
+ # Breaking changes
+ unless grouped_commits[:breaking].empty?
+ lines << "## #{BREAKING_CHANGE_HEADER}"
+ lines << ""
+ grouped_commits[:breaking].each do |commit|
+ scope_text = commit[:scope] ? "**#{commit[:scope]}:** " : ""
+ lines << "- #{scope_text}#{commit[:description]} (#{commit[:hash]})"
+ end
+ lines << ""
+ end
+
+ # Regular commits by type
+ COMMIT_TYPES.each do |type, header|
+ commits = grouped_commits[:types][type]
+ next if commits.empty?
+
+ lines << "## #{header}"
+ lines << ""
+
+ commits.each do |commit|
+ scope_text = commit[:scope] ? "**#{commit[:scope]}:** " : ""
+ lines << "- #{scope_text}#{commit[:description]} (#{commit[:hash]})"
+ end
+ lines << ""
+ end
+
+ # Statistics
+ lines << "---"
+ lines << ""
+ lines << "## ๐ Statistics"
+ lines << ""
+
+ total_commits = grouped_commits[:types].values.flatten.size
+ lines << "- Total commits: #{total_commits}"
+
+ if @from_tag
+ contributors = get_contributors
+ lines << "- Contributors: #{contributors.size}"
+ lines << ""
+ lines << "### Contributors"
+ lines << ""
+ contributors.each do |contributor|
+ lines << "- #{contributor[:name]} (#{contributor[:commits]} commits)"
+ end
+ end
+
+ lines.join("\n")
+ end
+
+ def get_contributors
+ range = @from_tag ? "#{@from_tag}..#{@to_ref}" : @to_ref
+ merge_flag = @include_merge_commits ? '' : '--no-merges'
+
+ contributors_raw = `git shortlog -sn #{merge_flag} #{range}`
+
+ contributors_raw.split("\n").map do |line|
+ if line =~ /^\s*(\d+)\s+(.+)$/
+ { commits: $1.to_i, name: $2.strip }
+ end
+ end.compact.sort_by { |c| -c[:commits] }
+ end
+
+ def write_to_file(content)
+ File.write(@output_file, content)
+ puts "Changelog written to #{@output_file}"
+ end
+end
+
+# CLI interface
+if __FILE__ == $0
+ options = {}
+
+ OptionParser.new do |opts|
+ opts.banner = "Usage: generate_changelog.rb [options]"
+
+ opts.on("-f", "--from TAG", "Starting tag (default: latest tag)") do |tag|
+ options[:from_tag] = tag
+ end
+
+ opts.on("-t", "--to REF", "Ending reference (default: HEAD)") do |ref|
+ options[:to_ref] = ref
+ end
+
+ opts.on("-v", "--version VERSION", "Version for the changelog") do |version|
+ options[:version] = version
+ end
+
+ opts.on("-o", "--output FILE", "Output file (default: stdout)") do |file|
+ options[:output_file] = file
+ end
+
+ opts.on("-m", "--include-merges", "Include merge commits") do
+ options[:include_merge_commits] = true
+ end
+
+ opts.on("-h", "--help", "Show this help message") do
+ puts opts
+ exit
+ end
+ end.parse!
+
+ generator = ChangelogGenerator.new(options)
+ generator.generate
+end
\ No newline at end of file
diff --git a/src/App.vue b/src/App.vue
index 3a29c8c..3c99c87 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -12,4 +12,4 @@
#app {
min-height: 100vh;
}
-
\ No newline at end of file
+
diff --git a/src/components/DataSources.vue b/src/components/DataSources.vue
new file mode 100644
index 0000000..7203c09
--- /dev/null
+++ b/src/components/DataSources.vue
@@ -0,0 +1,59 @@
+
+
+
+ {{ $t('dataSources.trustedBy') }}
+
+
+
+
+
+
diff --git a/src/components/MathFormula.vue b/src/components/MathFormula.vue
new file mode 100644
index 0000000..7271c96
--- /dev/null
+++ b/src/components/MathFormula.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/Navigation.vue b/src/components/Navigation.vue
new file mode 100644
index 0000000..b4e4cd1
--- /dev/null
+++ b/src/components/Navigation.vue
@@ -0,0 +1,218 @@
+
+
+
+
+
diff --git a/src/components/documentation/AuthenticationSection.vue b/src/components/documentation/AuthenticationSection.vue
new file mode 100644
index 0000000..38c898a
--- /dev/null
+++ b/src/components/documentation/AuthenticationSection.vue
@@ -0,0 +1,66 @@
+
+
+
+
{{ t('documentation.authentication.title') }}
+
{{ t('documentation.authentication.subtitle') }}
+
+
+
+
+
{{ t('documentation.authentication.title') }}
+
{{ t('documentation.authentication.subtitle') }}
+
+
+
+
+
+
+
+
{{ t('documentation.authentication.noAuthRequired') }}
+
{{ t('documentation.authentication.noAuthDescription') }}
+
+
+
+
+
+
+
{{ t('documentation.authentication.rateLimiting') }}
+
+ -
+
+ {{ detail }}
+
+
+
+
+
+
{{ t('documentation.authentication.bestPractices') }}
+
+ -
+
+ {{ practice }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/documentation/ErrorHandlingSection.vue b/src/components/documentation/ErrorHandlingSection.vue
new file mode 100644
index 0000000..ca733c4
--- /dev/null
+++ b/src/components/documentation/ErrorHandlingSection.vue
@@ -0,0 +1,122 @@
+
+
+
+
{{ t('documentation.errorHandling.title') }}
+
{{ t('documentation.errorHandling.subtitle') }}
+
+
+
+
+
{{ t('documentation.errorHandling.title') }}
+
{{ t('documentation.errorHandling.subtitle') }}
+
+
+
+
+
+
{{ t('documentation.errorHandling.httpStatusCodes') }}
+
+
+
200
+
+
{{ t('documentation.errorHandling.statusCodes.200.title') }}
+
{{ t('documentation.errorHandling.statusCodes.200.description') }}
+
+
+
+
+
400
+
+
{{ t('documentation.errorHandling.statusCodes.400.title') }}
+
{{ t('documentation.errorHandling.statusCodes.400.description') }}
+
+
+
+
+
404
+
+
{{ t('documentation.errorHandling.statusCodes.404.title') }}
+
{{ t('documentation.errorHandling.statusCodes.404.description') }}
+
+
+
+
+
429
+
+
{{ t('documentation.errorHandling.statusCodes.429.title') }}
+
{{ t('documentation.errorHandling.statusCodes.429.description') }}
+
+
+
+
+
500
+
+
{{ t('documentation.errorHandling.statusCodes.500.title') }}
+
{{ t('documentation.errorHandling.statusCodes.500.description') }}
+
+
+
+
+
+
+
+
{{ t('documentation.errorHandling.errorResponseFormat') }}
+
+
+
+
{{ t('documentation.errorHandling.errorResponse') }}
+
+
{
+
+
"status": "error",
+
"error": {
+
+
"code": 400,
+
"message": "Invalid page parameter",
+
"details": "Page must be a positive integer"
+
+
}
+
+
}
+
+
+
+
+
{{ t('documentation.errorHandling.errorFields') }}
+
+
+ status
+ {{ t('documentation.errorHandling.fieldDescriptions.status') }}
+
+
+ code
+ {{ t('documentation.errorHandling.fieldDescriptions.code') }}
+
+
+ message
+ {{ t('documentation.errorHandling.fieldDescriptions.message') }}
+
+
+ details
+ {{ t('documentation.errorHandling.fieldDescriptions.details') }}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/documentation/GlossarySection.vue b/src/components/documentation/GlossarySection.vue
new file mode 100644
index 0000000..04d7f61
--- /dev/null
+++ b/src/components/documentation/GlossarySection.vue
@@ -0,0 +1,340 @@
+
+
+
+
+
+
{{ t('documentation.glossary.title') }}
+
+
{{ t('documentation.glossary.subtitle') }}
+
+
+
+
+
{{ t('documentation.glossary.cardTitle') }}
+
{{ t('documentation.glossary.cardSubtitle') }}
+
+
+
+
+
+
+
Rt
+ {{ t('documentation.glossary.reproductionRate.title') }}
+
+
+
+
+
+
{{ t('documentation.glossary.reproductionRate.definition') }}
+
{{ t('documentation.glossary.reproductionRate.definitionText') }}
+
+
+
+
{{ t('documentation.glossary.reproductionRate.interpretation') }}
+
+
+ Rt > 1:
+ {{ t('documentation.glossary.reproductionRate.growing') }}
+
+
+ Rt = 1:
+ {{ t('documentation.glossary.reproductionRate.stable') }}
+
+
+ Rt < 1:
+ {{ t('documentation.glossary.reproductionRate.declining') }}
+
+
+
+
+
+
{{ t('documentation.glossary.reproductionRate.calculationMethod') }}
+
+
{{ t('documentation.glossary.reproductionRate.epiEstimTitle') }}
+
+
+
+
+
+
{{ t('documentation.glossary.reproductionRate.dataAvailability') }}
+
+
{{ t('documentation.glossary.reproductionRate.dataAvailabilityText') }}
+
+
+
+
+
+
+
{{ t('documentation.glossary.reproductionRate.formula') }}
+
+
{{ t('documentation.glossary.reproductionRate.formulaTitle') }}
+
{{ t('documentation.glossary.reproductionRate.formulaDescription') }}
+
+
+
+
+
1. {{ t('documentation.glossary.reproductionRate.formulaSteps.0') }}
+
+
+
+
2. {{ t('documentation.glossary.reproductionRate.formulaSteps.1') }}
+
+
+
+
+
+
+
3. {{ t('documentation.glossary.reproductionRate.formulaSteps.2') }}
+
+
+
+
4. {{ t('documentation.glossary.reproductionRate.formulaSteps.3') }}
+
+
+
+
+
+
+
+
+
{{ t('documentation.glossary.reproductionRate.references') }}
+
+
+
Cori, A., Ferguson, N. M., Fraser, C., & Cauchemez, S. (2013)
+
American Journal of Epidemiology
+
A new framework and software to estimate time-varying reproduction numbers during epidemics. 178(9), 1505-1512.
+
+ DOI: 10.1093/aje/kwt133
+
+
+
+
Thompson, R. N., et al. (2019)
+
Epidemics
+
Improved inference of time-varying reproduction numbers during infectious disease outbreaks. 29, 100356.
+
+ DOI: 10.1016/j.epidem.2019.100356
+
+
+
+
+
+
+
+
+
+
+
+
{{ t('documentation.glossary.caseClassifications.title') }}
+
+
+
{{ t('documentation.glossary.caseClassifications.positive.title') }}
+
{{ t('documentation.glossary.caseClassifications.positive.description') }}
+
+
+
{{ t('documentation.glossary.caseClassifications.recovered.title') }}
+
{{ t('documentation.glossary.caseClassifications.recovered.description') }}
+
+
+
{{ t('documentation.glossary.caseClassifications.deceased.title') }}
+
{{ t('documentation.glossary.caseClassifications.deceased.description') }}
+
+
+
{{ t('documentation.glossary.caseClassifications.active.title') }}
+
{{ t('documentation.glossary.caseClassifications.active.description') }}
+
+
+
+
+
+
{{ t('documentation.glossary.dataTypes.title') }}
+
+
+
{{ t('documentation.glossary.dataTypes.daily.title') }}
+
{{ t('documentation.glossary.dataTypes.daily.description') }}
+
+
+
{{ t('documentation.glossary.dataTypes.cumulative.title') }}
+
{{ t('documentation.glossary.dataTypes.cumulative.description') }}
+
+
+
{{ t('documentation.glossary.dataTypes.percentages.title') }}
+
{{ t('documentation.glossary.dataTypes.percentages.description') }}
+
+
+
{{ t('documentation.glossary.dataTypes.serialInterval.title') }}
+
{{ t('documentation.glossary.dataTypes.serialInterval.description') }}
+
+
+
+
+
+
+
+
{{ t('documentation.glossary.indonesianTerminology.title') }}
+
{{ t('documentation.glossary.indonesianTerminology.subtitle') }}
+
+
{{ t('documentation.glossary.indonesianTerminology.officialNote') }}
+
+
+
+
+
+
โ
+ {{ t('documentation.glossary.indonesianTerminology.currentTerms') }}
+
+
+
+
{{ t('documentation.glossary.indonesianTerminology.kasusSuspect.name') }}
+
{{ t('documentation.glossary.indonesianTerminology.kasusSuspect.translation') }}
+
{{ t('documentation.glossary.indonesianTerminology.kasusSuspect.description') }}
+
+
+
+
{{ t('documentation.glossary.indonesianTerminology.kasusProbable.name') }}
+
{{ t('documentation.glossary.indonesianTerminology.kasusProbable.translation') }}
+
{{ t('documentation.glossary.indonesianTerminology.kasusProbable.description') }}
+
+
+
+
{{ t('documentation.glossary.indonesianTerminology.kontakErat.name') }}
+
{{ t('documentation.glossary.indonesianTerminology.kontakErat.translation') }}
+
{{ t('documentation.glossary.indonesianTerminology.kontakErat.description') }}
+
+
+
+
{{ t('documentation.glossary.indonesianTerminology.kasusKonfirmasi.name') }}
+
{{ t('documentation.glossary.indonesianTerminology.kasusKonfirmasi.translation') }}
+
{{ t('documentation.glossary.indonesianTerminology.kasusKonfirmasi.description') }}
+
+
+
+
+
+
+
+
๐
+ {{ t('documentation.glossary.indonesianTerminology.legacyTerms') }}
+
+
+
+
+
+ {{ t('documentation.glossary.indonesianTerminology.odp.acronym') }}
+
+
+
{{ t('documentation.glossary.indonesianTerminology.odp.fullName') }}
+
{{ t('documentation.glossary.indonesianTerminology.odp.translation') }}
+
{{ t('documentation.glossary.indonesianTerminology.odp.description') }}
+
+
+ โ {{ t('documentation.glossary.indonesianTerminology.odp.replacedBy') }}
+
+
+
+
+
+
+
+
+
+ {{ t('documentation.glossary.indonesianTerminology.pdp.acronym') }}
+
+
+
{{ t('documentation.glossary.indonesianTerminology.pdp.fullName') }}
+
{{ t('documentation.glossary.indonesianTerminology.pdp.translation') }}
+
{{ t('documentation.glossary.indonesianTerminology.pdp.description') }}
+
+
+ โ {{ t('documentation.glossary.indonesianTerminology.pdp.replacedBy') }}
+
+
+
+
+
+
+
+
+
+ {{ t('documentation.glossary.indonesianTerminology.otg.acronym') }}
+
+
+
{{ t('documentation.glossary.indonesianTerminology.otg.fullName') }}
+
{{ t('documentation.glossary.indonesianTerminology.otg.translation') }}
+
{{ t('documentation.glossary.indonesianTerminology.otg.description') }}
+
+
+ โ {{ t('documentation.glossary.indonesianTerminology.otg.replacedBy') }}
+
+
+
+
+
+
+
+
+
+
+
{{ t('documentation.glossary.indonesianTerminology.classificationProgression.title') }}
+
{{ t('documentation.glossary.indonesianTerminology.classificationProgression.description') }}
+
+
+
+
+ "{{ t('documentation.glossary.indonesianTerminology.classificationProgression.officialQuote') }}"
+
+
โ Kementerian Kesehatan RI
+
+
+
+
+
{{ t('documentation.glossary.indonesianTerminology.classificationProgression.currentFlow') }}
+
+ Suspect
+ โ
+ Probable
+ โ
+ Confirmed
+ โ
+ Recovery/Death
+
+
+
+
+
{{ t('documentation.glossary.indonesianTerminology.classificationProgression.legacyFlow') }}
+
+ ODP
+ โ
+ PDP
+ โ
+ OTG
+ โ
+ Confirmed
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/documentation/NationalHistoricalSection.vue b/src/components/documentation/NationalHistoricalSection.vue
new file mode 100644
index 0000000..e810dea
--- /dev/null
+++ b/src/components/documentation/NationalHistoricalSection.vue
@@ -0,0 +1,96 @@
+
+
+
+
+
+
{{ t('documentation.nationalHistorical.title') }}
+
+
{{ t('documentation.nationalHistorical.description') }}
+
+
+
+
+
+
+
+
+
+
{{ t('documentation.nationalHistorical.queryParameters') }}
+
+
+
+
+ | {{ t('documentation.nationalHistorical.parameter') }} |
+ {{ t('documentation.nationalHistorical.type') }} |
+ {{ t('documentation.nationalHistorical.description') }} |
+ {{ t('documentation.nationalHistorical.default') }} |
+
+
+
+
+ | page |
+ integer |
+ Page number (1-based) |
+ 1 |
+
+
+ | limit |
+ integer |
+ Items per page (1-100) |
+ 10 |
+
+
+ | sort |
+ string |
+ Sort order: "asc" or "desc" |
+ desc |
+
+
+
+
+
+
+
+
+
{{ t('documentation.nationalHistorical.exampleRequest') }}
+
+
GET /api/v1/national?page=1&limit=5&sort=desc
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/documentation/NationalLatestSection.vue b/src/components/documentation/NationalLatestSection.vue
new file mode 100644
index 0000000..dd44c1f
--- /dev/null
+++ b/src/components/documentation/NationalLatestSection.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
+
{{ t('documentation.nationalLatest.title') }}
+
+
{{ t('documentation.nationalLatest.description') }}
+
+
+
+
+
+
+ GET
+ /national/latest
+
+
+
+
+
+
+
+
{{ t('documentation.nationalLatest.request') }}
+
+
GET /api/v1/national/latest HTTP/1.1
+
Host: pico-api.banuacoder.com
+
Accept: application/json
+
+
+
+
+
+
{{ t('documentation.nationalLatest.response') }}
+
+
{
+
+
"status": "success",
+
"data": {
+
+
"day": 883,
+
"date": "2022-12-18T16:37:54Z",
+
"daily": {
+
+
"positive": 860,
+
"recovered": 2035,
+
"deceased": 14,
+
"active": -1189
+
+
},
+
"cumulative": {
+
+
"positive": 6295729,
+
"recovered": 6126860,
+
"deceased": 157730,
+
"active": 11139
+
+
},
+
"statistics": {
+
+
"percentages": {
+
+
"active": 0.177,
+
"recovered": 97.318,
+
"deceased": 2.505
+
+
},
+
"reproduction_rate": {
+
+
"value": null,
+
"upper_bound": null,
+
"lower_bound": null
+
+
}
+
+
}
+
+
}
+
+
}
+
+
+
+
+
+
+
{{ t('documentation.nationalLatest.responseFields') }}
+
+
+
+
+ | Field |
+ Type |
+ Description |
+
+
+
+
+ | status |
+ string |
+ Response status ("success" or "error") |
+
+
+ | data.day |
+ integer |
+ Days since pandemic start |
+
+
+ | data.date |
+ string |
+ ISO 8601 date of the data |
+
+
+ | data.daily.* |
+ integer |
+ Daily new cases (positive, recovered, deceased, active) |
+
+
+ | data.cumulative.* |
+ integer |
+ Cumulative totals (positive, recovered, deceased, active) |
+
+
+ | data.statistics.percentages.* |
+ float |
+ Percentage breakdown (active, recovered, deceased) |
+
+
+ | data.statistics.reproduction_rate.* |
+ float|null |
+ Reproduction rate data (value, upper_bound, lower_bound) |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/documentation/OverviewSection.vue b/src/components/documentation/OverviewSection.vue
new file mode 100644
index 0000000..eacfb32
--- /dev/null
+++ b/src/components/documentation/OverviewSection.vue
@@ -0,0 +1,72 @@
+
+
+
+
{{ t('documentation.overview.title') }}
+
+ {{ t('documentation.overview.subtitle') }}
+
+
+
+
+
+
{{ t('documentation.overview.apiStatus') }}
+
+
+
+
+
+
+
{{ t('documentation.overview.gettingStarted') }}
+
{{ t('documentation.overview.gettingStartedSub') }}
+
+
+
+
+
+
{{ t('documentation.overview.baseUrl') }}
+
+ https://pico-api.banuacoder.com/api/v1
+
+
+
{{ t('documentation.overview.contentType') }}
+
+ application/json
+
+
+
+
+
{{ t('documentation.overview.quickExample') }}
+
+
+ $ curl -X GET \\
+
+
+ "https://pico-api.banuacoder.com/api/v1/national/latest"
+
+
+
+
+
+
+
{{ t('documentation.overview.noAuthRequired') }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/i18n.ts b/src/i18n.ts
index 13d3811..601f790 100644
--- a/src/i18n.ts
+++ b/src/i18n.ts
@@ -17,4 +17,4 @@ const i18n = createI18n({
messages
})
-export default i18n
\ No newline at end of file
+export default i18n
diff --git a/src/locales/en.json b/src/locales/en.json
index 063b2c7..0c14dfe 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -80,6 +80,188 @@
"ethicalHacker": "Ethical Hacker Indonesia - Security Partner"
}
},
+ "documentation": {
+ "title": "API Documentation",
+ "subtitle": "Comprehensive guide for integrating with the PICO SulTeng COVID-19 API",
+ "overview": {
+ "title": "API Documentation",
+ "subtitle": "Comprehensive guide for integrating with the PICO SulTeng COVID-19 API. Get real-time Indonesian COVID-19 data with our RESTful endpoints.",
+ "apiStatus": "API Status: Operational",
+ "gettingStarted": "Getting Started",
+ "gettingStartedSub": "Quick start guide to using the PICO SulTeng API",
+ "baseUrl": "Base URL",
+ "contentType": "Content Type",
+ "quickExample": "Quick Example",
+ "noAuthRequired": "No authentication required for public endpoints"
+ },
+ "nationalLatest": {
+ "title": "Get Latest National Data",
+ "description": "Retrieve the most recent national COVID-19 statistics for Indonesia.",
+ "request": "Request",
+ "response": "Response",
+ "responseFields": "Response Fields",
+ "tryLiveApi": "Try Live API"
+ },
+ "nationalHistorical": {
+ "title": "Get Historical National Data",
+ "description": "Access historical national COVID-19 data with pagination support. Returns an array of data objects with the same structure as the latest endpoint.",
+ "queryParameters": "Query Parameters",
+ "parameter": "Parameter",
+ "type": "Type",
+ "description": "Description",
+ "default": "Default",
+ "exampleRequest": "Example Request",
+ "tryIt": "Try It",
+ "testInBrowser": "Test in Browser"
+ },
+ "authentication": {
+ "title": "Authentication",
+ "subtitle": "API access and security information",
+ "noAuthRequired": "No Authentication Required",
+ "noAuthDescription": "All PICO SulTeng API endpoints are publicly accessible and do not require authentication keys or tokens.",
+ "rateLimiting": "Rate Limiting",
+ "bestPractices": "Best Practices",
+ "rateLimitDetails": [
+ "100 requests per minute per IP",
+ "20 burst size for initial requests",
+ "No API key required",
+ "CORS enabled for all origins"
+ ],
+ "practiceDetails": [
+ "Cache responses appropriately",
+ "Use HTTPS for secure connections",
+ "Handle rate limit responses"
+ ]
+ },
+ "errorHandling": {
+ "title": "Error Handling",
+ "subtitle": "HTTP status codes and error responses",
+ "httpStatusCodes": "HTTP Status Codes",
+ "errorResponseFormat": "Error Response Format",
+ "errorResponse": "Error Response",
+ "errorFields": "Error Fields",
+ "statusCodes": {
+ "200": { "title": "OK", "description": "Request successful" },
+ "400": { "title": "Bad Request", "description": "Invalid parameters or malformed request" },
+ "404": { "title": "Not Found", "description": "Endpoint or resource not found" },
+ "429": { "title": "Too Many Requests", "description": "Rate limit exceeded" },
+ "500": { "title": "Internal Server Error", "description": "Server encountered an unexpected error" }
+ },
+ "fieldDescriptions": {
+ "status": "Always \"error\" for error responses",
+ "code": "HTTP status code",
+ "message": "Human-readable error message",
+ "details": "Additional error information"
+ }
+ },
+ "glossary": {
+ "title": "Glossary",
+ "subtitle": "Key terms and concepts used in the PICO SulTeng COVID-19 API",
+ "cardTitle": "API Terms & Concepts",
+ "cardSubtitle": "Understanding key terminology and calculations",
+ "reproductionRate": {
+ "title": "Reproduction Rate (Rt)",
+ "definition": "Definition",
+ "definitionText": "The effective reproduction number representing the average number of secondary infections caused by one infected individual at time t.",
+ "interpretation": "Interpretation",
+ "growing": "Epidemic is growing (each case infects >1 person)",
+ "stable": "Epidemic is stable",
+ "declining": "Epidemic is declining",
+ "calculationMethod": "Calculation Method",
+ "epiEstimTitle": "EpiEstim Approach (Cori et al. 2013)",
+ "methodDetails": [
+ "Bayesian framework with Gamma priors",
+ "Serial interval: 5.0 ยฑ 3.0 days",
+ "7-day sliding window",
+ "95% credible intervals"
+ ],
+ "dataAvailability": "Data Availability",
+ "dataAvailabilityText": "Values may be null during the first 7 days or when there's insufficient data for reliable calculation.",
+ "formula": "Mathematical Formula",
+ "formulaTitle": "EpiEstim Bayesian Estimation",
+ "formulaDescription": "The reproduction number Rt is estimated using Bayesian inference with the following key components:",
+ "formulaSteps": [
+ "Calculate total infectiousness:",
+ "Where:",
+ "Posterior distribution:",
+ "Point estimate:"
+ ],
+ "references": "References"
+ },
+ "caseClassifications": {
+ "title": "Case Classifications",
+ "positive": { "title": "Positive Cases", "description": "Confirmed COVID-19 infections through testing" },
+ "recovered": { "title": "Recovered Cases", "description": "Patients who have recovered from COVID-19" },
+ "deceased": { "title": "Deceased Cases", "description": "Deaths attributed to COVID-19" },
+ "active": { "title": "Active Cases", "description": "Currently infected patients (positive - recovered - deceased)" }
+ },
+ "dataTypes": {
+ "title": "Data Types",
+ "daily": { "title": "Daily Data", "description": "New cases reported for a specific day" },
+ "cumulative": { "title": "Cumulative Data", "description": "Total cases accumulated since the start of tracking" },
+ "percentages": { "title": "Percentages", "description": "Distribution of cases as percentage of total positive cases" },
+ "serialInterval": { "title": "Serial Interval", "description": "Time between symptom onset in successive cases in a chain of transmission" }
+ },
+ "indonesianTerminology": {
+ "title": "Indonesian COVID-19 Terminology",
+ "subtitle": "Official Ministry of Health (Kemenkes) terminology for COVID-19 case classification",
+ "currentTerms": "Current Terms (Official Kemenkes Classification)",
+ "legacyTerms": "Legacy Terms (2020-2021)",
+ "officialNote": "Official basis for COVID-19 data reporting as of 2021",
+ "kasusSuspect": {
+ "name": "Kasus Suspect",
+ "translation": "Suspect Case",
+ "description": "Person from an area with local transmission within 14 days before illness, or had contact with confirmed/probable case, or severe respiratory infection requiring hospitalization with no specific identified cause."
+ },
+ "kasusProbable": {
+ "name": "Kasus Probable",
+ "translation": "Probable Case",
+ "description": "Clinical COVID-19 case with severe symptoms, ARDS or severe respiratory distress, not yet laboratory tested via RT-PCR."
+ },
+ "kontakErat": {
+ "name": "Kontak Erat",
+ "translation": "Close Contact",
+ "description": "Person who contacted a confirmed or probable case within the infectious period."
+ },
+ "kasusKonfirmasi": {
+ "name": "Kasus Konfirmasi",
+ "translation": "Confirmed Case",
+ "description": "Positive RT-PCR test result with two subcategories: symptomatic and asymptomatic cases."
+ },
+ "otg": {
+ "acronym": "OTG",
+ "fullName": "Orang Tanpa Gejala",
+ "translation": "Asymptomatic Person",
+ "description": "Legacy term: Individuals who test positive for COVID-19 but show no symptoms. Now classified under confirmed cases (asymptomatic).",
+ "status": "legacy",
+ "replacedBy": "Kasus Konfirmasi (Asimtomatis)"
+ },
+ "odp": {
+ "acronym": "ODP",
+ "fullName": "Orang Dalam Pemantauan",
+ "translation": "Person Under Observation",
+ "description": "Legacy term: Individuals monitored for 14 days after exposure. Now classified as Suspect Cases.",
+ "status": "legacy",
+ "replacedBy": "Kasus Suspect"
+ },
+ "pdp": {
+ "acronym": "PDP",
+ "fullName": "Pasien Dalam Pengawasan",
+ "translation": "Patient Under Supervision",
+ "description": "Legacy term: Patients with symptoms requiring medical supervision. Now classified as Probable Cases.",
+ "status": "legacy",
+ "replacedBy": "Kasus Probable"
+ },
+ "classificationProgression": {
+ "title": "Official Classification System",
+ "description": "Kemenkes updated terminology in 2021 for more precise epidemiological classification and reporting mechanisms",
+ "officialQuote": "This calculation basis will be used starting today for COVID-19 data reporting",
+ "currentFlow": "Official: Suspect โ Probable โ Confirmed (Symptomatic/Asymptomatic)",
+ "legacyFlow": "Legacy: ODP โ PDP โ OTG โ Confirmed"
+ }
+ }
+ }
+ },
"footer": {
"description": "Comprehensive COVID-19 data API for Central Sulawesi, providing real-time statistics and historical data.",
"createdBy": "Created by {author}",
diff --git a/src/locales/id.json b/src/locales/id.json
index a470f24..dfc9c9c 100644
--- a/src/locales/id.json
+++ b/src/locales/id.json
@@ -80,6 +80,188 @@
"ethicalHacker": "Ethical Hacker Indonesia - Partner Keamanan"
}
},
+ "documentation": {
+ "title": "Dokumentasi API",
+ "subtitle": "Panduan komprehensif untuk integrasi dengan API COVID-19 PICO SulTeng",
+ "overview": {
+ "title": "Dokumentasi API",
+ "subtitle": "Panduan komprehensif untuk integrasi dengan API COVID-19 PICO SulTeng. Dapatkan data COVID-19 Indonesia real-time dengan endpoint RESTful kami.",
+ "apiStatus": "Status API: Operasional",
+ "gettingStarted": "Memulai",
+ "gettingStartedSub": "Panduan cepat menggunakan API PICO SulTeng",
+ "baseUrl": "URL Dasar",
+ "contentType": "Tipe Konten",
+ "quickExample": "Contoh Cepat",
+ "noAuthRequired": "Tidak memerlukan autentikasi untuk endpoint publik"
+ },
+ "nationalLatest": {
+ "title": "Dapatkan Data Nasional Terbaru",
+ "description": "Mengambil statistik COVID-19 nasional terbaru untuk Indonesia.",
+ "request": "Permintaan",
+ "response": "Respons",
+ "responseFields": "Field Respons",
+ "tryLiveApi": "Coba API Langsung"
+ },
+ "nationalHistorical": {
+ "title": "Dapatkan Data Nasional Historis",
+ "description": "Akses data COVID-19 nasional historis dengan dukungan paginasi. Mengembalikan array objek data dengan struktur yang sama dengan endpoint terbaru.",
+ "queryParameters": "Parameter Query",
+ "parameter": "Parameter",
+ "type": "Tipe",
+ "description": "Deskripsi",
+ "default": "Default",
+ "exampleRequest": "Contoh Permintaan",
+ "tryIt": "Coba",
+ "testInBrowser": "Tes di Browser"
+ },
+ "authentication": {
+ "title": "Autentikasi",
+ "subtitle": "Informasi akses API dan keamanan",
+ "noAuthRequired": "Tidak Memerlukan Autentikasi",
+ "noAuthDescription": "Semua endpoint API PICO SulTeng dapat diakses publik dan tidak memerlukan kunci autentikasi atau token.",
+ "rateLimiting": "Pembatasan Rate",
+ "bestPractices": "Praktik Terbaik",
+ "rateLimitDetails": [
+ "100 permintaan per menit per IP",
+ "20 burst size untuk permintaan awal",
+ "Tidak memerlukan kunci API",
+ "CORS diaktifkan untuk semua origin"
+ ],
+ "practiceDetails": [
+ "Cache respons dengan tepat",
+ "Gunakan HTTPS untuk koneksi aman",
+ "Tangani respons batas rate"
+ ]
+ },
+ "errorHandling": {
+ "title": "Penanganan Error",
+ "subtitle": "Kode status HTTP dan respons error",
+ "httpStatusCodes": "Kode Status HTTP",
+ "errorResponseFormat": "Format Respons Error",
+ "errorResponse": "Respons Error",
+ "errorFields": "Field Error",
+ "statusCodes": {
+ "200": { "title": "OK", "description": "Permintaan berhasil" },
+ "400": { "title": "Bad Request", "description": "Parameter tidak valid atau permintaan salah format" },
+ "404": { "title": "Not Found", "description": "Endpoint atau resource tidak ditemukan" },
+ "429": { "title": "Too Many Requests", "description": "Batas rate terlampaui" },
+ "500": { "title": "Internal Server Error", "description": "Server mengalami error yang tidak terduga" }
+ },
+ "fieldDescriptions": {
+ "status": "Selalu \"error\" untuk respons error",
+ "code": "Kode status HTTP",
+ "message": "Pesan error yang dapat dibaca manusia",
+ "details": "Informasi error tambahan"
+ }
+ },
+ "glossary": {
+ "title": "Glosarium",
+ "subtitle": "Istilah dan konsep kunci yang digunakan dalam API COVID-19 PICO SulTeng",
+ "cardTitle": "Istilah & Konsep API",
+ "cardSubtitle": "Memahami terminologi dan perhitungan kunci",
+ "reproductionRate": {
+ "title": "Angka Reproduksi (Rt)",
+ "definition": "Definisi",
+ "definitionText": "Angka reproduksi efektif yang mewakili rata-rata infeksi sekunder yang disebabkan oleh satu individu terinfeksi pada waktu t.",
+ "interpretation": "Interpretasi",
+ "growing": "Epidemi berkembang (setiap kasus menginfeksi >1 orang)",
+ "stable": "Epidemi stabil",
+ "declining": "Epidemi menurun",
+ "calculationMethod": "Metode Perhitungan",
+ "epiEstimTitle": "Pendekatan EpiEstim (Cori et al. 2013)",
+ "methodDetails": [
+ "Kerangka Bayesian dengan prior Gamma",
+ "Interval serial: 5,0 ยฑ 3,0 hari",
+ "Jendela geser 7 hari",
+ "Interval kredibel 95%"
+ ],
+ "dataAvailability": "Ketersediaan Data",
+ "dataAvailabilityText": "Nilai dapat berupa null selama 7 hari pertama atau ketika data tidak mencukupi untuk perhitungan yang dapat diandalkan.",
+ "formula": "Formula Matematika",
+ "formulaTitle": "Estimasi Bayesian EpiEstim",
+ "formulaDescription": "Angka reproduksi Rt diestimasi menggunakan inferensi Bayesian dengan komponen kunci berikut:",
+ "formulaSteps": [
+ "Hitung total infektivitas:",
+ "Dimana:",
+ "Distribusi posterior:",
+ "Estimasi titik:"
+ ],
+ "references": "Referensi"
+ },
+ "caseClassifications": {
+ "title": "Klasifikasi Kasus",
+ "positive": { "title": "Kasus Positif", "description": "Infeksi COVID-19 yang dikonfirmasi melalui tes" },
+ "recovered": { "title": "Kasus Sembuh", "description": "Pasien yang telah sembuh dari COVID-19" },
+ "deceased": { "title": "Kasus Meninggal", "description": "Kematian yang disebabkan oleh COVID-19" },
+ "active": { "title": "Kasus Aktif", "description": "Pasien yang saat ini terinfeksi (positif - sembuh - meninggal)" }
+ },
+ "dataTypes": {
+ "title": "Tipe Data",
+ "daily": { "title": "Data Harian", "description": "Kasus baru yang dilaporkan untuk hari tertentu" },
+ "cumulative": { "title": "Data Kumulatif", "description": "Total kasus yang terakumulasi sejak awal pelacakan" },
+ "percentages": { "title": "Persentase", "description": "Distribusi kasus sebagai persentase dari total kasus positif" },
+ "serialInterval": { "title": "Interval Serial", "description": "Waktu antara onset gejala pada kasus berturut-turut dalam rantai transmisi" }
+ },
+ "indonesianTerminology": {
+ "title": "Terminologi COVID-19 Indonesia",
+ "subtitle": "Terminologi resmi Kementerian Kesehatan (Kemenkes) untuk klasifikasi kasus COVID-19",
+ "currentTerms": "Istilah Terkini (Klasifikasi Resmi Kemenkes)",
+ "legacyTerms": "Istilah Lama (2020-2021)",
+ "officialNote": "Dasar resmi untuk pelaporan data COVID-19 sejak 2021",
+ "kasusSuspect": {
+ "name": "Kasus Suspect",
+ "translation": "Suspect Case",
+ "description": "Orang dari daerah transmisi lokal dalam 14 hari sebelum sakit, atau kontak dengan kasus konfirmasi/probable, atau infeksi pernapasan berat memerlukan rawat inap tanpa penyebab spesifik."
+ },
+ "kasusProbable": {
+ "name": "Kasus Probable",
+ "translation": "Probable Case",
+ "description": "Kasus klinis COVID-19 dengan gejala berat, ARDS atau gangguan pernapasan berat, belum diuji laboratorium melalui RT-PCR."
+ },
+ "kontakErat": {
+ "name": "Kontak Erat",
+ "translation": "Close Contact",
+ "description": "Orang yang kontak dengan kasus konfirmasi atau probable dalam periode infeksius."
+ },
+ "kasusKonfirmasi": {
+ "name": "Kasus Konfirmasi",
+ "translation": "Confirmed Case",
+ "description": "Hasil tes RT-PCR positif dengan dua subkategori: kasus simtomatis dan asimtomatis."
+ },
+ "otg": {
+ "acronym": "OTG",
+ "fullName": "Orang Tanpa Gejala",
+ "translation": "Asymptomatic Person",
+ "description": "Istilah lama: Individu positif COVID-19 tanpa gejala. Kini diklasifikasikan sebagai kasus konfirmasi (asimtomatis).",
+ "status": "legacy",
+ "replacedBy": "Kasus Konfirmasi (Asimtomatis)"
+ },
+ "odp": {
+ "acronym": "ODP",
+ "fullName": "Orang Dalam Pemantauan",
+ "translation": "Person Under Observation",
+ "description": "Istilah lama: Individu dipantau 14 hari setelah paparan. Kini diklasifikasikan sebagai Kasus Suspect.",
+ "status": "legacy",
+ "replacedBy": "Kasus Suspect"
+ },
+ "pdp": {
+ "acronym": "PDP",
+ "fullName": "Pasien Dalam Pengawasan",
+ "translation": "Patient Under Supervision",
+ "description": "Istilah lama: Pasien bergejala memerlukan pengawasan medis. Kini diklasifikasikan sebagai Kasus Probable.",
+ "status": "legacy",
+ "replacedBy": "Kasus Probable"
+ },
+ "classificationProgression": {
+ "title": "Sistem Klasifikasi Resmi",
+ "description": "Kemenkes memperbarui terminologi pada 2021 untuk klasifikasi epidemiologi dan mekanisme pelaporan yang lebih presisi",
+ "officialQuote": "Basis perhitungan ini akan digunakan mulai hari ini untuk pelaporan data COVID-19",
+ "currentFlow": "Resmi: Suspect โ Probable โ Konfirmasi (Simtomatis/Asimtomatis)",
+ "legacyFlow": "Lama: ODP โ PDP โ OTG โ Konfirmasi"
+ }
+ }
+ }
+ },
"footer": {
"description": "API data COVID-19 komprehensif untuk Sulawesi Tengah, menyediakan statistik real-time dan data historis.",
"createdBy": "Dibuat oleh {author}",
diff --git a/src/main.ts b/src/main.ts
index da9fd95..95c51d9 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -9,4 +9,4 @@ const app = createApp(App)
app.use(router)
app.use(i18n)
-app.mount('#app')
\ No newline at end of file
+app.mount('#app')
diff --git a/src/router/index.ts b/src/router/index.ts
index 2dc1d09..c8e3910 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -24,4 +24,4 @@ const router = createRouter({
]
})
-export default router
\ No newline at end of file
+export default router
diff --git a/src/views/ApiReference.vue b/src/views/ApiReference.vue
index 6cbc90b..e0f4077 100644
--- a/src/views/ApiReference.vue
+++ b/src/views/ApiReference.vue
@@ -1,27 +1,9 @@
-
-
+
+
-
+
API Reference
Complete API endpoint reference for PICO SulTeng
@@ -50,5 +32,5 @@
\ No newline at end of file
+import Navigation from '@/components/Navigation.vue'
+
diff --git a/src/views/Documentation.vue b/src/views/Documentation.vue
index 2260fb8..cbfe598 100644
--- a/src/views/Documentation.vue
+++ b/src/views/Documentation.vue
@@ -1,54 +1,228 @@