forked from git.gladyson/openmonetis
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
|
- Calculadora arrastável via drag handle no header do dialog
|
||||||
- Callback `onSelectValue` na calculadora para inserir valor diretamente no campo de lançamento
|
- 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
|
### Alterado
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { headers } from "next/headers";
|
import { headers } from "next/headers";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
|
import { ChangelogTab } from "@/components/ajustes/changelog-tab";
|
||||||
import { CompanionTab } from "@/components/ajustes/companion-tab";
|
import { CompanionTab } from "@/components/ajustes/companion-tab";
|
||||||
import { DeleteAccountForm } from "@/components/ajustes/delete-account-form";
|
import { DeleteAccountForm } from "@/components/ajustes/delete-account-form";
|
||||||
import { PreferencesForm } from "@/components/ajustes/preferences-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 { Card } from "@/components/ui/card";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { auth } from "@/lib/auth/config";
|
import { auth } from "@/lib/auth/config";
|
||||||
|
import { parseChangelog } from "@/lib/changelog/parse-changelog";
|
||||||
|
|
||||||
import { fetchAjustesPageData } from "./data";
|
import { fetchAjustesPageData } from "./data";
|
||||||
|
|
||||||
@@ -28,6 +30,8 @@ export default async function Page() {
|
|||||||
const { authProvider, userPreferences, userApiTokens } =
|
const { authProvider, userPreferences, userApiTokens } =
|
||||||
await fetchAjustesPageData(session.user.id);
|
await fetchAjustesPageData(session.user.id);
|
||||||
|
|
||||||
|
const changelogVersions = parseChangelog();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Tabs defaultValue="preferencias" 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="nome">Alterar nome</TabsTrigger>
|
||||||
<TabsTrigger value="senha">Alterar senha</TabsTrigger>
|
<TabsTrigger value="senha">Alterar senha</TabsTrigger>
|
||||||
<TabsTrigger value="email">Alterar e-mail</TabsTrigger>
|
<TabsTrigger value="email">Alterar e-mail</TabsTrigger>
|
||||||
|
<TabsTrigger value="changelog">Changelog</TabsTrigger>
|
||||||
<TabsTrigger value="deletar" className="text-destructive">
|
<TabsTrigger value="deletar" className="text-destructive">
|
||||||
Deletar conta
|
Deletar conta
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
@@ -114,6 +119,10 @@ export default async function Page() {
|
|||||||
</Card>
|
</Card>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="changelog" className="mt-4">
|
||||||
|
<ChangelogTab versions={changelogVersions} />
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="deletar" className="mt-4">
|
<TabsContent value="deletar" className="mt-4">
|
||||||
<Card className="p-6">
|
<Card className="p-6">
|
||||||
<div className="space-y-4">
|
<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