feat(v3): restructure project + add complete frontend

- Restructure: move backend from new_project/ to backend/
- Add full React/TypeScript frontend (37 pages, 17 services, 16 type defs, 11 query hooks)
- Add docs/ with SRS specs, user stories, and workflow documentation
- Update .gitignore for new directory layout

Workflows implemented:
  WF1 User Signup, WF2 Placement Test, WF3 Exam Configuration,
  WF4 General English Exam, WF5 Course Generation,
  WF6 Entity Student Onboarding, AI Course Generation,
  Adaptive Learning Engine UI, White-Label Branding, Score Release

Made-with: Cursor
This commit is contained in:
Yamen Ahmad
2026-04-10 17:26:42 +04:00
commit 907a5c0e92
331 changed files with 23511 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="action_all_courses" model="ir.actions.act_window">
<field name="name">All Courses</field>
<field name="res_model">op.course</field>
<field name="view_mode">list,form</field>
</record>
<menuitem id="menu_all_courses"
name="All Courses"
parent="encoach_core.menu_encoach_courses"
action="encoach_course_gen.action_all_courses"
sequence="5"/>
<menuitem id="menu_course_modules"
name="Course Modules"
parent="encoach_core.menu_encoach_courses"
action="encoach_course_gen.action_course_module"
sequence="10"/>
<menuitem id="menu_gap_analysis"
name="Gap Analysis"
parent="encoach_core.menu_encoach_courses"
action="encoach_course_gen.action_gap_profile"
sequence="20"/>
</odoo>

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_course_module_form" model="ir.ui.view">
<field name="name">encoach.course.module.form</field>
<field name="model">encoach.course.module</field>
<field name="arch" type="xml">
<form string="Course Module">
<header>
<field name="status" widget="statusbar" statusbar_visible="locked,available,in_progress,completed"/>
</header>
<sheet>
<group>
<group>
<field name="name"/>
<field name="course_id"/>
<field name="cefr_target"/>
<field name="auto_generated"/>
</group>
<group>
<field name="completion_criteria"/>
<field name="score_threshold" invisible="completion_criteria != 'score_threshold'"/>
<field name="prerequisite_module_id"/>
<field name="sequence"/>
</group>
</group>
<group>
<field name="generation_brief"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_course_module_list" model="ir.ui.view">
<field name="name">encoach.course.module.list</field>
<field name="model">encoach.course.module</field>
<field name="arch" type="xml">
<list string="Course Modules">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="course_id"/>
<field name="cefr_target"/>
<field name="status" widget="badge" decoration-info="status == 'available'" decoration-success="status == 'completed'" decoration-warning="status == 'in_progress'"/>
</list>
</field>
</record>
<record id="view_course_module_search" model="ir.ui.view">
<field name="name">encoach.course.module.search</field>
<field name="model">encoach.course.module</field>
<field name="arch" type="xml">
<search string="Search Course Modules">
<field name="name"/>
<field name="course_id"/>
<separator/>
<filter string="Locked" name="locked" domain="[('status', '=', 'locked')]"/>
<filter string="Available" name="available" domain="[('status', '=', 'available')]"/>
<filter string="In Progress" name="in_progress" domain="[('status', '=', 'in_progress')]"/>
<filter string="Completed" name="completed" domain="[('status', '=', 'completed')]"/>
<filter string="Skipped" name="skipped" domain="[('status', '=', 'skipped')]"/>
<separator/>
<filter string="Course" name="group_course" context="{'group_by': 'course_id'}"/>
</search>
</field>
</record>
<record id="action_course_module" model="ir.actions.act_window">
<field name="name">Course Modules</field>
<field name="res_model">encoach.course.module</field>
<field name="view_mode">list,form</field>
</record>
</odoo>

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_encoach_course_section_form" model="ir.ui.view">
<field name="name">encoach.course.section.form</field>
<field name="model">encoach.course.section</field>
<field name="arch" type="xml">
<form string="Course Section">
<sheet>
<group>
<group>
<field name="name"/>
<field name="course_id"/>
<field name="skill"/>
<field name="sequence"/>
</group>
</group>
<notebook>
<page string="Modules">
<field name="module_ids">
<list>
<field name="name"/>
<field name="cefr_target"/>
<field name="status" widget="badge" decoration-info="status == 'available'" decoration-success="status == 'completed'" decoration-warning="status == 'in_progress'"/>
<field name="sequence"/>
</list>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_encoach_course_section_list" model="ir.ui.view">
<field name="name">encoach.course.section.list</field>
<field name="model">encoach.course.section</field>
<field name="arch" type="xml">
<list string="Course Sections">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="course_id"/>
<field name="skill" widget="badge"/>
</list>
</field>
</record>
<record id="view_encoach_course_section_search" model="ir.ui.view">
<field name="name">encoach.course.section.search</field>
<field name="model">encoach.course.section</field>
<field name="arch" type="xml">
<search string="Search Sections">
<field name="name"/>
<field name="course_id"/>
<separator/>
<filter string="Course" name="group_course" context="{'group_by': 'course_id'}"/>
<filter string="Skill" name="group_skill" context="{'group_by': 'skill'}"/>
</search>
</field>
</record>
<record id="action_encoach_course_section" model="ir.actions.act_window">
<field name="name">Course Sections</field>
<field name="res_model">encoach.course.section</field>
<field name="view_mode">list,form</field>
</record>
</odoo>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="action_gap_analysis_viz" model="ir.actions.client">
<field name="name">Gap Analysis</field>
<field name="tag">encoach_gap_analysis</field>
</record>
<menuitem id="menu_gap_analysis_viz"
name="Gap Analysis (Visual)"
parent="encoach_core.menu_encoach_courses"
action="action_gap_analysis_viz"
sequence="25"/>
</odoo>

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_gap_profile_form" model="ir.ui.view">
<field name="name">encoach.gap.profile.form</field>
<field name="model">encoach.gap.profile</field>
<field name="arch" type="xml">
<form string="Gap Profile">
<sheet>
<group>
<group>
<field name="student_id"/>
<field name="source_type"/>
<field name="source_id"/>
</group>
<group>
<field name="entity_id"/>
<field name="created_at"/>
</group>
</group>
<notebook>
<page string="Skill Gaps" name="skill_gaps">
<field name="skill_gaps"/>
</page>
<page string="Question Type Weaknesses" name="question_type_weaknesses">
<field name="question_type_weaknesses"/>
</page>
<page string="Topic Weaknesses" name="topic_weaknesses">
<field name="topic_weaknesses"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_gap_profile_list" model="ir.ui.view">
<field name="name">encoach.gap.profile.list</field>
<field name="model">encoach.gap.profile</field>
<field name="arch" type="xml">
<list string="Gap Profiles">
<field name="student_id"/>
<field name="source_type"/>
<field name="entity_id"/>
<field name="created_at"/>
</list>
</field>
</record>
<record id="view_gap_profile_search" model="ir.ui.view">
<field name="name">encoach.gap.profile.search</field>
<field name="model">encoach.gap.profile</field>
<field name="arch" type="xml">
<search string="Search Gap Profiles">
<field name="student_id"/>
<separator/>
<filter string="Placement" name="placement" domain="[('source_type', '=', 'placement')]"/>
<filter string="Exam" name="exam" domain="[('source_type', '=', 'exam')]"/>
<separator/>
<filter string="Student" name="group_student" context="{'group_by': 'student_id'}"/>
<filter string="Entity" name="group_entity" context="{'group_by': 'entity_id'}"/>
</search>
</field>
</record>
<record id="action_gap_profile" model="ir.actions.act_window">
<field name="name">Gap Analysis</field>
<field name="res_model">encoach.gap.profile</field>
<field name="view_mode">list,form</field>
</record>
</odoo>