# Course Rating Package PRD

## Overview

Course Rating Package adalah npm package untuk menampilkan rating course/pembelajaran setelah user menyelesaikan course. Rating bersifat wajib secara default: selama masih ada data course yang perlu diberi rating, modal akan muncul dan tidak bisa ditutup kecuali proses rating selesai.

Package ditargetkan untuk ReactJS dan React Native dengan API props yang sama, tetapi implementasi UI dipisahkan per platform agar tetap stabil dan maintainable.

## Goals

- Menampilkan popup rating setelah user menyelesaikan course.
- Mewajibkan user memberi rating selama ada data course aktif.
- Mendukung rating 1 sampai 5.
- Menampilkan pertanyaan feedback berdasarkan nilai rating.
- Selalu menyediakan opsi `Others`.
- Mewajibkan input tambahan ketika `Others` dipilih.
- Menampilkan success screen setelah submit berhasil.
- Mendukung multi-language `id` dan `en`.
- Mendukung theming dasar dan custom image assets.
- Menyediakan package yang bisa dipakai oleh ReactJS dan React Native.

## Non-Goals

- Package tidak melakukan request API langsung ke backend.
- Package tidak menentukan kapan course dianggap selesai.
- Package tidak menyimpan state rating secara persistent.
- Package tidak mengatur routing aplikasi host.
- Package tidak membuat dashboard/reporting hasil rating.

## Target Platforms

Package akan dipublikasikan sebagai satu npm package dengan entrypoint berbeda:

```ts
@imamasari1/course-rating/reactjs
@imamasari1/course-rating/reactnative
```

Shared logic digunakan untuk:

- Type definitions.
- Default labels.
- Rating validation.
- Feedback option normalization.
- Payload builder.
- Theme defaults.

UI rendering dipisahkan karena ReactJS dan React Native memakai primitive berbeda.

## User Flow

1. Backend atau host app menyediakan data course yang perlu diberi rating.
2. Host app mengirim `data` ke komponen package.
3. Selama `data` berisi minimal satu item, modal rating otomatis visible.
4. User memilih rating 1 sampai 5.
5. Package menampilkan pertanyaan feedback berdasarkan nilai rating.
6. User memilih minimal satu alasan feedback.
7. Jika user memilih `Others`, package menampilkan text input tambahan.
8. User wajib mengisi text input jika `Others` dipilih.
9. User menekan submit.
10. Package memanggil `onSubmitRating(payload)`.
11. Jika submit sukses, package menampilkan success screen.
12. Jika masih ada course berikutnya, user lanjut ke rating berikutnya.
13. Jika course terakhir selesai, modal otomatis hide dan package memanggil `onDone`.

## Visibility Rules

Modal visible jika:

```ts
data && data.length > 0
```

Default behavior:

- Modal tidak bisa ditutup.
- Tidak ada close button.
- Backdrop tidak bisa menutup modal.
- React Native Android hardware back sebaiknya diblok saat modal wajib aktif.

Optional close behavior:

```ts
enableClose?: boolean;
```

Jika `enableClose=true`:

- Close button `X` ditampilkan.
- `onClose` dipanggil ketika user menutup modal.
- Implementor bertanggung jawab menghapus atau mengubah `data` jika ingin modal tidak muncul kembali.

## Course Data

`data` berbentuk list agar package siap mendukung multi-course di masa depan, walaupun backend saat ini hanya mengirim satu item.

```ts
type CourseRatingData = {
  id: string;
  title: string;
  description?: string;
};
```

Field:

- `id`: identifier course, wajib dikirim dalam payload submit.
- `title`: nama course, ditampilkan pada course pill/button.
- `description`: deskripsi optional untuk kebutuhan tampilan atau payload.

Example:

```tsx
<CourseRating
  data={[
    {
      id: "course-001",
      title: "Refreshment AML, CFT & CPF",
      description: "Learning objective..."
    }
  ]}
  onSubmitRating={handleSubmitRating}
  onDone={handleRatingDone}
/>
```

## Rating Rules

Rating menggunakan skala 1 sampai 5.

Default threshold:

```ts
goodRatingThreshold?: number; // default 4
```

Rules:

- `rating >= goodRatingThreshold`: positive feedback.
- `rating < goodRatingThreshold`: improvement feedback.

Default:

- Rating 4 dan 5 dianggap baik.
- Rating 1, 2, dan 3 dianggap perlu perbaikan.

## Feedback Questions

Semua rating menampilkan pilihan alasan feedback yang sama. Pertanyaan berubah berdasarkan nilai rating.

Jika `rating >= 4`:

```txt
Apa saja yang membuatmu terkesan?
```

Jika `rating < 4`:

```txt
Apa saja yang harus kami perbaiki?
```

Jika `Others` dipilih, text input muncul dengan pertanyaan:

```txt
Apa yang bisa kami lakukan untuk meningkatkan pembelajaran ini?
```

## Feedback Options

Default options:

```ts
[
  "Akurasi & Relevansi Modul",
  "Durasi Materi",
  "Audio/Desain Visual",
  "Others"
]
```

Custom options:

```ts
feedbackOptions?: string[];
```

Rules:

- `Others` selalu wajib tersedia.
- Jika implementor tidak mengirim `Others`, package menambahkannya otomatis.
- User wajib memilih minimal satu option sebelum submit.
- Multi-select didukung.

## Validation

Submit aktif jika:

- Rating sudah dipilih.
- Minimal satu feedback option dipilih.
- Jika `Others` dipilih, `otherText` tidak kosong.

Submit disabled jika salah satu validasi belum terpenuhi.

Jika `onSubmitRating` gagal:

- Modal tetap berada pada form rating.
- Error state ditampilkan.
- User bisa mencoba submit ulang.

## Submit Payload

Package tidak hit backend secara langsung. Package hanya memanggil callback dari implementor.

```ts
type CourseRatingSubmitPayload = {
  courseId: string;
  courseTitle: string;
  courseDescription?: string;
  rating: number;
  sentiment: "positive" | "improvement";
  question: string;
  reasons: string[];
  otherText?: string;
  language: "id" | "en";
  index: number;
  total: number;
};
```

Callback:

```ts
onSubmitRating: (payload: CourseRatingSubmitPayload) => void | Promise<void>;
```

Behavior:

- Jika callback resolve, package menampilkan success screen.
- Jika callback reject atau throw error, package menampilkan error state.

## Success Screen

Success screen adalah bagian dari package.

Default ID:

```txt
Terima kasih telah meluangkan waktu untuk mengisi evaluasi ini
Masukan yang Anda berikan sangat berarti bagi kami.
Kembali ke home
Lanjut course
```

Default EN:

```txt
Thank you for taking the time to complete this evaluation
Your feedback means a lot to us.
Back to home
Next course
```

Button behavior:

- Jika masih ada next course, button label memakai `nextCourse`.
- Jika sudah course terakhir, button label memakai `backToHome`.
- Pada course terakhir, klik button akan hide modal otomatis.
- Setelah modal hide, package memanggil `onDone`.
- Jika `onBackToHome` tersedia, package juga memanggilnya pada course terakhir.

## Multi-Language

Language dipassing via prop:

```ts
language?: "id" | "en"; // default "id"
```

Package menyediakan default labels untuk `id` dan `en`.

Implementor bisa override label:

```ts
labels?: Partial<CourseRatingLabels>;
```

## Labels

```ts
type CourseRatingLabels = {
  title: string;
  description: string;
  impressiveQuestion: string;
  improvementQuestion: string;
  otherQuestion: string;
  submit: string;
  successTitle: string;
  successDescription: string;
  backToHome: string;
  nextCourse: string;
  others: string;
  loading: string;
  submitError: string;
};
```

## Assets and Responsive Images

Assets dapat dikirim dari host app.

```ts
type CourseRatingAssets = {
  logoImage?: unknown;
  mascotImage?: unknown;
  ratingBackgroundImage?: unknown;
  ratingBackgroundImageMobile?: unknown;
  ratingBackgroundImageDesktop?: unknown;
  successBackgroundImage?: unknown;
  successBackgroundImageMobile?: unknown;
  successBackgroundImageDesktop?: unknown;
};
```

Notes:

- Untuk ReactJS, image umumnya berupa URL string atau imported asset string.
- Untuk React Native, image umumnya berupa `ImageSourcePropType`.
- Type final dapat dibuat platform-specific pada masing-masing entrypoint.
- PNG assets didukung sebagai use case utama.
- Package harus tetap memiliki default styled background jika image tidak dikirim.
- Default styled background boleh dibuat dari CSS/View shape, gradient, dan wave sederhana, bukan wajib berupa PNG.
- Mobile dan desktop dapat memakai image berbeda karena rasio portrait dan landscape sangat berbeda.
- Jika image platform-specific tidak tersedia, package fallback ke generic image.
- Jika generic image tidak tersedia, package fallback ke default styled background.

Image priority untuk rating screen:

1. `ratingBackgroundImageMobile` atau `ratingBackgroundImageDesktop`.
2. `ratingBackgroundImage`.
3. Default styled rating background.

Image priority untuk success screen:

1. `successBackgroundImageMobile` atau `successBackgroundImageDesktop`.
2. `successBackgroundImage`.
3. Default styled success background.

Recommended image sizes:

```txt
ratingBackgroundImageMobile: 1080 x 1920
ratingBackgroundImageDesktop: 1440 x 900 atau 1920 x 1080
successBackgroundImageMobile: 1080 x 1920
successBackgroundImageDesktop: 1440 x 900 atau 1920 x 1080
mascotImage: PNG transparan minimal 512 x 512
logoImage: PNG atau SVG minimal tinggi 64px
```

## Theme

```ts
type CourseRatingTheme = {
  primaryColor?: string;
  starColor?: string;
  inactiveStarColor?: string;
  textColor?: string;
  mutedTextColor?: string;
  buttonTextColor?: string;
  courseButtonColor?: string;
  backgroundColor?: string;
  borderColor?: string;
  borderRadius?: number;
};
```

Default values:

```ts
{
  primaryColor: "#9B0008",
  starColor: "#FFD45A",
  inactiveStarColor: "#E5E5E5",
  textColor: "#222222",
  mutedTextColor: "#555555",
  buttonTextColor: "#FFFFFF",
  courseButtonColor: "#2E2E2E",
  backgroundColor: "#FFFFFF",
  borderColor: "#DDDDDD",
  borderRadius: 24
}
```

## Proposed Props

```ts
type CourseRatingProps = {
  data?: CourseRatingData[] | null;

  onSubmitRating: (
    payload: CourseRatingSubmitPayload
  ) => void | Promise<void>;

  onDone?: () => void;
  onBackToHome?: () => void;

  enableClose?: boolean;
  onClose?: () => void;

  language?: "id" | "en";
  labels?: Partial<CourseRatingLabels>;

  feedbackOptions?: string[];
  goodRatingThreshold?: number;

  assets?: CourseRatingAssets;
  theme?: CourseRatingTheme;
};
```

## UI Requirements

### Rating Form

- Mobile layout tampil seperti bottom sheet/full card dengan rounded top corners.
- Desktop layout tampil sebagai centered modal.
- Background host app diberi overlay gelap.
- Course title tampil sebagai pill/button gelap.
- Stars tampil horizontal dengan inactive dan active states.
- Feedback chips tampil setelah rating dipilih.
- Submit button disabled sampai form valid.
- Submit button active menggunakan `primaryColor`.

### Success Screen

- Success screen dominan memakai `primaryColor`.
- Menampilkan title, description, CTA button, dan optional mascot/background image.
- Pada item terakhir, CTA menutup modal dan memanggil completion callbacks.
- Pada multi-course, CTA lanjut ke item berikutnya.

## Accessibility Requirements

- Rating stars dapat diakses dengan keyboard pada ReactJS.
- Buttons dan chips memiliki accessible labels.
- Disabled submit harus jelas secara visual.
- Modal menggunakan dialog semantics pada ReactJS.
- Focus trap direkomendasikan pada ReactJS.
- React Native menggunakan accessibility role dan label yang sesuai.

## Package Architecture

Suggested structure:

```txt
src/
  shared/
    defaults.ts
    labels.ts
    types.ts
    validation.ts
    payload.ts
  reactjs/
    CourseRating.tsx
    CourseRatingModal.tsx
    CourseRatingForm.tsx
    CourseRatingSuccess.tsx
    styles.css
    index.ts
  reactnative/
    CourseRating.tsx
    CourseRatingModal.tsx
    CourseRatingForm.tsx
    CourseRatingSuccess.tsx
    index.ts
  index.ts
```

Package export names:

```json
{
  "./reactjs": "./dist/reactjs/index.js",
  "./reactnative": "./dist/reactnative/index.js",
  "./shared": "./dist/shared/index.js"
}
```

## Open Questions

- Final package name and npm scope.
- Whether default mascot assets will be bundled or always supplied by implementor.
- Whether close behavior should mark rating as skipped when `enableClose=true`.
- Whether analytics callbacks are needed, such as `onRatingStart`, `onRatingChange`, or `onSuccess`.
