Skip to content

Instantly share code, notes, and snippets.

@sethdavis512
Created July 10, 2025 00:55
Show Gist options
  • Save sethdavis512/fbf09c644a1403c407ff1eedabab3ad3 to your computer and use it in GitHub Desktop.
Save sethdavis512/fbf09c644a1403c407ff1eedabab3ad3 to your computer and use it in GitHub Desktop.
Custom Copilot Instructions
You are an AI programming assistant that is specialized in applying code changes to an existing document. Follow Microsoft content policies. Avoid content that violates copyrights. If you are asked to generate content that is harmful, hateful, racist, sexist, lewd, violent, or completely irrelevant to software engineering, only respond with "Sorry, I can't assist with that." Keep your answers short and impersonal. The user has the following code open in the editor, starting from line 1. # GitHub Copilot Custom Instructions for TWS Generative UI

Project Overview

This is a React Router 7 application that combines generative AI with industry-agnostic project management, featuring a chat-based interface with dynamically generated UI components. The application adapts to various industries through configurable terminology and workflows.

Technology Stack & Frameworks

Core Framework

  • React Router 7: Use React Router 7 framework mode with file-based routing, server-side rendering, and type-safe route definitions
  • Always prefer React Router 7 patterns over Next.js or other React frameworks
  • Use loader functions for data fetching and action functions for mutations
  • Implement route-based code splitting and progressive enhancement

Styling & UI Components

  • Tailwind CSS 4: Use Tailwind CSS utility classes for ALL styling - never use inline styles or custom CSS
  • daisyUI 5: Use daisyUI component classes following the semantic naming convention (e.g., btn, card, chat-bubble)
  • CVA (Class Variance Authority): Use the configured CVA setup in ~/cva.config.ts with twMerge integration
  • NEVER use inline styles: Always convert to Tailwind utility classes or daisyUI components
  • Always prefer daisyUI color names (primary, secondary, base-100) over Tailwind color names for theme consistency
  • Use responsive design patterns with mobile-first approach (sm:, md:, lg:, xl:)
  • CSS customization: Only use @apply directive in CSS files when absolutely necessary for complex components

daisyUI 5 Integration Rules:

  • Install: @plugin "daisyui"; in CSS file, not tailwind.config.js (deprecated in v4)
  • Component structure: Add component class (btn), part classes if available, and modifier classes
  • Customization: Use Tailwind utilities alongside daisyUI classes (e.g., btn px-10)
  • Force override: Use ! suffix for CSS specificity issues (e.g., btn bg-red-500!) as last resort
  • Color system: Use semantic color names (primary, error, base-100) that adapt to themes
  • No custom CSS: Prefer daisyUI + Tailwind utilities over writing custom styles

Authentication & Security

  • Better Auth: Use Better Auth for all authentication needs
  • Server-side auth instance is in ~/utils/auth.server.ts with Prisma adapter
  • Client-side auth utilities are in ~/utils/auth.ts with React hooks
  • Always check session status using auth.api.getSession() on server and useSession() on client
  • Implement role-based access control using the Role enum (ADMIN, USER, PROVIDER, CLIENT, MANAGER, etc.)

Database & Data Management

  • Prisma: Use Prisma ORM with PostgreSQL database
  • Generated Prisma client is located at ~/generated/prisma/client.ts
  • Always use the configured Prisma instance from ~/lib/prisma.ts
  • Follow the established schema patterns for User, WorkItem, Thread, Message, and Task models
  • Use proper error handling and transaction patterns

AI & Chat Functionality

  • Vercel AI SDK: Use AI SDK for chat functionality with OpenAI integration
  • OpenAI API: Integrate OpenAI models for generative responses and tool calling
  • Use streaming responses with the useChat hook for real-time chat updates
  • Implement tool invocations for project management actions
  • Follow the established message conversion patterns using convertToUIMessage

Code Style & Patterns

File Organization

  • Components go in ~/components/ with UI-specific components in ~/components/ui/
  • Server-side business logic goes in ~/models/
  • Utilities go in ~/utils/
  • Routes follow React Router 7 file-based routing in ~/routes/
  • API routes are in ~/routes/api/ for backend functionality

Route Structure:

app/routes/
├── authenticated.tsx      # Layout route with auth checks
├── home.tsx              # Public pages
├── chat.tsx              # Feature layout routes
├── thread.tsx            # Dynamic routes
└── api/                  # API-only routes
    ├── auth.ts           # Better Auth handler
    ├── chat.ts           # AI chat endpoint
    └── tools/            # AI tool functions

Component Structure:

app/components/
├── Button.tsx            # Base UI components
├── Card.tsx
├── TextField.tsx
└── ui/                   # Feature-specific components
    ├── CreateWorkItemForm.tsx
    ├── MessageRenderer.tsx
    └── FrequentMessageManager.tsx

Component Patterns

  • Use functional components with TypeScript
  • Implement proper prop interfaces with descriptive names
  • Use the CVA pattern for component variants (see existing components like Button, ChatBubble)
  • Follow the composable component pattern for complex UI elements

Import Conventions

  • Use ~ alias for imports from the app directory

  • Import types with import type syntax

  • Group imports in this order:

    // React/Framework imports first
    import { useState } from 'react';
    import { useFetcher, useLoaderData } from 'react-router';
    
    // Third-party libraries
    import { PlusIcon } from 'lucide-react';
    
    // Local imports (using ~ alias)
    import { Button } from '~/components/Button';
    import { getUserIdFromRequest } from '~/utils/request.server';
    import type { Route } from './+types/route-name';

Error Handling

  • Use proper error boundaries and error handling patterns
  • Implement loading states and optimistic UI updates
  • Handle database errors gracefully with user-friendly messages
  • Always use invariant() for required data in loaders/actions
  • Throw Response objects for HTTP errors in loaders/actions
  • Use data() utility with error status for API routes
  • Export ErrorBoundary components from route modules

AI & Generative UI Guidelines

Chat Interface

  • Use the established MessageRenderer and BaseChatCard components for displaying chat messages
  • Implement tool calling capabilities for project management actions
  • Support rich message types including forms, cards, and interactive elements
  • Maintain conversation context across sessions

Dynamic Component Generation

  • Generate appropriate UI components based on user context and project data
  • Use role-based component rendering (different UI for ADMIN vs USER)
  • Implement contextual action buttons and forms within chat interface
  • Support embedded forms for project creation and editing

Industry-Agnostic Context

This application is designed for flexible project management with the following domain-agnostic considerations:

  • Projects/Work Items have configurable status tracking (ACTIVE, PENDING, IN_PROGRESS, ON_HOLD, COMPLETED, CANCELLED)
  • Users have different roles (ADMIN, PROVIDER, CLIENT, MANAGER, USER) with varying permissions
  • Task management includes work items and issue tracking with customizable terminology
  • Email notifications and team communication are core features
  • Timeline and progress tracking are essential for business workflows
  • Business configuration system allows adaptation to different industries (Construction, Consulting, Healthcare, Legal, etc.)
  • Configurable terminology maps generic concepts to industry-specific language (Projects→Cases, Tasks→Issues, etc.)

Performance Considerations

  • Use React Router 7's built-in optimizations for data loading and caching
  • Implement proper loading states for chat streams and data fetching
  • Use Prisma's efficient querying patterns with proper includes and selections
  • Optimize chat message rendering with virtualization for long conversations
  • Implement proper error boundaries and fallback UI components

Development Practices

  • Always use TypeScript with strict type checking
  • Follow the established patterns for server/client code separation
  • Use configurable validation functions from model files (validateTaskData, validateWorkItemData, validateUserData)
  • Use Zod for runtime validation of forms and API inputs when additional schema validation is needed
  • Always validate data server-side in actions/loaders before database operations
  • Implement proper SEO and accessibility patterns
  • Test authentication flows and role-based access thoroughly
  • Ensure responsive design works across all device sizes

API & Route Patterns

  • Use React Router 7 API routes in ~/routes/api/ for backend functionality
  • Implement proper CORS and security headers
  • Use Better Auth's built-in CSRF protection
  • Follow RESTful patterns for data APIs
  • Implement proper rate limiting and validation

Canonical Architectural Patterns

This section defines the core patterns used throughout the TWS Generative UI application to ensure consistency and maintainability.

Data Flow & API Call Patterns

When to Use Each Data Loading Pattern:

  • React Router 7 Loaders (loader functions):

    • Initial page loads, navigation, URL changes
    • Server-side data fetching with automatic revalidation
    • Access via useLoaderData<typeof loader>() or loaderData prop
  • React Router 7 Actions (action functions):

    • Form submissions, data mutations that require navigation
    • Server-side mutations with automatic loader revalidation
    • Access via useActionData() or actionData prop
  • useFetcher for Non-Navigational Operations:

    • UI updates without navigation (toggles, inline edits, chat interactions)
    • Background data loading (dropdowns, search suggestions)
    • Use fetcher.state for loading states, fetcher.data for results
  • useChat for AI Interactions:

    • Real-time streaming chat with OpenAI
    • Tool calling and generative UI responses
    • Message persistence and conversation management

Standard API Route Structure:

export async function loader({ request }: Route.LoaderArgs) {
  const userId = await getUserIdFromRequest({ request });
  invariant(userId, 'User must be logged in');
  return { data: await fetchData({ userId }) };
}

export async function action({ request }: Route.ActionArgs) {
  const formData = await request.formData();
  const intent = String(formData.get('intent'));

  if (intent === 'create') {
    // Handle creation logic
    return data({ success: true, item });
  }

  return data({ error: 'Invalid intent' }, { status: 400 });
}

Component Architecture Patterns

All Forms Must Use Card Structure:

<Card variant="bordered" size="full">
  <CardBody>
    <h2 className="card-title">Create New {config.terminology.workItem}</h2>
    <form onSubmit={handleSubmit}>
      {/* Form fields using TextField, Select, etc. */}
    </form>
  </CardBody>
</Card>

CVA-Based Component Variants:

All UI components use Class Variance Authority (CVA) for consistent styling and type safety:

import { cva, type VariantProps } from 'cva';

const buttonVariants = cva({
  base: ['btn', 'transition-colors'],
  variants: {
    variant: {
      primary: 'btn-primary',
      secondary: 'btn-secondary',
      error: 'btn-error',
      ghost: 'btn-ghost',
    },
    size: {
      sm: 'btn-sm',
      md: 'btn-md',
      lg: 'btn-lg',
    },
    disabled: {
      false: null,
      true: ['opacity-50', 'cursor-not-allowed'],
    },
  },
  compoundVariants: [
    {
      variant: 'primary',
      disabled: false,
      class: 'hover:btn-primary-focus',
    },
  ],
  defaultVariants: {
    variant: 'primary',
    size: 'md',
    disabled: false,
  },
});

interface ButtonProps
  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
    VariantProps<typeof buttonVariants> {
  children: React.ReactNode;
}

export const Button: React.FC<ButtonProps> = ({
  className,
  variant,
  size,
  disabled,
  children,
  ...props
}) => (
  <button
    className={buttonVariants({ variant, size, disabled, className })}
    disabled={disabled || undefined}
    {...props}
  >
    {children}
  </button>
);

Form Component Pattern:

interface FormProps {
  onSubmit: (data: FormData) => void;
  onCancel: () => void;
  config: IndustryConfiguration;
  isLoading?: boolean;
  initialData?: any;
  validationErrors?: string[];
}

export function CustomForm({
  onSubmit,
  onCancel,
  config,
  isLoading,
  initialData,
  validationErrors,
}: FormProps) {
  const [formData, setFormData] = useState<FormData>(initialData || {});
  const [errors, setErrors] = useState<string[]>(validationErrors || []);

  // Use controlled components with validation
  // Implement real-time validation using server validation functions
  // Display errors using consistent error handling patterns

  return (
    <Card variant="bordered" size="full">
      <CardBody>
        <CardTitle>Create New {config.terminology.workItem}</CardTitle>
        <form onSubmit={handleSubmit} className="space-y-4">
          <TextField
            label={`${config.terminology.workItem} Title`}
            value={formData.title}
            onChange={(e) =>
              setFormData((prev) => ({ ...prev, title: e.target.value }))
            }
            error={errors.find((e) => e.includes('title'))}
            required
          />

          <div className="flex justify-end gap-2">
            <Button variant="ghost" onClick={onCancel}>
              Cancel
            </Button>
            <Button type="submit" disabled={isLoading}>
              {isLoading ? <Spinner size="sm" /> : 'Create'}
            </Button>
          </div>
        </form>
      </CardBody>
    </Card>
  );
}

React Router 7 Advanced Patterns

Data Loading Strategies:

// Server-side data loading with type safety
export async function loader({ request, params }: Route.LoaderArgs) {
  const session = await auth.api.getSession(request);
  invariant(session, 'User must be authenticated');

  // Parallel data loading for better performance
  const [workItems, tasks, config] = await Promise.all([
    getWorkItemsForTenant(session.user.tenantId),
    getTasksForTenant(session.user.tenantId),
    getBusinessConfiguration(session.user.tenantId),
  ]);

  return {
    workItems,
    tasks,
    config,
    user: session.user,
  };
}

// Client-side data loading for dynamic content
export async function clientLoader({
  params,
  serverLoader,
}: Route.ClientLoaderArgs) {
  const serverData = await serverLoader();

  // Add client-specific data (e.g., cached user preferences)
  const userPreferences = getUserPreferencesFromLocalStorage();

  return {
    ...serverData,
    userPreferences,
  };
}

Progressive Enhancement with Forms:

// Forms work with and without JavaScript
export function CreateWorkItemForm() {
  const fetcher = useFetcher();
  const isSubmitting = fetcher.state === 'submitting';

  return (
    <Card>
      <CardBody>
        <fetcher.Form
          method="post"
          action="/api/work-items"
          className="space-y-4"
        >
          <input type="hidden" name="intent" value="create" />

          <TextField
            name="title"
            label="Title"
            required
            disabled={isSubmitting}
          />

          <TextField
            name="description"
            label="Description"
            multiline
            rows={4}
            disabled={isSubmitting}
          />

          <Button type="submit" disabled={isSubmitting}>
            {isSubmitting ? <Spinner size="sm" /> : 'Create Work Item'}
          </Button>
        </fetcher.Form>
      </CardBody>
    </Card>
  );
}

Error Boundaries and Loading States:

// Route-level error boundary
export function ErrorBoundary() {
  const error = useRouteError();

  if (isRouteErrorResponse(error)) {
    return (
      <div className="flex min-h-screen items-center justify-center">
        <Card variant="error">
          <CardBody>
            <CardTitle>Error {error.status}</CardTitle>
            <p>{error.data || error.statusText}</p>
            <Button as={Link} to="/" variant="primary">
              Go Home
            </Button>
          </CardBody>
        </Card>
      </div>
    );
  }

  return (
    <div className="flex min-h-screen items-center justify-center">
      <Card variant="error">
        <CardBody>
          <CardTitle>Something went wrong</CardTitle>
          <p>{error?.message || 'An unexpected error occurred'}</p>
          <Button onClick={() => window.location.reload()}>Try Again</Button>
        </CardBody>
      </Card>
    </div>
  );
}

// Global loading indicator
export function LoadingIndicator() {
  const navigation = useNavigation();
  const isLoading = navigation.state === 'loading';

  if (!isLoading) return null;

  return (
    <div className="fixed left-0 right-0 top-0 z-50">
      <div className="bg-base-200 h-1">
        <div className="bg-primary h-full animate-pulse" />
      </div>
    </div>
  );
}

Streaming with Suspense:

// Defer non-critical data for faster page loads
export async function loader({ params }: Route.LoaderArgs) {
  // Critical data - wait for this
  const workItem = await getWorkItem(params.id);

  // Non-critical data - defer this
  const analytics = getWorkItemAnalytics(params.id); // Returns Promise

  return {
    workItem,
    analytics, // Deferred promise
  };
}

export default function WorkItemDetails({ loaderData }: Route.ComponentProps) {
  return (
    <div className="space-y-6">
      {/* Critical content renders immediately */}
      <Card>
        <CardBody>
          <CardTitle>{loaderData.workItem.title}</CardTitle>
          <p>{loaderData.workItem.description}</p>
        </CardBody>
      </Card>

      {/* Non-critical content streams in */}
      <React.Suspense fallback={<AnalyticsSkeleton />}>
        <Await resolve={loaderData.analytics}>
          {(analytics) => <AnalyticsChart data={analytics} />}
        </Await>
      </React.Suspense>
    </div>
  );
}

Optimistic UI Patterns:

export function TaskStatusToggle({ task }: { task: Task }) {
  const fetcher = useFetcher();

  // Optimistic state based on pending submission
  const isCompleted = fetcher.formData
    ? fetcher.formData.get('status') === 'COMPLETED'
    : task.status === 'COMPLETED';

  return (
    <fetcher.Form method="post" action={`/api/tasks/${task.id}/toggle-status`}>
      <input
        type="hidden"
        name="status"
        value={isCompleted ? 'IN_PROGRESS' : 'COMPLETED'}
      />

      <Button
        type="submit"
        variant={isCompleted ? 'success' : 'ghost'}
        size="sm"
        disabled={fetcher.state === 'submitting'}
      >
        {isCompleted ? (
          <>
            <CheckIcon className="h-4 w-4" />
            Completed
          </>
        ) : (
          <>
            <CircleIcon className="h-4 w-4" />
            Mark Complete
          </>
        )}
      </Button>
    </fetcher.Form>
  );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment