diff --git a/bun.lock b/bun.lock
index 189f5f9..d8d8025 100644
--- a/bun.lock
+++ b/bun.lock
@@ -12,6 +12,7 @@
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tooltip": "^1.2.8",
+ "@stepperize/react": "^5.1.9",
"@tailwindcss/vite": "^4.0.6",
"@tanstack/react-devtools": "^0.7.0",
"@tanstack/react-query": "^5.66.5",
@@ -362,6 +363,10 @@
"@solid-primitives/utils": ["@solid-primitives/utils@6.3.2", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-hZ/M/qr25QOCcwDPOHtGjxTD8w2mNyVAYvcfgwzBHq2RwNqHNdDNsMZYap20+ruRwW4A3Cdkczyoz0TSxLCAPQ=="],
+ "@stepperize/core": ["@stepperize/core@1.2.7", "", { "peerDependencies": { "typescript": ">=5.0.2" } }, "sha512-XiUwLZ0XRAfaDK6AzWVgqvI/BcrylyplhUXKO8vzgRw0FTmyMKHAAbQLDvU//ZJAqnmG2cSLZDSkcwLxU5zSYA=="],
+
+ "@stepperize/react": ["@stepperize/react@5.1.9", "", { "dependencies": { "@stepperize/core": "1.2.7" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-yBgw1I5Tx6/qZB4xTdVBaPGfTqH5aYS1WFB5vtR8+fwPeqd3YNuOnQ1pJM6w/xV/gvryuy31hbFw080lZc+/hw=="],
+
"@stylistic/eslint-plugin": ["@stylistic/eslint-plugin@5.6.1", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.0", "@typescript-eslint/types": "^8.47.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "estraverse": "^5.3.0", "picomatch": "^4.0.3" }, "peerDependencies": { "eslint": ">=9.0.0" } }, "sha512-JCs+MqoXfXrRPGbGmho/zGS/jMcn3ieKl/A8YImqib76C8kjgZwq5uUFzc30lJkMvcchuRn6/v8IApLxli3Jyw=="],
"@tailwindcss/node": ["@tailwindcss/node@4.1.18", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.6.1", "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.1.18" } }, "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ=="],
diff --git a/package.json b/package.json
index 95a361f..1b17817 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tooltip": "^1.2.8",
+ "@stepperize/react": "^5.1.9",
"@tailwindcss/vite": "^4.0.6",
"@tanstack/react-devtools": "^0.7.0",
"@tanstack/react-query": "^5.66.5",
diff --git a/src/components/planes/App.css b/src/components/planes/App.css
new file mode 100644
index 0000000..b9d355d
--- /dev/null
+++ b/src/components/planes/App.css
@@ -0,0 +1,42 @@
+#root {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
+}
+
+.logo {
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
+}
+.logo:hover {
+ filter: drop-shadow(0 0 2em #646cffaa);
+}
+.logo.react:hover {
+ filter: drop-shadow(0 0 2em #61dafbaa);
+}
+
+@keyframes logo-spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@media (prefers-reduced-motion: no-preference) {
+ a:nth-of-type(2) .logo {
+ animation: logo-spin infinite 20s linear;
+ }
+}
+
+.card {
+ padding: 2em;
+}
+
+.read-the-docs {
+ color: #888;
+}
diff --git a/src/components/planes/stepper.tsx b/src/components/planes/stepper.tsx
new file mode 100644
index 0000000..569fa81
--- /dev/null
+++ b/src/components/planes/stepper.tsx
@@ -0,0 +1,163 @@
+import { defineStepper } from '@stepperize/react'
+import * as React from 'react'
+
+import { Button } from '@/components/ui/button'
+import { Input } from '@/components/ui/input'
+import { Separator } from '@/components/ui/separator'
+import { Textarea } from '@/components/ui/textarea'
+// import './App.css'
+
+const { useStepper, steps, utils } = defineStepper(
+ {
+ id: 'shipping',
+ title: 'Shipping',
+ description: 'Enter your shipping details',
+ },
+ {
+ id: 'payment',
+ title: 'Payment',
+ description: 'Enter your payment details',
+ },
+ { id: 'complete', title: 'Complete', description: 'Checkout complete' },
+)
+
+function App() {
+ const stepper = useStepper()
+
+ const currentIndex = utils.getIndex(stepper.current.id)
+ return (
+
+
+
Checkout
+
+
+ Step {currentIndex + 1} of {steps.length}
+
+
+
+
+
+
+ {stepper.switch({
+ shipping: () =>
,
+ payment: () =>
,
+ complete: () =>
,
+ })}
+ {!stepper.isLast ? (
+
+
+
+
+ ) : (
+
+ )}
+
+
+ )
+}
+
+const ShippingComponent = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const PaymentComponent = () => {
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+const CompleteComponent = () => {
+ return Stepper complete 🔥
+}
+
+export default App
diff --git a/src/components/stepper.tsx b/src/components/stepper.tsx
new file mode 100644
index 0000000..93c3750
--- /dev/null
+++ b/src/components/stepper.tsx
@@ -0,0 +1,534 @@
+import { Slot } from '@radix-ui/react-slot'
+import * as Stepperize from '@stepperize/react'
+import { cva } from 'class-variance-authority'
+import * as React from 'react'
+
+import type { VariantProps } from 'class-variance-authority'
+
+import { Button } from '@/components/ui/button'
+import { cn } from '@/lib/utils'
+
+const StepperContext = React.createContext(null)
+
+const useStepperProvider = (): Stepper.ConfigProps => {
+ const context = React.useContext(StepperContext)
+ if (!context) {
+ throw new Error('useStepper must be used within a StepperProvider.')
+ }
+ return context
+}
+
+const defineStepper = >(
+ ...steps: Steps
+): Stepper.DefineProps => {
+ const { Scoped, useStepper, ...rest } = Stepperize.defineStepper(...steps)
+
+ const StepperContainer = ({
+ children,
+ className,
+ ...props
+ }: Omit, 'children'> & {
+ children:
+ | React.ReactNode
+ | ((props: { methods: Stepperize.Stepper }) => React.ReactNode)
+ }) => {
+ const methods = useStepper()
+
+ return (
+
+ {typeof children === 'function' ? children({ methods }) : children}
+
+ )
+ }
+
+ return {
+ ...rest,
+ useStepper,
+ Stepper: {
+ Provider: ({
+ variant = 'horizontal',
+ labelOrientation = 'horizontal',
+ tracking = false,
+ children,
+ className,
+ ...props
+ }) => {
+ return (
+
+
+
+ {children}
+
+
+
+ )
+ },
+ Navigation: ({
+ children,
+ 'aria-label': ariaLabel = 'Stepper Navigation',
+ ...props
+ }) => {
+ const { variant } = useStepperProvider()
+ return (
+
+ )
+ },
+ Step: ({ children, className, icon, ...props }) => {
+ const { variant, labelOrientation } = useStepperProvider()
+ const { current } = useStepper()
+
+ const utils = rest.utils
+ const steps = rest.steps
+
+ const stepIndex = utils.getIndex(props.of)
+ const step = steps[stepIndex]
+ const currentIndex = utils.getIndex(current.id)
+
+ const isLast = utils.getLast().id === props.of
+ const isActive = current.id === props.of
+
+ const dataState = getStepState(currentIndex, stepIndex)
+ const childMap = useStepChildren(children)
+
+ const title = childMap.get('title')
+ const description = childMap.get('description')
+ const panel = childMap.get('panel')
+
+ if (variant === 'circle') {
+ return (
+
+
+
+ {title}
+ {description}
+
+
+ )
+ }
+
+ return (
+ <>
+
+
+ {variant === 'horizontal' && labelOrientation === 'vertical' && (
+
+ )}
+
+ {title}
+ {description}
+
+
+
+ {variant === 'horizontal' && labelOrientation === 'horizontal' && (
+
+ )}
+
+ {variant === 'vertical' && (
+
+ {!isLast && (
+
+
+
+ )}
+
{panel}
+
+ )}
+ >
+ )
+ },
+ Title,
+ Description,
+ Panel: ({ children, asChild, ...props }) => {
+ const Comp = asChild ? Slot : 'div'
+ const { tracking } = useStepperProvider()
+
+ return (
+ scrollIntoStepperPanel(node, tracking)}
+ {...props}
+ >
+ {children}
+
+ )
+ },
+ Controls: ({ children, className, asChild, ...props }) => {
+ const Comp = asChild ? Slot : 'div'
+ return (
+
+ {children}
+
+ )
+ },
+ },
+ }
+}
+
+const Title = ({
+ children,
+ className,
+ asChild,
+ ...props
+}: React.ComponentProps<'h4'> & { asChild?: boolean }) => {
+ const Comp = asChild ? Slot : 'h4'
+
+ return (
+
+ {children}
+
+ )
+}
+
+const Description = ({
+ children,
+ className,
+ asChild,
+ ...props
+}: React.ComponentProps<'p'> & { asChild?: boolean }) => {
+ const Comp = asChild ? Slot : 'p'
+
+ return (
+
+ {children}
+
+ )
+}
+
+const StepperSeparator = ({
+ orientation,
+ isLast,
+ labelOrientation,
+ state,
+ disabled,
+}: {
+ isLast: boolean
+ state: string
+ disabled?: boolean
+} & VariantProps) => {
+ if (isLast) {
+ return null
+ }
+ return (
+
+ )
+}
+
+const CircleStepIndicator = ({
+ currentStep,
+ totalSteps,
+ size = 80,
+ strokeWidth = 6,
+}: Stepper.CircleStepIndicatorProps) => {
+ const radius = (size - strokeWidth) / 2
+ const circumference = radius * 2 * Math.PI
+ const fillPercentage = (currentStep / totalSteps) * 100
+ const dashOffset = circumference - (circumference * fillPercentage) / 100
+ return (
+
+
+
+
+ {currentStep} of {totalSteps}
+
+
+
+ )
+}
+
+const classForNavigationList = cva('flex gap-2', {
+ variants: {
+ variant: {
+ horizontal: 'flex-row items-center justify-between',
+ vertical: 'flex-col',
+ circle: 'flex-row items-center justify-between',
+ },
+ },
+})
+
+const classForSeparator = cva(
+ [
+ 'bg-muted',
+ 'data-[state=completed]:bg-primary data-[disabled]:opacity-50',
+ 'transition-all duration-300 ease-in-out',
+ ],
+ {
+ variants: {
+ orientation: {
+ horizontal: 'h-0.5 flex-1',
+ vertical: 'h-full w-0.5',
+ },
+ labelOrientation: {
+ vertical:
+ 'absolute top-5 right-[calc(-50%+20px)] left-[calc(50%+30px)] block shrink-0',
+ },
+ },
+ },
+)
+
+function scrollIntoStepperPanel(
+ node: HTMLDivElement | null,
+ tracking?: boolean,
+) {
+ if (tracking) {
+ node?.scrollIntoView({ behavior: 'smooth', block: 'center' })
+ }
+}
+
+const useStepChildren = (children: React.ReactNode) => {
+ return React.useMemo(() => extractChildren(children), [children])
+}
+
+const extractChildren = (children: React.ReactNode) => {
+ const childrenArray = React.Children.toArray(children)
+ const map = new Map()
+
+ for (const child of childrenArray) {
+ if (React.isValidElement(child)) {
+ if (child.type === Title) {
+ map.set('title', child)
+ } else if (child.type === Description) {
+ map.set('description', child)
+ } else {
+ map.set('panel', child)
+ }
+ }
+ }
+
+ return map
+}
+
+const onStepKeyDown = (
+ e: React.KeyboardEvent,
+ nextStep: Stepperize.Step,
+ prevStep: Stepperize.Step,
+) => {
+ const { key } = e
+ const directions = {
+ next: ['ArrowRight', 'ArrowDown'],
+ prev: ['ArrowLeft', 'ArrowUp'],
+ }
+
+ if (directions.next.includes(key) || directions.prev.includes(key)) {
+ const direction = directions.next.includes(key) ? 'next' : 'prev'
+ const step = direction === 'next' ? nextStep : prevStep
+
+ if (!step) {
+ return
+ }
+
+ const stepElement = document.getElementById(`step-${step.id}`)
+ if (!stepElement) {
+ return
+ }
+
+ const isActive =
+ stepElement.parentElement?.getAttribute('data-state') !== 'inactive'
+ if (isActive || direction === 'prev') {
+ stepElement.focus()
+ }
+ }
+}
+
+const getStepState = (currentIndex: number, stepIndex: number) => {
+ if (currentIndex === stepIndex) {
+ return 'active'
+ }
+ if (currentIndex > stepIndex) {
+ return 'completed'
+ }
+ return 'inactive'
+}
+
+namespace Stepper {
+ export type StepperVariant = 'horizontal' | 'vertical' | 'circle'
+ export type StepperLabelOrientation = 'horizontal' | 'vertical'
+
+ export type ConfigProps = {
+ variant?: StepperVariant
+ labelOrientation?: StepperLabelOrientation
+ tracking?: boolean
+ }
+
+ export type DefineProps> = Omit<
+ Stepperize.StepperReturn,
+ 'Scoped'
+ > & {
+ Stepper: {
+ Provider: (
+ props: Omit, 'children'> &
+ Omit, 'children'> &
+ Stepper.ConfigProps & {
+ children:
+ | React.ReactNode
+ | ((props: {
+ methods: Stepperize.Stepper
+ }) => React.ReactNode)
+ },
+ ) => React.ReactElement
+ Navigation: (props: React.ComponentProps<'nav'>) => React.ReactElement
+ Step: (
+ props: React.ComponentProps<'button'> & {
+ of: Stepperize.Get.Id
+ icon?: React.ReactNode
+ },
+ ) => React.ReactElement
+ Title: (props: AsChildProps<'h4'>) => React.ReactElement
+ Description: (props: AsChildProps<'p'>) => React.ReactElement
+ Panel: (props: AsChildProps<'div'>) => React.ReactElement
+ Controls: (props: AsChildProps<'div'>) => React.ReactElement
+ }
+ }
+
+ export type CircleStepIndicatorProps = {
+ currentStep: number
+ totalSteps: number
+ size?: number
+ strokeWidth?: number
+ }
+}
+
+type AsChildProps = React.ComponentProps & {
+ asChild?: boolean
+}
+
+export { defineStepper }
diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx
new file mode 100644
index 0000000..bb3ad74
--- /dev/null
+++ b/src/components/ui/separator.tsx
@@ -0,0 +1,26 @@
+import * as React from "react"
+import * as SeparatorPrimitive from "@radix-ui/react-separator"
+
+import { cn } from "@/lib/utils"
+
+function Separator({
+ className,
+ orientation = "horizontal",
+ decorative = true,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export { Separator }
diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx
new file mode 100644
index 0000000..7f21b5e
--- /dev/null
+++ b/src/components/ui/textarea.tsx
@@ -0,0 +1,18 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
+ return (
+
+ )
+}
+
+export { Textarea }
diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts
index c315158..d66293f 100644
--- a/src/routeTree.gen.ts
+++ b/src/routeTree.gen.ts
@@ -9,18 +9,14 @@
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
import { Route as rootRouteImport } from './routes/__root'
-import { Route as PlanesRouteImport } from './routes/planes'
import { Route as LoginRouteImport } from './routes/login'
import { Route as DashboardRouteImport } from './routes/dashboard'
import { Route as IndexRouteImport } from './routes/index'
-import { Route as PlanesIndexRouteImport } from './routes/planes/index'
import { Route as DemoTanstackQueryRouteImport } from './routes/demo/tanstack-query'
+import { Route as PlanesListaRouteRouteImport } from './routes/planes/_lista/route'
+import { Route as PlanesPlanIdRouteRouteImport } from './routes/planes/$planId/route'
+import { Route as PlanesListaNuevoRouteImport } from './routes/planes/_lista/nuevo'
-const PlanesRoute = PlanesRouteImport.update({
- id: '/planes',
- path: '/planes',
- getParentRoute: () => rootRouteImport,
-} as any)
const LoginRoute = LoginRouteImport.update({
id: '/login',
path: '/login',
@@ -36,40 +32,54 @@ const IndexRoute = IndexRouteImport.update({
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
-const PlanesIndexRoute = PlanesIndexRouteImport.update({
- id: '/',
- path: '/',
- getParentRoute: () => PlanesRoute,
-} as any)
const DemoTanstackQueryRoute = DemoTanstackQueryRouteImport.update({
id: '/demo/tanstack-query',
path: '/demo/tanstack-query',
getParentRoute: () => rootRouteImport,
} as any)
+const PlanesListaRouteRoute = PlanesListaRouteRouteImport.update({
+ id: '/planes/_lista',
+ path: '/planes',
+ getParentRoute: () => rootRouteImport,
+} as any)
+const PlanesPlanIdRouteRoute = PlanesPlanIdRouteRouteImport.update({
+ id: '/planes/$planId',
+ path: '/planes/$planId',
+ getParentRoute: () => rootRouteImport,
+} as any)
+const PlanesListaNuevoRoute = PlanesListaNuevoRouteImport.update({
+ id: '/nuevo',
+ path: '/nuevo',
+ getParentRoute: () => PlanesListaRouteRoute,
+} as any)
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/dashboard': typeof DashboardRoute
'/login': typeof LoginRoute
- '/planes': typeof PlanesRouteWithChildren
+ '/planes/$planId': typeof PlanesPlanIdRouteRoute
+ '/planes': typeof PlanesListaRouteRouteWithChildren
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
- '/planes/': typeof PlanesIndexRoute
+ '/planes/nuevo': typeof PlanesListaNuevoRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/dashboard': typeof DashboardRoute
'/login': typeof LoginRoute
+ '/planes/$planId': typeof PlanesPlanIdRouteRoute
+ '/planes': typeof PlanesListaRouteRouteWithChildren
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
- '/planes': typeof PlanesIndexRoute
+ '/planes/nuevo': typeof PlanesListaNuevoRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/dashboard': typeof DashboardRoute
'/login': typeof LoginRoute
- '/planes': typeof PlanesRouteWithChildren
+ '/planes/$planId': typeof PlanesPlanIdRouteRoute
+ '/planes/_lista': typeof PlanesListaRouteRouteWithChildren
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
- '/planes/': typeof PlanesIndexRoute
+ '/planes/_lista/nuevo': typeof PlanesListaNuevoRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
@@ -77,38 +87,41 @@ export interface FileRouteTypes {
| '/'
| '/dashboard'
| '/login'
+ | '/planes/$planId'
| '/planes'
| '/demo/tanstack-query'
- | '/planes/'
+ | '/planes/nuevo'
fileRoutesByTo: FileRoutesByTo
- to: '/' | '/dashboard' | '/login' | '/demo/tanstack-query' | '/planes'
+ to:
+ | '/'
+ | '/dashboard'
+ | '/login'
+ | '/planes/$planId'
+ | '/planes'
+ | '/demo/tanstack-query'
+ | '/planes/nuevo'
id:
| '__root__'
| '/'
| '/dashboard'
| '/login'
- | '/planes'
+ | '/planes/$planId'
+ | '/planes/_lista'
| '/demo/tanstack-query'
- | '/planes/'
+ | '/planes/_lista/nuevo'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
DashboardRoute: typeof DashboardRoute
LoginRoute: typeof LoginRoute
- PlanesRoute: typeof PlanesRouteWithChildren
+ PlanesPlanIdRouteRoute: typeof PlanesPlanIdRouteRoute
+ PlanesListaRouteRoute: typeof PlanesListaRouteRouteWithChildren
DemoTanstackQueryRoute: typeof DemoTanstackQueryRoute
}
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
- '/planes': {
- id: '/planes'
- path: '/planes'
- fullPath: '/planes'
- preLoaderRoute: typeof PlanesRouteImport
- parentRoute: typeof rootRouteImport
- }
'/login': {
id: '/login'
path: '/login'
@@ -130,13 +143,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
- '/planes/': {
- id: '/planes/'
- path: '/'
- fullPath: '/planes/'
- preLoaderRoute: typeof PlanesIndexRouteImport
- parentRoute: typeof PlanesRoute
- }
'/demo/tanstack-query': {
id: '/demo/tanstack-query'
path: '/demo/tanstack-query'
@@ -144,25 +150,47 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof DemoTanstackQueryRouteImport
parentRoute: typeof rootRouteImport
}
+ '/planes/_lista': {
+ id: '/planes/_lista'
+ path: '/planes'
+ fullPath: '/planes'
+ preLoaderRoute: typeof PlanesListaRouteRouteImport
+ parentRoute: typeof rootRouteImport
+ }
+ '/planes/$planId': {
+ id: '/planes/$planId'
+ path: '/planes/$planId'
+ fullPath: '/planes/$planId'
+ preLoaderRoute: typeof PlanesPlanIdRouteRouteImport
+ parentRoute: typeof rootRouteImport
+ }
+ '/planes/_lista/nuevo': {
+ id: '/planes/_lista/nuevo'
+ path: '/nuevo'
+ fullPath: '/planes/nuevo'
+ preLoaderRoute: typeof PlanesListaNuevoRouteImport
+ parentRoute: typeof PlanesListaRouteRoute
+ }
}
}
-interface PlanesRouteChildren {
- PlanesIndexRoute: typeof PlanesIndexRoute
+interface PlanesListaRouteRouteChildren {
+ PlanesListaNuevoRoute: typeof PlanesListaNuevoRoute
}
-const PlanesRouteChildren: PlanesRouteChildren = {
- PlanesIndexRoute: PlanesIndexRoute,
+const PlanesListaRouteRouteChildren: PlanesListaRouteRouteChildren = {
+ PlanesListaNuevoRoute: PlanesListaNuevoRoute,
}
-const PlanesRouteWithChildren =
- PlanesRoute._addFileChildren(PlanesRouteChildren)
+const PlanesListaRouteRouteWithChildren =
+ PlanesListaRouteRoute._addFileChildren(PlanesListaRouteRouteChildren)
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
DashboardRoute: DashboardRoute,
LoginRoute: LoginRoute,
- PlanesRoute: PlanesRouteWithChildren,
+ PlanesPlanIdRouteRoute: PlanesPlanIdRouteRoute,
+ PlanesListaRouteRoute: PlanesListaRouteRouteWithChildren,
DemoTanstackQueryRoute: DemoTanstackQueryRoute,
}
export const routeTree = rootRouteImport
diff --git a/src/routes/planes/$planId/route.tsx b/src/routes/planes/$planId/route.tsx
new file mode 100644
index 0000000..9537c88
--- /dev/null
+++ b/src/routes/planes/$planId/route.tsx
@@ -0,0 +1,10 @@
+import { createFileRoute } from '@tanstack/react-router'
+
+export const Route = createFileRoute('/planes/$planId')({
+ component: RouteComponent,
+})
+
+function RouteComponent() {
+ const { planId } = Route.useParams()
+ return Hello "/planes/{planId}"!
+}
diff --git a/src/routes/planes/_lista/nuevo.tsx b/src/routes/planes/_lista/nuevo.tsx
new file mode 100644
index 0000000..1c98084
--- /dev/null
+++ b/src/routes/planes/_lista/nuevo.tsx
@@ -0,0 +1,29 @@
+import { createFileRoute, useNavigate } from '@tanstack/react-router'
+
+import CheckoutStepper from '@/components/planes/stepper'
+import { Dialog, DialogContent } from '@/components/ui/dialog'
+
+export const Route = createFileRoute('/planes/_lista/nuevo')({
+ component: NuevoPlanModal,
+})
+
+function NuevoPlanModal() {
+ const navigate = useNavigate()
+
+ const handleClose = () => {
+ // Navegamos de regreso a la lista manteniendo el scroll donde estaba
+ navigate({ to: '/planes', resetScroll: false })
+ }
+
+ return (
+
+ )
+}
diff --git a/src/routes/planes.tsx b/src/routes/planes/_lista/route.tsx
similarity index 97%
rename from src/routes/planes.tsx
rename to src/routes/planes/_lista/route.tsx
index e92124e..71e2e05 100644
--- a/src/routes/planes.tsx
+++ b/src/routes/planes/_lista/route.tsx
@@ -1,4 +1,4 @@
-import { createFileRoute } from '@tanstack/react-router'
+import { createFileRoute, Outlet, Link } from '@tanstack/react-router'
import * as Icons from 'lucide-react'
import { useMemo, useState } from 'react'
@@ -8,7 +8,7 @@ import BarraBusqueda from '@/components/planes/BarraBusqueda'
import Filtro from '@/components/planes/Filtro'
import PlanEstudiosCard from '@/components/planes/PlanEstudiosCard'
-export const Route = createFileRoute('/planes')({
+export const Route = createFileRoute('/planes/_lista')({
component: RouteComponent,
})
@@ -313,6 +313,10 @@ function RouteComponent() {
})}
+
+ Nuevo plan de estudios
+
+
)
diff --git a/src/routes/planes/index.tsx b/src/routes/planes/index.tsx
deleted file mode 100644
index 096e925..0000000
--- a/src/routes/planes/index.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { createFileRoute } from '@tanstack/react-router'
-import { AppLayout } from '@/components/layout/AppLayout'
-import { PlanGrid } from '@/components/plans/PlanGrid'
-
-export const Route = createFileRoute('/planes/')({
- component: PlanesPage,
-})
-
-function PlanesPage() {
- return (
-
-
-
- )
-}