Quota Portal¶
The Quota Portal enables quota holders (sponsors, partners, officials) to manage their ticket allocations, claim tickets, and optionally delegate to sub-recipients.
Tech Stack¶
- Backend: Symfony 7.x (PHP 8.2+)
- Templates: Twig
- Styling: SCSS imported from HNS Drupal theme (
hns_theme) — same colors, fonts, forms, header, footer - Interactivity: Alpine.js for reactive dynamic components (multi-step forms, card selection, polling)
- Build: Gulp (SCSS compilation, JS minification — matches Drupal theme toolchain)
- Fonts: Hustle (headings) + Maven Pro (body) — same as HNS webshop
- Libraries: Choices.js (dropdowns), intl-tel-input (phone), Stripe.js (payments)
- Authentication: Same credentials as HNS mobile app (Drupal SSO → JWT)
Access Control¶
- Users access portal using HNS mobile app credentials
- Portal access granted only if user's email is associated with an active quota
- No separate registration process required
User Capabilities¶
| Permission | Description |
|---|---|
| View Quota | All quota holders can view their allocations |
| Claim Tickets | All quota holders can claim tickets (3-step process) |
| Create Subquotas | Only if can_create_subquotas = TRUE |
| Retract Subquotas | Only unclaimed (ALLOCATED) subquotas |
Navigation Structure¶
Quota Portal
├── Dashboard (My Quotas)
│ └── Quota Cards by Match
├── Quota Detail
│ ├── Allocation Summary
│ ├── Seat List
│ └── Subquota List (if permitted)
├── Claim Tickets (3-Step)
│ ├── Step 1: Select Seats
│ ├── Step 2: Ticket Holder Info
│ └── Step 3: Payment
├── Create Subquota (if permitted)
│ ├── Select Seats
│ └── Enter Recipient Details
└── My Tickets
└── Claimed Ticket History
Screens¶
Dashboard (My Quotas)¶
Purpose: Overview of all quotas assigned to the logged-in user.
Key Components:
- Quota Cards: One card per match with quota allocation
- Status Summary: Quick view of allocated/claimed/sold
- Action Buttons: Claim tickets, Open detail, Create subquota
Quota Card Layout:
┌─────────────────────────────────────────────────────────────────┐
│ 🇭🇷 Croatia vs Poland │
│ 📅 March 15, 2026 • 20:45 • Maksimir Stadium │
├─────────────────────────────────────────────────────────────────┤
│ Allocated Sectors: D1, C1, B2 │
│ Total Seats: 50 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Allocated│ │ Reserved │ │ Sold │ │ Delegated│ │
│ │ 15 │ │ 5 │ │ 20 │ │ 10 │ │
│ │ (30%) │ │ (10%) │ │ (40%) │ │ (20%) │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ Deadline: March 10, 2026 (5 days remaining) │
│ │
│ [Claim Tickets] [View Details] [Create Subquota*] │
└─────────────────────────────────────────────────────────────────┘
* Only shown if can_create_subquotas = TRUE
Status Indicators:
- Green Badge: Active quota
- Yellow Badge: Past deadline (still claimable)
- Red Badge: Cancelled by admin
Workflow Reference: Mobile Quota Claiming
Quota Detail¶
Purpose: Detailed view of a specific quota with seat-level visibility.
Sections:
Allocation Summary¶
- Match details (teams, date, venue)
- Allocated sectors
- Discount information
- Expiration date (visual reminder)
- Transfer permission indicator (YES/NO)
Status Breakdown¶
┌─────────────────────────────────────────────────────────────────┐
│ Quota Status │
├─────────────────────────────────────────────────────────────────┤
│ ████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ │
│ ■ Allocated: 15 seats (available for claiming) │
│ ■ Reserved: 5 seats (info filled, pending payment) │
│ ■ Sold: 20 seats (completed) │
│ ■ Delegated: 10 seats (assigned to subquotas) │
└─────────────────────────────────────────────────────────────────┘
Seat List Table¶
| Sector | Row | Seat | Status | Assigned To | Claimed Date |
|---|---|---|---|---|---|
| D1 | 5 | 1 | Sold | Ivan H. | 2026-01-15 |
| D1 | 5 | 2 | Sold | Marija K. | 2026-01-15 |
| D1 | 5 | 3 | Reserved | Pending... | - |
| D1 | 5 | 4 | Allocated | - | - |
| D1 | 5 | 5 | Delegated | → Subquota #123 | - |
Subquota List (if permitted)¶
| Recipient | Seats | Status | Created | Actions | |
|---|---|---|---|---|---|
| Ana Novak | ana@corp.hr | 5 | SOLD | Jan 10 | View |
| Pero Babic | pero@corp.hr | 3 | ALLOCATED | Jan 12 | View, Retract |
| Ivana Matic | ivana@corp.hr | 2 | RESERVED | Jan 14 | View |
Subquota Status Badges:
- Yellow (ALLOCATED): Not yet started claiming
- Blue (RESERVED): Info filled, pending payment
- Green (SOLD): Completed
- Red (Cancelled): Retracted or admin cancelled
Workflow Reference: Quota Holder Web Delegation
Claim Tickets (3-Step Process)¶
Purpose: Claim allocated tickets through a guided 3-step flow.
Step 1: Select Seats¶
For Numbered Matches: - Stadium map showing allocated seats - Click seats to select/deselect - Selected seats highlighted - Selection summary displayed
For Non-Numbered Matches: - Quantity selector only - No seat map shown
Components: - Stadium visualization (sector view) - Seat selection checkboxes - Selection counter - Continue button
Layout:
┌─────────────────────────────────────────────────────────────────┐
│ Step 1 of 3: Select Seats │
│ ━━━━━━━━━━━━●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Sector D1 - Your Allocated Seats │
│ │
│ 1 2 3 4 5 6 7 8 9 10 │
│ ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ │
│ │ ✓ │ ✓ │ ● │ ● │ ● │ ○ │ ○ │ ○ │ ○ │ ○ │ Row 5 │
│ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ │
│ │
│ Legend: ✓ Selected ● Available ○ Sold/Delegated │
│ │
│ Selected: 2 seats (Row 5: Seats 1-2) │
│ │
│ [← Back to Quota] [Continue to Step 2 →] │
└─────────────────────────────────────────────────────────────────┘
Step 2: Enter Ticket Holder Information¶
Important Notice (displayed at top):
"Email Required: Each ticket will be delivered to the HNS mobile app linked to the email address you provide. All tickets with the same email must enter the stadium together on the same device."
Per-Ticket Form:
| Field | Type | Validation |
|---|---|---|
| Full Name | Text | Required |
| Date of Birth | Date | Required |
| Nationality | Dropdown | Required |
| OIB | Text | Required for Croatians, checksum validated |
| Passport | Text | Required for foreigners |
| Required, format validated | ||
| Phone | Text | Optional |
Features: - Select from saved profiles dropdown - Add new profile option - Real-time validation - Blacklist check (silent) - Progress indicator (e.g., "2 of 5 tickets completed")
Identity Uniqueness (per match): - Each ticket holder must have a unique OIB (Croatians) or passport number (foreigners) per match - The same OIB/passport cannot appear on multiple tickets for the same match, even across different quotas - Validated both client-side (within claim form) and server-side (across all tickets for the match)
Minor Restriction: - If DOB indicates under 18, email field disabled - Message: "Tickets for minors cannot be assigned to a separate email. Minor must enter with accompanying adult."
Layout:
┌─────────────────────────────────────────────────────────────────┐
│ Step 2 of 3: Ticket Holder Information │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━●━━━━━━━━━━━━━━━━ │
├─────────────────────────────────────────────────────────────────┤
│ ℹ️ Email Required: Each ticket will be delivered to the HNS │
│ mobile app linked to the email address you provide. │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Ticket 1 of 2 - Row 5, Seat 1 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ [Select from saved profiles ▼] or enter new details │ │
│ │ │ │
│ │ Full Name: [________________________] │ │
│ │ Date of Birth: [__/__/____] │ │
│ │ Nationality: [Croatia ▼] │ │
│ │ OIB: [___________] ✓ │ │
│ │ Email: [____________________@____] │ │
│ │ Phone: [__________________] (optional) │ │
│ │ │ │
│ │ [ ] Save as new profile │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ Progress: ████░░░░░░ 1 of 2 completed │
│ │
│ [← Back to Seat Selection] [Continue to Payment →] │
└─────────────────────────────────────────────────────────────────┘
Workflow Reference: Mobile Quota Claiming - Step 5
Step 3: Payment¶
If Payment Required: - Order summary with itemized prices - Applied discount display - Payment method selection - Redirect to payment gateway
If Free Quota (discount_code = GR): - Payment step skipped - Direct confirmation
Order Summary:
┌─────────────────────────────────────────────────────────────────┐
│ Step 3 of 3: Payment │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━● │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Order Summary │
│ ───────────────────────────────────────────── │
│ 2x Tickets - Sector D1, Row 5 │
│ │
│ Original Price: 2 × €50.00 = €100.00 │
│ Sponsor Discount: - €100.00 (100%) │
│ ───────────────────────────────────────────── │
│ Total: €0.00 │
│ │
│ [Complete Claim] │
│ │
└─────────────────────────────────────────────────────────────────┘
Post-Payment: - Tickets delivered to recipients' HNS mobile apps - Confirmation email sent - Redirect to confirmation screen
Create Subquota¶
Purpose: Delegate part of quota allocation to another recipient.
Prerequisite: can_create_subquotas = TRUE
Step 1: Select Seats for Subquota¶
Components: - Seat map showing available seats - Multi-select capability - Selection summary
Seat States: - Selectable: Available for delegation - Grayed: Already claimed (SOLD) - Different color: Already delegated to subquota
Step 2: Enter Recipient Details¶
| Field | Type | Required |
|---|---|---|
| Recipient Email | Yes | |
| Recipient Name | Text | Yes |
| Internal Note | Text | No |
Validation: - Email format check - No duplicate detection (same email can have multiple subquotas)
Create Button: Creates subquota and sends email invitation
Layout:
┌─────────────────────────────────────────────────────────────────┐
│ Create Subquota │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Step 1: Select Seats │
│ │
│ Sector D1 - Available Seats │
│ 1 2 3 4 5 6 7 8 9 10 │
│ ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ │
│ │ ✓ │ ✓ │ ✓ │ ● │ ● │ ░ │ ░ │ ░ │ ░ │ ░ │ Row 5 │
│ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ │
│ │
│ Legend: ✓ Selected ● Available ░ Sold/Delegated │
│ │
│ Selected for subquota: 3 seats (Row 5: Seats 1-3) │
│ │
│ ───────────────────────────────────────────── │
│ │
│ Step 2: Recipient Details │
│ │
│ Recipient Email: [____________________@____] * │
│ Recipient Name: [________________________] * │
│ Internal Note: [________________________] │
│ │
│ * Required fields │
│ │
│ [Cancel] [Create Subquota] │
└─────────────────────────────────────────────────────────────────┘
Post-Creation: - Subquota created with status "ALLOCATED" - Email sent to recipient with claiming instructions - Push notification if recipient has HNS app - Parent's dashboard updated
Workflow Reference: Quota Holder Web Delegation
Retract Subquota¶
Purpose: Cancel an unclaimed subquota and return seats.
Conditions: - Subquota status must be "ALLOCATED" (not claimed yet) - Cannot retract RESERVED or SOLD subquotas
Process: 1. Click "Retract" button on subquota row 2. Confirmation dialog: "Are you sure? [Name] will be notified." 3. Confirm retraction 4. Seats returned to parent quota 5. Cancellation email sent to sub-recipient
Error Message (if claimed):
"Cannot retract subquota. Sub-recipient has already started or completed claiming process."
My Tickets¶
Purpose: View tickets that the quota holder has claimed for themselves.
Table Columns:
| Column | Description |
|---|---|
| Match | Event name and date |
| Seat | Sector, row, seat number |
| Ticket Holder | Name on ticket |
| Status | SOLD, Transferred, Refunded |
| Actions | View ticket |
Note: This shows tickets where the quota holder is the ticket holder (buyer), not tickets delegated to others.
Quota Status Definitions¶
| Status | Description | User Actions |
|---|---|---|
| ALLOCATED | Seats assigned, not claimed | Start claiming |
| RESERVED | Info filled, pending payment | Complete payment |
| SOLD | Payment complete, tickets issued | View in mobile app |
| Delegated | Assigned to subquota | View subquota status |
| Cancelled | Admin cancelled | None |
Transfer Permission¶
Tickets claimed from quotas inherit the quota's transfer setting:
| Setting | Behavior |
|---|---|
| Transfer = YES | Ticket holder sees "Reassign Ticket" in app, no time restrictions |
| Transfer = NO | Standard rules: contact support, 48-hour deadline |
This is typically set to YES only for players and special VIPs.
Important Notes¶
Expiration Date Behavior¶
- The expiration date is a visual reminder only
- There is no automatic expiry
- Quota holders can claim after deadline until admin manually cancels
- Past-deadline quotas show "Past Deadline" warning but remain functional
Subquota Refund Behavior¶
When a ticket claimed from a subquota is refunded:
- Refund goes to the payer
- Ticket marked as "Cancelled (Refunded)"
- Seat does NOT return to subquota owner
- Seat is treated as used, not available for re-claiming
- Admin must manually create new allocation if needed
Error Messages¶
| Scenario | Message |
|---|---|
| Blacklist rejection | "HNS is prevented from issuing a ticket to this person pursuant to applicable law. For all information regarding this restriction, please contact MUP (Ministry of Interior)." |
| Cannot retract | "Cannot retract subquota. Sub-recipient has already started or completed claiming process." |
| Quota cancelled | "This quota has been cancelled by the administrator." |
| Invalid email | "Please enter a valid email address." |
| OIB validation | "Invalid OIB checksum. Please verify the number." |
Related Documentation¶
- Mobile Quota Claiming Flow
- Quota Holder Web Delegation Flow
- Admin Quota Creation Flow
- E7 - Quota Management Epic
Last Updated: January 2026