- Updated release notes to use new JSON structure with individual commit timestamps - Removed hash display from release notes for cleaner UI - Fixed automatic recalculation of percentage-based installments in Invoice Editor and Preview - Integrated custom logo.svg in header and footer with cleaner styling - Moved all data files to /public/data/ for better organization - Cleaned up unused release data files and improved file structure
359 lines
14 KiB
JavaScript
359 lines
14 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import { Calendar, Sparkles, Bug, Zap, Shield, ChevronDown, ChevronUp } from 'lucide-react';
|
|
import ToolLayout from '../components/ToolLayout';
|
|
|
|
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 (keeping local version for now)
|
|
// eslint-disable-next-line no-unused-vars
|
|
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
|
|
];
|
|
|
|
if (skipPatterns.some(pattern => pattern.test(message))) {
|
|
return null;
|
|
}
|
|
|
|
// Transform commit messages to user-friendly descriptions
|
|
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 and GDPR compliance features for better discoverability and privacy protection'
|
|
},
|
|
{
|
|
pattern: /improve.*objecteditor.*tableeditor/i,
|
|
type: 'enhancement',
|
|
title: 'Enhanced Data Editors',
|
|
description: 'Major improvements to Object Editor and new Table Editor with advanced data manipulation features'
|
|
},
|
|
{
|
|
pattern: /enhanced.*object.*editor.*fetch.*mobile/i,
|
|
type: 'feature',
|
|
title: 'Object Editor with Data Fetching',
|
|
description: 'Added ability to fetch data from URLs directly in Object Editor with mobile-optimized interface'
|
|
},
|
|
{
|
|
pattern: /complete.*postman.*table.*view/i,
|
|
type: 'feature',
|
|
title: 'Postman-Style Table View',
|
|
description: 'New professional table visualization with consistent design and advanced data exploration features'
|
|
},
|
|
{
|
|
pattern: /enhanced.*mindmap.*visualization/i,
|
|
type: 'feature',
|
|
title: 'Professional Mindmap Visualization',
|
|
description: 'Beautiful mindmap interface for visualizing complex data structures with interactive navigation'
|
|
},
|
|
{
|
|
pattern: /add.*text.*length.*checker/i,
|
|
type: 'feature',
|
|
title: 'Text Analysis Tool',
|
|
description: 'New comprehensive text analysis tool with length checking and detailed text statistics'
|
|
},
|
|
{
|
|
pattern: /fix.*php.*serialization.*long.*text/i,
|
|
type: 'fix',
|
|
title: 'PHP Serialization Improvements',
|
|
description: 'Fixed PHP serialization handling and added support for long text fields in Visual Editor'
|
|
},
|
|
{
|
|
pattern: /enhanced.*developer.*tools.*ux/i,
|
|
type: 'enhancement',
|
|
title: 'Developer Tools UX Enhancement',
|
|
description: 'Improved overall user experience with visual enhancements and better tool organization'
|
|
}
|
|
];
|
|
|
|
for (const transform of transformations) {
|
|
if (transform.pattern.test(message)) {
|
|
return {
|
|
type: transform.type,
|
|
title: transform.title,
|
|
description: transform.description
|
|
};
|
|
}
|
|
}
|
|
|
|
// Fallback for unmatched patterns
|
|
if (message.includes('🐛') || message.toLowerCase().includes('fix')) {
|
|
return {
|
|
type: 'fix',
|
|
title: 'Bug Fixes',
|
|
description: message.replace(/🐛|fix/gi, '').trim()
|
|
};
|
|
}
|
|
|
|
if (message.includes('✨') || message.toLowerCase().includes('feat')) {
|
|
return {
|
|
type: 'feature',
|
|
title: 'New Feature',
|
|
description: message.replace(/✨|feat:/gi, '').trim()
|
|
};
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
// Get type icon and color
|
|
const getTypeConfig = (type) => {
|
|
const configs = {
|
|
feature: {
|
|
icon: <Sparkles className="h-4 w-4" />,
|
|
color: 'text-blue-600 dark:text-blue-400',
|
|
bgColor: 'bg-blue-100 dark:bg-blue-900/20',
|
|
label: 'New Feature'
|
|
},
|
|
enhancement: {
|
|
icon: <Zap className="h-4 w-4" />,
|
|
color: 'text-purple-600 dark:text-purple-400',
|
|
bgColor: 'bg-purple-100 dark:bg-purple-900/20',
|
|
label: 'Enhancement'
|
|
},
|
|
fix: {
|
|
icon: <Bug className="h-4 w-4" />,
|
|
color: 'text-green-600 dark:text-green-400',
|
|
bgColor: 'bg-green-100 dark:bg-green-900/20',
|
|
label: 'Bug Fix'
|
|
},
|
|
security: {
|
|
icon: <Shield className="h-4 w-4" />,
|
|
color: 'text-red-600 dark:text-red-400',
|
|
bgColor: 'bg-red-100 dark:bg-red-900/20',
|
|
label: 'Security'
|
|
}
|
|
};
|
|
return configs[type] || configs.enhancement;
|
|
};
|
|
|
|
// Group releases by date
|
|
const groupReleasesByDate = (releases) => {
|
|
const grouped = {};
|
|
releases.forEach(release => {
|
|
const date = new Date(release.date).toDateString();
|
|
if (!grouped[date]) {
|
|
grouped[date] = [];
|
|
}
|
|
grouped[date].push(release);
|
|
});
|
|
return grouped;
|
|
};
|
|
|
|
const toggleRelease = (date) => {
|
|
const newExpanded = new Set(expandedReleases);
|
|
if (newExpanded.has(date)) {
|
|
newExpanded.delete(date);
|
|
} else {
|
|
newExpanded.add(date);
|
|
}
|
|
setExpandedReleases(newExpanded);
|
|
};
|
|
|
|
useEffect(() => {
|
|
// Load release data from commits.json
|
|
const fetchReleases = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const response = await fetch('/data/commits.json');
|
|
const data = await response.json();
|
|
|
|
// Transform changelog data to release format
|
|
const releases = [];
|
|
data.changelog.forEach(dateEntry => {
|
|
dateEntry.changes.forEach(change => {
|
|
releases.push({
|
|
id: `${dateEntry.date}-${change.type}-${change.title.replace(/\s+/g, '-')}`,
|
|
date: change.datetime || dateEntry.date, // Use datetime if available, fallback to date
|
|
type: change.type,
|
|
title: change.title,
|
|
description: change.description
|
|
});
|
|
});
|
|
});
|
|
|
|
setReleases(releases);
|
|
} catch (error) {
|
|
console.error('Failed to load commits.json:', error);
|
|
setReleases([]);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
fetchReleases();
|
|
}, []);
|
|
|
|
const groupedReleases = groupReleasesByDate(releases);
|
|
|
|
return (
|
|
<ToolLayout
|
|
title="What's New"
|
|
description="Stay updated with the latest features, improvements, and bug fixes"
|
|
>
|
|
<div className="max-w-4xl mx-auto">
|
|
{/* Header */}
|
|
<div className="text-center mb-12">
|
|
<div className="inline-flex items-center justify-center w-16 h-16 bg-gradient-to-br from-blue-500 to-purple-600 rounded-2xl mb-6">
|
|
<Sparkles className="h-8 w-8 text-white" />
|
|
</div>
|
|
<h1 className="text-4xl font-bold text-gray-900 dark:text-gray-100 mb-4">
|
|
What's New
|
|
</h1>
|
|
<p className="text-xl text-gray-600 dark:text-gray-400 max-w-2xl mx-auto">
|
|
Discover the latest features, improvements, and bug fixes that make your development workflow even better.
|
|
</p>
|
|
</div>
|
|
|
|
{loading ? (
|
|
<div className="flex items-center justify-center py-12">
|
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
|
</div>
|
|
) : (
|
|
<div className="relative">
|
|
{/* Timeline line */}
|
|
<div className="absolute left-8 top-0 bottom-0 w-0.5 bg-gradient-to-b from-blue-500 via-purple-500 to-transparent"></div>
|
|
|
|
<div className="space-y-6">
|
|
{Object.entries(groupedReleases)
|
|
.sort(([a], [b]) => new Date(b) - new Date(a))
|
|
.map(([date, dayReleases], index) => {
|
|
const isExpanded = expandedReleases.has(date);
|
|
const releaseDate = new Date(date);
|
|
const isRecent = (new Date() - releaseDate) < 7 * 24 * 60 * 60 * 1000;
|
|
|
|
return (
|
|
<div key={date} className="relative">
|
|
{/* Timeline dot */}
|
|
<div className="absolute left-6 top-6 w-4 h-4 bg-gradient-to-r from-blue-500 to-purple-500 rounded-full border-4 border-white dark:border-gray-900 shadow-lg z-10"></div>
|
|
|
|
<div className="ml-16 bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden">
|
|
{/* Date Header */}
|
|
<button
|
|
onClick={() => toggleRelease(date)}
|
|
className="w-full px-6 py-4 flex items-center justify-between hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors"
|
|
>
|
|
<div className="flex items-center space-x-3">
|
|
<Calendar className="h-5 w-5 text-gray-500 dark:text-gray-400" />
|
|
<div className="text-left">
|
|
<h3 className="font-semibold text-gray-900 dark:text-gray-100">
|
|
{releaseDate.toLocaleDateString('en-US', {
|
|
weekday: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
})}
|
|
</h3>
|
|
<p className="text-sm text-gray-500 dark:text-gray-400">
|
|
{dayReleases.length} update{dayReleases.length !== 1 ? 's' : ''}
|
|
{isRecent && <span className="ml-2 text-blue-600 dark:text-blue-400">• Recent</span>}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
{isExpanded ? (
|
|
<ChevronUp className="h-5 w-5 text-gray-400" />
|
|
) : (
|
|
<ChevronDown className="h-5 w-5 text-gray-400" />
|
|
)}
|
|
</button>
|
|
|
|
{/* Release Items */}
|
|
{isExpanded && (
|
|
<div className="border-t border-gray-200 dark:border-gray-700">
|
|
{dayReleases.map((release, index) => {
|
|
const typeConfig = getTypeConfig(release.type);
|
|
|
|
return (
|
|
<div key={release.hash} className={`p-6 ${index !== dayReleases.length - 1 ? 'border-b border-gray-100 dark:border-gray-700' : ''}`}>
|
|
<div className="flex flex-col md:flex-row items-start md:space-x-4">
|
|
<div className={`flex-shrink-0 p-2 rounded-lg ${typeConfig.bgColor} flex items-center space-x-2 mb-2`}>
|
|
<div className={typeConfig.color}>
|
|
{typeConfig.icon}
|
|
</div>
|
|
<span className={`block md:hidden px-2 py-1 text-xs font-medium rounded-full ${typeConfig.bgColor} ${typeConfig.color}`}>
|
|
{typeConfig.label}
|
|
</span>
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-center space-x-2 mb-2">
|
|
<h4 className="font-semibold text-gray-900 dark:text-gray-100">
|
|
{release.title}
|
|
</h4>
|
|
<span className={`hidden md:block px-2 py-1 text-xs font-medium rounded-full ${typeConfig.bgColor} ${typeConfig.color}`}>
|
|
{typeConfig.label}
|
|
</span>
|
|
</div>
|
|
<p className="text-gray-600 dark:text-gray-400 leading-relaxed">
|
|
{release.description}
|
|
</p>
|
|
<div className="mt-3 flex items-center space-x-4 text-xs text-gray-500 dark:text-gray-400">
|
|
<span>
|
|
{new Date(release.date).toLocaleTimeString('en-US', {
|
|
hour: '2-digit',
|
|
minute: '2-digit'
|
|
})}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Footer */}
|
|
<div className="text-center mt-12 py-8 border-t border-gray-200 dark:border-gray-700">
|
|
<p className="text-gray-500 dark:text-gray-400">
|
|
Stay tuned for more exciting updates and improvements!
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</ToolLayout>
|
|
);
|
|
};
|
|
|
|
export default ReleaseNotes;
|