405 lines
13 KiB
JavaScript
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);
|
|
}
|
|
}
|