feat: enhance global styles and component themes with new semantic colors; integrate ThemeProvider for improved theme management and update color usage across various components for consistency
This commit is contained in:
800
app/globals.css
800
app/globals.css
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ import "./globals.css";
|
||||
import { AuthSessionProvider } from "@/components/providers/session-provider";
|
||||
import { QueryProvider } from "@/components/providers/query-provider";
|
||||
import { BackgroundProvider } from "@/components/providers/background-provider";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
|
||||
const _geist = Geist({ subsets: ["latin"] });
|
||||
const _geistMono = Geist_Mono({ subsets: ["latin"] });
|
||||
@@ -22,12 +23,19 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="fr">
|
||||
<html lang="fr" suppressHydrationWarning>
|
||||
<body className="font-sans antialiased">
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<BackgroundProvider />
|
||||
<QueryProvider>
|
||||
<AuthSessionProvider>{children}</AuthSessionProvider>
|
||||
</QueryProvider>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
@@ -188,7 +188,7 @@ export function AccountCard({
|
||||
? "text-base"
|
||||
: "text-xl",
|
||||
!compact && !isMobile && "mb-1.5",
|
||||
realBalance >= 0 ? "text-emerald-600" : "text-red-600",
|
||||
realBalance >= 0 ? "text-success" : "text-destructive",
|
||||
)}
|
||||
>
|
||||
{formatCurrency(realBalance)}
|
||||
|
||||
@@ -125,7 +125,7 @@ export function ParentCategoryRow({
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => onDelete(parent.id)}
|
||||
className="text-red-600"
|
||||
className="text-destructive"
|
||||
>
|
||||
<Trash2 className="w-4 h-4 mr-2" />
|
||||
Supprimer
|
||||
|
||||
@@ -89,7 +89,7 @@ export function AccountsSummary({ data }: AccountsSummaryProps) {
|
||||
<span
|
||||
className={cn(
|
||||
"text-xs font-semibold tabular-nums ml-auto",
|
||||
folderTotal >= 0 ? "text-emerald-600" : "text-red-600",
|
||||
folderTotal >= 0 ? "text-success" : "text-destructive",
|
||||
)}
|
||||
>
|
||||
{formatCurrency(folderTotal)}
|
||||
@@ -131,8 +131,8 @@ export function AccountsSummary({ data }: AccountsSummaryProps) {
|
||||
className={cn(
|
||||
"font-bold tabular-nums text-base",
|
||||
realBalance >= 0
|
||||
? "text-emerald-600 dark:text-emerald-400"
|
||||
: "text-red-600 dark:text-red-400",
|
||||
? "text-success"
|
||||
: "text-destructive",
|
||||
)}
|
||||
>
|
||||
{formatCurrency(realBalance)}
|
||||
@@ -204,7 +204,7 @@ export function AccountsSummary({ data }: AccountsSummaryProps) {
|
||||
<span
|
||||
className={cn(
|
||||
"text-xs font-semibold tabular-nums ml-auto",
|
||||
orphanTotal >= 0 ? "text-emerald-600" : "text-red-600",
|
||||
orphanTotal >= 0 ? "text-success" : "text-destructive",
|
||||
)}
|
||||
>
|
||||
{formatCurrency(orphanTotal)}
|
||||
@@ -245,8 +245,8 @@ export function AccountsSummary({ data }: AccountsSummaryProps) {
|
||||
className={cn(
|
||||
"font-bold tabular-nums text-base",
|
||||
realBalance >= 0
|
||||
? "text-emerald-600 dark:text-emerald-400"
|
||||
: "text-red-600 dark:text-red-400",
|
||||
? "text-success"
|
||||
: "text-destructive",
|
||||
)}
|
||||
>
|
||||
{formatCurrency(realBalance)}
|
||||
|
||||
@@ -60,7 +60,7 @@ export function OverviewCards({ data }: OverviewCardsProps) {
|
||||
<div className="grid gap-4 sm:gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-5">
|
||||
<Card className="stat-card-gradient-1 card-hover group relative overflow-hidden">
|
||||
{/* Icône en arrière-plan */}
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] dark:opacity-[0.03] z-0 pointer-events-none">
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] z-0 pointer-events-none">
|
||||
<Wallet className="h-20 w-20 sm:h-24 sm:w-24 md:h-28 md:w-28 text-primary" strokeWidth={1} />
|
||||
</div>
|
||||
<CardHeader className="flex flex-row items-start justify-between space-y-0 pb-3 px-5 pt-5 sm:px-6 sm:pt-6 relative z-10">
|
||||
@@ -73,8 +73,8 @@ export function OverviewCards({ data }: OverviewCardsProps) {
|
||||
className={cn(
|
||||
"text-2xl sm:text-3xl md:text-3xl lg:text-2xl xl:text-3xl font-black tracking-tight mb-3 leading-tight break-words",
|
||||
totalBalance >= 0
|
||||
? "text-emerald-600 dark:text-emerald-400"
|
||||
: "text-red-600 dark:text-red-400"
|
||||
? "text-success"
|
||||
: "text-destructive"
|
||||
)}
|
||||
>
|
||||
{formatCurrency(totalBalance)}
|
||||
@@ -87,7 +87,7 @@ export function OverviewCards({ data }: OverviewCardsProps) {
|
||||
|
||||
<Card className="stat-card-gradient-2 card-hover group relative overflow-hidden">
|
||||
{/* Icône en arrière-plan */}
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] dark:opacity-[0.03] z-0 pointer-events-none">
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] z-0 pointer-events-none">
|
||||
<TrendingUp className="h-20 w-20 sm:h-24 sm:w-24 md:h-28 md:w-28 text-success" strokeWidth={1} />
|
||||
</div>
|
||||
<CardHeader className="flex flex-row items-start justify-between space-y-0 pb-3 px-5 pt-5 sm:px-6 sm:pt-6 relative z-10">
|
||||
@@ -110,7 +110,7 @@ export function OverviewCards({ data }: OverviewCardsProps) {
|
||||
|
||||
<Card className="stat-card-gradient-3 card-hover group relative overflow-hidden">
|
||||
{/* Icône en arrière-plan */}
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] dark:opacity-[0.03] z-0 pointer-events-none">
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] z-0 pointer-events-none">
|
||||
<TrendingDown className="h-20 w-20 sm:h-24 sm:w-24 md:h-28 md:w-28 text-destructive" strokeWidth={1} />
|
||||
</div>
|
||||
<CardHeader className="flex flex-row items-start justify-between space-y-0 pb-3 px-5 pt-5 sm:px-6 sm:pt-6 relative z-10">
|
||||
@@ -133,7 +133,7 @@ export function OverviewCards({ data }: OverviewCardsProps) {
|
||||
|
||||
<Card className="stat-card-gradient-4 card-hover group relative overflow-hidden">
|
||||
{/* Icône en arrière-plan */}
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] dark:opacity-[0.03] z-0 pointer-events-none">
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] z-0 pointer-events-none">
|
||||
<CreditCard className="h-20 w-20 sm:h-24 sm:w-24 md:h-28 md:w-28 text-chart-4" strokeWidth={1} />
|
||||
</div>
|
||||
<CardHeader className="flex flex-row items-start justify-between space-y-0 pb-3 px-5 pt-5 sm:px-6 sm:pt-6 relative z-10">
|
||||
@@ -153,7 +153,7 @@ export function OverviewCards({ data }: OverviewCardsProps) {
|
||||
|
||||
<Card className="stat-card-gradient-5 card-hover group relative overflow-hidden">
|
||||
{/* Icône en arrière-plan */}
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] dark:opacity-[0.03] z-0 pointer-events-none">
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] z-0 pointer-events-none">
|
||||
<Tag className="h-20 w-20 sm:h-24 sm:w-24 md:h-28 md:w-28 text-chart-5" strokeWidth={1} />
|
||||
</div>
|
||||
<CardHeader className="flex flex-row items-start justify-between space-y-0 pb-3 px-5 pt-5 sm:px-6 sm:pt-6 relative z-10">
|
||||
|
||||
@@ -88,8 +88,8 @@ export function RecentTransactions({ data }: RecentTransactionsProps) {
|
||||
className={cn(
|
||||
"font-black tabular-nums text-sm md:text-base shrink-0 md:hidden",
|
||||
transaction.amount >= 0
|
||||
? "text-emerald-600 dark:text-emerald-400"
|
||||
: "text-red-600 dark:text-red-400"
|
||||
? "text-success"
|
||||
: "text-destructive"
|
||||
)}
|
||||
>
|
||||
{transaction.amount >= 0 ? "+" : ""}
|
||||
@@ -130,8 +130,8 @@ export function RecentTransactions({ data }: RecentTransactionsProps) {
|
||||
className={cn(
|
||||
"font-black tabular-nums text-base md:text-lg shrink-0 hidden md:block leading-tight",
|
||||
transaction.amount >= 0
|
||||
? "text-emerald-600 dark:text-emerald-400"
|
||||
: "text-red-600 dark:text-red-400"
|
||||
? "text-success"
|
||||
: "text-destructive"
|
||||
)}
|
||||
>
|
||||
{transaction.amount >= 0 ? "+" : ""}
|
||||
|
||||
@@ -88,20 +88,17 @@ function SidebarContent({
|
||||
"w-full justify-start gap-4 h-12 rounded-2xl",
|
||||
isActive &&
|
||||
"bg-gradient-to-r from-primary/15 via-primary/10 to-primary/5 border-2 border-primary/30 shadow-lg shadow-primary/10 backdrop-blur-sm",
|
||||
collapsed && "justify-center px-2 w-12 mx-auto",
|
||||
collapsed && "justify-center px-2 w-12 mx-auto"
|
||||
)}
|
||||
>
|
||||
<item.icon
|
||||
className={cn(
|
||||
"w-5 h-5 shrink-0",
|
||||
isActive && "text-primary",
|
||||
)}
|
||||
className={cn("w-5 h-5 shrink-0", isActive && "text-primary")}
|
||||
/>
|
||||
{!collapsed && (
|
||||
<span
|
||||
className={cn(
|
||||
"font-semibold text-sm",
|
||||
isActive && "text-primary font-bold",
|
||||
isActive && "text-primary font-bold"
|
||||
)}
|
||||
>
|
||||
{item.label}
|
||||
@@ -116,7 +113,7 @@ function SidebarContent({
|
||||
<div
|
||||
className={cn(
|
||||
"border-t border-border/30 space-y-2",
|
||||
collapsed ? "p-2" : "p-4",
|
||||
collapsed ? "p-2" : "p-4"
|
||||
)}
|
||||
>
|
||||
<Link href="/settings" onClick={handleLinkClick}>
|
||||
@@ -124,7 +121,7 @@ function SidebarContent({
|
||||
variant="ghost"
|
||||
className={cn(
|
||||
"w-full justify-start gap-4 h-12 rounded-2xl",
|
||||
collapsed && "justify-center px-2 w-12 mx-auto",
|
||||
collapsed && "justify-center px-2 w-12 mx-auto"
|
||||
)}
|
||||
>
|
||||
<Settings className="w-5 h-5 shrink-0" />
|
||||
@@ -139,7 +136,7 @@ function SidebarContent({
|
||||
className={cn(
|
||||
"w-full justify-start gap-4 h-12 rounded-2xl",
|
||||
"text-destructive",
|
||||
collapsed && "justify-center px-2 w-12 mx-auto",
|
||||
collapsed && "justify-center px-2 w-12 mx-auto"
|
||||
)}
|
||||
>
|
||||
<LogOut className="w-5 h-5 shrink-0" />
|
||||
@@ -164,7 +161,10 @@ export function Sidebar({ open, onOpenChange }: SidebarProps) {
|
||||
if (isMobile) {
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={onOpenChange}>
|
||||
<SheetContent side="left" className="w-64 p-0">
|
||||
<SheetContent
|
||||
side="left"
|
||||
className="w-64 p-0 bg-sidebar text-sidebar-foreground border-sidebar-border"
|
||||
>
|
||||
<SheetTitle className="sr-only">Navigation</SheetTitle>
|
||||
<div className="flex flex-col h-full">
|
||||
<SidebarContent
|
||||
@@ -180,15 +180,15 @@ export function Sidebar({ open, onOpenChange }: SidebarProps) {
|
||||
return (
|
||||
<aside
|
||||
className={cn(
|
||||
"hidden md:flex flex-col h-screen glass border-r border-border",
|
||||
"hidden md:flex flex-col h-screen bg-sidebar text-sidebar-foreground border-r border-sidebar-border",
|
||||
"backdrop-blur-xl",
|
||||
collapsed ? "w-16" : "w-64",
|
||||
collapsed ? "w-16" : "w-64"
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center border-b border-border/30",
|
||||
collapsed ? "justify-center p-4" : "justify-between p-6",
|
||||
collapsed ? "justify-center p-4" : "justify-between p-6"
|
||||
)}
|
||||
>
|
||||
{!collapsed && (
|
||||
@@ -205,10 +205,7 @@ export function Sidebar({ open, onOpenChange }: SidebarProps) {
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => setCollapsed(!collapsed)}
|
||||
className={cn(
|
||||
"rounded-xl",
|
||||
collapsed ? "" : "ml-auto",
|
||||
)}
|
||||
className={cn("rounded-xl", collapsed ? "" : "ml-auto")}
|
||||
>
|
||||
{collapsed ? (
|
||||
<ChevronRight className="w-5 h-5" />
|
||||
|
||||
@@ -74,7 +74,7 @@ export function DraggableAccountItem({
|
||||
<span
|
||||
className={cn(
|
||||
"text-sm tabular-nums",
|
||||
realBalance >= 0 ? "text-emerald-600" : "text-red-600",
|
||||
realBalance >= 0 ? "text-success" : "text-destructive",
|
||||
)}
|
||||
>
|
||||
{formatCurrency(realBalance)}
|
||||
|
||||
@@ -120,7 +120,7 @@ export function DraggableFolderItem({
|
||||
<span
|
||||
className={cn(
|
||||
"text-sm font-semibold tabular-nums",
|
||||
folderTotal >= 0 ? "text-emerald-600" : "text-red-600",
|
||||
folderTotal >= 0 ? "text-success" : "text-destructive",
|
||||
)}
|
||||
>
|
||||
{formatCurrency(folderTotal)}
|
||||
@@ -145,7 +145,7 @@ export function DraggableFolderItem({
|
||||
{folder.id !== "folder-root" && (
|
||||
<DropdownMenuItem
|
||||
onClick={() => onDelete(folder.id)}
|
||||
className="text-red-600"
|
||||
className="text-destructive"
|
||||
>
|
||||
<Trash2 className="w-4 h-4 mr-2" />
|
||||
Supprimer
|
||||
|
||||
@@ -505,9 +505,9 @@ export function OFXImportDialog({
|
||||
{importResults.map((result, i) => (
|
||||
<div key={i} className="flex items-center gap-2">
|
||||
{result.error ? (
|
||||
<AlertCircle className="w-4 h-4 text-red-500 flex-shrink-0" />
|
||||
<AlertCircle className="w-4 h-4 text-destructive flex-shrink-0" />
|
||||
) : (
|
||||
<CheckCircle2 className="w-4 h-4 text-emerald-500 flex-shrink-0" />
|
||||
<CheckCircle2 className="w-4 h-4 text-success flex-shrink-0" />
|
||||
)}
|
||||
<span className="truncate">{result.fileName}</span>
|
||||
{!result.error && (
|
||||
@@ -524,16 +524,16 @@ export function OFXImportDialog({
|
||||
|
||||
{step === "success" && (
|
||||
<div className="py-4">
|
||||
<CheckCircle2 className="w-16 h-16 mx-auto mb-4 text-emerald-600" />
|
||||
<CheckCircle2 className="w-16 h-16 mx-auto mb-4 text-success" />
|
||||
|
||||
{importResults.length > 1 && (
|
||||
<div className="max-h-48 overflow-auto space-y-1 text-sm mb-4 border rounded-lg p-2">
|
||||
{importResults.map((result, i) => (
|
||||
<div key={i} className="flex items-center gap-2 py-1">
|
||||
{result.error ? (
|
||||
<AlertCircle className="w-4 h-4 text-red-500 flex-shrink-0" />
|
||||
<AlertCircle className="w-4 h-4 text-destructive flex-shrink-0" />
|
||||
) : (
|
||||
<CheckCircle2 className="w-4 h-4 text-emerald-500 flex-shrink-0" />
|
||||
<CheckCircle2 className="w-4 h-4 text-success flex-shrink-0" />
|
||||
)}
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="truncate font-medium">
|
||||
@@ -544,7 +544,7 @@ export function OFXImportDialog({
|
||||
</p>
|
||||
</div>
|
||||
{result.error ? (
|
||||
<span className="text-xs text-red-500 flex-shrink-0">
|
||||
<span className="text-xs text-destructive flex-shrink-0">
|
||||
{result.error}
|
||||
</span>
|
||||
) : (
|
||||
@@ -559,7 +559,7 @@ export function OFXImportDialog({
|
||||
)}
|
||||
|
||||
{errorCount > 0 && (
|
||||
<p className="text-sm text-red-600 mb-4 text-center">
|
||||
<p className="text-sm text-destructive mb-4 text-center">
|
||||
{errorCount} fichier{errorCount > 1 ? "s" : ""} en erreur
|
||||
</p>
|
||||
)}
|
||||
@@ -572,7 +572,7 @@ export function OFXImportDialog({
|
||||
|
||||
{step === "error" && (
|
||||
<div className="text-center py-4">
|
||||
<AlertCircle className="w-16 h-16 mx-auto mb-4 text-red-600" />
|
||||
<AlertCircle className="w-16 h-16 mx-auto mb-4 text-destructive" />
|
||||
<Button onClick={() => setStep("upload")}>Réessayer</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { ReactNode } from "react";
|
||||
import { ReactNode, useEffect, useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Menu } from "lucide-react";
|
||||
import { useSidebarContext } from "@/components/layout/sidebar-context";
|
||||
@@ -21,6 +21,30 @@ export function PageHeader({
|
||||
}: PageHeaderProps) {
|
||||
const { setOpen } = useSidebarContext();
|
||||
const isMobile = useIsMobile();
|
||||
const [textColor, setTextColor] = useState("var(--foreground)");
|
||||
|
||||
useEffect(() => {
|
||||
const checkDarkBackground = () => {
|
||||
const pageBackground = document.querySelector(".page-background");
|
||||
if (pageBackground?.classList.contains("bg-solid-dark")) {
|
||||
setTextColor("#f5f5f5");
|
||||
} else {
|
||||
setTextColor("var(--foreground)");
|
||||
}
|
||||
};
|
||||
|
||||
checkDarkBackground();
|
||||
const observer = new MutationObserver(checkDarkBackground);
|
||||
const pageBackground = document.querySelector(".page-background");
|
||||
if (pageBackground) {
|
||||
observer.observe(pageBackground, {
|
||||
attributes: true,
|
||||
attributeFilter: ["class"],
|
||||
});
|
||||
}
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 mb-2">
|
||||
@@ -37,7 +61,13 @@ export function PageHeader({
|
||||
)}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-start justify-between gap-2 mb-2">
|
||||
<h1 className="text-2xl md:text-4xl lg:text-5xl font-black text-foreground tracking-tight leading-tight flex-1 min-w-0">
|
||||
<h1
|
||||
className="text-2xl md:text-4xl lg:text-5xl font-black tracking-tight leading-tight flex-1 min-w-0"
|
||||
style={{
|
||||
color: textColor,
|
||||
WebkitTextFillColor: textColor,
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
{rightContent && <div className="shrink-0">{rightContent}</div>}
|
||||
|
||||
@@ -133,7 +133,7 @@ export function RuleCreateDialog({
|
||||
catégorisées.
|
||||
</p>
|
||||
{existingCategory && (
|
||||
<div className="flex items-center gap-2 text-xs text-amber-600 dark:text-amber-400">
|
||||
<div className="flex items-center gap-2 text-xs text-warning">
|
||||
<AlertCircle className="h-3 w-3" />
|
||||
<span>
|
||||
Ce mot-clé existe déjà dans "{existingCategory.name}
|
||||
|
||||
@@ -35,42 +35,49 @@ const DEFAULT_BACKGROUNDS: Array<{
|
||||
value: BackgroundType;
|
||||
label: string;
|
||||
preview: string;
|
||||
description?: string;
|
||||
}> = [
|
||||
{
|
||||
value: "default",
|
||||
label: "Par défaut",
|
||||
preview:
|
||||
"linear-gradient(135deg, oklch(0.98 0.01 280) 0%, oklch(0.97 0.012 270) 50%, oklch(0.98 0.01 290) 100%)",
|
||||
label: "Neutre",
|
||||
preview: "linear-gradient(135deg, oklch(0.985 0 0) 0%, oklch(0.97 0.005 260) 50%, oklch(0.985 0 0) 100%)",
|
||||
description: "Fond neutre et élégant",
|
||||
},
|
||||
{
|
||||
value: "gradient-blue",
|
||||
label: "Dégradé bleu",
|
||||
preview: "linear-gradient(135deg, #e0f2fe 0%, #bae6fd 50%, #7dd3fc 100%)",
|
||||
label: "Océan",
|
||||
preview: "linear-gradient(135deg, oklch(0.95 0.03 230) 0%, oklch(0.88 0.08 225) 50%, oklch(0.78 0.12 220) 100%)",
|
||||
description: "Dégradé bleu apaisant",
|
||||
},
|
||||
{
|
||||
value: "gradient-purple",
|
||||
label: "Dégradé violet",
|
||||
preview: "linear-gradient(135deg, #f3e8ff 0%, #e9d5ff 50%, #d8b4fe 100%)",
|
||||
label: "Améthyste",
|
||||
preview: "linear-gradient(135deg, oklch(0.95 0.04 300) 0%, oklch(0.88 0.1 295) 50%, oklch(0.78 0.15 290) 100%)",
|
||||
description: "Dégradé violet sophistiqué",
|
||||
},
|
||||
{
|
||||
value: "gradient-green",
|
||||
label: "Dégradé vert",
|
||||
preview: "linear-gradient(135deg, #dcfce7 0%, #bbf7d0 50%, #86efac 100%)",
|
||||
label: "Forêt",
|
||||
preview: "linear-gradient(135deg, oklch(0.95 0.04 160) 0%, oklch(0.88 0.1 155) 50%, oklch(0.78 0.14 150) 100%)",
|
||||
description: "Dégradé vert naturel",
|
||||
},
|
||||
{
|
||||
value: "gradient-orange",
|
||||
label: "Dégradé orange",
|
||||
preview: "linear-gradient(135deg, #fff7ed 0%, #ffedd5 50%, #fed7aa 100%)",
|
||||
label: "Aurore",
|
||||
preview: "linear-gradient(135deg, oklch(0.97 0.03 80) 0%, oklch(0.92 0.08 60) 50%, oklch(0.85 0.14 45) 100%)",
|
||||
description: "Dégradé orange chaleureux",
|
||||
},
|
||||
{
|
||||
value: "solid-light",
|
||||
label: "Solide clair",
|
||||
preview: "#ffffff",
|
||||
label: "Lumineux",
|
||||
preview: "linear-gradient(135deg, oklch(1 0 0) 0%, oklch(0.98 0.005 260) 100%)",
|
||||
description: "Fond blanc épuré",
|
||||
},
|
||||
{
|
||||
value: "solid-dark",
|
||||
label: "Solide sombre",
|
||||
preview: "#1e293b",
|
||||
label: "Minuit",
|
||||
preview: "linear-gradient(135deg, oklch(0.18 0.02 260) 0%, oklch(0.08 0.015 250) 100%)",
|
||||
description: "Fond sombre immersif",
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ export function DangerZoneCard({
|
||||
const result = await onDeduplicate();
|
||||
if (result.deletedCount > 0) {
|
||||
alert(
|
||||
`${result.deletedCount} transaction${result.deletedCount > 1 ? "s" : ""} en double supprimée${result.deletedCount > 1 ? "s" : ""}`,
|
||||
`${result.deletedCount} transaction${result.deletedCount > 1 ? "s" : ""} en double supprimée${result.deletedCount > 1 ? "s" : ""}`
|
||||
);
|
||||
} else {
|
||||
alert("Aucun doublon trouvé");
|
||||
@@ -59,9 +59,9 @@ export function DangerZoneCard({
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Card className="border-red-200">
|
||||
<Card className="border-destructive/30 card-hover">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-red-600">
|
||||
<CardTitle className="flex items-center gap-2 text-destructive">
|
||||
<Trash2 className="w-5 h-5" />
|
||||
Zone dangereuse
|
||||
</CardTitle>
|
||||
|
||||
@@ -36,13 +36,13 @@ export function SavingsTrendChart({
|
||||
<CardTitle>Évolution des économies</CardTitle>
|
||||
<div className="flex items-center gap-2">
|
||||
{isPositive ? (
|
||||
<TrendingUp className="w-4 h-4 text-emerald-600" />
|
||||
<TrendingUp className="w-4 h-4 text-success" />
|
||||
) : (
|
||||
<TrendingDown className="w-4 h-4 text-red-600" />
|
||||
<TrendingDown className="w-4 h-4 text-destructive" />
|
||||
)}
|
||||
<span
|
||||
className={`text-sm font-semibold ${
|
||||
isPositive ? "text-emerald-600" : "text-red-600"
|
||||
isPositive ? "text-success" : "text-destructive"
|
||||
}`}
|
||||
>
|
||||
{formatCurrency(latestSavings)}
|
||||
|
||||
@@ -23,8 +23,8 @@ export function StatsSummaryCards({
|
||||
<div className="grid gap-3 md:gap-4 grid-cols-2 md:grid-cols-4">
|
||||
<Card className="stat-card-textured relative overflow-hidden">
|
||||
{/* Icône en arrière-plan */}
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] dark:opacity-[0.03] z-0 pointer-events-none">
|
||||
<TrendingUp className="h-16 w-16 md:h-20 md:w-20 text-emerald-600 dark:text-emerald-400" strokeWidth={1} />
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] z-0 pointer-events-none">
|
||||
<TrendingUp className="h-16 w-16 md:h-20 md:w-20 text-success" strokeWidth={1} />
|
||||
</div>
|
||||
<CardHeader className="pb-3 px-5 pt-5 relative z-10">
|
||||
<CardTitle className="text-[10px] md:text-xs font-semibold text-muted-foreground/80 uppercase tracking-wider">
|
||||
@@ -32,7 +32,7 @@ export function StatsSummaryCards({
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="px-5 pb-5 pt-0 relative z-10">
|
||||
<div className="text-xl md:text-2xl font-black text-emerald-600 dark:text-emerald-400 tracking-tight">
|
||||
<div className="text-xl md:text-2xl font-black text-success tracking-tight">
|
||||
{formatCurrency(totalIncome)}
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -40,8 +40,8 @@ export function StatsSummaryCards({
|
||||
|
||||
<Card className="stat-card-textured relative overflow-hidden">
|
||||
{/* Icône en arrière-plan */}
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] dark:opacity-[0.03] z-0 pointer-events-none">
|
||||
<TrendingDown className="h-16 w-16 md:h-20 md:w-20 text-red-600 dark:text-red-400" strokeWidth={1} />
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] z-0 pointer-events-none">
|
||||
<TrendingDown className="h-16 w-16 md:h-20 md:w-20 text-destructive" strokeWidth={1} />
|
||||
</div>
|
||||
<CardHeader className="pb-3 px-5 pt-5 relative z-10">
|
||||
<CardTitle className="text-[10px] md:text-xs font-semibold text-muted-foreground/80 uppercase tracking-wider">
|
||||
@@ -49,7 +49,7 @@ export function StatsSummaryCards({
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="px-5 pb-5 pt-0 relative z-10">
|
||||
<div className="text-xl md:text-2xl font-black text-red-600 dark:text-red-400 tracking-tight">
|
||||
<div className="text-xl md:text-2xl font-black text-destructive tracking-tight">
|
||||
{formatCurrency(totalExpenses)}
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -57,7 +57,7 @@ export function StatsSummaryCards({
|
||||
|
||||
<Card className="stat-card-textured relative overflow-hidden">
|
||||
{/* Icône en arrière-plan */}
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] dark:opacity-[0.03] z-0 pointer-events-none">
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] z-0 pointer-events-none">
|
||||
<ArrowRight className="h-16 w-16 md:h-20 md:w-20 text-muted-foreground/40" strokeWidth={1} />
|
||||
</div>
|
||||
<CardHeader className="pb-3 px-5 pt-5 relative z-10">
|
||||
@@ -74,12 +74,12 @@ export function StatsSummaryCards({
|
||||
|
||||
<Card className="stat-card-textured relative overflow-hidden">
|
||||
{/* Icône en arrière-plan */}
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] dark:opacity-[0.03] z-0 pointer-events-none">
|
||||
<div className="absolute bottom-2 right-2 opacity-[0.04] z-0 pointer-events-none">
|
||||
<div className={cn(
|
||||
"h-16 w-16 md:h-20 md:w-20 rounded-full border-2",
|
||||
savings >= 0
|
||||
? "border-emerald-600 dark:border-emerald-400"
|
||||
: "border-red-600 dark:border-red-400"
|
||||
? "border-success"
|
||||
: "border-destructive"
|
||||
)} />
|
||||
</div>
|
||||
<CardHeader className="pb-3 px-5 pt-5 relative z-10">
|
||||
@@ -91,7 +91,7 @@ export function StatsSummaryCards({
|
||||
<div
|
||||
className={cn(
|
||||
"text-xl md:text-2xl font-black tracking-tight",
|
||||
savings >= 0 ? "text-emerald-600 dark:text-emerald-400" : "text-red-600 dark:text-red-400",
|
||||
savings >= 0 ? "text-success" : "text-destructive",
|
||||
)}
|
||||
>
|
||||
{formatCurrency(savings)}
|
||||
|
||||
@@ -45,7 +45,7 @@ export function TopExpensesList({
|
||||
<p className="font-medium text-xs md:text-sm truncate flex-1">
|
||||
{expense.description}
|
||||
</p>
|
||||
<div className="text-red-600 font-semibold tabular-nums text-xs md:text-sm shrink-0">
|
||||
<div className="text-destructive font-semibold tabular-nums text-xs md:text-sm shrink-0">
|
||||
{formatCurrency(expense.amount)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -227,7 +227,7 @@ export function TransactionTable({
|
||||
);
|
||||
|
||||
return (
|
||||
<Card className="overflow-hidden w-full">
|
||||
<Card className="overflow-hidden w-full card-hover">
|
||||
<CardContent className="p-0 w-full">
|
||||
{transactions.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-12">
|
||||
@@ -323,8 +323,8 @@ export function TransactionTable({
|
||||
className={cn(
|
||||
"font-semibold tabular-nums text-base md:text-base shrink-0",
|
||||
transaction.amount >= 0
|
||||
? "text-emerald-600"
|
||||
: "text-red-600"
|
||||
? "text-success"
|
||||
: "text-destructive"
|
||||
)}
|
||||
>
|
||||
{transaction.amount >= 0 ? "+" : ""}
|
||||
@@ -398,7 +398,7 @@ export function TransactionTable({
|
||||
onDelete(transaction.id);
|
||||
}
|
||||
}}
|
||||
className="text-red-600 focus:text-red-600"
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<Trash2 className="w-4 h-4 mr-2" />
|
||||
Supprimer
|
||||
@@ -576,8 +576,8 @@ export function TransactionTable({
|
||||
className={cn(
|
||||
"p-3 text-right font-semibold tabular-nums",
|
||||
transaction.amount >= 0
|
||||
? "text-emerald-600"
|
||||
: "text-red-600"
|
||||
? "text-success"
|
||||
: "text-destructive"
|
||||
)}
|
||||
>
|
||||
{transaction.amount >= 0 ? "+" : ""}
|
||||
@@ -592,7 +592,7 @@ export function TransactionTable({
|
||||
className="p-1 hover:bg-muted rounded"
|
||||
>
|
||||
{transaction.isReconciled ? (
|
||||
<CheckCircle2 className="w-5 h-5 text-emerald-600" />
|
||||
<CheckCircle2 className="w-5 h-5 text-success" />
|
||||
) : (
|
||||
<Circle className="w-5 h-5 text-muted-foreground" />
|
||||
)}
|
||||
@@ -650,7 +650,7 @@ export function TransactionTable({
|
||||
onDelete(transaction.id);
|
||||
}
|
||||
}}
|
||||
className="text-red-600 focus:text-red-600"
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<Trash2 className="w-4 h-4 mr-2" />
|
||||
Supprimer
|
||||
|
||||
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
||||
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/30 aria-invalid:border-destructive overflow-hidden",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
@@ -14,7 +14,7 @@ const badgeVariants = cva(
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
"border-transparent bg-destructive text-destructive-foreground [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/30",
|
||||
outline:
|
||||
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
||||
},
|
||||
@@ -22,7 +22,7 @@ const badgeVariants = cva(
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
function Badge({
|
||||
|
||||
@@ -5,16 +5,16 @@ import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-semibold disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-xl text-sm font-semibold disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/30 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-gradient-to-r from-primary via-primary/95 to-primary/90 text-primary-foreground shadow-xl shadow-primary/30 backdrop-blur-sm border border-primary/20",
|
||||
destructive:
|
||||
"bg-gradient-to-r from-destructive via-destructive/95 to-destructive/90 text-white shadow-xl shadow-destructive/30 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 backdrop-blur-sm border border-destructive/20",
|
||||
"bg-gradient-to-r from-destructive via-destructive/95 to-destructive/90 text-destructive-foreground shadow-xl shadow-destructive/30 focus-visible:ring-destructive/30 backdrop-blur-sm border border-destructive/20",
|
||||
outline:
|
||||
"border-2 bg-background/95 backdrop-blur-md shadow-md dark:bg-input/40 dark:border-input",
|
||||
"border-2 bg-background/95 backdrop-blur-md shadow-md border-border",
|
||||
secondary:
|
||||
"bg-gradient-to-r from-secondary via-secondary/95 to-secondary/90 text-secondary-foreground backdrop-blur-sm",
|
||||
ghost: "backdrop-blur-sm",
|
||||
|
||||
@@ -201,7 +201,7 @@ function CalendarDayButton({
|
||||
data-range-end={modifiers.range_end}
|
||||
data-range-middle={modifiers.range_middle}
|
||||
className={cn(
|
||||
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 dark:hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70",
|
||||
"data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-ring/50 hover:text-accent-foreground flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:ring-[3px] data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md [&>span]:text-xs [&>span]:opacity-70",
|
||||
defaultClassNames.day,
|
||||
className,
|
||||
)}
|
||||
|
||||
@@ -14,8 +14,8 @@ function Checkbox({
|
||||
<CheckboxPrimitive.Root
|
||||
data-slot="checkbox"
|
||||
className={cn(
|
||||
"peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
"peer border-input bg-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/30 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
|
||||
@@ -126,7 +126,7 @@ function ContextMenuItem({
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/15 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -39,7 +39,7 @@ function DialogOverlay({
|
||||
data-slot="dialog-overlay"
|
||||
className={cn(
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||
className,
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
@@ -60,8 +60,8 @@ function DialogContent({
|
||||
<DialogPrimitive.Content
|
||||
data-slot="dialog-content"
|
||||
className={cn(
|
||||
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 flex flex-col w-full max-w-[calc(100%-2rem)] max-h-[calc(100vh-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 overflow-y-auto sm:max-w-lg",
|
||||
className,
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 flex flex-col w-full max-w-[calc(100%-2rem)] max-h-[calc(100vh-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 overflow-y-auto sm:max-w-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
@@ -96,7 +96,7 @@ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
data-slot="dialog-footer"
|
||||
className={cn(
|
||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||
className,
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@@ -76,7 +76,7 @@ function DropdownMenuItem({
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/15 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -117,7 +117,7 @@ function FieldLabel({
|
||||
className={cn(
|
||||
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
|
||||
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4",
|
||||
"has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10",
|
||||
"has-data-[state=checked]:bg-primary/8 has-data-[state=checked]:border-primary",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -13,7 +13,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
data-slot="input-group"
|
||||
role="group"
|
||||
className={cn(
|
||||
"group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none",
|
||||
"group/input-group border-input bg-input relative flex w-full items-center rounded-md border shadow-xs outline-none",
|
||||
"h-9 has-[>textarea]:h-auto",
|
||||
|
||||
// Variants based on alignment.
|
||||
@@ -26,7 +26,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
"has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]",
|
||||
|
||||
// Error state.
|
||||
"has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
|
||||
"has-[[data-slot][aria-invalid=true]]:ring-destructive/30 has-[[data-slot][aria-invalid=true]]:border-destructive",
|
||||
|
||||
className,
|
||||
)}
|
||||
@@ -135,7 +135,7 @@ function InputGroupInput({
|
||||
<Input
|
||||
data-slot="input-group-control"
|
||||
className={cn(
|
||||
"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",
|
||||
"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -151,7 +151,7 @@ function InputGroupTextarea({
|
||||
<Textarea
|
||||
data-slot="input-group-control"
|
||||
className={cn(
|
||||
"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent",
|
||||
"flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -51,7 +51,7 @@ function InputOTPSlot({
|
||||
data-slot="input-otp-slot"
|
||||
data-active={isActive}
|
||||
className={cn(
|
||||
"data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
|
||||
"data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/30 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive bg-input border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -9,14 +9,14 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground",
|
||||
"dark:bg-input/40 border-input h-9 w-full min-w-0 rounded-lg border bg-background/50 backdrop-blur-sm px-3 py-1 text-base",
|
||||
"shadow-sm transition-all duration-200 outline-none",
|
||||
"bg-input border-border h-9 w-full min-w-0 rounded-lg border backdrop-blur-sm px-3 py-1 text-base",
|
||||
"shadow-sm outline-none",
|
||||
"file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium",
|
||||
"disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
"focus-visible:border-primary/50 focus-visible:ring-primary/20 focus-visible:ring-[3px] focus-visible:shadow-md focus-visible:shadow-primary/10",
|
||||
"hover:border-primary/30 hover:shadow-sm",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
className,
|
||||
"aria-invalid:ring-destructive/30 aria-invalid:border-destructive",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@@ -7,8 +7,8 @@ function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
|
||||
className={cn(
|
||||
"bg-muted w-fit text-muted-foreground pointer-events-none inline-flex h-5 min-w-5 items-center justify-center gap-1 rounded-sm px-1 font-sans text-xs font-medium select-none",
|
||||
"[&_svg:not([class*='size-'])]:size-3",
|
||||
"[[data-slot=tooltip-content]_&]:bg-background/20 [[data-slot=tooltip-content]_&]:text-background dark:[[data-slot=tooltip-content]_&]:bg-background/10",
|
||||
className,
|
||||
"[[data-slot=tooltip-content]_&]:bg-background/15 [[data-slot=tooltip-content]_&]:text-background",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
|
||||
@@ -103,7 +103,7 @@ function MenubarItem({
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/15 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -27,7 +27,7 @@ function RadioGroupItem({
|
||||
<RadioGroupPrimitive.Item
|
||||
data-slot="radio-group-item"
|
||||
className={cn(
|
||||
"border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/30 aria-invalid:border-destructive bg-input aspect-square size-4 shrink-0 rounded-full border shadow-xs outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -37,7 +37,7 @@ function SelectTrigger({
|
||||
data-slot="select-trigger"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/30 aria-invalid:border-destructive bg-input hover:bg-input/80 flex w-fit items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm whitespace-nowrap shadow-xs outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -13,7 +13,7 @@ function Switch({
|
||||
<SwitchPrimitive.Root
|
||||
data-slot="switch"
|
||||
className={cn(
|
||||
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -21,7 +21,7 @@ function Switch({
|
||||
<SwitchPrimitive.Thumb
|
||||
data-slot="switch-thumb"
|
||||
className={
|
||||
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
|
||||
"bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
|
||||
}
|
||||
/>
|
||||
</SwitchPrimitive.Root>
|
||||
|
||||
@@ -42,7 +42,7 @@ function TabsTrigger({
|
||||
<TabsPrimitive.Trigger
|
||||
data-slot="tabs-trigger"
|
||||
className={cn(
|
||||
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"data-[state=active]:bg-background data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring data-[state=active]:border-input text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -8,11 +8,11 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
||||
data-slot="textarea"
|
||||
className={cn(
|
||||
"border-input placeholder:text-muted-foreground",
|
||||
"dark:bg-input/40 flex field-sizing-content min-h-16 w-full rounded-lg border bg-background/50 backdrop-blur-sm px-3 py-2 text-base",
|
||||
"shadow-sm transition-all duration-200 outline-none",
|
||||
"bg-input flex field-sizing-content min-h-16 w-full rounded-lg border backdrop-blur-sm px-3 py-2 text-base",
|
||||
"shadow-sm outline-none",
|
||||
"focus-visible:border-primary/50 focus-visible:ring-primary/20 focus-visible:ring-[3px] focus-visible:shadow-md focus-visible:shadow-primary/10",
|
||||
"hover:border-primary/30 hover:shadow-sm",
|
||||
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
"aria-invalid:ring-destructive/30 aria-invalid:border-destructive",
|
||||
"disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
className,
|
||||
)}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const toggleVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap",
|
||||
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none aria-invalid:ring-destructive/30 aria-invalid:border-destructive whitespace-nowrap",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
@import "tailwindcss";
|
||||
@import "tw-animate-css";
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
:root {
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.145 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.145 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.145 0 0);
|
||||
--primary: oklch(0.205 0 0);
|
||||
--primary-foreground: oklch(0.985 0 0);
|
||||
--secondary: oklch(0.97 0 0);
|
||||
--secondary-foreground: oklch(0.205 0 0);
|
||||
--muted: oklch(0.97 0 0);
|
||||
--muted-foreground: oklch(0.556 0 0);
|
||||
--accent: oklch(0.97 0 0);
|
||||
--accent-foreground: oklch(0.205 0 0);
|
||||
--destructive: oklch(0.577 0.245 27.325);
|
||||
--destructive-foreground: oklch(0.577 0.245 27.325);
|
||||
--border: oklch(0.922 0 0);
|
||||
--input: oklch(0.922 0 0);
|
||||
--ring: oklch(0.708 0 0);
|
||||
--chart-1: oklch(0.646 0.222 41.116);
|
||||
--chart-2: oklch(0.6 0.118 184.704);
|
||||
--chart-3: oklch(0.398 0.07 227.392);
|
||||
--chart-4: oklch(0.828 0.189 84.429);
|
||||
--chart-5: oklch(0.769 0.188 70.08);
|
||||
--radius: 0.625rem;
|
||||
--sidebar: oklch(0.985 0 0);
|
||||
--sidebar-foreground: oklch(0.145 0 0);
|
||||
--sidebar-primary: oklch(0.205 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.97 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||
--sidebar-border: oklch(0.922 0 0);
|
||||
--sidebar-ring: oklch(0.708 0 0);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: oklch(0.145 0 0);
|
||||
--foreground: oklch(0.985 0 0);
|
||||
--card: oklch(0.145 0 0);
|
||||
--card-foreground: oklch(0.985 0 0);
|
||||
--popover: oklch(0.145 0 0);
|
||||
--popover-foreground: oklch(0.985 0 0);
|
||||
--primary: oklch(0.985 0 0);
|
||||
--primary-foreground: oklch(0.205 0 0);
|
||||
--secondary: oklch(0.269 0 0);
|
||||
--secondary-foreground: oklch(0.985 0 0);
|
||||
--muted: oklch(0.269 0 0);
|
||||
--muted-foreground: oklch(0.708 0 0);
|
||||
--accent: oklch(0.269 0 0);
|
||||
--accent-foreground: oklch(0.985 0 0);
|
||||
--destructive: oklch(0.396 0.141 25.723);
|
||||
--destructive-foreground: oklch(0.637 0.237 25.331);
|
||||
--border: oklch(0.269 0 0);
|
||||
--input: oklch(0.269 0 0);
|
||||
--ring: oklch(0.439 0 0);
|
||||
--chart-1: oklch(0.488 0.243 264.376);
|
||||
--chart-2: oklch(0.696 0.17 162.48);
|
||||
--chart-3: oklch(0.769 0.188 70.08);
|
||||
--chart-4: oklch(0.627 0.265 303.9);
|
||||
--chart-5: oklch(0.645 0.246 16.439);
|
||||
--sidebar: oklch(0.205 0 0);
|
||||
--sidebar-foreground: oklch(0.985 0 0);
|
||||
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||
--sidebar-accent: oklch(0.269 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||
--sidebar-border: oklch(0.269 0 0);
|
||||
--sidebar-ring: oklch(0.439 0 0);
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--font-sans: "Geist", "Geist Fallback";
|
||||
--font-mono: "Geist Mono", "Geist Mono Fallback";
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
--color-card-foreground: var(--card-foreground);
|
||||
--color-popover: var(--popover);
|
||||
--color-popover-foreground: var(--popover-foreground);
|
||||
--color-primary: var(--primary);
|
||||
--color-primary-foreground: var(--primary-foreground);
|
||||
--color-secondary: var(--secondary);
|
||||
--color-secondary-foreground: var(--secondary-foreground);
|
||||
--color-muted: var(--muted);
|
||||
--color-muted-foreground: var(--muted-foreground);
|
||||
--color-accent: var(--accent);
|
||||
--color-accent-foreground: var(--accent-foreground);
|
||||
--color-destructive: var(--destructive);
|
||||
--color-destructive-foreground: var(--destructive-foreground);
|
||||
--color-border: var(--border);
|
||||
--color-input: var(--input);
|
||||
--color-ring: var(--ring);
|
||||
--color-chart-1: var(--chart-1);
|
||||
--color-chart-2: var(--chart-2);
|
||||
--color-chart-3: var(--chart-3);
|
||||
--color-chart-4: var(--chart-4);
|
||||
--color-chart-5: var(--chart-5);
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
--color-sidebar: var(--sidebar);
|
||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||
--color-sidebar-primary: var(--sidebar-primary);
|
||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||
--color-sidebar-accent: var(--sidebar-accent);
|
||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||
--color-sidebar-border: var(--sidebar-border);
|
||||
--color-sidebar-ring: var(--sidebar-ring);
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user