438 lines
26 KiB
XML
438 lines
26 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
||
<odoo>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════
|
||
SHARED: No student record found
|
||
═══════════════════════════════════════════════════════════ -->
|
||
<template id="portal_no_student" name="School: No Student Profile">
|
||
<t t-call="website.layout">
|
||
<div class="container" style="max-width: 520px; margin-top: 60px;">
|
||
<p class="alert alert-info">
|
||
No student profile is linked to your account. Please contact the school coordinator.
|
||
</p>
|
||
</div>
|
||
</t>
|
||
</template>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════
|
||
TEACHER: Assignment List
|
||
═══════════════════════════════════════════════════════════ -->
|
||
<template id="portal_teacher_assignment_list" name="School: Teacher – My Assignments">
|
||
<t t-call="website.layout">
|
||
<div class="container o_school_assignments" style="max-width: 900px; margin-top: 32px; margin-bottom: 80px;">
|
||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||
<h2 class="mb-0">My Assignments</h2>
|
||
<a href="/school/assignments/teacher/new" class="btn btn-primary">
|
||
<i class="fa fa-plus me-1"/> New Assignment
|
||
</a>
|
||
</div>
|
||
|
||
<t t-if="not assignments">
|
||
<div class="alert alert-info">
|
||
No assignments yet. Click <strong>New Assignment</strong> to create one.
|
||
</div>
|
||
</t>
|
||
|
||
<t t-if="assignments">
|
||
<div class="table-responsive">
|
||
<table class="table table-hover align-middle">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th>Title</th>
|
||
<th>Class</th>
|
||
<th>Due Date</th>
|
||
<th>Status</th>
|
||
<th>Submissions</th>
|
||
<th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<t t-foreach="assignments" t-as="asgn">
|
||
<tr>
|
||
<td>
|
||
<strong t-out="asgn.name"/>
|
||
</td>
|
||
<td t-out="asgn.class_id.name"/>
|
||
<td t-out="asgn.due_date" t-options='{"widget": "datetime"}'/>
|
||
<td>
|
||
<t t-if="asgn.state == 'draft'">
|
||
<span class="badge bg-secondary">Draft</span>
|
||
</t>
|
||
<t t-elif="asgn.state == 'published'">
|
||
<span class="badge bg-success">Published</span>
|
||
</t>
|
||
<t t-elif="asgn.state == 'closed'">
|
||
<span class="badge bg-dark">Closed</span>
|
||
</t>
|
||
</td>
|
||
<td>
|
||
<a t-attf-href="/school/assignments/teacher/#{asgn.id}/submissions"
|
||
class="badge bg-primary text-decoration-none">
|
||
<t t-out="asgn.submission_count"/> submitted
|
||
</a>
|
||
</td>
|
||
<td class="text-end">
|
||
<a t-attf-href="/school/assignments/teacher/#{asgn.id}/edit"
|
||
class="btn btn-sm btn-outline-secondary">Edit</a>
|
||
</td>
|
||
</tr>
|
||
</t>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</t>
|
||
</div>
|
||
</t>
|
||
</template>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════
|
||
TEACHER: Create / Edit Assignment Form
|
||
═══════════════════════════════════════════════════════════ -->
|
||
<template id="portal_teacher_assignment_form" name="School: Teacher – Assignment Form">
|
||
<t t-call="website.layout">
|
||
<div class="container o_school_assignment_form" style="max-width: 700px; margin-top: 32px; margin-bottom: 80px;">
|
||
<h2>
|
||
<t t-if="not assignment">New Assignment</t>
|
||
<t t-else="">Edit Assignment</t>
|
||
</h2>
|
||
|
||
<form method="post" enctype="multipart/form-data" class="mt-4">
|
||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold" for="asgn_title">Title <span class="text-danger">*</span></label>
|
||
<input type="text" class="form-control" id="asgn_title" name="title" required="required"
|
||
t-att-value="assignment.name if assignment else ''"/>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold" for="asgn_class">Class <span class="text-danger">*</span></label>
|
||
<select class="form-select" id="asgn_class" name="class_id" required="required">
|
||
<option value="">-- Select Class --</option>
|
||
<t t-foreach="classes" t-as="klass">
|
||
<option t-att-value="klass.id"
|
||
t-att-selected="'selected' if assignment and assignment.class_id.id == klass.id else None"
|
||
t-out="klass.name"/>
|
||
</t>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold" for="asgn_due">Due Date & Time <span class="text-danger">*</span></label>
|
||
<input type="datetime-local" class="form-control" id="asgn_due" name="due_date" required="required"
|
||
t-att-value="assignment.due_date.strftime('%Y-%m-%dT%H:%M') if assignment and assignment.due_date else ''"/>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold" for="asgn_score">Max Score</label>
|
||
<input type="number" class="form-control" id="asgn_score" name="max_score" min="1" max="1000"
|
||
t-att-value="assignment.max_score if assignment else 100"/>
|
||
</div>
|
||
|
||
<div class="mb-4">
|
||
<label class="form-label fw-semibold" for="asgn_desc">Instructions / Description</label>
|
||
<textarea class="form-control" id="asgn_desc" name="description" rows="6">
|
||
<t t-if="assignment and assignment.description" t-out="assignment.description"/>
|
||
</textarea>
|
||
</div>
|
||
|
||
<div class="d-flex gap-2 flex-wrap">
|
||
<button type="submit" class="btn btn-primary" name="action_state" value="publish">
|
||
<t t-if="not assignment">Create & Publish</t>
|
||
<t t-else="">Save & Publish</t>
|
||
</button>
|
||
<t t-if="assignment">
|
||
<t t-if="assignment.state != 'draft'">
|
||
<button type="submit" class="btn btn-outline-secondary" name="action_state" value="draft">
|
||
Set to Draft
|
||
</button>
|
||
</t>
|
||
<t t-if="assignment.state != 'closed'">
|
||
<button type="submit" class="btn btn-outline-dark" name="action_state" value="close">
|
||
Close Assignment
|
||
</button>
|
||
</t>
|
||
</t>
|
||
<a href="/school/assignments/teacher" class="btn btn-link text-muted">Cancel</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</t>
|
||
</template>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════
|
||
TEACHER: View Submissions for an Assignment
|
||
═══════════════════════════════════════════════════════════ -->
|
||
<template id="portal_teacher_submissions" name="School: Teacher – Submissions">
|
||
<t t-call="website.layout">
|
||
<div class="container o_school_submissions" style="max-width: 950px; margin-top: 32px; margin-bottom: 80px;">
|
||
<a href="/school/assignments/teacher" class="btn btn-sm btn-outline-secondary mb-3">
|
||
← Back to Assignments
|
||
</a>
|
||
<h2 t-out="assignment.name"/>
|
||
<p class="text-muted mb-1">
|
||
Class: <strong t-out="assignment.class_id.name"/>
|
||
· Due: <span t-out="assignment.due_date" t-options='{"widget": "datetime"}'/>
|
||
· Max Score: <strong t-out="assignment.max_score"/>
|
||
</p>
|
||
|
||
<t t-if="not submissions">
|
||
<div class="alert alert-info mt-4">No submissions received yet.</div>
|
||
</t>
|
||
|
||
<t t-if="submissions">
|
||
<div class="table-responsive mt-4">
|
||
<table class="table table-hover align-middle">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th>Student</th>
|
||
<th>Submitted On</th>
|
||
<th>Late?</th>
|
||
<th>File</th>
|
||
<th>Notes</th>
|
||
<th>Score</th>
|
||
<th>Status</th>
|
||
<th>Grade</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<t t-foreach="submissions" t-as="sub">
|
||
<tr>
|
||
<td t-out="sub.student_id.partner_id.name"/>
|
||
<td t-out="sub.submitted_on" t-options='{"widget": "datetime"}'/>
|
||
<td>
|
||
<t t-if="sub.is_late">
|
||
<span class="badge bg-warning text-dark">Late</span>
|
||
</t>
|
||
<t t-else="">
|
||
<span class="text-muted">—</span>
|
||
</t>
|
||
</td>
|
||
<td>
|
||
<t t-if="sub.attachment_name">
|
||
<a t-attf-href="/web/content/community.school.submission/#{sub.id}/attachment_data/#{sub.attachment_name}?download=true"
|
||
class="btn btn-sm btn-outline-primary">
|
||
<i class="fa fa-download me-1"/>
|
||
<t t-out="sub.attachment_name"/>
|
||
</a>
|
||
</t>
|
||
<t t-else=""><span class="text-muted">No file</span></t>
|
||
</td>
|
||
<td>
|
||
<t t-if="sub.notes">
|
||
<small t-out="sub.notes"/>
|
||
</t>
|
||
<t t-else=""><span class="text-muted">—</span></t>
|
||
</td>
|
||
<td>
|
||
<t t-if="sub.state == 'graded'">
|
||
<strong t-out="sub.score"/> / <t t-out="assignment.max_score"/>
|
||
</t>
|
||
<t t-else=""><span class="text-muted">—</span></t>
|
||
</td>
|
||
<td>
|
||
<t t-if="sub.state == 'graded'">
|
||
<span class="badge bg-success">Graded</span>
|
||
</t>
|
||
<t t-else="">
|
||
<span class="badge bg-secondary">Pending</span>
|
||
</t>
|
||
</td>
|
||
<td>
|
||
<!-- Inline grade form -->
|
||
<form t-attf-action="/school/assignments/teacher/submission/#{sub.id}/grade"
|
||
method="post" class="d-flex gap-1 align-items-center">
|
||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||
<input type="number" name="score" class="form-control form-control-sm"
|
||
style="width: 64px;"
|
||
t-att-value="sub.score if sub.state == 'graded' else ''"
|
||
t-att-max="assignment.max_score" min="0" placeholder="Pts"/>
|
||
<input type="text" name="feedback" class="form-control form-control-sm"
|
||
style="width: 110px;"
|
||
t-att-value="sub.feedback or ''"
|
||
placeholder="Feedback"/>
|
||
<button type="submit" class="btn btn-sm btn-success">✓</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
</t>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</t>
|
||
</div>
|
||
</t>
|
||
</template>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════
|
||
STUDENT: Assignment List
|
||
═══════════════════════════════════════════════════════════ -->
|
||
<template id="portal_student_assignment_list" name="School: Student – Assignments">
|
||
<t t-call="website.layout">
|
||
<div class="container o_school_student_assignments" style="max-width: 860px; margin-top: 32px; margin-bottom: 80px;">
|
||
<h2 class="mb-4">My Assignments</h2>
|
||
|
||
<t t-if="not assignments">
|
||
<div class="alert alert-info">No assignments have been published for your class yet.</div>
|
||
</t>
|
||
|
||
<t t-if="assignments">
|
||
<div class="row g-3">
|
||
<t t-foreach="assignments" t-as="asgn">
|
||
<div class="col-md-6">
|
||
<t t-set="my_sub" t-value="submission_by_assignment.get(asgn.id)"/>
|
||
<div class="card h-100 shadow-sm border-0">
|
||
<div class="card-body">
|
||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||
<h5 class="card-title mb-0" t-out="asgn.name"/>
|
||
<t t-if="my_sub and my_sub.state == 'graded'">
|
||
<span class="badge bg-success">Graded</span>
|
||
</t>
|
||
<t t-elif="my_sub">
|
||
<span class="badge bg-primary">Submitted</span>
|
||
</t>
|
||
<t t-else="">
|
||
<span class="badge bg-warning text-dark">Pending</span>
|
||
</t>
|
||
</div>
|
||
<p class="text-muted small mb-1">
|
||
Class: <strong t-out="asgn.class_id.name"/>
|
||
</p>
|
||
<p class="text-muted small mb-2">
|
||
Due: <strong t-out="asgn.due_date" t-options='{"widget": "datetime"}'/>
|
||
</p>
|
||
<t t-if="my_sub and my_sub.state == 'graded'">
|
||
<p class="mb-2">
|
||
Score: <strong t-out="my_sub.score"/> / <t t-out="asgn.max_score"/>
|
||
</p>
|
||
<t t-if="my_sub.feedback">
|
||
<p class="text-muted small mb-2">
|
||
Feedback: <em t-out="my_sub.feedback"/>
|
||
</p>
|
||
</t>
|
||
</t>
|
||
</div>
|
||
<div class="card-footer bg-transparent border-0 pt-0">
|
||
<a t-attf-href="/school/assignments/student/#{asgn.id}"
|
||
class="btn btn-sm btn-primary w-100">
|
||
<t t-if="not my_sub">View & Submit</t>
|
||
<t t-else="">View Submission</t>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</t>
|
||
</div>
|
||
</t>
|
||
</div>
|
||
</t>
|
||
</template>
|
||
|
||
<!-- ═══════════════════════════════════════════════════════════
|
||
STUDENT: Assignment Detail & Upload Form
|
||
═══════════════════════════════════════════════════════════ -->
|
||
<template id="portal_student_assignment_detail" name="School: Student – Assignment Detail">
|
||
<t t-call="website.layout">
|
||
<div class="container o_school_assignment_detail" style="max-width: 720px; margin-top: 32px; margin-bottom: 80px;">
|
||
<a href="/school/assignments/student" class="btn btn-sm btn-outline-secondary mb-3">
|
||
← Back to Assignments
|
||
</a>
|
||
|
||
<!-- Assignment header -->
|
||
<div class="card border-0 shadow-sm mb-4">
|
||
<div class="card-body">
|
||
<h3 t-out="assignment.name"/>
|
||
<p class="text-muted mb-1">
|
||
Class: <strong t-out="assignment.class_id.name"/>
|
||
</p>
|
||
<p class="text-muted mb-1">
|
||
Due: <strong t-out="assignment.due_date" t-options='{"widget": "datetime"}'/>
|
||
</p>
|
||
<p class="text-muted mb-0">
|
||
Max Score: <strong t-out="assignment.max_score"/>
|
||
</p>
|
||
<t t-if="assignment.description">
|
||
<hr/>
|
||
<div t-out="assignment.description"/>
|
||
</t>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Already submitted view -->
|
||
<t t-if="submission">
|
||
<div class="card border-0 shadow-sm">
|
||
<div class="card-body">
|
||
<h5>Your Submission</h5>
|
||
<p class="text-muted small">
|
||
Submitted on: <t t-out="submission.submitted_on" t-options='{"widget": "datetime"}'/>
|
||
<t t-if="submission.is_late">
|
||
<span class="badge bg-warning text-dark ms-2">Late</span>
|
||
</t>
|
||
</p>
|
||
<t t-if="submission.notes">
|
||
<p><strong>Your Notes:</strong><br/><t t-out="submission.notes"/></p>
|
||
</t>
|
||
<t t-if="submission.attachment_name">
|
||
<a t-attf-href="/web/content/community.school.submission/#{submission.id}/attachment_data/#{submission.attachment_name}?download=true"
|
||
class="btn btn-outline-primary">
|
||
<i class="fa fa-download me-1"/> Download: <t t-out="submission.attachment_name"/>
|
||
</a>
|
||
</t>
|
||
|
||
<!-- Graded result -->
|
||
<t t-if="submission.state == 'graded'">
|
||
<hr/>
|
||
<div class="alert alert-success mb-0">
|
||
<h6 class="alert-heading">Graded ✓</h6>
|
||
<p class="mb-1">
|
||
Score: <strong t-out="submission.score"/> / <t t-out="assignment.max_score"/>
|
||
</p>
|
||
<t t-if="submission.feedback">
|
||
<p class="mb-0">Feedback: <em t-out="submission.feedback"/></p>
|
||
</t>
|
||
</div>
|
||
</t>
|
||
<t t-else="">
|
||
<div class="alert alert-info mt-3 mb-0">
|
||
Your submission is awaiting grading.
|
||
</div>
|
||
</t>
|
||
</div>
|
||
</div>
|
||
</t>
|
||
|
||
<!-- Submit form (shown only if not yet submitted) -->
|
||
<t t-if="not submission">
|
||
<div class="card border-0 shadow-sm">
|
||
<div class="card-body">
|
||
<h5>Submit Your Work</h5>
|
||
<form t-attf-action="/school/assignments/student/#{assignment.id}"
|
||
method="post" enctype="multipart/form-data">
|
||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold" for="sub_file">
|
||
Upload File <span class="text-muted fw-normal">(PDF, DOC, JPG, PNG, etc.)</span>
|
||
</label>
|
||
<input type="file" class="form-control" id="sub_file" name="submission_file"/>
|
||
</div>
|
||
|
||
<div class="mb-4">
|
||
<label class="form-label fw-semibold" for="sub_notes">Notes (optional)</label>
|
||
<textarea class="form-control" id="sub_notes" name="notes" rows="4"
|
||
placeholder="Add any notes or comments for your teacher…"/>
|
||
</div>
|
||
|
||
<button type="submit" class="btn btn-primary">
|
||
<i class="fa fa-upload me-1"/> Submit Assignment
|
||
</button>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</t>
|
||
</div>
|
||
</t>
|
||
</template>
|
||
|
||
</odoo>
|