feat(ajustes): adicionar aba changelog com histórico de versões
Parser lê o CHANGELOG.md e exibe as versões com badges por tipo de mudança (Adicionado, Alterado, Corrigido) na página de ajustes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR
|
||||
|
||||
- Calculadora arrastável via drag handle no header do dialog
|
||||
- Callback `onSelectValue` na calculadora para inserir valor diretamente no campo de lançamento
|
||||
- Aba "Changelog" em Ajustes com histórico de versões parseado do CHANGELOG.md
|
||||
|
||||
### Alterado
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { ChangelogTab } from "@/components/ajustes/changelog-tab";
|
||||
import { CompanionTab } from "@/components/ajustes/companion-tab";
|
||||
import { DeleteAccountForm } from "@/components/ajustes/delete-account-form";
|
||||
import { PreferencesForm } from "@/components/ajustes/preferences-form";
|
||||
@@ -10,6 +11,7 @@ import { UpdatePasswordForm } from "@/components/ajustes/update-password-form";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { auth } from "@/lib/auth/config";
|
||||
import { parseChangelog } from "@/lib/changelog/parse-changelog";
|
||||
|
||||
import { fetchAjustesPageData } from "./data";
|
||||
|
||||
@@ -28,6 +30,8 @@ export default async function Page() {
|
||||
const { authProvider, userPreferences, userApiTokens } =
|
||||
await fetchAjustesPageData(session.user.id);
|
||||
|
||||
const changelogVersions = parseChangelog();
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<Tabs defaultValue="preferencias" className="w-full">
|
||||
@@ -37,6 +41,7 @@ export default async function Page() {
|
||||
<TabsTrigger value="nome">Alterar nome</TabsTrigger>
|
||||
<TabsTrigger value="senha">Alterar senha</TabsTrigger>
|
||||
<TabsTrigger value="email">Alterar e-mail</TabsTrigger>
|
||||
<TabsTrigger value="changelog">Changelog</TabsTrigger>
|
||||
<TabsTrigger value="deletar" className="text-destructive">
|
||||
Deletar conta
|
||||
</TabsTrigger>
|
||||
@@ -114,6 +119,10 @@ export default async function Page() {
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="changelog" className="mt-4">
|
||||
<ChangelogTab versions={changelogVersions} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="deletar" className="mt-4">
|
||||
<Card className="p-6">
|
||||
<div className="space-y-4">
|
||||
|
||||
54
components/ajustes/changelog-tab.tsx
Normal file
54
components/ajustes/changelog-tab.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import type { ChangelogVersion } from "@/lib/changelog/parse-changelog";
|
||||
|
||||
const sectionBadgeVariant: Record<
|
||||
string,
|
||||
"success" | "info" | "destructive" | "secondary"
|
||||
> = {
|
||||
Adicionado: "success",
|
||||
Alterado: "info",
|
||||
Corrigido: "destructive",
|
||||
Removido: "secondary",
|
||||
};
|
||||
|
||||
function getSectionVariant(type: string) {
|
||||
return sectionBadgeVariant[type] ?? "secondary";
|
||||
}
|
||||
|
||||
export function ChangelogTab({ versions }: { versions: ChangelogVersion[] }) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{versions.map((version) => (
|
||||
<Card key={version.version} className="p-6">
|
||||
<div className="flex items-baseline gap-3">
|
||||
<h3 className="text-lg font-bold">v{version.version}</h3>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{version.date}
|
||||
</span>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
{version.sections.map((section) => (
|
||||
<div key={section.type}>
|
||||
<Badge
|
||||
variant={getSectionVariant(section.type)}
|
||||
className="mb-2"
|
||||
>
|
||||
{section.type}
|
||||
</Badge>
|
||||
<ul className="space-y-1.5 text-muted-foreground">
|
||||
{section.items.map((item) => (
|
||||
<li key={item} className="flex gap-2">
|
||||
<span className="text-primary select-none">•</span>
|
||||
<span className="text-sm">{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
60
lib/changelog/parse-changelog.ts
Normal file
60
lib/changelog/parse-changelog.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
export type ChangelogSection = {
|
||||
type: string;
|
||||
items: string[];
|
||||
};
|
||||
|
||||
export type ChangelogVersion = {
|
||||
version: string;
|
||||
date: string;
|
||||
sections: ChangelogSection[];
|
||||
};
|
||||
|
||||
export function parseChangelog(): ChangelogVersion[] {
|
||||
const filePath = path.join(process.cwd(), "CHANGELOG.md");
|
||||
const content = fs.readFileSync(filePath, "utf-8");
|
||||
const lines = content.split("\n");
|
||||
|
||||
const versions: ChangelogVersion[] = [];
|
||||
let currentVersion: ChangelogVersion | null = null;
|
||||
let currentSection: ChangelogSection | null = null;
|
||||
|
||||
for (const line of lines) {
|
||||
const versionMatch = line.match(/^## \[(.+?)\] - (.+)$/);
|
||||
if (versionMatch) {
|
||||
if (currentSection && currentVersion) {
|
||||
currentVersion.sections.push(currentSection);
|
||||
}
|
||||
currentVersion = {
|
||||
version: versionMatch[1],
|
||||
date: versionMatch[2],
|
||||
sections: [],
|
||||
};
|
||||
versions.push(currentVersion);
|
||||
currentSection = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
const sectionMatch = line.match(/^### (.+)$/);
|
||||
if (sectionMatch && currentVersion) {
|
||||
if (currentSection) {
|
||||
currentVersion.sections.push(currentSection);
|
||||
}
|
||||
currentSection = { type: sectionMatch[1], items: [] };
|
||||
continue;
|
||||
}
|
||||
|
||||
const itemMatch = line.match(/^- (.+)$/);
|
||||
if (itemMatch && currentSection) {
|
||||
currentSection.items.push(itemMatch[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentSection && currentVersion) {
|
||||
currentVersion.sections.push(currentSection);
|
||||
}
|
||||
|
||||
return versions;
|
||||
}
|
||||
Reference in New Issue
Block a user