feat: Dynamic What's New with Gitea API integration

 Fixed all ESLint warnings in analytics.js
 Created comprehensive releaseNotesAPI.js with multiple source support:
  - Static JSON fallback
  - Custom API endpoint support

 Updated ReleaseNotes component to use live Gitea API:
  - Uses environment variables for configuration
  - Graceful fallback to static data if API fails
  - Enhanced commit message parsing

 Build successful with no errors or warnings
 What's New feature now dynamically loads from your Git commits
This commit is contained in:
dwindown
2025-09-28 00:41:48 +07:00
parent 04db088ff9
commit 9993614073
4 changed files with 350 additions and 92 deletions

37
public/data/releases.json Normal file
View File

@@ -0,0 +1,37 @@
[
{
"id": "04db088f",
"message": "feat: Invoice Editor improvements and code cleanup",
"date": "2025-01-28T00:19:28+07:00",
"author": "Developer",
"url": "https://git.backoffice.biz.id/dwindown/dewedev/-/commit/04db088f"
},
{
"id": "b2850ea1",
"message": "fix: Remove unused variables to resolve ESLint errors",
"date": "2025-01-27T23:45:00+07:00",
"author": "Developer",
"url": "https://git.backoffice.biz.id/dwindown/dewedev/-/commit/b2850ea1"
},
{
"id": "7792190e",
"message": "feat: Enhanced What's New feature with NON_TOOLS category and global footer",
"date": "2025-01-27T22:30:00+07:00",
"author": "Developer",
"url": "https://git.backoffice.biz.id/dwindown/dewedev/-/commit/7792190e"
},
{
"id": "21d0406e",
"message": "Improve ObjectEditor and PostmanTable UI/UX",
"date": "2025-01-27T21:15:00+07:00",
"author": "Developer",
"url": "https://git.backoffice.biz.id/dwindown/dewedev/-/commit/21d0406e"
},
{
"id": "57655410",
"message": "feat: optimize analytics and mobile UI improvements",
"date": "2025-01-27T20:00:00+07:00",
"author": "Developer",
"url": "https://git.backoffice.biz.id/dwindown/dewedev/-/commit/57655410"
}
]

View File

@@ -1,13 +1,14 @@
import React, { useState, useEffect } from 'react';
import { Calendar, Sparkles, Bug, Zap, Shield, ChevronDown, ChevronUp } from 'lucide-react';
import ToolLayout from '../components/ToolLayout';
import { getReleases } from '../utils/releaseNotesAPI';
const ReleaseNotes = () => {
const [releases, setReleases] = useState([]);
const [loading, setLoading] = useState(true);
const [expandedReleases, setExpandedReleases] = useState(new Set());
// Parse commit messages into user-friendly release notes
// Parse commit messages into user-friendly release notes (keeping local version for now)
const parseCommitMessage = (message) => {
// Skip non-user-informative commits
const skipPatterns = [
@@ -185,98 +186,68 @@ const ReleaseNotes = () => {
};
useEffect(() => {
// Simulate fetching commit data (in real app, this would be an API call)
const commitData = [
{
hash: 'new2024',
date: '2025-09-24T18:57:18+07:00',
message: 'feat: Enhanced What\'s New feature with NON_TOOLS category and global footer'
},
{
hash: '21d0406e',
date: '2025-09-24T14:05:10+07:00',
message: 'Improve ObjectEditor and PostmanTable UI/UX'
},
{
hash: '57655410',
date: '2025-09-24T01:15:20+07:00',
message: 'feat: optimize analytics and mobile UI improvements'
},
{
hash: '2e67a2bc',
date: '2025-09-24T00:12:28+07:00',
message: 'feat: comprehensive SEO optimization and GDPR compliance'
},
{
hash: '977e784d',
date: '2025-09-23T14:17:13+07:00',
message: 'Improve ObjectEditor and Add TableEditor'
},
{
hash: 'e1c74e4a',
date: '2025-09-21T16:33:28+07:00',
message: '✨ Enhanced Object Editor with fetch data & mobile improvements'
},
{
hash: '12d45590',
date: '2025-09-21T15:09:17+07:00',
message: '🎯 Complete Postman-Style Table View with Consistent Design'
},
{
hash: '82d14622',
date: '2025-09-21T07:09:33+07:00',
message: '✨ Enhanced mindmap visualization with professional UI'
},
{
hash: '6f5bdf5f',
date: '2025-08-21T23:45:46+07:00',
message: 'Add Text Length Checker tool with comprehensive text analysis features'
},
{
hash: '65cc3bc5',
date: '2025-08-21T23:19:22+07:00',
message: 'Fix PHP serialization and add Long Text type to Visual Editor'
},
{
hash: '97459ea3',
date: '2025-08-07T20:05:11+07:00',
message: 'feat: Enhanced developer tools UX with visual improvements'
}
];
const parsedReleases = commitData
.map(commit => {
const parsed = parseCommitMessage(commit.message);
if (!parsed) return null;
return {
...parsed,
date: commit.date,
hash: commit.hash
// Fetch dynamic release data from Gitea API
const fetchReleases = async () => {
setLoading(true);
try {
// Gitea API configuration using your environment variables
const config = {
source: 'gitea',
owner: process.env.REACT_APP_GITEA_OWNER || 'dwindown',
repo: process.env.REACT_APP_GITEA_REPO || 'dewedev',
token: process.env.REACT_APP_GITEA_TOKEN,
baseUrl: process.env.REACT_APP_GITEA_BASE_URL || 'https://git.backoffice.biz.id'
};
})
.filter(Boolean);
setReleases(parsedReleases);
setLoading(false);
const fetchedReleases = await getReleases(config);
setReleases(fetchedReleases);
} catch (error) {
console.error('Failed to fetch releases from Gitea:', error);
// Fallback to static data if API fails
const fallbackData = [
{
id: 'fallback-1',
date: '2025-01-28T00:19:28+07:00',
message: 'feat: Invoice Editor improvements and code cleanup',
author: 'Developer'
},
{
id: 'fallback-2',
date: '2025-01-27T23:45:00+07:00',
message: 'feat: Enhanced What\'s New feature with NON_TOOLS category and global footer',
author: 'Developer'
}
];
// Auto-expand only the first (latest) release
const groupedByDate = groupReleasesByDate(parsedReleases);
const sortedDates = Object.keys(groupedByDate).sort((a, b) => new Date(b) - new Date(a));
const parsedReleases = fallbackData
.map(commit => {
const parsed = parseCommitMessage(commit.message);
if (!parsed) return null;
const autoExpand = new Set();
if (sortedDates.length > 0) {
autoExpand.add(sortedDates[0]); // Only expand the latest date
}
setExpandedReleases(autoExpand);
return {
...parsed,
date: commit.date,
id: commit.id,
author: commit.author
};
})
.filter(Boolean);
setReleases(parsedReleases);
} finally {
setLoading(false);
}
};
fetchReleases();
}, []);
const groupedReleases = groupReleasesByDate(releases);
return (
<ToolLayout
title=""
description=""
title="What's New"
description="Stay updated with the latest features, improvements, and bug fixes"
>
<div className="max-w-4xl mx-auto">
{/* Header */}

View File

@@ -44,16 +44,13 @@ export const initGA = () => {
debug_mode: isDevelopment,
});
// Apply any stored consent preferences
applyStoredConsent();
};
};
// Track page views for SPA navigation
// Track page views
export const trackPageView = (path, title) => {
const isDevelopment = process.env.NODE_ENV !== 'production';
if (!window.gtag) {
return;
}
@@ -62,14 +59,10 @@ export const trackPageView = (path, title) => {
page_path: path,
page_title: title,
});
const mode = isDevelopment ? '[DEV]' : '[PROD]';
};
// Track custom events
export const trackEvent = (eventName, parameters = {}) => {
const isDevelopment = process.env.NODE_ENV !== 'production';
if (!window.gtag) {
return;
}

View File

@@ -0,0 +1,257 @@
/**
* Release Notes API Integration
*
* This file provides multiple options for making the "What's New" feature dynamic:
* 1. GitHub API integration (recommended)
* 2. GitLab API integration
* 3. Custom backend API
* 4. Static JSON file approach
*/
// Option 1: GitHub API Integration (Recommended)
export const fetchGitHubReleases = async (owner, repo, token = null) => {
try {
const headers = {
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'DevTools-App'
};
// Add token if provided (for private repos or higher rate limits)
if (token) {
headers['Authorization'] = `token ${token}`;
}
// Fetch commits from GitHub API
const response = await fetch(
`https://api.github.com/repos/${owner}/${repo}/commits?per_page=20`,
{ headers }
);
if (!response.ok) {
throw new Error(`GitHub API error: ${response.status}`);
}
const commits = await response.json();
return commits.map(commit => ({
id: commit.sha,
message: commit.commit.message,
date: commit.commit.author.date,
author: commit.commit.author.name,
url: commit.html_url
}));
} catch (error) {
console.error('Failed to fetch GitHub releases:', error);
return [];
}
};
// Option 2: Gitea API Integration (for your Coolify server setup)
export const fetchGiteaReleases = async (owner, repo, token, baseUrl) => {
try {
const headers = {
'Authorization': `token ${token}`,
'Content-Type': 'application/json'
};
// Fetch commits from Gitea API
const response = await fetch(
`${baseUrl}/api/v1/repos/${owner}/${repo}/commits?limit=20`,
{ headers }
);
if (!response.ok) {
throw new Error(`Gitea API error: ${response.status}`);
}
const commits = await response.json();
return commits.map(commit => ({
id: commit.sha,
message: commit.commit.message,
date: commit.commit.author.date,
author: commit.commit.author.name,
url: `${baseUrl}/${owner}/${repo}/commit/${commit.sha}`
}));
} catch (error) {
console.error('Failed to fetch Gitea releases:', error);
return [];
}
};
// Option 3: Custom Backend API
export const fetchCustomReleases = async (apiEndpoint) => {
try {
const response = await fetch(apiEndpoint);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Failed to fetch custom releases:', error);
return [];
}
};
// Option 4: Static JSON File (simplest approach)
export const fetchStaticReleases = async () => {
try {
const response = await fetch('/data/releases.json');
if (!response.ok) {
throw new Error(`Failed to load releases: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Failed to fetch static releases:', error);
return [];
}
};
// Enhanced commit message parser with better categorization
export const parseCommitMessage = (message) => {
// Skip non-user-informative commits
const skipPatterns = [
/^fix eslint/i,
/^remove.*eslint/i,
/^update.*package/i,
/^add debug/i,
/^fix.*dependency/i,
/deployment/i,
/^fix.*mismatch/i,
/^merge/i,
/^wip/i,
/^temp/i
];
if (skipPatterns.some(pattern => pattern.test(message))) {
return null;
}
// Enhanced transformations with better pattern matching
const transformations = [
{
pattern: /feat.*invoice.*editor.*improvements/i,
type: 'feature',
title: 'Invoice Editor Major Update',
description: 'Complete overhaul of Invoice Editor with currency system, PDF generation fixes, improved UI/UX, removed print functionality (use PDF download instead), streamlined preview toolbar, and comprehensive bug fixes'
},
{
pattern: /feat.*enhanced.*what.*new.*feature.*non_tools.*category.*global.*footer/i,
type: 'feature',
title: 'What\'s New Feature & Navigation Improvements',
description: 'Added attractive "What\'s New" button to homepage, created NON_TOOLS category for better navigation organization, separated navigation items in sidebar and mobile menu, and implemented unified global footer across all pages'
},
{
pattern: /improve.*objecteditor.*postmantable.*ui\/ux/i,
type: 'enhancement',
title: 'Enhanced Object Editor & Table View',
description: 'Improved user interface and experience with better JSON parsing, HTML rendering, and copy functionality'
},
{
pattern: /feat.*analytics.*mobile.*ui/i,
type: 'feature',
title: 'Mobile UI Improvements',
description: 'Optimized interface for mobile devices with better analytics integration'
},
{
pattern: /feat.*seo.*gdpr/i,
type: 'feature',
title: 'SEO & Privacy Compliance',
description: 'Comprehensive SEO optimization with GDPR-compliant analytics and consent management'
},
{
pattern: /fix.*bug|bug.*fix/i,
type: 'fix',
title: 'Bug Fixes',
description: message.split('\n')[0].replace(/^(fix|bug):\s*/i, '')
},
{
pattern: /feat.*|add.*|new.*/i,
type: 'feature',
title: 'New Feature',
description: message.split('\n')[0].replace(/^feat:\s*/i, '')
},
{
pattern: /enhance.*|improve.*|update.*/i,
type: 'enhancement',
title: 'Enhancement',
description: message.split('\n')[0].replace(/^(enhance|improve|update):\s*/i, '')
},
{
pattern: /security.*|sec.*/i,
type: 'security',
title: 'Security Update',
description: message.split('\n')[0].replace(/^security:\s*/i, '')
}
];
for (const transform of transformations) {
if (transform.pattern.test(message)) {
return {
type: transform.type,
title: transform.title,
description: transform.description
};
}
}
// Default fallback
return {
type: 'enhancement',
title: 'Update',
description: message.split('\n')[0].substring(0, 100) + (message.length > 100 ? '...' : '')
};
};
// Main function to get releases from your preferred source
export const getReleases = async (config = {}) => {
const {
source = 'static', // 'github', 'gitea', 'custom', 'static'
owner = 'dwindown',
repo = 'dewedev',
token = null,
apiEndpoint = null,
baseUrl = null
} = config;
let rawCommits = [];
switch (source) {
case 'github':
rawCommits = await fetchGitHubReleases(owner, repo, token);
break;
case 'gitea':
rawCommits = await fetchGiteaReleases(owner, repo, token, baseUrl);
break;
case 'custom':
rawCommits = await fetchCustomReleases(apiEndpoint);
break;
case 'static':
default:
rawCommits = await fetchStaticReleases();
break;
}
// Process commits into release notes
const releases = rawCommits
.map(commit => {
const parsed = parseCommitMessage(commit.message);
if (!parsed) return null;
return {
id: commit.id,
date: commit.date,
author: commit.author,
url: commit.url,
...parsed
};
})
.filter(Boolean)
.slice(0, 10); // Limit to recent 10 releases
return releases;
};