diff --git a/kits/automation/time-series-preprocessor/.env.example b/kits/automation/time-series-preprocessor/.env.example
new file mode 100644
index 00000000..aeca4c07
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/.env.example
@@ -0,0 +1,4 @@
+TIME_SERIES_PREPROCESSOR="Your Flow ID from Lamatic Studio"
+LAMATIC_API_URL="Your API Endpoint URL"
+LAMATIC_PROJECT_ID="Your Project ID"
+LAMATIC_API_KEY="Your API Key"
diff --git a/kits/automation/time-series-preprocessor/.gitignore b/kits/automation/time-series-preprocessor/.gitignore
new file mode 100644
index 00000000..0d1e747b
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/.gitignore
@@ -0,0 +1,29 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files
+.env
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
+
+.env.local
\ No newline at end of file
diff --git a/kits/automation/time-series-preprocessor/README.md b/kits/automation/time-series-preprocessor/README.md
new file mode 100644
index 00000000..3962d626
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/README.md
@@ -0,0 +1,162 @@
+# Time-Series Preprocessor — Lamatic AgentKit
+
+An automation kit that analyzes time-series dataset schemas and generates production-ready Python preprocessing pipelines using `pandas` and `scikit-learn`. Paste a JSON summary of your dataset and receive executable code in seconds.
+
+---
+
+## What It Does
+
+By providing a JSON summary of your dataset, the agent generates a complete Python script that handles:
+
+- **Missing value imputation** — forward-fill, mean, and median strategies selected based on column type
+- **Feature scaling** — MinMaxScaler or StandardScaler applied appropriately
+- **Datetime parsing and index management** — automatic timestamp detection and alignment
+- **Categorical encoding** — label or one-hot encoding based on cardinality
+- **Standardized implementation** — clean, readable `pandas` + `scikit-learn` code ready to run
+
+---
+
+## Project Background
+
+This kit was developed to solve the repetitive nature of data cleaning in time-series projects.
+
+The concept originated during the development of a **water demand forecasting model** for a college located in a rural area near Bhopal. Due to aging sensor hardware and inconsistent data streams, significant time was spent manually handling missing values and aligning disparate data sources — rainfall readings, local water levels, and consumption logs from different systems with mismatched timestamps.
+
+The goal was to automate the boilerplate preprocessing code, allowing engineers to focus on model performance rather than manual cleanup. This kit is the result of that experience.
+
+---
+
+## Who Is It For
+
+Data engineers and machine learning engineers who frequently work with time-series data and need a fast way to generate reliable, repeatable preprocessing pipelines without writing boilerplate code from scratch.
+
+---
+
+## Tech Stack
+
+| Tool | Role |
+|---|---|
+| [Lamatic.ai](https://lamatic.ai) | Flow orchestration and Edge deployment |
+| Gemini 2.5 Pro | AI-driven Python code generation |
+| Next.js 14 | Interactive frontend sandbox |
+| pandas + scikit-learn | Target libraries for generated pipelines |
+
+---
+
+## Setup
+
+### 1. Build and Deploy Flow in Lamatic Studio
+
+1. Sign in at [lamatic.ai](https://lamatic.ai)
+2. Create a new project (if you don't have one)
+3. Click **+ New Flow** and select **API Request** as the trigger
+4. Add a **Generate Text** node and select **Gemini 2.5 Pro**
+5. Set the input variable to `dataset_summary`
+6. Configure the system prompt to act as an expert data engineer
+7. Deploy the flow and copy your credentials from the Studio dashboard
+
+### 2. Environment Variables
+
+Create a `.env.local` file in the kit root directory:
+
+```env
+TIME_SERIES_PREPROCESSOR="Your Flow ID from Lamatic Studio"
+LAMATIC_API_URL="Your API Endpoint URL"
+LAMATIC_PROJECT_ID="Your Project ID"
+LAMATIC_API_KEY="Your API Key"
+```
+
+| Variable | Where to Find It |
+|---|---|
+| `TIME_SERIES_PREPROCESSOR` | Studio → Your Flow → Settings |
+| `LAMATIC_API_URL` | Studio → Your Flow → API Endpoint |
+| `LAMATIC_PROJECT_ID` | Studio → Project Settings |
+| `LAMATIC_API_KEY` | Studio → Project Settings → API Keys |
+
+### 3. Install and Run
+
+```bash
+npm install
+npm run dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) to access the frontend interface.
+
+---
+
+## Example Input
+
+Provide a JSON object describing your dataset structure:
+
+```json
+{
+ "dataset_name": "sensor_readings",
+ "frequency": "1min",
+ "columns": [
+ {"name": "timestamp", "type": "datetime"},
+ {"name": "temperature", "type": "float", "missing_pct": 5},
+ {"name": "pressure", "type": "float", "missing_pct": 2},
+ {"name": "status", "type": "categorical", "missing_pct": 0}
+ ],
+ "rows": 50000,
+ "target_column": "temperature"
+}
+```
+
+## Example Output
+
+The agent returns a fully executable Python script:
+
+```python
+import pandas as pd
+from sklearn.preprocessing import MinMaxScaler
+
+# Load and index
+df = pd.read_csv("sensor_readings.csv", parse_dates=["timestamp"])
+df.set_index("timestamp", inplace=True)
+
+# Impute missing values
+df["temperature"].fillna(method="ffill", inplace=True)
+df["pressure"].fillna(df["pressure"].mean(), inplace=True)
+
+# Scale numerical features
+scaler = MinMaxScaler()
+df[["temperature", "pressure"]] = scaler.fit_transform(df[["temperature", "pressure"]])
+
+print("Preprocessing complete.")
+print(df.head())
+```
+
+---
+
+## Project Structure
+
+````text
+time-series-preprocessor/
+├── actions/
+│ └── orchestrate.ts # Server action calling the Lamatic flow
+├── app/
+│ └── page.tsx # Main UI — input form and output display
+├── components/
+│ └── ui/ # shadcn/ui components
+├── flows/
+│ └── time-series-preprocessor/
+│ ├── config.json # Exported Lamatic flow graph
+│ ├── inputs.json # Input schema definition
+│ └── meta.json # Flow metadata
+├── lib/
+│ └── lamatic-client.ts # Lamatic SDK client
+├── .env.example # Environment variable template
+├── config.json # Kit metadata
+└── README.md
+````
+
+---
+
+## Contributing
+
+Contributions are welcome. Open an issue or pull request in the [AgentKit repository](https://github.com/Lamatic/AgentKit).
+
+## License
+
+MIT License — see [LICENSE](../../../LICENSE).
\ No newline at end of file
diff --git a/kits/automation/time-series-preprocessor/actions/orchestrate.ts b/kits/automation/time-series-preprocessor/actions/orchestrate.ts
new file mode 100644
index 00000000..bafc66bf
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/actions/orchestrate.ts
@@ -0,0 +1,31 @@
+"use server";
+
+import { createLamaticClient } from "@/lib/lamatic-client";
+
+const client = createLamaticClient();
+
+export async function preprocessTimeSeries(datasetSummary: string) {
+ if (!process.env.TIME_SERIES_PREPROCESSOR) {
+ throw new Error("TIME_SERIES_PREPROCESSOR environment variable is not set");
+ }
+
+ try {
+ const response = await client.executeFlow({
+ flowId: process.env.TIME_SERIES_PREPROCESSOR,
+ inputs: {
+ dataset_summary: datasetSummary,
+ },
+ });
+
+ return {
+ success: true,
+ result: response?.data?.generatedText || "",
+ };
+ } catch (error) {
+ console.error("Error calling Lamatic flow:", error);
+ return {
+ success: false,
+ result: "Failed to generate preprocessing pipeline. Please try again.",
+ };
+ }
+}
\ No newline at end of file
diff --git a/kits/automation/time-series-preprocessor/app/globals.css b/kits/automation/time-series-preprocessor/app/globals.css
new file mode 100644
index 00000000..90357922
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/app/globals.css
@@ -0,0 +1,15 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+body {
+ font-family: 'Inter', sans-serif;
+ background-color: #f5f5f5;
+ color: #111;
+}
\ No newline at end of file
diff --git a/kits/automation/time-series-preprocessor/app/layout.tsx b/kits/automation/time-series-preprocessor/app/layout.tsx
new file mode 100644
index 00000000..094e778a
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/app/layout.tsx
@@ -0,0 +1,29 @@
+import type { Metadata } from "next";
+import { ThemeProvider } from "@/components/ThemeProvider"
+import "./globals.css";
+
+export const metadata: Metadata = {
+ title: "Time-Series Preprocessor — Lamatic AgentKit",
+ description: "AI-powered time-series preprocessing pipeline generator",
+};
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/kits/automation/time-series-preprocessor/app/page.tsx b/kits/automation/time-series-preprocessor/app/page.tsx
new file mode 100644
index 00000000..6749ed88
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/app/page.tsx
@@ -0,0 +1,225 @@
+"use client";
+
+import { useState } from "react";
+import { preprocessTimeSeries } from "@/actions/orchestrate";
+
+const EXAMPLE_INPUT = `{
+ "dataset_name": "sensor_readings",
+ "frequency": "1min",
+ "columns": [
+ {"name": "timestamp", "type": "datetime"},
+ {"name": "temperature", "type": "float", "missing_pct": 5},
+ {"name": "pressure", "type": "float", "missing_pct": 2},
+ {"name": "status", "type": "categorical", "missing_pct": 0}
+ ],
+ "rows": 50000,
+ "target_column": "temperature"
+}`;
+
+const FEATURES = [
+ { title: "Missing value imputation", desc: "Forward-fill, mean, and median strategies selected based on column type" },
+ { title: "Feature scaling", desc: "MinMaxScaler or StandardScaler applied appropriately" },
+ { title: "Datetime parsing and index management", desc: "Automatic timestamp detection and alignment" },
+ { title: "Categorical encoding", desc: "Label or one-hot encoding based on cardinality" },
+ { title: "Standardized implementation", desc: "Clean, readable pandas + scikit-learn code ready to run" },
+];
+
+export default function Home() {
+ const [input, setInput] = useState("");
+ const [output, setOutput] = useState("");
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState("");
+ const [copied, setCopied] = useState(false);
+
+ async function handleSubmit() {
+ if (!input.trim()) { setError("Please enter a dataset summary."); return; }
+ setLoading(true); setError(""); setOutput("");
+ const result = await preprocessTimeSeries(input);
+ if (result.success) {
+ setOutput(result.result);
+ setTimeout(() => {
+ document.getElementById("output")?.scrollIntoView({ behavior: "smooth" });
+ }, 100);
+ } else {
+ setError(result.result);
+ }
+ setLoading(false);
+ }
+
+ function handleCopy() {
+ navigator.clipboard.writeText(output);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ }
+
+ return (
+
+
+ {/* Navbar */}
+
+
+ {/* Hero */}
+
+
+ AgentKit — Automation Kit
+
+
+ Turn Dataset Schemas into
+ Python Pipelines Instantly
+
+
+ Paste a JSON summary of your time-series dataset and receive a production-ready preprocessing script using pandas and scikit-learn.
+
+
+ {["Saves Hours of Boilerplate", "Preprocessing Pipelines", "Ready-to-Run Python Scripts"].map(tag => (
+ {tag}
+ ))}
+
+
+ Try It Now ↓
+
+
+
+ {/* Main Content */}
+
+
+ {/* What It Does */}
+
+
+ What It Does
+
+
+ By providing a JSON summary of your dataset, the agent generates a complete Python script that handles:
+
+
+ {FEATURES.map(item => (
+
+
+
+ {item.title}
+ — {item.desc}
+
+
+ ))}
+
+
+
+ {/* Input Card */}
+
+
+
+
+
Dataset Summary
+
JSON
+
+
+
+
+
+ {/* Output Card */}
+ {output && (
+
+
+
+
+
Generated Python Script
+
Ready to Run
+
+
+
+
+ {output.split('\n').map((line, i) => {
+ let color = "#f8f8f2";
+ if (line.trim().startsWith('#')) color = "#6272a4";
+ else if (line.includes('import ') || line.includes('from ')) color = "#ff79c6";
+ else if (line.includes('def ') || line.includes('class ')) color = "#50fa7b";
+ else if (line.includes('=') && !line.includes('==')) color = "#f8f8f2";
+ else if (line.trim().startsWith('df') || line.trim().startsWith('scaler') || line.trim().startsWith('encoder')) color = "#8be9fd";
+ return (
+
+
+ {String(i + 1).padStart(3, ' ')}
+
+ {line}
+
+ );
+ })}
+
+
+ )}
+
+ {/* Footer */}
+
+
+ Built by Siddhant Bagga · Lamatic AgentKit Challenge
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/kits/automation/time-series-preprocessor/components.json b/kits/automation/time-series-preprocessor/components.json
new file mode 100644
index 00000000..4ee62ee1
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/components.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": true,
+ "tsx": true,
+ "tailwind": {
+ "config": "",
+ "css": "app/globals.css",
+ "baseColor": "neutral",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "iconLibrary": "lucide"
+}
diff --git a/kits/automation/time-series-preprocessor/components/Header.tsx b/kits/automation/time-series-preprocessor/components/Header.tsx
new file mode 100644
index 00000000..2c2fcc5a
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/components/Header.tsx
@@ -0,0 +1,47 @@
+import Link from "next/link"
+import { FileText, Github } from "lucide-react"
+import Image from "next/image"
+
+export function Header() {
+ return (
+
+ )
+}
diff --git a/kits/automation/time-series-preprocessor/components/ThemeProvider.tsx b/kits/automation/time-series-preprocessor/components/ThemeProvider.tsx
new file mode 100644
index 00000000..55c2f6eb
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/components/ThemeProvider.tsx
@@ -0,0 +1,11 @@
+'use client'
+
+import * as React from 'react'
+import {
+ ThemeProvider as NextThemesProvider,
+ type ThemeProviderProps,
+} from 'next-themes'
+
+export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
+ return {children}
+}
diff --git a/kits/automation/time-series-preprocessor/components/ui/accordion.tsx b/kits/automation/time-series-preprocessor/components/ui/accordion.tsx
new file mode 100644
index 00000000..e538a33b
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/components/ui/accordion.tsx
@@ -0,0 +1,66 @@
+'use client'
+
+import * as React from 'react'
+import * as AccordionPrimitive from '@radix-ui/react-accordion'
+import { ChevronDownIcon } from 'lucide-react'
+
+import { cn } from '@/lib/utils'
+
+function Accordion({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function AccordionItem({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AccordionTrigger({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ svg]:rotate-180',
+ className,
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+ )
+}
+
+function AccordionContent({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ {children}
+
+ )
+}
+
+export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
diff --git a/kits/automation/time-series-preprocessor/components/ui/alert-dialog.tsx b/kits/automation/time-series-preprocessor/components/ui/alert-dialog.tsx
new file mode 100644
index 00000000..97044526
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/components/ui/alert-dialog.tsx
@@ -0,0 +1,157 @@
+'use client'
+
+import * as React from 'react'
+import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'
+
+import { cn } from '@/lib/utils'
+import { buttonVariants } from '@/components/ui/button'
+
+function AlertDialog({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function AlertDialogTrigger({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AlertDialogPortal({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AlertDialogOverlay({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AlertDialogContent({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+ )
+}
+
+function AlertDialogHeader({
+ className,
+ ...props
+}: React.ComponentProps<'div'>) {
+ return (
+
+ )
+}
+
+function AlertDialogFooter({
+ className,
+ ...props
+}: React.ComponentProps<'div'>) {
+ return (
+
+ )
+}
+
+function AlertDialogTitle({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AlertDialogDescription({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AlertDialogAction({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AlertDialogCancel({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export {
+ AlertDialog,
+ AlertDialogPortal,
+ AlertDialogOverlay,
+ AlertDialogTrigger,
+ AlertDialogContent,
+ AlertDialogHeader,
+ AlertDialogFooter,
+ AlertDialogTitle,
+ AlertDialogDescription,
+ AlertDialogAction,
+ AlertDialogCancel,
+}
diff --git a/kits/automation/time-series-preprocessor/components/ui/alert.tsx b/kits/automation/time-series-preprocessor/components/ui/alert.tsx
new file mode 100644
index 00000000..e6751abe
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/components/ui/alert.tsx
@@ -0,0 +1,66 @@
+import * as React from 'react'
+import { cva, type VariantProps } from 'class-variance-authority'
+
+import { cn } from '@/lib/utils'
+
+const alertVariants = cva(
+ 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current',
+ {
+ variants: {
+ variant: {
+ default: 'bg-card text-card-foreground',
+ destructive:
+ 'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ },
+ },
+)
+
+function Alert({
+ className,
+ variant,
+ ...props
+}: React.ComponentProps<'div'> & VariantProps) {
+ return (
+
+ )
+}
+
+function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
+ return (
+
+ )
+}
+
+function AlertDescription({
+ className,
+ ...props
+}: React.ComponentProps<'div'>) {
+ return (
+
+ )
+}
+
+export { Alert, AlertTitle, AlertDescription }
diff --git a/kits/automation/time-series-preprocessor/components/ui/aspect-ratio.tsx b/kits/automation/time-series-preprocessor/components/ui/aspect-ratio.tsx
new file mode 100644
index 00000000..40bb1208
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/components/ui/aspect-ratio.tsx
@@ -0,0 +1,11 @@
+'use client'
+
+import * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio'
+
+function AspectRatio({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+export { AspectRatio }
diff --git a/kits/automation/time-series-preprocessor/components/ui/avatar.tsx b/kits/automation/time-series-preprocessor/components/ui/avatar.tsx
new file mode 100644
index 00000000..aa98465a
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/components/ui/avatar.tsx
@@ -0,0 +1,53 @@
+'use client'
+
+import * as React from 'react'
+import * as AvatarPrimitive from '@radix-ui/react-avatar'
+
+import { cn } from '@/lib/utils'
+
+function Avatar({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AvatarImage({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AvatarFallback({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export { Avatar, AvatarImage, AvatarFallback }
diff --git a/kits/automation/time-series-preprocessor/components/ui/badge.tsx b/kits/automation/time-series-preprocessor/components/ui/badge.tsx
new file mode 100644
index 00000000..fc4126b7
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/components/ui/badge.tsx
@@ -0,0 +1,46 @@
+import * as React from 'react'
+import { Slot } from '@radix-ui/react-slot'
+import { cva, type VariantProps } from 'class-variance-authority'
+
+import { cn } from '@/lib/utils'
+
+const badgeVariants = cva(
+ 'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden',
+ {
+ variants: {
+ variant: {
+ default:
+ 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
+ secondary:
+ 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
+ destructive:
+ 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
+ outline:
+ 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ },
+ },
+)
+
+function Badge({
+ className,
+ variant,
+ asChild = false,
+ ...props
+}: React.ComponentProps<'span'> &
+ VariantProps & { asChild?: boolean }) {
+ const Comp = asChild ? Slot : 'span'
+
+ return (
+
+ )
+}
+
+export { Badge, badgeVariants }
diff --git a/kits/automation/time-series-preprocessor/components/ui/breadcrumb.tsx b/kits/automation/time-series-preprocessor/components/ui/breadcrumb.tsx
new file mode 100644
index 00000000..1750ff26
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/components/ui/breadcrumb.tsx
@@ -0,0 +1,109 @@
+import * as React from 'react'
+import { Slot } from '@radix-ui/react-slot'
+import { ChevronRight, MoreHorizontal } from 'lucide-react'
+
+import { cn } from '@/lib/utils'
+
+function Breadcrumb({ ...props }: React.ComponentProps<'nav'>) {
+ return
+}
+
+function BreadcrumbList({ className, ...props }: React.ComponentProps<'ol'>) {
+ return (
+
+ )
+}
+
+function BreadcrumbItem({ className, ...props }: React.ComponentProps<'li'>) {
+ return (
+
+ )
+}
+
+function BreadcrumbLink({
+ asChild,
+ className,
+ ...props
+}: React.ComponentProps<'a'> & {
+ asChild?: boolean
+}) {
+ const Comp = asChild ? Slot : 'a'
+
+ return (
+
+ )
+}
+
+function BreadcrumbPage({ className, ...props }: React.ComponentProps<'span'>) {
+ return (
+
+ )
+}
+
+function BreadcrumbSeparator({
+ children,
+ className,
+ ...props
+}: React.ComponentProps<'li'>) {
+ return (
+ svg]:size-3.5', className)}
+ {...props}
+ >
+ {children ?? }
+
+ )
+}
+
+function BreadcrumbEllipsis({
+ className,
+ ...props
+}: React.ComponentProps<'span'>) {
+ return (
+
+
+ More
+
+ )
+}
+
+export {
+ Breadcrumb,
+ BreadcrumbList,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+ BreadcrumbEllipsis,
+}
diff --git a/kits/automation/time-series-preprocessor/components/ui/button-group.tsx b/kits/automation/time-series-preprocessor/components/ui/button-group.tsx
new file mode 100644
index 00000000..09d44309
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/components/ui/button-group.tsx
@@ -0,0 +1,83 @@
+import { Slot } from '@radix-ui/react-slot'
+import { cva, type VariantProps } from 'class-variance-authority'
+
+import { cn } from '@/lib/utils'
+import { Separator } from '@/components/ui/separator'
+
+const buttonGroupVariants = cva(
+ "flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2",
+ {
+ variants: {
+ orientation: {
+ horizontal:
+ '[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none',
+ vertical:
+ 'flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none',
+ },
+ },
+ defaultVariants: {
+ orientation: 'horizontal',
+ },
+ },
+)
+
+function ButtonGroup({
+ className,
+ orientation,
+ ...props
+}: React.ComponentProps<'div'> & VariantProps) {
+ return (
+
+ )
+}
+
+function ButtonGroupText({
+ className,
+ asChild = false,
+ ...props
+}: React.ComponentProps<'div'> & {
+ asChild?: boolean
+}) {
+ const Comp = asChild ? Slot : 'div'
+
+ return (
+
+ )
+}
+
+function ButtonGroupSeparator({
+ className,
+ orientation = 'vertical',
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export {
+ ButtonGroup,
+ ButtonGroupSeparator,
+ ButtonGroupText,
+ buttonGroupVariants,
+}
diff --git a/kits/automation/time-series-preprocessor/components/ui/button.tsx b/kits/automation/time-series-preprocessor/components/ui/button.tsx
new file mode 100644
index 00000000..f64632d1
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/components/ui/button.tsx
@@ -0,0 +1,60 @@
+import * as React from 'react'
+import { Slot } from '@radix-ui/react-slot'
+import { cva, type VariantProps } from 'class-variance-authority'
+
+import { cn } from '@/lib/utils'
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
+ {
+ variants: {
+ variant: {
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
+ destructive:
+ 'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
+ outline:
+ 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
+ secondary:
+ 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
+ ghost:
+ 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
+ link: 'text-primary underline-offset-4 hover:underline',
+ },
+ size: {
+ default: 'h-9 px-4 py-2 has-[>svg]:px-3',
+ sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
+ lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
+ icon: 'size-9',
+ 'icon-sm': 'size-8',
+ 'icon-lg': 'size-10',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ size: 'default',
+ },
+ },
+)
+
+function Button({
+ className,
+ variant,
+ size,
+ asChild = false,
+ ...props
+}: React.ComponentProps<'button'> &
+ VariantProps & {
+ asChild?: boolean
+ }) {
+ const Comp = asChild ? Slot : 'button'
+
+ return (
+
+ )
+}
+
+export { Button, buttonVariants }
diff --git a/kits/automation/time-series-preprocessor/components/ui/calendar.tsx b/kits/automation/time-series-preprocessor/components/ui/calendar.tsx
new file mode 100644
index 00000000..eaa373e2
--- /dev/null
+++ b/kits/automation/time-series-preprocessor/components/ui/calendar.tsx
@@ -0,0 +1,213 @@
+'use client'
+
+import * as React from 'react'
+import {
+ ChevronDownIcon,
+ ChevronLeftIcon,
+ ChevronRightIcon,
+} from 'lucide-react'
+import { DayButton, DayPicker, getDefaultClassNames } from 'react-day-picker'
+
+import { cn } from '@/lib/utils'
+import { Button, buttonVariants } from '@/components/ui/button'
+
+function Calendar({
+ className,
+ classNames,
+ showOutsideDays = true,
+ captionLayout = 'label',
+ buttonVariant = 'ghost',
+ formatters,
+ components,
+ ...props
+}: React.ComponentProps & {
+ buttonVariant?: React.ComponentProps['variant']
+}) {
+ const defaultClassNames = getDefaultClassNames()
+
+ return (
+ svg]:rotate-180`,
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
+ className,
+ )}
+ captionLayout={captionLayout}
+ formatters={{
+ formatMonthDropdown: (date) =>
+ date.toLocaleString('default', { month: 'short' }),
+ ...formatters,
+ }}
+ classNames={{
+ root: cn('w-fit', defaultClassNames.root),
+ months: cn(
+ 'flex gap-4 flex-col md:flex-row relative',
+ defaultClassNames.months,
+ ),
+ month: cn('flex flex-col w-full gap-4', defaultClassNames.month),
+ nav: cn(
+ 'flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between',
+ defaultClassNames.nav,
+ ),
+ button_previous: cn(
+ buttonVariants({ variant: buttonVariant }),
+ 'size-(--cell-size) aria-disabled:opacity-50 p-0 select-none',
+ defaultClassNames.button_previous,
+ ),
+ button_next: cn(
+ buttonVariants({ variant: buttonVariant }),
+ 'size-(--cell-size) aria-disabled:opacity-50 p-0 select-none',
+ defaultClassNames.button_next,
+ ),
+ month_caption: cn(
+ 'flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)',
+ defaultClassNames.month_caption,
+ ),
+ dropdowns: cn(
+ 'w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5',
+ defaultClassNames.dropdowns,
+ ),
+ dropdown_root: cn(
+ 'relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md',
+ defaultClassNames.dropdown_root,
+ ),
+ dropdown: cn(
+ 'absolute bg-popover inset-0 opacity-0',
+ defaultClassNames.dropdown,
+ ),
+ caption_label: cn(
+ 'select-none font-medium',
+ captionLayout === 'label'
+ ? 'text-sm'
+ : 'rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5',
+ defaultClassNames.caption_label,
+ ),
+ table: 'w-full border-collapse',
+ weekdays: cn('flex', defaultClassNames.weekdays),
+ weekday: cn(
+ 'text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none',
+ defaultClassNames.weekday,
+ ),
+ week: cn('flex w-full mt-2', defaultClassNames.week),
+ week_number_header: cn(
+ 'select-none w-(--cell-size)',
+ defaultClassNames.week_number_header,
+ ),
+ week_number: cn(
+ 'text-[0.8rem] select-none text-muted-foreground',
+ defaultClassNames.week_number,
+ ),
+ day: cn(
+ 'relative w-full h-full p-0 text-center [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none',
+ defaultClassNames.day,
+ ),
+ range_start: cn(
+ 'rounded-l-md bg-accent',
+ defaultClassNames.range_start,
+ ),
+ range_middle: cn('rounded-none', defaultClassNames.range_middle),
+ range_end: cn('rounded-r-md bg-accent', defaultClassNames.range_end),
+ today: cn(
+ 'bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none',
+ defaultClassNames.today,
+ ),
+ outside: cn(
+ 'text-muted-foreground aria-selected:text-muted-foreground',
+ defaultClassNames.outside,
+ ),
+ disabled: cn(
+ 'text-muted-foreground opacity-50',
+ defaultClassNames.disabled,
+ ),
+ hidden: cn('invisible', defaultClassNames.hidden),
+ ...classNames,
+ }}
+ components={{
+ Root: ({ className, rootRef, ...props }) => {
+ return (
+
+ )
+ },
+ Chevron: ({ className, orientation, ...props }) => {
+ if (orientation === 'left') {
+ return (
+
+ )
+ }
+
+ if (orientation === 'right') {
+ return (
+
+ )
+ }
+
+ return (
+
+ )
+ },
+ DayButton: CalendarDayButton,
+ WeekNumber: ({ children, ...props }) => {
+ return (
+
+
+ {children}
+
+ |
+ )
+ },
+ ...components,
+ }}
+ {...props}
+ />
+ )
+}
+
+function CalendarDayButton({
+ className,
+ day,
+ modifiers,
+ ...props
+}: React.ComponentProps) {
+ const defaultClassNames = getDefaultClassNames()
+
+ const ref = React.useRef(null)
+ React.useEffect(() => {
+ if (modifiers.focused) ref.current?.focus()
+ }, [modifiers.focused])
+
+ return (
+