feat: adicionar estrutura para gerenciamento de mudanças de código
Implementa um sistema para registrar e gerenciar mudanças de código de forma eficiente. A estrutura permite a adição, remoção e edição de entradas de mudanças, facilitando o acompanhamento do histórico de alterações no projeto.
16
README.md
@@ -1,8 +1,12 @@
|
|||||||
# Opensheets
|
<p align="center">
|
||||||
|
<img src="./public/logo_small.png" alt="Opensheets Logo" height="80" />
|
||||||
|
</p>
|
||||||
|
|
||||||
> Projeto pessoal de gestão financeira. Self-hosted, manual e open source.
|
<p align="center">
|
||||||
|
Projeto pessoal de gestão financeira. Self-hosted, manual e open source.
|
||||||
|
</p>
|
||||||
|
|
||||||
> **⚠️ Não há versão online hospedada.** Você precisa clonar o repositório e rodar localmente ou no seu próprio servidor.
|
> **⚠️ Não há versão online hospedada.** Você precisa clonar o repositório e rodar localmente ou no seu próprio servidor ou computador.
|
||||||
|
|
||||||
[](https://nextjs.org/)
|
[](https://nextjs.org/)
|
||||||
[](https://www.typescriptlang.org/)
|
[](https://www.typescriptlang.org/)
|
||||||
@@ -11,6 +15,12 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="./public/dashboard-preview.png" alt="Dashboard Preview" width="800" />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 📖 Índice
|
## 📖 Índice
|
||||||
|
|
||||||
- [Sobre o Projeto](#-sobre-o-projeto)
|
- [Sobre o Projeto](#-sobre-o-projeto)
|
||||||
|
|||||||
@@ -158,8 +158,6 @@ export default async function Page() {
|
|||||||
<section className="py-8 md:py-16">
|
<section className="py-8 md:py-16">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="mx-auto max-w-6xl">
|
<div className="mx-auto max-w-6xl">
|
||||||
<div className="relative rounded-xl border bg-muted/20 shadow-2xl overflow-hidden">
|
|
||||||
<div className="absolute inset-0 bg-linear-to-t from-background/80 to-transparent z-10 pointer-events-none" />
|
|
||||||
<Image
|
<Image
|
||||||
src="/dashboard-preview.png"
|
src="/dashboard-preview.png"
|
||||||
alt="opensheets Dashboard Preview"
|
alt="opensheets Dashboard Preview"
|
||||||
@@ -170,7 +168,6 @@ export default async function Page() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* What's Here Section */}
|
{/* What's Here Section */}
|
||||||
|
|||||||
BIN
app/apple-icon.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
app/favicon.ico
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 15 KiB |
1
app/icon0.svg
Normal file
|
After Width: | Height: | Size: 8.3 KiB |
BIN
app/icon1.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
@@ -17,6 +17,9 @@ export default function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" suppressHydrationWarning>
|
<html lang="en" suppressHydrationWarning>
|
||||||
|
<head>
|
||||||
|
<meta name="apple-mobile-web-app-title" content="Opensheets" />
|
||||||
|
</head>
|
||||||
<body
|
<body
|
||||||
className={`${main_font.className} antialiased`}
|
className={`${main_font.className} antialiased`}
|
||||||
suppressHydrationWarning
|
suppressHydrationWarning
|
||||||
|
|||||||
21
app/manifest.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "Opensheets",
|
||||||
|
"short_name": "Opensheets",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/web-app-manifest-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/web-app-manifest-512x512.png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"theme_color": "#F2ECE7",
|
||||||
|
"background_color": "#F2ECE7",
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -7,6 +8,7 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { cn } from "@/lib/utils/ui";
|
import { cn } from "@/lib/utils/ui";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
@@ -51,26 +53,26 @@ export function LogoPickerTrigger({
|
|||||||
onClick={onOpen}
|
onClick={onOpen}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex w-full items-center gap-3 rounded-lg border p-3 text-left transition-colors hover:border-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60",
|
"flex w-full items-center gap-2 rounded-md border p-2 text-left transition-colors hover:border-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-60",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className="flex size-14 shrink-0 items-center justify-center overflow-hidden rounded-lg border border-border/40 bg-background shadow-xs">
|
<span className="flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-md border border-border/40 bg-background shadow-xs">
|
||||||
{selectedLogoPath ? (
|
{selectedLogoPath ? (
|
||||||
<Image
|
<Image
|
||||||
src={selectedLogoPath}
|
src={selectedLogoPath}
|
||||||
alt={selectedLogoLabel || "Logo selecionado"}
|
alt={selectedLogoLabel || "Logo selecionado"}
|
||||||
width={48}
|
width={28}
|
||||||
height={48}
|
height={28}
|
||||||
className="h-full w-full object-contain"
|
className="h-full w-full object-contain"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-xs text-muted-foreground">{placeholder}</span>
|
<span className="text-[10px] text-muted-foreground">Logo</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span className="flex min-w-0 flex-1 flex-col">
|
<span className="flex min-w-0 flex-1 flex-col">
|
||||||
<span className="truncate font-medium text-foreground">
|
<span className="truncate text-sm font-medium text-foreground">
|
||||||
{selectedLogoLabel || placeholder}
|
{selectedLogoLabel || placeholder}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs text-muted-foreground">
|
<span className="text-xs text-muted-foreground">
|
||||||
@@ -104,9 +106,22 @@ export function LogoPickerDialog({
|
|||||||
description = "Selecione o logo que será usado para identificar este item.",
|
description = "Selecione o logo que será usado para identificar este item.",
|
||||||
emptyState,
|
emptyState,
|
||||||
}: LogoPickerDialogProps) {
|
}: LogoPickerDialogProps) {
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
const filteredLogos = logos.filter((logo) => {
|
||||||
|
if (!search.trim()) return true;
|
||||||
|
const logoLabel = deriveNameFromLogo(logo).toLowerCase();
|
||||||
|
return logoLabel.includes(search.toLowerCase().trim());
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleOpenChange = (isOpen: boolean) => {
|
||||||
|
if (!isOpen) setSearch("");
|
||||||
|
onOpenChange(isOpen);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||||
<DialogContent className="sm:max-w-xl">
|
<DialogContent className="sm:max-w-2xl">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>{title}</DialogTitle>
|
<DialogTitle>{title}</DialogTitle>
|
||||||
{description ? (
|
{description ? (
|
||||||
@@ -114,15 +129,29 @@ export function LogoPickerDialog({
|
|||||||
) : null}
|
) : null}
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
|
{logos.length > 0 && (
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Pesquisar..."
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
className="h-8 text-sm"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{logos.length === 0 ? (
|
{logos.length === 0 ? (
|
||||||
emptyState ?? (
|
emptyState ?? (
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Nenhum logo encontrado. Adicione arquivos na pasta de logos.
|
Nenhum logo encontrado. Adicione arquivos na pasta de logos.
|
||||||
</p>
|
</p>
|
||||||
)
|
)
|
||||||
|
) : filteredLogos.length === 0 ? (
|
||||||
|
<p className="py-4 text-center text-sm text-muted-foreground">
|
||||||
|
Nenhum logo encontrado para “{search}”
|
||||||
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid max-h-72 grid-cols-3 gap-4 overflow-y-auto p-1 sm:grid-cols-4">
|
<div className="grid max-h-custom-height-1 grid-cols-4 gap-2 overflow-y-auto p-1 sm:grid-cols-4 md:grid-cols-5">
|
||||||
{logos.map((logo) => {
|
{filteredLogos.map((logo) => {
|
||||||
const isActive = value === logo;
|
const isActive = value === logo;
|
||||||
const logoLabel = deriveNameFromLogo(logo);
|
const logoLabel = deriveNameFromLogo(logo);
|
||||||
|
|
||||||
@@ -132,21 +161,21 @@ export function LogoPickerDialog({
|
|||||||
key={logo}
|
key={logo}
|
||||||
onClick={() => onSelect(logo)}
|
onClick={() => onSelect(logo)}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col items-center gap-2 border rounded-lg bg-card p-2 text-center text-xs transition-all hover:border-primary hover:bg-primary/5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
"flex flex-col items-center gap-1 rounded-md bg-card p-2 text-center text-xs transition-all hover:border-primary hover:bg-primary/5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||||
isActive &&
|
isActive &&
|
||||||
"border-primary bg-primary/5 ring-2 ring-primary/40"
|
"border-primary bg-primary/5 ring-2 ring-primary/40"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<span className="flex w-full items-center justify-center overflow-hidden rounded-lg">
|
<span className="flex w-full items-center justify-center overflow-hidden rounded-md">
|
||||||
<Image
|
<Image
|
||||||
src={resolveLogoSrc(logo, basePath)}
|
src={resolveLogoSrc(logo, basePath)}
|
||||||
alt={logoLabel || logo}
|
alt={logoLabel || logo}
|
||||||
width={48}
|
width={40}
|
||||||
height={48}
|
height={40}
|
||||||
className="rounded-lg"
|
className="rounded-md"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span className="line-clamp-2 leading-tight text-muted-foreground">
|
<span className="line-clamp-1 text-[10px] leading-tight text-muted-foreground">
|
||||||
{logoLabel}
|
{logoLabel}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
BIN
public/dashboard-preview.png
Normal file
|
After Width: | Height: | Size: 869 KiB |
@@ -31,8 +31,8 @@ const inter = Inter({
|
|||||||
weight: ["400", "500", "600", "700"],
|
weight: ["400", "500", "600", "700"],
|
||||||
});
|
});
|
||||||
|
|
||||||
const main_font = anthropic_sans;
|
const main_font = funnel_display;
|
||||||
const money_font = funnel_display;
|
const money_font = anthropic_sans;
|
||||||
const title_font = funnel_display;
|
const title_font = anthropic_sans;
|
||||||
|
|
||||||
export { main_font, money_font, title_font };
|
export { main_font, money_font, title_font };
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
BIN
public/logos/abank.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/logos/abcbrasil.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
public/logos/abnamro.png
Normal file
|
After Width: | Height: | Size: 782 B |
BIN
public/logos/abrapetite.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
public/logos/absolutbank.png
Normal file
|
After Width: | Height: | Size: 754 B |
BIN
public/logos/acessobank.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
public/logos/activo.bank.png
Normal file
|
After Width: | Height: | Size: 695 B |
BIN
public/logos/activtrades.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/logos/agbank.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/logos/agibank.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
public/logos/agora.png
Normal file
|
After Width: | Height: | Size: 707 B |
BIN
public/logos/agribank.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
public/logos/aktia.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/logos/alfabank.png
Normal file
|
After Width: | Height: | Size: 803 B |
BIN
public/logos/alphabank.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/logos/alt.bank.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/logos/amazon.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/logos/amazonia.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/logos/ame.png
Normal file
|
After Width: | Height: | Size: 859 B |
BIN
public/logos/ameriprisefinancial.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
public/logos/amex.png
Normal file
|
After Width: | Height: | Size: 898 B |
BIN
public/logos/amppersonalbanking.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
public/logos/anz.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
public/logos/asaas.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
public/logos/asn.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
public/logos/astro-pay.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/logos/atlantico.png
Normal file
|
After Width: | Height: | Size: 980 B |
BIN
public/logos/atticabank.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/logos/aura.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
public/logos/avenida.png
Normal file
|
After Width: | Height: | Size: 1008 B |
BIN
public/logos/avenuesecuritie.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
BIN
public/logos/bahamas-cred.png
Normal file
|
After Width: | Height: | Size: 971 B |
BIN
public/logos/bancamediolanum.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
public/logos/bancamps.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
public/logos/bancaopopularedisondrio.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/logos/banco-bai.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
public/logos/banco-bic.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
public/logos/banco-comercio-industria.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
public/logos/banco-parana.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
public/logos/banco-poupanca-credito.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/logos/bancobpm.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
public/logos/bancodavivienda.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
public/logos/bancodebogota.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
public/logos/bancodelbajio.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
public/logos/bancodeoccident.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/logos/bancohipotecario.png
Normal file
|
After Width: | Height: | Size: 539 B |
BIN
public/logos/bancolombia.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
public/logos/bancomacro.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
public/logos/banconacion.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
public/logos/bancopatagonia.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
public/logos/bancoposta.png
Normal file
|
After Width: | Height: | Size: 504 B |
BIN
public/logos/banese.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
public/logos/banestes.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/logos/bangkokbank.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/logos/banif.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/logos/bankia.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/logos/bankmandiri.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/logos/banknorwegian.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
public/logos/bankofaland.png
Normal file
|
After Width: | Height: | Size: 911 B |
BIN
public/logos/bankofamerica.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/logos/bankofchina.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/logos/bankrakyat.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
public/logos/bankwest.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
public/logos/banorte.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/logos/banpara.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 4.1 KiB |
BIN
public/logos/bari.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
BIN
public/logos/bbva.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/logos/bca.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/logos/bemol.png
Normal file
|
After Width: | Height: | Size: 681 B |
BIN
public/logos/bendigobank.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/logos/benvisavale.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/logos/betfair.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
public/logos/bidv.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
public/logos/bilhete-unico.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
public/logos/binance.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/logos/binomo.png
Normal file
|
After Width: | Height: | Size: 964 B |
BIN
public/logos/bitybank.png
Normal file
|
After Width: | Height: | Size: 478 B |
BIN
public/logos/bitz.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/logos/bmg-corinthians.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 685 B After Width: | Height: | Size: 776 B |
BIN
public/logos/bmo.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
public/logos/bni.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/logos/bnl.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/logos/bnpparibas.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
public/logos/boq.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |