forked from git.gladyson/openmonetis
feat: implementar sistema de preferências do usuário e refatorar changelog
Adiciona sistema completo de preferências de usuário: - Cria tabela userPreferences no schema com campos disableMagnetlines, periodMonthsBefore e periodMonthsAfter - Implementa página de Ajustes com abas (Preferências, Alterar nome, Senha, E-mail, Deletar conta) - Adiciona componente PreferencesForm para configuração de magnetlines e períodos de exibição - Propaga periodPreferences para todos os componentes de lançamentos e calendário Refatora sistema de changelog: - Remove implementação anterior baseada em JSON estático - Adiciona nova página de changelog dinâmica em app/(dashboard)/changelog - Adiciona componente changelog-list.tsx - Remove arquivos obsoletos (changelog-notification, actions, data, utils, scripts) Adiciona controle de saldo inicial em contas: - Novo campo excludeInitialBalanceFromIncome em contas - Permite excluir saldo inicial do cálculo de receitas - Atualiza queries de lançamentos para respeitar esta configuração Melhorias adicionais: - Adiciona componente ui/accordion.tsx do shadcn/ui - Refatora formatPeriodLabel para displayPeriod centralizado - Propaga estabelecimentos para componentes de lançamentos - Remove variável DB_PROVIDER obsoleta do .env.example e documentação - Adiciona 6 migrações de banco de dados (0003-0008)
This commit is contained in:
23
app/(dashboard)/changelog/layout.tsx
Normal file
23
app/(dashboard)/changelog/layout.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import PageDescription from "@/components/page-description";
|
||||
import { RiGitCommitLine } from "@remixicon/react";
|
||||
|
||||
export const metadata = {
|
||||
title: "Changelog | Opensheets",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 px-6">
|
||||
<PageDescription
|
||||
icon={<RiGitCommitLine />}
|
||||
title="Changelog"
|
||||
subtitle="Histórico completo de alterações e atualizações do projeto."
|
||||
/>
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
31
app/(dashboard)/changelog/loading.tsx
Normal file
31
app/(dashboard)/changelog/loading.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
|
||||
export default function Loading() {
|
||||
return (
|
||||
<main className="container mx-auto px-4 py-8 max-w-4xl">
|
||||
<div className="mb-8">
|
||||
<Skeleton className="h-9 w-48 mb-2" />
|
||||
<Skeleton className="h-5 w-96" />
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<Card key={i}>
|
||||
<CardHeader className="pb-3">
|
||||
<Skeleton className="h-6 w-3/4 mb-2" />
|
||||
<div className="flex gap-3">
|
||||
<Skeleton className="h-4 w-20" />
|
||||
<Skeleton className="h-4 w-32" />
|
||||
<Skeleton className="h-4 w-24" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-0">
|
||||
<Skeleton className="h-4 w-32" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
102
app/(dashboard)/changelog/page.tsx
Normal file
102
app/(dashboard)/changelog/page.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import { ChangelogList } from "@/components/changelog/changelog-list";
|
||||
import { execSync } from "child_process";
|
||||
|
||||
type GitCommit = {
|
||||
hash: string;
|
||||
shortHash: string;
|
||||
author: string;
|
||||
date: string;
|
||||
message: string;
|
||||
body: string;
|
||||
filesChanged: string[];
|
||||
};
|
||||
|
||||
function getGitRemoteUrl(): string | null {
|
||||
try {
|
||||
const remoteUrl = execSync("git config --get remote.origin.url", {
|
||||
encoding: "utf-8",
|
||||
cwd: process.cwd(),
|
||||
}).trim();
|
||||
|
||||
// Converter SSH para HTTPS se necessário
|
||||
if (remoteUrl.startsWith("git@")) {
|
||||
return remoteUrl
|
||||
.replace("git@github.com:", "https://github.com/")
|
||||
.replace("git@gitlab.com:", "https://gitlab.com/")
|
||||
.replace(".git", "");
|
||||
}
|
||||
|
||||
return remoteUrl.replace(".git", "");
|
||||
} catch (error) {
|
||||
console.error("Error fetching git remote URL:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getGitCommits(): GitCommit[] {
|
||||
try {
|
||||
// Buscar os últimos 50 commits
|
||||
const commits = execSync(
|
||||
'git log -50 --pretty=format:"%H|%h|%an|%ad|%s|%b" --date=iso --name-only',
|
||||
{
|
||||
encoding: "utf-8",
|
||||
cwd: process.cwd(),
|
||||
}
|
||||
)
|
||||
.trim()
|
||||
.split("\n\n");
|
||||
|
||||
return commits
|
||||
.map((commitBlock) => {
|
||||
const lines = commitBlock.split("\n");
|
||||
const [hash, shortHash, author, date, message, ...rest] =
|
||||
lines[0].split("|");
|
||||
|
||||
// Separar body e arquivos
|
||||
const bodyLines: string[] = [];
|
||||
const filesChanged: string[] = [];
|
||||
let isBody = true;
|
||||
|
||||
rest.forEach((line) => {
|
||||
if (line && !line.includes("/") && !line.includes(".")) {
|
||||
bodyLines.push(line);
|
||||
} else {
|
||||
isBody = false;
|
||||
}
|
||||
});
|
||||
|
||||
lines.slice(1).forEach((line) => {
|
||||
if (line.trim()) {
|
||||
filesChanged.push(line.trim());
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
hash,
|
||||
shortHash,
|
||||
author,
|
||||
date,
|
||||
message,
|
||||
body: bodyLines.join("\n").trim(),
|
||||
filesChanged: filesChanged.filter(
|
||||
(f) => f && !f.startsWith("git log")
|
||||
),
|
||||
};
|
||||
})
|
||||
.filter((commit) => commit.hash && commit.message);
|
||||
} catch (error) {
|
||||
console.error("Error fetching git commits:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export default async function ChangelogPage() {
|
||||
const commits = getGitCommits();
|
||||
const repoUrl = getGitRemoteUrl();
|
||||
|
||||
return (
|
||||
<main>
|
||||
<ChangelogList commits={commits} repoUrl={repoUrl} />
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user