-
+
-
{children}
+
+ {children}
+
diff --git a/src/app/(routes)/transactions/create/operation-empty-accordion.tsx b/src/app/(routes)/transactions/create/operation-empty-accordion.tsx
new file mode 100644
index 00000000..0d66dd1a
--- /dev/null
+++ b/src/app/(routes)/transactions/create/operation-empty-accordion.tsx
@@ -0,0 +1,18 @@
+export type OperationEmptyAccordionProps = {
+ title: string
+ description?: string
+}
+
+export const OperationEmptyAccordion = ({
+ title,
+ description
+}: OperationEmptyAccordionProps) => {
+ return (
+
+
+
{title}
+
{description}
+
+
+ )
+}
diff --git a/src/app/(routes)/transactions/create/stepper.tsx b/src/app/(routes)/transactions/create/stepper.tsx
new file mode 100644
index 00000000..f0708cba
--- /dev/null
+++ b/src/app/(routes)/transactions/create/stepper.tsx
@@ -0,0 +1,52 @@
+import { useIntl } from 'react-intl'
+import {
+ Stepper as PrimitiveStepper,
+ StepperItem,
+ StepperItemNumber,
+ StepperItemText
+} from '../primitives/stepper'
+
+export type StepperProps = {
+ step?: number
+}
+
+export const Stepper = ({ step = 0 }: StepperProps) => {
+ const intl = useIntl()
+
+ return (
+
+
+ 1
+
+
+
+ 2
+
+
+
+ 3
+
+
+
+ )
+}
diff --git a/src/app/(routes)/transactions/primitives/paper-collapsible.tsx b/src/app/(routes)/transactions/primitives/paper-collapsible.tsx
new file mode 100644
index 00000000..2267c74a
--- /dev/null
+++ b/src/app/(routes)/transactions/primitives/paper-collapsible.tsx
@@ -0,0 +1,55 @@
+import { ElementRef, forwardRef, HTMLAttributes } from 'react'
+import { cn } from '@/lib/utils'
+import {
+ CollapsibleProps,
+ CollapsibleTriggerProps
+} from '@radix-ui/react-collapsible'
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger
+} from '@/components/ui/collapsible'
+import { Paper } from '@/components/ui/paper'
+import { ChevronDown } from 'lucide-react'
+
+export type PaperCollapsibleProps = CollapsibleProps
+
+export const PaperCollapsible = forwardRef<
+ ElementRef
,
+ PaperCollapsibleProps
+>(({ className, children, ...props }, ref) => (
+
+ {children}
+
+))
+PaperCollapsible.displayName = 'PaperCollapsible'
+
+export const PaperCollapsibleBanner = forwardRef<
+ HTMLDivElement,
+ HTMLAttributes
+>(({ className, children, ...props }, ref) => (
+
+))
+PaperCollapsibleBanner.displayName = 'PaperCollapsibleBanner'
+
+export const PaperCollapsibleTrigger = forwardRef<
+ ElementRef,
+ CollapsibleTriggerProps
+>(({ className, children, ...props }, ref) => (
+ svg]:rotate-180',
+ className
+ )}
+ {...props}
+ >
+
+
+))
+PaperCollapsibleTrigger.displayName = 'PaperCollapsibleTrigger'
+
+export const PaperCollapsibleContent = CollapsibleContent
diff --git a/src/app/(routes)/transactions/primitives/stepper.tsx b/src/app/(routes)/transactions/primitives/stepper.tsx
new file mode 100644
index 00000000..e8c60f38
--- /dev/null
+++ b/src/app/(routes)/transactions/primitives/stepper.tsx
@@ -0,0 +1,76 @@
+import { cn } from '@/lib/utils'
+import { forwardRef, HTMLAttributes, PropsWithChildren } from 'react'
+
+export const Stepper = forwardRef<
+ HTMLDivElement,
+ HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+Stepper.displayName = 'Stepper'
+
+export type StepperItemProps = HTMLAttributes & {
+ active?: boolean
+}
+
+export const StepperItem = forwardRef(
+ ({ className, active, ...props }, ref) => (
+
+ )
+)
+StepperItem.displayName = 'StepperItem'
+
+export const StepperItemNumber = forwardRef<
+ HTMLDivElement,
+ HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+StepperItemNumber.displayName = 'StepperItemNumber'
+
+export type StepperItemTextProps = HTMLAttributes & {
+ title: string
+ description?: string
+}
+
+export const StepperItemText = forwardRef<
+ HTMLParagraphElement,
+ StepperItemTextProps
+>(({ className, title, description, ...props }, ref) => (
+
+
+ {description && (
+
{description}
+ )}
+
+))
+StepperItemText.displayName = 'StepperItemText'
+
+export type StepperControlProps = PropsWithChildren & {
+ active?: boolean
+}
+
+export const StepperContent = ({ active, children }: StepperControlProps) => {
+ return active ? <>{children}> : null
+}
diff --git a/src/app/app.tsx b/src/app/app.tsx
index 8a70c6c1..0beee3ba 100644
--- a/src/app/app.tsx
+++ b/src/app/app.tsx
@@ -9,21 +9,19 @@ import ZodSchemaProvider from '@/lib/zod/zod-schema-provider'
export default async function App({ children }: { children: React.ReactNode }) {
return (
-
-
-
-
-
- {children}
-
-
-
-
-
-
-
+
+
+
+
+ {children}
+
+
+
+
+
+
)
}
diff --git a/src/app/globals.css b/src/app/globals.css
index 7113fe48..765ba9a8 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -74,7 +74,11 @@
@apply border-border;
}
+ html {
+ @apply h-full overflow-y-auto;
+ }
+
body {
- @apply bg-background text-foreground;
+ @apply h-full overflow-y-auto bg-background text-foreground;
}
}
diff --git a/src/components/form/input-field/index.tsx b/src/components/form/input-field/index.tsx
index 6d810e9c..9e5d021f 100644
--- a/src/components/form/input-field/index.tsx
+++ b/src/components/form/input-field/index.tsx
@@ -1,5 +1,6 @@
import {
FormControl,
+ FormDescription,
FormField,
FormItem,
FormLabel,
@@ -7,6 +8,7 @@ import {
FormTooltip
} from '@/components/ui/form'
import { Input } from '@/components/ui/input'
+import { Textarea } from '@/components/ui/textarea'
import { HTMLInputTypeAttribute, ReactNode } from 'react'
import { Control } from 'react-hook-form'
@@ -17,9 +19,12 @@ export type InputFieldProps = {
tooltip?: string
labelExtra?: ReactNode
placeholder?: string
+ description?: ReactNode
control: Control
disabled?: boolean
readOnly?: boolean
+ rows?: number
+ textArea?: boolean
required?: boolean
}
@@ -29,8 +34,11 @@ export const InputField = ({
tooltip,
labelExtra,
placeholder,
+ description,
required,
readOnly,
+ rows,
+ textArea,
...others
}: InputFieldProps) => {
return (
@@ -48,14 +56,24 @@ export const InputField = ({
)}
-
+ {textArea ? (
+
+ ) : (
+
+ )}
+ {description && {description}}
)}
/>
diff --git a/src/components/page-footer/index.tsx b/src/components/page-footer/index.tsx
new file mode 100644
index 00000000..13c2d523
--- /dev/null
+++ b/src/components/page-footer/index.tsx
@@ -0,0 +1,50 @@
+import { forwardRef, HTMLAttributes } from 'react'
+import { cn } from '@/lib/utils'
+
+export type PageFooterProps = HTMLAttributes & {
+ open?: boolean
+ thumb?: boolean
+}
+
+export const PageFooter = forwardRef(
+ ({ className, open = true, thumb = true, children, ...props }, ref) => (
+
+ {thumb &&
}
+
+ {children}
+
+
+ )
+)
+PageFooter.displayName = 'PageFooter'
+
+export const PageFooterThumb = forwardRef<
+ HTMLDivElement,
+ HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+PageFooterThumb.displayName = 'PageFooterThumb'
+
+export const PageFooterSection = forwardRef<
+ HTMLDivElement,
+ HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+PageFooterSection.displayName = 'PageFooterSection'
diff --git a/src/components/ui/alert/index.tsx b/src/components/ui/alert/index.tsx
index 6fd7a005..ba15db5e 100644
--- a/src/components/ui/alert/index.tsx
+++ b/src/components/ui/alert/index.tsx
@@ -9,6 +9,8 @@ const alertVariants = cva(
variants: {
variant: {
default: 'bg-white text-slate-950 dark:bg-slate-950 dark:text-slate-50',
+ informative:
+ 'bg-[#EFF6FF] border-opacity-50 border-[#2563eb] text-[#1E40AF] dark:bg-blue-900 dark:text-blue-50 [&>svg]:text-blue-600',
destructive:
'border-red-500/50 text-red-500 dark:border-red-500 [&>svg]:text-red-500 dark:border-red-900/50 dark:text-red-900 dark:dark:border-red-900 dark:[&>svg]:text-red-900'
}
diff --git a/src/components/ui/textarea/index.tsx b/src/components/ui/textarea/index.tsx
new file mode 100644
index 00000000..2a2113dc
--- /dev/null
+++ b/src/components/ui/textarea/index.tsx
@@ -0,0 +1,22 @@
+import * as React from 'react'
+
+import { cn } from '@/lib/utils'
+
+const Textarea = React.forwardRef<
+ HTMLTextAreaElement,
+ React.ComponentProps<'textarea'>
+>(({ className, ...props }, ref) => {
+ return (
+
+ )
+})
+Textarea.displayName = 'Textarea'
+
+export { Textarea }
diff --git a/src/components/ui/textarea/textarea.mdx b/src/components/ui/textarea/textarea.mdx
new file mode 100644
index 00000000..2940bca2
--- /dev/null
+++ b/src/components/ui/textarea/textarea.mdx
@@ -0,0 +1,14 @@
+import { Meta, Controls, Primary, Canvas } from '@storybook/blocks'
+import * as Story from './textarea.stories'
+
+
+
+# Textarea
+
+Displays a form textarea or a component that looks like a textarea.
+
+### Default
+
+
+
+
diff --git a/src/components/ui/textarea/textarea.stories.tsx b/src/components/ui/textarea/textarea.stories.tsx
new file mode 100644
index 00000000..66e40cd0
--- /dev/null
+++ b/src/components/ui/textarea/textarea.stories.tsx
@@ -0,0 +1,35 @@
+import { ComponentProps } from 'react'
+import { Meta, StoryObj } from '@storybook/react'
+import { Textarea } from '.'
+import { FormProvider, useForm } from 'react-hook-form'
+
+const meta: Meta> = {
+ title: 'Primitives/Textarea',
+ component: Textarea,
+ argTypes: {
+ disabled: {
+ type: 'boolean',
+ description: 'If the input is disabled'
+ },
+ className: {
+ type: 'string',
+ description: "The input's class"
+ }
+ }
+}
+
+export default meta
+
+export const Default: StoryObj> = {
+ args: {
+ placeholder: 'Textarea...'
+ },
+ render: (args) => {
+ const form = useForm()
+ return (
+
+
+
+ )
+ }
+}
diff --git a/src/lib/zod/messages.ts b/src/lib/zod/messages.ts
index bb55e1a0..3a352100 100644
--- a/src/lib/zod/messages.ts
+++ b/src/lib/zod/messages.ts
@@ -26,6 +26,10 @@ const messages = defineMessages({
defaultMessage:
'Field must contain over {minimum} {minimum, plural, =0 {characters} one {character} other {characters}}'
},
+ too_small_number_not_inclusive: {
+ id: 'errors.too_small.number.not_inclusive',
+ defaultMessage: 'Field must be greater than {minimum}'
+ },
too_small_date_exact: {
id: 'errors.too_small.date.exact',
defaultMessage: 'Date must be exactly {minimum}'
@@ -55,6 +59,10 @@ const messages = defineMessages({
defaultMessage:
'Field must contain under {maximum} {maximum, plural, =0 {characters} one {character} other {characters}}'
},
+ too_big_number_inclusive: {
+ id: 'errors.too_big.number.inclusive',
+ defaultMessage: 'Field must be less than or equal to {maximum}'
+ },
too_big_date_exact: {
id: 'errors.too_big.date.exact',
defaultMessage: 'Date must be exactly {maximum}'
diff --git a/src/schema/transactions.ts b/src/schema/transactions.ts
new file mode 100644
index 00000000..760fa896
--- /dev/null
+++ b/src/schema/transactions.ts
@@ -0,0 +1,35 @@
+import { z } from 'zod'
+import { metadata } from './metadata'
+import { assets } from './assets'
+
+const description = z.string().max(1024)
+
+const chartOfAccounts = z.string().max(255)
+
+const value = z.coerce
+ .number()
+ .positive()
+ .max(1000 * 1000 * 1000 * 1000)
+
+const scale = z.number().positive().max(1000)
+
+const account = z.string().min(1).max(255)
+
+const remaining = z.string().min(1).max(255)
+
+const asset = assets.code
+
+const source = {
+ account,
+ remaining
+}
+
+export const transaction = {
+ value,
+ asset,
+ scale,
+ description,
+ chartOfAccounts,
+ source,
+ metadata
+}