Owner: Amoghavarsh Desai 
Reviewers: Dr Mithun James 
Status: Draft v3
Last updated: 28 Aug 2025 (Asia/Kolkata)
Ticket: https://support.piramalfoundation.org/jira/browse/AMM-1337


1) Context & Rationale

AMRIT (Accessible Medical Records via Integrated Technologies) spans multiple service lines—1097, 104, HWC, MMU, TM, and ECD. We need a shared, reusable Feedback capability that can be embedded across all these service lines and also works post‑logout. The solution should unify collection and privacy access while minimizing coupling to individual services.

Key drivers


2) High‑Level Solution Overview

We add a standalone UI package in Common‑UI and expose ingestion + category config endpoints in common-api. Every service line integrates the widget(s). Analytics is handled by BIDA by reading from the platform tables; the platform does not run aggregation jobs.

[Service Lines: 1097 | 104 | HWC | MMU | TM | ECD]
         │
         └──> Reusable Feedback Launcher + Dialog (Common‑UI)
                              │
                              ▼
                 common-api (ingestion + category config)
        ┌──────────────────────┬────────────────────────────────────────┐
        │/common-api/feedback  │  /common-api/feedback/categories (GET)
        └─────────────────────────────┴─────────────────────────────────┘
                                      │                                         
                               MySQL (source)                      
                            - feedback (content)                   
                         - feedback_category (config)

3) Requirements → Capabilities Mapping

RequirementCapability
Submissions with rating, category, commentAngular dialog + POST /common-api/feedback; validation
Anonymous or identifiedToggle in UI; platform stores internal user_ref_id only if not anonymous
Submit after logoutPublic /feedback route (Common‑UI), open endpoint with rate limiting
Security & privacyhashed-IP rate limiting in Redis

4) Frontend Design (Common‑UI )

4.1 Packaging & Integration

4.2 Components

4.3 Services & Interceptors


5) Backend Design (common-api)

5.1 Modules

5.2 Endpoints

Public (no auth required)

5.3 Abuse Protection & Rate Limiting

Since the APIs are open we need to secure them from abuse so here are the steps we plan to take to prevent any attack


6) Database Design (MySQL)

6.1 Tables (MVP)


-- Categories


CREATE TABLE m_feedback_category (
  CategoryID CHAR(36) PRIMARY KEY,
  Label VARCHAR(128) NOT NULL,
  Scope ENUM('GLOBAL','1097','104','HWC','MMU','TM','ECD') NOT NULL DEFAULT 'GLOBAL',
  Active BIT(1) NOT NULL DEFAULT b'1',
  Value
  CreatedAt DATETIME NOT NULL,
  UpdatedAt DATETIME NOT NULL
) ;


-- Feedback (content)


CREATE TABLE m_platform_feedback (
  FeedbackID CHAR(36) PRIMARY KEY,
  CreatedAt DATETIME NOT NULL,
  UpdatedAt DATETIME NOT NULL,
  Rating TINYINT NOT NULL CHECK (Rating BETWEEN 1 AND 5),
  CategoryID CHAR(36) NOT NULL,
  Comment TEXT NOT NULL,
  ServiceLine ENUM('1097','104','HWC','MMU','TM','ECD') NOT NULL,
  IsAnonymous BIT(1) NOT NULL DEFAULT b'1',
  UserID INT,
  CONSTRAINT fk_platform_feedback_user FOREIGN KEY (UserID) REFERENCES m_user(UserID),
  CONSTRAINT fk_platform_feedback_category FOREIGN KEY (CategoryID) REFERENCES m_feedback_category(CategoryID)
);


CREATE INDEX ix_feedback_CreatedAt   ON m_platform_feedback (CreatedAt);
CREATE INDEX ix_feedback_ServiceLine ON m_platform_feedback (ServiceLine);
CREATE INDEX ix_feedback_CategoryID  ON m_platform_feedback (CategoryID);
CREATE INDEX ix_feedback_IsAnonymous ON m_platform_feedback (IsAnonymous);



7) API Contract (OpenAPI sketch)

paths:
  /common-api/feedback:
    post:
      summary: Submit feedback (public, no role required)
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FeedbackRequest'
      responses:
        '201': { description: FeedbackResponse }

  /common-api/feedback/categories:
    get: { summary: List categories }


components:
  schemas:
    FeedbackRequest:
      type: object
      properties:
        rating:     { type: integer, minimum: 1, maximum: 5 }
        categoryId: { type: string, format: uuid }
        comment:    { type: string, maxLength: 2000 }
        isAnonymous:{ type: boolean }
        serviceLine:
          type: string
          enum: ['1097','104','HWC','MMU','TM','ECD']
      userId:     { type: integer, description: "Internal m_user.UserID; required when isAnonymous=false" }
      required:
        - rating
        - categoryId
        - comment
        - isAnonymous
        - serviceLine
      oneOf:
        # Case 1: Anonymous submission -> userId MUST be absent
        - properties:
            isAnonymous:
              type: boolean
              enum: [true]
          not:
            required: [userId]
        # Case 2: Identified submission -> userId is REQUIRED
        - properties:
            isAnonymous:
              type: boolean
              enum: [false]
          required: [userId]
      examples:
        anonymous:
          summary: Anonymous feedback
          value:
            rating: 5
            categoryId: "2b1c2c7f-7f1e-4c6f-9f2a-6b5f3a0c1e9a"
            comment: "Great UX on TM."
            isAnonymous: true
            serviceLine: "TM"
        identified:
          summary: Identified feedback
          value:
            rating: 4
            categoryId: "2b1c2c7f-7f1e-4c6f-9f2a-6b5f3a0c1e9a"
            comment: "Queue times improved."
            isAnonymous: false
            userId: 12345
            serviceLine: "TM"