Skip to content

Commit

Permalink
feat(transaction): add transaction actions
Browse files Browse the repository at this point in the history
  • Loading branch information
iammursal committed Mar 3, 2024
1 parent 5c51ff3 commit a10457f
Show file tree
Hide file tree
Showing 17 changed files with 253 additions and 136 deletions.
6 changes: 3 additions & 3 deletions app/(common)/components/HeroSection/HeroSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function HeroSection() {


return (
<section className="p-8 ">
<section className="py-8">
{/* start:: Filters */}
<TransactionFilterFromModal />
{/* end:: Filters */}
Expand Down Expand Up @@ -53,7 +53,7 @@ export function HeroSection() {
</div>
<div className="grid grid-cols-2 font-bold gap-4 px-4 pt-16 text-center text-white">
<Link
href="/borrow/create"
href="/transactions/create?type=credit"
className="py-2 bg-destructive rounded-lg hover:bg-danger/95"
>
<span className="flex mx-auto gap-2 items-center justify-center">
Expand All @@ -62,7 +62,7 @@ export function HeroSection() {
</span>
</Link>
<Link
href="/lend/create"
href="/transactions/create?type=debit"
className="py-2 bg-success rounded-lg hover:bg-success/95"
>
<span className="flex gap-2 items-center justify-center">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client'

import { TransactionList } from '@/modules/transactions/components/list'
import { merge } from 'lodash-es'
import Link from 'next/link'
import { useContext } from 'react'
import { TransactionFilterContext } from '../../context/TransactionFilterProvider'
Expand All @@ -18,7 +19,11 @@ export function RecentTransactionsSection({ }) {
</div>
{/* list */}
<div className="flex flex-col gap-y-4 divide-y divide-gray-500 ">
<TransactionList filters={filters} />
<TransactionList filters={merge({}, filters, {
whereNotNull: [
'deleted_at'
]
})} />
</div>
</section>
)
Expand Down
17 changes: 0 additions & 17 deletions app/borrow/create/page.tsx

This file was deleted.

3 changes: 3 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
--warning: 40 90% 50%;
--warning-foreground: 0 0% 22%;

--info: 210 90% 50%;
--info-foreground: 0 0% 98%;

--success: 150 70% 50%; /* Adjusted for more vibrancy */
--success-foreground: 0 0% 98%;

Expand Down
15 changes: 0 additions & 15 deletions app/lend/create/page.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use client'

import { TransactionMutateForm } from '@/modules/transactions/components'
import { useSearchParams } from 'next/navigation'
import { FC } from 'react'

export const TransactionEditForm: FC<{}> = () => {
const searchParams = useSearchParams()
let type = searchParams.get('type')
const transaction = {
type
} as {
type: 'credit' | 'debit'
}
return <TransactionMutateForm transaction={transaction} />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './TransactionEditForm';

15 changes: 15 additions & 0 deletions app/transactions/create/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Metadata } from 'next'
import { TransactionEditForm } from './(common)/components/UserEditForm/TransactionEditForm'

export const metadata: Metadata = {
title: 'Lending',
// description: '...',
}

export default function BorrowCreatePage() {
return (
<div className="container">
<TransactionEditForm />
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use client'

import useQueryFilter from '@/hooks/useQueryFilter/useQueryFilter'
import { TransactionMutateForm } from '@/modules/transactions/components'
import { useSearchParams } from 'next/navigation'
import { FC } from 'react'

export const TransactionEditForm: FC<{}> = () => {
const searchParams = useSearchParams()
let id = searchParams.get('id') as any
id = typeof id === 'string' ? parseInt(id) : null
const { isLoading, data: transactions } = useQueryFilter(
'transactions',
{
where: { id },
}
)

if (isLoading) {
return <div>Loading...</div>
}
const transaction = transactions ? transactions?.[0] : undefined

return <TransactionMutateForm transaction={transaction} />
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './TransactionEditForm';

15 changes: 15 additions & 0 deletions app/transactions/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Metadata } from 'next'
import { TransactionEditForm } from './(common)/components/UserEditForm/TransactionEditForm'

export const metadata: Metadata = {
title: 'Lending',
// description: '...',
}

export default function BorrowCreatePage() {
return (
<div className="container">
<TransactionEditForm />
</div>
)
}
10 changes: 7 additions & 3 deletions components/form/Field/SearchSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,21 @@ export const SearchSelect: FC<FieldProps> = (props) => {
!field.value && 'text-muted-foreground'
)}
>
{typeof field.value !== 'undefined' && field.value != null ? (
{typeof field.value === 'string' ? (
<div className="flex justify-between w-full items-center">
<span>
{
options?.find(
(option) => option.value === field.value
(option) => {
console.log(option.value, field.value)
return option.value == field.value
}
)?.label
}
</span>
{isClearable && (
{isClearable && !!field.value && (
<Button
type='button'
variant={'ghost'}
size={'sm'}
onClick={() => form.setValue(name, null)}
Expand Down
96 changes: 49 additions & 47 deletions components/ui/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,58 +5,60 @@ import * as React from 'react'
import { cn } from '@/lib/utils'

const buttonVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default:
'bg-primary text-primary-foreground hover:bg-primary/90',
destructive:
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
success:
'bg-success text-success-foreground hover:bg-success/90',
outline:
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
isLoading: {
true: 'cursor-not-allowed opacity-50 after:animate-spin',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default:
'bg-primary text-primary-foreground hover:bg-primary/90',
destructive:
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
success:
'bg-success text-success-foreground hover:bg-success/90',
outline:
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
info: 'bg-info text-info-foreground hover:bg-info/80',
warning: 'bg-warning text-warning-foreground hover:bg-warning/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'h-10 w-10',
},
isLoading: {
true: 'cursor-not-allowed opacity-50 after:animate-spin',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
)

export type ButtonProps =
React.ButtonHTMLAttributes<HTMLButtonElement> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
isLoading?: boolean
}
React.ButtonHTMLAttributes<HTMLButtonElement> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean
isLoading?: boolean
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button'
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button'
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = 'Button'

Expand Down
49 changes: 35 additions & 14 deletions modules/transactions/components/form/TransactionMutateForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
import Field from '@/components/form/Field'
import { Button } from '@/components/ui/button'
import { Form } from '@/components/ui/form'
import useQueryFilter from '@/hooks/useQueryFilter/useQueryFilter'
import { useTransactionCreate } from '@/modules/transactions/hooks'
import { useUserListQuery } from '@/modules/users/hooks'
import { Transaction } from '@/modules/transactions/types'
import { User } from '@/modules/users/types'
import { zodResolver } from '@hookform/resolvers/zod'
import { useRouter } from 'next/navigation'
import { useMemo } from 'react'
import { useForm } from 'react-hook-form'
import { toast } from 'sonner'
import { z } from 'zod'
Expand All @@ -19,13 +22,19 @@ const FormSchema = z.object({
type: z.enum(['credit', 'debit']),
})

const typeOptions = [
{ label: 'Credit', value: 'credit' },
{ label: 'Debit', value: 'debit' },
]

export function TransactionMutateForm({
type,
transaction,
}: {
type: 'credit' | 'debit'
transaction?: Partial<Transaction>
}) {
const { type } = transaction || { type: 'credit' }
const router = useRouter()
const { data: users } = useUserListQuery()
const userQuery = useQueryFilter('users', {})
const createTransaction = useTransactionCreate({
onSuccess: () => {
toast('Success', {
Expand All @@ -39,22 +48,25 @@ export function TransactionMutateForm({
})
},
})

let usersOptions = users?.map((user) => {
let defaultValues = {
amount: undefined,
user_id: undefined,
transacted_at: new Date().toISOString().substr(0, 16),
notes: undefined,
type: 'credit',
...transaction
}
let usersOptions = useMemo(() => userQuery?.data?.map((user: User) => {
return { label: user.name, value: `${user.id}` }
})
}), [userQuery.isLoading])

const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: {
amount: undefined,
user_id: undefined,
transacted_at: new Date().toISOString().substr(0, 16),
notes: undefined,
type,
},
// @ts-ignore
defaultValues,
})


async function onSubmit(data: z.infer<typeof FormSchema>) {
createTransaction.mutate({
...data,
Expand All @@ -80,6 +92,15 @@ export function TransactionMutateForm({
min={1}
required
/>
<Field
name="type"
type="select"
label="Type"
placeholder="Select the transaction type"
options={typeOptions}
form={form}
required
/>
<Field
name="user_id"
type="select"
Expand Down
Loading

0 comments on commit a10457f

Please sign in to comment.