Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@

A powerful tool for generating realistic mock data using AI LLMs.

## New in v1.1.0: External API
## New in v1.2.0: Template & UI overhaul

**Programmatic Access with API Keys**
**Enhanced UX, templates, and event analytics**

- **External API Endpoint**: `POST /api/v1/generate` for programmatic data generation
- **Secure API Key Management**: Create, manage, and revoke API keys with 90-day expiry
- **Bearer Token Authentication**: Use API keys in Authorization header
- **Clean Data Response**: Get clean JSON/SQL/CSV/etc, without explanatory text
- **Rate Limiting**: 5 free generations per day (paid tiers coming soon).
- **Multiple Formats**: JSON, SQL, CSV, XML, HTML, TXT support
- Dedicated **Templates page** with searchable categories, SQL/NoSQL toggles, and generator injection
- Modal template browser using gradient CTAs, skeleton loaders, and dual-schema cards that prefill the generator
- Refreshed **Generator UI**: skeleton loaders, progress/cancel controls, CTA gradient utilities, and repositioned save/reset controls
- **Events dashboard table** with filters plus portrait jsPDF export for auditing
- Unified dark theme and CSS overhual, nav/footer polish, and consistent CTA styles

### Simplest Quick Start with External API

Expand Down Expand Up @@ -103,9 +102,9 @@ VERCEL_OIDC_TOKEN=token_to_connect_to_redis_service
- Real-time record generation request tracking using Redis Streams/Upstash
- Rate limitting of free API record generation requests using Redis
- Export data in JSON, SQL, or CSV formats
- **NEW**: External API with secure API key authentication
- **NEW**: Programmatic data generation for integrations
- **NEW**: API key management with usage tracking
- **NEW**: Templates catalogue & modal picker with SQL/NoSQL toggle and generator injection
- **NEW**: Generator progress/cancel controls plus CTA gradient system
- **NEW**: Events dashboard table with filters and portrait jsPDF export

## Tech Stack

Expand Down Expand Up @@ -214,6 +213,12 @@ npm run test:critical

## Releases

### v1.2.0 - UI, templates, and analytics
- **Templates catalogue**: Dedicated templates page + modal picker with SQL/NoSQL toggle, dual-schema data, and generator injection
- **Generator polish**: Skeletons, CTA gradient system, progress/cancellation controls, and repositioned save/reset actions
- **Events dashboard**: Filterable table prepared for audits and portrait jsPDF export
- **Global theming**: Unified dark CSS theme, navbar/footer polish, and CTA color tokens

### v1.1.0 - External API & API Key Management
- **External API**: New `/api/v1/generate` endpoint for programmatic access
- **API Key Management**: Create, manage, and revoke API keys with 90-day expiry
Expand Down
4 changes: 2 additions & 2 deletions __tests__/components/ui/Button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ describe('Button Component', () => {
expect(button).toBeInTheDocument();

// Default variant classes should be applied
expect(button).toHaveClass('bg-primary');
expect(button).toHaveClass('text-primary-foreground');
expect(button).toHaveClass('cta-button');
expect(button).toHaveClass('text-white');

// Default size classes should be applied
expect(button).toHaveClass('h-9');
Expand Down
46 changes: 46 additions & 0 deletions __tests__/components/ui/alert.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* @jest-environment jsdom
*/

import React from "react"
import { render, screen } from "@testing-library/react"
import "@testing-library/jest-dom"

jest.mock("@/lib/utils", () => ({
cn: (...args: string[]) => args.filter(Boolean).join(" "),
}))

import {
Alert,
AlertDescription,
AlertTitle,
} from "@/components/ui/alert"

describe("Alert components", () => {
it("renders default alert with expected classes", () => {
render(
<Alert data-testid="alert-default">
<AlertTitle>Title</AlertTitle>
<AlertDescription>Description</AlertDescription>
</Alert>
)

const alert = screen.getByTestId("alert-default")
expect(alert).toHaveRole("alert")
expect(alert).toHaveClass("bg-background", "text-foreground")
expect(screen.getByText("Title")).toHaveClass("font-medium")
expect(screen.getByText("Description")).toHaveClass("text-sm")
})

it("applies destructive variant classes", () => {
render(
<Alert variant="destructive" data-testid="alert-destructive">
Destructive
</Alert>
)

const alert = screen.getByTestId("alert-destructive")
expect(alert).toHaveClass("border-destructive/50", "text-destructive")
})
})

81 changes: 81 additions & 0 deletions __tests__/components/ui/card.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* @jest-environment jsdom
*/

import React from "react"
import { render, screen } from "@testing-library/react"
import "@testing-library/jest-dom"

jest.mock("@/lib/utils", () => ({
cn: (...args: string[]) => args.filter(Boolean).join(" "),
}))

import {
Card,
CardAction,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"

describe("Card components", () => {
it("renders the Card wrapper and inner surface with provided props", () => {
render(
<Card data-testid="card-inner" className="extra-class">
card body
</Card>
)

const inner = screen.getByTestId("card-inner")
expect(inner).toBeInTheDocument()
expect(inner).toHaveClass("min-h-full", "extra-class")

const outer = inner.closest("[data-slot='card']")
expect(outer).toBeInTheDocument()
expect(outer).toHaveStyle(
"background-image: linear-gradient(135deg, var(--card-gradient-start), var(--card-gradient-middle), var(--card-gradient-end))"
)
})

it("applies slot classes for header, title, and description", () => {
render(
<Card>
<CardHeader data-testid="card-header">
<CardTitle data-testid="card-title">Title</CardTitle>
<CardDescription data-testid="card-description">
Description
</CardDescription>
</CardHeader>
</Card>
)

const header = screen.getByTestId("card-header")
expect(header).toHaveAttribute("data-slot", "card-header")
expect(header).toHaveClass("grid", "items-start")

expect(screen.getByTestId("card-title")).toHaveClass("font-semibold")
expect(screen.getByTestId("card-description")).toHaveClass(
"text-muted-foreground"
)
})

it("renders action, content, and footer slots", () => {
render(
<Card>
<CardContent data-testid="card-content">Content</CardContent>
<CardAction data-testid="card-action">Action</CardAction>
<CardFooter data-testid="card-footer">Footer</CardFooter>
</Card>
)

expect(screen.getByTestId("card-content")).toHaveClass("px-6")
expect(screen.getByTestId("card-action")).toHaveAttribute(
"data-slot",
"card-action"
)
expect(screen.getByTestId("card-footer")).toHaveClass("flex")
})
})

41 changes: 41 additions & 0 deletions __tests__/components/ui/label.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* @jest-environment jsdom
*/

import React from "react"
import { render, screen } from "@testing-library/react"
import "@testing-library/jest-dom"

jest.mock("@/lib/utils", () => ({
cn: (...args: string[]) => args.filter(Boolean).join(" "),
}))

import { Label } from "@/components/ui/label"

describe("Label component", () => {
it("renders the label root with the provided text and classes", () => {
render(
<Label data-testid="label-root" className="custom-label">
Label Text
</Label>
)

const label = screen.getByTestId("label-root")
expect(label).toHaveTextContent("Label Text")
expect(label).toHaveAttribute("data-slot", "label")
expect(label).toHaveClass("flex", "items-center", "custom-label")
})

it("supports disabled styling via attributes", () => {
render(
<Label data-testid="label-disabled" disabled>
Disabled
</Label>
)

expect(screen.getByTestId("label-disabled")).toHaveClass(
"group-data-[disabled=true]:pointer-events-none"
)
})
})

24 changes: 24 additions & 0 deletions __tests__/components/ui/skeleton.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @jest-environment jsdom
*/

import React from "react"
import { render, screen } from "@testing-library/react"
import "@testing-library/jest-dom"

jest.mock("@/lib/utils", () => ({
cn: (...args: string[]) => args.filter(Boolean).join(" "),
}))

import Skeleton from "@/components/ui/skeleton"

describe("Skeleton component", () => {
it("renders the skeleton surface with default classes", () => {
render(<Skeleton data-testid="skeleton" className="extra" />)

const skeleton = screen.getByTestId("skeleton")
expect(skeleton).toHaveAttribute("role", "presentation")
expect(skeleton).toHaveClass("animate-pulse", "rounded-lg", "extra")
})
})

18 changes: 8 additions & 10 deletions __tests__/components/ui/toast.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,9 @@ describe('Toast Components', () => {
);

const toast = screen.getByRole('alert', { name: 'toast' });
expect(toast).toHaveClass('destructive');
expect(toast).toHaveClass('border-destructive');
expect(toast).toHaveClass('bg-destructive');
expect(toast).toHaveClass('text-destructive-foreground');
expect(toast).toHaveClass('border-destructive');
expect(toast).toHaveClass('bg-destructive');
expect(toast).toHaveClass('text-destructive-foreground');
});

it('renders with success variant', () => {
Expand All @@ -112,11 +111,11 @@ describe('Toast Components', () => {
);

const toast = screen.getByRole('alert', { name: 'toast' });
expect(toast).toHaveClass('border-green-500');
expect(toast).toHaveClass('bg-green-100');
expect(toast).toHaveClass('text-green-900');
expect(toast).toHaveClass('dark:bg-green-900/20');
expect(toast).toHaveClass('dark:border-green-500/30');
expect(toast).toHaveClass('border-green-500');
expect(toast).toHaveClass('bg-green-50');
expect(toast).toHaveClass('text-green-900');
expect(toast).toHaveClass('dark:bg-green-900/20');
expect(toast).toHaveClass('dark:border-green-500/30');
expect(toast).toHaveClass('dark:text-green-300');
});

Expand Down Expand Up @@ -174,7 +173,6 @@ describe('Toast Components', () => {
render(<Toast variant="destructive">Destructive Toast</Toast>);
const toast = screen.getByRole('alert', { name: 'toast' });
expect(toast).toBeInTheDocument();
expect(toast).toHaveClass('destructive');
});

it('renders Toast with success variant', () => {
Expand Down
Loading