UI

ui-step-wizard

Implements multi-step forms with validation, progress indicators, and audit logging.

Install

mkdir -p .claude/skills/ui-step-wizard && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/14938" && unzip -o skill.zip -d .claude/skills/ui-step-wizard && rm skill.zip

Installs to .claude/skills/ui-step-wizard

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

StepWizard pentru formulare multi-step ERP: validare per step cu Zod, progress indicator, back/next navigation, submit la final. PrintLayout pentru documente printabile cu @media print. AuditTrail — timeline din audit.audit_log.
228 charsno explicit “when” trigger
Intermediate

Key capabilities

  • Create multi-step forms with navigation
  • Validate form data per step using Zod schemas
  • Display a progress indicator for multi-step forms
  • Generate printable documents with custom CSS for print media
  • Display an audit trail timeline from audit logs
  • Handle form submission and loading states

How it works

The skill provides React components for a multi-step wizard with Zod validation and navigation, a print layout component for styling documents for printing, and an audit trail component to visualize log data as a timeline. These components are designed for ERP forms.

Inputs & outputs

You give it
Array of WizardStep objects, form instance from react-hook-form, onSubmit function
You get back
Multi-step form UI, printable document, audit trail timeline

When to use ui-step-wizard

  • Build ERP forms
  • Manage form state
  • Implement step-based navigation
  • Validate forms with Zod

About this skill

StepWizard, PrintLayout, AuditTrail


StepWizard

// components/common/StepWizard/StepWizard.tsx
import { useState } from 'react';
import { CheckIcon } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import type { ZodSchema } from 'zod';
import type { UseFormReturn } from 'react-hook-form';

export interface WizardStep {
  id:          string;
  title:       string;
  description?: string;
  schema?:     ZodSchema;         // validare Zod pentru acest step
  component:   React.ReactNode;
}

interface StepWizardProps {
  steps:       WizardStep[];
  form:        UseFormReturn<any>;
  onSubmit:    (data: any) => void;
  isSubmitting?: boolean;
}

export function StepWizard({
  steps,
  form,
  onSubmit,
  isSubmitting,
}: StepWizardProps) {
  const [current, setCurrent] = useState(0);
  const isFirst = current === 0;
  const isLast  = current === steps.length - 1;

  const goNext = async () => {
    const step = steps[current];
    if (step.schema) {
      // Validează doar câmpurile din step-ul curent
      const values = form.getValues();
      const result = step.schema.safeParse(values);
      if (!result.success) {
        // Triggerează erorile RHF pentru câmpurile invalide
        result.error.issues.forEach((issue) => {
          const path = issue.path.join('.');
          form.setError(path as any, { message: issue.message });
        });
        return;
      }
    }
    setCurrent((c) => c + 1);
  };

  const goBack = () => setCurrent((c) => c - 1);

  return (
    <div className="space-y-6">
      {/* Progress indicator */}
      <div className="flex items-center">
        {steps.map((step, index) => (
          <div key={step.id} className="flex items-center flex-1 last:flex-none">
            {/* Circle */}
            <div className={cn(
              'h-8 w-8 rounded-full flex items-center justify-center',
              'text-xs font-semibold shrink-0 border-2 transition-colors',
              index < current
                ? 'bg-primary-500 border-primary-500 text-white'
                : index === current
                  ? 'border-primary-500 text-primary-600'
                  : 'border-border-strong text-text-muted'
            )}>
              {index < current
                ? <CheckIcon className="h-4 w-4" />
                : index + 1
              }
            </div>

            {/* Label */}
            <div className="ml-2 hidden sm:block">
              <p className={cn(
                'text-xs font-medium',
                index === current ? 'text-primary-600' : 'text-text-muted'
              )}>
                {step.title}
              </p>
            </div>

            {/* Connector */}
            {index < steps.length - 1 && (
              <div className={cn(
                'flex-1 h-0.5 mx-3',
                index < current ? 'bg-primary-500' : 'bg-border-default'
              )} />
            )}
          </div>
        ))}
      </div>

      {/* Step content */}
      <div className="min-h-[300px]">
        <div className="mb-4">
          <h3 className="text-base font-semibold">{steps[current].title}</h3>
          {steps[current].description && (
            <p className="text-sm text-text-muted mt-1">
              {steps[current].description}
            </p>
          )}
        </div>
        {steps[current].component}
      </div>

      {/* Navigation */}
      <div className="flex justify-between pt-4 border-t border-border-default">
        <Button
          type="button"
          variant="outline"
          onClick={goBack}
          disabled={isFirst}
        >
          Înapoi
        </Button>

        {isLast ? (
          <Button
            type="button"
            onClick={form.handleSubmit(onSubmit)}
            disabled={isSubmitting}
          >
            {isSubmitting ? 'Se salvează...' : 'Finalizează'}
          </Button>
        ) : (
          <Button type="button" onClick={goNext}>
            Continuă
          </Button>
        )}
      </div>
    </div>
  );
}

PrintLayout — Document Printabil

// components/common/PrintLayout/PrintLayout.tsx
// CSS @media print — ascunde UI, afișează doar documentul

interface PrintLayoutProps {
  children:  React.ReactNode;
  title?:    string;   // titlu document în header print
}

export function PrintLayout({ children, title }: PrintLayoutProps) {
  return (
    <>
      {/* Buton print — ascuns la printare */}
      <div className="print:hidden mb-4 flex gap-2">
        <Button variant="outline" onClick={() => window.print()}>
          Printează
        </Button>
      </div>

      {/* Conținut document */}
      <div className="print:block">
        {children}
      </div>

      {/* Stiluri print */}
      <style>{`
        @media print {
          /* Ascunde UI */
          nav, aside, header, .print\\:hidden { display: none !important; }

          /* Dimensiuni pagină A4 */
          @page {
            size: A4 portrait;
            margin: 15mm 20mm;
          }

          body {
            font-size: 11pt;
            color: #000;
            background: #fff;
          }

          /* Evită ruperea tabelelor între pagini */
          table { page-break-inside: avoid; }
          tr    { page-break-inside: avoid; }

          /* Header pe fiecare pagină */
          thead { display: table-header-group; }
          tfoot { display: table-footer-group; }
        }
      `}</style>
    </>
  );
}

// Template factură printabilă
function InvoicePrint({ invoice }: { invoice: InvoiceDetailDto }) {
  return (
    <PrintLayout title={`Factură ${invoice.invoiceNumber}`}>
      <div className="p-8 max-w-3xl mx-auto">
        {/* Header */}
        <div className="flex justify-between mb-8">
          <div>
            <h1 className="text-2xl font-bold">FACTURĂ FISCALĂ</h1>
            <p className="text-lg font-semibold">{invoice.invoiceNumber}</p>
          </div>
          <div className="text-right text-sm">
            <p>Data: {new Date(invoice.createdAt).toLocaleDateString('ro-RO')}</p>
            <p>Scadent: {new Date(invoice.dueDate).toLocaleDateString('ro-RO')}</p>
          </div>
        </div>

        {/* Furnizor / Client */}
        <div className="grid grid-cols-2 gap-8 mb-8 text-sm">
          <div>
            <h3 className="font-semibold mb-1">Furnizor</h3>
            {/* date furnizor */}
          </div>
          <div>
            <h3 className="font-semibold mb-1">Client</h3>
            <p>{invoice.customerName}</p>
          </div>
        </div>

        {/* Linii */}
        <table className="w-full text-sm border-collapse mb-6">
          <thead>
            <tr className="border-b-2 border-black">
              <th className="text-left py-2">Descriere</th>
              <th className="text-right py-2">Cant.</th>
              <th className="text-right py-2">Preț unit.</th>
              <th className="text-right py-2">TVA</th>
              <th className="text-right py-2">Total</th>
            </tr>
          </thead>
          <tbody>
            {invoice.lines?.map((line, i) => (
              <tr key={i} className="border-b border-gray-200">
                <td className="py-1">{line.description}</td>
                <td className="text-right py-1">{line.quantity}</td>
                <td className="text-right py-1 font-mono">
                  {line.unitPrice.toLocaleString('ro-RO', { minimumFractionDigits: 2 })}
                </td>
                <td className="text-right py-1">{line.vatRate * 100}%</td>
                <td className="text-right py-1 font-mono">
                  {line.lineTotal.toLocaleString('ro-RO', { minimumFractionDigits: 2 })}
                </td>
              </tr>
            ))}
          </tbody>
          <tfoot>
            <tr className="border-t-2 border-black font-semibold">
              <td colSpan={4} className="text-right py-2">TOTAL:</td>
              <td className="text-right py-2 font-mono">
                {invoice.totalAmount.toLocaleString('ro-RO', {
                  minimumFractionDigits: 2
                })} RON
              </td>
            </tr>
          </tfoot>
        </table>
      </div>
    </PrintLayout>
  );
}

AuditTrail — Timeline Modificări

// components/common/AuditTrail/AuditTrail.tsx
import { useQuery } from '@tanstack/react-query';
import { api } from '@/lib/axios';
import { format } from 'date-fns';
import { ro } from 'date-fns/locale';

interface AuditEntry {
  id:          number;
  action:      string;
  userName:    string;
  oldValues?:  Record<string, unknown>;
  newValues?:  Record<string, unknown>;
  createdAt:   string;
}

interface AuditTrailProps {
  entityType: string;
  entityId:   string;
}

export function AuditTrail({ entityType, entityId }: AuditTrailProps) {
  const { data = [], isLoading } = useQuery({
    queryKey: ['audit', entityType, entityId],
    queryFn:  () => api.get<AuditEntry[]>('/administration/audit', {
      params: { entityType, entityId }
    }),
    staleTime: 60_000,
  });

  if (isLoading) {
    return <div className="space-y-3">
      {[1,2,3].map(i => (
        <div key={i} className="h-16 bg-surface-muted rounded animate-pulse" />
      ))}
    </div>;
  }

  if (data.length === 0) {
    return (
      <p className="text-sm text-text-muted text-center py-8">
        Nu există istoric modificări.
      </p>
    );
  }

  return (
    <div className="space-y-0">
      {data.map((entry, index) => (
        <div key={entry.id} className="flex gap-3">
          {/* Timeline line */}
          <div className="flex flex-col items-center">
            <div className="h-2.5 w-2.5 rounded-full bg-primary-400 mt-1.5 shrink-0" />
            {index < data.length - 1 && (
              <div className="flex-1 w-px bg-border-default mt-1" />
            )}
          </div>

          {/* Content */}
          <div className="pb-4 min-w-0 fl

---

*Content truncated.*

When not to use it

  • When a single-step form is sufficient
  • When print layouts are not required
  • When audit trail visualization is not needed

Limitations

  • Specific to React and Zod for form validation
  • PrintLayout uses CSS @media print for styling
  • AuditTrail component expects a specific audit log data structure

How it compares

This skill offers pre-built, integrated UI components for complex multi-step forms, print layouts, and audit trails, providing a ready-to-use solution that simplifies development compared to building these functionalities from scratch.

Compared to similar skills

ui-step-wizard side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
ui-step-wizard (this skill)03moReviewIntermediate
ui-ux-pro-max1,9095moReviewIntermediate
scroll-experience1016moNo flagsIntermediate
ui-styling129moReviewBeginner

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

ui-ux-pro-max

nextlevelbuilder

UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient.

1,9092,023

scroll-experience

davila7

Expert in building immersive scroll-driven experiences - parallax storytelling, scroll animations, interactive narratives, and cinematic web experiences. Like NY Times interactives, Apple product pages, and award-winning web experiences. Makes websites feel like experiences, not just pages. Use when: scroll animation, parallax, scroll storytelling, interactive story, cinematic website.

101142

ui-styling

mrgoonie

Create beautiful, accessible user interfaces with shadcn/ui components (built on Radix UI + Tailwind), Tailwind CSS utility-first styling, and canvas-based visual designs. Use when building user interfaces, implementing design systems, creating responsive layouts, adding accessible components (dialogs, dropdowns, forms, tables), customizing themes and colors, implementing dark mode, generating visual designs and posters, or establishing consistent styling patterns across applications.

12132

elegant-design

rand

Create world-class, accessible, responsive interfaces with sophisticated interactive elements including chat, terminals, code display, and streaming content. Use when building user interfaces that need professional polish and developer-focused features.

21108

figma

openai

Use the Figma MCP server to fetch design context, screenshots, variables, and assets from Figma, and to translate Figma nodes into production code. Trigger when a task involves Figma URLs, node IDs, design-to-code implementation, or Figma MCP setup and troubleshooting.

2266

interaction-design

wshobson

Design and implement microinteractions, motion design, transitions, and user feedback patterns. Use when adding polish to UI interactions, implementing loading states, or creating delightful user experiences.

1550

Search skills

Search the agent skills registry