odoo18/addons/web/static/src/views/calendar/calendar_controller.js

405 lines
13 KiB
JavaScript

import {
deleteConfirmationMessage,
ConfirmationDialog,
} from "@web/core/confirmation_dialog/confirmation_dialog";
import { _t } from "@web/core/l10n/translation";
import { useOwnedDialogs, useService } from "@web/core/utils/hooks";
import { Layout } from "@web/search/layout";
import { useModelWithSampleData } from "@web/model/model";
import { FormViewDialog } from "@web/views/view_dialogs/form_view_dialog";
import { useSetupAction } from "@web/search/action_hook";
import { DateTimePicker } from "@web/core/datetime/datetime_picker";
import { CalendarFilterPanel } from "./filter_panel/calendar_filter_panel";
import { CalendarMobileFilterPanel } from "./mobile_filter_panel/calendar_mobile_filter_panel";
import { CalendarQuickCreate } from "./quick_create/calendar_quick_create";
import { SearchBar } from "@web/search/search_bar/search_bar";
import { useSearchBarToggler } from "@web/search/search_bar/search_bar_toggler";
import { ViewScaleSelector } from "@web/views/view_components/view_scale_selector";
import { CogMenu } from "@web/search/cog_menu/cog_menu";
import { browser } from "@web/core/browser/browser";
import { standardViewProps } from "@web/views/standard_view_props";
import { getLocalWeekNumber } from "@web/core/l10n/dates";
import { Component, useState } from "@odoo/owl";
const { DateTime } = luxon;
export const SCALE_LABELS = {
day: _t("Day"),
week: _t("Week"),
month: _t("Month"),
year: _t("Year"),
};
function useUniqueDialog() {
const displayDialog = useOwnedDialogs();
let close = null;
return (...args) => {
if (close) {
close();
}
close = displayDialog(...args);
};
}
export class CalendarController extends Component {
static components = {
DatePicker: DateTimePicker,
FilterPanel: CalendarFilterPanel,
MobileFilterPanel: CalendarMobileFilterPanel,
QuickCreate: CalendarQuickCreate,
QuickCreateFormView: FormViewDialog,
Layout,
SearchBar,
ViewScaleSelector,
CogMenu,
};
static template = "web.CalendarController";
static props = {
...standardViewProps,
Model: Function,
Renderer: Function,
archInfo: Object,
buttonTemplate: String,
session: { type: Object, optional: true },
itemCalendarProps: { type: Object, optional: true },
};
setup() {
this.action = useService("action");
this.orm = useService("orm");
this.displayDialog = useUniqueDialog();
this.model = useModelWithSampleData(
this.props.Model,
{
...this.props.archInfo,
resModel: this.props.resModel,
domain: this.props.domain,
fields: this.props.fields,
allFilter: this.props.state?.allFilter ?? {},
date: this.props.state?.date,
},
{
onWillStart: this.onWillStartModel.bind(this),
}
);
useSetupAction({
getLocalState: () => this.model.exportedState,
});
const sessionShowSidebar = browser.sessionStorage.getItem("calendar.showSideBar");
this.state = useState({
isWeekendVisible:
browser.localStorage.getItem("calendar.isWeekendVisible") != null
? JSON.parse(browser.localStorage.getItem("calendar.isWeekendVisible"))
: true,
showSideBar:
!this.env.isSmall &&
Boolean(sessionShowSidebar != null ? JSON.parse(sessionShowSidebar) : true),
});
this.searchBarToggler = useSearchBarToggler();
}
get currentDate() {
const meta = this.model.meta;
const scale = meta.scale;
if (this.env.isSmall && ["week", "month"].includes(scale)) {
const date = meta.date || DateTime.now();
let text = "";
if (scale === "week") {
const startMonth = date.startOf("week");
const endMonth = date.endOf("week");
if (startMonth.toFormat("LLL") !== endMonth.toFormat("LLL")) {
text = `${startMonth.toFormat("LLL")}-${endMonth.toFormat("LLL")}`;
} else {
text = startMonth.toFormat("LLLL");
}
} else if (scale === "month") {
text = date.toFormat("LLLL");
}
return ` - ${text} ${date.year}`;
} else {
return "";
}
}
get date() {
return this.model.meta.date || DateTime.now();
}
get today() {
return DateTime.now().toFormat("d");
}
get currentYear() {
return this.date.toFormat("y");
}
get dayHeader() {
return `${this.date.toFormat("d")} ${this.date.toFormat("MMMM")} ${this.date.year}`;
}
get weekHeader() {
const { rangeStart, rangeEnd } = this.model;
if (rangeStart.year != rangeEnd.year) {
return `${rangeStart.toFormat("MMMM")} ${rangeStart.year} - ${rangeEnd.toFormat(
"MMMM"
)} ${rangeEnd.year}`;
} else if (rangeStart.month != rangeEnd.month) {
return `${rangeStart.toFormat("MMMM")} - ${rangeEnd.toFormat("MMMM")} ${
rangeStart.year
}`;
}
return `${rangeStart.toFormat("MMMM")} ${rangeStart.year}`;
}
get currentMonth() {
return `${this.date.toFormat("MMMM")} ${this.date.year}`;
}
get currentWeek() {
return getLocalWeekNumber(this.model.rangeStart);
}
get rendererProps() {
return {
model: this.model,
isWeekendVisible: this.model.scale === "day" || this.state.isWeekendVisible,
createRecord: this.createRecord.bind(this),
deleteRecord: this.deleteRecord.bind(this),
editRecord: this.editRecord.bind(this),
setDate: this.setDate.bind(this),
};
}
get containerProps() {
return {
model: this.model,
};
}
get datePickerProps() {
return {
type: "date",
showWeekNumbers: false,
maxPrecision: "days",
daysOfWeekFormat: "narrow",
onSelect: (date) => {
let scale = "week";
if (this.model.date.hasSame(date, "day")) {
const scales = ["month", "week", "day"];
scale = scales[(scales.indexOf(this.model.scale) + 1) % scales.length];
} else {
// Check if dates are on the same week
// As a.hasSame(b, "week") does not depend on locale and week always starts on Monday,
// we are comparing derivated dates instead to take this into account.
const currentDate =
this.model.date.weekday === 7
? this.model.date.plus({ day: 1 })
: this.model.date;
const pickedDate = date.weekday === 7 ? date.plus({ day: 1 }) : date;
// a.hasSame(b, "week") does not depend on locale and week alway starts on Monday
if (currentDate.hasSame(pickedDate, "week")) {
scale = "day";
}
}
this.model.load({ scale, date });
},
value: this.model.date,
};
}
get filterPanelProps() {
return {
model: this.model,
};
}
get mobileFilterPanelProps() {
return {
model: this.model,
sideBarShown: this.state.showSideBar,
toggleSideBar: () => {
this.state.showSideBar = !this.state.showSideBar;
},
};
}
toggleSideBar() {
this.state.showSideBar = !this.state.showSideBar;
browser.sessionStorage.setItem("calendar.showSideBar", this.state.showSideBar);
}
get showCalendar() {
return !this.env.isSmall || !this.state.showSideBar;
}
get showSideBar() {
return this.state.showSideBar;
}
get className() {
return this.props.className;
}
get editRecordDefaultDisplayText() {
return _t("New Event");
}
getQuickCreateProps(record) {
return {
record,
model: this.model,
editRecord: this.editRecordInCreation.bind(this),
title: this.props.context.default_name,
};
}
getQuickCreateFormViewProps(record) {
const rawRecord = this.model.buildRawRecord(record);
const context = this.model.makeContextDefaults(rawRecord);
return {
resModel: this.model.resModel,
viewId: this.model.quickCreateFormViewId,
title: _t("New Event"),
context,
};
}
createRecord(record) {
if (!this.model.canCreate) {
return;
}
if (this.model.hasQuickCreate) {
if (this.model.quickCreateFormViewId) {
return new Promise((resolve) => {
this.displayDialog(
this.constructor.components.QuickCreateFormView,
this.getQuickCreateFormViewProps(record),
{
onClose: () => resolve(),
}
);
});
}
return new Promise((resolve) => {
this.displayDialog(
this.constructor.components.QuickCreate,
this.getQuickCreateProps(record),
{
onClose: () => resolve(),
}
);
});
} else {
return this.editRecordInCreation(record);
}
}
async editRecord(record, context = {}, shouldFetchFormViewId = true) {
if (this.model.hasEditDialog) {
return new Promise((resolve) => {
this.displayDialog(
FormViewDialog,
{
resModel: this.model.resModel,
resId: record.id || false,
context,
title: record.id
? _t("Open: %s", record.title)
: this.editRecordDefaultDisplayText,
viewId: this.model.formViewId,
onRecordSaved: () => this.model.load(),
},
{ onClose: () => resolve() }
);
});
} else {
let formViewId = this.model.formViewId;
if (shouldFetchFormViewId) {
formViewId = await this.orm.call(
this.model.resModel,
"get_formview_id",
[[record.id]],
context
);
}
const action = {
type: "ir.actions.act_window",
res_model: this.model.resModel,
views: [[formViewId || false, "form"]],
target: "current",
context,
};
if (record.id) {
action.res_id = record.id;
}
this.action.doAction(action);
}
}
editRecordInCreation(record) {
const rawRecord = this.model.buildRawRecord(record);
const context = this.model.makeContextDefaults(rawRecord);
return this.editRecord(record, context, false);
}
deleteConfirmationDialogProps(record) {
return {
title: _t("Bye-bye, record!"),
body: deleteConfirmationMessage,
confirm: () => {
this.model.unlinkRecord(record.id);
},
confirmLabel: _t("Delete"),
cancel: () => {
// `ConfirmationDialog` needs this prop to display the cancel
// button but we do nothing on cancel.
},
cancelLabel: _t("No, keep it"),
};
}
deleteRecord(record) {
this.displayDialog(ConfirmationDialog, this.deleteConfirmationDialogProps(record));
}
onWillStartModel() {}
async setDate(move) {
let date = null;
switch (move) {
case "next":
date = this.model.date.plus({ [`${this.model.scale}s`]: 1 });
break;
case "previous":
date = this.model.date.minus({ [`${this.model.scale}s`]: 1 });
break;
case "today":
date = luxon.DateTime.local().startOf("day");
if (date.ts === this.date.startOf("day").ts) {
this.model.bus.trigger("SCROLL_TO_CURRENT_HOUR", false);
}
break;
}
await this.model.load({ date });
}
get scales() {
return Object.fromEntries(
this.model.scales.map((s) => [s, { description: SCALE_LABELS[s] }])
);
}
async setScale(scale) {
await this.model.load({ scale });
browser.sessionStorage.setItem("calendar-scale", this.model.scale);
}
toggleWeekendVisibility() {
this.state.isWeekendVisible = !this.state.isWeekendVisible;
browser.localStorage.setItem("calendar.isWeekendVisible", this.state.isWeekendVisible);
}
}