Skip to content

Requirement specifications

Document Version: 1.0 Date: 2024-11-12 Copyright: Perun Consulting Services FZ LLE System: TQPRO - Trip Offer Management Module


Table of Contents

  1. Executive Summary
  2. System Overview
  3. Functional Requirements
  4. Data Requirements
  5. API Requirements
  6. User Interface Requirements
  7. Business Logic Requirements
  8. Non-Functional Requirements
  9. Architecture Requirements
  10. Integration Requirements
  11. Security Requirements
  12. Testing Requirements

1. Executive Summary

1.1 Purpose

The Trip Offer Management System provides a comprehensive solution for managing travel destination pages and trip variant snippets. The system enables content administrators to create, organize, and publish travel packages through a master-detail web interface with automated HTML page generation capabilities.

1.2 Scope

The system encompasses: - Management of destination trip pages - Management of trip variant snippets with rich metadata - Template-based HTML page generation - Master-detail UI pattern for related entity management - RESTful API for all CRUD operations - Session-based authentication and authorization

1.3 Stakeholders

  • Content Administrators: Primary users who manage trip pages and snippets
  • Web Developers: Integration with TQPRO platform
  • End Users: Consumers of generated HTML pages (indirect)

2. System Overview

2.1 System Context

The Trip Offer Management System is a module within the TQPRO (TourLinQ Professional) platform, integrated with: - NTS (Native Transaction Service) database layer - TQPRO authentication and session management - Foundation CSS frontend framework - TQPRO entity management framework

2.2 Key Features

  1. Trip Page Management: Create and manage destination pages with metadata
  2. Trip Snippet Management: Manage detailed trip variant offerings
  3. Master-Detail Interface: Coordinated management of pages and their snippets
  4. HTML Generation: Template-based page generation with placeholder replacement
  5. Pagination Support: Handle large datasets efficiently
  6. Active Status Filtering: Control snippet visibility

2.3 Technology Stack

  • Backend: Java 17+, Jakarta EE, Hibernate/JPA
  • Database: PostgreSQL (nts schema)
  • API Layer: JAX-RS (REST)
  • Frontend: HTML5, JavaScript ES6+, jQuery, Foundation CSS 6.8
  • Build System: Gradle

3. Functional Requirements

3.1 Trip Page Management (FR-PAGE)

FR-PAGE-001: Create Trip Page

Priority: High Description: System shall allow users to create new trip destination pages.

Input Requirements: - page_name (String, max 200 chars, required): HTML filename for the destination page - page_desc (String, max 1000 chars, required): Verbal description of the destination

Business Rules: - Page name must be unique - Page name should follow HTML filename conventions (e.g., "dubai-packages.html") - Page description is mandatory

Output: - Newly created page with system-generated page_id - Success/failure notification


FR-PAGE-002: Read Trip Page

Priority: High Description: System shall allow retrieval of trip page details.

Input Requirements: - page_id (Integer, required): Unique identifier of the page

Output: - Complete page record with all fields - Error if page not found


FR-PAGE-003: Update Trip Page

Priority: High Description: System shall allow modification of existing trip pages.

Input Requirements: - page_id (Integer, required): Identifier of page to update - page_name (String, max 200 chars, required) - page_desc (String, max 1000 chars, required)

Business Rules: - Cannot update non-existent page - Page name uniqueness constraint applies


FR-PAGE-004: Delete Trip Page

Priority: High Description: System shall allow deletion of trip pages.

Input Requirements: - page_id (Integer, required): Identifier of page to delete

Business Rules: - CASCADE DELETE: Deleting a page should trigger deletion of all associated snippets - User must confirm deletion (UI requirement)

Side Effects: - All trip snippets with matching page_id are deleted


FR-PAGE-005: List Trip Pages

Priority: High Description: System shall provide paginated listing of all trip pages.

Input Requirements: - page (Integer, optional): Zero-based page number (default: 0) - pageSize (Integer, optional): Records per page (default: 10)

Output: - List of trip page records - Supports in-memory pagination

Business Rules: - Results ordered by page_id ascending - Empty list returned if no pages exist


3.2 Trip Snippet Management (FR-SNIPPET)

FR-SNIPPET-001: Create Trip Snippet

Priority: High Description: System shall allow users to create trip variant snippets.

Input Requirements (15 fields):

Field Type Max Length Required Description
snippet_id Integer - Auto-generated Primary key
page_id Integer - Yes Foreign key to trip_page
code String 20 Yes Unique public package code
title String 50 Yes Package title
request_desc String 200 No Chat request description template
destination String 50 No Destination name
image String 200 No Image URL/path
imagetitle String 100 No Image alt text
tagline1 String 100 No First promotional tagline
tagline2 String 100 No Second promotional tagline
price String 20 No Price display text
shortdesc String 200 No Short description
description String 1000 No Full description
inclusionhtml String 1000 No HTML list of inclusions
active Integer - No Status flag (1=active, 0=inactive)

Business Rules: - code must be unique across all snippets - page_id must reference existing trip_page - title is mandatory - active defaults to 1 if not specified


FR-SNIPPET-002: Read Trip Snippet

Priority: High Description: System shall allow retrieval of snippet details.

Input Requirements: - snippet_id (Integer, required)

Output: - Complete snippet record with all 15 fields


FR-SNIPPET-003: Update Trip Snippet

Priority: High Description: System shall allow modification of existing snippets.

Input Requirements: - All fields from FR-SNIPPET-001 - snippet_id must exist

Business Rules: - Cannot change page_id to non-existent page - Unique code constraint applies


FR-SNIPPET-004: Delete Trip Snippet

Priority: High Description: System shall allow deletion of trip snippets.

Input Requirements: - snippet_id (Integer, required)

Business Rules: - User confirmation required (UI)


FR-SNIPPET-005: List Snippets by Page

Priority: High Description: System shall provide paginated listing of snippets for a specific page.

Input Requirements: - page_id (Integer, required): Filter by this page - page (Integer, optional): Page number - pageSize (Integer, optional): Records per page

Output: - List of snippet records for the specified page - Ordered by snippet_id


FR-SNIPPET-006: Get Active Snippets

Priority: High Description: System shall retrieve only active snippets for a page.

Input Requirements: - page_id (Integer, required)

Output: - List of snippets where active = 1

Business Rules: - Used exclusively for HTML generation - No pagination (returns all active snippets)


3.3 Template Management (FR-TEMPLATE)

FR-TEMPLATE-001: List Skeleton Templates

Priority: High Description: System shall provide list of available HTML skeleton templates.

Output: - List of skeleton records containing: - skeleton_id (Integer): Unique identifier - skeleton_name (String, 200): Display name - skeleton_desc (String, 500): Description - skeleton_file (String, 500): File path to skeleton HTML

Business Rules: - Skeleton files must exist on filesystem - Skeleton files must contain the marker: ---INSERT SNIPPETS HERE---


FR-TEMPLATE-002: List Snippet Templates

Priority: High Description: System shall provide list of available snippet template files.

Output: - List of snippet file records containing: - snippet_file_id (Integer): Unique identifier - snippet_file_name (String, 200): Display name - snippet_file_desc (String, 500): Description - snippet_file (String, 500): File path to snippet template HTML

Business Rules: - Snippet template files must exist on filesystem - Templates use placeholder syntax: %%fieldname%%


3.4 HTML Generation (FR-GENERATE)

FR-GENERATE-001: Generate HTML Page

Priority: Critical Description: System shall generate complete HTML pages by combining skeleton templates with snippet data.

Input Requirements: - page_id (Integer, required): Trip page to generate - skeleton_id (Integer, required): Skeleton template to use - snippet_file_id (Integer, required): Snippet template to use

Processing Steps:

  1. Validate Inputs: All three IDs must exist
  2. Load Page Data: Retrieve trip_page record
  3. Load Skeleton: Read skeleton HTML file from filesystem
  4. Load Snippet Template: Read snippet template file from filesystem
  5. Fetch Active Snippets: Get all snippets where page_id matches and active = 1
  6. For Each Snippet:
    • Clone snippet template
    • Replace all placeholders with snippet data
    • Append to snippet collection
  7. Final Assembly: Replace ---INSERT SNIPPETS HERE--- marker in skeleton with all snippet HTML
  8. Return Result: Generated HTML content and filename

Placeholder Mapping:

Placeholder Source Field Notes
%%code%% snippet.code Package code
%%title%% snippet.title Package title
%%request_desc%% snippet.request_desc Chat request description
%%destination%% snippet.destination Destination name
%%image%% snippet.image Image URL
%%imagetitle%% snippet.imagetitle Image alt text
%%tagline1%% snippet.tagline1 First tagline
%%tagline2%% snippet.tagline2 Second tagline
%%price%% snippet.price Price display
%%shortdesc%% snippet.shortdesc Short description
%%description%% snippet.description Full description
%%inclusionhtml%% snippet.inclusionhtml Inclusions HTML

Output:

{
  "success": true,
  "fileName": "dubai-packages.html",
  "content": "<html>...</html>"
}

Business Rules: - NULL values are replaced with empty strings - Only active snippets (active = 1) are included - Snippets are concatenated with newline separators - Original skeleton and template files are not modified - Generated HTML is returned to client for download

Error Conditions: - Page not found - Skeleton not found - Snippet file not found - File I/O errors - No active snippets (warning, not error - generates empty page)


3.5 Master-Detail Coordination (FR-COORD)

FR-COORD-001: Page Selection Updates Snippets

Priority: High Description: When a trip page is selected in the UI, the snippet list must automatically update.

Behavior: - Clicking a page row highlights it - Snippet table immediately loads snippets for selected page - Previous snippet selection is cleared - If no snippets exist, display "No snippets found for this page"


FR-COORD-002: Page Deletion Clears Snippets

Priority: High Description: When selected page is deleted, snippet panel must clear.

Behavior: - If currently displayed page is deleted, clear snippet table - Reset selected page state to null - Display "Select a page to view snippets"


FR-COORD-003: Snippet Requires Page Selection

Priority: High Description: Users cannot create snippets without first selecting a page.

Behavior: - "New Snippet" button disabled when no page selected - Creating snippet auto-populates page_id from selected page


4. Data Requirements

4.1 Database Schema Requirements

4.1.1 Schema Location

  • Schema Name: nts
  • Database: PostgreSQL 12+

4.1.2 Table: nts.trip_page

Purpose: Stores destination page definitions

CREATE TABLE nts.trip_page (
  page_id INTEGER NOT NULL,
  page_name VARCHAR(200) NOT NULL,
  page_desc VARCHAR(1000) NOT NULL,
  CONSTRAINT trip_page_pk PRIMARY KEY(page_id)
);

Columns:

Column Type Constraints Description
page_id INTEGER PRIMARY KEY, NOT NULL Unique identifier (sequence-generated)
page_name VARCHAR(200) NOT NULL HTML filename for destination
page_desc VARCHAR(1000) NOT NULL Description of destination

Indexes: - Primary key index on page_id

Sequences: - nts.trip_page_seq: Managed by Hibernate


4.1.3 Table: nts.trip_snippet

Purpose: Stores trip variant snippet details

CREATE TABLE nts.trip_snippet (
  snippet_id INTEGER NOT NULL,
  page_id INTEGER NOT NULL,
  code VARCHAR(20),
  request_desc VARCHAR(200),
  destination VARCHAR(50),
  image VARCHAR(200),
  imagetitle VARCHAR(100),
  tagline1 VARCHAR(100),
  tagline2 VARCHAR(100),
  price VARCHAR(20),
  title VARCHAR(50),
  shortdesc VARCHAR(200),
  description VARCHAR(1000),
  inclusionhtml VARCHAR(1000),
  active INTEGER,
  CONSTRAINT trip_snippet_pk PRIMARY KEY(snippet_id),
  CONSTRAINT trip_snippet_code_uindex UNIQUE(code),
  CONSTRAINT trip_snippet_trip_page_page_id_fk
    FOREIGN KEY (page_id) REFERENCES nts.trip_page(page_id)
    ON DELETE NO ACTION
);

Columns: (See FR-SNIPPET-001 for detailed field specifications)

Constraints: - UNIQUE on code column - FOREIGN KEY to trip_page.page_id - ON DELETE NO ACTION (application-level cascade)

Indexes: - Primary key on snippet_id - Unique index on code - Foreign key index on page_id

Sequences: - nts.trip_snippet_seq: Managed by Hibernate


4.1.4 Table: nts.trip_skeleton

Purpose: Stores HTML skeleton template metadata

CREATE TABLE nts.trip_skeleton (
  skeleton_id INTEGER NOT NULL,
  skeleton_name VARCHAR(200) NOT NULL,
  skeleton_desc VARCHAR(500),
  skeleton_file VARCHAR(500) NOT NULL,
  CONSTRAINT trip_skeleton_pk PRIMARY KEY(skeleton_id)
);

Columns:

Column Type Constraints Description
skeleton_id INTEGER PRIMARY KEY Unique identifier
skeleton_name VARCHAR(200) NOT NULL Display name
skeleton_desc VARCHAR(500) NULL Description
skeleton_file VARCHAR(500) NOT NULL Filesystem path to HTML file

Business Rules: - skeleton_file must be absolute or relative path accessible by server - Skeleton HTML must contain marker: ---INSERT SNIPPETS HERE---


4.1.5 Table: nts.trip_snippet_file

Purpose: Stores snippet template metadata

CREATE TABLE nts.trip_snippet_file (
  snippet_file_id INTEGER NOT NULL,
  snippet_file_name VARCHAR(200) NOT NULL,
  snippet_file_desc VARCHAR(500),
  snippet_file VARCHAR(500) NOT NULL,
  CONSTRAINT trip_snippet_file_pk PRIMARY KEY(snippet_file_id)
);

Columns:

Column Type Constraints Description
snippet_file_id INTEGER PRIMARY KEY Unique identifier
snippet_file_name VARCHAR(200) NOT NULL Display name
snippet_file_desc VARCHAR(500) NULL Description
snippet_file VARCHAR(500) NOT NULL Filesystem path to template file

Business Rules:

  • Template files must use %%placeholder%% syntax
  • Files must be accessible by server process

4.2 Entity Layer Requirements

4.2.1 Database Entity Classes (Hibernate/JPA Layer)

Package: com.perun.tlinq.client.nts.db.trip

Classes Required: 1. TrippageEntity - Maps to nts.trip_page 2. TripsnippetEntity - Maps to nts.trip_snippet 3. TripskeletonEntity - Maps to nts.trip_skeleton 4. TripsnippetfileEntity - Maps to nts.trip_snippet_file

Requirements: - Must extend NTSEntity - Use JPA annotations: @Entity, @Table, @Column, @Id - Use @GeneratedValue with SEQUENCE strategy - Use @SequenceGenerator for ID generation - Use validation annotations: @NotNull, @Size - Override getId() method from NTSEntity

Example Structure:

@Entity
@Table(name = "trip_page", schema = "nts")
public class TrippageEntity extends NTSEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "trip_page_id_gen")
    @SequenceGenerator(name = "trip_page_id_gen",
                       sequenceName = "nts.trip_page_seq",
                       allocationSize = 1)
    @Column(name = "page_id", nullable = false)
    private Integer pageId;

    // Additional fields with JPA annotations
    // Getters and setters
    // Override getId()
}


4.2.2 Canonical Entity Classes (Business Layer)

Package: com.perun.tlinq.entity.trip

Classes Required: 1. CTripPage - Business entity for trip pages 2. CTripSnippet - Business entity for trip snippets 3. CTripSkeleton - Business entity for skeletons 4. CTripSnippetFile - Business entity for snippet files

Requirements: - Must extend TlinqEntity - Must implement Serializable - Platform-independent POJOs (no JPA annotations) - All fields use Java wrapper types (Integer, not int) - Include toString() implementation - Field names match database entity field names for mapping

Example Structure:

public class CTripPage extends TlinqEntity implements Serializable {
    private Integer pageId;
    private String pageName;
    private String pageDesc;

    // Getters and setters
    // toString() implementation
}


4.3 Entity Mapping Configuration

File: config/tourlinq-config.xml

Requirement: Map each canonical entity to its corresponding native entity with explicit field mappings.

Configuration Pattern:

<Entity name="TripPage"
        class="com.perun.tlinq.entity.trip.CTripPage"
        idField="pageId"
        defaultFactory="NTSServiceFactory">
    <EntityFactoryList>
        <Factory name="NTSServiceFactory"
                 nativeEntity="com.perun.tlinq.client.nts.db.trip.TrippageEntity">
            <ServiceList>
                <Service name="saveTripPage" action="create"/>
                <Service name="saveTripPage" action="update"/>
                <Service name="readTripPage" action="read"/>
                <Service name="readTripPage" action="search"/>
                <Service name="deleteTripPage" action="delete"/>
            </ServiceList>
            <FieldMappingList>
                <FieldMapping targetField="pageId" sourceField="pageId"
                              mapping="DirectMapping"/>
                <FieldMapping targetField="pageName" sourceField="pageName"
                              mapping="DirectMapping"/>
                <FieldMapping targetField="pageDesc" sourceField="pageDesc"
                              mapping="DirectMapping"/>
            </FieldMappingList>
        </Factory>
    </EntityFactoryList>
</Entity>

Requirements: - One <Entity> block per canonical entity - idField must match primary key field name - All entity fields must have corresponding <FieldMapping> - Use DirectMapping for one-to-one field mappings - Service names follow convention: save[EntityName], read[EntityName], delete[EntityName]

Entities to Configure: 1. TripPage (3 fields) 2. TripSnippet (15 fields) 3. TripSkeleton (4 fields) 4. TripSnippetFile (4 fields)


5. API Requirements

5.1 API Architecture

Base Path: /api/tripoffer Protocol: HTTP/HTTPS Method: POST (all endpoints) Content-Type: application/json Response Type: application/json


5.2 API Response Format

All API endpoints must return TlinqApiResponse format:

Success Response:

{
  "success": true,
  "data": { /* payload */ },
  "message": null,
  "errorCode": null
}

Error Response:

{
  "success": false,
  "data": null,
  "message": "Error description",
  "errorCode": "ERROR_CODE"
}


5.3 API Endpoints Specification

5.3.1 POST /api/tripoffer/page/list

Purpose: List all trip pages with pagination

Request Body:

{
  "session": "string (optional)",
  "page": 0,
  "pageSize": 10
}

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "pageId": 1,
      "pageName": "dubai-packages.html",
      "pageDesc": "Dubai vacation packages"
    }
  ]
}

Implementation: - Delegate to TripOfferFacade.listPages() - Extract session from header X-Auth-Request-Access-Token if not in body


5.3.2 POST /api/tripoffer/page/read

Purpose: Retrieve single trip page

Request Body:

{
  "session": "string",
  "pageId": 1
}

Response:

{
  "success": true,
  "data": {
    "pageId": 1,
    "pageName": "dubai-packages.html",
    "pageDesc": "Dubai vacation packages"
  }
}

Error Codes:

  • MISSING_PARAMETER: pageId not provided

  • ENTITY_NOT_FOUND: Page does not exist


5.3.3 POST /api/tripoffer/page/write

Purpose: Create or update trip page

Request Body:

{

  "session": "string",

  "pageId": 1,  // null for create

  "pageName": "dubai-packages.html",

  "pageDesc": "Dubai vacation packages"

}

Response:

{

  "success": true,

  "data": {

    "pageId": 1,  // generated for new records

    "pageName": "dubai-packages.html",

    "pageDesc": "Dubai vacation packages"

  }

}

Validation:

  • pageName required
  • pageDesc required

5.3.4 POST /api/tripoffer/page/delete

Purpose: Delete trip page Request Body:

{
  "session": "string",
  "pageId": 1
}

Response:

{
  "success": true,
  "data": null
}

Side Effects: All snippets with matching page_id are deleted


5.3.5 POST /api/tripoffer/snippet/list

Purpose: List snippets for a page

Request Body:

{

  "session": "string",

  "pageId": 1,

  "page": 0,

  "pageSize": 10

}

Response:

{

  "success": true,

  "data": [

    {

      "snippetId": 1,

      "pageId": 1,

      "code": "DXB-PKG-001",

      "title": "Dubai Premium Package",

      "price": "$599",

      "active": 1,

      // ... other 10 fields

    }

  ]

}

5.3.6 POST /api/tripoffer/snippet/read

Purpose: Retrieve single snippet

Request Body:

{

  "session": "string",

  "snippetId": 1

}

Response: Complete snippet record with all 15 fields


5.3.7 POST /api/tripoffer/snippet/write

Purpose: Create or update snippet

Request Body: All 15 snippet fields

Validation:

  • code required and unique

  • title required

  • pageId must reference existing page


5.3.8 POST /api/tripoffer/snippet/delete

Purpose: Delete snippet

Request Body:

{

  "session": "string",

  "snippetId": 1

}

5.3.9 POST /api/tripoffer/skeleton/list

Purpose: List skeleton templates

Request Body:

{

  "session": "string"

}

Response:

{

  "success": true,

  "data": [

    {

      "skeletonId": 1,

      "skeletonName": "Standard Destination Page",

      "skeletonDesc": "Standard template",

      "skeletonFile": "/path/to/skeleton.html"

    }

  ]

}

5.3.10 POST /api/tripoffer/snippetfile/list

Purpose: List snippet templates

Request Body:

{

  "session": "string"

}

Response: Similar to skeleton/list


5.3.11 POST /api/tripoffer/page/generate

Purpose: Generate HTML page with snippets

Request Body:

{

  "session": "string",

  "pageId": 1,

  "skeletonId": 1,

  "snippetFileId": 1

}

Response:

{

  "success": true,

  "data": {

    "success": true,

    "fileName": "dubai-packages.html",

    "content": "<html>...</html>"

  }

}

Processing: See FR-GENERATE-001 for detailed algorithm


5.4 API Implementation Requirements

Class: com.perun.tlinq.api.TripOfferApi

Pattern: Endpoint methods wrap doXXX() implementation methods

Structure:

@POST

@Path("/page/list")

public Response listPages(Map reqData, @Context HttpHeaders headers) {

    try {

        session = ApiUtil.gmp(reqData, "session", String.class, false);

        // Extract from header if needed

        ar = doListPages(reqData);

    } catch (TlinqClientException e) {

        ar = new TlinqApiResponse(e.getErrorCode(), e.getMessage());

    }

    return Response.ok(ar).build();

}



private TlinqApiResponse doListPages(Map reqData) throws TlinqClientException {

    // Extract parameters

    // Create facade instance

    // Delegate to facade

    // Return response

}

Requirements:

  • All endpoints use @POST

  • All consume and produce application/json

  • Extract session from header X-Auth-Request-Access-Token as fallback

  • Comprehensive logging: BEGIN/END APICALL with session

  • Exception handling for TlinqClientException and general exceptions

  • All business logic delegated to TripOfferFacade


6. User Interface Requirements

6.1 Page Layout Requirements

File: tqweb-adm/offerman.html


6.1.1 Overall Layout

Framework: Foundation CSS 6.8

Grid System: Foundation grid with responsive classes

Structure:

┌─────────────────────────────────────────────┐

│          Page Header                        │

│          "Trip Offer Management"            │

├─────────────┬───────────────────────────────┤

│  Trip       │  Trip Snippets                │

│  Pages      │  (for selected page)          │

│  (1/3)      │  (2/3)                        │

│             │                               │

│  [Table]    │  [Table]                      │

│  [Paging]   │  [Paging]                     │

│             │                               │

│             │  HTML Generation Section      │

│             │  [Skeleton Dropdown]          │

│             │  [Snippet File Dropdown]      │

│             │  [Generate Button]            │

└─────────────┴───────────────────────────────┘

Responsive Behavior:

  • Desktop: 1/3 - 2/3 split

  • Tablet: Equal columns

  • Mobile: Stacked vertically


6.1.2 Trip Pages Panel

Location: Left panel (1/3 width on desktop)

Components:

  1. Header Section:

    • Title: "Trip Pages" with icon

    • "New Page" button (success/green)

  2. Table Columns:

    • Page ID

    • Page Name & Description (stacked)

    • Actions (Edit, Delete buttons)

  3. Pagination Controls:

    • Info text: "Showing X-Y"

    • Previous button

    • Next button

Visual Requirements:

  • Selected row highlighted with purple left border

  • Hover effect on rows

  • Action buttons: pencil icon (edit), X icon (delete)

  • Empty state: "No pages found" message


6.1.3 Trip Snippets Panel

Location: Right panel (2/3 width on desktop)

Components:

  1. Header Section:

    • Title: "Trip Snippets" with icon

    • "New Snippet" button (success/green, disabled if no page selected)

  2. Table Columns:

    • Snippet ID

    • Code

    • Title

    • Price

    • Active Status (badge: green for active, red for inactive)

    • Actions (Edit, Delete)

  3. Pagination Controls: Same as pages panel

Visual Requirements:

  • Active badges: green background

  • Inactive badges: red background

  • Disabled state for "New Snippet" when no page selected

  • Empty state: "Select a page to view snippets" or "No snippets found for this page"


6.1.4 HTML Generation Section

Location: Below snippets table

Components:

  1. Skeleton Template Selector:

    • Label: "Skeleton Template"

    • Dropdown populated from /api/tripoffer/skeleton/list

    • Display: skeleton_name

  2. Snippet Template Selector:

    • Label: "Snippet Template"

    • Dropdown populated from /api/tripoffer/snippetfile/list

    • Display: snippet_file_name

  3. Generate Button:

    • Text: "Generate & Download"

    • Primary/purple button

    • Icon: download icon

    • Disabled if no page selected

Behavior:

  • Clicking generates HTML and triggers browser download

  • Shows loading indicator during generation

  • Success notification on completion


6.2 Modal Dialogs

6.2.1 Page Dialog (page_dlg)

Fields:

  • Page ID (hidden/readonly for edits)

  • Page Name (text input, required)

  • Page Description (textarea, required)

Buttons:

  • Save

  • Cancel

Validation:

  • Page name cannot be empty

6.2.2 Snippet Dialog (snippet_dlg)

Fields (15 fields matching CTripSnippet):

| Field | Input Type | Required |

|-------|------------|----------|

| Snippet ID | Hidden | Auto |

| Page ID | Hidden | Auto |

| Code | Text | Yes |

| Title | Text | Yes |

| Request Description | Textarea | No |

| Destination | Text | No |

| Image URL | Text | No |

| Image Title | Text | No |

| Tagline 1 | Text | No |

| Tagline 2 | Text | No |

| Price | Text | No |

| Short Description | Textarea | No |

| Description | Textarea | No |

| Inclusion HTML | Textarea | No |

| Active | Checkbox | No |

Layout: Two-column form for better space utilization

Validation:

  • Code required and unique

  • Title required


6.3 Design System Requirements

6.3.1 Color Palette

--primary-color: #362c5d;        /* Purple for primary actions */

--primary-dark: #2a2149;         /* Darker purple for hover */

--secondary-color: #FFC166;      /* Orange accent */

--success-color: #3adb76;        /* Green for success */

--warning-color: #ffae00;        /* Orange for warnings */

--alert-color: #cc4b37;          /* Red for errors/delete */

--light-bg: #f8f9fa;             /* Page background */

--border-color: #e1e4e8;         /* Borders */

6.3.2 Component Styling

Content Cards:

  • White background

  • Border radius: 8px

  • Box shadow: 0 2px 4px rgba(0,0,0,0.1)

  • Header with gradient background

Tables:

  • Header: Gray gradient background

  • Row hover: Light blue background

  • Selected row: Blue background with purple left border

  • Cell padding: 15px

Buttons:

  • Border radius: 6px

  • Font weight: 600

  • Hover: Slight upward movement with shadow

  • Primary: Purple background

  • Success: Green background

  • Alert: Red background

Form Inputs:

  • Border radius: 6px

  • Padding: 12px 15px

  • Focus: Purple border with shadow


6.4 JavaScript Module Requirements

File: tqweb-adm/js/modules/offerman.js

Module System: ES6 modules


6.4.1 Module State

export let currentPagePage = 0;

export let currentSnippetPage = 0;

export let selectedPageId = null;

export let selectedPageData = null;

export const pageSize = 10;



let pages = [];

let snippets = [];

let skeletons = [];

let snippetFiles = [];

let pageRecord = {};

let snippetRecord = {};

6.4.2 Exported Functions

Initialization:

  • initialize(): Load initial data, setup bindings

Page Management:

  • loadPages(page): Fetch and render pages

  • selectPage(e): Handle page row click

  • newPage(): Open create dialog

  • editPage(e): Open edit dialog

  • savePage(): Submit page data

  • deletePage(e): Delete page with confirmation

Snippet Management:

  • loadSnippets(page): Fetch and render snippets

  • newSnippet(): Open create dialog

  • editSnippet(e): Open edit dialog

  • saveSnippet(): Submit snippet data

  • deleteSnippet(e): Delete snippet with confirmation

Template Management:

  • loadSkeletons(): Populate skeleton dropdown

  • loadSnippetFiles(): Populate snippet file dropdown

HTML Generation:

  • generatePage(): Generate and download HTML

Pagination:

  • prevPagePages(): Previous page of pages

  • nextPagePages(): Next page of pages

  • prevPageSnippets(): Previous page of snippets

  • nextPageSnippets(): Next page of snippets


6.4.3 Data Binding

Requirements:

  • Use $util.setUpEntityData() to bind entity to form fields

  • Use $util.extractEntityData() to extract form data to entity

  • Form fields use data-entity-name and data-entity-field attributes

Example:

<input type="text" data-entity-name="pageRecord" data-entity-field="pageName" />

6.4.4 AJAX Requirements

Configuration:

  • URL: /api/tripoffer/*

  • Method: POST

  • Content-Type: application/json

  • Data: JSON.stringify(reqData)

Error Handling:

  • Display user-friendly notifications using $.notify()

  • Log errors to console

Success Handling:

  • Update UI

  • Show success notification

  • Close dialogs

  • Refresh data


7. Business Logic Requirements

7.1 Facade Architecture

Class: com.perun.tlinq.entity.trip.TripOfferFacade

Package: com.perun.tlinq.entity.trip

Module: tqapp

Extends: EntityFacade


7.2 Facade Responsibilities

  1. Business Logic: All validation and business rules

  2. Session Management: Handle session tokens

  3. Entity Operations: CRUD via EntityFacade base class

  4. Factory Integration: Use NTSServiceFactory

  5. Error Handling: Throw TlinqClientException with appropriate error codes


7.3 Constructor Requirements

public TripOfferFacade(String token) {

    super(TypeUtil.isEmptyString(token) ? sysSession : token);

}

Behavior:

  • If token is null/empty, use system session

  • System session loaded from tourlinq-config.xml


7.4 Validation Rules

7.4.1 Page Validation

Method: writePage(CTripPage page)

Rules:

  1. Page entity cannot be null → Error: MISSING_PARAMETER

  2. Page name cannot be null/empty → Error: MISSING_PARAMETER

  3. Page description cannot be null/empty (implicitly handled by DB constraint)


7.4.2 Snippet Validation

Method: writeSnippet(CTripSnippet snippet)

Rules:

  1. Snippet entity cannot be null → Error: MISSING_PARAMETER

  2. Code cannot be null/empty → Error: MISSING_PARAMETER

  3. Code must be unique → Database constraint violation

  4. Title cannot be null/empty → Error: MISSING_PARAMETER

  5. Page ID must reference existing page → Foreign key constraint


7.4.3 Generation Validation

Method: generateHtmlPage(Integer pageId, Integer skeletonId, Integer snippetFileId)

Rules:

  1. All three parameters required → Error: MISSING_PARAMETER

  2. Page must exist → Error: ENTITY_NOT_FOUND

  3. Skeleton must exist → Error: ENTITY_NOT_FOUND

  4. Snippet file must exist → Error: ENTITY_NOT_FOUND

  5. Skeleton file must be readable → Error: GENERAL with descriptive message

  6. Snippet file must be readable → Error: GENERAL with descriptive message


7.5 Pagination Logic

Implementation: In-memory pagination

if (page != null && pageSize != null) {

    int start = page * pageSize;

    int end = Math.min(start + pageSize, results.size());

    if (start < results.size()) {

        return results.subList(start, end);

    } else {

        return Collections.emptyList();

    }

}

Characteristics:

  • Zero-based page numbering

  • Returns empty list if start index exceeds result size

  • Does not use database-level pagination (LIMIT/OFFSET)


7.6 HTML Generation Algorithm

Method: generateHtmlPage()

Pseudocode:

1. VALIDATE inputs (page_id, skeleton_id, snippet_file_id)

2. READ trip_page record

3. READ skeleton record

4. READ snippet_file record

5. FETCH active snippets WHERE page_id = ? AND active = 1

6. READ skeleton HTML file from filesystem

7. READ snippet template HTML file from filesystem

8. INITIALIZE empty snippet_html string

9. FOR EACH active snippet:

   a. CLONE snippet template

   b. REPLACE all %%placeholder%% with snippet field values

   c. APPEND to snippet_html with newline

10. REPLACE "---INSERT SNIPPETS HERE---" in skeleton with snippet_html

11. RETURN { success: true, fileName: page.pageName, content: final_html }

Placeholder Replacement:

  • Use String.replace("%%field%%", nvl(snippet.getField()))

  • Null values converted to empty strings via nvl() helper


8. Non-Functional Requirements

8.1 Performance Requirements

NFR-PERF-001: API Response Time

  • 95th percentile: < 500ms for list operations

  • 95th percentile: < 200ms for read operations

  • 95th percentile: < 1000ms for write operations

  • 95th percentile: < 2000ms for HTML generation

NFR-PERF-002: Pagination

  • Support up to 10,000 records per entity without performance degradation

  • Page size configurable (default: 10)

NFR-PERF-003: Concurrent Users

  • Support at least 20 concurrent users without performance degradation

8.2 Scalability Requirements

NFR-SCALE-001: Database Indexes

  • All foreign keys indexed

  • Unique constraints on code fields

  • Primary keys auto-indexed

NFR-SCALE-002: Entity Caching

  • Leverage Hibernate second-level cache (if configured)

8.3 Usability Requirements

NFR-USE-001: Responsive Design

  • Functional on desktop (1920x1080)

  • Functional on tablet (768x1024)

  • Functional on mobile (375x667)

NFR-USE-002: User Feedback

  • All actions provide visual feedback (notifications, loading indicators)

  • Error messages are user-friendly and actionable

  • Confirmation dialogs for destructive actions

NFR-USE-003: Accessibility

  • Form labels associated with inputs

  • Keyboard navigation supported

  • Color contrast meets WCAG AA standards


8.4 Maintainability Requirements

NFR-MAINT-001: Code Organization

  • Clear separation of concerns (API → Facade → Entity → Database)

  • Follow established TQPRO patterns

  • Comprehensive inline documentation

NFR-MAINT-002: Logging

  • All API calls logged with session ID

  • Errors logged with stack traces

  • Use java.util.logging.Logger

NFR-MAINT-003: Configuration

  • Entity mappings in XML (not hardcoded)

  • Database sequences managed by Hibernate

  • Template file paths configurable via database


8.5 Reliability Requirements

NFR-REL-001: Data Integrity

  • Referential integrity enforced via foreign keys

  • Unique constraints prevent duplicate codes

  • Transactions ensure atomicity

NFR-REL-002: Error Recovery

  • Database errors caught and returned as API errors

  • File I/O errors handled gracefully

  • No partial updates (transaction rollback on error)


9. Architecture Requirements

9.1 Layered Architecture Compliance

Requirement: System must strictly follow TQPRO's layered architecture.

Layers (top to bottom):

  1. Presentation Layer: HTML + JavaScript

  2. API Layer: REST endpoints (TripOfferApi)

  3. Business Logic Layer: Facade (TripOfferFacade)

  4. Canonical Entity Layer: Platform-independent entities (CTripPage, etc.)

  5. Native Entity Layer: Database-specific entities (TrippageEntity, etc.)

  6. Database Layer: PostgreSQL

Rules:

  • No layer should skip the layer directly below it

  • API layer must not contain business logic

  • Canonical entities must not have JPA annotations

  • Configuration-driven mapping between canonical and native entities


9.2 Design Patterns

Pattern 1: Facade Pattern

  • TripOfferFacade provides simplified interface to complex subsystem

  • Encapsulates business logic and entity operations

Pattern 2: Data Transfer Object (DTO)

  • Canonical entities serve as DTOs between layers

  • Serializable for potential remote calls

Pattern 3: Template Method

  • HTML generation uses template method pattern

  • Skeleton provides structure, snippets fill in details

Pattern 4: Master-Detail

  • UI pattern for managing related entities

  • Page selection drives snippet display


9.3 Dependency Management

Module Structure:

  • tqapp: Contains facades and canonical entities

  • tqapi: Contains REST API endpoints

  • tqweb-adm: Contains HTML and JavaScript

  • config: Contains entity mappings

Dependencies:

  • tqapi depends on tqapp

  • tqapp depends on tqcommon (for EntityFacade)

  • All modules depend on config


10. Integration Requirements

10.1 Database Integration

Connection: Via Hibernate SessionFactory managed by TQPRO

Transaction Management: Container-managed (Jakarta EE)

Schema: nts schema in PostgreSQL


10.2 Session Management Integration

Authentication: TQPRO session tokens

Session Extraction:

  1. From request body session field

  2. From header X-Auth-Request-Access-Token (fallback)

  3. Use system session if neither present

Authorization: All API endpoints require valid session


10.3 Frontend Integration

Foundation CSS: Version 6.8.1 from CDN

jQuery: Required for AJAX and DOM manipulation

Utility Module: utilmod.js for data binding helpers


11. Security Requirements

11.1 Authentication

SEC-AUTH-001: All API endpoints require session token

SEC-AUTH-002: Invalid/expired sessions return error

SEC-AUTH-003: System session used only for background operations


11.2 Input Validation

SEC-VAL-001: All user inputs validated on server side

SEC-VAL-002: SQL injection prevented via Hibernate parameterized queries

SEC-VAL-003: XSS prevention in HTML generation (context: generated HTML is for offline use)


11.3 File System Security

SEC-FILE-001: Template file paths validated before access

SEC-FILE-002: File paths restricted to designated template directories

SEC-FILE-003: No user input directly used in file paths


11.4 Authorization

SEC-AUTHZ-001: Role-based access control (if implemented in TQPRO)

SEC-AUTHZ-002: Session validation on every API call


12. Testing Requirements

12.1 Unit Testing

Test Coverage:

  • Facade methods: All public methods

  • Validation logic: All validation rules

  • Placeholder replacement: Edge cases (null, empty, special chars)

Test Framework: JUnit 5

Mocking: Mockito for EntityFacade dependencies


12.2 Integration Testing

Database Tests:

  • CRUD operations for all entities

  • Foreign key constraints

  • Unique constraints

  • Cascade deletes (application-level)

API Tests:

  • All endpoints with valid inputs

  • Error scenarios (missing parameters, invalid IDs)

  • Session handling


12.3 Frontend Testing

Manual Testing:

  • All CRUD operations via UI

  • Pagination (forward/backward, edge cases)

  • Master-detail coordination

  • HTML generation and download

  • Responsive layout on different screen sizes

Browser Compatibility:

  • Chrome (latest)

  • Firefox (latest)

  • Safari (latest)

  • Edge (latest)


12.4 End-to-End Testing

Scenarios:

  1. Create page → Add snippets → Generate HTML → Verify output

  2. Edit page → Verify snippets retained

  3. Delete page → Verify snippets deleted

  4. Pagination → Verify data consistency

  5. Multiple users → Verify session isolation


13. Deployment and Configuration

13.1 Database Deployment

Steps:

  1. Execute doc/trip_offer_schema.sql

  2. Verify tables created in nts schema

  3. Insert sample skeleton and snippet file records

  4. Grant appropriate permissions to application user


13.2 Application Deployment

Modules to Deploy:

  • tqapp.jar: Contains facades and entities

  • tqapi.war: Contains REST endpoints

  • tqweb-adm: Static web resources

Configuration:

  • Update config/tourlinq-config.xml with entity mappings

  • Verify NTSServiceFactory configuration

  • Configure system session in properties


13.3 Template File Setup

Skeleton Templates:

  • Place HTML files in designated directory

  • Ensure files contain marker: ---INSERT SNIPPETS HERE---

  • Insert records in nts.trip_skeleton table

Snippet Templates:

  • Place HTML template files in designated directory

  • Use %%placeholder%% syntax for dynamic fields

  • Insert records in nts.trip_snippet_file table


14. Appendices

Appendix A: Error Codes

| Code | Description | HTTP Status |

|------|-------------|-------------|

| MISSING_PARAMETER | Required parameter not provided | 200 (in JSON) |

| ENTITY_NOT_FOUND | Entity does not exist | 200 (in JSON) |

| GENERAL | General error (see message) | 200 (in JSON) |

Note: All errors return HTTP 200 with error details in JSON body


Appendix B: Sample Data

Sample Page:

INSERT INTO nts.trip_page (page_id, page_name, page_desc)

VALUES (1, 'dubai-packages.html', 'Dubai vacation packages and tour offers');

Sample Snippet:

INSERT INTO nts.trip_snippet (

  snippet_id, page_id, code, title, price, active,

  description, inclusionhtml

)

VALUES (

  1, 1, 'DXB-PKG-001', 'Dubai Premium Package', '$599', 1,

  'Explore magnificent Dubai with our premium package',

  '<ul><li>4 nights accommodation</li><li>Daily breakfast</li></ul>'

);

Appendix C: File Structure

tqpro/

├── config/

│   └── tourlinq-config.xml           (Entity mappings)

├── doc/

│   └── trip_offer_schema.sql         (Database schema)

├── tqapp/src/main/java/com/perun/tlinq/

│   ├── client/nts/db/trip/           (Hibernate entities)

│   │   ├── TrippageEntity.java

│   │   ├── TripsnippetEntity.java

│   │   ├── TripskeletonEntity.java

│   │   └── TripsnippetfileEntity.java

│   └── entity/trip/                  (Canonical entities & facade)

│       ├── CTripPage.java

│       ├── CTripSnippet.java

│       ├── CTripSkeleton.java

│       ├── CTripSnippetFile.java

│       └── TripOfferFacade.java

├── tqapi/src/main/java/com/perun/tlinq/api/

│   └── TripOfferApi.java             (REST API)

└── tqweb-adm/

    ├── offerman.html                 (UI)

    └── js/modules/

        └── offerman.js               (Frontend logic)

Appendix D: Placeholder Reference

Complete list of supported placeholders in snippet templates:

| Placeholder | Field | Example |

|-------------|-------|---------|

| %%code%% | code | DXB-PKG-001 |

| %%title%% | title | Dubai Premium Package |

| %%request_desc%% | request_desc | I would like to inquire about... |

| %%destination%% | destination | Dubai |

| %%image%% | image | images/dubai.jpg |

| %%imagetitle%% | imagetitle | Dubai Skyline |

| %%tagline1%% | tagline1 | LIMITED OFFER |

| %%tagline2%% | tagline2 | Save 20% |

| %%price%% | price | $599 per person |

| %%shortdesc%% | shortdesc | 5 Days / 4 Nights |

| %%description%% | description | Explore magnificent Dubai... |

| %%inclusionhtml%% | inclusionhtml | <ul><li>...</li></ul> |


Appendix E: Revision History

| Version | Date | Author | Changes |

|---------|------|--------|---------|

| 1.0 | 2024-11-12 | Development Team | Initial specification based on implementation |


End of Document