# SK Tiong Achievement Gallery

> A bilingual showcase website for awards, honors, and milestones of Tan Sri Datuk Tiong Su Kouk and the SK Tiong Group of Companies.

## Overview

**Purpose:** Display personal and corporate achievements including:
- Business & Corporate Awards
- National Honors & Titles
- Commemorative Plaques & Trophies
- Milestone Events (IPO listings, building inaugurations)
- Community Service Recognition
- Notable Photos & Meetings

**Two main sections:**
1. **Public Gallery** - Browse achievements by category, year, or industry (EN/ZH, responsive)
2. **Admin Panel** - Manage items, categories, and images (English only, desktop only)

---

## Tech Stack

| Layer | Technology | Purpose |
|-------|-----------|---------|
| **Backend** | Laravel 12 + Breeze | Server-side logic, authentication |
| **Frontend** | Vue 3 + Quasar + Inertia.js | UI components, bilingual display |
| **Database** | MySQL | Store items with EN/ZH content |

**Architecture Principles:**
- Keep it simple - no over-engineering
- Frontend handles language switching (EN/ZH)
- Backend stays in English
- Direct Laravel to Vue data flow via Inertia.js

---

## Bilingual Support (Simplified)

### How It Works

| Component | Language | Notes |
|-----------|----------|-------|
| **Database content** | EN + ZH columns | `name_en`, `name_zh`, etc. |
| **Public UI labels** | JSON files | `lang/en.json`, `lang/zh.json` |
| **Admin panel** | English only | Desktop only, forms show both EN/ZH fields |
| **Backend code** | English only | No server-side locale logic |

### Language Switching

- User clicks language toggle (EN/中文)
- Frontend switches Vue I18n locale
- Vue components display appropriate `_en` or `_zh` fields
- Preference saved in localStorage

**No URL prefixes, no server-side middleware, no complex routing.**

---

## Database Schema

### Categories

| Column | Type | Description |
|--------|------|-------------|
| `id` | bigint | Primary key |
| `name_en` | string | English name |
| `name_zh` | string | Chinese name |
| `slug` | string | URL identifier |
| `description_en` | text | English description |
| `description_zh` | text | Chinese description |
| `cover_image` | string | Cover image path |
| `timestamps` | | Created/updated |

**Example Categories:**
- Business Awards (商业奖项)
- National Honors (国家荣誉)
- Corporate Milestones (企业里程碑)
- Community Service (社区服务)
- Commemorative Items (纪念品)
- Notable Events (重要活动)

### Display Items (Awards/Achievements)

| Column | Type | Description |
|--------|------|-------------|
| `id` | bigint | Primary key |
| `category_id` | foreignId | Category link |
| `name_en` | string | English title |
| `name_zh` | string | Chinese title |
| `slug` | string | URL identifier |
| `description_en` | text | English description |
| `description_zh` | text | Chinese description |
| `accomplishment_en` | text | Achievement details (EN) |
| `accomplishment_zh` | text | Achievement details (ZH) |
| `country_en` | string | Country (EN) |
| `country_zh` | string | Country (ZH) |
| `related_year` | integer | Year of achievement |
| `related_industry_en` | string | Industry sector (EN) |
| `related_industry_zh` | string | Industry sector (ZH) |
| `acquisition_date` | date | Date received |
| `additional_source_links` | text | Reference URLs |
| `is_featured` | boolean | Show on homepage |
| `is_published` | boolean | Public visibility |
| `timestamps` | | Created/updated |

### Images

| Column | Type | Description |
|--------|------|-------------|
| `id` | bigint | Primary key |
| `display_item_id` | foreignId | Item link |
| `filename` | string | File name |
| `file_path` | string | Storage path |
| `alt_text_en` | string | Alt text (EN) |
| `alt_text_zh` | string | Alt text (ZH) |
| `sort_order` | integer | Display order |
| `is_primary` | boolean | Main image |
| `timestamps` | | Created/updated |

---

## Frontend Translation

### Setup Vue I18n

```bash
npm install vue-i18n@9
```

### Translation Files

**lang/en.json:**
```json
{
  "nav": {
    "home": "Home",
    "gallery": "Gallery",
    "about": "About"
  },
  "home": {
    "title": "Achievement Gallery",
    "subtitle": "Awards, Honors & Milestones",
    "featured": "Featured",
    "view_all": "View All"
  },
  "item": {
    "year": "Year",
    "country": "Country",
    "industry": "Industry",
    "achievement": "Achievement",
    "date_received": "Date Received",
    "source": "Source"
  },
  "filter": {
    "all_categories": "All Categories",
    "all_years": "All Years",
    "search": "Search..."
  }
}
```

**lang/zh.json:**
```json
{
  "nav": {
    "home": "首页",
    "gallery": "展览馆",
    "about": "关于"
  },
  "home": {
    "title": "成就展览馆",
    "subtitle": "奖项、荣誉与里程碑",
    "featured": "精选",
    "view_all": "查看全部"
  },
  "item": {
    "year": "年份",
    "country": "国家",
    "industry": "行业",
    "achievement": "成就",
    "date_received": "获得日期",
    "source": "来源"
  },
  "filter": {
    "all_categories": "所有分类",
    "all_years": "所有年份",
    "search": "搜索..."
  }
}
```

### Language Switcher Component

```vue
<template>
  <q-btn-toggle
    v-model="currentLocale"
    toggle-color="primary"
    :options="[
      { label: 'EN', value: 'en' },
      { label: '中文', value: 'zh' }
    ]"
    @update:model-value="switchLocale"
  />
</template>

<script setup>
import { ref } from 'vue'
import { useI18n } from 'vue-i18n'

const { locale } = useI18n()
const currentLocale = ref(localStorage.getItem('locale') || 'en')

function switchLocale(newLocale) {
  locale.value = newLocale
  localStorage.setItem('locale', newLocale)
}
</script>
```

### Using Bilingual Data in Components

```vue
<template>
  <q-card>
    <q-card-section>
      <!-- Display based on current locale -->
      <div class="text-h6">{{ localizedName }}</div>
      <div class="text-body2">{{ localizedDescription }}</div>
    </q-card-section>
  </q-card>
</template>

<script setup>
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'

const props = defineProps({
  item: Object
})

const { locale } = useI18n()

const localizedName = computed(() =>
  locale.value === 'zh' ? props.item.name_zh : props.item.name_en
)

const localizedDescription = computed(() =>
  locale.value === 'zh' ? props.item.description_zh : props.item.description_en
)
</script>
```

---

## Routes

### Public
```
GET /                      → Homepage (featured items)
GET /gallery               → All items with filters
GET /item/{slug}           → Item detail page
GET /category/{slug}       → Items by category
```

### Admin (Auth Required)
```
GET    /admin/dashboard
GET    /admin/categories
POST   /admin/categories
PUT    /admin/categories/{id}
DELETE /admin/categories/{id}

GET    /admin/display-items
POST   /admin/display-items
PUT    /admin/display-items/{id}
DELETE /admin/display-items/{id}
GET    /admin/display-items/{id}/qrcode

POST   /admin/images
DELETE /admin/images/{id}

GET    /admin/display-items/template
POST   /admin/display-items/import
GET    /admin/display-items/export
```

---

## Admin Form Layout

### Display Item Form (Shows Both Languages)

```
┌────────────────────────────────────────────────┐
│ Create / Edit Item                             │
├────────────────────────────────────────────────┤
│                                                │
│  Category: [_______________▼]                  │
│                                                │
│  ┌──────────────────┐  ┌──────────────────┐   │
│  │ Title (EN)       │  │ Title (ZH)       │   │
│  │ [______________] │  │ [______________] │   │
│  └──────────────────┘  └──────────────────┘   │
│                                                │
│  ┌──────────────────┐  ┌──────────────────┐   │
│  │ Description (EN) │  │ Description (ZH) │   │
│  │ [              ] │  │ [              ] │   │
│  └──────────────────┘  └──────────────────┘   │
│                                                │
│  ┌──────────────────┐  ┌──────────────────┐   │
│  │ Achievement (EN) │  │ Achievement (ZH) │   │
│  │ [              ] │  │ [              ] │   │
│  └──────────────────┘  └──────────────────┘   │
│                                                │
│  Year: [____]    Date Received: [__________]  │
│                                                │
│  ┌──────────────────┐  ┌──────────────────┐   │
│  │ Country (EN)     │  │ Country (ZH)     │   │
│  │ [______________] │  │ [______________] │   │
│  └──────────────────┘  └──────────────────┘   │
│                                                │
│  ┌──────────────────┐  ┌──────────────────┐   │
│  │ Industry (EN)    │  │ Industry (ZH)    │   │
│  │ [______________] │  │ [______________] │   │
│  └──────────────────┘  └──────────────────┘   │
│                                                │
│  Source URL: [_______________________________] │
│                                                │
│  ☐ Featured    ☐ Published                    │
│                                                │
│  Images: [Upload] [img1] [img2] [img3]        │
│                                                │
│  [Cancel]                          [Save]      │
└────────────────────────────────────────────────┘
```

---

## Excel Import Template

| Column | Required | Description |
|--------|----------|-------------|
| `name_en` | Yes | English title |
| `name_zh` | Yes | Chinese title |
| `category` | Yes | Category slug |
| `description_en` | No | English description |
| `description_zh` | No | Chinese description |
| `accomplishment_en` | No | Achievement (EN) |
| `accomplishment_zh` | No | Achievement (ZH) |
| `country_en` | No | Country (EN) |
| `country_zh` | No | Country (ZH) |
| `related_year` | No | Year |
| `related_industry_en` | No | Industry (EN) |
| `related_industry_zh` | No | Industry (ZH) |
| `acquisition_date` | No | YYYY-MM-DD |
| `additional_source_links` | No | URL |
| `is_featured` | No | 1 or 0 |
| `is_published` | No | 1 or 0 (default: 1) |

---

## Quick Start

```bash
# 1. Install dependencies
composer install
npm install

# 2. Environment setup
cp .env.example .env
php artisan key:generate
# Configure database in .env

# 3. Database
php artisan migrate
php artisan db:seed

# 4. Storage
php artisan storage:link

# 5. Run
php artisan serve   # Terminal 1
npm run dev         # Terminal 2
```

Access: `http://localhost:8000`

---

## Project Structure

```
museumWeb/
├── app/
│   ├── Http/Controllers/
│   │   ├── Admin/              # Admin CRUD
│   │   ├── HomepageController.php
│   │   ├── DisplayItemController.php
│   │   └── CategoryController.php
│   └── Models/
│       ├── Category.php
│       ├── DisplayItem.php
│       └── Image.php
├── resources/js/
│   ├── app.js
│   ├── i18n.js                 # Vue I18n setup
│   ├── Components/
│   │   └── LanguageSwitcher.vue
│   ├── Layouts/
│   │   ├── PublicLayout.vue
│   │   └── AdminLayout.vue
│   └── Pages/
│       ├── Homepage.vue
│       ├── Gallery.vue
│       ├── DisplayItems/Show.vue
│       └── Admin/...
├── lang/
│   ├── en.json                 # English UI
│   └── zh.json                 # Chinese UI
├── database/
│   ├── migrations/
│   └── seeders/
│       └── SKTiongSeeder.json  # Initial data
└── routes/web.php
```

---

## Sample Data Categories

Based on the seeder data, suggested categories:

| Slug | English | Chinese |
|------|---------|---------|
| `business-awards` | Business Awards | 商业奖项 |
| `national-honors` | National Honors | 国家荣誉 |
| `corporate-milestones` | Corporate Milestones | 企业里程碑 |
| `community-service` | Community Service | 社区服务 |
| `investor-relations` | Investor Relations | 投资者关系 |
| `commemorative` | Commemorative Items | 纪念品 |
| `notable-events` | Notable Events | 重要活动 |

---

## Key Features

### Public Pages (Responsive)
- **Homepage**: Featured achievements with hero section
- **Gallery**: Filter by category, year, country
- **Detail Page**: Full item info with image gallery
- **Language Toggle**: Switch EN/中文 instantly
- **Mobile-friendly**: Responsive design with Quasar

### Admin Panel (Desktop Only)
- **Dashboard**: Overview stats
- **CRUD**: Manage categories and items
- **Image Upload**: Multiple images per item
- **QR Codes**: Generate QR for each item
- **Excel Import/Export**: Bulk data management
- **No mobile support**: Optimized for desktop browsers only

---

## Deployment

```bash
# Build
npm run build

# Optimize
php artisan config:cache
php artisan route:cache
php artisan view:cache

# Permissions
chmod -R 755 storage bootstrap/cache
```

---

**Last Updated:** January 2026
