Changelog.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
  2. import { Badge } from "@/components/ui/badge";
  3. import { Separator } from "@/components/ui/separator";
  4. import { ScrollArea } from "@/components/ui/scroll-area";
  5. import { Sparkles, TrendingUp, Bug, Shield } from "lucide-react";
  6. import { changelogData, ChangelogEntry } from "@/data/changelog";
  7. import { SEO } from "@/components/SEO";
  8. import { format, parseISO } from "date-fns";
  9. import { fr } from "date-fns/locale";
  10. const getCategoryConfig = (category: ChangelogEntry['category']) => {
  11. const configs = {
  12. feature: {
  13. icon: Sparkles,
  14. label: "Nouvelle fonctionnalité",
  15. variant: "default" as const,
  16. color: "text-primary"
  17. },
  18. improvement: {
  19. icon: TrendingUp,
  20. label: "Amélioration",
  21. variant: "secondary" as const,
  22. color: "text-green-600 dark:text-green-400"
  23. },
  24. bugfix: {
  25. icon: Bug,
  26. label: "Correction de bug",
  27. variant: "outline" as const,
  28. color: "text-orange-600 dark:text-orange-400"
  29. },
  30. security: {
  31. icon: Shield,
  32. label: "Sécurité",
  33. variant: "destructive" as const,
  34. color: "text-destructive"
  35. }
  36. };
  37. return configs[category];
  38. };
  39. const Changelog = () => {
  40. const sortedChangelog = [...changelogData].sort((a, b) =>
  41. new Date(b.date).getTime() - new Date(a.date).getTime()
  42. );
  43. const latestUpdate = sortedChangelog[0]?.date;
  44. return (
  45. <>
  46. <SEO
  47. title="Changelog - Historique des évolutions"
  48. description="Découvrez toutes les évolutions, nouvelles fonctionnalités et améliorations de Feeds.Duhaz.fr. Historique complet des mises à jour."
  49. keywords="changelog, mises à jour, évolutions, nouveautés, historique, versions"
  50. />
  51. <div className="min-h-screen bg-background">
  52. <div className="container mx-auto px-4 py-8 max-w-4xl">
  53. {/* Header */}
  54. <div className="mb-8">
  55. <div className="flex items-center gap-3 mb-4">
  56. <div className="h-12 w-12 rounded-lg bg-primary/10 flex items-center justify-center">
  57. <Sparkles className="h-6 w-6 text-primary" />
  58. </div>
  59. <div>
  60. <h1 className="text-3xl font-bold">Changelog</h1>
  61. <p className="text-muted-foreground">
  62. Historique des évolutions du site
  63. </p>
  64. </div>
  65. </div>
  66. <div className="flex items-center gap-4 text-sm text-muted-foreground">
  67. <span>{sortedChangelog.length} versions</span>
  68. <Separator orientation="vertical" className="h-4" />
  69. <span>
  70. Dernière mise à jour : {latestUpdate && format(parseISO(latestUpdate), "d MMMM yyyy", { locale: fr })}
  71. </span>
  72. </div>
  73. </div>
  74. {/* Timeline */}
  75. <ScrollArea className="h-[calc(100vh-250px)]">
  76. <div className="space-y-6">
  77. {sortedChangelog.map((entry, index) => {
  78. const config = getCategoryConfig(entry.category);
  79. const Icon = config.icon;
  80. return (
  81. <div key={`${entry.version}-${entry.date}`}>
  82. <Card>
  83. <CardHeader>
  84. <div className="flex items-start justify-between gap-4">
  85. <div className="flex items-center gap-3">
  86. <div className={`h-10 w-10 rounded-lg bg-card border flex items-center justify-center ${config.color}`}>
  87. <Icon className="h-5 w-5" />
  88. </div>
  89. <div>
  90. <div className="flex items-center gap-2 mb-1">
  91. <CardTitle className="text-xl">
  92. {entry.title}
  93. </CardTitle>
  94. </div>
  95. <div className="flex items-center gap-2 flex-wrap">
  96. <Badge variant={config.variant} className="gap-1">
  97. <Icon className="h-3 w-3" />
  98. {config.label}
  99. </Badge>
  100. <Badge variant="outline">v{entry.version}</Badge>
  101. <span className="text-sm text-muted-foreground">
  102. {format(parseISO(entry.date), "d MMMM yyyy", { locale: fr })}
  103. </span>
  104. </div>
  105. </div>
  106. </div>
  107. </div>
  108. <CardDescription className="mt-3">
  109. {entry.description}
  110. </CardDescription>
  111. </CardHeader>
  112. {entry.details && entry.details.length > 0 && (
  113. <CardContent>
  114. <ul className="space-y-2">
  115. {entry.details.map((detail, idx) => (
  116. <li key={idx} className="flex items-start gap-2 text-sm">
  117. <span className="text-primary mt-1">•</span>
  118. <span className="text-muted-foreground">{detail}</span>
  119. </li>
  120. ))}
  121. </ul>
  122. </CardContent>
  123. )}
  124. </Card>
  125. {index < sortedChangelog.length - 1 && (
  126. <div className="flex justify-center my-4">
  127. <Separator className="w-[2px] h-6" orientation="vertical" />
  128. </div>
  129. )}
  130. </div>
  131. );
  132. })}
  133. </div>
  134. </ScrollArea>
  135. </div>
  136. </div>
  137. </>
  138. );
  139. };
  140. export default Changelog;