# KC Systems — Full System Plan

> Stack: Next.js (App Router) + TypeScript + MySQL (Prisma)
> Payments: Stripe | Mail: Mailgun | Auth: NextAuth.js
> Domain: https://kc.inleads-it.com.my
> Logo: https://kcs.inleadsit.com/wp-content/uploads/2026/05/KC-System_dark-e1779440763671.png

---

## 1. Site Architecture & Page Routes

### Public Routes
| Route | Page |
|---|---|
| `/` | Home |
| `/about` | About |
| `/shop` | Shop — product catalog |
| `/shop/[slug]` | Product detail |
| `/cart` | Cart |
| `/shop/checkout` | Shop checkout (Stripe) |
| `/journal` | Journal listing |
| `/journal/[slug]` | Journal post view |
| `/scholarships` | Scholarships listing |
| `/scholarships/[id]` | Scholarship detail |
| `/jobs` | Job listings (from API) |
| `/jobs/[id]` | Job detail |
| `/login` | Login |
| `/register` | Register |

### Authenticated Routes (Subscriber+)
| Route | Page |
|---|---|
| `/journal/new` | Write a new journal post |
| `/journal/edit/[slug]` | Edit own journal post |
| `/jobs/[id]/apply` | Apply for a job |
| `/dashboard` | Subscriber dashboard |

### Admin Routes
| Route | Page |
|---|---|
| `/admin` | Admin overview |
| `/admin/products` | Manage products |
| `/admin/journals` | Manage & moderate journals |
| `/admin/scholarships` | Manage scholarships |
| `/admin/jobs` | Manage jobs / API sync |
| `/admin/subscriptions` | Manage subscribers & payments |
| `/admin/settings` | Site settings |

### API Routes (Next.js Route Handlers)
```
/api/auth/[...nextauth]     — NextAuth
/api/products               — CRUD products
/api/journals               — CRUD journals
/api/scholarships           — CRUD scholarships
/api/jobs                   — Sync + list jobs
/api/jobs/apply             — Submit job application
/api/cart                   — Cart operations
/api/orders                 — Order management
/api/subscriptions          — Subscription management
/api/webhooks/stripe        — Stripe webhook handler
```

---

## 2. Database Schema

### `users`
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| name | VARCHAR(255) | |
| email | VARCHAR(255) UNIQUE | |
| password_hash | VARCHAR(255) | bcrypt |
| role | ENUM('guest','subscriber','job_seeker','admin') | default: subscriber |
| email_verified_at | TIMESTAMP NULL | |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |

### `subscriptions`
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| user_id | INT FK → users | |
| stripe_customer_id | VARCHAR(255) | |
| stripe_subscription_id | VARCHAR(255) | |
| plan | ENUM('basic','job_seeker') | |
| status | ENUM('active','cancelled','past_due','expired') | |
| current_period_start | TIMESTAMP | |
| current_period_end | TIMESTAMP | |
| created_at | TIMESTAMP | |

### `products`
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| name | VARCHAR(255) | |
| slug | VARCHAR(255) UNIQUE | |
| description | TEXT | |
| price | DECIMAL(10,2) | |
| stock | INT | |
| images | JSON | array of image URLs |
| category | VARCHAR(100) | |
| is_active | TINYINT(1) | default 1 |
| created_at | TIMESTAMP | |

### `orders`
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| user_id | INT FK → users | nullable (guest checkout) |
| email | VARCHAR(255) | for guest orders |
| total | DECIMAL(10,2) | |
| status | ENUM('pending','paid','shipped','completed','refunded') | |
| stripe_payment_intent_id | VARCHAR(255) | |
| shipping_address | JSON | |
| created_at | TIMESTAMP | |

### `order_items`
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| order_id | INT FK → orders | |
| product_id | INT FK → products | |
| quantity | INT | |
| unit_price | DECIMAL(10,2) | price at time of order |

### `journals`
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| user_id | INT FK → users | author |
| title | VARCHAR(255) | |
| slug | VARCHAR(255) UNIQUE | |
| content | LONGTEXT | rich text / markdown |
| status | ENUM('draft','published','flagged') | |
| published_at | TIMESTAMP NULL | |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |

### `scholarships`
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| title | VARCHAR(255) | |
| description | TEXT | |
| provider | VARCHAR(255) | |
| amount | VARCHAR(100) | e.g. "Full Tuition" or "USD 5,000" |
| country | VARCHAR(100) | |
| deadline | DATE NULL | |
| link | VARCHAR(500) | |
| source | ENUM('manual','api') | default: manual |
| is_active | TINYINT(1) | default 1 |
| created_at | TIMESTAMP | |

### `jobs`
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| external_id | VARCHAR(255) | API job ID |
| title | VARCHAR(255) | |
| company | VARCHAR(255) | |
| location | VARCHAR(255) | |
| description | LONGTEXT | |
| type | ENUM('full_time','part_time','contract','remote') | |
| salary | VARCHAR(100) | nullable |
| apply_url | VARCHAR(500) | original apply link |
| source_api | VARCHAR(100) | e.g. "adzuna", "jooble" |
| expires_at | TIMESTAMP NULL | |
| created_at | TIMESTAMP | |

### `job_seekers` (profile, one per user)
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| user_id | INT FK → users UNIQUE | |
| full_name | VARCHAR(255) | |
| phone | VARCHAR(50) | |
| skills | TEXT | comma-separated or JSON |
| experience_years | INT | |
| education | VARCHAR(255) | |
| resume_path | VARCHAR(500) | stored file path |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |

### `job_applications`
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| user_id | INT FK → users | |
| job_id | INT FK → jobs | |
| cover_letter | TEXT NULL | |
| resume_path | VARCHAR(500) | snapshot at time of apply |
| status | ENUM('submitted','viewed','shortlisted','rejected') | |
| created_at | TIMESTAMP | |

---

## 3. API Integrations

### Jobs API
- **Primary option**: [Adzuna API](https://developer.adzuna.com/) — free tier, global job listings
- **Backup option**: JSearch via RapidAPI — extensive job data
- **Sync strategy**: Cron job runs every 6 hours via `/api/jobs/sync`, upserts into `jobs` table by `external_id`
- **Location filter**: Malaysia + Remote

### Scholarships
- **Strategy**: Manual entry via admin dashboard (no reliable free API)
- **Enhancement**: Admin can also bulk-import from CSV
- **Future**: Integrate with [Scholarship Portal API](https://www.scholarshipportal.com/) if access is granted

---

## 4. Subscription Tiers & Pricing

| Plan | Price | Module | Access |
|---|---|---|---|
| **Free / Guest** | RM 0 | All | Browse Home, About, Shop catalog, Tours listing, Jobs listing, Scholarships listing |
| **Registered User** | RM 0 | Shop & Tours | Normal sign-up → can buy products + make tour enquiries / bookings |
| **Journal Subscriber** | RM 49/month | Journals | All above + read & write journals, full scholarship details |
| **Job Seeker** | RM 49/month | Jobs | All above + apply for jobs, build resume, manage seeker profile |

> Shop and Tours do NOT require a paid subscription — a free registered account is sufficient.
> Journal and Jobs access are each gated behind a RM49/mo subscription plan.
> Users can hold both plans simultaneously.

- Stripe Products + Prices created for Journal Subscriber + Job Seeker plans
- Subscription managed via Stripe Billing (webhooks update local DB)
- Annual billing option: ~20% discount (RM 470/yr per plan)
- Super admin can create, edit, enable/disable subscription packages from the admin panel (Package Management)

---

## 5. User Roles & Permissions

| Feature | Guest | Registered | Journal Sub | Job Seeker | Admin |
|---|---|---|---|---|---|
| Browse shop | ✅ | ✅ | ✅ | ✅ | ✅ |
| Buy products | ❌ | ✅ | ✅ | ✅ | ✅ |
| Browse tours | ✅ | ✅ | ✅ | ✅ | ✅ |
| Book / enquire tour | ❌ | ✅ | ✅ | ✅ | ✅ |
| View job listings | ✅ | ✅ | ✅ | ✅ | ✅ |
| View scholarship listings | ✅ | ✅ | ✅ | ✅ | ✅ |
| Read journals | ❌ | ❌ | ✅ | ✅ | ✅ |
| Write journals | ❌ | ❌ | ✅ | ✅ | ✅ |
| View scholarship details | ❌ | ❌ | ✅ | ✅ | ✅ |
| Apply for jobs | ❌ | ❌ | ❌ | ✅ | ✅ |
| Upload / build resume | ❌ | ❌ | ❌ | ✅ | ✅ |
| Admin dashboard | ❌ | ❌ | ❌ | ❌ | ✅ |

---

## 6. Mail Flows (Mailgun)

| Trigger | Recipient | Template |
|---|---|---|
| User registers | User | Welcome email + verify email link |
| Email verified | User | Account confirmed |
| Subscription activated | User | Subscription welcome + plan details |
| Payment successful | User | Receipt + invoice |
| Payment failed | User | Payment failed — update card |
| Subscription cancelled | User | Cancellation confirmation |
| Subscription expiring soon (3 days) | User | Renewal reminder |
| Job application submitted | User | Application confirmation |
| Journal post published | User | Your post is live |
| Password reset | User | Reset link (expires 1 hour) |
| New subscriber | Admin | New subscriber notification |

---

## 7. Next.js Project Folder Structure

```
kc-system/
├── app/
│   ├── layout.tsx                    ← Root layout (navbar, footer)
│   ├── page.tsx                      ← Home
│   ├── about/
│   │   └── page.tsx
│   ├── shop/
│   │   ├── page.tsx                  ← Catalog
│   │   ├── [slug]/
│   │   │   └── page.tsx              ← Product detail
│   │   └── checkout/
│   │       └── page.tsx
│   ├── cart/
│   │   └── page.tsx
│   ├── journal/
│   │   ├── page.tsx                  ← Listing
│   │   ├── new/
│   │   │   └── page.tsx              ← Write post
│   │   ├── edit/
│   │   │   └── [slug]/page.tsx
│   │   └── [slug]/
│   │       └── page.tsx              ← View post
│   ├── scholarships/
│   │   ├── page.tsx
│   │   └── [id]/
│   │       └── page.tsx
│   ├── jobs/
│   │   ├── page.tsx
│   │   ├── [id]/
│   │   │   ├── page.tsx
│   │   │   └── apply/
│   │   │       └── page.tsx
│   ├── login/
│   │   └── page.tsx
│   ├── register/
│   │   └── page.tsx
│   ├── dashboard/
│   │   └── page.tsx                  ← Subscriber dashboard
│   ├── admin/
│   │   ├── layout.tsx                ← Admin layout
│   │   ├── page.tsx
│   │   ├── products/page.tsx
│   │   ├── journals/page.tsx
│   │   ├── scholarships/page.tsx
│   │   ├── jobs/page.tsx
│   │   ├── subscriptions/page.tsx
│   │   └── settings/page.tsx
│   └── api/
│       ├── auth/
│       │   └── [...nextauth]/route.ts
│       ├── products/route.ts
│       ├── journals/route.ts
│       ├── scholarships/route.ts
│       ├── jobs/
│       │   ├── route.ts
│       │   ├── sync/route.ts
│       │   └── apply/route.ts
│       ├── cart/route.ts
│       ├── orders/route.ts
│       ├── subscriptions/route.ts
│       └── webhooks/
│           └── stripe/route.ts
├── components/
│   ├── ui/                           ← Buttons, inputs, modals, badges
│   ├── layout/                       ← Navbar, Footer, Sidebar
│   └── shared/                       ← ProductCard, JobCard, JournalCard, etc.
├── lib/
│   ├── db.ts                         ← Prisma client
│   ├── stripe.ts                     ← Stripe client
│   ├── mailgun.ts                    ← Mailgun client
│   ├── auth.ts                       ← NextAuth config
│   └── utils.ts                      ← Helpers (slugify, format, etc.)
├── prisma/
│   └── schema.prisma
├── public/
│   ├── images/
│   └── uploads/
├── styles/
│   └── globals.css
├── .env.local
├── next.config.ts
├── tailwind.config.ts
└── tsconfig.json
```

---

## 8. Environment Variables (.env.local)

```env
# Database
DATABASE_URL="mysql://USER:PASSWORD@localhost:3306/kcsystems"


# NextAuth
NEXTAUTH_SECRET="generate-a-strong-secret"
NEXTAUTH_URL="https://kc.inleads-it.com.my"

# Stripe
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="pk_live_..."
STRIPE_SECRET_KEY="sk_live_..."
STRIPE_WEBHOOK_SECRET="whsec_..."
STRIPE_PRICE_BASIC_MONTHLY="price_..."
STRIPE_PRICE_BASIC_YEARLY="price_..."
STRIPE_PRICE_JOB_SEEKER_MONTHLY="price_..."
STRIPE_PRICE_JOB_SEEKER_YEARLY="price_..."

# Mailgun
MAILGUN_API_KEY="key-..."
MAILGUN_DOMAIN="mg.kc.inleads-it.com.my"
MAILGUN_FROM="KC Systems <no-reply@kc.inleads-it.com.my>"

# Jobs API (Adzuna)
ADZUNA_APP_ID="..."
ADZUNA_APP_KEY="..."
ADZUNA_API_URL="https://api.adzuna.com/v1/api/jobs/my/search"

# App
NEXT_PUBLIC_APP_URL="https://kc.inleads-it.com.my"
NEXT_PUBLIC_APP_NAME="KC Systems"
UPLOAD_DIR="/var/www/kc.inleads-it.com.my/uploads"
```

---

## 9. Visa Assistance Module

> A full-featured Visa Consultation & Lead Management system.
> Admin configures countries + visa types; customers submit enquiries; super admin works applications in a built-in CRM.

---

### 9.1 Module Overview

| Layer | Role |
|---|---|
| **Public** | Browse countries & visa types, read requirements, submit an enquiry form |
| **Admin** | Manage countries, visa types, and visa service listings |
| **CRM** | Receive, assign, track, and action every application through a full pipeline |

---

### 9.2 Page Routes

#### Public Routes

| Route | Page |
|---|---|
| `/visa` | Visa Assistance landing — featured countries + visa type grid |
| `/visa/[country-slug]` | Country overview — all visa types available for that country |
| `/visa/[country-slug]/[visa-type-slug]` | Visa Service detail — requirements, fees, process steps, CTA form |
| `/visa/apply` | Standalone general enquiry form (no specific visa pre-selected) |
| `/visa/apply/[service-id]` | Pre-filled enquiry form for a specific country + visa type |
| `/visa/track` | Application tracker — customer enters reference number + email to check status |

#### Authenticated Routes (Registered User)

| Route | Page |
|---|---|
| `/dashboard/visa` | My visa applications — list with statuses and reference numbers |
| `/dashboard/visa/[ref]` | My application detail — timeline, status, uploaded documents |

#### Admin Routes

| Route | Page |
|---|---|
| `/admin/visa` | CRM overview — stats, pipeline board, recent applications |
| `/admin/visa/applications` | All applications table — filter/search/sort |
| `/admin/visa/applications/[id]` | Application detail + activity log + action panel |
| `/admin/visa/countries` | Manage countries list |
| `/admin/visa/countries/new` | Add country |
| `/admin/visa/countries/[id]/edit` | Edit country |
| `/admin/visa/types` | Manage visa types (Student, Work, Tourist…) |
| `/admin/visa/types/new` | Add visa type |
| `/admin/visa/types/[id]/edit` | Edit visa type |
| `/admin/visa/services` | Manage visa services (country × visa type combinations) |
| `/admin/visa/services/new` | Create a visa service listing |
| `/admin/visa/services/[id]/edit` | Edit a visa service listing |

---

### 9.3 API Routes

```
GET    /api/visa/countries                   — Active countries list
GET    /api/visa/countries/[slug]            — Country detail + its services
GET    /api/visa/services                    — List services (filter: ?country=&type=)
GET    /api/visa/services/[id]               — Service detail
POST   /api/visa/apply                       — Submit application / enquiry
GET    /api/visa/track                       — Track by reference + email

GET    /api/admin/visa/countries             — Admin: list countries
POST   /api/admin/visa/countries             — Admin: create country
PUT    /api/admin/visa/countries/[id]        — Admin: update country
DELETE /api/admin/visa/countries/[id]        — Admin: delete/deactivate

GET    /api/admin/visa/types                 — Admin: list visa types
POST   /api/admin/visa/types                 — Admin: create visa type
PUT    /api/admin/visa/types/[id]            — Admin: update visa type
DELETE /api/admin/visa/types/[id]            — Admin: delete/deactivate

GET    /api/admin/visa/services              — Admin: list visa services
POST   /api/admin/visa/services              — Admin: create visa service
PUT    /api/admin/visa/services/[id]         — Admin: update visa service
DELETE /api/admin/visa/services/[id]         — Admin: delete/deactivate

GET    /api/admin/visa/applications          — CRM: list applications (filter/search/paginate)
GET    /api/admin/visa/applications/[id]     — CRM: application detail
PUT    /api/admin/visa/applications/[id]     — CRM: update status, assignee, internal notes
POST   /api/admin/visa/applications/[id]/notes  — CRM: add activity note
GET    /api/admin/visa/applications/export   — CRM: export to CSV
```

---

### 9.4 Database Schema

#### `visa_countries`
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| name | VARCHAR(255) | e.g. "Australia" |
| slug | VARCHAR(255) UNIQUE | e.g. "australia" |
| flag_image | VARCHAR(500) NULL | image path or URL |
| cover_image | VARCHAR(500) NULL | hero image for country page |
| description | TEXT NULL | brief intro shown on listing |
| is_active | TINYINT(1) | default 1 |
| sort_order | INT | default 0, controls display order |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |

#### `visa_types`
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| name | VARCHAR(255) | e.g. "Student Visa", "Work Visa" |
| slug | VARCHAR(255) UNIQUE | e.g. "student-visa" |
| icon | VARCHAR(100) NULL | icon name or SVG path |
| description | TEXT NULL | short description |
| is_active | TINYINT(1) | default 1 |
| sort_order | INT | default 0 |
| created_at | TIMESTAMP | |

#### `visa_services`
> The actual offering: a specific visa type for a specific country.

| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| country_id | INT FK → visa_countries | |
| visa_type_id | INT FK → visa_types | |
| title | VARCHAR(255) | e.g. "Australia Student Visa (Subclass 500)" |
| slug | VARCHAR(255) UNIQUE | e.g. "australia-student-visa" |
| overview | TEXT NULL | intro paragraph |
| requirements | JSON | array of document/requirement strings |
| process_steps | JSON | ordered array of step objects `{step, title, description}` |
| processing_time | VARCHAR(100) NULL | e.g. "4–8 weeks" |
| government_fee | VARCHAR(100) NULL | e.g. "AUD 650" |
| service_fee | DECIMAL(10,2) NULL | KC Systems agency fee in MYR |
| validity | VARCHAR(100) NULL | e.g. "Single Entry / Up to 5 years" |
| age_min | INT NULL | minimum applicant age |
| age_max | INT NULL | maximum applicant age |
| notes | TEXT NULL | important notes / disclaimers |
| is_active | TINYINT(1) | default 1 |
| sort_order | INT | default 0 |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |

#### `visa_applications`
| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| reference_no | VARCHAR(30) UNIQUE | auto-generated: `VA-YYYY-XXXXX` |
| user_id | INT FK → users NULL | linked if logged in |
| visa_service_id | INT FK → visa_services NULL | pre-selected service; NULL = general enquiry |
| country_id | INT FK → visa_countries NULL | denormalised for quick filtering |
| visa_type_id | INT FK → visa_types NULL | denormalised for quick filtering |
| **Personal Info** | | |
| full_name | VARCHAR(255) | |
| email | VARCHAR(255) | |
| phone | VARCHAR(50) | |
| nationality | VARCHAR(100) | |
| date_of_birth | DATE NULL | |
| passport_number | VARCHAR(100) NULL | |
| passport_expiry | DATE NULL | |
| marital_status | ENUM('single','married','divorced','widowed') NULL | |
| **Travel Info** | | |
| intended_travel_date | DATE NULL | |
| intended_duration | VARCHAR(100) NULL | e.g. "3 months" |
| purpose | TEXT NULL | customer's own description |
| previous_visa_refusal | TINYINT(1) | default 0 |
| refusal_details | TEXT NULL | if refusal = 1 |
| **Documents** | | |
| uploaded_documents | JSON NULL | array of `{label, path, uploaded_at}` |
| **CRM Pipeline** | | |
| status | ENUM('new','in_review','documents_requested','processing','submitted_to_embassy','approved','rejected','cancelled') | default: 'new' |
| assigned_to | INT FK → users NULL | admin staff assigned |
| priority | ENUM('normal','high','urgent') | default: 'normal' |
| internal_notes | TEXT NULL | latest admin note (quick reference) |
| follow_up_date | DATE NULL | scheduled follow-up reminder |
| **Timestamps** | | |
| created_at | TIMESTAMP | |
| updated_at | TIMESTAMP | |

#### `visa_application_notes`
> CRM activity log — every status change, call, note, and document request is recorded here.

| Column | Type | Notes |
|---|---|---|
| id | INT PK AUTO_INCREMENT | |
| application_id | INT FK → visa_applications | |
| admin_id | INT FK → users | staff who added the entry |
| type | ENUM('note','status_change','document_request','email_sent','call_log') | |
| content | TEXT | note body |
| meta | JSON NULL | e.g. `{from_status, to_status}` for status changes |
| created_at | TIMESTAMP | |

---

### 9.5 Application Enquiry Form Fields

The public form collects:

**Section 1 — Visa Selection** (pre-filled if coming from a service page)
- Destination country (dropdown — populated from `visa_countries`)
- Visa type (dropdown — filtered by selected country from `visa_services`)

**Section 2 — Personal Details**
- Full name `*`
- Email address `*`
- Phone number `*`
- Nationality / Passport country `*`
- Date of birth
- Passport number
- Passport expiry date
- Marital status

**Section 3 — Travel Details**
- Intended travel date
- Intended stay duration
- Purpose / additional message (textarea)
- Previous visa refusal? (yes/no toggle → shows detail field if yes)

**Section 4 — Document Upload** *(optional at enquiry stage)*
- Passport copy (PDF/JPG)
- Supporting documents (multi-file)

**Privacy / PDPA consent checkbox** `*`

---

### 9.6 CRM Pipeline Stages

```
New → In Review → Documents Requested → Processing → Submitted to Embassy → Approved / Rejected / Cancelled
```

| Stage | Meaning |
|---|---|
| **New** | Freshly submitted, unread |
| **In Review** | Admin has opened and is reviewing the case |
| **Documents Requested** | Customer has been asked to provide additional documents |
| **Processing** | All docs received; case being prepared |
| **Submitted to Embassy** | Application formally lodged |
| **Approved** | Visa granted |
| **Rejected** | Visa refused |
| **Cancelled** | Customer or admin cancelled the application |

---

### 9.7 Admin CRM Features

- **Dashboard stats**: Total applications, New (unread), In Review, Approved this month, Rejected this month
- **Pipeline Kanban board** (optional) OR standard filterable table view
- **Filters**: Country, Visa type, Status, Assigned staff, Priority, Date range
- **Bulk actions**: Reassign, Change status, Export
- **Application detail view**:
  - Applicant info panel
  - Visa service info panel
  - Document list with download links
  - Status update dropdown with one-click change
  - Activity log (timeline of all notes/status changes)
  - Add note / call log
  - Schedule follow-up date
  - Send email to applicant (template-based via Mailgun)
- **Export to CSV**: all or filtered results

---

### 9.8 Mail Flows (Visa Module)

| Trigger | Recipient | Template |
|---|---|---|
| Application submitted | Customer | Confirmation + reference number |
| Application submitted | Admin | New visa application notification |
| Status → In Review | Customer | "We're reviewing your application" |
| Status → Documents Requested | Customer | "Please provide the following documents…" (lists requested docs) |
| Status → Processing | Customer | "Your application is being processed" |
| Status → Submitted to Embassy | Customer | "Your application has been submitted to the embassy" |
| Status → Approved | Customer | "Congratulations! Your visa has been approved" |
| Status → Rejected | Customer | "Update on your visa application" (diplomatic rejection notice) |
| Follow-up reminder | Admin | Internal reminder email for scheduled follow-ups |

---

### 9.9 Access Control

| Feature | Guest | Registered | Any Subscriber | Admin |
|---|---|---|---|---|
| Browse visa countries & types | ✅ | ✅ | ✅ | ✅ |
| View visa service detail & requirements | ✅ | ✅ | ✅ | ✅ |
| Submit visa enquiry / application | ✅ (with email) | ✅ | ✅ | ✅ |
| Track application by reference + email | ✅ | ✅ | ✅ | ✅ |
| View own applications in dashboard | ❌ | ✅ | ✅ | ✅ |
| Admin: manage countries / types / services | ❌ | ❌ | ❌ | ✅ |
| Admin: CRM — view & action applications | ❌ | ❌ | ❌ | ✅ |

> Visa enquiry is intentionally open to guests — it is a lead-capture form. Linking to a user account happens automatically when the email matches a registered user, or after the guest registers post-submission.

---

### 9.10 Component Structure

```
components/
└── visa/
    ├── CountryCard.tsx          ← Card shown in country grid on /visa
    ├── VisaTypeChip.tsx         ← Badge/pill for visa type labels
    ├── VisaServiceCard.tsx      ← Card for a specific country+type service
    ├── RequirementsList.tsx     ← Formatted checklist of document requirements
    ├── ProcessSteps.tsx         ← Numbered step-by-step process timeline
    ├── ApplicationForm.tsx      ← Multi-step public enquiry / application form
    ├── ApplicationTracker.tsx   ← Public status tracker widget
    └── crm/
        ├── ApplicationTable.tsx ← Admin CRM table view
        ├── ApplicationDetail.tsx← Full application detail panel
        ├── ActivityLog.tsx      ← Timeline of notes & status changes
        ├── StatusBadge.tsx      ← Colour-coded status pill
        └── AddNoteForm.tsx      ← Note / call log entry form

app/
└── visa/
    ├── page.tsx                          ← Landing: countries + visa types
    ├── apply/
    │   └── page.tsx                      ← Standalone general enquiry form
    ├── track/
    │   └── page.tsx                      ← Reference number tracker
    └── [country-slug]/
        ├── page.tsx                      ← Country overview
        └── [visa-type-slug]/
            └── page.tsx                  ← Service detail + inline apply CTA

app/admin/visa/
    ├── page.tsx                          ← CRM dashboard
    ├── applications/
    │   ├── page.tsx                      ← Applications table
    │   └── [id]/
    │       └── page.tsx                  ← Application detail
    ├── countries/
    │   ├── page.tsx
    │   ├── new/page.tsx
    │   └── [id]/edit/page.tsx
    ├── types/
    │   ├── page.tsx
    │   ├── new/page.tsx
    │   └── [id]/edit/page.tsx
    └── services/
        ├── page.tsx
        ├── new/page.tsx
        └── [id]/edit/page.tsx
```

---

### 9.11 Homepage Integration

Add a **Visa Assistance section** on the home page showing:
- Featured destination countries (4–6 cards with flag + name)
- Visa type quick-links (icon grid: Student, Work, Tourist, Holiday, Business…)
- CTA button: "Check Requirements" → `/visa`

Component: `components/home/VisaSection.tsx`

---

### 9.12 Navbar Update

Add **Visa** to the main navigation menu alongside Shop, Journals, Jobs, Scholarships.

---

### 9.13 Schema Additions to `plan.md § 2`

The four new tables to add to Prisma schema:

1. `visa_countries`
2. `visa_types`
3. `visa_services`
4. `visa_applications`
5. `visa_application_notes`
