diff --git a/addons/calendar/__manifest__.py b/addons/calendar/__manifest__.py index 071e71a9f31..b4919c79173 100644 --- a/addons/calendar/__manifest__.py +++ b/addons/calendar/__manifest__.py @@ -5,7 +5,7 @@ 'name': 'Calendar', 'version': '1.1', 'sequence': 165, - 'depends': ['base', 'mail'], + 'depends': ['base', 'mail', 'onboarding'], 'summary': "Schedule employees' meetings", 'description': """ This is a full-featured calendar system. @@ -28,6 +28,7 @@ If you need to manage your meetings, you should install the CRM module. 'data/calendar_cron.xml', 'data/mail_template_data.xml', 'data/calendar_data.xml', + 'data/calendar_onboarding_data.xml', 'data/mail_activity_type_data.xml', 'data/mail_message_subtype_data.xml', 'views/mail_activity_views.xml', diff --git a/addons/calendar/controllers/main.py b/addons/calendar/controllers/main.py index 0b87bf02bf1..4b8e4571cd8 100644 --- a/addons/calendar/controllers/main.py +++ b/addons/calendar/controllers/main.py @@ -113,3 +113,8 @@ class CalendarController(http.Controller): event._create_videocall_channel() return request.redirect(event.videocall_channel_id.invitation_url) + + @http.route('/calendar/check_credentials', type='json', auth='user') + def check_calendar_credentials(self): + # method should be overwritten by sync providers + return {} diff --git a/addons/calendar/data/calendar_onboarding_data.xml b/addons/calendar/data/calendar_onboarding_data.xml new file mode 100644 index 00000000000..1e0deed3524 --- /dev/null +++ b/addons/calendar/data/calendar_onboarding_data.xml @@ -0,0 +1,24 @@ + + + + + + Connect your calendar + With Outlook or Google + Add + Done! + action_view_start_calendar_sync + + onboarding_calendar.png + Onboarding Calendar Synchronization + 1 + + + + Calendar Onboarding + + action_close_calendar_onboarding + calendar + + + diff --git a/addons/calendar/models/__init__.py b/addons/calendar/models/__init__.py index 7f7cb86aad2..7f1847fe1ef 100644 --- a/addons/calendar/models/__init__.py +++ b/addons/calendar/models/__init__.py @@ -14,3 +14,5 @@ from . import mail_activity from . import mail_activity_mixin from . import mail_activity_type from . import res_users +from . import onboarding_onboarding +from . import onboarding_onboarding_step diff --git a/addons/calendar/models/calendar_attendee.py b/addons/calendar/models/calendar_attendee.py index 986e8c01ce4..fb93a39aa49 100644 --- a/addons/calendar/models/calendar_attendee.py +++ b/addons/calendar/models/calendar_attendee.py @@ -24,9 +24,9 @@ class Attendee(models.Model): STATE_SELECTION = [ ('needsAction', 'Needs Action'), - ('tentative', 'Uncertain'), - ('declined', 'Declined'), - ('accepted', 'Accepted'), + ('tentative', 'Maybe'), + ('declined', 'No'), + ('accepted', 'Yes'), ] # event @@ -40,7 +40,7 @@ class Attendee(models.Model): access_token = fields.Char('Invitation Token', default=_default_access_token) mail_tz = fields.Selection(_tz_get, compute='_compute_mail_tz', help='Timezone used for displaying time in the mail template') # state - state = fields.Selection(STATE_SELECTION, string='Status', readonly=True, default='needsAction') + state = fields.Selection(STATE_SELECTION, string='Status', default='needsAction') availability = fields.Selection( [('free', 'Available'), ('busy', 'Busy')], 'Available/Busy', readonly=True) diff --git a/addons/calendar/models/calendar_event.py b/addons/calendar/models/calendar_event.py index c753ef2e8d7..6e46121c572 100644 --- a/addons/calendar/models/calendar_event.py +++ b/addons/calendar/models/calendar_event.py @@ -183,6 +183,9 @@ class Meeting(models.Model): # attendees attendee_ids = fields.One2many( 'calendar.attendee', 'event_id', 'Participant') + current_attendee = fields.Many2one("calendar.attendee", compute="_compute_current_attendee", search="_search_current_attendee") + current_status = fields.Selection(string="Attending?", related="current_attendee.state", readonly=False) + should_show_status = fields.Boolean(compute="_compute_should_show_status") partner_ids = fields.Many2many( 'res.partner', 'calendar_event_res_partner_rel', string='Attendees', default=_default_partners) @@ -248,6 +251,20 @@ class Meeting(models.Model): awaiting_count = fields.Integer(compute="_compute_attendees_count") user_can_edit = fields.Boolean(compute='_compute_user_can_edit') + @api.depends("attendee_ids") + def _compute_should_show_status(self): + for event in self: + event.should_show_status = event.current_attendee and any(attendee.partner_id != self.env.user.partner_id for attendee in event.attendee_ids) + + @api.depends('attendee_ids', 'attendee_ids.state') + def _compute_current_attendee(self): + for event in self: + current_attendee = event.attendee_ids.filtered(lambda attendee: attendee.partner_id == self.env.user.partner_id) + event.current_attendee = current_attendee and current_attendee[0] + + def _search_current_attendee(self, operator, value): + return [("id", operator, value)] + @api.depends('attendee_ids', 'attendee_ids.state', 'partner_ids') def _compute_attendees_count(self): for event in self: diff --git a/addons/calendar/models/onboarding_onboarding.py b/addons/calendar/models/onboarding_onboarding.py new file mode 100644 index 00000000000..d637c1037bb --- /dev/null +++ b/addons/calendar/models/onboarding_onboarding.py @@ -0,0 +1,11 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, models + + +class Onboarding(models.Model): + _inherit = 'onboarding.onboarding' + + @api.model + def action_close_calendar_onboarding(self): + self.action_close_panel('calendar.onboarding_onboarding_calendar') diff --git a/addons/calendar/models/onboarding_onboarding_step.py b/addons/calendar/models/onboarding_onboarding_step.py new file mode 100644 index 00000000000..00fc1ffbfe2 --- /dev/null +++ b/addons/calendar/models/onboarding_onboarding_step.py @@ -0,0 +1,13 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, models + + +class OnboardingStep(models.Model): + _inherit = 'onboarding.onboarding.step' + + @api.model + def action_view_start_calendar_sync(self): + self.env['onboarding.onboarding.step'].action_validate_step('calendar.onboarding_onboarding_step_setup_calendar_integration') + action = self.env["ir.actions.actions"]._for_xml_id("calendar.action_view_start_calendar_sync") + return action diff --git a/addons/calendar/static/src/components/calendar_sync_cog_menu/calendar_sync_cog_menu.js b/addons/calendar/static/src/components/calendar_sync_cog_menu/calendar_sync_cog_menu.js new file mode 100644 index 00000000000..fc7b45ea215 --- /dev/null +++ b/addons/calendar/static/src/components/calendar_sync_cog_menu/calendar_sync_cog_menu.js @@ -0,0 +1,44 @@ +/** @odoo-module */ + +import { _t } from "@web/core/l10n/translation"; +import { registry } from "@web/core/registry"; +import { Component } from "@odoo/owl"; +import { useService } from "@web/core/utils/hooks"; +import { DropdownItem } from "@web/core/dropdown/dropdown_item"; + +const cogMenuRegistry = registry.category("cogMenu"); + +export class CalendarSyncCogMenu extends Component { + static props = {}; + static template = "calendar.CalendarSyncCogMenu"; + static components = { DropdownItem }; + + setup() { + this.actionService = useService("action"); + } + + startCalendarSync() { + this.actionService.doAction({ + name: _t('Connect your Calendar'), + type: 'ir.actions.act_window', + res_model: 'calendar.provider.config', + views: [[false, "form"]], + view_mode: "form", + target: 'new', + context: { + 'dialog_size': 'medium', + } + }); + } +}; + +cogMenuRegistry.add( + 'calendar-sync-cog-menu', + { + Component: CalendarSyncCogMenu, + groupNumber: 10, + isDisplayed: ({ config }) => { + return config.viewSubType === "attendee_calendar"; + }, + }, +); diff --git a/addons/calendar/static/src/components/calendar_sync_cog_menu/calendar_sync_cog_menu.xml b/addons/calendar/static/src/components/calendar_sync_cog_menu/calendar_sync_cog_menu.xml new file mode 100644 index 00000000000..93781d2d613 --- /dev/null +++ b/addons/calendar/static/src/components/calendar_sync_cog_menu/calendar_sync_cog_menu.xml @@ -0,0 +1,8 @@ + + + + + Add integrations + + + diff --git a/addons/calendar/static/src/scss/calendar.scss b/addons/calendar/static/src/scss/calendar.scss index 9b495b78c15..9599e31aee3 100644 --- a/addons/calendar/static/src/scss/calendar.scss +++ b/addons/calendar/static/src/scss/calendar.scss @@ -1,13 +1,11 @@ .o_cw_popover .o_cw_popover_fields_secondary { max-height: 75vh; + & > li > div { min-width: 0; } } -.o_cw_body { - max-width: 328px; -} .o_cw_body .o_field_copy { max-width: calc(100% - 6rem); @@ -18,110 +16,94 @@ padding-top: 0px !important; } -.o_field_many2manyattendee .o_field_tags { +.o_field_many2manyattendeeexpandable .o_field_tags { flex-direction: column; + span.badge { width: fit-content; } - .o_field_many2many_selection { - flex: 1 0 auto; - } } -.o_attendee_border { - border-width: 2px; - border-style: solid; -} +@for $-i from 1 through length($o-colors-complete) { + $-color: nth($o-colors-complete, $-i); + $-color-subtle: mix($o-white, $-color, 85%); -.o_attendee_border_accepted { - border-color: map-get($theme-colors, 'success'); -} -.o_attendee_border_declined { - border-color: map-get($theme-colors, 'danger'); -} -.o_attendee_border_tentative { - border-color: map-get($theme-colors, 'light'); -} + .o_calendar_renderer .fc-event.o_calendar_color_#{$-i - 1} { + &:not(.o_event_dot) { + --o-overlay-opacity: .5; -.o_cw_popover_link { - &.o_attendee_status_needsAction { - background-color: rgba($o-view-background-color, 0.9) !important; - } - - &.o_attendee_status_declined { - text-decoration: line-through; - background-color: rgba($o-view-background-color, 0.9) !important; - } -} - -.o_calendar_renderer { - .fc-event { - &.o_attendee_status_needsAction, - &.o_attendee_status_tentative, - &.o_attendee_status_declined, - &.o_attendee_status_alone { - border-width: 2px !important; - } - - &.o_attendee_status_alone, - &.o_attendee_status_needsAction { - background-color: rgba($o-view-background-color, 0.9) !important; - color: $o-black !important; - .fc-bg { - background-color: rgba($o-view-background-color, 0.9) !important; - } - } - - &.o_attendee_status_declined { - text-decoration: line-through; - background-color: rgba($o-view-background-color, 0.9) !important; - color: $o-black !important; - .fc-bg { - background-color: rgba($o-view-background-color, 0.9) !important; - } - } - } -} - -@for $i from 1 through length($o-colors-complete) { - $color: nth($o-colors-complete, $i); - - .o_cw_popover_link.o_calendar_color_#{$i - 1} { - &.o_attendee_status_tentative { - color: color-contrast($color); - background: repeating-linear-gradient( - 45deg, - $color, - $color 10px, - rgba($color, 0.7) 10px, - rgba($color, 0.7) 20px - ) !important; - } - } - - .o_calendar_renderer { - .fc-event.o_calendar_color_#{$i - 1} { - &.o_attendee_status_needsAction, - &.o_attendee_status_tentative, - &.o_attendee_status_declined, - &.o_attendee_status_alone { - &.o_cw_custom_highlight { - background-color: $color; - } + &.o_attendee_status_active { + --o-bg-opacity: 1; } - &.o_attendee_status_tentative { - color: color-contrast($color) !important; + &.o_attendee_status_tentative, &.o_event_hatched { + --o-bg-opacity: .5; + .fc-bg { background: repeating-linear-gradient( 45deg, - $color, - $color 10px, - rgba(white, 0.4) 10px, - rgba(white, 0.4) 20px + rgba($-color, .3), + rgba($-color, .3) 10px, + rgba($-color, .6) 10px, + rgba($-color, .6) 20px ) !important; } } + + &.o_attendee_status_alone, + &.o_attendee_status_needsAction { + --o-bg-opacity: .5; + } + + &.o_attendee_status_declined { + --o-bg-opacity: 0; + } + } + + &.o_event_dot { + --o-overlay-opacity: .25; + + display: flex; + align-items: center; + gap: map-get($spacers, 1 ); + border: none; + background-color: rgba($-color-subtle, var(--o-bg-opacity)); + + &:before { + content:'\f111'; // fa-circle + display: inline-block; + z-index: 2; + font-family: Fontawesome; + color: $-color; + } + + &.o_attendee_status_needsAction, &.o_event_hatched { + &:before { + content: "\f1db"; // fa-circle-thin + } + } + + &.o_attendee_status_tentative:before { + content: "\f059"; // fa-question-circle + } + + &.o_attendee_status_declined:before { + content: "\f05e"; // fa-ban + } + + &.o_attendee_status_alone { + content: "\f06a"; // fa-exclamation-circle + } + + &:hover { + --o-bg-opacity: 1; + } + } + + &.o_attendee_status_declined, &.o_event_striked { + .fc-content { + text-decoration: line-through; + } } } } diff --git a/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_controller.xml b/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_controller.xml index ee965f55822..9b01d73a84a 100644 --- a/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_controller.xml +++ b/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_controller.xml @@ -1,25 +1,6 @@ - -
-
- Synchronize with: -
-
-
- -
-
- -
-
-
-
diff --git a/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_model.js b/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_model.js index 2558118e2e3..7bc2d42b9fe 100644 --- a/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_model.js +++ b/addons/calendar/static/src/views/attendee_calendar/attendee_calendar_model.js @@ -4,11 +4,16 @@ import { _t } from "@web/core/l10n/translation"; import { CalendarModel } from "@web/views/calendar/calendar_model"; import { askRecurrenceUpdatePolicy } from "@calendar/views/ask_recurrence_update_policy_hook"; import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; +import { onWillStart } from "@odoo/owl"; export class AttendeeCalendarModel extends CalendarModel { - setup(params, { dialog }) { + setup(params, { dialog, rpc }) { super.setup(...arguments); this.dialog = dialog; + this.rpc = rpc; + onWillStart(async () => { + this.credentialStatus = await this.rpc("/calendar/check_credentials"); + }); } get attendees() { diff --git a/addons/calendar/static/src/views/attendee_calendar/common/attendee_calendar_common_popover.js b/addons/calendar/static/src/views/attendee_calendar/common/attendee_calendar_common_popover.js index 04857c04169..daa5cad80d0 100644 --- a/addons/calendar/static/src/views/attendee_calendar/common/attendee_calendar_common_popover.js +++ b/addons/calendar/static/src/views/attendee_calendar/common/attendee_calendar_common_popover.js @@ -29,7 +29,7 @@ export class AttendeeCalendarCommonPopover extends CalendarCommonPopover { accepted: "text-success", declined: "text-danger", tentative: "text-muted", - needsAction: "text-dark", + needsAction: "false", }; this.statusInfo = {}; for (const selection of stateSelections) { diff --git a/addons/calendar/static/src/views/attendee_calendar/common/attendee_calendar_common_popover.xml b/addons/calendar/static/src/views/attendee_calendar/common/attendee_calendar_common_popover.xml index f069e13da56..7fb126b216a 100644 --- a/addons/calendar/static/src/views/attendee_calendar/common/attendee_calendar_common_popover.xml +++ b/addons/calendar/static/src/views/attendee_calendar/common/attendee_calendar_common_popover.xml @@ -24,22 +24,19 @@ Delete -
- - - - - - Accept - - - Decline - - - Uncertain - - -
+ +
+ + + +
+
diff --git a/addons/calendar/static/src/views/attendee_calendar/common/attendee_calendar_common_renderer.xml b/addons/calendar/static/src/views/attendee_calendar/common/attendee_calendar_common_renderer.xml index efc01d141c4..5ed90026bb3 100644 --- a/addons/calendar/static/src/views/attendee_calendar/common/attendee_calendar_common_renderer.xml +++ b/addons/calendar/static/src/views/attendee_calendar/common/attendee_calendar_common_renderer.xml @@ -1,18 +1,21 @@ -
-
- - - - -
- +
+ +
+ + + + +
- +
+ + , + - , -
- +
diff --git a/addons/calendar/static/src/views/calendar_form/calendar_quick_create.xml b/addons/calendar/static/src/views/calendar_form/calendar_quick_create.xml index 1eb0d79bc35..2b35bcb968e 100644 --- a/addons/calendar/static/src/views/calendar_form/calendar_quick_create.xml +++ b/addons/calendar/static/src/views/calendar_form/calendar_quick_create.xml @@ -2,7 +2,7 @@ - + diff --git a/addons/calendar/static/src/views/fields/attendee_tags_list.js b/addons/calendar/static/src/views/fields/attendee_tags_list.js new file mode 100644 index 00000000000..dc9ed75383c --- /dev/null +++ b/addons/calendar/static/src/views/fields/attendee_tags_list.js @@ -0,0 +1,7 @@ +/** @odoo-module **/ + +import { TagsList } from "@web/core/tags_list/tags_list"; + +export class AttendeeTagsList extends TagsList {} + +AttendeeTagsList.template = "calendar.AttendeeTagsList"; diff --git a/addons/calendar/static/src/views/fields/attendee_tags_list.scss b/addons/calendar/static/src/views/fields/attendee_tags_list.scss new file mode 100644 index 00000000000..9f6fcb06f35 --- /dev/null +++ b/addons/calendar/static/src/views/fields/attendee_tags_list.scss @@ -0,0 +1,24 @@ +.o_attendee_tags_list { + --Avatar-size: 24px; + + position: relative; + + .attendee_tag_status { + @include o-position-absolute($bottom: -0.375em, $right: -0.375em); + aspect-ratio: 1; + border: 1px $o-view-background-color solid; + } + + .o_attendee_status_accepted { + background-color: tint-color($success, 80%); + color: $success; + } + .o_attendee_status_declined { + background-color: tint-color($danger, 80%); + color: $danger; + } + .o_attendee_status_tentative { + background-color: $gray-200; + color: $dark; + } +} diff --git a/addons/calendar/static/src/views/fields/attendee_tags_list.xml b/addons/calendar/static/src/views/fields/attendee_tags_list.xml new file mode 100644 index 00000000000..0e7f48dbe57 --- /dev/null +++ b/addons/calendar/static/src/views/fields/attendee_tags_list.xml @@ -0,0 +1,18 @@ + + + + + + + + +
+ +
+
+ + + diff --git a/addons/calendar/static/src/views/fields/many2many_attendee.js b/addons/calendar/static/src/views/fields/many2many_attendee.js index 095d9410577..62cd034c6c2 100644 --- a/addons/calendar/static/src/views/fields/many2many_attendee.js +++ b/addons/calendar/static/src/views/fields/many2many_attendee.js @@ -6,7 +6,13 @@ import { many2ManyTagsAvatarField, } from "@web/views/fields/many2many_tags_avatar/many2many_tags_avatar_field"; import { useSpecialData } from "@web/views/fields/relational_utils"; +import { AttendeeTagsList } from "@calendar/views/fields/attendee_tags_list"; +const ICON_BY_STATUS = { + accepted: "fa-check", + declined: "fa-times", + tentative: "fa-question", +}; export class Many2ManyAttendee extends Many2ManyTagsAvatarField { setup() { super.setup(); @@ -28,7 +34,8 @@ export class Many2ManyAttendee extends Many2ManyTagsAvatarField { const tags = super.tags.map((tag) => { const partner = partnerIds.find((partner) => tag.resId === partner.id); if (partner) { - tag.imageClass = `o_attendee_border o_attendee_border_${partner.status}`; + tag.status = partner.status; + tag.statusIcon = ICON_BY_STATUS[partner.status]; } return tag; }); @@ -46,6 +53,12 @@ export class Many2ManyAttendee extends Many2ManyTagsAvatarField { } } +Many2ManyAttendee.template = "calendar.Many2ManyAttendee"; +Many2ManyAttendee.components = { + ...Many2ManyAttendee.components, + TagsList: AttendeeTagsList, +}; + export const many2ManyAttendee = { ...many2ManyTagsAvatarField, component: Many2ManyAttendee, diff --git a/addons/calendar/static/src/views/fields/many2many_attendee.xml b/addons/calendar/static/src/views/fields/many2many_attendee.xml new file mode 100644 index 00000000000..9ce4cea2a07 --- /dev/null +++ b/addons/calendar/static/src/views/fields/many2many_attendee.xml @@ -0,0 +1,11 @@ + + + + + + + + props.placeholder + + + diff --git a/addons/calendar/static/src/views/widgets/calendar_week_days.xml b/addons/calendar/static/src/views/widgets/calendar_week_days.xml index 0f1788337da..c2dc1ab9348 100644 --- a/addons/calendar/static/src/views/widgets/calendar_week_days.xml +++ b/addons/calendar/static/src/views/widgets/calendar_week_days.xml @@ -1,9 +1,9 @@ -
+
-
+
diff --git a/addons/calendar/static/tests/attendee_calendar_views_tests.js b/addons/calendar/static/tests/attendee_calendar_views_tests.js index 3c3420c0fbb..b8be68008b1 100644 --- a/addons/calendar/static/tests/attendee_calendar_views_tests.js +++ b/addons/calendar/static/tests/attendee_calendar_views_tests.js @@ -1,15 +1,8 @@ /** @odoo-module **/ -import { - getFixture, - patchDate, - patchWithCleanup, -} from "@web/../tests/helpers/utils"; +import { getFixture, patchDate, patchWithCleanup } from "@web/../tests/helpers/utils"; -import { - changeScale, - clickEvent, -} from "@web/../tests/views/calendar/helpers"; +import { changeScale, clickEvent } from "@web/../tests/views/calendar/helpers"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import { browser } from "@web/core/browser/browser"; import { registry } from "@web/core/registry"; @@ -27,7 +20,7 @@ QUnit.module("CalendarView", ({ beforeEach }) => { patchWithCleanup(browser, { setTimeout: (fn) => fn(), - clearTimeout: () => { }, + clearTimeout: () => {}, }); target = getFixture(); @@ -93,7 +86,7 @@ QUnit.module("CalendarView", ({ beforeEach }) => { }, is_hatched: { string: "Hatched", type: "boolean" }, is_striked: { string: "Striked", type: "boolean" }, - res_model_name: { string: "Linked Model Name", type: "char" } + res_model_name: { string: "Linked Model Name", type: "char" }, }, records: [ { @@ -124,7 +117,7 @@ QUnit.module("CalendarView", ({ beforeEach }) => { methods: { check_access_rights() { return Promise.resolve(true); - } + }, }, }, user: { @@ -171,8 +164,8 @@ QUnit.module("CalendarView", ({ beforeEach }) => { { id: 2, user_id: uid, partner_id: 2, partner_checked: true }, { id: 3, user_id: 4, partner_id: 3, partner_checked: true }, ], - } - } + }, + }, }; }); @@ -188,8 +181,7 @@ QUnit.module("CalendarView", ({ beforeEach }) => { stop: "2016-12-11 10:00:00", allday: false, partner_ids: [1, 2, 3], - res_model_name: 'Time Off', - + res_model_name: "Time Off", }); await makeView({ @@ -222,6 +214,8 @@ QUnit.module("CalendarView", ({ beforeEach }) => { return Promise.resolve(true); } else if (route === "/web/dataset/call_kw/res.partner/get_attendee_detail") { return Promise.resolve([]); + } else if (route === "/calendar/check_credentials") { + return Promise.resolve({}); } }, }); @@ -230,13 +224,9 @@ QUnit.module("CalendarView", ({ beforeEach }) => { ".o_calendar_renderer .fc-view-container", "should instance of fullcalendar" ); - await changeScale(target, 'week'); + await changeScale(target, "week"); await clickEvent(target, 3); - assert.containsOnce( - target, - ".fa-link", - "A link icon should be present" - ); + assert.containsOnce(target, ".fa-link", "A link icon should be present"); assert.strictEqual(target.querySelector("li a[href='#']").textContent, "Time Off"); }); }); diff --git a/addons/calendar/static/tests/calendar_tests.js b/addons/calendar/static/tests/calendar_tests.js index 8398e2a5ffb..98258ab181f 100644 --- a/addons/calendar/static/tests/calendar_tests.js +++ b/addons/calendar/static/tests/calendar_tests.js @@ -102,8 +102,8 @@ QUnit.module( "the tag should be correctly named" ); assert.hasClass( - badges[0].querySelector('img'), - "o_attendee_border_accepted", + badges[0].querySelector(".attendee_tag_status"), + "o_attendee_status_accepted", "Jesus should attend the meeting" ); assert.strictEqual( @@ -112,8 +112,8 @@ QUnit.module( "the tag should be correctly named" ); assert.hasClass( - badges[1].querySelector('img'), - "o_attendee_border_tentative", + badges[1].querySelector(".attendee_tag_status"), + "o_attendee_status_tentative", "Mohamet should still confirm his attendance to the meeting" ); assert.containsOnce(badges[0], "img", "should have img tag"); diff --git a/addons/calendar/static/tests/tours/calendar_tour.js b/addons/calendar/static/tests/tours/calendar_tour.js index b524cdc9b2f..7b23d6c12ac 100644 --- a/addons/calendar/static/tests/tours/calendar_tour.js +++ b/addons/calendar/static/tests/tours/calendar_tour.js @@ -1,156 +1,161 @@ /** @odoo-module **/ - import { registry } from "@web/core/registry"; - import { stepUtils } from "@web_tour/tour_service/tour_utils"; +import { registry } from "@web/core/registry"; +import { stepUtils } from "@web_tour/tour_service/tour_utils"; - const todayDate = function() { - let now = new Date(); - let year = now.getFullYear(); - let month = String(now.getMonth() + 1).padStart(2, '0'); - let day = String(now.getDate()).padStart(2, '0'); +const todayDate = function () { + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, "0"); + const day = String(now.getDate()).padStart(2, "0"); - return `${month}/${day}/${year} 10:00:00`; - }; + return `${month}/${day}/${year} 10:00:00`; +}; - registry.category("web_tour.tours").add('calendar_appointments_hour_tour', { - url: '/web', - test: true, - steps: () => [stepUtils.showAppsMenuItem(), +registry.category("web_tour.tours").add("calendar_appointments_hour_tour", { + url: "/web", + test: true, + steps: () => [ + stepUtils.showAppsMenuItem(), { trigger: '.o_app[data-menu-xmlid="calendar.mail_menu_calendar"]', - content: 'Open Calendar', - run: 'click', + content: "Open Calendar", + run: "click", }, { - trigger: '.o-calendar-button-new', - content: 'Create a new event', - run: 'click', + trigger: ".o-calendar-button-new", + content: "Create a new event", + run: "click", }, { - trigger: '#name_0', - content: 'Give a name to the new event', - run: 'text TEST EVENT', + trigger: "#name_0", + content: "Give a name to the new event", + run: "text TEST EVENT", }, { - trigger: '#start_0', - content: 'Give a date to the new event', + trigger: "#start_0", + content: "Give a date to the new event", run: `text ${todayDate()}`, }, { - trigger: '.fa-cloud-upload', - content: 'Save the new event', - run: 'click', + trigger: ".fa-cloud-upload", + content: "Save the new event", + run: "click", }, { - trigger: '.o_back_button', - content: 'Go back to Calendar view', - run: 'click', + trigger: ".o_back_button", + content: "Go back to Calendar view", + run: "click", }, { trigger: '.dropdown-toggle:contains("Week")', - content: 'Click to change calendar view', - run: 'click', + content: "Click to change calendar view", + run: "click", }, { trigger: '.dropdown-item:contains("Month")', - content: 'Change the calendar view to Month', - run: 'click', + content: "Change the calendar view to Month", + run: "click", }, { - trigger: '.fc-day-header:contains("Monday")', - content: 'Change the calendar view to week', + trigger: '.fc-day-header:contains("Mon")', + content: "Check the day is properly displayed", }, { trigger: '.fc-time:contains("10:00")', - content: 'Check the time is properly displayed', + content: "Check the time is properly displayed", }, { trigger: '.o_event_title:contains("TEST EVENT")', - content: 'Check the event title', + content: "Check the event title", isCheck: true, }, - ]}); + ], +}); - registry.category("web_tour.tours").add('test_calendar_delete_tour', { - test: true, - steps: () => [ +registry.category("web_tour.tours").add("test_calendar_delete_tour", { + test: true, + steps: () => [ { - content: 'Select filter (everybody)', + content: "Select filter (everybody)", trigger: 'div[data-value="all"] input', }, { - content: 'Click on the event (focus + waiting)', + content: "Click on the event (focus + waiting)", trigger: 'a .fc-content:contains("Test Event")', async run() { $('a .fc-content:contains("Test Event")').click(); await new Promise((r) => setTimeout(r, 1000)); $('a .fc-content:contains("Test Event")').click(); - } + }, }, { - content: 'Delete the event', - trigger: '.o_cw_popover_delete', + content: "Delete the event", + trigger: ".o_cw_popover_delete", }, { - content: 'Validate the deletion', - trigger:'button:contains("Delete")', + content: "Validate the deletion", + trigger: 'button:contains("Delete")', async run() { $('button:contains("Delete")').click(); await new Promise((r) => setTimeout(r, 1000)); - } + }, }, - ]}); - - registry.category("web_tour.tours").add('test_calendar_decline_tour', { - test: true, - steps: () => [ + ], +}); + +registry.category("web_tour.tours").add("test_calendar_decline_tour", { + test: true, + steps: () => [ { - content: 'Click on the event (focus + waiting)', + content: "Click on the event (focus + waiting)", trigger: 'a .fc-content:contains("Test Event")', async run() { $('a .fc-content:contains("Test Event")').click(); await new Promise((r) => setTimeout(r, 1000)); $('a .fc-content:contains("Test Event")').click(); - } + }, }, { - content: 'Delete the event', - trigger: '.o_cw_popover_delete', + content: "Delete the event", + trigger: ".o_cw_popover_delete", }, { - content: 'Wait declined status', - trigger: '.o_attendee_status_declined', + content: "Wait declined status", + trigger: ".o_attendee_status_declined", isCheck: true, }, - ]}); - - registry.category("web_tour.tours").add('test_calendar_decline_with_everybody_filter_tour', { - test: true, - steps: () => [ + ], +}); + +registry.category("web_tour.tours").add("test_calendar_decline_with_everybody_filter_tour", { + test: true, + steps: () => [ { - content: 'Select filter (everybody)', + content: "Select filter (everybody)", trigger: 'div[data-value="all"] input', }, { - content: 'Click on the event (focus + waiting)', + content: "Click on the event (focus + waiting)", trigger: 'a .fc-content:contains("Test Event")', async run() { $('a .fc-content:contains("Test Event")').click(); await new Promise((r) => setTimeout(r, 1000)); $('a .fc-content:contains("Test Event")').click(); - } + }, }, { - content: 'Delete the event', - trigger: '.o_cw_popover_delete', + content: "Delete the event", + trigger: ".o_cw_popover_delete", }, { - content: 'Select filter (everybody)', + content: "Select filter (everybody)", trigger: 'div[data-value="all"] input', }, { - content: 'Wait declined status', - trigger: '.o_attendee_status_declined', + content: "Wait declined status", + trigger: ".o_attendee_status_declined", isCheck: true, }, - ]}); + ], +}); diff --git a/addons/calendar/views/calendar_views.xml b/addons/calendar/views/calendar_views.xml index 8b027fcf237..08c6adff3e0 100644 --- a/addons/calendar/views/calendar_views.xml +++ b/addons/calendar/views/calendar_views.xml @@ -126,111 +126,42 @@
- - - - - -