Converting to shadcn, steps, validate page
This commit is contained in:
31
inventory/package-lock.json
generated
31
inventory/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
28
inventory/src/components/ui/checkbox.tsx
Normal file
28
inventory/src/components/ui/checkbox.tsx
Normal 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 }
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
AlertDialogPortal,
|
||||
AlertDialogOverlay,
|
||||
} from "@/components/ui/alert-dialog"
|
||||
import { useState } from "react"
|
||||
import { useRsi } from "../hooks/useRsi"
|
||||
|
||||
type ModalCloseButtonProps = {
|
||||
|
||||
@@ -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}
|
||||
<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}
|
||||
>
|
||||
{steps.map((key) => (
|
||||
<Step label={translations[key].title} key={key} />
|
||||
))}
|
||||
</Stepper>
|
||||
</ModalHeader>
|
||||
<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} />
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -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">
|
||||
<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
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
isChecked={filterByErrors}
|
||||
onChange={() => setFilterByErrors(!filterByErrors)}
|
||||
>
|
||||
checked={filterByErrors}
|
||||
onCheckedChange={setFilterByErrors}
|
||||
id="filter-errors"
|
||||
/>
|
||||
<label htmlFor="filter-errors" className="text-sm text-muted-foreground">
|
||||
{translations.validationStep.filterSwitchTitle}
|
||||
</Switch>
|
||||
</Box>
|
||||
</Box>
|
||||
<Table
|
||||
</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}
|
||||
components={{
|
||||
noRowsFallback: (
|
||||
<Box display="flex" justifyContent="center" gridColumn="1/-1" mt="32px">
|
||||
className="h-full w-full"
|
||||
noRowsFallback={
|
||||
<div className="col-span-full mt-8 flex justify-center">
|
||||
{filterByErrors
|
||||
? translations.validationStep.noRowsMessageWhenFiltered
|
||||
: translations.validationStep.noRowsMessage}
|
||||
</Box>
|
||||
),
|
||||
}}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</ModalBody>
|
||||
<ContinueButton
|
||||
isLoading={isSubmitting}
|
||||
onContinue={onContinue}
|
||||
onBack={onBack}
|
||||
title={translations.validationStep.nextButtonTitle}
|
||||
backTitle={translations.validationStep.backButtonTitle}
|
||||
/>
|
||||
</>
|
||||
</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>
|
||||
)}
|
||||
<Button
|
||||
className="ml-auto"
|
||||
disabled={isSubmitting}
|
||||
onClick={onContinue}
|
||||
>
|
||||
{translations.validationStep.nextButtonTitle}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/zsh
|
||||
|
||||
#Clear previous mount in case it’s 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/"
|
||||
Reference in New Issue
Block a user