npm package: @rts-hr/careers
Description: Embeddable React careers widget for RTS hiring platform
Latest version: 0.1.5

# @rts-hr/careers

Embeddable React widget that displays your job board and application form directly on your website. Connects to your RTS hiring platform tenant via the public API.

## Installation

```bash
npm install @rts-hr/careers
```

Peer dependencies (must already be in your project):

```bash
npm install react react-dom
```

## Quick start

```tsx
import { CareersWidget } from "@rts-hr/careers";
import "@rts-hr/careers/style.css";

function App() {
  return <CareersWidget tenant="acme" />;
}
```

That's it. The widget renders a searchable job list. Clicking a job shows the full description and application form.

## Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `tenant` | `string` | — | Your tenant identifier (e.g. `"acme"`). Required when `fetchFn` is not provided. |
| `apiBaseUrl` | `string` | `https://{tenant}.rts-hr.cc/api` | Override the API URL. Useful for local development or custom domains. |
| `fetchFn` | `(endpoint, options?) => Promise<Response>` | — | Custom fetch function. When provided, `tenant` and `apiBaseUrl` are not needed — the caller controls all fetch logic. |
| `disableTheme` | `boolean` | `false` | Skip the built-in `ThemeProvider` + `ScopedCssBaseline`. Use when a parent component already provides an MUI theme. |
| `theme` | `object` | see below | Customize colors, font, and border radius. Ignored when `disableTheme` is true. |
| `locale` | `"de" \| "en"` | `"de"` | UI language. |
| `initialJobId` | `number` | — | Open a specific job on load instead of the job list. |
| `onNavigate` | `(view, jobId?) => void` | — | Called when the user navigates between list and detail views. |
| `onApplicationSubmitted` | `(jobId, applicationId) => void` | — | Called after a successful application submission. |
| `maxHeight` | `number \| "auto"` | `"auto"` | Constrain the widget height in pixels. Adds internal scrolling. |

### Theme object

```tsx
<CareersWidget
  tenant="acme"
  theme={{
    primaryColor: "#FF6600",    // Brand color (buttons, highlights)
    fontFamily: "Inter, sans-serif",
    borderRadius: 8,            // In pixels
    mode: "light",              // "light" or "dark"
  }}
/>
```

All theme properties are optional. Unset values use sensible defaults.

## Examples

### Basic embed

```tsx
import { CareersWidget } from "@rts-hr/careers";
import "@rts-hr/careers/style.css";

export default function CareersPage() {
  return (
    <div style={{ maxWidth: 1200, margin: "0 auto" }}>
      <h1>Join our team</h1>
      <CareersWidget tenant="acme" />
    </div>
  );
}
```

### Dark mode with custom brand color

```tsx
<CareersWidget
  tenant="acme"
  theme={{ primaryColor: "#7c3aed", mode: "dark" }}
/>
```

### English locale

```tsx
<CareersWidget tenant="acme" locale="en" />
```

### Deep-link to a specific job

```tsx
// Opens job #42 directly with back button to return to the list
<CareersWidget tenant="acme" initialJobId={42} />
```

### Sync navigation with your router

```tsx
import { useRouter } from "next/navigation";

function Careers() {
  const router = useRouter();

  return (
    <CareersWidget
      tenant="acme"
      onNavigate={(view, jobId) => {
        if (view === "detail" && jobId) {
          router.push(`/careers/${jobId}`, { scroll: false });
        } else {
          router.push("/careers", { scroll: false });
        }
      }}
    />
  );
}
```

### Track application submissions

```tsx
<CareersWidget
  tenant="acme"
  onApplicationSubmitted={(jobId, applicationId) => {
    analytics.track("application_submitted", { jobId, applicationId });
  }}
/>
```

### Fixed height with scrolling

```tsx
<CareersWidget tenant="acme" maxHeight={600} />
```

### Custom fetch function (e.g. adding auth headers)

If you already have a fetch wrapper that adds headers (like tenant ID or auth tokens), pass it via `fetchFn`. The widget will use it for all API calls instead of constructing its own.

```tsx
import { usePublicFetch } from "@/lib/auth";

function Careers() {
  const publicFetch = usePublicFetch();

  return (
    <CareersWidget
      fetchFn={publicFetch}
      disableTheme // skip widget's ThemeProvider when parent already provides one
    />
  );
}
```

### Local development

Point the widget at your local API:

```tsx
<CareersWidget
  tenant="local"
  apiBaseUrl="http://localhost:8000/api"
/>
```

## How it works

1. The widget calls `GET {apiBaseUrl}/public/jobs` to fetch active job listings and renders them as a searchable, filterable list with infinite scroll.
2. When a user clicks a job, it calls `GET {apiBaseUrl}/public/jobs/{id}` to fetch the full description and displays it with an application form.
3. On form submission, it sends a `POST {apiBaseUrl}/applications/jobs/{id}/apply` with the candidate's data and CV as `multipart/form-data`.

All endpoints are public (no authentication). CORS is configured to allow requests from any origin on these endpoints.

## Browser support

Works in all modern browsers (Chrome, Firefox, Safari, Edge). Requires React 18 or 19.

## TypeScript

The package ships with full TypeScript declarations. Import the props type if needed:

```tsx
import type { CareersWidgetProps } from "@rts-hr/careers";
```