feat(fusion_clock): Publish & Notify range + portal Schedule fold-in [A6-A7]
Generalise post_week into fclk_publish_range/fclk_email_posted_range + planner Publish… panel + publish_range endpoint. Fold the /my/clock/schedule controller+template+css from fusion_planning into fusion_clock (native schedule only, role colour); inline Schedule nav across all portal pages. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
109
fusion_clock/static/src/css/portal_schedule.css
Normal file
109
fusion_clock/static/src/css/portal_schedule.css
Normal file
@@ -0,0 +1,109 @@
|
||||
/* Fusion Planning - Portal Schedule
|
||||
* Inherits Fusion Clock dark-theme tokens (--fclk-card, --fclk-green, etc.)
|
||||
*/
|
||||
|
||||
/* ---- 4-tab nav fit (keep items grouped at center, just tighter padding) ---- */
|
||||
.fclk-nav-item {
|
||||
padding: 8px 19px !important;
|
||||
}
|
||||
|
||||
/* ---- Next Shift hero card ---- */
|
||||
.fpl-next-shift {
|
||||
text-align: center;
|
||||
padding: 20px 16px;
|
||||
}
|
||||
|
||||
.fpl-next-label {
|
||||
font-size: 11px;
|
||||
color: var(--fclk-text-dim);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.fpl-next-date {
|
||||
font-size: 18px;
|
||||
color: var(--fclk-text);
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.fpl-next-time {
|
||||
font-size: 32px;
|
||||
color: var(--fclk-green);
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.02em;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.fpl-next-role {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
color: var(--fclk-text-dim);
|
||||
background: rgba(16, 185, 129, 0.08);
|
||||
border: 1px solid rgba(16, 185, 129, 0.18);
|
||||
padding: 4px 12px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
/* ---- Empty state ---- */
|
||||
.fpl-empty-card {
|
||||
text-align: center;
|
||||
padding: 28px 16px;
|
||||
}
|
||||
|
||||
.fpl-empty-icon {
|
||||
margin-bottom: 12px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.fpl-empty-title {
|
||||
font-size: 16px;
|
||||
color: var(--fclk-text);
|
||||
font-weight: 600;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.fpl-empty-sub {
|
||||
font-size: 13px;
|
||||
color: var(--fclk-text-dim);
|
||||
}
|
||||
|
||||
/* ---- Group headers ---- */
|
||||
.fpl-group {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.fpl-group-title {
|
||||
font-size: 13px;
|
||||
color: var(--fclk-text-dim);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
font-weight: 600;
|
||||
margin: 12px 16px 8px;
|
||||
}
|
||||
|
||||
.fpl-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* ---- Shift item polish ---- */
|
||||
.fpl-shift-item .fclk-recent-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.fpl-shift-note {
|
||||
font-size: 11px;
|
||||
color: var(--fclk-text-dim);
|
||||
margin-top: 2px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* ---- Bottom padding so nav doesn't cover last shift ---- */
|
||||
.fclk-container {
|
||||
padding-bottom: 80px;
|
||||
}
|
||||
@@ -49,6 +49,7 @@ export class FusionClockShiftPlanner extends Component {
|
||||
showRepeat: false,
|
||||
repeat: { interval: 1, unit: "week", type: "forever", until: "", number: 4 },
|
||||
},
|
||||
publish: { open: false, from: "", to: "", message: "" },
|
||||
});
|
||||
|
||||
onWillStart(async () => {
|
||||
@@ -327,6 +328,46 @@ export class FusionClockShiftPlanner extends Component {
|
||||
this.state.saving = false;
|
||||
}
|
||||
|
||||
togglePublishPanel() {
|
||||
this.state.publish.open = !this.state.publish.open;
|
||||
if (this.state.publish.open && !this.state.publish.from) {
|
||||
this.state.publish.from = this.state.weekStart;
|
||||
this.state.publish.to = this.state.weekEnd;
|
||||
}
|
||||
}
|
||||
|
||||
onPublishField(field, ev) {
|
||||
this.state.publish[field] = ev.target.value;
|
||||
}
|
||||
|
||||
async publishRange() {
|
||||
const publish = this.state.publish;
|
||||
this.state.saving = true;
|
||||
try {
|
||||
const result = await rpc("/fusion_clock/shift_planner/publish_range", {
|
||||
date_from: publish.from,
|
||||
date_to: publish.to,
|
||||
message: publish.message,
|
||||
week_start: this.state.weekStart,
|
||||
});
|
||||
if (result.error || result.success === false) {
|
||||
this.notification.add(result.error || result.message || "Could not publish.", {
|
||||
type: "danger",
|
||||
});
|
||||
} else {
|
||||
this._applyData(result.data);
|
||||
this.state.publish.open = false;
|
||||
this.notification.add(
|
||||
`Published ${result.posted} shift(s); notified ${result.notified} employee(s).`,
|
||||
{ type: "success" }
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.notification.add(error.message || "Could not publish.", { type: "danger" });
|
||||
}
|
||||
this.state.saving = false;
|
||||
}
|
||||
|
||||
closeCellEditor() {
|
||||
this.state.editor.open = false;
|
||||
this.activeCellAnchor = null;
|
||||
|
||||
@@ -239,6 +239,23 @@
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.fclk-planner__publish-panel {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin: 0 10px 10px;
|
||||
padding: 10px 12px;
|
||||
background: var(--fclk-planner-card, #ffffff);
|
||||
border: 1px solid var(--fclk-planner-border, #d8dadd);
|
||||
border-radius: 6px;
|
||||
|
||||
.fclk-planner__publish-msg {
|
||||
flex: 1 1 220px;
|
||||
min-width: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
.fclk-planner__repeat-panel {
|
||||
border-top: 1px solid var(--fclk-planner-border, #d8dadd);
|
||||
margin-top: 6px;
|
||||
|
||||
@@ -32,9 +32,23 @@
|
||||
<i class="fa fa-paper-plane me-1"/> Post Schedule
|
||||
<t t-if="state.draftCount">(<t t-esc="state.draftCount"/> draft)</t>
|
||||
</button>
|
||||
<button class="btn btn-outline-success" t-on-click="() => this.togglePublishPanel()" t-att-disabled="state.loading or state.saving" title="Publish a custom date range and notify employees">
|
||||
<i class="fa fa-calendar-check-o me-1"/> Publish…
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div t-if="state.publish.open" class="fclk-planner__publish-panel">
|
||||
<label>From <input type="date" t-att-value="state.publish.from" t-on-change="(ev) => this.onPublishField('from', ev)"/></label>
|
||||
<label>To <input type="date" t-att-value="state.publish.to" t-on-change="(ev) => this.onPublishField('to', ev)"/></label>
|
||||
<input type="text" class="fclk-planner__publish-msg" placeholder="Optional message to employees…"
|
||||
t-att-value="state.publish.message" t-on-change="(ev) => this.onPublishField('message', ev)"/>
|
||||
<button class="btn btn-success btn-sm" t-on-click="() => this.publishRange()" t-att-disabled="state.saving">
|
||||
<i class="fa fa-paper-plane me-1"/> Publish & Notify
|
||||
</button>
|
||||
<button class="btn btn-light btn-sm" t-on-click="() => this.togglePublishPanel()">Cancel</button>
|
||||
</div>
|
||||
|
||||
<t t-if="state.error">
|
||||
<div class="alert alert-danger mx-3 mt-3"><t t-esc="state.error"/></div>
|
||||
</t>
|
||||
|
||||
Reference in New Issue
Block a user