> ## Documentation Index
> Fetch the complete documentation index at: https://docs.quinnsambal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Recruitment ats

<style>
  {`
    [class*="max-w-none"][class*="table"] {
      display: block !important;
      overflow-x: auto !important;
      max-width: 100% !important;
      width: 100% !important;
      flex-grow: 0 !important;
    }
    [class*="max-w-none"][class*="table"] > table {
      width: 100% !important;
      max-width: 100% !important;
      table-layout: fixed !important;
    }
    .mermaid {
      max-width: 100% !important;
      overflow-x: auto !important;
    }
    article svg[role="img"] {
      max-width: 100% !important;
      height: auto !important;
    }
    article img, .prose img {
      max-width: 100% !important;
      height: auto !important;
    }
    img[src*="LOGO"], img[src*="logo"] {
      max-width: 120px !important;
      max-height: 40px !important;
      width: auto !important;
      height: auto !important;
      object-fit: contain !important;
    }
    `}
</style>

***

title: "Recruitment ATS — Applicant Tracking System"
description: "Sistem pelacakan pelamar dengan job posting, kanban pipeline 6 tahap, dan React Query di SNISHOP ERP."
--------------------------------------------------------------------------------------------------------------------

# Recruitment ATS — Applicant Tracking System

<img src="https://mintcdn.com/quinnofspicy/e4f_upKhVWcjsUmM/docs/mintlify/screenshots/hr/recruitment-ats.png?fit=max&auto=format&n=e4f_upKhVWcjsUmM&q=85&s=dad54675da38e0df86d92dab4084faa6" alt="Recruitment ATS" width="1920" height="1080" data-path="docs/mintlify/screenshots/hr/recruitment-ats.png" />

Recruitment ATS mengelola seluruh proses rekrutmen dari pembuatan lowongan hingga penerimaan karyawan. Sistem menggunakan kanban board dengan 6 tahap pipeline untuk melacak progress setiap pelamar.

## Arsitektur

```mermaid theme={null}
flowchart TB
    subgraph TABS["2 Tab"]
        JOBS[Tab: Jobs<br/>Lowongan Pekerjaan]
        APP[Tab: Applicants<br/>Kanban Pipeline]
    end

    subgraph PIPELINE["Kanban Pipeline — 7 Kolom"]
        NEW[New<br/>Pelamar Baru]
        SCR[Screening<br/>Seleksi Berkas]
        INT_S[Interview<br/>Scheduled]
        INT_D[Interviewed<br/>Sudah Interview]
        OFF[Offer<br/>Extended]
        HIR[Hired<br/>Diterima]
        REJ[Rejected<br/>Ditolak]
    end

    subgraph ENTITIES["Entitas"]
        JP[JobPosting]
        AP[Applicant]
    end

    JOBS --> JP
    APP --> AP
    AP --> NEW --> SCR --> INT_S --> INT_D --> OFF --> HIR
    NEW --> REJ
    SCR --> REJ
    INT_S --> REJ
    INT_D --> REJ
    OFF --> REJ
```

## Akses Halaman

URL: `/recruitment-ats`

## Dua Tab Utama

| Tab | Label | Konten |
| - | - | - |
| `jobs` | Lowongan | Daftar job posting dengan create/edit |
| `applicants` | Pelamar | Kanban pipeline pelamar |

## Statistik

| Metrik | Perhitungan |
| - | - |
| **Active Jobs** | COUNT JobPosting `status = 'open'` |
| **Total Applicants** | COUNT semua Applicant |
| **In Process** | COUNT Applicant `status ≠ hired, rejected` |
| **Total Hired** | COUNT Applicant `status = 'hired'` |

## Entitas: JobPosting

| Field | Tipe | Deskripsi |
| - | - | - |
| `company_id` | string | ID perusahaan |
| `position_title` | string | Judul posisi |
| `department` | string | Departemen |
| `description` | string | Deskripsi pekerjaan |
| `requirements` | array | Daftar persyaratan |
| `salary_range_min` | number | Gaji minimum |
| `salary_range_max` | number | Gaji maksimum |
| `employment_type` | enum | Jenis pekerjaan |
| `job_location` | string | Lokasi kerja |
| `closing_date` | date | Tanggal penutupan |
| `status` | enum | Status lowongan |
| `posted_by` | string | ID pembuat |
| `posting_date` | date | Tanggal posting |

### Jenis Pekerjaan

| Tipe | Label |
| - | - |
| `full_time` | Full Time |
| `part_time` | Part Time |
| `contract` | Kontrak |
| `internship` | Magang |

### Status Lowongan

| Status | Label | Keterangan |
| - | - | - |
| `open` | Dibuka | Masih menerima lamaran |
| `closed` | Ditutup | Sudah ditutup |
| `on_hold` | Ditunda | Ditunda sementara |

## Entitas: Applicant

| Field | Tipe | Deskripsi |
| - | - | - |
| `company_id` | string | ID perusahaan |
| `job_id` | string | ID lowongan yang dilamar |
| `name` | string | Nama pelamar |
| `email` | string | Email pelamar |
| `phone` | string | Nomor telepon |
| `resume_url` | string | URL file CV/resume |
| `cover_letter` | string | Surat lamaran |
| `status` | enum | Status pipeline |
| `applied_date` | date | Tanggal melamar |
| `notes` | string | Catatan dari recruiter |

## Kanban Pipeline

### 7 Kolom Status

| Kolom | Status | Warna | Deskripsi |
| - | - | - | - |
| **New** | `new` | Biru | Pelamar baru masuk |
| **Screening** | `screening` | Cyan | Sedang diseleksi berkas |
| **Interview Scheduled** | `interview_scheduled` | Kuning | Jadwal interview sudah diatur |
| **Interviewed** | `interviewed` | Orange | Sudah diinterview |
| **Offer Extended** | `offer_extended` | Hijau Muda | Penawaran sudah diberikan |
| **Hired** | `hired` | Hijau | Diterima |
| **Rejected** | `rejected` | Merah | Ditolak |

### Alur Progresi

```mermaid theme={null}
stateDiagram-v2
    [*] --> new: Lamaran Masuk
    new --> screening: Lolos Seleksi Berkas
    screening --> interview_scheduled: Jadwal Interview
    interview_scheduled --> interviewed: Selesai Interview
    interviewed --> offer_extended: Offer Diberikan
    offer_extended --> hired: Offer Diterima
    
    new --> rejected: Tidak Lolos
    screening --> rejected: Tidak Lolos
    interview_scheduled --> rejected: Tidak Lolos
    interviewed --> rejected: Tidak Lolos
    offer_extended --> rejected: Offer Ditolak
    
    hired --> [*]
    rejected --> [*]
```

### Tombol "Maju"

Setiap pelamar memiliki tombol **"Maju"** yang memajukan status ke tahap berikutnya:

```
new → screening → interview_scheduled → interviewed → offer_extended → hired
```

### Applicant Detail Dialog

Dialog detail pelamar menampilkan:

| Info | Detail |
| - | - |
| Nama | Nama lengkap pelamar |
| Email | Alamat email |
| Telepon | Nomor telepon |
| Lowongan | Posisi yang dilamar |
| Tanggal Melamar | `applied_date` |
| Resume | Link download CV |
| Cover Letter | Isi surat lamaran |
| Catatan | Catatan dari recruiter |
| Status Buttons | Tombol untuk ubah status (termasuk reject) |

## React Query

ATS menggunakan **React Query** (`useQuery`, `useMutation`, `useQueryClient`) untuk manajemen data:

| Hook | Fungsi |
| - | - |
| `useQuery` | Fetch data job posting dan applicant |
| `useMutation` | Create/update/delete job dan applicant |
| `useQueryClient` | Invalidate cache setelah mutasi |

### Keuntungan React Query

| Fitur | Manfaat |
| - | - |
| **Automatic Caching** | Data di-cache, tidak re-fetch saat navigasi kembali |
| **Background Refetch** | Data di-refresh di background |
| **Optimistic Updates** | UI update instan sebelum server response |
| **Mutation Retry** | Retry otomatis jika mutasi gagal |

## Job Posting Card

Setiap lowongan ditampilkan dalam card dengan informasi:

| Field | Tampilan |
| - | - |
| Position Title | Judul besar di card |
| Department | Badge departemen |
| Employment Type | Badge tipe (full-time/part-time/contract) |
| Salary Range | Format Rupiah (min - max) |
| Location | Lokasi kerja |
| Closing Date | Tanggal penutupan |
| Status | Badge warna (open/closed/on\_hold) |
| Applicant Count | Jumlah pelamar |

## Integrasi dengan HR Module

```mermaid theme={null}
flowchart LR
    ATS[Recruitment ATS] -->|Hired Applicant| CM[CompanyMember]
    CM -->|New Employee| HR[HR Hub]
    CM -->|Self-Service| EP[Employee Portal]
    CM -->|Attendance| ATT[Attendance]
```

Saat pelamar berstatus `hired`, data bisa dikonversi menjadi `CompanyMember` (karyawan baru) melalui proses onboarding di EmployeeForm.

## Tips Penggunaan

* Buat job posting dengan deskripsi dan persyaratan yang jelas untuk menarik pelamar berkualitas
* Review pelamar baru secara berkala dan majukan yang memenuhi syarat ke tahap screening
* Jadwalkan interview segera setelah screening selesai
* Berikan catatan pada setiap pelamar untuk referensi di tahap selanjutnya
* Tutup lowongan yang sudah terisi agar tidak menerima lamaran baru
* Gunakan data statistik (active jobs, total hired) untuk evaluasi proses rekrutmen

***

## Entity Relationship Diagram

```mermaid theme={null}
erDiagram
    Company ||--o{ JobPosting : "memiliki lowongan"
    Company ||--o{ Applicant : "menerima pelamar"
    Company ||--o{ InterviewSchedule : "menjadwalkan interview"
    Company ||--o{ CompanyInvitation : "mengirim undangan"
    Company ||--o{ CompanyMember : "memiliki anggota"
    Company ||--o{ Employee : "mempekerjakan"

    JobPosting ||--o{ Applicant : "dilamar oleh"
    JobPosting {
        string company_id PK
        string position_title
        string department
        string description
        array requirements
        number salary_range_min
        number salary_range_max
        enum employment_type
        string job_location
        date posting_date
        date closing_date
        enum status
        string posted_by FK
    }

    Applicant {
        string company_id PK
        string job_posting_id FK
        string full_name
        string email
        string phone
        string resume_url
        string cover_letter
        datetime application_date
        enum current_status
        array education
        array experience
        array skills
        string assigned_to FK
        string notes
    }

    InterviewSchedule {
        string company_id PK
        string applicant_id FK
        enum interview_type
        datetime interview_date
        string interview_location
        array interviewer_ids FK
        string description
        enum status
        string interview_notes
        number rating
        array feedback
        enum recommendation
    }

    CompanyInvitation {
        string company_id PK
        string invited_email
        string invited_by FK
        string invited_by_name
        enum role
        string department
        string position
        string description
        object permissions
        string message
        enum status
        datetime expires_at
        datetime accepted_at
        datetime rejected_at
        datetime cancelled_at
    }

    CompanyMember {
        string company_id PK
        string user_id FK
        string user_email
        string user_name
        enum role
        string employee_id FK
        string department
        string position
        enum status
        date joined_date
        string invited_by FK
        object permissions
        array assigned_locations
        object working_hours
        number salary
        string notes
    }

    Employee {
        string company_id PK
        string user_id FK
        string employee_id
        string full_name
        string email
        string phone
        enum department
        string position
        string description
        enum employment_type
        date hire_date
        number salary
        object bank_account
        object emergency_contact
        string address
        date date_of_birth
        enum status
        string avatar_url
    }

    Applicant ||--o{ InterviewSchedule : "memiliki jadwal"
    Applicant }o--|| CompanyMember : "dikonversi saat hired"
    CompanyMember }o--o| Employee : "terhubung ke data karyawan"
```

## Tabel Schema — Entitas Rekrutmen

### JobPosting

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `company_id` | string | Ya | ID perusahaan pembuat lowongan |
| `position_title` | string | Ya | Judul posisi yang ditawarkan |
| `department` | string | Ya | Departemen penempatan |
| `description` | string | Ya | Deskripsi lengkap pekerjaan |
| `requirements` | array\[string] | Tidak | Daftar persyaratan kualifikasi |
| `salary_range_min` | number | Tidak | Batas bawah gaji |
| `salary_range_max` | number | Tidak | Batas atas gaji |
| `employment_type` | enum | Tidak | Jenis pekerjaan (default: `full_time`) |
| `job_location` | string | Tidak | Lokasi penempatan kerja |
| `posting_date` | date | Tidak | Tanggal lowongan diposting |
| `closing_date` | date | Ya | Tanggal penutupan lowongan |
| `status` | enum | Tidak | Status lowongan (default: `open`) |
| `posted_by` | string | Tidak | User ID HR yang membuat posting |

### Applicant

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `company_id` | string | Ya | ID perusahaan tujuan lamaran |
| `job_posting_id` | string | Ya | ID lowongan yang dilamar |
| `full_name` | string | Ya | Nama lengkap pelamar |
| `email` | string (email) | Ya | Alamat email pelamar |
| `phone` | string | Tidak | Nomor telepon pelamar |
| `resume_url` | string | Tidak | URL file CV/resume |
| `cover_letter` | string | Tidak | Surat lamaran |
| `application_date` | datetime | Tidak | Tanggal dan waktu melamar |
| `current_status` | enum | Tidak | Status pipeline (default: `new`) |
| `education` | array\[object] | Tidak | Riwayat pendidikan |
| `experience` | array\[object] | Tidak | Riwayat pengalaman kerja |
| `skills` | array\[string] | Tidak | Daftar keahlian |
| `assigned_to` | string | Tidak | HR/Hiring Manager yang menangani |
| `notes` | string | Tidak | Catatan dari recruiter |

### InterviewSchedule

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `company_id` | string | Ya | ID perusahaan |
| `applicant_id` | string | Ya | ID pelamar yang diinterview |
| `interview_type` | enum | Tidak | Tipe wawancara |
| `interview_date` | datetime | Ya | Tanggal dan waktu interview |
| `interview_location` | string | Tidak | Lokasi atau link wawancara |
| `interviewer_ids` | array\[string] | Tidak | User IDs interviewer |
| `description` | string (max 1000) | Tidak | Agenda dan topik wawancara |
| `status` | enum | Tidak | Status jadwal (default: `scheduled`) |
| `interview_notes` | string | Tidak | Catatan hasil wawancara |
| `rating` | number (1-5) | Tidak | Rating kandidat |
| `feedback` | array\[object] | Tidak | Feedback per interviewer |
| `recommendation` | enum | Tidak | Rekomendasi akhir (`pass`/`reject`/`undecided`) |

### CompanyInvitation

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `company_id` | string | Ya | ID perusahaan pengundang |
| `company_name` | string | Tidak | Nama perusahaan |
| `invited_email` | string | Ya | Email yang diundang |
| `invited_by` | string | Ya | Email pengundang |
| `invited_by_name` | string | Tidak | Nama pengundang |
| `role` | enum | Tidak | Role yang diberikan (default: `employee`) |
| `department` | string | Tidak | Departemen penempatan |
| `position` | string | Tidak | Jabatan |
| `description` | string (max 1000) | Tidak | Pesan undangan |
| `permissions` | object | Tidak | Hak akses detail |
| `message` | string | Tidak | Pesan tambahan |
| `status` | enum | Tidak | Status undangan (default: `pending`) |
| `expires_at` | datetime | Tidak | Waktu kedaluwarsa undangan |
| `accepted_at` | datetime | Tidak | Waktu undangan diterima |
| `rejected_at` | datetime | Tidak | Waktu undangan ditolak |
| `cancelled_at` | datetime | Tidak | Waktu undangan dibatalkan |

### CompanyMember

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `company_id` | string | Ya | ID perusahaan |
| `user_id` | string | Tidak | Link ke User entity |
| `user_email` | string | Ya | Email pengguna |
| `user_name` | string | Tidak | Nama pengguna |
| `role` | enum | Tidak | Role perusahaan (default: `employee`) |
| `employee_id` | string | Tidak | Link ke Employee entity |
| `department` | string | Tidak | Departemen |
| `position` | string | Tidak | Jabatan |
| `status` | enum | Tidak | Status keanggotaan (default: `active`) |
| `joined_date` | date | Tidak | Tanggal bergabung |
| `invited_by` | string | Tidak | Email yang mengundang |
| `permissions` | object | Tidak | Hak akses detail |
| `assigned_locations` | array\[string] | Tidak | Lokasi gudang/outlet yang diizinkan |
| `working_hours` | object | Tidak | Jam kerja (start, end) |
| `salary` | number | Tidak | Gaji karyawan |
| `notes` | string | Tidak | Catatan tambahan |

### Employee

| Field | Tipe | Required | Deskripsi |
| - | - | - | - |
| `company_id` | string | Ya | ID perusahaan |
| `user_id` | string | Tidak | Link ke User entity |
| `employee_id` | string | Ya | ID karyawan unik |
| `full_name` | string | Ya | Nama lengkap karyawan |
| `email` | string | Ya | Email karyawan |
| `phone` | string | Tidak | Nomor telepon |
| `department` | enum | Ya | Departemen |
| `position` | string | Ya | Jabatan |
| `description` | string (max 1000) | Tidak | Deskripsi/catatan karyawan |
| `employment_type` | enum | Tidak | Jenis pekerjaan (default: `full_time`) |
| `hire_date` | date | Ya | Tanggal diterima |
| `salary` | number | Tidak | Gaji bulanan |
| `bank_account` | object | Tidak | Info rekening bank |
| `emergency_contact` | object | Tidak | Kontak darurat |
| `address` | string | Tidak | Alamat |
| `date_of_birth` | date | Tidak | Tanggal lahir |
| `status` | enum | Tidak | Status karyawan (default: `active`) |
| `avatar_url` | string | Tidak | URL foto profil |

## State Machine — Application Lifecycle

```mermaid theme={null}
stateDiagram-v2
    [*] --> new: Pelamar Submit Lamaran
    new --> screening: Lolos Review Berkas
    new --> rejected: Tidak Memenuhi Syarat

    screening --> interview_scheduled: Lolos Screening
    screening --> rejected: Gagal Screening

    interview_scheduled --> interviewed: Interview Selesai
    interview_scheduled --> rejected: Tidak Hadir / Gagal

    interviewed --> offer_extended: Kandidat Layak
    interviewed --> rejected: Tidak Lolos Interview

    offer_extended --> hired: Offer Diterima Pelamar
    offer_extended --> rejected: Offer Ditolak

    hired --> CompanyMember: Konversi ke Karyawan
    hired --> CompanyInvitation: Kirim Undangan Bergabung

    rejected --> [*]: Proses Selesai
    hired --> [*]: Onboarding Dimulai

    state hired {
        [*] --> KonversiData
        KonversiData --> BuatEmployee
        BuatEmployee --> BuatCompanyMember
        BuatCompanyMember --> KirimInvitation
        KirimInvitation --> [*]
    }
```

### Transisi Status Pelamar

| Dari | Ke | Trigger | Deskripsi |
| - | - | - | - |
| — | `new` | Submit lamaran | Pelamar mendaftar ke lowongan |
| `new` | `screening` | Tombol "Maju" | recruiter meloloskan seleksi berkas |
| `new` | `rejected` | Tombol "Reject" | Berkas tidak memenuhi syarat |
| `screening` | `interview_scheduled` | Tombol "Maju" + buat InterviewSchedule | Jadwalkan interview |
| `screening` | `rejected` | Tombol "Reject" | Gagal tahap screening |
| `interview_scheduled` | `interviewed` | InterviewSchedule status → `completed` | Interview telah dilaksanakan |
| `interview_scheduled` | `rejected` | Tombol "Reject" | Tidak hadir atau gagal interview |
| `interviewed` | `offer_extended` | Tombol "Maju" | Kandidat layak mendapat penawaran |
| `interviewed` | `rejected` | Tombol "Reject" | Tidak lolos tahap interview |
| `offer_extended` | `hired` | Tombol "Maju" | Pelamar menerima penawaran |
| `offer_extended` | `rejected` | Tombol "Reject" | Pelamar menolak penawaran |
| `hired` | — | Konversi otomatis | Data dikonversi ke Employee + CompanyMember + CompanyInvitation |

## Sequence Diagrams

### 1. Submit Lamaran

```mermaid theme={null}
sequenceDiagram
    participant P as Pelamar
    participant UI as Frontend (React)
    participant RQ as React Query
    participant API as Base44 API
    participant DB as Database

    P->>UI: Isi form lamaran (nama, email, CV)
    UI->>RQ: useMutation → create Applicant
    RQ->>API: POST /applicant {company_id, job_posting_id, full_name, email, resume_url}
    API->>DB: INSERT Applicant (current_status = 'new')
    DB-->>API: Document created
    API-->>RQ: Applicant object
    RQ->>RQ: invalidateQueries(['applicant'])
    RQ-->>UI: Optimistic update — card muncul di kolom "New"
    UI-->>P: Tampilkan notifikasi "Lamaran berhasil dikirim"
```

### 2. Jadwal Interview

```mermaid theme={null}
sequenceDiagram
    participant HR as Recruiter / HR
    participant UI as Frontend (React)
    participant RQ as React Query
    participant API as Base44 API
    participant DB as Database

    HR->>UI: Klik "Maju" pada pelamar di kolom screening
    UI->>RQ: useMutation → update Applicant (current_status = 'interview_scheduled')
    RQ->>API: PATCH /applicant/{id}
    API->>DB: UPDATE Applicant SET current_status = 'interview_scheduled'
    DB-->>API: Updated
    API-->>RQ: Updated Applicant

    HR->>UI: Buat InterviewSchedule (tanggal, tipe, lokasi)
    UI->>RQ: useMutation → create InterviewSchedule
    RQ->>API: POST /interview_schedule {applicant_id, interview_date, interview_type, interviewer_ids}
    API->>DB: INSERT InterviewSchedule (status = 'scheduled')
    DB-->>API: Document created
    API-->>RQ: InterviewSchedule object
    RQ->>RQ: invalidateQueries(['interview_schedule', 'applicant'])
    RQ-->>UI: Card pindah ke kolom "Interview Scheduled"
    UI-->>HR: Tampilkan jadwal interview di calendar view
```

### 3. Offer Letter & Hiring

```mermaid theme={null}
sequenceDiagram
    participant HR as Recruiter / HR
    participant UI as Frontend (React)
    participant RQ as React Query
    participant API as Base44 API
    participant DB as Database

    HR->>UI: Klik "Maju" pada pelamar di kolom interviewed
    UI->>RQ: useMutation → update Applicant (current_status = 'offer_extended')
    RQ->>API: PATCH /applicant/{id}
    API->>DB: UPDATE Applicant SET current_status = 'offer_extended'
    DB-->>API: Updated
    API-->>RQ: Updated Applicant
    RQ-->>UI: Card pindah ke kolom "Offer Extended"

    Note over HR,DB: Setelah pelamar menerima offer...

    HR->>UI: Klik "Maju" → status = 'hired'
    UI->>RQ: useMutation → update Applicant (current_status = 'hired')
    RQ->>API: PATCH /applicant/{id}
    API->>DB: UPDATE Applicant SET current_status = 'hired'
    DB-->>API: Updated

    HR->>UI: Konversi ke Employee + CompanyMember
    UI->>RQ: useMutation → create Employee
    RQ->>API: POST /employee {company_id, full_name, email, department, position, hire_date}
    API->>DB: INSERT Employee
    DB-->>API: Employee created

    UI->>RQ: useMutation → create CompanyMember
    RQ->>API: POST /company_member {company_id, user_email, role, department, position}
    API->>DB: INSERT CompanyMember
    DB-->>API: CompanyMember created

    UI->>RQ: useMutation → create CompanyInvitation
    RQ->>API: POST /company_invitation {company_id, invited_email, role}
    API->>DB: INSERT CompanyInvitation (status = 'pending')
    DB-->>API: Invitation created

    RQ->>RQ: invalidateQueries(['employee', 'company_member', 'applicant'])
    RQ-->>UI: Update semua tab — pelamar hilang dari kanban, muncul di HR Hub
    UI-->>HR: Tampilkan "Pelamar berhasil dikonversi menjadi karyawan"
```

## Enum Tables

### application\_status (Applicant.current\_status)

| Nilai | Label | Warna Badge | Deskripsi |
| - | - | - | - |
| `new` | Pelamar Baru | Biru | Lamaran baru masuk, belum direview |
| `screening` | Screening | Cyan | Sedang dalam tahap seleksi berkas |
| `interview_scheduled` | Interview Scheduled | Kuning | Jadwal interview sudah ditentukan |
| `interviewed` | Interviewed | Orange | Interview sudah dilaksanakan |
| `offer_extended` | Offer Extended | Hijau Muda | Penawaran kerja sudah diberikan |
| `hired` | Hired | Hijau | Pelamar diterima sebagai karyawan |
| `rejected` | Rejected | Merah | Lamaran ditolak di tahap manapun |

### employment\_type (JobPosting.employment\_type)

| Nilai | Label | Deskripsi |
| - | - | - |
| `full_time` | Full Time | Pekerjaan penuh waktu |
| `part_time` | Part Time | Pekerjaan paruh waktu |
| `contract` | Kontrak | Pekerjaan berbasis kontrak jangka tertentu |
| `internship` | Magang | Program magang / internship |

### job\_status (JobPosting.status)

| Nilai | Label | Deskripsi |
| - | - | - |
| `open` | Dibuka | Lowongan aktif menerima lamaran |
| `closed` | Ditutup | Lowongan sudah ditutup, tidak menerima lamaran |
| `on_hold` | Ditunda | Lowongan ditunda sementara |

### interview\_type (InterviewSchedule.interview\_type)

| Nilai | Label | Deskripsi |
| - | - | - |
| `phone_screening` | Phone Screening | Wawancara telepon awal |
| `first_round` | First Round | Interview putaran pertama |
| `second_round` | Second Round | Interview putaran kedua |
| `final_round` | Final Round | Interview putaran akhir |
| `hr_round` | HR Round | Interview khusus dengan tim HR |

### interview\_status (InterviewSchedule.status)

| Nilai | Label | Deskripsi |
| - | - | - |
| `scheduled` | Terjadwal | Interview belum dilaksanakan |
| `completed` | Selesai | Interview sudah dilaksanakan |
| `cancelled` | Dibatalkan | Interview dibatalkan |

### interview\_recommendation (InterviewSchedule.recommendation)

| Nilai | Label | Deskripsi |
| - | - | - |
| `pass` | Lolos | Kandidat direkomendasikan untuk lanjut |
| `reject` | Tolak | Kandidat tidak direkomendasikan |
| `undecided` | Belum Diputuskan | Interviewer belum memberikan keputusan |

### invitation\_status (CompanyInvitation.status)

| Nilai | Label | Deskripsi |
| - | - | - |
| `pending` | Menunggu | Undangan belum direspon |
| `accepted` | Diterima | Undangan sudah diterima |
| `rejected` | Ditolak | Undangan ditolak oleh penerima |
| `expired` | Kedaluwarsa | Undangan sudah melewati batas waktu |
| `cancelled` | Dibatalkan | Undangan dibatalkan oleh pengirim |

### company\_role (CompanyInvitation.role / CompanyMember.role)

| Nilai | Label | Deskripsi |
| - | - | - |
| `owner` | Owner | Pemilik perusahaan |
| `admin` | Admin | Administrator perusahaan |
| `supervisor` | Supervisor | Penyelia divisi |
| `store_admin` | Store Admin | Admin toko/outlet |
| `stock_admin` | Stock Admin | Admin stok/gudang |
| `finance_admin` | Finance Admin | Admin keuangan |
| `hr_admin` | HR Admin | Admin HR / rekrutmen |
| `transaction_admin` | Transaction Admin | Admin transaksi |
| `employee` | Employee | Karyawan biasa |
| `production_operator` | Production Operator | Operator produksi |
| `qc_inspector` | QC Inspector | Inspektor quality control |
| `sales_marketing` | Sales & Marketing | Tim sales dan marketing |
| `partner_distributor` | Partner Distributor | Mitra distributor |

### employee\_status (Employee.status)

| Nilai | Label | Deskripsi |
| - | - | - |
| `active` | Aktif | Karyawan aktif bekerja |
| `on_leave` | Cuti | Karyawan sedang cuti |
| `terminated` | Nonaktif | Karyawan sudah tidak bekerja |

### member\_status (CompanyMember.status)

| Nilai | Label | Deskripsi |
| - | - | - |
| `active` | Aktif | Anggota aktif |
| `inactive` | Nonaktif | Anggota dinonaktifkan |
| `pending` | Menunggu | Undangan belum diterima |

## RBAC — Recruitment ATS

| Aksi | Owner | Admin | HR Admin | Supervisor | Employee |
| - | :-: | :-: | :-: | :-: | :-: |
| **Lihat semua JobPosting** | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Buat JobPosting** | ✅ | ✅ | ✅ | ❌ | ❌ |
| **Edit JobPosting** | ✅ | ✅ | ✅ | ❌ | ❌ |
| **Hapus JobPosting** | ✅ | ✅ | ❌ | ❌ | ❌ |
| **Ubah status JobPosting** | ✅ | ✅ | ✅ | ❌ | ❌ |
| **Lihat semua Applicant** | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Tambah Applicant** | ✅ | ✅ | ✅ | ❌ | ❌ |
| **Majukan status Applicant** | ✅ | ✅ | ✅ | ❌ | ❌ |
| **Reject Applicant** | ✅ | ✅ | ✅ | ❌ | ❌ |
| **Buat InterviewSchedule** | ✅ | ✅ | ✅ | ❌ | ❌ |
| **Isi feedback interview** | ✅ | ✅ | ✅ | ✅ | ❌ |
| **Konversi Applicant → Employee** | ✅ | ✅ | ✅ | ❌ | ❌ |
| **Kirim CompanyInvitation** | ✅ | ✅ | ✅ | ❌ | ❌ |
| **Lihat data Employee** | ✅ | ✅ | ✅ | ✅ | ✅ (diri sendiri) |
| **Edit data Employee** | ✅ | ✅ | ✅ | ❌ | ❌ |
| **Kelola permissions member** | ✅ | ✅ | ❌ | ❌ | ❌ |
| **Hapus Employee** | ✅ | ✅ | ❌ | ❌ | ❌ |

### Catatan RLS (Row-Level Security)

Semua entitas rekrutmen menggunakan RLS berbasis `company_id`:

| Kondisi | Akses |
| - | - |
| `data.company_id == user.data.active_company_id` | Akses penuh ke data dalam perusahaan aktif |
| `created_by_id == user.id` | Akses ke data yang dibuat sendiri |
| `user_condition.role == 'admin'` | Akses penuh ke semua data (system admin) |
| `data.invited_email == user.email` (CompanyInvitation) | Undangan yang ditujukan ke email user |
| `data.user_email == user.email` (CompanyMember) | Data keanggotaan milik sendiri |
| `data.user_id == user.id` (Employee) | Data karyawan milik sendiri |

***

## Referensi Schema Nested Object

Bagian ini mendokumentasikan struktur detail untuk field bertipe `object` dan `array[object]` yang merujuk pada sub-schema dari masing-masing entitas.

### Applicant.education — Sub-Schema

Setiap elemen dalam array `education` pada entitas Applicant memiliki struktur berikut:

| Field | Tipe | Deskripsi |
| - | - | - |
| `school_name` | string | Nama institusi pendidikan |
| `degree` | string | Gelar (misal: S1, D3, S2) |
| `field` | string | Bidang studi / jurusan |
| `graduation_year` | number | Tahun kelulusan |

Contoh data:

```json theme={null}
[
  {
    "school_name": "Universitas Indonesia",
    "degree": "S1",
    "field": "Teknik Informatika",
    "graduation_year": 2023
  }
]
```

### Applicant.experience — Sub-Schema

Setiap elemen dalam array `experience` pada entitas Applicant memiliki struktur berikut:

| Field | Tipe | Deskripsi |
| - | - | - |
| `company_name` | string | Nama perusahaan tempat bekerja |
| `position` | string | Jabatan di perusahaan tersebut |
| `duration_years` | number | Lama bekerja dalam tahun |
| `description` | string | Deskripsi tanggung jawab dan pencapaian |

Contoh data:

```json theme={null}
[
  {
    "company_name": "PT Maju Bersama",
    "position": "Frontend Developer",
    "duration_years": 2,
    "description": "Mengembangkan dashboard internal menggunakan React dan TypeScript"
  }
]
```

### InterviewSchedule.feedback — Sub-Schema

Setiap elemen dalam array `feedback` pada entitas InterviewSchedule memiliki struktur berikut:

| Field | Tipe | Deskripsi |
| - | - | - |
| `interviewer_id` | string | User ID interviewer yang memberikan feedback |
| `feedback_text` | string | Catatan atau komentar dari interviewer |
| `rating` | number | Rating individu dari interviewer (1-5) |

Contoh data:

```json theme={null}
[
  {
    "interviewer_id": "user_abc123",
    "feedback_text": "Kandidat memiliki kemampuan komunikasi yang baik dan penguasaan teknis memadai",
    "rating": 4
  },
  {
    "interviewer_id": "user_def456",
    "feedback_text": "Perlu pendalaman di bidang system design",
    "rating": 3
  }
]
```

### Employee.bank\_account — Sub-Schema

Field `bank_account` pada entitas Employee memiliki struktur berikut:

| Field | Tipe | Deskripsi |
| - | - | - |
| `bank_name` | string | Nama bank (misal: BCA, Mandiri, BNI) |
| `account_number` | string | Nomor rekening bank |
| `account_name` | string | Nama pemilik rekening |

Contoh data:

```json theme={null}
{
  "bank_name": "BCA",
  "account_number": "1234567890",
  "account_name": "Budi Santoso"
}
```

### Employee.emergency\_contact — Sub-Schema

Field `emergency_contact` pada entitas Employee memiliki struktur berikut:

| Field | Tipe | Deskripsi |
| - | - | - |
| `name` | string | Nama kontak darurat |
| `relationship` | string | Hubungan dengan karyawan (misal: istri, suami, orang tua) |
| `phone` | string | Nomor telepon kontak darurat |

Contoh data:

```json theme={null}
{
  "name": "Siti Rahayu",
  "relationship": "Istri",
  "phone": "081234567890"
}
```

### CompanyMember.working\_hours — Sub-Schema

Field `working_hours` pada entitas CompanyMember memiliki struktur berikut:

| Field | Tipe | Default | Deskripsi |
| - | - | - | - |
| `start` | string (HH:mm) | `09:00` | Jam mulai kerja |
| `end` | string (HH:mm) | `17:00` | Jam selesai kerja |

Contoh data:

```json theme={null}
{
  "start": "08:00",
  "end": "17:00"
}
```

### CompanyInvitation.permissions / CompanyMember.permissions — Sub-Schema Lengkap

Field `permissions` pada CompanyInvitation dan CompanyMember berisi objek dengan boolean flags yang menentukan hak akses member. Berikut daftar lengkap seluruh permission:

#### Hak Akses Umum (Default Aktif untuk Semua Member)

| Permission | Tipe | Default | Deskripsi |
| - | - | - | - |
| `can_view_dashboard` | boolean | `true` | Melihat dashboard utama |
| `can_view_tasks` | boolean | `true` | Melihat daftar tugas |
| `can_create_tasks` | boolean | `true` | Membuat tugas baru |
| `can_edit_tasks` | boolean | `true` | Mengedit tugas |
| `can_view_notes` | boolean | `true` | Melihat catatan |
| `can_create_notes` | boolean | `true` | Membuat catatan baru |
| `can_edit_notes` | boolean | `true` | Mengedit catatan |

#### Hak Akses Umum (Default Nonaktif)

| Permission | Tipe | Default | Deskripsi |
| - | - | - | - |
| `can_delete_tasks` | boolean | `false` | Menghapus tugas |
| `can_delete_notes` | boolean | `false` | Menghapus catatan |

#### Hak Akses HR & Keuangan

| Permission | Tipe | Default | Deskripsi |
| - | - | - | - |
| `can_view_hr` | boolean | `false` | Melihat data HR (karyawan, rekrutmen) |
| `can_edit_hr` | boolean | `false` | Mengedit data HR |
| `can_view_finance` | boolean | `false` | Melihat laporan keuangan |
| `can_edit_finance` | boolean | `false` | Mengedit data keuangan |

#### Hak Akses Inventori

| Permission | Tipe | Default | Deskripsi |
| - | - | - | - |
| `can_view_inventory` | boolean | `false` | Melihat data inventori / stok |
| `can_edit_inventory` | boolean | `false` | Mengedit data inventori |
| `can_transfer_inventory` | boolean | `false` | Melakukan transfer antar lokasi |
| `can_approve_stock_opname` | boolean | `false` | Menyetujui hasil stock opname |
| `can_count_stock_opname` | boolean | `false` | Menghitung stock opname |

#### Hak Akses Proyek

| Permission | Tipe | Default | Deskripsi |
| - | - | - | - |
| `can_view_projects` | boolean | `false` | Melihat daftar proyek |
| `can_edit_projects` | boolean | `false` | Mengedit data proyek |

#### Hak Akses POS (Point of Sale)

| Permission | Tipe | Default | Deskripsi |
| - | - | - | - |
| `can_view_pos` | boolean | `false` | Melihat tampilan POS |
| `can_use_pos` | boolean | `false` | Menggunakan POS untuk transaksi |
| `can_manage_cashier_shift` | boolean | `false` | Mengelola shift kasir |

#### Hak Akses Laporan & Manajemen

| Permission | Tipe | Default | Deskripsi |
| - | - | - | - |
| `can_view_reports` | boolean | `false` | Melihat laporan |
| `can_manage_members` | boolean | `false` | Mengelola anggota tim |
| `can_manage_roles` | boolean | `false` | Mengelola role dan hak akses |
| `can_view_settings` | boolean | `false` | Melihat pengaturan sistem |
| `can_edit_settings` | boolean | `false` | Mengedit pengaturan sistem |

#### Hak Akses Produksi

| Permission | Tipe | Default | Deskripsi |
| - | - | - | - |
| `can_create_production_batch` | boolean | `false` | Membuat batch produksi |
| `can_release_production_qc` | boolean | `false` | Melepaskan hasil QC produksi |

#### Hak Akses HPP & Distribusi

| Permission | Tipe | Default | Deskripsi |
| - | - | - | - |
| `can_view_hpp` | boolean | `false` | Melihat perhitungan HPP (Harga Pokok Produksi) |
| `can_manage_channel_pricing` | boolean | `false` | Mengelola harga per channel distribusi |
| `can_view_distribution` | boolean | `false` | Melihat data distribusi |
| `can_create_distribution_shipment` | boolean | `false` | Membuat pengiriman distribusi |
| `can_confirm_distribution_shipment` | boolean | `false` | Mengonfirmasi pengiriman distribusi |

#### Hak Akses B2B

| Permission | Tipe | Default | Deskripsi |
| - | - | - | - |
| `can_view_b2b_invoices` | boolean | `false` | Melihat invoice B2B |
| `can_create_b2b_invoice` | boolean | `false` | Membuat invoice B2B |
| `can_verify_b2b_payment` | boolean | `false` | Verifikasi pembayaran B2B |

***

## Enum Tambahan — Employee.department

Field `department` pada entitas Employee menggunakan enum yang berbeda dari field `department` pada JobPosting atau CompanyMember (yang bertipe string bebas). Berikut daftar nilai enum untuk Employee.department:

| Nilai | Deskripsi |
| - | - |
| `Management` | Departemen manajemen / pimpinan |
| `Sales` | Departemen penjualan |
| `Marketing` | Departemen pemasaran |
| `Operations` | Departemen operasional |
| `Finance` | Departemen keuangan |
| `IT` | Departemen teknologi informasi |
| `HR` | Departemen sumber daya manusia |
| `Customer Service` | Departemen layanan pelanggan |

### Perbedaan Field department antar Entitas

| Entitas | Tipe | Format | Deskripsi |
| - | - | - | - |
| `JobPosting.department` | string | Teks bebas | Departemen diisi manual saat buat lowongan |
| `CompanyMember.department` | string | Teks bebas | Departemen diisi manual saat invite member |
| `Employee.department` | enum | Nilai terbatas | Dipilih dari daftar enum tetap (8 nilai) |

***

## Perbedaan employment\_type antar Entitas

Terdapat perbedaan kecil nilai enum `employment_type` antara JobPosting dan Employee:

| Nilai | JobPosting | Employee | Deskripsi |
| - | :-: | :-: | - |
| `full_time` | Ya | Ya | Pekerjaan penuh waktu |
| `part_time` | Ya | Ya | Pekerjaan paruh waktu |
| `contract` | Ya | Ya | Pekerjaan berbasis kontrak |
| `internship` | Ya | Tidak | Program magang (khusus JobPosting) |
| `intern` | Tidak | Ya | Magang (khusus Employee, penamaan berbeda) |

> **Catatan:** JobPosting menggunakan `internship` sedangkan Employee menggunakan `intern` untuk konsep yang sama. Perbedaan penamaan ini perlu diperhatikan saat konversi data dari Applicant yang di-hired menjadi Employee.

***

## Kebijakan RLS (Row-Level Security) — Detail Lengkap

Berikut adalah aturan RLS lengkap untuk setiap operasi CRUD pada masing-masing entitas rekrutmen, diekstrak langsung dari definisi JSONC.

### JobPosting — RLS Rules

| Operasi | Kondisi | Keterangan |
| - | - | - |
| **Create** | `data.company_id == user.data.active_company_id` DAN `data.company_id != null/""` | HR harus berada di perusahaan aktif |
| **Create** | `created_by_id == user.id` | Pengguna yang membuat data |
| **Create** | `user_condition.role == 'admin'` | System admin |
| **Read** | Sama dengan Create | Akses baca mengikuti aturan yang sama |
| **Update** | Sama dengan Create | Akses ubah mengikuti aturan yang sama |
| **Delete** | Sama dengan Create | Akses hapus mengikuti aturan yang sama |

### Applicant — RLS Rules

| Operasi | Kondisi | Keterangan |
| - | - | - |
| **Create** | `data.company_id == user.data.active_company_id` DAN `data.company_id != null/""` | HR di perusahaan aktif |
| **Create** | `created_by_id == user.id` | Pembuat data |
| **Create** | `user_condition.role == 'admin'` | System admin |
| **Read** | Sama dengan Create | Akses baca mengikuti aturan yang sama |
| **Update** | Sama dengan Create | Akses ubah mengikuti aturan yang sama |
| **Delete** | Sama dengan Create | Akses hapus mengikuti aturan yang sama |

### InterviewSchedule — RLS Rules

| Operasi | Kondisi | Keterangan |
| - | - | - |
| **Create** | `data.company_id == user.data.active_company_id` DAN `data.company_id != null/""` | HR di perusahaan aktif |
| **Create** | `created_by_id == user.id` | Pembuat data |
| **Create** | `user_condition.role == 'admin'` | System admin |
| **Read/Update/Delete** | Sama dengan Create | Semua operasi mengikuti aturan yang sama |

### CompanyInvitation — RLS Rules

| Operasi | Kondisi | Keterangan |
| - | - | - |
| **Create** | `data.company_id == user.data.active_company_id` DAN `data.company_id != null/""` | Pengundang di perusahaan aktif |
| **Create** | `created_by_id == user.id` | Pembuat undangan |
| **Create** | `data.invited_email == user.email` | Penerima undangan bisa membuat untuk dirinya sendiri |
| **Create** | `user_condition.role == 'admin'` | System admin |
| **Read/Update/Delete** | Sama dengan Create | Termasuk akses untuk penerima undangan (`invited_email`) |

### CompanyMember — RLS Rules

| Operasi | Kondisi | Keterangan |
| - | - | - |
| **Create** | `data.company_id == user.data.active_company_id` DAN `data.company_id != null/""` | Admin di perusahaan aktif |
| **Create** | `created_by_id == user.id` | Pembuat data |
| **Create** | `data.user_email == user.email` | Member bisa melihat data dirinya sendiri |
| **Create** | `data.user_id == user.id` | Member berdasarkan user\_id |
| **Create** | `user_condition.role == 'admin'` | System admin |
| **Read/Update/Delete** | Sama dengan Create | Semua operasi mengikuti aturan yang sama |

### Employee — RLS Rules

| Operasi | Kondisi | Keterangan |
| - | - | - |
| **Create** | `data.company_id == user.data.active_company_id` DAN `data.company_id != null/""` | HR di perusahaan aktif |
| **Create** | `created_by_id == user.id` | Pembuat data |
| **Create** | `data.user_id == user.id` | Karyawan bisa mengakses data dirinya sendiri |
| **Create** | `user_condition.role == 'admin'` | System admin |
| **Read/Update/Delete** | Sama dengan Create | Semua operasi mengikuti aturan yang sama |

### Ringkasan Pola RLS

```
Semua entitas mengikuti pola yang konsisten:

1. company_id match     → Akses penuh dalam scope perusahaan aktif
2. created_by_id match  → Akses ke data yang dibuat sendiri
3. user_id/email match  → Akses ke data milik sendiri (self-service)
4. role == 'admin'      → Akses penuh tanpa batasan (system override)
```

***

## Diagram Alur Konversi Pelamar ke Karyawan

```mermaid theme={null}
flowchart TD
    A[Applicant status = hired] --> B[Buat Employee baru]
    B --> B1[Salin: full_name, email, phone]
    B --> B2[Set: department, position dari JobPosting]
    B --> B3[Set: hire_date = hari ini]
    B --> B4[Set: employment_type = full_time]
    B --> B5[Set: status = active]
    B1 & B2 & B3 & B4 & B5 --> C[Buat CompanyMember baru]
    C --> C1[Salin: full_name → user_name, email → user_email]
    C --> C2[Set: role sesuai kebutuhan]
    C --> C3[Set: department, position]
    C --> C4[Set: status = active]
    C --> C5[Set: joined_date = hari ini]
    C --> C6[Link: employee_id → Employee yang baru dibuat]
    C1 & C2 & C3 & C4 & C5 & C6 --> D[Kirim CompanyInvitation]
    D --> D1[Set: invited_email = email karyawan]
    D --> D2[Set: role sama dengan CompanyMember]
    D --> D3[Set: status = pending]
    D --> D4[Set: permissions sesuai role]
    D1 & D2 & D3 & D4 --> E[Selesai — Karyawan baru siap onboarding]
```

***

## Validasi dan Constraints

### Field Required per Entitas

| Entitas | Field Required | Jumlah |
| - | - | - |
| **JobPosting** | `company_id`, `position_title`, `department`, `description`, `closing_date` | 5 |
| **Applicant** | `company_id`, `job_posting_id`, `full_name`, `email` | 4 |
| **InterviewSchedule** | `company_id`, `applicant_id`, `interview_date` | 3 |
| **CompanyInvitation** | `company_id`, `invited_email`, `invited_by` | 3 |
| **CompanyMember** | `company_id`, `user_email` | 2 |
| **Employee** | `company_id`, `employee_id`, `full_name`, `email`, `department`, `position`, `hire_date` | 7 |

### Format Field Khusus

| Entitas | Field | Format | Keterangan |
| - | - | - | - |
| Applicant | `email` | email | Validasi format email |
| Applicant | `application_date` | date-time | ISO 8601 datetime |
| InterviewSchedule | `interview_date` | date-time | ISO 8601 datetime |
| InterviewSchedule | `description` | maxLength: 1000 | Maksimal 1000 karakter |
| InterviewSchedule | `rating` | number (1-5) | Skala rating 1 sampai 5 |
| CompanyInvitation | `description` | maxLength: 1000 | Maksimal 1000 karakter |
| CompanyInvitation | `expires_at` | date-time | ISO 8601 datetime |
| CompanyInvitation | `accepted_at` | date-time | ISO 8601 datetime |
| CompanyInvitation | `rejected_at` | date-time | ISO 8601 datetime |
| CompanyInvitation | `cancelled_at` | date-time | ISO 8601 datetime |
| CompanyMember | `joined_date` | date | Format tanggal |
| Employee | `hire_date` | date | Format tanggal |
| Employee | `date_of_birth` | date | Format tanggal |
| Employee | `description` | maxLength: 1000 | Maksimal 1000 karakter |
| JobPosting | `posting_date` | date | Format tanggal |
| JobPosting | `closing_date` | date | Format tanggal |

***

## Default Values per Entitas

| Entitas | Field | Default | Deskripsi |
| - | - | - | - |
| JobPosting | `employment_type` | `full_time` | Jenis pekerjaan default |
| JobPosting | `status` | `open` | Status lowongan saat pertama dibuat |
| Applicant | `current_status` | `new` | Status pipeline pelamar baru |
| InterviewSchedule | `status` | `scheduled` | Status jadwal interview baru |
| CompanyInvitation | `role` | `employee` | Role default untuk undangan baru |
| CompanyInvitation | `status` | `pending` | Status undangan baru |
| CompanyMember | `role` | `employee` | Role default untuk member baru |
| CompanyMember | `status` | `active` | Status default member |
| CompanyMember.working\_hours | `start` | `09:00` | Jam mulai kerja default |
| CompanyMember.working\_hours | `end` | `17:00` | Jam selesai kerja default |
| Employee | `employment_type` | `full_time` | Jenis pekerjaan default |
| Employee | `status` | `active` | Status karyawan default |

***

## Mapping Entitas Sumber (JSONC)

Dokumentasi ini disusun berdasarkan file definisi entitas berikut:

| File JSONC | Entitas | Lokasi |
| - | - | - |
| `JobPosting.jsonc` | JobPosting | `base44/entities/` |
| `Applicant.jsonc` | Applicant | `base44/entities/` |
| `InterviewSchedule.jsonc` | InterviewSchedule | `base44/entities/` |
| `CompanyInvitation.jsonc` | CompanyInvitation | `base44/entities/` |
| `CompanyMember.jsonc` | CompanyMember | `base44/entities/` |
| `Employee.jsonc` | Employee | `base44/entities/` |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.