admin/components/forms/ConsumerFormLayout.tsx
alisaza e1eaf5eff5 feat: initial ghabilee-admin backoffice app
Extract admin dashboard from ghabilee-frontend2 into a dedicated Next.js
app for backoffice.ghabilee.ir (no SEO indexing / Clarity).
2026-09-05 13:12:59 +03:30

88 lines
3.3 KiB
TypeScript

import type { PropsWithChildren, ReactNode } from 'react'
import clsx from 'clsx'
type ConsumerFormSectionProps = PropsWithChildren<{
className?: string
description?: ReactNode
title: ReactNode
variant?: 'surface' | 'embedded'
}>
export const ConsumerFormSection = ({ children, className, description, title, variant = 'surface' }: ConsumerFormSectionProps) => (
<section className={clsx(variant === 'surface' ? 'consumer-surface p-4 sm:p-6' : 'p-4 sm:p-6', className)}>
<div className="mb-5 border-b border-consumer-border pb-4">
<h2 className="text-base font-extrabold text-consumer-text sm:text-lg">{title}</h2>
{description ? <p className="mt-1 text-xs leading-6 text-secondary-20">{description}</p> : null}
</div>
{children}
</section>
)
type ConsumerFormShellProps = PropsWithChildren<{
className?: string
description?: ReactNode
progress?: ReactNode
/** Keeps chrome static while the form body owns the vertical scrollport. */
scrollable?: boolean
status?: ReactNode
title?: ReactNode
utilityAction?: ReactNode
}>
/**
* Shared visual shell for long, task-oriented consumer forms.
* Domain forms keep ownership of their fields and validation; the shell only
* standardises hierarchy, canvas/surface contrast, status and progress.
*/
export const ConsumerFormShell = ({
children,
className,
description,
progress,
scrollable = false,
status,
title,
utilityAction,
}: ConsumerFormShellProps) => (
<div className={clsx('mx-auto w-full max-w-[54rem]', scrollable && 'flex h-full min-h-0 flex-col', className)}>
{title || status || description || utilityAction ? (
<div className="mb-4 flex flex-col gap-3 px-1 sm:flex-row sm:items-start sm:justify-between">
<div className="min-w-0">
<div className="flex flex-wrap items-center gap-2">
{title ? <h1 className="text-lg font-extrabold text-consumer-text sm:text-xl">{title}</h1> : null}
{status ? (
<span className="rounded-full border border-primary-100 bg-primary-50 px-3 py-1 text-smt-semibold text-primary-700">
{status}
</span>
) : null}
</div>
{description ? <p className="mt-1 max-w-2xl text-xs leading-6 text-secondary-20">{description}</p> : null}
</div>
{utilityAction ? <div className="shrink-0">{utilityAction}</div> : null}
</div>
) : null}
<div className={clsx('consumer-surface', scrollable && 'flex min-h-0 flex-1 flex-col overflow-hidden')}>
{progress ? (
<div className="overflow-hidden rounded-t-[inherit] border-b border-consumer-border bg-consumer-surface-muted/70 p-4">
{progress}
</div>
) : null}
{scrollable ? <div className="min-h-0 flex-1 overflow-y-auto">{children}</div> : children}
</div>
</div>
)
type ConsumerFormActionsProps = PropsWithChildren<{
className?: string
helper?: ReactNode
}>
export const ConsumerFormActions = ({ children, className, helper }: ConsumerFormActionsProps) => (
<div className={clsx('consumer-sticky-action flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between', className)}>
{helper ? <div className="min-w-0">{helper}</div> : <span />}
<div className="flex shrink-0 flex-col gap-2 sm:flex-row">{children}</div>
</div>
)