Per discussion: the marketing homepage looked polished but the rest of the public site (the admission form, contact us, the backend login screen) was still bare stock Odoo, and the site nav still showed stock Odoo's own default logo/favicon rather than Metatroncube's real ones. This brings the same design system (Manrope + Work Sans, the navy/indigo palette) to all three pages and wires up the two supplied brand assets - static/img/logo.png (the wordmark, used as website.logo, so it renders in every page's nav automatically) and static/img/favicon.png (the cube mark, website.favicon). Loaded via a post_init_hook (hooks.py) rather than a plain <record>: website's two stock demo sites (website.default_website, website.website2) are both noupdate=True, and Python is the tool that isn't gated by that the way a <record>/<function> tag would be on an already-installed database (see hooks.py's own docstring for the exact mechanism and its fresh-install-only limitation). Admissions keeps its exact form markup/field names/model untouched - only the surrounding hero and "what happens next" strip are new, added via position="before"/"after" around the existing <section> so the stock s_website_form JS widget this page depends on is never touched. Restyling contact us and the login screen surfaced a chain of real, separate placeholder-content bugs, each found by looking at the next page down rather than assuming the previous fix was complete: 1. website.contactus's sidebar hardcodes "My Company" and a fake US street address - shared/DEMO_SCRIPT.md explicitly bans placeholder content, and it had been live here the whole time. Reading res_company fields instead means it shows whichever school is actually active, same as the admission form's own live program list. 2. website.footer_custom - the footer on EVERY page, not just contact us - carries the identical yourcompany.example.com / +1 555-555-5556 placeholder, plus a "Products/Services/Legal" link list to nowhere and dead social icons. Fixing #1 alone would have left this sitting directly underneath it on every single page. 3. website.header_text_element, the nav bar's own phone/email widget, carries the same fake number - a fourth, separate occurrence, this time in the header. First pass here fixed only its "phone_mail" variant, assumed (wrongly) to be what this site's header actually uses; the live page still showed the fake number afterward. It turned out both nav slots (desktop, mobile) use the plain default branch instead - caught only by re-checking the real page rather than trusting that assumption, and fixed every variant that carries contact info this time, not just the one guessed at. 4. Underneath all three page-level fixes: base.main_company itself still carried stock Odoo's own demo phone (+1 555-555-5556) and email (info@yourcompany.com) - the page-level fixes above only surfaced this because they read real company data for the first time; the data itself needed fixing too. Real Indian/Canadian contact details added to both demo companies in mc_theme_demo.xml. 5. web.login_layout hardcodes its own separate "Powered by Odoo" link - a third distinct occurrence, different from the portal sidebar text and the web.brand_promotion badge already fixed, reached by the very first screen anyone doing this demo sees. Full suite (all 9 modules + web_responsive) re-verified together after every fix in this chain: 0 failed, 0 error(s), tests up to 93. One correction to this session's own earlier work: mc_cast_users_demo.xml claimed <function> "isn't subject to" the noupdate/mode guard that blocks a plain <record> on an already-installed database - false, odoo/tools/convert.py's _tag_function has the exact same "if self.noupdate and self.mode != 'init': return" check. What actually fixed that earlier bug was applying the change as a one-off manual Python snippet, not the tag choice - the same manual-application pattern used again here for base.main_company's contact fields on the already-running school_demo database. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
249 lines
17 KiB
XML
249 lines
17 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<odoo>
|
|
<!-- Same type pairing as mc_education_theme's marketing homepage
|
|
(Manrope for headings, Work Sans for body) so a visitor moving
|
|
from the homepage to this page doesn't land on a visibly
|
|
different, unstyled product. Loaded here too, not only from
|
|
theme, so this module still looks intentional if installed on
|
|
its own - matching CLAUDE.md's "each addon installs
|
|
independently" rule; the browser dedupes the identical URL if
|
|
both happen to be loaded on the same site. -->
|
|
<template id="admission_fonts" inherit_id="website.layout" name="Admission form fonts">
|
|
<xpath expr="//head" position="inside">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com"/>
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="crossorigin"/>
|
|
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@600;700;800&family=Work+Sans:wght@400;500;600;700&display=swap" rel="stylesheet"/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!--
|
|
Hand-written to match the DOM structure/classes of Odoo core's own
|
|
s_website_form snippet (website/views/snippets/s_website_form.xml)
|
|
exactly, so the stock frontend widget (form.js, loaded by every
|
|
website page via website.layout) picks it up automatically - no
|
|
custom JS, no custom submit controller. Posting still goes through
|
|
Odoo's generic /website/form/<model> controller; see
|
|
data/mc_applicant_website_form_data.xml for the field whitelist
|
|
that is the actual security boundary on that path.
|
|
|
|
The hero band and "what happens next" strip around the form are
|
|
presentation only, matching the site design system - they do not
|
|
touch a single field, name or class inside <form>, since that
|
|
exact markup is what the stock form.js widget depends on.
|
|
-->
|
|
<template id="admission_apply_page" name="Admission Application">
|
|
<t t-call="website.layout">
|
|
<div id="wrap" class="mc-admissions">
|
|
<style>
|
|
.mc-admissions{ --navy:#131B2E; --navy-2:#1C2A46; --indigo:#4B3F92; --indigo-2:#2E63B8;
|
|
--paper:#F6F7FB; --card:#FFFFFF; --line:#E4E7F0; --ink:#16192B; --ink-soft:#5B6178;
|
|
--shadow:0 1px 2px rgba(19,27,46,.06), 0 14px 34px -16px rgba(19,27,46,.22);
|
|
font-family:'Work Sans', system-ui, sans-serif; color:var(--ink); background:var(--paper);
|
|
}
|
|
.mc-admissions *{ box-sizing:border-box; }
|
|
.mc-admissions h1, .mc-admissions h2, .mc-admissions h3{
|
|
font-family:'Manrope', system-ui, sans-serif; font-weight:800; letter-spacing:-.01em;
|
|
color:var(--ink); text-wrap:balance; margin:0;
|
|
}
|
|
.mc-adm-hero{
|
|
background: radial-gradient(900px 380px at 90% -10%, rgba(75,63,146,.5), transparent 60%),
|
|
linear-gradient(180deg, var(--navy), var(--navy-2));
|
|
color:#fff; padding:52px 24px 60px; text-align:center;
|
|
}
|
|
.mc-adm-hero .pill{
|
|
display:inline-block; font-size:.72rem; letter-spacing:.08em; text-transform:uppercase;
|
|
font-weight:600; color:#EFE9FF; background:linear-gradient(90deg, var(--indigo), var(--indigo-2));
|
|
padding:6px 14px; border-radius:20px; margin-bottom:16px;
|
|
}
|
|
.mc-adm-hero h1{ color:#fff; font-size:clamp(1.6rem,3.4vw,2.3rem); max-width:20ch; margin:0 auto; }
|
|
.mc-adm-hero p{ color:#C4C9E2; max-width:52ch; margin:14px auto 0; font-size:1rem; line-height:1.6; }
|
|
|
|
.mc-adm-body{ max-width:900px; margin:-30px auto 0; padding:0 20px 64px; position:relative; }
|
|
.mc-adm-card{
|
|
background:var(--card); border:1px solid var(--line); border-radius:16px;
|
|
box-shadow:var(--shadow); padding:36px 32px;
|
|
}
|
|
@media (max-width:600px){ .mc-adm-card{ padding:26px 18px; } }
|
|
.mc-adm-card h1{ font-size:1.4rem; margin-bottom:6px; }
|
|
.mc-adm-card .lede{ color:var(--ink-soft); margin-bottom:26px; font-size:.94rem; }
|
|
|
|
.mc-admissions .s_website_form_label{ font-weight:600; color:var(--ink); font-size:.88rem; }
|
|
.mc-admissions .s_website_form_mark{ color:var(--indigo); }
|
|
.mc-admissions .form-control, .mc-admissions .form-select{
|
|
border-color:var(--line); border-radius:9px; padding:10px 12px; font-family:'Work Sans', sans-serif;
|
|
}
|
|
.mc-admissions .form-control:focus, .mc-admissions .form-select:focus{
|
|
border-color:var(--indigo); box-shadow:0 0 0 3px rgba(75,63,146,.15);
|
|
}
|
|
.mc-admissions .btn-primary{
|
|
background:linear-gradient(90deg, var(--indigo), var(--indigo-2)); border:none;
|
|
padding:11px 26px; border-radius:9px; font-weight:600;
|
|
}
|
|
.mc-admissions .btn-primary:hover{ filter:brightness(1.08); }
|
|
|
|
.mc-adm-steps{ max-width:900px; margin:0 auto; padding:0 20px 70px; }
|
|
.mc-adm-steps h2{ font-size:1.1rem; text-align:center; margin-bottom:24px; }
|
|
.mc-steps-row{ display:grid; grid-template-columns:repeat(5,1fr); gap:10px; }
|
|
@media (max-width:760px){ .mc-steps-row{ grid-template-columns:repeat(2,1fr); } }
|
|
.mc-step{ background:var(--card); border:1px solid var(--line); border-radius:12px; padding:16px 12px; text-align:center; }
|
|
.mc-step .n{
|
|
width:26px; height:26px; border-radius:50%; background:var(--indigo); color:#fff;
|
|
display:flex; align-items:center; justify-content:center; margin:0 auto 8px;
|
|
font-family:'Manrope',sans-serif; font-weight:700; font-size:.8rem;
|
|
}
|
|
.mc-step .t{ font-size:.82rem; font-weight:600; color:var(--ink); }
|
|
</style>
|
|
|
|
<div class="mc-adm-hero">
|
|
<span class="pill">Admissions Open</span>
|
|
<h1>Apply for Admission</h1>
|
|
<p>Fill in your child's details, attach a birth certificate, and submit - no office visit needed. You'll get an application number the moment you submit, and can track its progress from Applied through to Enrolled.</p>
|
|
</div>
|
|
|
|
<div class="mc-adm-body">
|
|
<div class="mc-adm-card">
|
|
<section class="s_website_form pt32 pb32" data-snippet="s_website_form">
|
|
<div class="container-fluid">
|
|
<h1 class="mb-4">Application Form</h1>
|
|
<form action="/website/form/" method="post" enctype="multipart/form-data"
|
|
class="o_mark_required" data-mark="*"
|
|
data-model_name="mc.applicant"
|
|
data-success-mode="message">
|
|
<div class="s_website_form_rows row s_col_no_bgcolor">
|
|
|
|
<div data-name="Field" class="s_website_form_field mb-3 col-12 s_website_form_required" data-type="char">
|
|
<div class="row s_col_no_resize s_col_no_bgcolor">
|
|
<label class="col-form-label col-sm-3 s_website_form_label" for="admission_name">
|
|
<span class="s_website_form_label_content">Student Name</span>
|
|
<span class="s_website_form_mark"> *</span>
|
|
</label>
|
|
<div class="col-sm-9">
|
|
<input class="form-control s_website_form_input" type="text"
|
|
name="name" id="admission_name" required="1"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div data-name="Field" class="s_website_form_field mb-3 col-12" data-type="date">
|
|
<div class="row s_col_no_resize s_col_no_bgcolor">
|
|
<label class="col-form-label col-sm-3 s_website_form_label" for="admission_dob">
|
|
<span class="s_website_form_label_content">Date of Birth</span>
|
|
</label>
|
|
<div class="col-sm-9">
|
|
<input class="form-control s_website_form_input" type="date"
|
|
name="dob" id="admission_dob"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div data-name="Field" class="s_website_form_field mb-3 col-12 s_website_form_required" data-type="many2one">
|
|
<div class="row s_col_no_resize s_col_no_bgcolor">
|
|
<label class="col-form-label col-sm-3 s_website_form_label" for="admission_program_id">
|
|
<span class="s_website_form_label_content">Grade Applied For</span>
|
|
<span class="s_website_form_mark"> *</span>
|
|
</label>
|
|
<div class="col-sm-9">
|
|
<select class="form-select s_website_form_input"
|
|
name="program_id" id="admission_program_id" required="1">
|
|
<option value="">Select a grade...</option>
|
|
<!-- Program display labels are public information (a
|
|
school's grade list is meant to be seen by anyone
|
|
filling this form) - sudo() only reads name/id for
|
|
this dropdown, nothing is written here. -->
|
|
<t t-foreach="request.env['mc.program'].sudo().search([], order='sequence_no')" t-as="program">
|
|
<option t-att-value="program.id" t-esc="program.display_label"/>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div data-name="Field" class="s_website_form_field mb-3 col-12 s_website_form_required" data-type="char">
|
|
<div class="row s_col_no_resize s_col_no_bgcolor">
|
|
<label class="col-form-label col-sm-3 s_website_form_label" for="admission_guardian_name">
|
|
<span class="s_website_form_label_content">Parent / Guardian Name</span>
|
|
<span class="s_website_form_mark"> *</span>
|
|
</label>
|
|
<div class="col-sm-9">
|
|
<input class="form-control s_website_form_input" type="text"
|
|
name="guardian_name" id="admission_guardian_name" required="1"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div data-name="Field" class="s_website_form_field mb-3 col-12 s_website_form_required" data-type="tel">
|
|
<div class="row s_col_no_resize s_col_no_bgcolor">
|
|
<label class="col-form-label col-sm-3 s_website_form_label" for="admission_guardian_phone">
|
|
<span class="s_website_form_label_content">Parent / Guardian Phone</span>
|
|
<span class="s_website_form_mark"> *</span>
|
|
</label>
|
|
<div class="col-sm-9">
|
|
<input class="form-control s_website_form_input" type="tel"
|
|
name="guardian_phone" id="admission_guardian_phone" required="1"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div data-name="Field" class="s_website_form_field mb-3 col-12 s_website_form_required" data-type="email">
|
|
<div class="row s_col_no_resize s_col_no_bgcolor">
|
|
<label class="col-form-label col-sm-3 s_website_form_label" for="admission_guardian_email">
|
|
<span class="s_website_form_label_content">Parent / Guardian Email</span>
|
|
<span class="s_website_form_mark"> *</span>
|
|
</label>
|
|
<div class="col-sm-9">
|
|
<input class="form-control s_website_form_input" type="email"
|
|
name="guardian_email" id="admission_guardian_email" required="1"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div data-name="Field" class="s_website_form_field mb-3 col-12" data-type="binary">
|
|
<div class="row s_col_no_resize s_col_no_bgcolor">
|
|
<label class="col-form-label col-sm-3 s_website_form_label" for="admission_birth_certificate">
|
|
<span class="s_website_form_label_content">Birth Certificate</span>
|
|
</label>
|
|
<div class="col-sm-9">
|
|
<!-- "birth_certificate" matches no real field on mc.applicant,
|
|
so Odoo's generic form controller stores it as a plain
|
|
ir.attachment linked to the new record - exactly the
|
|
"file uploads to ir.attachment" the spec asks for,
|
|
with no dedicated Binary field needed on the model. -->
|
|
<input class="form-control s_website_form_input" type="file"
|
|
name="birth_certificate" id="admission_birth_certificate"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-0 py-2 col-12 s_website_form_submit text-end s_website_form_no_submit_label" data-name="Submit Button">
|
|
<div class="s_website_form_label"/>
|
|
<span id="s_website_form_result"/>
|
|
<a href="#" role="button" class="btn btn-primary s_website_form_send">Submit Application</a>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mc-adm-steps">
|
|
<h2>What happens after you submit</h2>
|
|
<div class="mc-steps-row">
|
|
<div class="mc-step"><div class="n">1</div><div class="t">Applied</div></div>
|
|
<div class="mc-step"><div class="n">2</div><div class="t">Document Verification</div></div>
|
|
<div class="mc-step"><div class="n">3</div><div class="t">Interview</div></div>
|
|
<div class="mc-step"><div class="n">4</div><div class="t">Offered</div></div>
|
|
<div class="mc-step"><div class="n">5</div><div class="t">Enrolled</div></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<record id="website_page_admission_apply" model="website.page">
|
|
<field name="url">/admissions/apply</field>
|
|
<field name="view_id" ref="admission_apply_page"/>
|
|
<field name="is_published">True</field>
|
|
</record>
|
|
</odoo>
|