bookwiz.io / app / changelog / page.tsx
page.tsx
Raw
import fs from 'fs';
import path from 'path';
import { Metadata } from 'next';
import DashboardLayout from '@/components/dashboard/DashboardLayout';
import { ClockIcon, CodeBracketIcon, DocumentTextIcon } from '@heroicons/react/24/outline';

export const metadata: Metadata = {
  title: 'Changelog - Bookwiz',
  description: 'Latest updates and changes to Bookwiz',
};

// Get version from package.json
function getAppVersion() {
  try {
    const packagePath = path.join(process.cwd(), 'package.json');
    const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
    return packageJson.version || '0.1.0';
  } catch (error) {
    return '0.1.0';
  }
}

function parseChangelog(content: string) {
  // Split by version headers (## [version] or ## version)
  const sections = content.split(/(?=^## )/gm).filter(section => section.trim());
  
  return sections.map((section, index) => {
    const lines = section.split('\n');
    const header = lines[0];
    const body = lines.slice(1).join('\n').trim();
    
    // Extract version and date from header
    const versionMatch = header.match(/## \[?([^\]]+)\]?(?:\s*-\s*(.+))?/);
    const version = versionMatch?.[1] || `Section ${index + 1}`;
    const date = versionMatch?.[2] || '';
    
    return {
      version,
      date,
      content: body,
      isTitle: index === 0 && !versionMatch, // First section might be title
    };
  });
}

function formatChangelogContent(content: string) {
  // Parse markdown-style lists and formatting
  const lines = content.split('\n');
  const formattedLines: JSX.Element[] = [];
  
  lines.forEach((line, index) => {
    if (line.trim() === '') {
      formattedLines.push(<br key={index} />);
    } else if (line.startsWith('### ')) {
      const heading = line.replace('### ', '');
      formattedLines.push(
        <h4 key={index} className="text-lg font-bold text-white mt-6 mb-3 first:mt-0">
          {heading}
        </h4>
      );
    } else if (line.startsWith('- ')) {
      const item = line.replace('- ', '');
      formattedLines.push(
        <div key={index} className="flex items-start space-x-3 mb-2">
          <div className="w-1.5 h-1.5 rounded-full bg-blue-400 mt-2 flex-shrink-0"></div>
          <span className="text-gray-300">{item}</span>
        </div>
      );
    } else if (line.trim()) {
      formattedLines.push(
        <p key={index} className="text-gray-300 mb-2">
          {line}
        </p>
      );
    }
  });
  
  return formattedLines;
}

export default function ChangelogPage() {
  const appVersion = getAppVersion();
  let changelog = '';
  let parsedSections: any[] = [];
  
  try {
    const changelogPath = path.join(process.cwd(), 'CHANGELOG.md');
    if (fs.existsSync(changelogPath)) {
      changelog = fs.readFileSync(changelogPath, 'utf8');
      parsedSections = parseChangelog(changelog);
    }
  } catch (error) {
    console.error('Error reading changelog:', error);
  }

  if (!changelog) {
    return (
      <div className="min-h-screen bg-black relative overflow-hidden">
        {/* Modern gradient background */}
        <div className="absolute inset-0 bg-gradient-to-br from-black via-gray-950 to-black pointer-events-none" />
        
        {/* Subtle animated gradient overlay */}
        <div className="absolute inset-0 opacity-30 pointer-events-none">
          <div className="absolute top-0 left-1/4 w-96 h-96 bg-gradient-to-r from-blue-600/20 to-purple-600/20 rounded-full blur-3xl animate-pulse" />
          <div className="absolute bottom-0 right-1/4 w-96 h-96 bg-gradient-to-r from-purple-600/20 to-pink-600/20 rounded-full blur-3xl animate-pulse" style={{ animationDelay: '1s' }} />
        </div>

        <div className="relative z-10">
          <DashboardLayout 
            title="Changelog"
            subtitle={`Track the latest updates and improvements to Bookwiz v${appVersion}`}
          >
            <div className="max-w-4xl mx-auto">
              <div className="bg-white/5 backdrop-blur-sm border border-white/10 rounded-2xl p-8 text-center">
                <DocumentTextIcon className="w-16 h-16 mx-auto mb-4 text-gray-400" />
                <h3 className="text-xl font-bold text-white mb-2">No changelog available yet</h3>
                <p className="text-gray-400 mb-4">
                  The changelog will be automatically generated from commit messages using conventional commits.
                </p>
                <div className="text-sm text-gray-500">
                  <p>Start using conventional commit messages to populate this changelog:</p>
                  <div className="mt-3 space-y-1 text-left max-w-md mx-auto">
                    <code className="block bg-black/30 rounded px-2 py-1">feat: add new feature</code>
                    <code className="block bg-black/30 rounded px-2 py-1">fix: resolve bug</code>
                    <code className="block bg-black/30 rounded px-2 py-1">docs: update documentation</code>
                  </div>
                </div>
              </div>
            </div>
          </DashboardLayout>
        </div>
      </div>
    );
  }

  return (
    <div className="min-h-screen bg-black relative overflow-hidden">
      {/* Modern gradient background */}
      <div className="absolute inset-0 bg-gradient-to-br from-black via-gray-950 to-black pointer-events-none" />
      
      {/* Subtle animated gradient overlay */}
      <div className="absolute inset-0 opacity-30 pointer-events-none">
        <div className="absolute top-0 left-1/4 w-96 h-96 bg-gradient-to-r from-blue-600/20 to-purple-600/20 rounded-full blur-3xl animate-pulse" />
        <div className="absolute bottom-0 right-1/4 w-96 h-96 bg-gradient-to-r from-purple-600/20 to-pink-600/20 rounded-full blur-3xl animate-pulse" style={{ animationDelay: '1s' }} />
      </div>

      <div className="relative z-10">
        <DashboardLayout 
          title="Changelog"
          subtitle={`Track the latest updates and improvements to Bookwiz v${appVersion}`}
        >
          <div className="max-w-4xl mx-auto">
            {/* Current Version Banner */}
            <div className="bg-gradient-to-r from-blue-500/10 to-purple-500/10 border border-white/10 rounded-2xl p-6 mb-8">
              <div className="flex items-center justify-between">
                <div className="flex items-center space-x-4">
                  <div className="w-12 h-12 rounded-xl bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center">
                    <CodeBracketIcon className="w-6 h-6 text-white" />
                  </div>
                  <div>
                    <h3 className="text-lg font-bold text-white">Current Version</h3>
                    <p className="text-gray-400">Bookwiz v{appVersion}</p>
                  </div>
                </div>
                <div className="text-right">
                  <div className="inline-flex items-center space-x-2 px-3 py-1 bg-green-500/20 text-green-400 rounded-full text-sm font-medium">
                    <div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
                    <span>Live</span>
                  </div>
                </div>
              </div>
            </div>

            {/* Changelog Sections */}
            <div className="space-y-6">
              {parsedSections.map((section, index) => {
                if (section.isTitle) {
                  return null; // Skip title section as we have our own header
                }

                const isUnreleased = section.version.toLowerCase() === 'unreleased';

                return (
                  <div key={index} className="bg-white/5 backdrop-blur-sm border border-white/10 rounded-2xl overflow-hidden">
                    <div className={`px-6 py-4 border-b border-white/10 ${
                      isUnreleased 
                        ? 'bg-gradient-to-r from-amber-500/20 to-orange-500/20' 
                        : 'bg-gradient-to-r from-blue-500/10 to-purple-500/10'
                    }`}>
                      <div className="flex items-center justify-between">
                        <div className="flex items-center space-x-3">
                          {isUnreleased ? (
                            <div className="w-8 h-8 rounded-lg bg-gradient-to-br from-amber-500 to-orange-600 flex items-center justify-center">
                              <ClockIcon className="w-4 h-4 text-white" />
                            </div>
                          ) : (
                            <div className="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center">
                              <DocumentTextIcon className="w-4 h-4 text-white" />
                            </div>
                          )}
                          <h2 className="text-xl font-black text-white">
                            {isUnreleased ? 'Unreleased' : (section.version.startsWith('v') ? section.version : `v${section.version}`)}
                          </h2>
                          {isUnreleased && (
                            <span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-amber-500/20 text-amber-400">
                              Coming Soon
                            </span>
                          )}
                        </div>
                        {section.date && !isUnreleased && (
                          <span className="text-gray-400 text-sm font-medium">{section.date}</span>
                        )}
                      </div>
                    </div>
                    
                    <div className="px-6 py-6">
                      <div className="space-y-1">
                        {formatChangelogContent(section.content)}
                      </div>
                    </div>
                  </div>
                );
              })}
            </div>

            {/* Footer */}
            <div className="mt-12 text-center">
              <div className="bg-white/5 backdrop-blur-sm border border-white/10 rounded-2xl p-6">
                <p className="text-gray-400 text-sm mb-3">
                  This changelog is automatically generated from our commit messages.
                </p>
                <p className="text-gray-500 text-xs">
                  <span className="text-gray-400">Repository:</span>{' '}
                  <span className="font-mono">github.com/kristiyanTs/bookwiz.io</span>{' '}
                  <span className="text-gray-600">(Private)</span>
                </p>
              </div>
            </div>
          </div>
        </DashboardLayout>
      </div>
    </div>
  );
}