Skip to content

Commit

Permalink
creating modal and user dropdown menu
Browse files Browse the repository at this point in the history
  • Loading branch information
mtguerson committed Apr 29, 2024
1 parent 597c21c commit 8f8c835
Show file tree
Hide file tree
Showing 13 changed files with 253 additions and 14 deletions.
7 changes: 1 addition & 6 deletions frontend/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,5 @@ module.exports = {
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
rules: {},
}
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"@headlessui/react": "^1.7.19",
"@hookform/resolvers": "^3.3.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-icons": "^1.3.0",
"@tanstack/react-query": "^5.29.0",
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function App() {
<Toaster />
</AuthProvider>

<ReactQueryDevtools />
<ReactQueryDevtools buttonPosition={"bottom-left"} />
</QueryClientProvider>
);
}
6 changes: 4 additions & 2 deletions frontend/src/view/components/DropdownMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export function DropdownMenuRoot({ children }: { children: React.ReactNode }) {

export function DropdownMenuTrigger({ children }: { children: React.ReactNode }) {
return (
<RdxDropdownMenu.Trigger className="outline-none">
<RdxDropdownMenu.Trigger className="outline-none" asChild>
{children}
</RdxDropdownMenu.Trigger>
);
Expand All @@ -27,7 +27,9 @@ export function DropdownMenuContent({ children, className }: DropdownMenuContent
<RdxDropdownMenu.Portal>
<RdxDropdownMenu.Content
className={cn(
"rounded-2xl p-2 bg-white space-y-2 shadow-[0px_11px_20px_0px_rgba(0,0,0,0.10)] data-[side=bottom]:animate-slide-up-and-fade z-50",
"rounded-2xl p-2 bg-white space-y-2 shadow-[0px_11px_20px_0px_rgba(0,0,0,0.10)]",
"data-[side=bottom]:animate-slide-up-and-fade z-50",
"data-[side=top]:animate-slide-down-and-fade z-50",
className,
)}
>
Expand Down
57 changes: 57 additions & 0 deletions frontend/src/view/components/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import * as Dialog from '@radix-ui/react-dialog';
import { cn } from '../../app/utils/cn';
import { Cross2Icon } from '@radix-ui/react-icons';

interface ModalProps {
open: boolean;
children: React.ReactNode;
title: string;
rightAction?: React.ReactNode;
onClose?(): void;
}

export function Modal({ open, title, children, rightAction, onClose }: ModalProps) {
return (
<Dialog.Root open={open} onOpenChange={onClose}>
<Dialog.Portal>
<Dialog.Overlay
className={cn(
"fixed inset-0 bg-black/80 backdrop-blur-sm z-50",
"data-[state=open]:animate-overlay-show"
)}
/>

<Dialog.Content
className={cn(
"fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 p-6 space-y-10 bg-white rounded-2xl z-[51] shadow-[0px_11px_20px_0px_rgba(0,0,0,0.10)] w-full max-w-[400px] outline-none",
"data-[state=open]:animate-content-show"
)}
>
<header className="h-12 flex items-center justify-between text-gray-800">

<button
className="w-12 h-12 flex items-center justify-center outline-none"
onClick={onClose}
>
<Cross2Icon className="w-6 h-6" />
</button>

<span className="text-lg tracking-[-1px] font-bold">
{title}
</span>

<div className="w-12 h-12 flex items-center justify-center">
{rightAction}
</div>

</header>

<div>
{children}
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}

11 changes: 8 additions & 3 deletions frontend/src/view/pages/Dashboard/components/Fab/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { PlusIcon } from "@radix-ui/react-icons";
import { DropdownMenu, DropdownMenuItem } from "../../../../components/DropdownMenu";
import { CategoryIcon } from "../../../../components/icons/categories/CategoryIcon";
import { BankAccountIcon } from "../../../../components/icons/BankAccountIcon";

export function Fab() {
return (
Expand All @@ -14,15 +16,18 @@ export function Fab() {
</DropdownMenu.Trigger>

<DropdownMenu.Content>
<DropdownMenuItem>
<DropdownMenuItem className="gap-2">
<CategoryIcon type="expense" />
Nova Despesa
</DropdownMenuItem>

<DropdownMenuItem>
<DropdownMenuItem className="gap-2">
<CategoryIcon type="income" />
Nova Receita
</DropdownMenuItem>

<DropdownMenuItem>
<DropdownMenuItem className="gap-2">
<BankAccountIcon />
Nova Conta
</DropdownMenuItem>
</DropdownMenu.Content>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons";
import { Modal } from "../../../../../components/Modal";
import { Button } from "../../../../../components/Button";
import { useFiltersModal } from "./useFiltersModal";
import { cn } from "../../../../../../app/utils/cn";

interface FiltersModalProps {
open: boolean;
onClose(): void;
}

const mockedAccounts = [
{
id: '123',
name: 'Nubank',
},
{
id: '456',
name: 'XP Investimentos',
},
{
id: '789',
name: 'Dinheiro',
},
];

export function FiltersModal({ open, onClose }: FiltersModalProps) {
const {
selectedBankAccountId,
handleSelectBankAccount,
selectedYear,
handleChangeYear,
} = useFiltersModal();

return (
<Modal open={open} onClose={onClose} title="Filtros">
<div>
<span className="text-lg tracking-[-1px] font-bold text-gray-800">
Conta
</span>
<div className="space-y-2 mt-2">
{mockedAccounts.map(account => (
<button
key={account.id}
onClick={() => handleSelectBankAccount(account.id)}
className={cn(
"p-2 rounded-2xl w-full text-left hover:bg-gray-50 transition-colors",
account.id === selectedBankAccountId && "!bg-gray-200",
)}
>
{account.name}
</button>
))}
</div>
</div>

<div className="mt-10 text-gray-800">
<span className="text-lg tracking-[-1px] font-bold">
Ano
</span>
<div className="mt-2 w-52 flex items-center justify-between">
<button
className="w-12 h-12 flex items-center justify-center"
onClick={() => handleChangeYear(-1)}
>
<ChevronLeftIcon className="w-6 h-6" />

</button>
<div className="flex-1 text-center">
<span className="text-sm font-medium tracking-[-0.5px]">
{selectedYear}
</span>
</div>

<button
className="w-12 h-12 flex items-center justify-center"
onClick={() => handleChangeYear(1)}
>
<ChevronRightIcon className="w-6 h-6" />
</button>
</div>
</div>

<Button className="w-full mt-10">
Aplicar Filtros
</Button>
</Modal>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useState } from "react";

export function useFiltersModal() {
const [selectedBankAccountId, setSelectedBankAccountId] = useState<null | string>(null);
const [selectedYear, setSelectedYear] = useState(new Date().getFullYear());

function handleSelectBankAccount(bankAccountId: string) {
setSelectedBankAccountId(prevState => (
prevState === bankAccountId
? null
: bankAccountId
));
}

function handleChangeYear(step: number) {
setSelectedYear(prevState => prevState + step);
}

return {
handleSelectBankAccount,
selectedBankAccountId,
selectedYear,
handleChangeYear,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,18 @@ import { cn } from "../../../../../app/utils/cn";
import Spinner from "../../../../components/Spinner";
import emptyStateImage from "../../../../../assets/empty-state.svg";
import { TransactionTypeDropdown } from "./TransactionTypeDropdown";
import { FiltersModal } from "./FiltersModal";

export function Transactions() {
const { areValuesVisible, isInitialLoading, isLoading, transactions } = useTransactionsController();
const {
areValuesVisible,
isInitialLoading,
isLoading,
transactions,
isFiltersModalOpen,
handleOpenFiltersModal,
handleCloseFiltersModal,
} = useTransactionsController();

const hasTransactions = transactions.length > 0;

Expand All @@ -26,10 +35,15 @@ export function Transactions() {

{!isInitialLoading && (
<>
<FiltersModal
open={isFiltersModalOpen}
onClose={handleCloseFiltersModal}
/>
<header>
<div className="flex items-center justify-between">
<TransactionTypeDropdown />
<button>

<button onClick={handleOpenFiltersModal}>
<FilterIcon />
</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import { useState } from "react";
import { useDashboard } from "../DashboardContext/useDashboard";

export function useTransactionsController() {
const { areValuesVisible } = useDashboard();

const [isFiltersModalOpen, setIsFiltersModalOpen] = useState(true);

function handleOpenFiltersModal() {
setIsFiltersModalOpen(true);
}

function handleCloseFiltersModal() {
setIsFiltersModalOpen(false);
}

return {
areValuesVisible,
transactions: [],
isInitialLoading: false,
isLoading: false,
handleOpenFiltersModal,
handleCloseFiltersModal,
isFiltersModalOpen,
};
}
1 change: 1 addition & 0 deletions frontend/src/view/pages/Dashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export function Dashboard() {
</main>

<Fab />

</div>
</DashboardProvider>
);
Expand Down
15 changes: 15 additions & 0 deletions frontend/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,24 @@ export default {
from: { opacity: '0', transform: 'translateY(2px)' },
to: { opacity: '1', transform: 'translateY(0)' },
},
slideDownAndFade: {
from: { opacity: '0', transform: 'translateY(-2px)' },
to: { opacity: '1', transform: 'translateY(0)' },
},
overlayShow: {
from: { opacity: '0' },
to: { opacity: '1' },
},
contentShow: {
from: { opacity: '0', transform: 'translate(-50%, -48%) scale(0.96)' },
to: { opacity: '1', transform: 'translate(-50%, -50%) scale(1)' },
},
},
animation: {
'slide-up-and-fade': 'slideUpAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)',
'slide-down-and-fade': 'slideDownAndFade 400ms cubic-bezier(0.16, 1, 0.3, 1)',
'overlay-show': 'overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1)',
'content-show': 'contentShow 150ms cubic-bezier(0.16, 1, 0.3, 1)',
},
},
},
Expand Down
21 changes: 21 additions & 0 deletions frontend/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,27 @@
dependencies:
"@babel/runtime" "^7.13.10"

"@radix-ui/react-dialog@^1.0.5":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz#71657b1b116de6c7a0b03242d7d43e01062c7300"
integrity sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "1.0.1"
"@radix-ui/react-compose-refs" "1.0.1"
"@radix-ui/react-context" "1.0.1"
"@radix-ui/react-dismissable-layer" "1.0.5"
"@radix-ui/react-focus-guards" "1.0.1"
"@radix-ui/react-focus-scope" "1.0.4"
"@radix-ui/react-id" "1.0.1"
"@radix-ui/react-portal" "1.0.4"
"@radix-ui/react-presence" "1.0.1"
"@radix-ui/react-primitive" "1.0.3"
"@radix-ui/react-slot" "1.0.2"
"@radix-ui/react-use-controllable-state" "1.0.1"
aria-hidden "^1.1.1"
react-remove-scroll "2.5.5"

"@radix-ui/react-direction@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.1.tgz#9cb61bf2ccf568f3421422d182637b7f47596c9b"
Expand Down

0 comments on commit 8f8c835

Please sign in to comment.