event qr code updated
This commit is contained in:
parent
23b3075a3b
commit
b04e39b202
@ -3,13 +3,13 @@
|
|||||||
<record id="paperformat_membership_card" model="report.paperformat">
|
<record id="paperformat_membership_card" model="report.paperformat">
|
||||||
<field name="name">Membership Card (85x54mm)</field>
|
<field name="name">Membership Card (85x54mm)</field>
|
||||||
<field name="format">custom</field>
|
<field name="format">custom</field>
|
||||||
<field name="page_width">85</field>
|
<field name="page_width">86</field>
|
||||||
<field name="page_height">54</field>
|
<field name="page_height">54</field>
|
||||||
<field name="orientation">Landscape</field>
|
<field name="orientation">Portrait</field>
|
||||||
<field name="margin_top">2</field>
|
<field name="margin_top">0</field>
|
||||||
<field name="margin_bottom">2</field>
|
<field name="margin_bottom">0</field>
|
||||||
<field name="margin_left">2</field>
|
<field name="margin_left">0</field>
|
||||||
<field name="margin_right">2</field>
|
<field name="margin_right">0</field>
|
||||||
<field name="disable_shrinking" eval="True"/>
|
<field name="disable_shrinking" eval="True"/>
|
||||||
<field name="dpi">96</field>
|
<field name="dpi">96</field>
|
||||||
</record>
|
</record>
|
||||||
@ -29,37 +29,141 @@
|
|||||||
<template id="report_membership_card">
|
<template id="report_membership_card">
|
||||||
<t t-call="web.basic_layout">
|
<t t-call="web.basic_layout">
|
||||||
<t t-foreach="docs" t-as="partner">
|
<t t-foreach="docs" t-as="partner">
|
||||||
<div style="width: 100%; height: 100%; padding: 6pt; box-sizing: border-box; font-family: monospace;
|
<style>
|
||||||
border: 1pt solid #cccccc; border-radius: 6pt;">
|
html, body {
|
||||||
<table style="width: 100%; height: 100%; border-collapse: collapse;">
|
margin: 0 !important;
|
||||||
<tr>
|
padding: 0 !important;
|
||||||
<td style="width: 65%; vertical-align: top;">
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
background: #ffffff !important;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
header, footer, .o_report_layout_standard,
|
||||||
|
#wrapwrap, main, .container, .container-fluid, .page {
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
min-height: 0 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- Full-bleed outer page wrapper -->
|
||||||
|
<div style="width: 100%; height: 100%; margin: 0; padding: 2mm; box-sizing: border-box; background: #ffffff;">
|
||||||
|
|
||||||
|
<!-- Card outer container with borders on all 4 sides -->
|
||||||
|
<div style="
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 2pt;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 2pt solid #0d3d2a;
|
||||||
|
border-radius: 6pt;
|
||||||
|
">
|
||||||
|
<!-- Card inner decorative gold border on all 4 sides -->
|
||||||
|
<div style="
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 0.75pt solid #c9a227;
|
||||||
|
border-radius: 4pt;
|
||||||
|
padding: 6pt 8pt;
|
||||||
|
text-align: center;
|
||||||
|
">
|
||||||
|
<!-- 1. Center top: Logo -->
|
||||||
<t t-if="partner.company_id.logo">
|
<t t-if="partner.company_id.logo">
|
||||||
<img t-att-src="image_data_uri(partner.company_id.logo)" style="max-height: 26pt; max-width: 90%;" alt="Logo"/>
|
<div style="text-align: center; margin-bottom: 4pt;">
|
||||||
</t>
|
<img t-att-src="image_data_uri(partner.company_id.logo)"
|
||||||
<div style="font-family: Arial, sans-serif; font-size: 9pt; font-weight: bold; margin-top: 4pt;">
|
style="max-height: 20pt; max-width: 70pt; display: inline-block;"
|
||||||
<span t-out="partner.membership_org_name or partner.company_id.name or ''"/>
|
alt="Logo"/>
|
||||||
</div>
|
</div>
|
||||||
<div style="font-family: Arial, sans-serif; font-size: 11pt; font-weight: bold; margin-top: 6pt;">
|
</t>
|
||||||
|
|
||||||
|
<!-- 2. Center top: Organization Name -->
|
||||||
|
<div style="
|
||||||
|
font-size: 7pt;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #0d3d2a;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.4pt;
|
||||||
|
line-height: 1.2;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 4pt;
|
||||||
|
">
|
||||||
|
<span t-out="partner.membership_org_name or partner.company_id.name or 'TAMIL NADU CULTURAL SOCIETY OF CANADA'"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Divider line -->
|
||||||
|
<div style="
|
||||||
|
width: 50mm;
|
||||||
|
height: 0.75pt;
|
||||||
|
background-color: #c9a227;
|
||||||
|
margin: 4pt auto 6pt auto;
|
||||||
|
"/>
|
||||||
|
|
||||||
|
<!-- 3. Under that: Member Name -->
|
||||||
|
<div style="
|
||||||
|
font-size: 12pt;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #111827;
|
||||||
|
line-height: 1.2;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 5pt;
|
||||||
|
">
|
||||||
<span t-out="partner.name"/>
|
<span t-out="partner.name"/>
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size: 9pt; margin-top: 2pt;">
|
|
||||||
<span t-out="partner.membership_member_id or ''"/>
|
<!-- 4. Under that: Membership Tier Name -->
|
||||||
|
<div style="
|
||||||
|
font-size: 8pt;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #b48316;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.6pt;
|
||||||
|
line-height: 1.2;
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 5pt;
|
||||||
|
">
|
||||||
|
<span t-out="partner.membership_tier_id.name or 'MEMBER'"/>
|
||||||
</div>
|
</div>
|
||||||
<div style="font-family: Arial, sans-serif; font-size: 8pt; margin-top: 4pt;">
|
|
||||||
<span t-out="partner.membership_tier_id.name or ''"/>
|
<!-- 5. Under that: Valid Date -->
|
||||||
|
<div style="
|
||||||
|
font-size: 6.5pt;
|
||||||
|
color: #4b5563;
|
||||||
|
line-height: 1.2;
|
||||||
|
text-align: center;
|
||||||
|
letter-spacing: 0.3pt;
|
||||||
|
">
|
||||||
|
<span style="font-weight: 600; color: #374151;">VALID UNTIL: </span>
|
||||||
|
<span t-out="partner.membership_expiry or 'LIFETIME'"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 6. Member ID (Optional) -->
|
||||||
|
<!-- <div style="
|
||||||
|
font-size: 5.5pt;
|
||||||
|
color: #374151;
|
||||||
|
font-family: 'Courier New', Courier, monospace;
|
||||||
|
letter-spacing: 0.5pt;
|
||||||
|
line-height: 1.15;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 4pt;
|
||||||
|
">
|
||||||
|
<span>ID: </span>
|
||||||
|
<span style="font-weight: bold;" t-out="partner.membership_member_id or '—'"/>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
<!-- 7. QR Code (Scan to Verify) -->
|
||||||
|
<t t-if="partner.membership_card_qr">
|
||||||
|
<div style="text-align: center; margin-top: 5pt;">
|
||||||
|
<img t-att-src="image_data_uri(partner.membership_card_qr)"
|
||||||
|
style="width: 20pt; height: 20pt; display: inline-block;"
|
||||||
|
alt="QR"/>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
</div>
|
</div>
|
||||||
<div style="font-family: Arial, sans-serif; font-size: 7pt; color: #666666;">
|
|
||||||
Valid until:
|
|
||||||
<span t-out="partner.membership_expiry or 'Lifetime'"/>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
|
||||||
<td style="width: 35%; text-align: center; vertical-align: middle;">
|
|
||||||
<img t-if="partner.membership_card_qr" t-att-src="image_data_uri(partner.membership_card_qr)"
|
|
||||||
style="width: 60pt; height: 60pt;" alt="QR"/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
</t>
|
</t>
|
||||||
|
|||||||
@ -31,11 +31,13 @@ OS product module required.
|
|||||||
'data': [
|
'data': [
|
||||||
'data/ir_sequence.xml',
|
'data/ir_sequence.xml',
|
||||||
'report/event_ticket_report.xml',
|
'report/event_ticket_report.xml',
|
||||||
|
'views/event_registration_views.xml',
|
||||||
'views/checkin_templates.xml',
|
'views/checkin_templates.xml',
|
||||||
],
|
],
|
||||||
'demo': [],
|
'demo': [],
|
||||||
'assets': {
|
'assets': {
|
||||||
'web.assets_frontend': [
|
'web.assets_frontend': [
|
||||||
|
'event_qr_ticketing/static/src/lib/jsQR.js',
|
||||||
'event_qr_ticketing/static/src/js/event_checkin.js',
|
'event_qr_ticketing/static/src/js/event_checkin.js',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -44,10 +44,12 @@ class EventCheckinController(http.Controller):
|
|||||||
|
|
||||||
result = {'status': status}
|
result = {'status': status}
|
||||||
if registration:
|
if registration:
|
||||||
|
names = [r.name or r.partner_id.name or 'Unknown' for r in registration]
|
||||||
|
display_name = names[0] if len(names) == 1 else f"{names[0]} + {len(names)-1} others"
|
||||||
result.update(
|
result.update(
|
||||||
name=registration.name or registration.partner_id.name,
|
name=display_name,
|
||||||
event=registration.event_id.name,
|
event=registration[0].event_id.name,
|
||||||
ticket_ref=registration.ticket_ref,
|
ticket_ref=registration[0].ticket_ref,
|
||||||
)
|
)
|
||||||
if status == 'ok':
|
if status == 'ok':
|
||||||
registration.action_check_in()
|
registration.action_check_in()
|
||||||
|
|||||||
@ -19,14 +19,16 @@ class EventRegistration(models.Model):
|
|||||||
check_in_time = fields.Datetime(string='Check-in Time', readonly=True, copy=False)
|
check_in_time = fields.Datetime(string='Check-in Time', readonly=True, copy=False)
|
||||||
check_in_user_id = fields.Many2one('res.users', string='Checked In By', readonly=True, copy=False)
|
check_in_user_id = fields.Many2one('res.users', string='Checked In By', readonly=True, copy=False)
|
||||||
|
|
||||||
_ticket_ref_uniq = models.Constraint('unique(ticket_ref)', 'Ticket reference must be unique.')
|
|
||||||
|
|
||||||
@api.model_create_multi
|
@api.model_create_multi
|
||||||
def create(self, vals_list):
|
def create(self, vals_list):
|
||||||
registrations = super().create(vals_list)
|
registrations = super().create(vals_list)
|
||||||
|
event_ticket_refs = {}
|
||||||
for registration in registrations:
|
for registration in registrations:
|
||||||
if not registration.ticket_ref:
|
if not registration.ticket_ref:
|
||||||
registration.ticket_ref = registration._generate_ticket_ref()
|
if registration.event_id.id not in event_ticket_refs:
|
||||||
|
event_ticket_refs[registration.event_id.id] = registration._generate_ticket_ref()
|
||||||
|
registration.ticket_ref = event_ticket_refs[registration.event_id.id]
|
||||||
return registrations
|
return registrations
|
||||||
|
|
||||||
def _generate_ticket_ref(self):
|
def _generate_ticket_ref(self):
|
||||||
@ -39,9 +41,9 @@ class EventRegistration(models.Model):
|
|||||||
return secret.encode()
|
return secret.encode()
|
||||||
|
|
||||||
def _compute_ticket_token(self):
|
def _compute_ticket_token(self):
|
||||||
"""HMAC of event_id:registration_id:ticket_ref, signed with the per-database secret."""
|
"""HMAC of event_id:ticket_ref, signed with the per-database secret."""
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
payload = f"{self.event_id.id}:{self.id}:{self.ticket_ref}".encode()
|
payload = f"{self.event_id.id}:{self.ticket_ref}".encode()
|
||||||
return hmac.new(self._get_signing_secret(), payload, hashlib.sha256).hexdigest()[:16]
|
return hmac.new(self._get_signing_secret(), payload, hashlib.sha256).hexdigest()[:16]
|
||||||
|
|
||||||
def _get_ticket_qr_value(self):
|
def _get_ticket_qr_value(self):
|
||||||
@ -65,7 +67,7 @@ class EventRegistration(models.Model):
|
|||||||
def _verify_scanned_token(self, scanned_value):
|
def _verify_scanned_token(self, scanned_value):
|
||||||
"""Verify a scanned QR payload ('ticket_ref|token').
|
"""Verify a scanned QR payload ('ticket_ref|token').
|
||||||
|
|
||||||
Returns (status, registration) where status is one of:
|
Returns (status, registrations) where status is one of:
|
||||||
'ok', 'already', 'invalid' (bad/forged token), 'not_found'.
|
'ok', 'already', 'invalid' (bad/forged token), 'not_found'.
|
||||||
"""
|
"""
|
||||||
parts = (scanned_value or '').split('|')
|
parts = (scanned_value or '').split('|')
|
||||||
@ -73,36 +75,55 @@ class EventRegistration(models.Model):
|
|||||||
return 'invalid', self.env['event.registration']
|
return 'invalid', self.env['event.registration']
|
||||||
|
|
||||||
ticket_ref, token = parts
|
ticket_ref, token = parts
|
||||||
registration = self.search([('ticket_ref', '=', ticket_ref)], limit=1)
|
registrations = self.search([('ticket_ref', '=', ticket_ref)])
|
||||||
if not registration:
|
if not registrations:
|
||||||
return 'not_found', registration
|
return 'not_found', registrations
|
||||||
|
|
||||||
expected_token = registration._compute_ticket_token()
|
expected_token = registrations[0]._compute_ticket_token()
|
||||||
if not hmac.compare_digest(expected_token, token):
|
if not hmac.compare_digest(expected_token, token):
|
||||||
return 'invalid', registration
|
return 'invalid', registrations
|
||||||
|
|
||||||
if registration.checked_in:
|
if all(r.checked_in for r in registrations):
|
||||||
return 'already', registration
|
return 'already', registrations
|
||||||
|
|
||||||
return 'ok', registration
|
return 'ok', registrations
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _lookup_by_ticket_ref(self, ticket_ref):
|
def _lookup_by_ticket_ref(self, ticket_ref):
|
||||||
"""Manual entry lookup: staff-trusted, no signature check required."""
|
"""Manual entry lookup: staff-trusted, no signature check required."""
|
||||||
registration = self.search([('ticket_ref', '=', ticket_ref)], limit=1)
|
registrations = self.search([('ticket_ref', '=', ticket_ref)])
|
||||||
if not registration:
|
if not registrations:
|
||||||
return 'not_found', registration
|
return 'not_found', registrations
|
||||||
if registration.checked_in:
|
if all(r.checked_in for r in registrations):
|
||||||
return 'already', registration
|
return 'already', registrations
|
||||||
return 'ok', registration
|
return 'ok', registrations
|
||||||
|
|
||||||
def action_check_in(self):
|
def action_check_in(self):
|
||||||
self.ensure_one()
|
for reg in self:
|
||||||
self.write({
|
reg.write({
|
||||||
'checked_in': True,
|
'checked_in': True,
|
||||||
'check_in_time': fields.Datetime.now(),
|
'check_in_time': fields.Datetime.now(),
|
||||||
'check_in_user_id': self.env.user.id,
|
'check_in_user_id': self.env.user.id,
|
||||||
})
|
})
|
||||||
if self.state != 'done':
|
if reg.state != 'done':
|
||||||
self.action_set_done()
|
reg.action_set_done()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def action_set_done(self):
|
||||||
|
"""Override to check in all registrations sharing the same ticket_ref."""
|
||||||
|
res = super().action_set_done()
|
||||||
|
for reg in self:
|
||||||
|
if reg.ticket_ref:
|
||||||
|
siblings = self.env['event.registration'].search([
|
||||||
|
('ticket_ref', '=', reg.ticket_ref),
|
||||||
|
('id', '!=', reg.id),
|
||||||
|
('state', 'not in', ['cancel', 'done']),
|
||||||
|
])
|
||||||
|
for sib in siblings:
|
||||||
|
sib.write({
|
||||||
|
'checked_in': True,
|
||||||
|
'check_in_time': fields.Datetime.now(),
|
||||||
|
'check_in_user_id': self.env.user.id,
|
||||||
|
})
|
||||||
|
sib.action_set_done()
|
||||||
|
return res
|
||||||
|
|||||||
8
addons/event_qr_ticketing/static/src/lib/jsQR.js
Normal file
8
addons/event_qr_ticketing/static/src/lib/jsQR.js
Normal file
File diff suppressed because one or more lines are too long
52
addons/event_qr_ticketing/views/event_registration_views.xml
Normal file
52
addons/event_qr_ticketing/views/event_registration_views.xml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- Event Registration Form: Show Ticket Reference, Status & QR Code -->
|
||||||
|
<record id="view_event_registration_form_inherit_qr" model="ir.ui.view">
|
||||||
|
<field name="name">event.registration.form.inherit.qr</field>
|
||||||
|
<field name="model">event.registration</field>
|
||||||
|
<field name="inherit_id" ref="event.view_event_registration_form"/>
|
||||||
|
<field name="priority">20</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<!-- Add Ticket Reference & QR Info in Event Information -->
|
||||||
|
<xpath expr="//group[@name='event']" position="inside">
|
||||||
|
<field name="ticket_ref" string="Ticket Reference" readonly="1" class="fw-bold text-primary"/>
|
||||||
|
<field name="checked_in" readonly="1"/>
|
||||||
|
<field name="check_in_time" invisible="not checked_in" readonly="1"/>
|
||||||
|
<field name="check_in_user_id" invisible="not checked_in" readonly="1"/>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
|
<!-- Add Dedicated Ticket QR Tab in Notebook -->
|
||||||
|
<xpath expr="//notebook" position="inside">
|
||||||
|
<page string="Ticket & QR Code" name="ticket_qr_tab">
|
||||||
|
<group>
|
||||||
|
<group string="Ticket Details">
|
||||||
|
<field name="ticket_ref" readonly="1"/>
|
||||||
|
<field name="checked_in" readonly="1"/>
|
||||||
|
<field name="check_in_time" invisible="not checked_in" readonly="1"/>
|
||||||
|
<field name="check_in_user_id" invisible="not checked_in" readonly="1"/>
|
||||||
|
</group>
|
||||||
|
<group string="QR Code (Scan for Check-in)">
|
||||||
|
<field name="ticket_qr" widget="image" class="oe_avatar" style="width: 140px; height: 140px;" readonly="1"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Event Registration List: Add Ticket Ref & Checked-in Badge -->
|
||||||
|
<record id="view_event_registration_tree_inherit_qr" model="ir.ui.view">
|
||||||
|
<field name="name">event.registration.list.inherit.qr</field>
|
||||||
|
<field name="model">event.registration</field>
|
||||||
|
<field name="inherit_id" ref="event.view_event_registration_tree"/>
|
||||||
|
<field name="priority">20</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='name']" position="before">
|
||||||
|
<field name="ticket_ref" optional="show" string="Ticket Ref"/>
|
||||||
|
</xpath>
|
||||||
|
<xpath expr="//field[@name='state']" position="before">
|
||||||
|
<field name="checked_in" optional="show" widget="boolean_toggle"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
BIN
addons/tncsc_deployment/static/src/img/membership-bg.png
Normal file
BIN
addons/tncsc_deployment/static/src/img/membership-bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 431 KiB |
@ -4741,43 +4741,260 @@
|
|||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<t t-name="tncsc_deployment.page_membership_thank_you">
|
<t t-name="tncsc_deployment.page_membership_thank_you">
|
||||||
<t t-call="website.layout">
|
<t t-call="website.layout">
|
||||||
<div id="wrap" class="py-5" style="background-color: #f7f8fc; min-height: 80vh;">
|
<!-- Membership Thank You Page Styles -->
|
||||||
<div class="container py-4">
|
<style>
|
||||||
<div class="row justify-content-center">
|
.tncsc-thankyou-page {
|
||||||
<div class="col-md-8 col-lg-7">
|
min-height: 80vh;
|
||||||
<div class="card border-0 shadow-sm rounded-4 p-4 p-md-5 text-center bg-white">
|
background: linear-gradient(135deg, #f0ede6 0%, #e8e3d8 50%, #ede8df 100%);
|
||||||
<div class="mb-4">
|
display: flex;
|
||||||
<span class="d-inline-flex align-items-center justify-content-center rounded-circle"
|
align-items: center;
|
||||||
style="width: 80px; height: 80px; background-color: #e8f5e9; color: #2e7d32; font-size: 2.5rem;">
|
padding: 48px 0;
|
||||||
<i class="fa fa-check"></i>
|
}
|
||||||
</span>
|
.tncsc-thankyou-card {
|
||||||
</div>
|
position: relative;
|
||||||
<h2 class="fw-bold mb-2" style="color: #120735;">Application Received!</h2>
|
background-image: url('/tncsc_deployment/static/src/img/membership-bg.png');
|
||||||
<h5 class="mb-3 text-muted">விண்ணப்பம் பெறப்பட்டது - நன்றி!</h5>
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
border: none;
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 48px 44px 44px;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 20px 60px rgba(18, 7, 53, 0.18), 0 4px 16px rgba(18, 7, 53, 0.10);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.tncsc-thankyou-card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
border-radius: 24px;
|
||||||
|
border: 2px solid rgba(180, 140, 60, 0.25);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
/* Check icon with radiating lines */
|
||||||
|
.tncsc-check-wrap {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.tncsc-check-circle {
|
||||||
|
width: 88px;
|
||||||
|
height: 88px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #e8f5ef;
|
||||||
|
border: 3px solid #0d5c3a;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 2.4rem;
|
||||||
|
color: #0d5c3a;
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
/* Title */
|
||||||
|
.tncsc-ty-title {
|
||||||
|
font-family: 'Georgia', 'Times New Roman', serif;
|
||||||
|
font-size: 2.2rem;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #0d3d2a;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
letter-spacing: -0.5px;
|
||||||
|
}
|
||||||
|
/* Tamil subtitle */
|
||||||
|
.tncsc-ty-tamil {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #0d5c3a;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
/* Gold lotus divider */
|
||||||
|
.tncsc-gold-divider {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 12px 0 20px;
|
||||||
|
}
|
||||||
|
.tncsc-gold-divider .tncsc-div-line {
|
||||||
|
height: 2px;
|
||||||
|
width: 70px;
|
||||||
|
background: linear-gradient(90deg, transparent, #c9a227, transparent);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.tncsc-gold-divider .tncsc-div-icon {
|
||||||
|
color: #c9a227;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
/* Thank you text */
|
||||||
|
.tncsc-ty-body {
|
||||||
|
color: #2c2c2c;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.7;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
/* What Happens Next box */
|
||||||
|
.tncsc-next-box {
|
||||||
|
background: rgba(255, 255, 255, 0.70);
|
||||||
|
border: 1.5px solid rgba(13, 92, 58, 0.18);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 20px 24px;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 28px;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
}
|
||||||
|
.tncsc-next-box h6 {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #0d3d2a;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
.tncsc-next-items {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.tncsc-next-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
color: #3a3a3a;
|
||||||
|
font-size: 0.93rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
.tncsc-next-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 34px;
|
||||||
|
height: 34px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin-top: 1px;
|
||||||
|
}
|
||||||
|
.tncsc-next-icon.icon-email {
|
||||||
|
background: #e6f4ea;
|
||||||
|
color: #0d5c3a;
|
||||||
|
}
|
||||||
|
.tncsc-next-icon.icon-review {
|
||||||
|
background: #fff8e1;
|
||||||
|
color: #b8860b;
|
||||||
|
}
|
||||||
|
.tncsc-next-icon.icon-card {
|
||||||
|
background: #e8eaf6;
|
||||||
|
color: #3949ab;
|
||||||
|
}
|
||||||
|
/* Buttons */
|
||||||
|
.tncsc-btn-home {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 11px 28px;
|
||||||
|
border: 2px solid #c9a227;
|
||||||
|
border-radius: 50px;
|
||||||
|
background: transparent;
|
||||||
|
color: #8a6b00;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.22s ease;
|
||||||
|
}
|
||||||
|
.tncsc-btn-home:hover {
|
||||||
|
background: #c9a227;
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.tncsc-btn-portal {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 11px 28px;
|
||||||
|
border-radius: 50px;
|
||||||
|
background: #0d3d2a;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: all 0.22s ease;
|
||||||
|
border: 2px solid #0d3d2a;
|
||||||
|
}
|
||||||
|
.tncsc-btn-portal:hover {
|
||||||
|
background: #0d5c3a;
|
||||||
|
border-color: #0d5c3a;
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.tncsc-thankyou-card { padding: 36px 20px 30px; }
|
||||||
|
.tncsc-ty-title { font-size: 1.6rem; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<p class="text-secondary mb-4">
|
<div id="wrap" class="tncsc-thankyou-page">
|
||||||
Thank you, <b t-esc="partner.name if partner else 'Applicant'"/>! Your application for
|
<div class="container">
|
||||||
<b class="text-primary"><t t-esc="tier.name if tier else 'TNCSC Membership'"/></b>
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-9 col-lg-7 col-xl-6">
|
||||||
|
<div class="tncsc-thankyou-card">
|
||||||
|
|
||||||
|
<!-- Check icon -->
|
||||||
|
<div class="tncsc-check-wrap">
|
||||||
|
<div class="tncsc-check-circle">
|
||||||
|
<i class="fa fa-check"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Title -->
|
||||||
|
<h1 class="tncsc-ty-title">Application Received!</h1>
|
||||||
|
|
||||||
|
<!-- Tamil subtitle -->
|
||||||
|
<div class="tncsc-ty-tamil">விண்ணப்பம் பெறப்பட்டது – நன்றி!</div>
|
||||||
|
|
||||||
|
<!-- Gold lotus divider -->
|
||||||
|
<div class="tncsc-gold-divider">
|
||||||
|
<div class="tncsc-div-line"></div>
|
||||||
|
<span class="tncsc-div-icon">✶</span>
|
||||||
|
<div class="tncsc-div-line"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Body text -->
|
||||||
|
<p class="tncsc-ty-body">
|
||||||
|
Thank you, <strong t-esc="partner.name if partner else 'Applicant'"/>! Your application for
|
||||||
|
<strong><t t-esc="tier.name if tier else 'TNCSC Membership'"/></strong>
|
||||||
has been successfully submitted to the Tamil Nadu Cultural Society of Canada.
|
has been successfully submitted to the Tamil Nadu Cultural Society of Canada.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="p-3 bg-light rounded-3 border text-start mb-4">
|
<!-- What Happens Next -->
|
||||||
<h6 class="fw-bold mb-2 text-dark"><i class="fa fa-info-circle text-primary me-2"></i>What Happens Next:</h6>
|
<div class="tncsc-next-box">
|
||||||
<ul class="text-muted small mb-0 ps-3">
|
<h6><i class="fa fa-info-circle me-2" style="color:#0d5c3a;"></i>What Happens Next:</h6>
|
||||||
<li class="mb-1"><b>Confirmation Email:</b> An acknowledgment email has been dispatched to your registered email address (<t t-esc="partner.email if partner else 'your email'"/>).</li>
|
<div class="tncsc-next-items">
|
||||||
<li class="mb-1"><b>Board Review:</b> In accordance with the TNCSC Constitution, your application is pending review and approval by the Board of Directors.</li>
|
<div class="tncsc-next-item">
|
||||||
<li><b>Portal & Membership Card:</b> Once approved by the Board, your membership will be officially activated. You will receive an approval email and can log in with username <code t-esc="username or 'your chosen username'"/> to view your digital membership card.</li>
|
<div class="tncsc-next-icon icon-email"><i class="fa fa-envelope"></i></div>
|
||||||
</ul>
|
<div><b>Confirmation Email:</b> An acknowledgment email has been dispatched to your registered email address (<t t-esc="partner.email if partner else 'your email'"/>).</div>
|
||||||
|
</div>
|
||||||
|
<div class="tncsc-next-item">
|
||||||
|
<div class="tncsc-next-icon icon-review"><i class="fa fa-users"></i></div>
|
||||||
|
<div><b>Board Review:</b> In accordance with the TNCSC Constitution, your application is pending review and approval by the Board of Directors.</div>
|
||||||
|
</div>
|
||||||
|
<div class="tncsc-next-item">
|
||||||
|
<div class="tncsc-next-icon icon-card"><i class="fa fa-id-card"></i></div>
|
||||||
|
<div><b>Portal & Membership Card:</b> Once approved by the Board, your membership will be officially activated. You will receive an approval email and can log in with username <strong><t t-esc="username or 'your chosen username'"/></strong> to view your digital membership card.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Action Buttons -->
|
||||||
<div class="d-flex flex-wrap gap-3 justify-content-center">
|
<div class="d-flex flex-wrap gap-3 justify-content-center">
|
||||||
<a href="/" class="btn btn-outline-secondary px-4 py-2 rounded-pill">
|
<a href="/" class="tncsc-btn-home">
|
||||||
<i class="fa fa-home me-2"></i>Return to Home
|
<i class="fa fa-home"></i> Return to Home
|
||||||
</a>
|
</a>
|
||||||
<a href="/my" class="btn px-4 py-2 rounded-pill fw-bold text-white" style="background-color: #120735;">
|
<a href="/my" class="tncsc-btn-portal">
|
||||||
<i class="fa fa-user me-2"></i>Member Login / Portal
|
<i class="fa fa-user"></i> Member Login / Portal →
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user