Converting to shadcn, steps, validate page

This commit is contained in:
2025-02-18 15:41:00 -05:00
parent 9ce3793067
commit f823841b15
7 changed files with 181 additions and 80 deletions

View File

@@ -31,6 +31,7 @@
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-alert-dialog": "^1.1.4",
"@radix-ui/react-avatar": "^1.1.2",
"@radix-ui/react-checkbox": "^1.1.4",
"@radix-ui/react-collapsible": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.4",
"@radix-ui/react-dropdown-menu": "^2.1.4",
@@ -3300,6 +3301,36 @@
}
}
},
"node_modules/@radix-ui/react-checkbox": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.4.tgz",
"integrity": "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.1",
"@radix-ui/react-compose-refs": "1.1.1",
"@radix-ui/react-context": "1.1.1",
"@radix-ui/react-presence": "1.1.2",
"@radix-ui/react-primitive": "2.0.2",
"@radix-ui/react-use-controllable-state": "1.1.0",
"@radix-ui/react-use-previous": "1.1.0",
"@radix-ui/react-use-size": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collapsible": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.3.tgz",

View File

@@ -33,6 +33,7 @@
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-alert-dialog": "^1.1.4",
"@radix-ui/react-avatar": "^1.1.2",
"@radix-ui/react-checkbox": "^1.1.4",
"@radix-ui/react-collapsible": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.4",
"@radix-ui/react-dropdown-menu": "^2.1.4",

View File

@@ -0,0 +1,28 @@
import * as React from "react"
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
import { Check } from "lucide-react"
import { cn } from "@/lib/utils"
const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className
)}
{...props}
>
<CheckboxPrimitive.Indicator
className={cn("flex items-center justify-center text-current")}
>
<Check className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
))
Checkbox.displayName = CheckboxPrimitive.Root.displayName
export { Checkbox }

View File

@@ -13,7 +13,6 @@ import {
AlertDialogPortal,
AlertDialogOverlay,
} from "@/components/ui/alert-dialog"
import { useState } from "react"
import { useRsi } from "../hooks/useRsi"
type ModalCloseButtonProps = {

View File

@@ -1,13 +1,13 @@
import { StepState, StepType, UploadFlow } from "./UploadFlow"
import { ModalHeader } from "@chakra-ui/react"
import { useSteps, Step, Steps as Stepper } from "chakra-ui-steps"
import { CgCheck } from "react-icons/cg"
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="36px" color={color} />
const CheckIcon = ({ color }: { color: string }) => <CgCheck size="24" className={color} />
export const Steps = () => {
const { initialStepState, translations, isNavigationEnabled } = useRsi()
@@ -44,18 +44,49 @@ export const Steps = () => {
return (
<>
<ModalHeader display={["none", "none", "block"]}>
<Stepper
activeStep={activeStep}
checkIcon={CheckIcon}
onClickStep={isNavigationEnabled ? onClickStep : undefined}
responsive={false}
>
{steps.map((key) => (
<Step label={translations[key].title} key={key} />
))}
</Stepper>
</ModalHeader>
<div className="hidden border-b bg-muted px-8 py-6 md:block lg:px-24 xl:px-36">
<nav className="mx-auto flex items-center justify-between" aria-label="Steps">
{steps.map((key, index) => {
const isActive = index === activeStep
const isCompleted = index < activeStep
return (
<div key={key} className="flex items-center">
<button
className={`group flex items-center ${isNavigationEnabled ? 'cursor-pointer' : 'cursor-default'}`}
onClick={isNavigationEnabled ? () => onClickStep(index) : undefined}
disabled={!isNavigationEnabled}
>
<div className={`flex shrink-0 h-10 w-10 items-center justify-center rounded-full border-2 ${
isActive
? 'border-primary bg-primary text-primary-foreground'
: isCompleted
? 'border-primary bg-primary text-primary-foreground'
: 'border-muted-foreground/20 bg-background'
}`}>
{isCompleted ? (
<CheckIcon color="text-primary-foreground" />
) : (
<span className={`text-sm font-medium ${
isActive ? 'text-primary-foreground' : 'text-muted-foreground'
}`}>
{index + 1}
</span>
)}
</div>
<span className={`ml-2 text-sm font-medium w-24 ${
isActive ? 'text-foreground' : 'text-muted-foreground'
}`}>
{translations[key].title}
</span>
</button>
{index < steps.length - 1 && (
<Separator className="ml-2 mr-0 bg-muted-foreground/80" />
)}
</div>
)
})}
</nav>
</div>
<UploadFlow state={state} onNext={onNext} onBack={isNavigationEnabled ? onBack : undefined} />
</>
)

View File

@@ -1,14 +1,13 @@
import { useCallback, useMemo, useState } from "react"
import { Box, Button, Heading, ModalBody, Switch, useStyleConfig, useToast } from "@chakra-ui/react"
import { ContinueButton } from "../../components/ContinueButton"
import { useRsi } from "../../hooks/useRsi"
import type { Meta } from "./types"
import { addErrorsAndRunHooks } from "./utils/dataMutations"
import { generateColumns } from "./components/columns"
import { Table } from "../../components/Table"
// @ts-ignore
import type { Column, RowsChangeData } from "react-data-grid"
// @ts-ignore
import DataGrid from "react-data-grid"
import type { Data } from "../../types"
import type { themeOverrides } from "../../theme"
import type { RowsChangeData } from "react-data-grid"
import {
AlertDialog,
AlertDialogAction,
@@ -21,6 +20,9 @@ import {
AlertDialogPortal,
AlertDialogOverlay,
} from "@/components/ui/alert-dialog"
import { Button } from "@/components/ui/button"
import { Switch } from "@/components/ui/switch"
import { useToast } from "@/hooks/use-toast"
type Props<T extends string> = {
initialData: (Data<T> & Meta)[]
@@ -30,13 +32,9 @@ type Props<T extends string> = {
export const ValidationStep = <T extends string>({ initialData, file, onBack }: Props<T>) => {
const { translations, fields, onClose, onSubmit, rowHook, tableHook, allowInvalidSubmit } = useRsi<T>()
const styles = useStyleConfig(
"ValidationStep",
) as (typeof themeOverrides)["components"]["ValidationStep"]["baseStyle"]
const toast = useToast()
const { toast } = useToast()
const [data, setData] = useState<(Data<T> & Meta)[]>(initialData)
const [selectedRows, setSelectedRows] = useState<ReadonlySet<number | string>>(new Set())
const [filterByErrors, setFilterByErrors] = useState(false)
const [showSubmitAlert, setShowSubmitAlert] = useState(false)
@@ -63,7 +61,7 @@ export const ValidationStep = <T extends string>({ initialData, file, onBack }:
const updateRows = useCallback(
(rows: typeof data, changedData?: RowsChangeData<(typeof data)[number]>) => {
const changes = changedData?.indexes.reduce((acc, index) => {
const changes = changedData?.indexes.reduce((acc: Record<number, (typeof data)[number]>, index: number) => {
// when data is filtered val !== actual index in data
const realIndex = data.findIndex((value) => value.__index === rows[index].__index)
acc[realIndex] = rows[index]
@@ -119,12 +117,9 @@ export const ValidationStep = <T extends string>({ initialData, file, onBack }:
})
.catch((err: Error) => {
toast({
status: "error",
variant: "left-accent",
position: "bottom-left",
title: `${translations.alerts.submitError.title}`,
description: err?.message || `${translations.alerts.submitError.defaultMessage}`,
isClosable: true,
variant: "destructive",
title: translations.alerts.submitError.title,
description: err?.message || translations.alerts.submitError.defaultMessage,
})
})
.finally(() => {
@@ -149,7 +144,7 @@ export const ValidationStep = <T extends string>({ initialData, file, onBack }:
}
return (
<>
<div className="flex h-full flex-col">
<AlertDialog open={showSubmitAlert} onOpenChange={setShowSubmitAlert}>
<AlertDialogPortal>
<AlertDialogOverlay className="z-[1400]" />
@@ -177,48 +172,64 @@ export const ValidationStep = <T extends string>({ initialData, file, onBack }:
</AlertDialogContent>
</AlertDialogPortal>
</AlertDialog>
<ModalBody pb={0}>
<Box display="flex" justifyContent="space-between" alignItems="center" mb="2rem" flexWrap="wrap" gap="8px">
<Heading sx={styles.heading}>{translations.validationStep.title}</Heading>
<Box display="flex" gap="16px" alignItems="center" flexWrap="wrap">
<Button variant="outline" size="sm" onClick={deleteSelectedRows}>
{translations.validationStep.discardButtonTitle}
<div className="flex-1 overflow-hidden">
<div className="px-8 py-6">
<div className="mb-8 flex flex-wrap items-center justify-between gap-2">
<h2 className="text-3xl font-semibold text-foreground">
{translations.validationStep.title}
</h2>
<div className="flex flex-wrap items-center gap-4">
<Button variant="outline" size="sm" onClick={deleteSelectedRows}>
{translations.validationStep.discardButtonTitle}
</Button>
<div className="flex items-center gap-2">
<Switch
checked={filterByErrors}
onCheckedChange={setFilterByErrors}
id="filter-errors"
/>
<label htmlFor="filter-errors" className="text-sm text-muted-foreground">
{translations.validationStep.filterSwitchTitle}
</label>
</div>
</div>
</div>
<div className="h-[calc(100vh-20rem)] w-full">
<DataGrid
rowKeyGetter={rowKeyGetter}
rows={tableData}
onRowsChange={updateRows}
columns={columns}
selectedRows={selectedRows}
onSelectedRowsChange={setSelectedRows}
className="h-full w-full"
noRowsFallback={
<div className="col-span-full mt-8 flex justify-center">
{filterByErrors
? translations.validationStep.noRowsMessageWhenFiltered
: translations.validationStep.noRowsMessage}
</div>
}
/>
</div>
</div>
</div>
<div className="px-8 py-8 border-t bg-muted -mt-4">
<div className="flex items-center justify-between -mt-4">
{onBack && (
<Button variant="outline" onClick={onBack}>
{translations.validationStep.backButtonTitle}
</Button>
<Switch
display="flex"
alignItems="center"
isChecked={filterByErrors}
onChange={() => setFilterByErrors(!filterByErrors)}
>
{translations.validationStep.filterSwitchTitle}
</Switch>
</Box>
</Box>
<Table
rowKeyGetter={rowKeyGetter}
rows={tableData}
onRowsChange={updateRows}
columns={columns}
selectedRows={selectedRows}
onSelectedRowsChange={setSelectedRows}
components={{
noRowsFallback: (
<Box display="flex" justifyContent="center" gridColumn="1/-1" mt="32px">
{filterByErrors
? translations.validationStep.noRowsMessageWhenFiltered
: translations.validationStep.noRowsMessage}
</Box>
),
}}
/>
</ModalBody>
<ContinueButton
isLoading={isSubmitting}
onContinue={onContinue}
onBack={onBack}
title={translations.validationStep.nextButtonTitle}
backTitle={translations.validationStep.backButtonTitle}
/>
</>
)}
<Button
className="ml-auto"
disabled={isSubmitting}
onClick={onContinue}
>
{translations.validationStep.nextButtonTitle}
</Button>
</div>
</div>
</div>
)
}

View File

@@ -1,7 +1,7 @@
#!/bin/zsh
#Clear previous mount in case its still there
umount /Users/matt/Library/Mobile Documents/com~apple~CloudDocs/Dev/inventory/inventory-server
umount "/Users/matt/Library/Mobile Documents/com~apple~CloudDocs/Dev/inventory/inventory-server"
#Mount
sshfs matt@dashboard.kent.pw:/var/www/html/inventory -p 22122 /Users/matt/Library/Mobile Documents/com~apple~CloudDocs/Dev/inventory/inventory-server/
sshfs matt@dashboard.kent.pw:/var/www/html/inventory -p 22122 "/Users/matt/Library/Mobile Documents/com~apple~CloudDocs/Dev/inventory/inventory-server/"