mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 19:01:47 +00:00
style(ui): polimento visual — tema, cards, dark mode e landing page
Raio de borda global 0.625rem → 0.7rem; ajustes finos em --card e --border. DotPattern removido do layout, tela de auth e landing page. Account-card redesenhado (cores de saldo, tooltip de flags de exclusão). Budget-card, card-item, calendário (day-cell, event-modal) com layout revisado. Auth-card-shell simplificado (sem glassmorphism/blob). Landing page com mainFeatures + extraFeatures em grid único e dark mode nos botões de CTA. Imagens de preview da landing atualizadas. CSS --data-7..10 removidas. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -59,7 +59,7 @@ export function LogoPickerTrigger({
|
||||
className="object-contain p-0.5"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-[10px] text-muted-foreground">Logo</span>
|
||||
<span className="text-xs text-muted-foreground">Logo</span>
|
||||
)}
|
||||
</span>
|
||||
|
||||
@@ -172,7 +172,7 @@ export function LogoPickerDialog({
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
<span className="line-clamp-1 text-[10px] leading-tight text-muted-foreground">
|
||||
<span className="line-clamp-1 text-xs leading-tight text-muted-foreground">
|
||||
{logoLabel}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -8,6 +8,10 @@ interface LogoProps {
|
||||
invertTextOnDark?: boolean;
|
||||
/** Exibe o ícone na cor original, sem filtro preto. Apenas nos variants "full" e "compact" */
|
||||
colorIcon?: boolean;
|
||||
/** Classes extras aplicadas na imagem do ícone */
|
||||
iconClassName?: string;
|
||||
/** Classes extras aplicadas na imagem do texto */
|
||||
textClassName?: string;
|
||||
}
|
||||
|
||||
const iconFilterClass = "brightness-0 saturate-0";
|
||||
@@ -17,6 +21,8 @@ export function Logo({
|
||||
className,
|
||||
invertTextOnDark = true,
|
||||
colorIcon = false,
|
||||
iconClassName,
|
||||
textClassName,
|
||||
}: LogoProps) {
|
||||
if (variant === "compact") {
|
||||
return (
|
||||
@@ -27,7 +33,11 @@ export function Logo({
|
||||
alt="OpenMonetis"
|
||||
fill
|
||||
sizes="32px"
|
||||
className={cn("object-contain", !colorIcon && iconFilterClass)}
|
||||
className={cn(
|
||||
"object-contain",
|
||||
!colorIcon && iconFilterClass,
|
||||
iconClassName,
|
||||
)}
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
@@ -37,7 +47,11 @@ export function Logo({
|
||||
alt="OpenMonetis"
|
||||
fill
|
||||
sizes="110px"
|
||||
className={cn("object-contain", invertTextOnDark && "dark:invert")}
|
||||
className={cn(
|
||||
"object-contain",
|
||||
invertTextOnDark && "dark:invert",
|
||||
textClassName,
|
||||
)}
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -37,7 +37,7 @@ export default function MonthNavigation() {
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="sticky top-16 z-10 flex w-full flex-row p-4 backdrop-blur-xs supports-backdrop-filter:bg-card/80 ">
|
||||
<Card className="sticky top-18 z-10 flex w-full flex-row p-4 backdrop-blur-md supports-backdrop-filter:bg-card/60">
|
||||
<div className="flex items-center gap-1">
|
||||
<NavigationButton
|
||||
direction="left"
|
||||
|
||||
@@ -8,12 +8,12 @@ export default function PageDescription({
|
||||
icon?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold flex items-center gap-1 ">
|
||||
<div className="space-y-2">
|
||||
<h1 className="text-2xl font-semibold flex items-center gap-1">
|
||||
<span className="text-primary">{icon}</span>
|
||||
{title}
|
||||
</h1>
|
||||
<h2 className="text-sm max-w-2xl text-muted-foreground leading-relaxed mt-1.5">
|
||||
<h2 className="text-sm max-w-2xl text-muted-foreground leading-relaxed">
|
||||
{subtitle}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
@@ -72,7 +72,7 @@ export function TransactionTypeBadge({
|
||||
variant="outline"
|
||||
data-kind={normalizedKind ?? "custom"}
|
||||
className={cn(
|
||||
"h-6 gap-1.5 rounded-full border-transparent px-2 py-0 text-xs font-medium shadow-none",
|
||||
"h-6 gap-1 border-none rounded-md px-2 py-0 text-xs shadow-none",
|
||||
config?.className ??
|
||||
"bg-muted/30 text-muted-foreground dark:bg-muted/20",
|
||||
className,
|
||||
|
||||
@@ -20,7 +20,7 @@ const buttonVariants = cva(
|
||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
navbar:
|
||||
"bg-transparent text-black/75 shadow-none hover:bg-black/10 hover:text-black focus-visible:ring-black/20",
|
||||
"bg-transparent text-black/75 shadow-none hover:bg-black/10 hover:text-black focus-visible:ring-black/20 dark:text-white/75 dark:hover:bg-white/10 dark:hover:text-white dark:focus-visible:ring-white/20",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||
|
||||
@@ -90,7 +90,7 @@ function Calendar({
|
||||
table: "w-full border-collapse",
|
||||
weekdays: cn("flex", defaultClassNames.weekdays),
|
||||
weekday: cn(
|
||||
"text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none",
|
||||
"text-muted-foreground rounded-md flex-1 font-normal text-xs select-none",
|
||||
defaultClassNames.weekday,
|
||||
),
|
||||
week: cn("flex w-full mt-2", defaultClassNames.week),
|
||||
@@ -99,7 +99,7 @@ function Calendar({
|
||||
defaultClassNames.week_number_header,
|
||||
),
|
||||
week_number: cn(
|
||||
"text-[0.8rem] select-none text-muted-foreground",
|
||||
"text-xs select-none text-muted-foreground",
|
||||
defaultClassNames.week_number,
|
||||
),
|
||||
day: cn(
|
||||
|
||||
@@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="card"
|
||||
className={cn(
|
||||
"bg-card text-card-foreground flex flex-col gap-4 border py-6 rounded-lg hover:border-primary/40",
|
||||
"bg-card text-card-foreground flex flex-col gap-4 border border-border/70 dark:border-border/40 py-6 rounded-lg hover:border-primary/60 transition-colors duration-200",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -29,6 +29,8 @@ export type CalendarEvent =
|
||||
status: string;
|
||||
logo: string | null;
|
||||
totalDue: number | null;
|
||||
isPaid: boolean;
|
||||
paymentDate: string | null;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Os valores são CSS variables definidas em globals.css,
|
||||
* com variantes light/dark — sem hardcode de hex fora do tema.
|
||||
*/
|
||||
const DATA_PALETTE_SIZE = 10;
|
||||
const DATA_PALETTE_SIZE = 6;
|
||||
|
||||
/** Array de CSS variables da paleta de dados — usado em gráficos e charts. */
|
||||
export const CATEGORY_COLORS = Array.from(
|
||||
@@ -20,19 +20,18 @@ function hashNameToIndex(name: string): number {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna a CSS variable de cor para um nome (determinístico via hash).
|
||||
* Cor do ícone — sempre primary para consistência visual.
|
||||
*/
|
||||
export function getCategoryColorFromName(name: string): string {
|
||||
const n = hashNameToIndex(name) + 1;
|
||||
return `var(--data-${n})`;
|
||||
export function getCategoryColorFromName(_name: string): string {
|
||||
return "var(--foreground)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna o background com transparência usando color-mix.
|
||||
* Background distinto por nome (hash), com 20% de opacidade.
|
||||
*/
|
||||
export function getCategoryBgColorFromName(name: string): string {
|
||||
const n = hashNameToIndex(name) + 1;
|
||||
return `color-mix(in oklch, var(--data-${n}) 14%, transparent)`;
|
||||
return `color-mix(in oklch, var(--data-${n}) 20%, transparent)`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,20 +48,3 @@ export function buildInitials(name: string): string {
|
||||
const b = parts[1]?.[0] ?? "";
|
||||
return `${a}${b}`.toUpperCase() || "?";
|
||||
}
|
||||
|
||||
// --- compatibilidade retroativa (para não quebrar callers durante migração) ---
|
||||
|
||||
/** @deprecated Use getCategoryColorFromName */
|
||||
export function getCategoryColor(index: number): string {
|
||||
return `var(--data-${(index % DATA_PALETTE_SIZE) + 1})`;
|
||||
}
|
||||
|
||||
/** @deprecated Use getCategoryBgColorFromName */
|
||||
export function getCategoryBgColor(index: number): string {
|
||||
return `color-mix(in oklch, var(--data-${(index % DATA_PALETTE_SIZE) + 1}) 14%, transparent)`;
|
||||
}
|
||||
|
||||
/** @deprecated Use buildInitials */
|
||||
export function buildCategoryInitials(value: string): string {
|
||||
return buildInitials(value);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ const MONTH_NAMES = [
|
||||
"dezembro",
|
||||
] as const;
|
||||
|
||||
export const OPENMONETIS_TIME_ZONE = "America/Sao_Paulo";
|
||||
const OPENMONETIS_TIME_ZONE = "America/Sao_Paulo";
|
||||
|
||||
type DateOnlyParts = {
|
||||
year: number;
|
||||
@@ -200,7 +200,7 @@ export function getTodayDateString(date: Date = new Date()): string {
|
||||
/**
|
||||
* Gets a date string in YYYY-MM-DD format for a specific timezone
|
||||
*/
|
||||
export function getDateStringInTimeZone(
|
||||
function getDateStringInTimeZone(
|
||||
timeZone: string,
|
||||
date: Date = new Date(),
|
||||
): string {
|
||||
@@ -215,14 +215,6 @@ export function getBusinessDateString(date: Date = new Date()): string {
|
||||
return getDateStringInTimeZone(OPENMONETIS_TIME_ZONE, date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets today's date as Date object
|
||||
* @returns Date object for today
|
||||
*/
|
||||
export function getTodayDate(date: Date = new Date()): Date {
|
||||
return parseLocalDateString(getTodayDateString(date));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets today's date as Date object using the app business timezone
|
||||
*/
|
||||
@@ -397,19 +389,6 @@ export function formatDateOnlyLabel(
|
||||
return prefix ? `${prefix} ${formatted}` : formatted;
|
||||
}
|
||||
|
||||
export function formatDateTimeLabel(
|
||||
value: string | Date | null | undefined,
|
||||
prefix?: string,
|
||||
options?: Intl.DateTimeFormatOptions,
|
||||
): string | null {
|
||||
const formatted = formatDateTime(value, options);
|
||||
if (!formatted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return prefix ? `${prefix} ${formatted}` : formatted;
|
||||
}
|
||||
|
||||
export function compareDateOnly(
|
||||
left: string | Date | null | undefined,
|
||||
right: string | Date | null | undefined,
|
||||
@@ -505,19 +484,7 @@ export function friendlyDate(date: Date): string {
|
||||
// TIME-BASED UTILITIES
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Gets appropriate greeting based on time of day
|
||||
* @param date - Date to get greeting for (defaults to now)
|
||||
* @returns "Bom dia", "Boa tarde", or "Boa noite"
|
||||
*/
|
||||
export function getGreeting(date: Date = new Date()): string {
|
||||
const hour = date.getHours();
|
||||
if (hour >= 5 && hour < 12) return "Bom dia";
|
||||
if (hour >= 12 && hour < 18) return "Boa tarde";
|
||||
return "Boa noite";
|
||||
}
|
||||
|
||||
export function getGreetingInTimeZone(
|
||||
function getGreetingInTimeZone(
|
||||
timeZone: string,
|
||||
date: Date = new Date(),
|
||||
): string {
|
||||
@@ -531,7 +498,7 @@ export function getBusinessGreeting(date: Date = new Date()): string {
|
||||
return getGreetingInTimeZone(OPENMONETIS_TIME_ZONE, date);
|
||||
}
|
||||
|
||||
export function formatCurrentDateInTimeZone(
|
||||
function formatCurrentDateInTimeZone(
|
||||
timeZone: string,
|
||||
date: Date = new Date(),
|
||||
): string {
|
||||
@@ -550,6 +517,3 @@ export function formatCurrentDateInTimeZone(
|
||||
export function formatBusinessCurrentDate(date: Date = new Date()): string {
|
||||
return formatCurrentDateInTimeZone(OPENMONETIS_TIME_ZONE, date);
|
||||
}
|
||||
|
||||
// Re-export MONTH_NAMES for convenience
|
||||
export { MONTH_NAMES };
|
||||
|
||||
Reference in New Issue
Block a user