Add workplace handbook and improve appointment booking
This commit is contained in:
parent
597b1bfddb
commit
27320bc0ac
@ -14,6 +14,11 @@
|
||||
"views/appointment_templates.xml",
|
||||
"views/menu.xml",
|
||||
],
|
||||
"assets": {
|
||||
"web.assets_frontend": [
|
||||
"mcs_appointment_booking/static/src/scss/appointment_booking.scss",
|
||||
],
|
||||
},
|
||||
"installable": True,
|
||||
"application": True,
|
||||
}
|
||||
|
||||
@ -0,0 +1,320 @@
|
||||
.mcs-appointment {
|
||||
--mcs-appt-ink: #111827;
|
||||
--mcs-appt-muted: #667085;
|
||||
--mcs-appt-line: #d9e0ea;
|
||||
--mcs-appt-soft: #f5f7fb;
|
||||
--mcs-appt-panel: #ffffff;
|
||||
--mcs-appt-blue: #1c4ed8;
|
||||
--mcs-appt-navy: #071426;
|
||||
--mcs-appt-teal: #0f766e;
|
||||
|
||||
min-height: calc(100vh - 76px);
|
||||
padding: 42px 18px 72px;
|
||||
background: linear-gradient(180deg, #f8fafc 0%, #eef3f8 100%);
|
||||
color: var(--mcs-appt-ink);
|
||||
font-family: Inter, Manrope, "Segoe UI", sans-serif;
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
letter-spacing: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-appt-shell {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(280px, 380px) minmax(0, 1fr);
|
||||
gap: 0;
|
||||
max-width: 1180px;
|
||||
margin: 0 auto;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--mcs-appt-line);
|
||||
border-radius: 16px;
|
||||
background: var(--mcs-appt-panel);
|
||||
box-shadow: 0 24px 70px rgba(17, 24, 39, 0.12);
|
||||
}
|
||||
|
||||
.mcs-appt-info {
|
||||
padding: 34px;
|
||||
background: var(--mcs-appt-navy);
|
||||
color: #ffffff;
|
||||
|
||||
h1 {
|
||||
margin: 18px 0 18px;
|
||||
font-size: clamp(2rem, 5vw, 3.45rem);
|
||||
line-height: 1.04;
|
||||
font-weight: 820;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-appt-back {
|
||||
display: inline-flex;
|
||||
margin-bottom: 28px;
|
||||
color: rgba(255, 255, 255, 0.82);
|
||||
font-weight: 800;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.mcs-appt-kicker {
|
||||
color: #f1b94d;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
}
|
||||
|
||||
.mcs-appt-meta {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
color: rgba(255, 255, 255, 0.78);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mcs-appt-description {
|
||||
margin-top: 24px;
|
||||
color: rgba(255, 255, 255, 0.78);
|
||||
|
||||
p {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-appt-note {
|
||||
margin-top: 28px;
|
||||
padding: 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||
border-radius: 10px;
|
||||
background: rgba(255, 255, 255, 0.07);
|
||||
color: rgba(255, 255, 255, 0.82);
|
||||
}
|
||||
|
||||
.mcs-appt-booking {
|
||||
min-width: 0;
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
.mcs-appt-section-head {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 14px;
|
||||
|
||||
span {
|
||||
color: var(--mcs-appt-blue);
|
||||
font-size: 0.76rem;
|
||||
font-weight: 900;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 1.24rem;
|
||||
line-height: 1.25;
|
||||
font-weight: 840;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-appt-section-head--spaced {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.mcs-appt-date-strip {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(82px, 1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.mcs-appt-date {
|
||||
display: grid;
|
||||
min-height: 82px;
|
||||
place-items: center;
|
||||
padding: 10px;
|
||||
border: 1px solid var(--mcs-appt-line);
|
||||
border-radius: 10px;
|
||||
background: #ffffff;
|
||||
color: var(--mcs-appt-muted);
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
transition: border-color 160ms ease-out, background 160ms ease-out, color 160ms ease-out, transform 160ms ease-out;
|
||||
|
||||
strong,
|
||||
span,
|
||||
small {
|
||||
display: block;
|
||||
}
|
||||
|
||||
span {
|
||||
color: var(--mcs-appt-ink);
|
||||
font-size: 1.35rem;
|
||||
line-height: 1;
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
small {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-color: var(--mcs-appt-blue);
|
||||
background: #eef4ff;
|
||||
color: var(--mcs-appt-blue);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
.mcs-appt-date:hover {
|
||||
border-color: var(--mcs-appt-blue);
|
||||
color: var(--mcs-appt-blue);
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-appt-empty {
|
||||
padding: 18px;
|
||||
border: 1px dashed var(--mcs-appt-line);
|
||||
border-radius: 10px;
|
||||
color: var(--mcs-appt-muted);
|
||||
background: var(--mcs-appt-soft);
|
||||
}
|
||||
|
||||
.mcs-appt-slot-list {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.mcs-appt-slot {
|
||||
input[type="radio"] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
min-height: 56px;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid var(--mcs-appt-line);
|
||||
border-radius: 10px;
|
||||
background: #ffffff;
|
||||
color: var(--mcs-appt-ink);
|
||||
cursor: pointer;
|
||||
transition: border-color 160ms ease-out, background 160ms ease-out, transform 160ms ease-out;
|
||||
|
||||
span {
|
||||
font-size: 1.05rem;
|
||||
font-weight: 850;
|
||||
}
|
||||
|
||||
small {
|
||||
color: var(--mcs-appt-teal);
|
||||
font-weight: 800;
|
||||
}
|
||||
}
|
||||
|
||||
label:active {
|
||||
transform: scale(0.99);
|
||||
}
|
||||
|
||||
input[type="radio"]:checked + label {
|
||||
border-color: var(--mcs-appt-blue);
|
||||
background: #eef4ff;
|
||||
}
|
||||
|
||||
input[type="radio"]:checked ~ .mcs-appt-form {
|
||||
display: grid;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-appt-form {
|
||||
display: none;
|
||||
gap: 16px;
|
||||
margin-top: 10px;
|
||||
padding: 18px;
|
||||
border: 1px solid #cbd8f7;
|
||||
border-radius: 12px;
|
||||
background: #f8fbff;
|
||||
}
|
||||
|
||||
.mcs-appt-form-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14px;
|
||||
|
||||
label {
|
||||
display: grid;
|
||||
gap: 7px;
|
||||
color: var(--mcs-appt-muted);
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
width: 100%;
|
||||
min-height: 46px;
|
||||
border: 1px solid var(--mcs-appt-line);
|
||||
border-radius: 10px;
|
||||
padding: 11px 12px;
|
||||
background: #ffffff;
|
||||
color: var(--mcs-appt-ink);
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 104px;
|
||||
resize: vertical;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-appt-form-wide {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.mcs-appt-form button {
|
||||
justify-self: start;
|
||||
min-height: 46px;
|
||||
border: 0;
|
||||
border-radius: 10px;
|
||||
padding: 0 18px;
|
||||
background: var(--mcs-appt-blue);
|
||||
color: #ffffff;
|
||||
font-weight: 850;
|
||||
box-shadow: 0 10px 24px rgba(28, 78, 216, 0.2);
|
||||
transition: transform 160ms ease-out, background 160ms ease-out;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.mcs-appt-shell {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.mcs-appt-info,
|
||||
.mcs-appt-booking {
|
||||
padding: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.mcs-appointment {
|
||||
padding: 18px 12px 42px;
|
||||
}
|
||||
|
||||
.mcs-appt-date-strip,
|
||||
.mcs-appt-form-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.mcs-appt-section-head {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
@ -32,60 +32,76 @@
|
||||
|
||||
<template id="appointment_type_page" name="Appointment Booking Page">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap" class="oe_structure">
|
||||
<section class="container py-5">
|
||||
<a href="/appointments" class="btn btn-link px-0 mb-3">Back to appointments</a>
|
||||
<h1 class="mb-2" t-esc="appointment_type.name"/>
|
||||
<p class="text-muted mb-4">
|
||||
<span t-esc="appointment_type.duration"/> hour appointment
|
||||
</p>
|
||||
<div id="wrap" class="mcs-appointment">
|
||||
<section class="mcs-appt-shell">
|
||||
<aside class="mcs-appt-info">
|
||||
<a href="/appointments" class="mcs-appt-back">Back to appointments</a>
|
||||
<span class="mcs-appt-kicker">Metatroncube consultation</span>
|
||||
<h1 t-esc="appointment_type.name"/>
|
||||
<div class="mcs-appt-meta">
|
||||
<span><t t-esc="appointment_type.duration"/> hour appointment</span>
|
||||
<span t-esc="appointment_type.user_id.name"/>
|
||||
</div>
|
||||
<div t-if="appointment_type.description" class="mcs-appt-description">
|
||||
<t t-out="appointment_type.description"/>
|
||||
</div>
|
||||
<div class="mcs-appt-note">
|
||||
Select a date, choose one available time, then enter your details to confirm the meeting.
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="mb-4 d-flex flex-wrap gap-2">
|
||||
<t t-foreach="day_options" t-as="day">
|
||||
<a t-attf-class="btn #{'btn-primary' if day == selected_day else 'btn-outline-primary'}"
|
||||
t-att-href="'/appointments/%s?date=%s' % (appointment_type.id, day.isoformat())">
|
||||
<t t-esc="day.strftime('%a %d %b')"/>
|
||||
</a>
|
||||
</t>
|
||||
</div>
|
||||
<main class="mcs-appt-booking">
|
||||
<header class="mcs-appt-section-head">
|
||||
<span>Step 1</span>
|
||||
<h2>Select a date</h2>
|
||||
</header>
|
||||
<div class="mcs-appt-date-strip">
|
||||
<t t-foreach="day_options" t-as="day">
|
||||
<a t-attf-class="mcs-appt-date #{'active' if day == selected_day else ''}"
|
||||
t-att-href="'/appointments/%s?date=%s' % (appointment_type.id, day.isoformat())">
|
||||
<strong t-esc="day.strftime('%a')"/>
|
||||
<span t-esc="day.strftime('%d')"/>
|
||||
<small t-esc="day.strftime('%b')"/>
|
||||
</a>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
<div t-if="not slots" class="alert alert-info">
|
||||
No free slots are available on this day.
|
||||
</div>
|
||||
<header class="mcs-appt-section-head mcs-appt-section-head--spaced">
|
||||
<span>Step 2</span>
|
||||
<h2>Choose a time on <t t-esc="selected_day.strftime('%A, %d %B')"/></h2>
|
||||
</header>
|
||||
|
||||
<div class="row g-3">
|
||||
<t t-foreach="slots" t-as="slot">
|
||||
<div class="col-12 col-lg-6">
|
||||
<form t-att-action="'/appointments/%s/book' % appointment_type.id" method="post" class="card">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<input type="hidden" name="start" t-att-value="slot['start']"/>
|
||||
<input type="hidden" name="stop" t-att-value="slot['stop']"/>
|
||||
<div class="card-body">
|
||||
<h2 class="h5 mb-3" t-esc="slot['label']"/>
|
||||
<div class="row g-2">
|
||||
<div class="col-12">
|
||||
<label class="form-label">Name</label>
|
||||
<input class="form-control" name="customer_name" required="required"/>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label">Email</label>
|
||||
<input class="form-control" name="customer_email" type="email" required="required"/>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label">Phone</label>
|
||||
<input class="form-control" name="customer_phone"/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label">Notes</label>
|
||||
<textarea class="form-control" name="notes" rows="2"/>
|
||||
</div>
|
||||
<div t-if="not slots" class="mcs-appt-empty">
|
||||
No free slots are available on this day.
|
||||
</div>
|
||||
|
||||
<div t-if="slots" class="mcs-appt-slot-list">
|
||||
<t t-foreach="slots" t-as="slot">
|
||||
<div class="mcs-appt-slot">
|
||||
<input type="radio"
|
||||
name="selected_slot"
|
||||
t-att-id="'mcs_slot_%s' % slot_index"
|
||||
t-att-checked="'checked' if slot_index == 0 else None"/>
|
||||
<label t-att-for="'mcs_slot_%s' % slot_index">
|
||||
<span t-esc="slot['label']"/>
|
||||
<small>Available</small>
|
||||
</label>
|
||||
<form t-att-action="'/appointments/%s/book' % appointment_type.id" method="post" class="mcs-appt-form">
|
||||
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
||||
<input type="hidden" name="start" t-att-value="slot['start']"/>
|
||||
<input type="hidden" name="stop" t-att-value="slot['stop']"/>
|
||||
<div class="mcs-appt-form-grid">
|
||||
<label>Name<input name="customer_name" required="required"/></label>
|
||||
<label>Email<input name="customer_email" type="email" required="required"/></label>
|
||||
<label>Phone<input name="customer_phone"/></label>
|
||||
<label class="mcs-appt-form-wide">Notes<textarea name="notes" rows="3"/></label>
|
||||
</div>
|
||||
<button class="btn btn-primary mt-3" type="submit">Book Appointment</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<button type="submit">Confirm Appointment</button>
|
||||
</form>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</main>
|
||||
</section>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@ -61,6 +61,7 @@ class McsCorporateWebsite(http.Controller):
|
||||
("Dashboard", "/workplace", "dashboard"),
|
||||
("Attendance", "/workplace/attendance", "attendance"),
|
||||
("Leave", "/workplace/leave", "leave"),
|
||||
("Handbook", "/workplace/handbook", "handbook"),
|
||||
("Support", "/workplace/tickets", "support"),
|
||||
("Knowledge", "/workplace/knowledge", "knowledge"),
|
||||
("Announcements", "/workplace/announcements", "announcements"),
|
||||
@ -269,6 +270,12 @@ class McsCorporateWebsite(http.Controller):
|
||||
leaves = request.env["hr.leave"].sudo().search([("employee_id", "=", employee.id)], order="request_date_from desc", limit=20)
|
||||
return self._workplace_render("mcs_corporate_website.workplace_leave", {"leave_balance": self._leave_balance(employee), "leaves": leaves})
|
||||
|
||||
@http.route(["/workplace/handbook"], type="http", auth="user", website=True)
|
||||
def workplace_handbook(self, **kwargs):
|
||||
if self._role() == "client":
|
||||
return request.redirect("/workplace")
|
||||
return self._workplace_render("mcs_corporate_website.workplace_handbook")
|
||||
|
||||
@http.route(["/workplace/tickets", "/workplace/support-queue"], type="http", auth="user", website=True)
|
||||
def workplace_tickets(self, **kwargs):
|
||||
role = self._role()
|
||||
|
||||
@ -0,0 +1,194 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>MCS Operating System Handbook</title>
|
||||
<style>
|
||||
:root {
|
||||
--ink: #15171d;
|
||||
--muted: #606a7a;
|
||||
--line: #dfe5ee;
|
||||
--paper: #ffffff;
|
||||
--surface: #f5f7fa;
|
||||
--brand: #1c4ed8;
|
||||
--navy: #081426;
|
||||
--accent: #d9931e;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Inter, Segoe UI, Arial, sans-serif;
|
||||
color: var(--ink);
|
||||
background: var(--surface);
|
||||
line-height: 1.65;
|
||||
}
|
||||
header {
|
||||
background: var(--navy);
|
||||
color: #fff;
|
||||
padding: 44px clamp(20px, 5vw, 72px);
|
||||
}
|
||||
header p { max-width: 880px; margin: 12px 0 0; color: rgba(255,255,255,.78); }
|
||||
h1 { margin: 0; font-size: clamp(2rem, 5vw, 4rem); line-height: 1.05; letter-spacing: 0; }
|
||||
h2 { margin: 0 0 12px; font-size: 1.45rem; letter-spacing: 0; }
|
||||
h3 { margin: 0 0 8px; font-size: 1rem; letter-spacing: 0; }
|
||||
main {
|
||||
display: grid;
|
||||
grid-template-columns: 260px minmax(0, 1fr);
|
||||
gap: 24px;
|
||||
max-width: 1220px;
|
||||
margin: 0 auto;
|
||||
padding: 28px clamp(16px, 4vw, 40px) 64px;
|
||||
}
|
||||
nav {
|
||||
position: sticky;
|
||||
top: 20px;
|
||||
align-self: start;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
padding: 16px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
background: var(--paper);
|
||||
}
|
||||
nav a {
|
||||
color: var(--muted);
|
||||
text-decoration: none;
|
||||
font-weight: 700;
|
||||
padding: 8px 10px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
nav a:hover { color: var(--ink); background: #eef3ff; }
|
||||
article {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
}
|
||||
section {
|
||||
padding: 24px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
background: var(--paper);
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.grid div {
|
||||
padding: 14px;
|
||||
border-left: 4px solid var(--brand);
|
||||
background: #f8fafc;
|
||||
}
|
||||
.grid span { display: block; color: var(--muted); }
|
||||
li + li { margin-top: 7px; }
|
||||
strong { color: var(--ink); }
|
||||
.note {
|
||||
border-left: 4px solid var(--accent);
|
||||
color: var(--muted);
|
||||
background: #fff8ea;
|
||||
}
|
||||
@media (max-width: 820px) {
|
||||
main { grid-template-columns: 1fr; }
|
||||
nav { position: static; }
|
||||
.grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<h1>MCS Operating System Handbook</h1>
|
||||
<p>A practical guide for Metatroncube employees and managers to run attendance, timesheets, leave, client support, delivery tasks, and operating reviews inside the Workplace system.</p>
|
||||
</header>
|
||||
<main>
|
||||
<nav aria-label="Handbook sections">
|
||||
<a href="#daily">Daily Flow</a>
|
||||
<a href="#roles">Roles</a>
|
||||
<a href="#attendance">Attendance</a>
|
||||
<a href="#work-hours">Work Hours</a>
|
||||
<a href="#leave">Leave</a>
|
||||
<a href="#tasks">Tasks</a>
|
||||
<a href="#support">Support</a>
|
||||
<a href="#reviews">Reviews</a>
|
||||
</nav>
|
||||
<article>
|
||||
<section id="daily">
|
||||
<h2>Daily Operating Flow</h2>
|
||||
<ol>
|
||||
<li>Start the day by checking attendance status, assigned work, blockers, and support tickets.</li>
|
||||
<li>Pick the highest-impact client or internal task and update its next action before starting work.</li>
|
||||
<li>Log work hours against the correct project/task on the same day the work is done.</li>
|
||||
<li>Raise blockers immediately with clear owner, context, and required decision.</li>
|
||||
<li>End the day by checking out, updating task status, and confirming tomorrow's first priority.</li>
|
||||
</ol>
|
||||
</section>
|
||||
<section id="roles">
|
||||
<h2>Roles and Responsibilities</h2>
|
||||
<div class="grid">
|
||||
<div><h3>Delivery Team</h3><span>Own task progress, timesheets, client updates, blockers, and delivery quality.</span></div>
|
||||
<div><h3>Support Team</h3><span>Own ticket triage, customer communication, SLA follow-up, and closure notes.</span></div>
|
||||
<div><h3>Managers</h3><span>Own priorities, assignment, weekly review, escalations, and client health.</span></div>
|
||||
<div><h3>CEO/Admin</h3><span>Own revenue board, company-level blockers, payroll, contracts, and operating cadence.</span></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="attendance">
|
||||
<h2>Attendance</h2>
|
||||
<ul>
|
||||
<li>Use Workplace > Attendance or the Odoo Attendance app to check in when work starts.</li>
|
||||
<li>Check out when the workday ends. Do not leave open attendance records overnight.</li>
|
||||
<li>If a check-in or check-out is missed, inform the manager on the same day with the corrected time.</li>
|
||||
<li>Attendance confirms availability; timesheets confirm what work was delivered.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="work-hours">
|
||||
<h2>Work Hours and Timesheets</h2>
|
||||
<ul>
|
||||
<li>Log every work block to the correct project and task.</li>
|
||||
<li>Use billable delivery for client work, client support for customer issues, sales support for proposals, internal product for product work, and admin only for operational work.</li>
|
||||
<li>Each entry should explain the actual output, not only the activity name.</li>
|
||||
<li>Submit timesheets daily; stale timesheets reduce dashboard accuracy.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="leave">
|
||||
<h2>Leave and Time Off</h2>
|
||||
<ul>
|
||||
<li>Request planned leave from Workplace > Leave before the leave date.</li>
|
||||
<li>Emergency leave must be reported to the manager as early as possible and entered into Odoo afterward.</li>
|
||||
<li>Managers approve or reject leave based on delivery coverage and team capacity.</li>
|
||||
<li>Do not assign urgent work to a person with approved leave unless the leave is cancelled.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="tasks">
|
||||
<h2>Task Handling Rules</h2>
|
||||
<ul>
|
||||
<li>Every task needs an assignee, deadline, project, clear title, and next action.</li>
|
||||
<li>Move blocked work to the correct status and describe the blocker, decision needed, and owner.</li>
|
||||
<li>Client-facing tasks must be linked to the right client company and workstream.</li>
|
||||
<li>Managers review overdue, blocked, stale, and no-next-action tasks every working day.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="support">
|
||||
<h2>Support Ticket Handling</h2>
|
||||
<ul>
|
||||
<li>Clients create tickets from Workplace or the public Support page.</li>
|
||||
<li>Support agents triage priority, service area, owner, and first response.</li>
|
||||
<li>All replies and attachments stay inside the ticket conversation.</li>
|
||||
<li>Close a ticket only after the customer issue is resolved or a manager approves closure.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="reviews">
|
||||
<h2>Review Cadence</h2>
|
||||
<ul>
|
||||
<li>Daily: attendance, open blockers, urgent tickets, overdue receivables.</li>
|
||||
<li>Weekly: project health, delivery capacity, client risks, revenue pipeline.</li>
|
||||
<li>Monthly: payroll, payslips, collected revenue, unpaid invoices, team utilization.</li>
|
||||
<li>CEO Dashboard is the source of truth for operating review, not separate manual sheets.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section class="note">
|
||||
<h2>Operating Standard</h2>
|
||||
<p>Work is considered properly handled only when the task, owner, date, status, blocker, and time entry are visible in the system. Verbal updates are useful, but they do not replace system updates.</p>
|
||||
</section>
|
||||
</article>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@ -906,6 +906,88 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mcs-handbook {
|
||||
display: grid;
|
||||
grid-template-columns: 220px minmax(0, 1fr);
|
||||
gap: 22px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.mcs-handbook-toc {
|
||||
position: sticky;
|
||||
top: 92px;
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
padding: 14px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 14px;
|
||||
background: var(--surface);
|
||||
box-shadow: 0 14px 38px rgba(17, 24, 39, 0.06);
|
||||
|
||||
a {
|
||||
padding: 9px 10px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-secondary);
|
||||
font-weight: 800;
|
||||
text-decoration: none;
|
||||
transition: color 160ms ease-out, background 160ms ease-out;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--text-primary);
|
||||
background: #EEF2FF;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-handbook-body {
|
||||
display: grid;
|
||||
gap: 26px;
|
||||
|
||||
section {
|
||||
padding-bottom: 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
section:last-child {
|
||||
padding-bottom: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0 0 12px;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 840;
|
||||
}
|
||||
|
||||
li + li {
|
||||
margin-top: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
.mcs-handbook-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
|
||||
div {
|
||||
padding: 14px;
|
||||
border: 1px solid var(--border);
|
||||
border-left: 4px solid var(--accent);
|
||||
border-radius: 10px;
|
||||
background: #F8FAFC;
|
||||
}
|
||||
|
||||
strong,
|
||||
span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-top: 5px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.mcs-workplace {
|
||||
grid-template-columns: 1fr;
|
||||
@ -919,10 +1001,16 @@
|
||||
.mcs-wp-grid--2,
|
||||
.mcs-wp-grid--3,
|
||||
.mcs-wp-grid--4,
|
||||
.mcs-wp-two {
|
||||
.mcs-wp-two,
|
||||
.mcs-handbook,
|
||||
.mcs-handbook-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.mcs-handbook-toc {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.mcs-wp-content {
|
||||
padding: 22px;
|
||||
}
|
||||
|
||||
@ -169,6 +169,105 @@
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="workplace_handbook" name="MCS Operating System Handbook">
|
||||
<t t-call="mcs_corporate_website.workplace_layout">
|
||||
<section class="mcs-wp-page-head">
|
||||
<div>
|
||||
<p>Operating handbook</p>
|
||||
<h1>MCS Operating System</h1>
|
||||
</div>
|
||||
<a class="mcs-wp-btn mcs-wp-btn--secondary" href="/mcs_corporate_website/static/handbook/mcs-operating-system-handbook.html" target="_blank">Open HTML Handbook</a>
|
||||
</section>
|
||||
<section class="mcs-handbook">
|
||||
<aside class="mcs-handbook-toc">
|
||||
<a href="#daily">Daily Flow</a>
|
||||
<a href="#roles">Roles</a>
|
||||
<a href="#attendance">Attendance</a>
|
||||
<a href="#work-hours">Work Hours</a>
|
||||
<a href="#leave">Leave</a>
|
||||
<a href="#tasks">Tasks</a>
|
||||
<a href="#support">Support</a>
|
||||
<a href="#reviews">Reviews</a>
|
||||
</aside>
|
||||
<article class="mcs-wp-card mcs-handbook-body">
|
||||
<section id="daily">
|
||||
<h2>Daily Operating Flow</h2>
|
||||
<ol>
|
||||
<li>Start the day by checking attendance status, assigned work, blocked items, and support tickets.</li>
|
||||
<li>Pick the highest-impact client or internal task and update its next action before starting work.</li>
|
||||
<li>Log work hours against the correct project/task on the same day the work is done.</li>
|
||||
<li>Raise blockers immediately with clear owner, context, and required decision.</li>
|
||||
<li>End the day by checking out, updating task status, and confirming tomorrow's first priority.</li>
|
||||
</ol>
|
||||
</section>
|
||||
<section id="roles">
|
||||
<h2>Roles and Responsibilities</h2>
|
||||
<div class="mcs-handbook-grid">
|
||||
<div><strong>Delivery Team</strong><span>Own task progress, timesheets, client updates, blockers, and delivery quality.</span></div>
|
||||
<div><strong>Support Team</strong><span>Own ticket triage, customer communication, SLA follow-up, and closure notes.</span></div>
|
||||
<div><strong>Managers</strong><span>Own priorities, assignment, weekly review, escalations, and client health.</span></div>
|
||||
<div><strong>CEO/Admin</strong><span>Own revenue board, company-level blockers, payroll, contracts, and operating cadence.</span></div>
|
||||
</div>
|
||||
</section>
|
||||
<section id="attendance">
|
||||
<h2>Attendance</h2>
|
||||
<ul>
|
||||
<li>Use Workplace > Attendance or the Odoo Attendance app to check in when work starts.</li>
|
||||
<li>Check out when the workday ends. Do not leave open attendance records overnight.</li>
|
||||
<li>If a check-in or check-out is missed, inform the manager on the same day with the corrected time.</li>
|
||||
<li>Attendance confirms availability; timesheets confirm what work was delivered.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="work-hours">
|
||||
<h2>Work Hours and Timesheets</h2>
|
||||
<ul>
|
||||
<li>Log every work block to the correct project and task.</li>
|
||||
<li>Use billable delivery for client work, client support for customer issues, sales support for proposals, internal product for product work, and admin only for operational work.</li>
|
||||
<li>Each entry should explain the actual output, not only the activity name.</li>
|
||||
<li>Submit timesheets daily; stale timesheets reduce dashboard accuracy.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="leave">
|
||||
<h2>Leave and Time Off</h2>
|
||||
<ul>
|
||||
<li>Request planned leave from Workplace > Leave before the leave date.</li>
|
||||
<li>Emergency leave must be reported to the manager as early as possible and entered into Odoo afterward.</li>
|
||||
<li>Managers approve or reject leave based on delivery coverage and team capacity.</li>
|
||||
<li>Do not assign urgent work to a person with approved leave unless the leave is cancelled.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="tasks">
|
||||
<h2>Task Handling Rules</h2>
|
||||
<ul>
|
||||
<li>Every task needs an assignee, deadline, project, clear title, and next action.</li>
|
||||
<li>Move blocked work to the correct status and describe the blocker, decision needed, and owner.</li>
|
||||
<li>Client-facing tasks must be linked to the right client company and workstream.</li>
|
||||
<li>Managers review overdue, blocked, stale, and no-next-action tasks every working day.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="support">
|
||||
<h2>Support Ticket Handling</h2>
|
||||
<ul>
|
||||
<li>Clients create tickets from Workplace or the public Support page.</li>
|
||||
<li>Support agents triage priority, service area, owner, and first response.</li>
|
||||
<li>All replies and attachments stay inside the ticket conversation.</li>
|
||||
<li>Close a ticket only after the customer issue is resolved or a manager approves closure.</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section id="reviews">
|
||||
<h2>Review Cadence</h2>
|
||||
<ul>
|
||||
<li>Daily: attendance, open blockers, urgent tickets, overdue receivables.</li>
|
||||
<li>Weekly: project health, delivery capacity, client risks, revenue pipeline.</li>
|
||||
<li>Monthly: payroll, payslips, collected revenue, unpaid invoices, team utilization.</li>
|
||||
<li>CEO Dashboard is the source of truth for operating review, not separate manual sheets.</li>
|
||||
</ul>
|
||||
</section>
|
||||
</article>
|
||||
</section>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="workplace_ticket_list" name="Workplace Tickets">
|
||||
<t t-call="mcs_corporate_website.workplace_layout">
|
||||
<section class="mcs-wp-page-head"><div><p>Support Tickets</p><h1>Support Tickets</h1></div><a class="mcs-wp-btn" href="/workplace/tickets/new">Create Ticket</a></section>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user