Shadcn conversion, lots of styling
This commit is contained in:
@@ -1,60 +0,0 @@
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { X } from "lucide-react"
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
AlertDialogPortal,
|
||||
AlertDialogOverlay,
|
||||
} from "@/components/ui/alert-dialog"
|
||||
import { useRsi } from "../hooks/useRsi"
|
||||
|
||||
type ModalCloseButtonProps = {
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export const ModalCloseButton = ({ onClose }: ModalCloseButtonProps) => {
|
||||
const { translations } = useRsi()
|
||||
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="absolute right-4 top-4 z-[60] h-10 w-10 rounded-full bg-background/80 p-0 text-muted-foreground backdrop-blur-sm hover:bg-background/50"
|
||||
>
|
||||
<X className="h-5 w-5" />
|
||||
<span className="sr-only">Close modal</span>
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay className="z-[1400]" />
|
||||
<AlertDialogContent className="z-[1500]">
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
{translations.alerts.confirmClose.headerTitle}
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{translations.alerts.confirmClose.bodyText}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>
|
||||
{translations.alerts.confirmClose.cancelButtonTitle}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={onClose}>
|
||||
{translations.alerts.confirmClose.exitButtonTitle}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogPortal>
|
||||
</AlertDialog>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,26 @@
|
||||
import type React from "react"
|
||||
import { Modal, ModalContent, ModalOverlay } from "@chakra-ui/react"
|
||||
import { ModalCloseButton } from "./ModalCloseButton"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
DialogClose,
|
||||
} from "@/components/ui/dialog"
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
AlertDialogPortal,
|
||||
AlertDialogOverlay,
|
||||
} from "@/components/ui/alert-dialog"
|
||||
import { useRsi } from "../hooks/useRsi"
|
||||
import { useState } from "react"
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode
|
||||
@@ -10,24 +29,60 @@ type Props = {
|
||||
}
|
||||
|
||||
export const ModalWrapper = ({ children, isOpen, onClose }: Props) => {
|
||||
const { rtl } = useRsi()
|
||||
const { rtl, translations } = useRsi()
|
||||
const [showCloseAlert, setShowCloseAlert] = useState(false)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
id="rsi"
|
||||
variant="rsi"
|
||||
closeOnEsc={false}
|
||||
closeOnOverlayClick={false}
|
||||
scrollBehavior="inside"
|
||||
<>
|
||||
<Dialog open={isOpen} onOpenChange={() => setShowCloseAlert(true)} modal>
|
||||
<DialogPortal>
|
||||
<DialogOverlay className="bg-background/80 backdrop-blur-sm" />
|
||||
<DialogContent
|
||||
onEscapeKeyDown={(e) => {
|
||||
e.preventDefault()
|
||||
setShowCloseAlert(true)
|
||||
}}
|
||||
onPointerDownOutside={(e) => e.preventDefault()}
|
||||
className="fixed left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] w-[calc(100%-2rem)] h-[calc(100%-2rem)] max-w-[100vw] max-h-[100vh] flex flex-col overflow-hidden rounded-lg border bg-background p-0 shadow-lg sm:w-[calc(100%-3rem)] sm:h-[calc(100%-3rem)] md:w-[calc(100%-4rem)] md:h-[calc(100%-4rem)]"
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<div dir={rtl ? "rtl" : "ltr"} className="relative">
|
||||
<ModalCloseButton onClose={onClose} />
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<DialogClose className="absolute right-4 top-4" onClick={(e) => {
|
||||
e.preventDefault()
|
||||
setShowCloseAlert(true)
|
||||
}} />
|
||||
</AlertDialogTrigger>
|
||||
</AlertDialog>
|
||||
<div dir={rtl ? "rtl" : "ltr"} className="flex-1 overflow-auto">
|
||||
{children}
|
||||
</div>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</DialogContent>
|
||||
</DialogPortal>
|
||||
</Dialog>
|
||||
|
||||
<AlertDialog open={showCloseAlert} onOpenChange={setShowCloseAlert}>
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay className="z-[1400]" />
|
||||
<AlertDialogContent className="z-[1500]">
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
{translations.alerts.confirmClose.headerTitle}
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{translations.alerts.confirmClose.bodyText}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel onClick={() => setShowCloseAlert(false)}>
|
||||
{translations.alerts.confirmClose.cancelButtonTitle}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={onClose}>
|
||||
{translations.alerts.confirmClose.exitButtonTitle}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogPortal>
|
||||
</AlertDialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export const ColumnGrid = <T extends string>({
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="flex h-[calc(100vh-9.5rem)] flex-col">
|
||||
<div className="flex h-[calc(100vh-10rem)] flex-col">
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="px-8 py-6">
|
||||
<div className="mb-8">
|
||||
@@ -94,7 +94,7 @@ export const ColumnGrid = <T extends string>({
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t bg-muted px-8 py-4">
|
||||
<div className="border-t bg-muted px-8 py-4 -mb-1">
|
||||
<div className="flex items-center justify-between">
|
||||
{onBack && (
|
||||
<Button variant="outline" onClick={onBack}>
|
||||
|
||||
@@ -21,16 +21,14 @@ import {
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion"
|
||||
import { Check } from "lucide-react"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
type TemplateColumnProps<T extends string> = {
|
||||
column: Column<T>
|
||||
onChange: (value: T, columnIndex: number) => void
|
||||
onSubChange: (value: string, columnIndex: number, entry: string) => void
|
||||
}
|
||||
|
||||
const getAccordionTitle = <T extends string>(fields: Fields<T>, column: Column<T>, translations: any) => {
|
||||
const fieldLabel = fields.find((field) => "value" in column && field.key === column.value)!.label
|
||||
const fieldLabel = fields.find((field: Field<T>) => "value" in column && field.key === column.value)!.label
|
||||
return `${translations.matchColumnsStep.matchDropdownTitle} ${fieldLabel} (${
|
||||
"matchedOptions" in column && column.matchedOptions.filter((option) => !option.value).length
|
||||
} ${translations.matchColumnsStep.unmatched})`
|
||||
|
||||
@@ -4,7 +4,6 @@ import { X, RotateCcw } from "lucide-react"
|
||||
import type { Column } from "../MatchColumnsStep"
|
||||
import { ColumnType } from "../MatchColumnsStep"
|
||||
import type { RawData } from "../../../types"
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
type UserTableColumnProps<T extends string> = {
|
||||
column: Column<T>
|
||||
|
||||
@@ -25,23 +25,20 @@ export const SelectHeaderStep = ({ data, onContinue, onBack }: SelectHeaderProps
|
||||
}, [onContinue, data, selectedRows])
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="flex flex-col">
|
||||
<div className="px-8 py-6">
|
||||
<div className="mb-8">
|
||||
<h2 className="text-3xl font-semibold text-foreground">
|
||||
<h2 className="text-2xl font-semibold text-foreground">
|
||||
{translations.selectHeaderStep.title}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="flex-1 px-8 mb-12 overflow-auto">
|
||||
<SelectHeaderTable
|
||||
data={data}
|
||||
selectedRows={selectedRows}
|
||||
setSelectedRows={setSelectedRows}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="border-t bg-muted px-8 py-4 mt-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center justify-between border-t bg-muted px-8 py-4 mt-2">
|
||||
{onBack && (
|
||||
<Button variant="outline" onClick={onBack}>
|
||||
{translations.selectHeaderStep.backButtonTitle}
|
||||
@@ -56,6 +53,5 @@ export const SelectHeaderStep = ({ data, onContinue, onBack }: SelectHeaderProps
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export const SelectSheetStep = ({ sheetNames, onContinue, onBack }: SelectSheetP
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="flex h-[calc(100vh-9.5rem)] flex-col">
|
||||
<div className="flex h-[calc(100vh-10rem)] flex-col">
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="px-8 py-6">
|
||||
<div className="mb-8">
|
||||
@@ -53,7 +53,7 @@ export const SelectSheetStep = ({ sheetNames, onContinue, onBack }: SelectSheetP
|
||||
</RadioGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-t p-6 bg-muted">
|
||||
<div className="flex items-center justify-between border-t px-8 py-4 bg-muted -mb-1">
|
||||
{onBack && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
|
||||
@@ -3,23 +3,14 @@ import { useRsi } from "../hooks/useRsi"
|
||||
import { useRef, useState } from "react"
|
||||
import { steps, stepTypeToStepIndex, stepIndexToStepType } from "../utils/steps"
|
||||
import { CgCheck } from "react-icons/cg"
|
||||
import { Separator } from "@/components/ui/separator"
|
||||
// @ts-ignore
|
||||
import { useSteps, Step, Steps as Stepper } from "chakra-ui-steps"
|
||||
|
||||
const CheckIcon = ({ color }: { color: string }) => <CgCheck size="24" className={color} />
|
||||
|
||||
export const Steps = () => {
|
||||
const { initialStepState, translations, isNavigationEnabled } = useRsi()
|
||||
|
||||
const initialStep = stepTypeToStepIndex(initialStepState?.type)
|
||||
|
||||
const { nextStep, activeStep, setStep } = useSteps({
|
||||
initialStep,
|
||||
})
|
||||
|
||||
const [activeStep, setActiveStep] = useState(initialStep)
|
||||
const [state, setState] = useState<StepState>(initialStepState || { type: StepType.upload })
|
||||
|
||||
const history = useRef<StepState[]>([])
|
||||
|
||||
const onClickStep = (stepIndex: number) => {
|
||||
@@ -29,7 +20,7 @@ export const Steps = () => {
|
||||
const nextHistory = history.current.slice(0, historyIdx + 1)
|
||||
history.current = nextHistory
|
||||
setState(nextHistory[nextHistory.length - 1])
|
||||
setStep(stepIndex)
|
||||
setActiveStep(stepIndex)
|
||||
}
|
||||
|
||||
const onBack = () => {
|
||||
@@ -39,7 +30,7 @@ export const Steps = () => {
|
||||
const onNext = (v: StepState) => {
|
||||
history.current.push(state)
|
||||
setState(v)
|
||||
v.type !== StepType.selectSheet && nextStep()
|
||||
v.type !== StepType.selectSheet && setActiveStep(activeStep + 1)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -79,7 +70,6 @@ export const Steps = () => {
|
||||
{translations[key].title}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useCallback, useState } from "react"
|
||||
import { Progress, useToast } from "@chakra-ui/react"
|
||||
import type XLSX from "xlsx-ugnis"
|
||||
import type XLSX from "xlsx"
|
||||
import { UploadStep } from "./UploadStep/UploadStep"
|
||||
import { SelectHeaderStep } from "./SelectHeaderStep/SelectHeaderStep"
|
||||
import { SelectSheetStep } from "./SelectSheetStep/SelectSheetStep"
|
||||
@@ -11,6 +10,8 @@ import { MatchColumnsStep } from "./MatchColumnsStep/MatchColumnsStep"
|
||||
import { exceedsMaxRecords } from "../utils/exceedsMaxRecords"
|
||||
import { useRsi } from "../hooks/useRsi"
|
||||
import type { RawData } from "../types"
|
||||
import { Progress } from "@/components/ui/progress"
|
||||
import { useToast } from "@/hooks/use-toast"
|
||||
|
||||
export enum StepType {
|
||||
upload = "upload",
|
||||
@@ -59,16 +60,13 @@ export const UploadFlow = ({ state, onNext, onBack }: Props) => {
|
||||
tableHook,
|
||||
} = useRsi()
|
||||
const [uploadedFile, setUploadedFile] = useState<File | null>(null)
|
||||
const toast = useToast()
|
||||
const { toast } = useToast()
|
||||
const errorToast = useCallback(
|
||||
(description: string) => {
|
||||
toast({
|
||||
status: "error",
|
||||
variant: "left-accent",
|
||||
position: "bottom-left",
|
||||
title: `${translations.alerts.toast.error}`,
|
||||
variant: "destructive",
|
||||
title: translations.alerts.toast.error,
|
||||
description,
|
||||
isClosable: true,
|
||||
})
|
||||
},
|
||||
[toast, translations],
|
||||
@@ -165,6 +163,6 @@ export const UploadFlow = ({ state, onNext, onBack }: Props) => {
|
||||
case StepType.validateData:
|
||||
return <ValidationStep initialData={state.data} file={uploadedFile!} onBack={onBack} />
|
||||
default:
|
||||
return <Progress isIndeterminate />
|
||||
return <Progress value={33} className="w-full" />
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import type XLSX from "xlsx-ugnis"
|
||||
import { Box, Heading, ModalBody, Text, useStyleConfig } from "@chakra-ui/react"
|
||||
import { DropZone } from "./components/DropZone"
|
||||
import { useRsi } from "../../hooks/useRsi"
|
||||
import { ExampleTable } from "./components/ExampleTable"
|
||||
import { useCallback, useState } from "react"
|
||||
import { useRsi } from "../../hooks/useRsi"
|
||||
import { DropZone } from "./components/DropZone"
|
||||
import { ExampleTable } from "./components/ExampleTable"
|
||||
import { FadingOverlay } from "./components/FadingOverlay"
|
||||
import type { themeOverrides } from "../../theme"
|
||||
|
||||
type UploadProps = {
|
||||
onContinue: (data: XLSX.WorkBook, file: File) => Promise<void>
|
||||
@@ -13,8 +11,8 @@ type UploadProps = {
|
||||
|
||||
export const UploadStep = ({ onContinue }: UploadProps) => {
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const styles = useStyleConfig("UploadStep") as (typeof themeOverrides)["components"]["UploadStep"]["baseStyle"]
|
||||
const { translations, fields } = useRsi()
|
||||
|
||||
const handleOnContinue = useCallback(
|
||||
async (data: XLSX.WorkBook, file: File) => {
|
||||
setIsLoading(true)
|
||||
@@ -23,16 +21,19 @@ export const UploadStep = ({ onContinue }: UploadProps) => {
|
||||
},
|
||||
[onContinue],
|
||||
)
|
||||
|
||||
return (
|
||||
<ModalBody>
|
||||
<Heading sx={styles.heading}>{translations.uploadStep.title}</Heading>
|
||||
<Text sx={styles.title}>{translations.uploadStep.manifestTitle}</Text>
|
||||
<Text sx={styles.subtitle}>{translations.uploadStep.manifestDescription}</Text>
|
||||
<Box sx={styles.tableWrapper}>
|
||||
<div className="p-6">
|
||||
<h2 className="text-2xl font-semibold mb-4">{translations.uploadStep.title}</h2>
|
||||
<p className="text-lg mb-2">{translations.uploadStep.manifestTitle}</p>
|
||||
<p className="text-muted-foreground mb-6">{translations.uploadStep.manifestDescription}</p>
|
||||
<div className="relative mb-0 border-t rounded-lg h-[80px]">
|
||||
<div className="absolute inset-0">
|
||||
<ExampleTable fields={fields} />
|
||||
</div>
|
||||
<FadingOverlay />
|
||||
</Box>
|
||||
</div>
|
||||
<DropZone onContinue={handleOnContinue} isLoading={isLoading} />
|
||||
</ModalBody>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,5 +12,14 @@ export const ExampleTable = <T extends string>({ fields }: Props<T>) => {
|
||||
const data = useMemo(() => generateExampleRow(fields), [fields])
|
||||
const columns = useMemo(() => generateColumns(fields), [fields])
|
||||
|
||||
return <Table rows={data} columns={columns} className={"rdg-example"} />
|
||||
return (
|
||||
<div className="h-full w-full">
|
||||
<Table
|
||||
rows={data}
|
||||
columns={columns}
|
||||
className="rdg-example h-full"
|
||||
style={{ height: '100%' }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
import { Box } from "@chakra-ui/react"
|
||||
|
||||
export const FadingOverlay = () => (
|
||||
<Box
|
||||
position="absolute"
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
height="48px"
|
||||
pointerEvents="none"
|
||||
bgGradient="linear(to bottom, backgroundAlpha, background)"
|
||||
<div
|
||||
className="absolute inset-x-0 bottom-0 h-12 pointer-events-none bg-gradient-to-t from-background to-transparent"
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import type { Column } from "react-data-grid"
|
||||
import { Box, Tooltip } from "@chakra-ui/react"
|
||||
import type { Fields } from "../../../types"
|
||||
import { CgInfo } from "react-icons/cg"
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip"
|
||||
|
||||
export const generateColumns = <T extends string>(fields: Fields<T>) =>
|
||||
fields.map(
|
||||
@@ -10,23 +15,30 @@ export const generateColumns = <T extends string>(fields: Fields<T>) =>
|
||||
name: column.label,
|
||||
minWidth: 150,
|
||||
headerRenderer: () => (
|
||||
<Box display="flex" gap={1} alignItems="center" position="relative">
|
||||
<Box flex={1} overflow="hidden" textOverflow="ellipsis">
|
||||
<div className="flex items-center gap-1 relative">
|
||||
<div className="flex-1 overflow-hidden text-ellipsis">
|
||||
{column.label}
|
||||
</Box>
|
||||
</div>
|
||||
{column.description && (
|
||||
<Tooltip placement="top" hasArrow label={column.description}>
|
||||
<Box flex={"0 0 auto"}>
|
||||
<CgInfo size="16px" />
|
||||
</Box>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex-none">
|
||||
<CgInfo className="h-4 w-4" />
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
{column.description}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
</Box>
|
||||
</div>
|
||||
),
|
||||
formatter: ({ row }) => (
|
||||
<Box minWidth="100%" minHeight="100%" overflow="hidden" textOverflow="ellipsis">
|
||||
<div className="min-w-full min-h-full overflow-hidden text-ellipsis">
|
||||
{row[column.key]}
|
||||
</Box>
|
||||
</div>
|
||||
),
|
||||
}),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user