The UI presented on this page is a demo/development sandbox built for API exploration and component validation. It is not a design specification. For production implementation, follow the UX/UI patterns and component library agreed with the QDF digital team.
My Applications
Browse and filter all your licensing applications from the Ministry of Communications portal.
- Click the button below to visit a MoC service page
- Log in with your MoC credentials on that page
- Click the ← back button in the Hukoomi header — your applications will load automatically
Important Notices
The visual layout of this page is a developer sandbox only. It is provided to help engineers explore the MoC API, validate authentication flows, and test bilingual rendering. Do not use the UI patterns shown here as a design reference for production Sitecore components. All UX/UI decisions must align with the patterns, component library, and design system agreed with the QDF digital and design team.
This guide was produced by AI (Claude / Anthropic) by observing and reverse-engineering the API behaviour of the live MoC production portal — based on network traffic analysis, cookie inspection, and request/response observation. It is not derived from an official MoC API specification.
Field names, status codes, parameter conventions, and behaviours described here reflect the portal at the time of analysis and may change without notice. The MoC backend does not publish a versioned public API contract.
Areas of uncertainty include: the complete set of valid FkStatusID values, full list of RequestType variants for business accounts, and exact error response formats when the session expires.
For authoritative specifications, schema changes, or any integration support beyond this guide, contact the MoC IT team directly.
Overview
The Ministry of Communications (MoC) portal exposes a general-purpose ASP.NET HTTP handler (Common.ashx) as an internal API gateway. The GetAllRequests action returns all licensing applications for an authenticated applicant including workflow status, submission date, and issued licence number.
Because the Sitecore portal and the MoC portal are served from the same domain via the QDF edge proxy (/entity-services/moc/…), the browser automatically includes MoC session cookies — no CORS configuration or server-side relay is required.
Key facts
- Transport:
POST— all parameters in the query string; body is always{}. - Auth: cookie-based session (
credentials:'include') plusX-Auth-Tokenheader from thetokencookie. - Login detection: check for the
ApplicantQIDcookie — only present when a MoC session is active. - Data strategy: fetch all records at once (
PageSize=500), filter and paginate client-side byFkStatusID. - Serialisation quirk:
undefinedvalues must be sent as the literal stringundefined, not omitted. StandardURLSearchParamsdrops them silently — use the helper in the Sample Code tab. - Bilingual: every response includes both
RequestTypeEn/RequestTypeAr,StatusEn/StatusAr, andActivityEn/ActivityAr. Select the appropriate field based on the user's language preference.
Authentication
token, ApplicantQID, RoleName, ApplicantName, EstablishmentID) on the shared proxy domain.ApplicantQID to confirm session, calls API with credentials:'include'./__wwwnas/.Session cookies set by MoC on login
| Cookie | Used as | Notes |
|---|---|---|
token | X-Auth-Token header | Primary API auth credential |
ApplicantQID | ApplicantQID param | Qatar National ID; also used to detect active session |
RoleName | RoleName param | Applicant (individual) or Establishment (business) |
ApplicantName | Display name | Human-readable name of authenticated user |
EstablishmentID | EstablishmentID param | CR number — present only for business accounts |
API Reference
Proxy endpoint (use from Sitecore):POST /entity-services/moc/Departments/Common/Common.ashx?{querystring}
Query String Parameters
| Parameter | Type | Description | |
|---|---|---|---|
action | req | string | Always GetAllRequests |
RequestType | req | string | ApplicantRequests (individual) or EstablishmentRequests (business) |
ApplicantQID | req | string | Qatar National ID from ApplicantQID cookie |
RoleName | opt | string | Applicant (default) or Establishment |
EstablishmentID | opt | string | CR number; pass "" for individuals |
FkStatusID | opt | integer | Filter by status. Pass literal string undefined for all records. Do not omit. |
PageSize | req | integer | Use 500 to fetch all, then paginate client-side |
PageNumber | req | integer | 1-based. Use 1 when fetching all records |
OrderBy | opt | string | CreationDate |
SortDir | opt | string | ASC or DESC |
Request Headers
| Header | Value | Notes |
|---|---|---|
Content-Type | application/json | Required by the ASP.NET handler |
X-Requested-With | XMLHttpRequest | Required — enables JSON response path |
X-Auth-Token | Value of token cookie | Required when authenticated |
Request Body
Always send an empty JSON object: {}
Response Structure
{
"Requests": [
{
"ID": 12345,
"ApplicationNumber": "MOC-2024-001234",
"RequestTypeEn": "License Request to Operate Commercial Printer",
"RequestTypeAr": "\u0637\u0644\u0628 \u062a\u0631\u062e\u064a\u0635 \u0644\u062a\u0634\u063a\u064a\u0644 \u0645\u0637\u0628\u0639\u0629 \u062a\u062c\u0627\u0631\u064a\u0629",
"ActivityEn": "Press & Publications",
"ActivityAr": "\u0627\u0644\u0635\u062d\u0627\u0641\u0629 \u0648\u0627\u0644\u0646\u0634\u0631",
"FkStatusID": 10,
"StatusEn": "Approved",
"StatusAr": "\u0645\u0648\u0627\u0641\u0642 \u0639\u0644\u064a\u0647",
"Date": "2024-03-15T00:00:00",
"ApprovedLicense": "LIC-2024-5678"
}
]
}
Response Fields
| Field | Type | Description |
|---|---|---|
ID | integer | Internal MoC identifier |
ApplicationNumber | string | Human-readable reference shown to applicant |
RequestTypeEn / Ar | string | Service name in English / Arabic |
ActivityEn / Ar | string | Service category in English / Arabic |
FkStatusID | integer | Numeric status code — see Status Codes tab |
StatusEn / Ar | string | Localised status label — prefer this over hard-coded labels |
Date | ISO 8601 | Submission date (YYYY-MM-DDT00:00:00) |
ApprovedLicense | string|null | Licence number when FkStatusID===10; null otherwise |
Application Status Reference
Use FkStatusID=undefined to fetch all statuses at once and filter client-side — more efficient than one API call per status. The Component Preview column shows exactly how each status renders in the live My Applications dashboard.
| FkStatusID | StatusEn / StatusAr | Component Preview | Meaning |
|---|---|---|---|
0 |
Draft مسودة |
✏️ Draft |
Saved but not yet submitted |
1 |
Under Review قيد المراجعة |
🔍 Under Review |
Being assessed by MoC |
2 |
Awaiting Revision بانتظار التعديل |
⚠️ Awaiting Revision |
Returned for correction |
3 |
In Progress قيد التنفيذ |
⏳ In Progress |
Approved; final processing underway |
10 |
Approved موافق عليه |
✅ Approved |
Licence issued — ApprovedLicense populated |
11 |
Rejected مرفوض |
❌ Rejected |
Application declined |
630 |
Cancelled ملغي |
⊘ Cancelled |
Withdrawn or administratively cancelled |
Status badge HTML pattern
This is the exact markup rendered by the live My Applications component for each status. Use the same classes in your Sitecore component to guarantee visual consistency.
<!-- Status icon + label (matches live My Applications component) -->
<div class="item-icon-wrap ic-{statusId}">{emoji}</div>
<span class="item-status-label st-{statusId}">{StatusEn or StatusAr}</span>
<!-- Status ID → emoji mapping (mirrors STATUS_META in dashboard JS) -->
<!-- 0→✏️ 1→🔍 2→⚠️ 3→⏳ 10→✅ 11→❌ 630→⊘ default→• -->
<!-- CSS classes (from demo-portal.html / your shared stylesheet) -->
.ic-0 { background: #f3f4f6; } .st-0 { color: #6b7280; }
.ic-1 { background: #eff6ff; } .st-1 { color: #1d4ed8; }
.ic-2 { background: #fffbeb; } .st-2 { color: #b45309; }
.ic-3 { background: #f0fdfa; } .st-3 { color: #0f766e; }
.ic-10 { background: #f0fdf4; } .st-10 { color: #15803d; }
.ic-11 { background: #fef2f2; } .st-11 { color: #b91c1c; }
.ic-630 { background: #f9fafb; } .st-630 { color: #6b7280; }
The API may return FkStatusID values not listed above. Always implement a neutral fallback (ic-def / st-def) — never treat this set as exhaustive.
Sample Implementation — Sitecore Portal
Drop these into a Sitecore component or shared JS bundle. Works from any page on the same proxy domain (hukoomi.gov.qa / web-*.qdf.gov.qa) — no server-side relay needed.
1 — Helpers: cookie reader, auth headers, query-string serialiser
function getCookie(name) {
var m = document.cookie.match(new RegExp('(?:^|;\\s*)' + name + '=([^;]*)'));
return m ? decodeURIComponent(m[1]) : null;
}
function mocHeaders() {
var h = { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' };
var token = getCookie('token');
if (token) h['X-Auth-Token'] = token;
return h;
}
// AngularJS $http convention: null/undefined → literal string "undefined"
// Standard URLSearchParams silently drops undefined — use this instead.
function mocQs(params) {
return Object.keys(params).map(function (k) {
var v = params[k];
return encodeURIComponent(k) + '=' +
(v === null || v === undefined ? 'undefined' : encodeURIComponent(v));
}).join('&');
}
2 — Fetch all applications (language-aware)
var MOC_API = '/entity-services/moc/Departments/Common/Common.ashx';
function fetchMoCApplications() {
var qid = getCookie('ApplicantQID');
if (!qid) return Promise.resolve([]);
var qs = mocQs({
action: 'GetAllRequests',
RequestType: 'ApplicantRequests',
ApplicantQID: qid,
RoleName: getCookie('RoleName') || 'Applicant',
EstablishmentID: getCookie('EstablishmentID') || '',
FkStatusID: undefined, // sends "undefined" → API returns all statuses
PageSize: 500,
PageNumber: 1,
OrderBy: 'CreationDate',
SortDir: 'DESC'
});
return fetch(MOC_API + '?' + qs, {
method: 'POST', credentials: 'include', headers: mocHeaders(), body: '{}'
})
.then(function (r) { if (!r.ok) throw new Error('API ' + r.status); return r.json(); })
.then(function (d) { return d.Requests || []; });
}
// Language-aware field selection
// lang: 'en' | 'ar' (read from __qdf_lang cookie or user preference)
function appLabel(app, lang) {
return {
service: lang === 'ar' ? (app.RequestTypeAr || app.RequestTypeEn) : app.RequestTypeEn,
activity: lang === 'ar' ? (app.ActivityAr || app.ActivityEn) : app.ActivityEn,
status: lang === 'ar' ? (app.StatusAr || app.StatusEn) : app.StatusEn
};
}
3 — Render logged-in user name & QID
// Populate the user identity banner on page load.
// Call after confirming ApplicantQID cookie is present.
function renderUserCard(qid, name, role) {
var el = document.getElementById('moc-user-card');
if (!el) return;
// Derive initials from display name (up to 2 chars), fallback to last 2 digits of QID
var initials = (name || '').trim()
.split(/\s+/).map(function (w) { return w[0] || ''; }).join('').slice(0, 2).toUpperCase()
|| qid.slice(-2);
// Mask QID: show last 4 digits only — e.g. ●●●●●●●1234
var maskedQid = qid.slice(0, -4).replace(/./g, '●') + qid.slice(-4);
el.style.display = '';
el.querySelector('.moc-user-avatar').textContent = initials;
el.querySelector('.moc-user-name').textContent = name || 'Applicant';
el.querySelector('.moc-user-qid').textContent = 'QID: ' + maskedQid;
el.querySelector('.moc-user-role').textContent = role || 'Applicant';
}
// HTML template for the user card (place before the dashboard container):
/*
<div id="moc-user-card" style="display:none" class="moc-user-card">
<div class="moc-user-avatar"></div>
<div class="moc-user-info">
<div class="moc-user-name"></div>
<div class="moc-user-meta">
<span class="moc-user-qid"></span>
<span class="moc-user-role"></span>
</div>
</div>
<div class="moc-session-dot"></div>
</div>
*/
4 — Render a status badge (matches live component)
// STATUS_META: maps FkStatusID → emoji + CSS class suffixes
// These class names and colours are defined in the shared portal stylesheet.
var STATUS_META = {
0: { emoji: '✏️', ic: 'ic-0', st: 'st-0' }, // Draft
1: { emoji: '🔍', ic: 'ic-1', st: 'st-1' }, // Under Review
2: { emoji: '⚠️', ic: 'ic-2', st: 'st-2' }, // Awaiting Revision
3: { emoji: '⏳', ic: 'ic-3', st: 'st-3' }, // In Progress
10: { emoji: '✅', ic: 'ic-10', st: 'st-10' }, // Approved
11: { emoji: '❌', ic: 'ic-11', st: 'st-11' }, // Rejected
630: { emoji: '⊘', ic: 'ic-630', st: 'st-630' }, // Cancelled
};
function statusMeta(id) {
return STATUS_META[+id] || { emoji: '•', ic: 'ic-def', st: 'st-def' };
}
// Render a status cell into a container element:
// <div class="item-icon-wrap {ic}">{emoji}</div>
// <span class="item-status-label {st}">{label}</span>
function renderStatusCell(container, app, lang) {
var meta = statusMeta(app.FkStatusID);
var label = lang === 'ar' ? (app.StatusAr || app.StatusEn) : (app.StatusEn || '—');
var icon = document.createElement('div');
icon.className = 'item-icon-wrap ' + meta.ic;
icon.textContent = meta.emoji;
var badge = document.createElement('span');
badge.className = 'item-status-label ' + meta.st;
badge.textContent = label;
container.appendChild(icon);
container.appendChild(badge);
}
5 — Login detection & conditional rendering
document.addEventListener('DOMContentLoaded', function () {
var lang = getCookie('__qdf_lang') || 'en';
var qid = getCookie('ApplicantQID');
var name = getCookie('ApplicantName') || 'Applicant';
if (!qid) {
document.getElementById('moc-login-prompt').style.display = 'block';
return;
}
document.getElementById('moc-dashboard').style.display = 'block';
document.getElementById('moc-user-name').textContent = name;
fetchMoCApplications().then(function (apps) {
// Filter client-side
var approved = apps.filter(function (a) { return +a.FkStatusID === 10; });
var pending = apps.filter(function (a) { return +a.FkStatusID === 1; });
// Render with correct language
apps.forEach(function (app) {
var labels = appLabel(app, lang);
var date = (app.Date || '').split('T')[0];
console.log(app.ApplicationNumber, labels.service, labels.status, date);
});
}).catch(function (err) {
console.error('MoC API failed:', err);
});
});
// Works identically after NAS SSO go-live — same cookies, zero code changes.
NAS SSO sets the exact same MoC session cookies as direct login. All snippets above continue to work without modification once SSO is enabled.
Local Development & API Mocking
The MoC portal does not provide a lower-environment (dev/test/staging) instance accessible to QDF. All real API calls only work against the MoC production environment after a user has authenticated. The approaches below let you build and test the Sitecore integration locally without that dependency.
Option 1 — Browser DevTools Network Override (quickest)
Chrome and Edge let you override a URL response locally with a static file — no code changes needed.
- Open DevTools → Network tab → right-click the
Common.ashxrequest → Override content. - Paste the mock JSON (see below) into the editor and save.
- Reload the page — DevTools serves the static file instead of hitting the real endpoint.
Also useful: open DevTools → Application → Cookies and manually set ApplicantQID, token, RoleName to any values — this simulates a logged-in MoC session without needing to authenticate.
Option 2 — Mock Service Worker (msw)
For Sitecore component development, Mock Service Worker intercepts fetch/XHR in the browser without a server. Install once, declare handlers per URL pattern.
// handlers.js (msw v2)
import { http, HttpResponse } from 'msw';
import mockApps from './moc-mock-data.json';
export const handlers = [
http.post('*/Common.ashx', ({ request }) => {
const url = new URL(request.url);
const action = url.searchParams.get('action');
if (action !== 'GetAllRequests') return;
// Simulate empty response when ApplicantQID is missing
const qid = url.searchParams.get('ApplicantQID');
if (!qid || qid === 'undefined') {
return HttpResponse.json({ Requests: [] });
}
return HttpResponse.json(mockApps);
}),
];
// browser.js (msw v2 setup)
import { setupWorker } from 'msw/browser';
import { handlers } from './handlers';
export const worker = setupWorker(...handlers);
// In your app entry point (only in dev):
if (process.env.NODE_ENV === 'development') {
worker.start({ onUnhandledRequest: 'bypass' });
}
Option 3 — Local Node.js Mock Server
A minimal Express server that returns mock data. Run alongside your local Sitecore dev server and proxy the API path to it.
// mock-moc-server.js (Node 18+, no dependencies)
import { createServer } from 'http';
import { readFileSync } from 'fs';
const mockData = JSON.parse(readFileSync('./moc-mock-data.json', 'utf8'));
createServer((req, res) => {
if (req.method === 'POST' && req.url.includes('Common.ashx')) {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify(mockData));
return;
}
res.writeHead(404);
res.end();
}).listen(3001, () => console.log('MoC mock server on http://localhost:3001'));
// Run: node mock-moc-server.js
// Then proxy /entity-services/moc → http://localhost:3001 in your dev config.
Mock Data File (moc-mock-data.json)
A representative dataset covering all known FkStatusID values — paste into any of the approaches above.
{
"Requests": [
{
"ID": 1001,
"ApplicationNumber": "MOC-2024-001001",
"RequestTypeEn": "License Request to Operate Commercial Printer",
"RequestTypeAr": "\u0637\u0644\u0628 \u062a\u0631\u062e\u064a\u0635 \u0644\u062a\u0634\u063a\u064a\u0644 \u0645\u0637\u0628\u0639\u0629 \u062a\u062c\u0627\u0631\u064a\u0629",
"ActivityEn": "Press & Publications",
"ActivityAr": "\u0627\u0644\u0635\u062d\u0627\u0641\u0629 \u0648\u0627\u0644\u0646\u0634\u0631",
"FkStatusID": 10,
"StatusEn": "Approved",
"StatusAr": "\u0645\u0648\u0627\u0641\u0642 \u0639\u0644\u064a\u0647",
"Date": "2024-01-15T00:00:00",
"ApprovedLicense": "LIC-2024-5001"
},
{
"ID": 1002,
"ApplicationNumber": "MOC-2024-001002",
"RequestTypeEn": "Media Production Company Registration",
"RequestTypeAr": "\u062a\u0633\u062c\u064a\u0644 \u0634\u0631\u0643\u0629 \u0625\u0646\u062a\u0627\u062c \u0625\u0639\u0644\u0627\u0645\u064a",
"ActivityEn": "Media Production",
"ActivityAr": "\u0627\u0644\u0625\u0646\u062a\u0627\u062c \u0627\u0644\u0625\u0639\u0644\u0627\u0645\u064a",
"FkStatusID": 1,
"StatusEn": "Under Review",
"StatusAr": "\u0642\u064a\u062f \u0627\u0644\u0645\u0631\u0627\u062c\u0639\u0629",
"Date": "2024-03-10T00:00:00",
"ApprovedLicense": null
},
{
"ID": 1003,
"ApplicationNumber": "MOC-2024-001003",
"RequestTypeEn": "Journalist Accreditation Renewal",
"RequestTypeAr": "\u062a\u062c\u062f\u064a\u062f \u0627\u0639\u062a\u0645\u0627\u062f \u0635\u062d\u0641\u064a",
"ActivityEn": "Press & Publications",
"ActivityAr": "\u0627\u0644\u0635\u062d\u0627\u0641\u0629 \u0648\u0627\u0644\u0646\u0634\u0631",
"FkStatusID": 2,
"StatusEn": "Awaiting Revision",
"StatusAr": "\u0628\u0627\u0646\u062a\u0638\u0627\u0631 \u0627\u0644\u062a\u0639\u062f\u064a\u0644",
"Date": "2024-03-22T00:00:00",
"ApprovedLicense": null
},
{
"ID": 1004,
"ApplicationNumber": "MOC-2024-001004",
"RequestTypeEn": "Satellite Broadcasting Permit",
"RequestTypeAr": "\u062a\u0635\u0631\u064a\u062d \u0628\u062b \u0641\u0636\u0627\u0626\u064a",
"ActivityEn": "Broadcasting",
"ActivityAr": "\u0627\u0644\u0628\u062b \u0627\u0644\u062a\u0644\u0641\u0632\u064a\u0648\u0646\u064a",
"FkStatusID": 3,
"StatusEn": "In Progress",
"StatusAr": "\u0642\u064a\u062f \u0627\u0644\u062a\u0646\u0641\u064a\u0630",
"Date": "2024-02-05T00:00:00",
"ApprovedLicense": null
},
{
"ID": 1005,
"ApplicationNumber": "MOC-2023-000905",
"RequestTypeEn": "Advertising Agency License",
"RequestTypeAr": "\u062a\u0631\u062e\u064a\u0635 \u0648\u0643\u0627\u0644\u0629 \u0625\u0639\u0644\u0627\u0646\u064a\u0629",
"ActivityEn": "Advertising",
"ActivityAr": "\u0627\u0644\u0625\u0639\u0644\u0627\u0646",
"FkStatusID": 11,
"StatusEn": "Rejected",
"StatusAr": "\u0645\u0631\u0641\u0648\u0636",
"Date": "2023-11-18T00:00:00",
"ApprovedLicense": null
},
{
"ID": 1006,
"ApplicationNumber": "MOC-2023-000810",
"RequestTypeEn": "Digital Platform Registration",
"RequestTypeAr": "\u062a\u0633\u062c\u064a\u0644 \u0645\u0646\u0635\u0629 \u0631\u0642\u0645\u064a\u0629",
"ActivityEn": "Digital Media",
"ActivityAr": "\u0627\u0644\u0625\u0639\u0644\u0627\u0645 \u0627\u0644\u0631\u0642\u0645\u064a",
"FkStatusID": 630,
"StatusEn": "Cancelled",
"StatusAr": "\u0645\u0644\u063a\u064a",
"Date": "2023-09-01T00:00:00",
"ApprovedLicense": null
},
{
"ID": 1007,
"ApplicationNumber": "MOC-2024-001007",
"RequestTypeEn": "Press Card Application",
"RequestTypeAr": "\u0637\u0644\u0628 \u0628\u0637\u0627\u0642\u0629 \u0635\u062d\u0641\u064a\u0629",
"ActivityEn": "Press & Publications",
"ActivityAr": "\u0627\u0644\u0635\u062d\u0627\u0641\u0629 \u0648\u0627\u0644\u0646\u0634\u0631",
"FkStatusID": 0,
"StatusEn": "Draft",
"StatusAr": "\u0645\u0633\u0648\u062f\u0629",
"Date": "2024-04-01T00:00:00",
"ApprovedLicense": null
}
]
}
Simulating cookies in DevTools
To trick the dashboard script into showing the logged-in state locally, set these cookies from the DevTools console:
// Paste in DevTools console → simulates a logged-in MoC individual account document.cookie = 'ApplicantQID=28712345678; path=/'; document.cookie = 'token=mock-token-abc123; path=/'; document.cookie = 'RoleName=Applicant; path=/'; document.cookie = 'ApplicantName=Test%20User; path=/'; document.cookie = 'EstablishmentID=; path=/'; location.reload();
Run document.cookie.split(';').forEach(c => document.cookie = c.split('=')[0].trim() + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/') in the console to wipe all cookies before switching back to a real session.
User Identity Card
Displays the authenticated user's identity from session cookies — no API call needed.
Showing demo data — log in via MoC to see your real identity.
- Avatar circle — 2-char initials from
ApplicantName - Full name —
ApplicantNamecookie value - Masked QID — last 4 digits of
ApplicantQIDvisible - Role badge —
RoleNamecookie (ApplicantorEstablishment) - Green dot — session active (present when
ApplicantQIDcookie exists)
Important Notices
This component preview is a developer reference only. The visual style shown here is an implementation example — do not use it as a design specification for production. All UX/UI decisions must align with the QDF digital design system.
This component reads session cookies set by the MoC portal. Real data is only available after the user has authenticated via /entity-services/moc/. The preview above falls back to demo data when not signed in.
Overview
The User Identity Card is a stateless display component — it reads values directly from document.cookie and renders them. No API calls, no async state.
When to use
- At the top of any entity-specific dashboard (My Applications, My Profile, etc.)
- On any Sitecore page that accesses MoC-protected data after the user has authenticated
- As a persistent session indicator so users know they are logged in
Design decisions
- QID masking: last 4 digits only — reduces PII exposure while confirming the right account
- Gradient avatar: derived from the Hukoomi brand palette (maroon → purple) — no image dependency
- Green session dot: inferred from cookie presence — no heartbeat or ping required
- Role badge: makes the account type (individual vs. business) immediately visible
Data Sources
All fields come from cookies set by the MoC portal on successful login. These are readable from document.cookie because the QDF proxy strips HttpOnly before forwarding them to the browser.
| Cookie | Card field | Transformation |
|---|---|---|
ApplicantName | Display name + initials | Raw value for name; first letter of each word → 2-char initials |
ApplicantQID | Masked QID | qid.slice(0,-4).replace(/./g,'●') + qid.slice(-4) |
RoleName | Role badge | Applicant or Establishment — display as-is or translate |
ApplicantQID (presence) | Green session dot | Show dot when cookie exists, hide when absent |
The QDF edge proxy (rewriteResponseHeaders in cookies.js) strips HttpOnly from MoC cookies so they are accessible via document.cookie. This is intentional — see the proxy documentation for the security rationale.
Sample Code
HTML template
<div id="moc-user-card" style="display:none" class="moc-user-card">
<div class="moc-avatar"></div>
<div class="moc-user-info">
<div class="moc-user-name"></div>
<div class="moc-user-meta">
<span class="moc-user-qid"></span>
<span class="moc-user-role"></span>
</div>
</div>
<div class="moc-session-dot"></div>
</div>JavaScript
function getCookie(name) {
var m = document.cookie.match(new RegExp('(?:^|;\\s*)' + name + '=([^;]*)'));
return m ? decodeURIComponent(m[1]) : null;
}
function renderUserCard() {
var qid = getCookie('ApplicantQID');
var name = getCookie('ApplicantName') || 'Applicant';
var role = getCookie('RoleName') || 'Applicant';
var el = document.getElementById('moc-user-card');
if (!qid || !el) return;
var initials = name.trim()
.split(/\s+/).map(function(w){ return w[0]||''; })
.join('').slice(0,2).toUpperCase() || qid.slice(-2);
var masked = qid.slice(0,-4).replace(/./g,'●') + qid.slice(-4);
el.style.display = '';
el.querySelector('.moc-avatar').textContent = initials;
el.querySelector('.moc-user-name').textContent = name;
el.querySelector('.moc-user-qid').textContent = 'QID · ' + masked;
el.querySelector('.moc-user-role').textContent = role;
}
document.addEventListener('DOMContentLoaded', renderUserCard);Minimal CSS
.moc-user-card { display:flex; align-items:center; gap:12px; padding:14px 18px; background:#fff; border-radius:12px; }
.moc-avatar { width:40px; height:40px; border-radius:50%; background:linear-gradient(135deg,#8a1538,#502691); color:#fff; display:flex; align-items:center; justify-content:center; font-size:14px; font-weight:700; flex-shrink:0; }
.moc-user-name { font-size:14px; font-weight:700; color:#111827; }
.moc-user-meta { display:flex; align-items:center; gap:8px; margin-top:3px; }
.moc-user-qid { font-size:11px; color:#6b7280; font-family:monospace; }
.moc-user-role { font-size:10px; font-weight:700; padding:2px 8px; border-radius:20px; background:#eff6ff; color:#1d4ed8; border:1px solid #bfdbfe; text-transform:uppercase; }
.moc-session-dot { width:9px; height:9px; border-radius:50%; background:#22c55e; box-shadow:0 0 0 3px rgba(34,197,94,.18); margin-left:auto; }Integration Notes
Bilingual support
The ApplicantName cookie value is returned by MoC as-is — typically in English. The QID and role badge are language-neutral. If you need to translate the role label, use a mapping:
var ROLE_LABELS = {
en: { Applicant: 'Applicant', Establishment: 'Establishment' },
ar: { Applicant: 'مقدّم الطلب', Establishment: 'منشأة' }
};
var lang = getCookie('__qdf_lang') || 'en';
var roleLabel = ROLE_LABELS[lang][role] || role;RTL layout
The card uses flexbox which naturally mirrors in dir="rtl". Add this CSS override:
[dir="rtl"] .moc-user-card { direction: rtl; }
[dir="rtl"] .moc-session-dot { margin-left: 0; margin-right: auto; }After NAS SSO go-live
NAS SSO sets the exact same MoC session cookies on login. The component reads the same cookies and continues to work without modification once SSO is enabled.
Sitecore placement
- Render server-side via a Sitecore rendering or via client-side JS on
DOMContentLoaded - Recommended: place in the entity-specific layout (not the global header) — this component only makes sense on pages that require MoC auth
- Check for
ApplicantQIDcookie presence before rendering — if absent, show a login prompt instead
Application Status
Renders each application as a list item with status icon, label, service name and metadata.
Important Notices
The component shown here is a developer reference. Do not derive production design decisions from this sandbox. All component design must align with the QDF digital design system and agreed patterns.
The 7 status codes documented here were observed from live MoC production traffic. The MoC API does not publish a formal contract — additional FkStatusID values may exist. Always implement a neutral fallback for unknown codes.
Overview
Each application is rendered as a .list-item row containing three visual zones: a status icon (coloured background + emoji), the content block (status label, service name, metadata), and a chevron for navigation.
CSS class system
Status colouring is driven by two CSS class families applied based on FkStatusID:
.ic-{id}— sets the icon background colour.st-{id}— sets the status label text colour
This two-class pattern keeps the colour system extensible: adding a new status requires one CSS rule pair, not a new component variant.
Data flow
Data comes from the MoC GetAllRequests API (see the MoC Applications module). The status component is purely a renderer — pass it a record and a language, and it builds the DOM.
Status Reference
| FkStatusID | StatusEn / StatusAr | Live Preview | Meaning |
|---|---|---|---|
0 | Draft مسودة | ✏️ Draft | Saved but not submitted |
1 | Under Review قيد المراجعة | 🔍 Under Review | Being assessed by MoC |
2 | Awaiting Revision بانتظار التعديل | ⚠️ Awaiting Revision | Returned for correction |
3 | In Progress قيد التنفيذ | ⏳ In Progress | Approved; final processing underway |
10 | Approved موافق عليه | ✅ Approved | Licence issued |
11 | Rejected مرفوض | ❌ Rejected | Application declined |
630 | Cancelled ملغي | ⊘ Cancelled | Withdrawn or cancelled |
Use .ic-def { background:#f3f4f6 } / .st-def { color:#6b7280 } for any FkStatusID not in the table above. The API may return undocumented codes.
Sample Code
STATUS_META map + helper
var STATUS_META = {
0: { emoji: '✏️', ic: 'ic-0', st: 'st-0' },
1: { emoji: '🔍', ic: 'ic-1', st: 'st-1' },
2: { emoji: '⚠️', ic: 'ic-2', st: 'st-2' },
3: { emoji: '⏳', ic: 'ic-3', st: 'st-3' },
10: { emoji: '✅', ic: 'ic-10', st: 'st-10' },
11: { emoji: '❌', ic: 'ic-11', st: 'st-11' },
630: { emoji: '⊘', ic: 'ic-630', st: 'st-630' }
};
function statusMeta(id) {
return STATUS_META[+id] || { emoji: '•', ic: 'ic-def', st: 'st-def' };
}Render a single list item
// lang: 'en' | 'ar'
function renderStatusItem(app, lang) {
var meta = statusMeta(app.FkStatusID);
var label = lang==='ar' ? (app.StatusAr||app.StatusEn) : app.StatusEn;
var title = lang==='ar' ? (app.RequestTypeAr||app.RequestTypeEn) : app.RequestTypeEn;
var meta2 = lang==='ar' ? (app.ActivityAr||app.ActivityEn) : app.ActivityEn;
var date = (app.Date||'').split('T')[0].split('-').reverse().join('/');
var item = document.createElement('div');
item.className = 'list-item';
item.setAttribute('role', 'listitem');
item.setAttribute('aria-label', label + ': ' + title);
item.innerHTML =
'<div class="item-icon-wrap ' + meta.ic + '">' + meta.emoji + '</div>' +
'<div class="item-content">' +
'<span class="item-status-label ' + meta.st + '">' + label + '</span>' +
'<div class="item-title">' + title + '</div>' +
'<div class="item-meta">' + meta2 + ' · ' + date +
(app.ApprovedLicense ? ' · ' + app.ApprovedLicense : '') +
'</div>' +
'</div>' +
'<span class="item-chevron" aria-hidden="true">›</span>';
return item;
}CSS (ic-* and st-* classes)
.ic-0 { background:#f3f4f6; } .st-0 { color:#6b7280; }
.ic-1 { background:#eff6ff; } .st-1 { color:#1d4ed8; }
.ic-2 { background:#fffbeb; } .st-2 { color:#b45309; }
.ic-3 { background:#f0fdfa; } .st-3 { color:#0f766e; }
.ic-10 { background:#f0fdf4; } .st-10 { color:#15803d; }
.ic-11 { background:#fef2f2; } .st-11 { color:#b91c1c; }
.ic-630 { background:#f9fafb; } .st-630 { color:#6b7280; }
.ic-def { background:#f3f4f6; } .st-def { color:#6b7280; }Accessibility
Conveying status without colour alone
Each status uses both an emoji icon and a text label — status meaning is never conveyed by colour alone, satisfying WCAG 1.4.1 (Use of Color).
ARIA markup
<!-- List container -->
<div role="list" aria-label="My Applications">
<!-- Each item -->
<div role="listitem"
class="list-item"
tabindex="0"
aria-label="Under Review: Media Production Company Registration">
<div class="item-icon-wrap ic-1" aria-hidden="true">🔍</div>
<div class="item-content">
<span class="item-status-label st-1">Under Review</span>
<div class="item-title">Media Production Company Registration</div>
</div>
</div>
</div>Keyboard navigation
Add tabindex="0" to each .list-item and handle keydown for Enter/Space if the items are interactive:
item.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
item.click();
}
});RTL chevron
The › chevron must mirror in RTL. The portal stylesheet already handles this:
[dir="rtl"] .item-chevron { transform: scaleX(-1); }Colour contrast
| Status | Text colour | Background | Contrast ratio |
|---|---|---|---|
| Draft | #6b7280 | #f3f4f6 | ≥ 4.5:1 ✓ |
| Under Review | #1d4ed8 | #eff6ff | ≥ 4.5:1 ✓ |
| Approved | #15803d | #f0fdf4 | ≥ 4.5:1 ✓ |
| Rejected | #b91c1c | #fef2f2 | ≥ 4.5:1 ✓ |